The PokéCommunity Forums  

Go Back   The PokéCommunity Forums > ROM Hacking > Research & Development
Sign Up Rules/FAQ Live Battle Blogs Mark Forums Read

Notices

Research & Development Got a well-founded knack with ROM hacking? Love reverse-engineering the Pokémon games? Or perhaps you love your assembly language. This is the spot for polling and gathering your ideas, and then implementing them! Share your hypothesis, get ideas from others, and collaborate to create!
Research & Development programs in this forum are subject to moderator approval before they are displayed.


Advertise here

Reply
 
Thread Tools
  #1    
Old January 4th, 2010, 08:14 PM
pokemonforever27's Avatar
pokemonforever27
IS RIDING LUGIA...
 
Join Date: Jul 2008
Location: At the computer...
Age: 23
Gender: Male
Nature: Calm

Advertise here
If you play long enough and make it to the TRAINER TOWER in many POKEMON games, one of the things that's unique about the tower is that the trainers' POKEMON in there all match the highest level of your POKEMON. That's the only place in the game where that occurs.

After hours of digging through commands, tutorials, FIRERED TRAINER TOWER scripts, and tools, I finally figured it out! This comes in four parts:

PART A - JPAN/pokemonforever27's LevelCheck ASM routine
When Darthatron first suggested I look into JPAN's hacked FIRERED engine, I thought "I'm stumped now, I'll look into it." So I took a look, and that solved part of the problem. A way to do this was implemented, but that still didn't solve the burning question in my head: What is THE way? So I dug more. Eventually, JPAN posted an ASM routine that would store the highest level in LASTRESULT. I then took to learning ASM, and finally got it and the CheckLevel routine learned. This is also when I realized how GAMEFREAK did it.

PART B - GAMEFREAK's Method
GAMEFREAK's method is rather long and drawn out. It consists of checking party pointers in variables and comparing them until all possible combinations have been exhausted. The process is so long, in fact, that trying to read through it all is next to impossible. It takes them over 400 lines of code to make the check! Why do it this way? Well, because they only had to code it once (in the TRAINER TOWER). Since it was only needed once, why go through the extra trouble to make the function? The battles are custom as well, in a similar fashion. There are actually 4 hidden person events that setup the custom battles. I've included images in the attachment so that you can take a look at the script and person events for yourself.

PART C - Mysterious msgbox
The one thing that puzzled me though, even after figuring out their method, is a textbox. You can find it's code in the file "MSGBOX.txt" in the attachment. Confused? I'd be surprised if you weren't. I didn't dig into every command in this one like I did the LevelCheck because that wasn't what I was researching. However, somehow, these commands, when called in the form "msgbox 0x2021D18 MSG_KEEPOPEN" repeat the last string the player read. If you talk to the girl in PALLET TOWN she'll say something about raising strong POKEMON, then execute this script, it will say the same thing. If you talk with the man about technology and the PC, the script will say the same thing. Even go so far as to say "Previously on your quest..."

PART D - End result
The best method for implementing this is to learn ASM, and write a simple routine to checklevels there. GAMEFREAK's method would take way too long to decipher fully, and the time could be better spent learning something that could be far more powerfull to know. I still have some work to do on implementation, but then I'll include a patch for FIRERED in the attachment. For now, use JPAN's engine.

SPECIAL THANKS
Nintendo/GAMEFREAK - POKEMON FIRERED VERSION
Forgotten and the VBA team - VisualBoyAdvance Emulator
HackMew - XSE; ASM Tutorials
LU-HO Poke - Advance Map
PokeCommunity - Hosting the ROM hacking scene
JPAN - Helping me learn ASM; ASM Tutorials; getting me started; FireRed Hacked Engine
interdpth - GetLevel Function; Simple ASM routine; LevelCheck routine in POKEMON EMERALD
Darthatron - Getting me started; Fixing the memory address for LASTRESULT

Goals:
1. Figure out how GAMEFREAK checks the levels of the player's POKEMON. - DONE
2. Work out the bugs of the script and implement it. - 90% DONE
3. Write in a simple command (e.x. "checkpokemonlevel") to call the function. - DONE (JPAN's hacked engine)

Any suggestions are both appreciated and encouraged.
Attached Files
File Type: zip LEVELCHECK.zip‎ (372.7 KB, 48 views) (Save to Dropbox)
__________________
TUTORIALS (ROM HACKING):

GAMES:

"We tend to move towards and act like that which we picture in our minds" (Henry David Thoreau)

Last edited by pokemonforever27; January 9th, 2010 at 07:08 PM. Reason: SOLVED
Reply With Quote
  #2    
Old January 5th, 2010, 12:33 AM
Darthatron's Avatar
Darthatron
巨大なトロール。
Community Supporter Tier 2
 
Join Date: Jan 2006
Location: Melbourne, Australia
Age: 22
Gender: Male
Nature: Modest
JPAN did this in his FireRed hacked engine, maybe you should look in to that?
__________________
あなた は しきしゃ です
わたし は ばか です
Reply With Quote
  #3    
Old January 5th, 2010, 06:45 AM
pokemonforever27's Avatar
pokemonforever27
IS RIDING LUGIA...
 
Join Date: Jul 2008
Location: At the computer...
Age: 23
Gender: Male
Nature: Calm
Thank you, Darthatron.

That still leaves figuring out Nintendo's method. I'd at least like to know how they did it. There is a million different variables and comparisons in those scripts. After looking at JPAN's method, the levels are probably in there, but every time I try to dig through them, I end up getting lost!

If it isn't in there, then it's probably hidden somewhere else that I haven't thought of to look.
__________________
TUTORIALS (ROM HACKING):

GAMES:

"We tend to move towards and act like that which we picture in our minds" (Henry David Thoreau)
Reply With Quote
  #4    
Old January 5th, 2010, 09:53 AM
interdpth
I've seen things, man.
 
Join Date: Jul 2004
In FireRed there is this GetLevel function at 803E7C4

I'm not sure how it decides which level to get but it returns the level in r0. :)
__________________
yes, this is doge.

potato is not here.

bye
Reply With Quote
  #5    
Old January 5th, 2010, 01:14 PM
Powerflare's Avatar
Powerflare
Pokémon Trainer N
 
Join Date: May 2008
Location: Unova
Age: 17
Gender: Male
Nature: Rash
It is an interesting question you pose—"how?" It is so strange that they somehow can slip these things into the game that are so complicated to figure out or determine its location.
__________________
I like that face! Like to battle, do you?"-Crasher Wake

"... ... ... ... ... ... ... ... ... ... ... ..." -Red

Pokémon Black Version || Pokémon White Version

I believe in Jesus Christ my Savior. If you do too, and aren't scared to admit it, then copy and paste this in your signature.
Reply With Quote
  #6    
Old January 5th, 2010, 04:36 PM
interdpth
I've seen things, man.
 
Join Date: Jul 2004
I think it'd be
LDR r0,=PokemonVal(Not sure if it'd be an index number or a pointer to a struct)
BL 0x803E7C4
r0 would be the level!
__________________
yes, this is doge.

potato is not here.

bye
Reply With Quote
  #7    
Old January 5th, 2010, 07:36 PM
pokemonforever27's Avatar
pokemonforever27
IS RIDING LUGIA...
 
Join Date: Jul 2008
Location: At the computer...
Age: 23
Gender: Male
Nature: Calm
Quote:
Originally Posted by interdpth View Post
I think it'd be
LDR r0,=PokemonVal(Not sure if it'd be an index number or a pointer to a struct)
BL 0x803E7C4
r0 would be the level!
Oh boy, here we go! I have no knowledge of assembler at all. You're probably right. This is probably a stupid question, but how then, would you implement this in, say, XSE, for example? The ultimate goal is to make an ips patch that would add a simple command to use it.

Thanks for all your help, guys!
__________________
TUTORIALS (ROM HACKING):

GAMES:

"We tend to move towards and act like that which we picture in our minds" (Henry David Thoreau)
Reply With Quote
  #8    
Old January 5th, 2010, 07:51 PM
ZodiacDaGreat's Avatar
ZodiacDaGreat
Working on a Mobile System
 
Join Date: Feb 2007
Location: South Pacific
Age: 21
Gender: Male
Nature: Relaxed
Send a message via ICQ to ZodiacDaGreat
Then you'd have to make a routine or command or special or whatever that returns the level to a variable, so it can be tempered with by scripts.
__________________
Reply With Quote
  #9    
Old January 5th, 2010, 08:42 PM
JPAN
pokemon rom researcher
 
Join Date: Dec 2008
Quote:
Originally Posted by pokemonforever27 View Post
Oh boy, here we go! I have no knowledge of assembler at all.
Well, I'll try to explain a simple sample code in compilable ASM and, hopefully, teach those who don't know ASM something, even if little. For all Experienced ASM hackers out there, better not look at this code, as it is over commented:
Code:
.align 2
.thumb
/*Get your pokemon highest level*/
/*this code can be implemented in any version if you change the pointers that are
indicated*/
start: push {r1-r4, lr} /*this is the start of the code. it saves the stuff the*/
                              /*command that came before this one used*/
         ldr r4, Party_pointer /*gets the location for the pokemon party from the data*/
         mov r0, #0x0   /*this one puts 0 in R0 so we can use it as our highest*/
                              /*level. Unless you have no pokemon, your pokemon will*/
                              /*have a level higher than 0, so it works fine*/
        mov r3, #0x6    /*this puts 6 in r3, as 6 is the max number of pokemon*/
                             /*you can have. this will be a counter to check how many*/
                             /*pokemon are left*/
        add r4, #0x54  /*adds to the party pointer the level location. this will*/
                            /*allow us to read from where the level is stored*/
                            /*read bulbapedia's "Pokémon data structure in Generation III"*/
                            /*for more info*/
loop: ldrb r1, [r4]     /*ldrb loads a byte from the pointer inside the R on the*/
                            /*right and puts it inside the one on the left*/
                            /*the lable on the left will let us make a loop. It can*/
                            /*have any name you want.*/
       cmp r0, r1       /*compares r0 to r1.*/
       bgt next         /*if r0 is Greater than r1, than we have a bigger level*/
                           /*if this one is the highest, we will keep it in r0*/
      add r0, r1, #0x0    /*this will store in r0 the contents of R1 plus 0, which*/
                               /*is R1. this is used to pass bytes from one register to*/
                               /*the other*/
                              /*be it the highest or not, we want to perform this. So,*/
                             /*we now want to know of all the pokemon you have. we*/
                              /*checked one so far. so, let's check the others*/
next: add r4, #0x64  /*that is the size of the party pokemon. so adding 0x64*/
                             /*we'll have the next pokemon*/
       sub r3, #0x1    /*subtracting one to the counter, as we now have one less*/
                            /*pokemon to check*/
       cmp r3, #0x0   /*now we check the counter to see if*/
       bgt loop          /*if it's not the last, then we need to check the rest.*/
                           /*makes it return to the Loop lable, and check all again*/
                           /*if it was the last pokemon, and the code is correct,*/
                           /*then it's time to leave. leaves R0 with the biggest level*/
 
                          /*to allow this to run properly with the callasm command*/
                          /*I'll also allow this to return to 0x800d (LASTRESULT)*/
 ldr r2, Var_800d   /*as with the party one, this one loads a pointer, but*/
                         /*this time is the variable 0x800d location*/
 strh r0, [r2]         /*this stores in the variable the contents of r0, that is*/
                          /*the highest level*/
 pop {r1-r4, pc}    /*this one ends the execution of the code, it's like the*/
                         /*return command on XSE. restores the old values so the*/
                         /*program running before can continue normally*/
                         /*notice r0 is not saved or restored. with specials, the*/
                         /*contents of r0 are returned to the variable you want*/
/*here follows data that the program needs to load to work porperly. The pointers can*/
/*be changed so that this program can be used in any other version, like ruby*/
Party_pointer: .word 0x02024284  /*this is the party pokemon location in FR(u)*/
Var_800d: .word 0x020370d2       /*and this is variable 0x800d, in FR(u)*/
Quote:
Originally Posted by pokemonforever27 View Post
how then, would you implement this in, say, XSE, for example?
After compiling this routine(and making it compatible with your version first), just put it at one of the empty special slots in the game (in r/s, you'ld have to make one, but all others have them) and run it there. Or, if you want, you can also run it with the callasm command.
And this is how we check the party highest level. For the second one, it takes a lot more work.
PS: sorry about the poor visibility, but this board doesn't allow tabs.
__________________
Here are the links for my work


Currently working on:
Battle Script Documentation
Another large project
Reply With Quote
  #10    
Old January 8th, 2010, 11:39 AM
pokemonforever27's Avatar
pokemonforever27
IS RIDING LUGIA...
 
Join Date: Jul 2008
Location: At the computer...
Age: 23
Gender: Male
Nature: Calm
I've got the ASM routine compiled and put into the ROM. I checked all the memory values in VBA and the routine appears to be working. I tried implementing it with the callasm function, but can't seem to get it to work. That leaves the other option.

Quote:
After compiling this routine(and making it compatible with your version first), just put it at one of the empty special slots in the game (in r/s, you'ld have to make one, but all others have them) and run it there
How do I insert something into an empty special?

PS, once I get this working in the ROM, I'll make a big post with all the information I've discovered.
__________________
TUTORIALS (ROM HACKING):

GAMES:

"We tend to move towards and act like that which we picture in our minds" (Henry David Thoreau)
Reply With Quote
  #11    
Old January 8th, 2010, 01:35 PM
interdpth
I've seen things, man.
 
Join Date: Jul 2004
I was looking at the battle tower script in Emerald, it looked like special EA and EB have stuff to do with the get level stuff, that's all I found out since I don't hack Emerald. D;
__________________
yes, this is doge.

potato is not here.

bye
Reply With Quote
  #12    
Old January 8th, 2010, 08:17 PM
Darthatron's Avatar
Darthatron
巨大なトロール。
Community Supporter Tier 2
 
Join Date: Jan 2006
Location: Melbourne, Australia
Age: 22
Gender: Male
Nature: Modest
Quote:
Originally Posted by pokemonforever27 View Post
I've got the ASM routine compiled and put into the ROM. I checked all the memory values in VBA and the routine appears to be working. I tried implementing it with the callasm function, but can't seem to get it to work. That leaves the other option.
He had the wrong offset for LASTRESULT, change the line...
Code:
Var_800d: .word 0x020370d2       /*and this is variable 0x800d, in FR(u)*/
to...
Code:
Var_800d: .word 0x020370d0       /*and this is variable 0x800d, in FR(u)*/
Also, make sure you add 1 to the offset when using callasm.
__________________
あなた は しきしゃ です
わたし は ばか です
Reply With Quote
  #13    
Old January 15th, 2010, 05:18 AM
HackMew's Avatar
HackMew
Mewtwo Strikes Back
 
Join Date: Jun 2006
I've got a couple of routines for you. They are designed to get the maximum and minum party level respectively. Both are tested and working, and can be executed through callasm.

Code:
.text
.align 2
.thumb
.thumb_func
.global GetMaxPartyLevel

main:
	push {r0-r3, lr}
	ldr r1, .PARTY_LVL
	ldrb r0, [r1]
	mov r3, #0x5

loop:
	add r1, #0x64
	ldrb r2, [r1]
	cmp r2, r0
	ble next
	add r0, r2, #0x0

next:
	sub r3, #0x1
	cmp r3, #0x0
	bne loop

return:
	ldr r1, .VAR
	strh r0, [r1]
	pop {r0-r3, pc}

.align 2
.PARTY_LVL:
	.word 0x02024284 + 0x54
.VAR:
	.word 0x020270B6 + (0x800D * 2)
Code:
.text
.align 2
.thumb
.thumb_func
.global GetMinPartyLevel

main:
	push {r0-r3, lr}
	ldr r1, .PARTY_LVL
	ldrb r0, [r1]
	mov r3, #0x5

loop:
	add r1, #0x64
	ldrb r2, [r1]
	cmp r2, #0x0
	beq return
	cmp r2, r0
	bge next
	add r0, r2, #0x0

next:
	sub r3, #0x1
	cmp r3, #0x0
	bne loop

return:
	ldr r1, .VAR
	strh r0, [r1]
	pop {r0-r3, pc}

.align 2
.PARTY_LVL:
	.word 0x02024284 + 0x54
.VAR:
	.word 0x020270B6 + (0x800D * 2)
As you can see, the routines are pretty short and simple so I did not comment them on purpose.
Either way, here's a brief description of what happens:
  1. Get the Pokémon party level address into r1.
  2. Load the first value into r0, and assume it's the maximum (or minimum) value yet.
  3. Loop 5 times, and update the maximum (or mininum) value if needed.
  4. Get the LASTRESULT var address into r1 and store the maximum (or minimum) level.
The minimum routine, however, is slighty different because a zero-level is not an acceptable value so there's an extra check to take that into account (otherwhise as long as your team is not full, the minimum level would always be zero, which is no good obviously). That also means the routine will loop up to 5 times rather than always 5 as it happens in the maximum routine.
__________________
Reply With Quote
  #14    
Old January 16th, 2010, 05:30 PM
pokemonforever27's Avatar
pokemonforever27
IS RIDING LUGIA...
 
Join Date: Jul 2008
Location: At the computer...
Age: 23
Gender: Male
Nature: Calm
Thank you, HackMew.

As soon as I get a demo released for the hack I'm currently working on, I will update the first post again with some more indepth stuff. I've learned a lot from troubleshooting my hacks.
__________________
TUTORIALS (ROM HACKING):

GAMES:

"We tend to move towards and act like that which we picture in our minds" (Henry David Thoreau)
Reply With Quote
Reply
Quick Reply

Sponsored Links


Advertise here
Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Minimum Characters Per Post: 25



All times are UTC -8. The time now is 09:00 AM.


Style by Nymphadora, artwork by Sa-Dui.
Like our Facebook Page Follow us on Twitter © 2002 - 2014 The PokéCommunity™, pokecommunity.com.
Pokémon characters and images belong to The Pokémon Company International and Nintendo. This website is in no way affiliated with or endorsed by Nintendo, Creatures, GAMEFREAK, The Pokémon Company or The Pokémon Company International. We just love Pokémon.
All forum styles, their images (unless noted otherwise) and site designs are © 2002 - 2014 The PokéCommunity / PokéCommunity.com.
PokéCommunity™ is a trademark of The PokéCommunity. All rights reserved. Sponsor advertisements do not imply our endorsement of that product or service. User generated content remains the property of its creator.