The PokéCommunity Forums Fan Games ROM Hacking Research & Development
Development Making Dive Work in FireRed

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!

Reply
 
Thread Tools
  #1    
Old July 19th, 2011 (8:53 AM).
Giga Universe's Avatar
Giga Universe Giga Universe is offline
Working on a tool.
 
Join Date: Aug 2007
Location: South Africa
Age: 22
Gender: Male
Nature: Calm
Posts: 121
Send a message via ICQ to Giga Universe Send a message via AIM to Giga Universe Send a message via Yahoo to Giga Universe Send a message via Skype™ to Giga Universe
Dive doesn't work in FireRed, I'm not sure if the code is there for it to work, but I suppose we could just write some more, as we could then create things like the underground.
I'm not sure if it helps, but I wrote a routine that finds a connection and stores the map and bank of that connection in a variable. You have to tell it what kind of connection to search for though...
0x5 is a 'Dive' connection, and 0x6 is a 'Emerge' connection.

Code:
.text
.align 2
.thumb
.thumb_func

main:
	push {r0-r5,lr}
	ldr r0, mapheader
	ldr r0, [r0,#0xC]
	ldr r1, [r0]
	ldr r0, [r0,#0x4]
	ldr r4, variable
	ldrh r5, [r4]
	mov r2, #0x0
	b search

search:
	cmp r1, #0x0
	beq notfound
	ldrb r3, [r0,r2]
	cmp r3, r5
	beq found
	add r2,#0xC
	sub r1, #0x1
	b search
	
notfound:
	pop {r0-r5,pc}

found:
	add r2, #0x8
	ldrb r1, [r0,r2]
	add r2, #0x1
	ldrb r2, [r0,r2]
	lsl r3, r1, #0x8
	orr r3, r2
	strh r3, [r4]
	pop {r0-r5,pc}
	
.align 2

mapheader:
	.word 0x02036DFC
	
variable:
	.word 0x020370D0 @Last result
All we need from here is to make it warp, and then we can make a tile behavior for it or something. I have absolutely no idea how to make it warp, so if someone could help there, then I could make this thing work...
__________________
Reply With Quote

Relevant Advertising!

  #2    
Old July 19th, 2011 (12:53 PM). Edited July 19th, 2011 by knizz.
knizz's Avatar
knizz knizz is offline
 
Join Date: Aug 2007
Posts: 192
From Underwater: call script 081BE3D4
From Overworld: call script 081BE38B

To connect change the hometown-connection to a dive-connection set 08352754 to 05

To disable the check whether you actually have a dive pokemon with you set the 6 bytes 081BE394-081BE399 to 0. This function also decides which pokemon is shown in the black bar. So if you disable it and see a bad egg. That's because of this.

Disable the badge-check for HMs set 0812462E to 0.

There are two ways to warp. The special one is just used when the behaviour of the tile the player is standing on is 0x19 and when the light byte is set to 0x5. Through different ways the execution can reach 0806DFB0. To avoid that you can destroy the bne/beqs. Write 0000 to 0806DF82 and 0806DF9C. This is not a good solution though. I'm sure there is a reason why these if's are there.

I don't know if the warp destination is correct. But the bank and mapnumber are in the variables 7004 and 7005.
__________________
Firered IDA 6.6 DB: https://www.dropbox.com/s/d856o3pyndyr5sr/firered.idb
VBA-M with lua scripting support
Reply With Quote
  #3    
Old July 28th, 2011 (12:01 PM). Edited July 29th, 2011 by Teh Blazer.
Teh Blazer's Avatar
Teh Blazer Teh Blazer is offline
Divider of Zero
 
Join Date: Feb 2009
Location: 'MERICA
Age: 21
Gender: Male
Nature: Relaxed
Posts: 762
I may not be the brightest Ampharos in the lighthouse, but how would I make sense of this? Do we have to write a script and put this in, and how do we give stuff tile behaviors?
__________________
Being a hero is overrated anyways
Reply With Quote
  #4    
Old July 29th, 2011 (3:45 PM).
Putin's Avatar
Putin Putin is offline
Anspruchsvolle Narr
 
Join Date: Dec 2010
Location: Florida
Gender: Male
Nature: Brave
Posts: 52
Perhaps this is flawed somehow, but could this not effectively be done using signpost scripts? You'd have to use it sparingly unless you had the space to spare, but then, using too many dive points is kind of obnoxious anyway.
Reply With Quote
  #5    
Old July 29th, 2011 (4:13 PM).
Lyzo's Avatar
Lyzo Lyzo is offline
On vacation
 
Join Date: Mar 2007
Location: The Netherlands
Age: 22
Gender: Male
Nature: Bold
Posts: 252
Send a message via Windows Live Messenger to Lyzo
Quote:
Originally Posted by Putin View Post
Perhaps this is flawed somehow, but could this not effectively be done using signpost scripts? You'd have to use it sparingly unless you had the space to spare, but then, using too many dive points is kind of obnoxious anyway.
You could do that, but if you wanted to make big dive areas like in Ruby and Saphire, this would make the game lag. So putting an ASM routine in the tile behaviour, either using JPAN's engine, or... something else Is much more efficient.

If you would like to make Dive work in only one part of your game, then your solution would work
__________________


Thank you The Blueprint !!!
Reply With Quote
  #6    
Old July 30th, 2011 (5:11 AM).
sonic1's Avatar
sonic1 sonic1 is offline
ASM is my life now...
 
Join Date: May 2008
Location: Portugal
Age: 22
Gender: Male
Nature: Timid
Posts: 79
Why don't you locate one of those useless tile behaviour scripts like the ones that are the TV's talking or something and make a 'goto' (like in asm bx r0 xD ) to the diving scripts?

This way it isn't needed JPAN's engine for it to work. And its more easier IMO.
__________________
This signature has been disabled.
over 350px high
Please review and fix the issues by reading the signature rules.

You must edit it to meet the limits set by the rules before you may remove the [sig-reason] code from your signature. Removing this tag will re-enable it.

Do not remove the tag until you fix the issues in your signature. You may be infracted for removing this tag if you do not fix the specified issues. Do not use this tag for decoration purposes.
Reply With Quote
  #7    
Old October 19th, 2011 (11:46 AM).
avolonsaber avolonsaber is offline
 
Join Date: Oct 2010
Posts: 182
Wow, I was just about to start a thread about this :3

When it comes to ASM hacking Pokemon games I know nothing, in fact, my ASM knowledge is limited at best, and only limited to the Sonic The Hedgehog titles for the Genesis, lol. If you can port various things from one Sonic title to another though, I'm sure someone could port Dive and it's functionality to Fire Red. Sooo, yeah, if anyone is working on this, keep at it!
Reply With Quote
  #8    
Old October 19th, 2011 (7:38 PM).
Missingyep Missingyep is offline
 
Join Date: Jul 2010
Posts: 275
Quote:
Originally Posted by sonic1 View Post
Why don't you locate one of those useless tile behaviour scripts like the ones that are the TV's talking or something and make a 'goto' (like in asm bx r0 xD ) to the diving scripts?
Those are actually scripts, not ASM.

As for the DIVE functionality, I can tell you guys that the scripts (as in XSE) for it still exist in FireRed. They don't have any identifiable warp calls, so either the warping results from the field move animation commands, or ASM detects when the script ends and warps then.

If the former, then that would explain why the DIVE field move animation crashes when triggered from script: it needs to read values from some location to know where to warp to, and it's not getting valid ones. Figure out how the field move animation works, and it may then be possible to make DIVE fully-functional.
Reply With Quote
  #9    
Old October 20th, 2011 (7:34 AM).
sonic1's Avatar
sonic1 sonic1 is offline
ASM is my life now...
 
Join Date: May 2008
Location: Portugal
Age: 22
Gender: Male
Nature: Timid
Posts: 79
I know that they were scripts. I was refering to the 'goto' script command.

About DIVE, i already have a fully functional version, and if you watch carefully, the coordinates are the ones of the player when warping.

(Cheap Test): http://www.youtube.com/watch?v=pZiSs0r9yHI
__________________
This signature has been disabled.
over 350px high
Please review and fix the issues by reading the signature rules.

You must edit it to meet the limits set by the rules before you may remove the [sig-reason] code from your signature. Removing this tag will re-enable it.

Do not remove the tag until you fix the issues in your signature. You may be infracted for removing this tag if you do not fix the specified issues. Do not use this tag for decoration purposes.
Reply With Quote
  #10    
Old November 18th, 2011 (7:29 AM). Edited March 18th, 2012 by Hackrex.
Hackrex Hackrex is offline
 
Join Date: Aug 2007
Gender:
Posts: 35
Wow, it´s realy working?
for dive you can insert a new tileset (underwater tileset maybe tiles from kyledove). Ok, I try to make it work on a german rom. I hope I´m succesful.
What do I need in the Script? Should I use any special commands?

//Edit1: Ok, it works on a german rom, too.

//Edit2: Ok, I have written a tutorial. I hope you can understand it. If not please tell me and I would explain it. http://www.pokecommunity.com/showthread.php?t=276031
__________________
Temporarily not available
Reply With Quote
  #11    
Old May 13th, 2012 (12:25 AM).
buggy131313 buggy131313 is offline
 
Join Date: May 2012
Posts: 2
Well, another trick is to have Dive at certain spots make you 'wash up' on some other location. Say, a beach that you warp to.
Reply With Quote
  #12    
Old October 14th, 2014 (6:21 PM). Edited October 14th, 2014 by Lance32497.
Lance32497's Avatar
Lance32497 Lance32497 is online now
LanceKoijer of Pokemon_Addicts
 
Join Date: Aug 2014
Location: Criscanto town-Ginoa Region xD
Gender: Male
Nature: Adamant
Posts: 793
Quote:
Originally Posted by knizz View Post
From Underwater: call script 081BE3D4
From Overworld: call script 081BE38B
Good Day knizz, about the quoted one, How to call that offsets? So do I need to call it as a script or something? What is the step by step procedure?

Quote:
Originally Posted by knizz View Post
To connect change the hometown-connection to a dive-connection set 08352754 to 05
What is 08352754? Is that 0x352754 offset? And 08 refers to a POINTER? Am I right?



And.... Do I need to insert the ASM code given by giga universe?
Reply With Quote
  #13    
Old May 12th, 2016 (7:40 PM).
MartinTheHuman MartinTheHuman is offline
 
Join Date: Apr 2015
Gender: Male
Posts: 48
Any progress on this?
Reply With Quote
  #14    
Old May 13th, 2016 (6:42 AM).
BluRose BluRose is offline
practicing asmagic
 
Join Date: Apr 2014
Location: hyakkoku city
Age: 16
Gender: Male
Nature: Timid
Posts: 638
Quote:
Originally Posted by MartinTheHuman View Post
Any progress on this?
It's complete, I believe...
__________________
let's go somewhere i don't know yet
Reply With Quote
  #15    
Old August 1st, 2016 (12:32 PM). Edited 3 Weeks Ago by Spherical Ice.
NicolasLukas NicolasLukas is offline
 
Join Date: Aug 2015
Location: Germany
Gender: Male
Nature: Relaxed
Posts: 136
So how do you add the HM Dive in Pokemon Firered exactly. I have no idea how do do it. Can anybody help me, please?

I have no idea how to make this work properly:(

Okay, now it works :D :D :D
Yeah!
The only problem is the surfing sprite, that´s all.
Reply With Quote
  #16    
Old 3 Weeks Ago (11:57 AM). Edited 2 Weeks Ago by Spherical Ice.
Spherical Ice's Avatar
Spherical Ice Spherical Ice is online now
 
Join Date: Nov 2007
Location: Bristol, UK
Age: 19
Posts: 5,041
Originally I was going to explain how to fully port Dive to FR but I'm leaving out how to get the actual movement between maps when diving and whatnot because it's already sort of been documented and the method I'm using for it in my hack is not really "correct" (it just uses JPAN's behaviour byte hack and the existing unused Dive / Emerge scripts documented in earlier posts in this thread, essentially). What this does explain is how to port the properties of maps with the Underwater light level.

Changing the player's overworld for Underwater maps
This post details how to port the bobbing animation and change of overworld sprite that occurs in RSE when in an Underwater map.

Select two overworld sprites to replace with your Diving overworlds for the male player character and the female player character. For the sake of this tutorial, we will use overworld sprite 0x5B for the male diving player character and overworld sprite 0x5A for the female diving player character. You will notice that the order here is a little strange: the female overworld sprite comes before the male overworld sprite. The reason for this is partially laziness, and will be made clear later on in this tutorial. This will work for any overworld ID (I would assume anything greater than 0xFF is unsafe, however), and they do not need to be consecutive, but the male's sprite ID must be higher than the female's. Note that the sprites must share the original palette (0x00 by default) of the regular player character overworlds. You will only need 3 frames: facing down, facing up, and facing to the left.

Repoint the list at x35B874 to 14 bytes of free space; there should be only one pointer, at x5C7F4. It is 12 bytes long in an unedited FireRed ROM. The bytes correspond to the sprites for: walking; cycling; sitting down (which is used when surfing); reaching for a Poké Ball; fishing and reaching for the VS Seeker. The list alternates between male and female, so the first byte, 0x0, is the male player character's walking sprite, the second byte, 0x7, is the female player character's, the third byte is the male player character's cycling sprite, etc. As an aside, it's worth noting that the overworld sprite's for the player characters reaching for a Poké Ball or the VS Seeker when on a bicycle is stored in separate table.

To expand this table, append to the end the hexadecimal sprite number for the diving sprites for the male player character and the female player character. For this tutorial's sake, the newly repointed table will appear as such:
Code:
00 07 01 08 02 09 03 0A 04 0B 05 0C 5B 5A
A problem which arises at this stage is the function which retroactively determines the player's gender based on their current overworld sprite. This function, located at x5C824, simply assumes that any sprite number which is both below or equal to 0xB and greater than or equal to 0x7 is female, and anything else is male. Expressed in pseudocode, the function is simply:
Code:
bool get_gender_from_npc_type(signed int player_sprite_id)
{
    return !(player_sprite_id > 11 || player_sprite_id < 7);
}
This function exploits the fact that the Boolean value true is equivalent to 1, which also happens to be the bit for being female. Therefore, if player_sprite_id is greater than 11 or less than 7 (i.e. if the sprite is male), the parenthesis resolves to true (1), and so NOT 1, 0 (male), is returned. Similarly, if the player_sprite_id is greater than or equal to 7, or less than or equal to 11, the parenthesis resolves to false (0), and so NOT 0, 1 (female), is returned. Therefore, to expand this range to include our new diving overworlds, we simply need to change one of the boundaries. For the sake of this tutorial, the new function looks like this now:

Code:
bool __fastcall get_gender_from_npc_type(signed int player_sprite_id)
{
    return !(player_sprite_id > 0x5A || player_sprite_id < 0x7);
}
This means that only one single byte change is necessary:

Code:
Location: Original bytes -> Replacement bytes
x5C82A: 0B -> XX

where XX is the female diving overworld sprite's number in hexadecimal
Next, repoint the list at x35B882 to 16 bytes of free space; there should be two pointers, at x5C9B0 and x5C9F4. This list is structured is a sequence of sprites followed by the corresponding bit for their movement behaviour. For this list, the male sprites are listed first, and then the female sprites follow. To expand this table, and implement support for the new diving movement behaviour, we need to utilise the next unused bit in the bitfield, which is represented by the hexadecimal value 0x10. This means that for both the male and female lists, there will now be the diving sprite's overworld number in hexadecimal followed by 0x10. Our new table looks like so:

Code:
00 01 01 02 02 08 5B 10 07 01 08 02 09 08 5A 10
In order for this table to be read correctly, we need to adjust how it is read. Currently, it loops through the walkrun_state's sprite_change field, and, where it finds a 1 bit, returns the corresponding overworld sprite ID from the table. To access the female player's sprites, it multiplies the gender bit of the player (0 if male, 1 if female) by 0x6 (the old length of one gender's list) and adds it to the current iteration, multiplied by two due to the format of the table. This is expressed through the following pseudocode generated by IDA Pro of the function at x5C988:

Code:
signed int __fastcall decide_player_sprite_0(unsigned __int8 a1, unsigned __int8 a2)
{
    unsigned int v2; // [email protected]
    int v3; // [email protected]
    int v4; // [email protected]

    v2 = 0;
    v3 = 6 * a2;
    do
    {
        v4 = 2 * v2 + v3;
        if ( byte_835B882[v4] == a1 )
            return byte_835B882[v4 + 1];
        v2 = (v2 + 1) & 0xFF;
    }
    while ( v2 <= 2 );
    return 1;
}
(A very similar function exists at x5C9C8.)

It's therefore evident that, to let the ROM properly read this table, we need to change the 6 value to an 8 to accommodate the extra two bytes we've added to each individual gender's list. I'll gloss over the actual changes to the ASM because it involves confusing bit shifts, but in hex edits this simply looks like so:

Code:
Location: Original bytes -> Replacement bytes
x5C998: 40 18 44 00 -> 84 00 00 00
x5C9D6: 40 18 44 00 -> 84 00 00 00
The ROM will now correctly read the list again, meaning it will know to load the diving overworld sprites if the bit for diving's movement behaviour is set. This same logic could theoretically be used to add additional custom sprites for the player, using different bits.

Now that the walkrun_state's sprite_change field accommodates our new movement behaviour, we need to actually implement what happens when the 0x10 bit is set. The main function we are concerned with is x5BE08, whose IDA Pro pseudocode appears as follows:
Code:
int change_sprite_based_on_walkrun()
{
    unsigned int v0;
    unsigned int v1;
    int v3;

    sprite_change_bitfield = walkrun_state.sprite_change;
    if ( walkrun_state.sprite_change )
    {
        i = 0;
        do
        {
            if ( sprite_change_bitfield & 1 )
                call_via_r1(&npc_states[walkrun_state.npcid], change_player_sprite_to[i]);
            i = (i + 1) & 0xFF;
            sprite_change_bitfield >>= 1;
        }
        while ( i <= 7 );
        walkrun_state.sprite_change = 0;
    }
    return v3;
}
In essence, for every 1 in the bitfield, the player's movement behaviour is changed accordingly. We are therefore interested in expanding the table change_player_sprite_to, located at x35B844, which lists routines for walking, cycling, sitting and diving. The only problem is, in a vanilla FireRed ROM, the routine for diving, located at x5BEA4 looks like this:
Code:
void change_sprite_to_diving()
{
    ;
}
...in other words, it simply returns and does nothing. That's not very helpful, but taking a look at the routines for the other movement behaviours is more helpful. Walking's looks like so:

Code:
int change_sprite_to_normal()
{
    int v1; // [sp+0h] [bp-4h]@0

    sub_8150474(0);
    set_player_behaviour(0);
    return v1;
}
The routine for sitting looks like this:
Code:
int change_sprite_to_sitting()
{
    int v1; // [sp+0h] [bp-4h]@0

    sub_8150474(3u);
    set_player_behaviour(3u);
    return v1;
}}
And so on (the routine for cycling is slightly different but largely the same). Essentially, all that changes is the parameters passed to sub_8150474 (a function at x150474) and set_player_behaviour (a function at x150498). The function for diving we are to create will simply pass 0x4 to these functions. In order to save time dealing with hooks and whatnot, we're going to make some space for the diving routine at x5BEA4 by repointing the conveniently branchless routine directly after it, at x5BEA8, to 16 bytes of free space. This is literally just a matter of copying 16 bytes from x5BEA8 and pasting it in (word-aligned) free space, then replacing the sole pointer at x35B858 to one which points to the free space (remembering to +1!).

There are now 20 bytes of free space at the diving routine at x5BEA4, which is conveniently exactly as much space as we need. Paste the following routine at x5BEA4. I won't bother posting the disassembled version of it as it is literally just a clone of change_sprite_to_normal or change_sprite_to_sitting but passes 4 instead of 0 or 3 instead, and has the relative branches adjusted as necessary.
Code:
x5BEA4: 00 B5 04 20 F4 F0 E4 FA 04 20 F4 F0 F3 FA 01 BC 00 47 00 00
Now that that's out of the way, we can dive deeper into what these functions do. sub_8150474 seems to deal with the "Previously on your quest..." nonsense, and I don't really care enough to investigate into what it does. The interesting function, however, is set_player_behaviour, located at x150498. Let's take a look:

Code:
int __fastcall set_player_behaviour(unsigned __int8 a1)
{
    int v2; // [sp+0h] [bp-4h]@0

    select_player_behaviour(a1);
    return v2;
}
...okay, it's just a wrapper function that passes the byte it received to select_player_behaviour. We need to go deeper!

The function select_player_behaviour, at x150454, looks as such:
Code:
int __fastcall select_player_behaviour(unsigned __int8 a1)
{
    int v2; // [sp+0h] [bp-4h]@0

    if ( a1 <= 8u )
        call_via_r0(player_behaviours[a1]);
    return v2;
}
All this function does is call the function indexed by the value we passed way back in those change_sprite_to functions at the table player_behaviours. As we passed 0x4 for diving, it will call the fifth routine of that table. Note the limit of eight for the parameter: if you wanted to add extra movement behaviours, you'd have to change that limiter too. For our purposes, however, this function does not need to be changed.

The table player_behaviours is located at x471EDC, and is a list of pointers to routines. The first routine (index 0x0) is for the regular walking behaviour, the second (0x1) for cycling, the third (0x2) for fishing, the fourth (0x3) for surfing. The subsequent routine, it seems to just be filler functions. I haven't actually looked into them so while they may be unsafe to overwrite, I haven't noticed anything go wrong yet so please let me know if you detect an issue here. Under the assumption that this is safe to overwrite, we're going to change the pointer to the routine x1507CC to point to a brand new function which sets the behaviour for diving. The routine, which is copied near verbatim from Emerald, looks like such:
Code:
.thumb

push {r4,lr}
ldr r0, =(0x02037078) @ walkrun_state
ldrb r0, [r0,#0x5]
lsl r4,r0,#0x03
add r4,r4,r0
lsl r4,r4,#0x02
ldr r0, =(0x02036E38) @ npc_states
add r4,r4,r0
mov r0, #0x6
ldr r3, =(0x0805C808+1) @ get_sprite_for_player
bl call_via_r3
add r1,r0,#0x0
lsl r1,r1,#0x18
lsr r1,r1,#0x18
add r0,r4,#0x0
ldr r3, =(0x081507BC+1) @ npc_change_sprite_call
bl call_via_r3
ldrb r1, [r4,#0x18]
lsr r1,r1,#0x04
add r0,r4,#0x0
ldr r3, =(0x0805F218+1) @ npc_turn
bl call_via_r3
mov r0, #0x8
ldr r3, =(0x0805C970+1) @ change_sprite_bitfield
bl call_via_r3
ldrb r0, [r4,#0x4]
ldr r3, =(0x80DC6B0+1) @ bobbing
bl call_via_r3
strb r0,[r4,#0x1A]
pop {r4}
pop {r0}
bx r0

call_via_r3:
bx r3
Spoiler: Assembled binary
Code:
10 B5 12 48 40 79 C4 00 24 18 A4 00 10 48 24 18 06 20 10 4B 00 F0 19 F8 01 1C 09 06 09 0E 20 1C 0D 4B 00 F0 12 F8 21 7E 09 09 20 1C 0B 4B 00 F0 0C F8 08 20 0A 4B 00 F0 08 F8 20 79 09 4B 00 F0 04 F8 A0 76 10 BC 01 BC 00 47 18 47 78 70 03 02 38 6E 03 02 09 C8 05 08 BD 07 15 08 19 F2 05 08 71 C9 05 08 B1 C6 0D 08


So simply assemble the routine above (or copy the assembled binary in the spoiler above), insert it to word-aligned free space (the routine takes up 104 bytes) and change the pointer at x471EEC to point to it, again remembering to +1.

An interesting thing worth noting at this point is that the routine called "bobbing" in that routine, at xDC6B0, was already in the FireRed ROM, but was not pointed to by anything. Perhaps Game Freak intended for Dive in the Sevii Islands at one stage?

Anyway, with all the above changes made, your sprite will now automatically change to the specified ones when entering an Underwater map (whether that be through Diving or just simply warping there directly) with the correct bobbing animation (as well as running being disabled and whatnot). Do this, slap on the already existing Underwater Mist weather effect, port over the Underwater music from Emerald, and you've got yourself fully functional underwater maps (again provided you find a way to get Diving and Emerging to work based on behaviour bytes or whatever. Maybe one day I'll revisit this with the "proper" way to get those scripts / behaviour bytes functional).


tl;dr: https://www.pokecommunity.com/showthread.php?p=9615471#post9615471
__________________

A Pokemon that is discriminated!
Support squirtle and make it everyone's favourite.
Reply With Quote
  #17    
Old 2 Weeks Ago (9:31 PM). Edited 2 Weeks Ago by XD XD XD XD XD.
XD XD XD XD XD's Avatar
XD XD XD XD XD XD XD XD XD XD is offline
 
Join Date: Nov 2015
Gender: Male
Posts: 14
Quote:
Originally Posted by Spherical Ice View Post
Originally I was going to explain how to fully port Dive to FR but I'm leaving out how to get the actual movement between maps when diving and whatnot because it's already sort of been documented and the method I'm using for it in my hack is not really "correct" (it just uses JPAN's behaviour byte hack and the existing unused Dive / Emerge scripts documented in earlier posts in this thread, essentially). What this does explain is how to port the properties of maps with the Underwater light level.

Changing the player's overworld for Underwater maps
This post details how to port the bobbing animation and change of overworld sprite that occurs in RSE when in an Underwater map.

Select two overworld sprites to replace with your Diving overworlds for the male player character and the female player character. For the sake of this tutorial, we will use overworld sprite 0x5B for the male diving player character and overworld sprite 0x5A for the female diving player character. You will notice that the order here is a little strange: the female overworld sprite comes before the male overworld sprite. The reason for this is partially laziness, and will be made clear later on in this tutorial. This will work for any overworld ID (I would assume anything greater than 0xFF is unsafe, however), and they do not need to be consecutive, but the male's sprite ID must be higher than the female's. Note that the sprites must share the original palette (0x00 by default) of the regular player character overworlds. You will only need 3 frames: facing down, facing up, and facing to the left.

Repoint the list at x35B874 to 14 bytes of free space; there should be only one pointer, at x5C7F4. It is 12 bytes long in an unedited FireRed ROM. The bytes correspond to the sprites for: walking; cycling; sitting down (which is used when surfing); reaching for a Poké Ball; fishing and reaching for the VS Seeker. The list alternates between male and female, so the first byte, 0x0, is the male player character's walking sprite, the second byte, 0x7, is the female player character's, the third byte is the male player character's cycling sprite, etc. As an aside, it's worth noting that the overworld sprite's for the player characters reaching for a Poké Ball or the VS Seeker when on a bicycle is stored in separate table.

To expand this table, append to the end the hexadecimal sprite number for the diving sprites for the male player character and the female player character. For this tutorial's sake, the newly repointed table will appear as such:
Code:
00 07 01 08 02 09 03 0A 04 0B 05 0C 5B 5A
A problem which arises at this stage is the function which retroactively determines the player's gender based on their current overworld sprite. This function, located at x5C824, simply assumes that any sprite number which is both below or equal to 0xB and greater than or equal to 0x7 is female, and anything else is male. Expressed in pseudocode, the function is simply:
Code:
bool get_gender_from_npc_type(signed int player_sprite_id)
{
    return !(player_sprite_id > 11 || player_sprite_id < 7);
}
This function exploits the fact that the Boolean value true is equivalent to 1, which also happens to be the bit for being female. Therefore, if player_sprite_id is greater than 11 or less than 7 (i.e. if the sprite is male), the parenthesis resolves to true (1), and so NOT 1, 0 (male), is returned. Similarly, if the player_sprite_id is greater than or equal to 7, or less than or equal to 11, the parenthesis resolves to false (0), and so NOT 0, 1 (female), is returned. Therefore, to expand this range to include our new diving overworlds, we simply need to change one of the boundaries. For the sake of this tutorial, the new function looks like this now:

Code:
bool __fastcall get_gender_from_npc_type(signed int player_sprite_id)
{
    return !(player_sprite_id > 0x5A || player_sprite_id < 0x7);
}
This means that only one single byte change is necessary:

Code:
Location: Original bytes -> Replacement bytes
x5C82A: 0B -> XX

where XX is the female diving overworld sprite's number in hexadecimal
Next, repoint the list at x35B882 to 16 bytes of free space; there should be two pointers, at x5C9B0 and x5C9F4. This list is structured is a sequence of sprites followed by the corresponding bit for their movement behaviour. For this list, the male sprites are listed first, and then the female sprites follow. To expand this table, and implement support for the new diving movement behaviour, we need to utilise the next unused bit in the bitfield, which is represented by the hexadecimal value 0x10. This means that for both the male and female lists, there will now be the diving sprite's overworld number in hexadecimal followed by 0x10. Our new table looks like so:

Code:
00 01 01 02 02 08 5B 10 07 01 08 02 09 08 5A 10
In order for this table to be read correctly, we need to adjust how it is read. Currently, it loops through the walkrun_state's sprite_change field, and, where it finds a 1 bit, returns the corresponding overworld sprite ID from the table. To access the female player's sprites, it multiplies the gender bit of the player (0 if male, 1 if female) by 0x6 (the old length of one gender's list) and adds it to the current iteration, multiplied by two due to the format of the table. This is expressed through the following pseudocode generated by IDA Pro of the function at x5C988:

Code:
signed int __fastcall decide_player_sprite_0(unsigned __int8 a1, unsigned __int8 a2)
{
    unsigned int v2; // [email protected]
    int v3; // [email protected]
    int v4; // [email protected]

    v2 = 0;
    v3 = 6 * a2;
    do
    {
        v4 = 2 * v2 + v3;
        if ( byte_835B882[v4] == a1 )
            return byte_835B882[v4 + 1];
        v2 = (v2 + 1) & 0xFF;
    }
    while ( v2 <= 2 );
    return 1;
}
(A very similar function exists at x5C9C8.)

It's therefore evident that, to let the ROM properly read this table, we need to change the 6 value to an 8 to accommodate the extra two bytes we've added to each individual gender's list. I'll gloss over the actual changes to the ASM because it involves confusing bit shifts, but in hex edits this simply looks like so:

Code:
Location: Original bytes -> Replacement bytes
x5C998: 40 18 44 00 -> 84 00 00 00
x5C9D6: 40 18 44 00 -> 84 00 00 00
The ROM will now correctly read the list again, meaning it will know to load the diving overworld sprites if the bit for diving's movement behaviour is set. This same logic could theoretically be used to add additional custom sprites for the player, using different bits.

Now that the walkrun_state's sprite_change field accommodates our new movement behaviour, we need to actually implement what happens when the 0x10 bit is set. The main function we are concerned with is x5BE08, whose IDA Pro pseudocode appears as follows:
Code:
int change_sprite_based_on_walkrun()
{
    unsigned int v0;
    unsigned int v1;
    int v3;

    sprite_change_bitfield = walkrun_state.sprite_change;
    if ( walkrun_state.sprite_change )
    {
        i = 0;
        do
        {
            if ( sprite_change_bitfield & 1 )
                call_via_r1(&npc_states[walkrun_state.npcid], change_player_sprite_to[i]);
            i = (i + 1) & 0xFF;
            sprite_change_bitfield >>= 1;
        }
        while ( i <= 7 );
        walkrun_state.sprite_change = 0;
    }
    return v3;
}
In essence, for every 1 in the bitfield, the player's movement behaviour is changed accordingly. We are therefore interested in expanding the table change_player_sprite_to, located at x35B844, which lists routines for walking, cycling, sitting and diving. The only problem is, in a vanilla FireRed ROM, the routine for diving, located at x5BEA4 looks like this:
Code:
void change_sprite_to_diving()
{
    ;
}
...in other words, it simply returns and does nothing. That's not very helpful, but taking a look at the routines for the other movement behaviours is more helpful. Walking's looks like so:

Code:
int change_sprite_to_normal()
{
    int v1; // [sp+0h] [bp-4h]@0

    sub_8150474(0);
    set_player_behaviour(0);
    return v1;
}
The routine for sitting looks like this:
Code:
int change_sprite_to_sitting()
{
    int v1; // [sp+0h] [bp-4h]@0

    sub_8150474(3u);
    set_player_behaviour(3u);
    return v1;
}}
And so on (the routine for cycling is slightly different but largely the same). Essentially, all that changes is the parameters passed to sub_8150474 (a function at x150474) and set_player_behaviour (a function at x150498). The function for diving we are to create will simply pass 0x4 to these functions. In order to save time dealing with hooks and whatnot, we're going to make some space for the diving routine at x5BEA4 by repointing the conveniently branchless routine directly after it, at x5BEA8, to 16 bytes of free space. This is literally just a matter of copying 16 bytes from x5BEA8 and pasting it in (word-aligned) free space, then replacing the sole pointer at x35B858 to one which points to the free space (remembering to +1!).

There are now 20 bytes of free space at the diving routine at x5BEA4, which is conveniently exactly as much space as we need. Paste the following routine at x5BEA4. I won't bother posting the disassembled version of it as it is literally just a clone of change_sprite_to_normal or change_sprite_to_sitting but passes 4 instead of 0 or 3 instead, and has the relative branches adjusted as necessary.
Code:
x5BEA4: 00 B5 04 20 F4 F0 E4 FA 04 20 F4 F0 F3 FA 01 BC 00 47 00 00
Now that that's out of the way, we can dive deeper into what these functions do. sub_8150474 seems to deal with the "Previously on your quest..." nonsense, and I don't really care enough to investigate into what it does. The interesting function, however, is set_player_behaviour, located at x150498. Let's take a look:

Code:
int __fastcall set_player_behaviour(unsigned __int8 a1)
{
    int v2; // [sp+0h] [bp-4h]@0

    select_player_behaviour(a1);
    return v2;
}
...okay, it's just a wrapper function that passes the byte it received to select_player_behaviour. We need to go deeper!

The function select_player_behaviour, at x150454, looks as such:
Code:
int __fastcall select_player_behaviour(unsigned __int8 a1)
{
    int v2; // [sp+0h] [bp-4h]@0

    if ( a1 <= 8u )
        call_via_r0(player_behaviours[a1]);
    return v2;
}
All this function does is call the function indexed by the value we passed way back in those change_sprite_to functions at the table player_behaviours. As we passed 0x4 for diving, it will call the fifth routine of that table. Note the limit of eight for the parameter: if you wanted to add extra movement behaviours, you'd have to change that limiter too. For our purposes, however, this function does not need to be changed.

The table player_behaviours is located at x471EDC, and is a list of pointers to routines. The first routine (index 0x0) is for the regular walking behaviour, the second (0x1) for cycling, the third (0x2) for fishing, the fourth (0x3) for surfing. The subsequent routine, it seems to just be filler functions. I haven't actually looked into them so while they may be unsafe to overwrite, I haven't noticed anything go wrong yet so please let me know if you detect an issue here. Under the assumption that this is safe to overwrite, we're going to change the pointer to the routine x1507CC to point to a brand new function which sets the behaviour for diving. The routine, which is copied near verbatim from Emerald, looks like such:
Code:
.thumb

push {r4,lr}
ldr r0, =(0x02037078) @ walkrun_state
ldrb r0, [r0,#0x5]
lsl r4,r0,#0x03
add r4,r4,r0
lsl r4,r4,#0x02
ldr r0, =(0x02036E38) @ npc_states
add r4,r4,r0
mov r0, #0x6
ldr r3, =(0x0805C808+1) @ get_sprite_for_player
bl call_via_r3
add r1,r0,#0x0
lsl r1,r1,#0x18
lsr r1,r1,#0x18
add r0,r4,#0x0
ldr r3, =(0x081507BC+1) @ npc_change_sprite_call
bl call_via_r3
ldrb r1, [r4,#0x18]
lsr r1,r1,#0x04
add r0,r4,#0x0
ldr r3, =(0x0805F218+1) @ npc_turn
bl call_via_r3
mov r0, #0x8
ldr r3, =(0x0805C970+1) @ change_sprite_bitfield
bl call_via_r3
ldrb r0, [r4,#0x4]
ldr r3, =(0x80DC6B0+1) @ bobbing
bl call_via_r3
strb r0,[r4,#0x1A]
pop {r4}
pop {r0}
bx r0

call_via_r3:
bx r3
Spoiler: Assembled binary
Code:
10 B5 12 48 40 79 C4 00 24 18 A4 00 10 48 24 18 06 20 10 4B 00 F0 19 F8 01 1C 09 06 09 0E 20 1C 0D 4B 00 F0 12 F8 21 7E 09 09 20 1C 0B 4B 00 F0 0C F8 08 20 0A 4B 00 F0 08 F8 20 79 09 4B 00 F0 04 F8 A0 76 10 BC 01 BC 00 47 18 47 78 70 03 02 38 6E 03 02 09 C8 05 08 BD 07 15 08 19 F2 05 08 71 C9 05 08 B1 C6 0D 08


So simply assemble the routine above (or copy the assembled binary in the spoiler above), insert it to word-aligned free space (the routine takes up 104 bytes) and change the pointer at x471EEC to point to it, again remembering to +1.

An interesting thing worth noting at this point is that the routine called "bobbing" in that routine, at xDC6B0, was already in the FireRed ROM, but was not pointed to by anything. Perhaps Game Freak intended for Dive in the Sevii Islands at one stage?

Anyway, with all the above changes made, your sprite will now automatically change to the specified ones when entering an Underwater map (whether that be through Diving or just simply warping there directly) with the correct bobbing animation (as well as running being disabled and whatnot). Do this, slap on the already existing Underwater Mist weather effect, port over the Underwater music from Emerald, and you've got yourself fully functional underwater maps (again provided you find a way to get Diving and Emerging to work based on behaviour bytes or whatever. Maybe one day I'll revisit this with the "proper" way to get those scripts / behaviour bytes functional).

it look like the original, thanks spherical ice

EDIT: how to insert this at FR? it's complicated :3
Reply With Quote
  #18    
Old 2 Weeks Ago (4:09 AM).
Spherical Ice's Avatar
Spherical Ice Spherical Ice is online now
 
Join Date: Nov 2007
Location: Bristol, UK
Age: 19
Posts: 5,041
I probably should have just added a tl;dr of what byte changes to do. Here:

- Insert your underwater player overworld sprites. The female character must come before the male character (if you insert the male character's overworld at 0x5B, the female's should be 0x5A or smaller).
- Make the following byte changes:
Code:
x5C7F4: [Pointer to LOCATION1]
xLOCATION1: 00 07 01 08 02 09 03 0A 04 0B 05 0C YY XX
where XX is the female underwater sprite's index and YY is the male
- Make the following byte change:
Code:
x5C82A: XX
where XX is the female underwater sprite's index
- Make the following byte changes:
Code:
x5C9B0: [Pointer to LOCATION2]
x5C9F4: [Pointer to LOCATION2]
xLOCATION2: 00 01 01 02 02 08 YY 10 07 01 08 02 09 08 XX 10
where XX is the female underwater sprite's index and YY is the male
- Make the following byte changes:
Code:
x5C998: 84 00 00 00
x5C9D6: 84 00 00 00
- Make the following byte changes:
Code:
x35B858: [Pointer to LOCATION3+1]
xLOCATION3: 02 4A 11 78 20 20 08 43 10 70 70 47 78 70 03 02
- Make the following byte changes:
Code:
x5BEA4: 00 B5 04 20 F4 F0 E4 FA 04 20 F4 F0 F3 FA 01 BC 00 47 00 00
- Make the following byte changes:
Code:
x471EEC: [Pointer to LOCATION4+1]
xLOCATION4: 10 B5 12 48 40 79 C4 00 24 18 A4 00 10 48 24 18 06 20 10 4B 00 F0 19 F8 01 1C 09 06 09 0E 20 1C 0D 4B 00 F0 12 F8 21 7E 09 09 20 1C 0B 4B 00 F0 0C F8 08 20 0A 4B 00 F0 08 F8 20 79 09 4B 00 F0 04 F8 A0 76 10 BC 01 BC 00 47 18 47 78 70 03 02 38 6E 03 02 09 C8 05 08 BD 07 15 08 19 F2 05 08 71 C9 05 08 B1 C6 0D 08
__________________

A Pokemon that is discriminated!
Support squirtle and make it everyone's favourite.
Reply With Quote
Reply
Quick Reply

Sponsored Links
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

Forum Jump


All times are GMT -8. The time now is 5:06 PM.