Registrarse

[ASM] E|Wild pokemons and weather varying, based on the Emerald internal clock

aptitud

Usuario mítico
For now, this tutorial is only available in English.
Disculpe.


Acknowledgements :
a) The Emerald decompilation project, for their provided decompilation code,
letting me figure-out available routines to use and edit, to get the final result.
b) The thumb utilities for an easy way to actually compile my rewritten code.


Context and motivations (i.e. "Who and Why ?")
You may notice that I am a long registered user, and, still I seldom post.
Indeed, since years, now, I dabble in pokemon emerald modding,
with a silver lining project "Pokemon metalternate", whose overall end-purpose is to
"offer an elseworld full-experience of the gen2 games, with the gen3 engine perks."

My way to mod being a "functionality first, content second",
I don't actually have proper playable versions to regularly share.

Still, I make an effort to log and redact guides to my major mod discoveries and code,
so that they can be repurposed and used freely by everyone else.


To reproduce a "full-on gen2 experience"
naturally recall having day periods, both providing a graphical shift in mood
(between "morning" and "night" with regard to the default "daytime")
as well as proposing to have variations on catchable mon tables between these periods.

Hence, I set-up to explore the range of
"what could be possible, fitting and interesting, in these regard"
both for my own project and in general.

For starters :
To keep in line with my general ethos of
I only use the proper in-game internal clock and routines as building blocks.
(So, no DNS routines, shoehorning external clock/calendars,
through fixed-address ram insert, which also eventually induce issues,
with how emerald protect its ram from external manipulations.)

Thus, I explored the perspective of finding and editing the routine
handling the choosing of the wild pokemon to battle, in grass.

Regarding having variable mons to battle, depending on day periods,
I have seen several gameplay paradigm proposed around the general mod communities.
(The most recursing of which seeming to be :
divide the wild-mons tables into 3 and each period use one of these third.)

...

Eventually, this "default divide" gameplay design choice
seemed both inefficient and frustrating, to me,
as we loose one perk of not altering the base gameplay paradigm of Emerald :
All pokemons are always available, at any time of the day.
!
As such, I concluded that the best, both coding and design wise would be to :
"apply circular permutations on the mon-slots odds, according the periods of the day."

so, the edited code would have to be optimised and adapted to :
i) Include a code-part to check on the time of the day
and define a starting point, according to it.
ii) Replace "assigning results" to the selected slot register,
with "adding the result" to the same register.

Additionally, if one starts having the period of the day impact the game, globally,
having if communicated to the player, in a similar way to gen2 seems a given.


Thus, instead of tinkering with palettes or whatnot,
I elected to use the extensive weather system of gen3 :
With the paradigm of :
When it is morning or night, the default weather 0x2
is replaced by visually fitting -and mostly cosmetic- weathers.
(0xa -light_fog- or 0xb -cloudy-, respectively.)




What the proposed code does and how to insert it :
The code actually meshes two sub-codes uses,
combined in order to fit where the original code for the "ChooseWildMonIndex_Land" was.

nota/disclaimer
that I couldn't compress the code more,
and, unfortunately, it overwrite two 00 00 at the end,
that are very probably padding to the next routine.
Yet I cannot be fully certain,
so if it turns out that it is not,
do not hesitate to revert back to the original code,
provided at the end of this post.​

1) The main part provides a rewritten routine to choose the wild pokemon to battle, where,
by day (from 10h00 to 17h59) it is virtually identical to the original ;
by night (from 18h00 to 3h59) the role of each index slot, is circularly permuted by +8 ;
in the morning (from 4h00 to 9h59) the role of each index slot, is circularly permuted by +4

where "circularly permuted by +N" means :
N is added to the default value and, then,
if the result is greater or equal to 12 (i.e total numbers of slots)
then, 12 is subtracted to the end-result.

Thus, for instance, by night :
The slot-indexes 0x8 to 0xb have the respective odds of the default slots 0x0 to 0x3
while the slots 0x0 to 0x7, then, have the respective odds, of the default slots 0x4 to 0xb​

2) The secondary part of the code provides an alternate routine,
for when a map has its weather set to the default exterior weather 0x2 :
it checks the current period of the day, the same as for the main wild mon related part,
and, then, depending, it sets the value to return for the weather,
to be either 0x2, by day ; 0xa, in the morning ; 0xb, by night.




code to insert at address 0xb4ac8
00 B5 7A F7 5D FD 2A 48 81 78 BA F7 7B FD 04 39
0E 29 0D D2 06 29 05 D3 64 21 33 F2 7D F8 01 1C
00 20 0A E0 64 21 33 F2 77 F8 01 1C 04 20 04 E0
64 21 33 F2 71 F8 01 1C 08 20 13 29 28 D3 27 29
01 D8 01 30 24 E0 31 29 01 D8 02 30 20 E0 3B 29
01 D8 03 30 1C E0 45 29 01 D8 04 30 18 E0 4F 29
01 D8 05 30 14 E0 54 29 01 D8 06 30 10 E0 59 29
01 D8 07 30 0C E0 5D 29 01 D8 08 30 08 E0 61 29
01 D8 09 30 04 E0 62 29 01 D0 0B 30 00 E0 0A 30
0B 28 00 D9 0B 38 02 BC 08 47 7A F7 11 FD 04 48
80 78 04 38 0E 28 07 D2 06 28 03 D3 02 20 F2 E7
F8 5C 00 03 0A 20 EE E7 0B 20 EC E7

(pointer) to change at address 0xaef08
62 4b 0b 08

as 0x0b4ac8 + 0x9a = 0xb4b62
which is the entry point address for the new (secondary sub-)code handling
the 0x2 weather effect, swapped for 0xa and 0xb, at morning and night respectively.


customising the behaviour for this code :
a)The values in italics are used to control the turning hours of the periods :
04 :: the starting hour of the morning \\b\; and of the whole morning-day-night cycle
0E :: (=14=18-4) :: the -relative- starting hour for the nightime period
06 :: (=10-4) :: the -relative- starting hour for the daytime (default) period
b) The values in bold are used to define the shifting of the circular permutations
00 :: the index shift for the daytime period
(here, set to +0, to align daytime with the default table)
04 :: the index shift for the morning daytime period
08 :: the index shift for the nightime period




push {lr}
.word 0xfD5dF77a
ldr r0, .L1754
ldrb r1, [r0, #0x2]
.word 0xfd7bf7ba
sub r1, r1, #0x4
cmp r1, #0xe
bhs .LCB16207
cmp r1, #0x6
blo .LCB16206
mov r1, #0x64
.word 0xf87df233
mov r1, r0
mov r0, #0x0
b .LCB16208
.LCB16206:
mov r1, #0x64
.word 0xf877f233
mov r1, r0
mov r0, #0x4
b .LCB16208
.LCB16207:
mov r1, #0x64
.word 0xf871f233
mov r1, r0
mov r0, #0x8
.LCB16208:

cmp r1, #0x13
.LM60:
blo .L75
.L53:
.LM61:

cmp r1, #0x27
bhi .L55 @cond_branch
.LM62:

add r0, r0, #0x1
b .L75
.L55:
.LM63:

cmp r1, #0x31
bhi .L57 @cond_branch
.LM64:

add r0, r0, #0x2
b .L75
.L57:
.LM65:

cmp r1, #0x3b
bhi .L59 @cond_branch
.LM66:

add r0, r0, #0x3
b .L75
.L59:
.LM67:

cmp r1, #0x45
bhi .L61 @cond_branch
.LM68:

add r0, r0, #0x4
b .L75
.L61:
.LM69:

cmp r1, #0x4f
bhi .L63 @cond_branch
.LM70:

add r0, r0, #0x5
b .L75
.L63:
.LM71:

cmp r1, #0x54
bhi .L65 @cond_branch
.LM72:

add r0, r0, #0x6
b .L75
.L65:
.LM73:

cmp r1, #0x59
bhi .L67 @cond_branch
.LM74:

add r0, r0, #0x7
b .L75
.L67:
.LM75:

cmp r1, #0x5d
bhi .L69 @cond_branch
.LM76:

add r0, r0, #0x8
b .L75
.L69:
.LM77:

cmp r1, #0x61
bhi .L71 @cond_branch
.LM78:

add r0, r0, #0x9
b .L75
.L71:
.LM79:

cmp r1, #0x62
beq .L73 @cond_branch
.LM80:

add r0, r0, #0xb
b .L75
.L73:
.LM81:

add r0, r0, #0xa
.L75:
cmp r0, #0xb
bls .L890
.L76:
sub r0, r0, #0xb
.L890:

pop {r1}
bx r1


.word 0xfd11f77a
ldr r0, .L1754
ldrb r0, [r0, #0x2]
sub r0, r0, #0x4
cmp r0, #0xe
bhs .LM1122
cmp r0, #0x6
blo .LM1119
mov r0, #0x2
b .L890
.L1755:
.align 2, 0
.L1754:
.word 0x03005CF8

.LM1119:

mov r0, #0xa
b .L890
.LM1122:

mov r0, #0xb

b .L890

the asm bl command :
It is conceptually a asm "call" command (i.e. go to code and resume afterward)
whose parameter is the (signed) offset to the address of the called code,
relative (max +-0x40 000) to the end address of the calling bl instruction.

As such, when writing this isolated code,
I had to compile it with placeholder offsets,
and then replace them with recalculated offsets,
based on the projected actual address of the compiled code, once inserted in rom.

This is why such bl instruction are replaced with hardcoded .word
for the compiler to provide a compiled instruction corresponding to the actual offset.

mini course :
"bl to relative_offset XYZ UVW" is compiled to (2 complementary instructions) :
YZ FX vw Ft
where uvw = UVW/2 and t=u+8

Also, as words are compiled, in reversed order :
"bl to relative_offset XYZ UVW"
will have been written, here, as
".word 0xFtvwFXYZ"

As such,
a) ".word 0xfD5dF77a" and ".word 0xfd11f77a"
are instances of "bl RtcCalcLocalTime", adjusted for compiled addresses,
"RtcCalcLocalTime" being at 0x2f 588

b) ". word 0xfd7bf7ba" is an instance of "bl Random"
("Random" would, thus, be at 0x6F5CE)

c) ".word 0xf87df233", ".word 0xf877f233" and ".word 0xf871f233"
are instances of "bl __umodsi3", adjusted for compiled addresses,
("bl __umodsi3" would, thus, be at 0x2e7be0)


Now :
".word 0x03005CF8" would not denote an instance of transcribed bl,
but a reference address (in ram) to extract the calculated in-game-clock time from.
(Also, note that, here, only one instance of such .word
is shared by the two combined code snippet, to be used in two ldr instructions,
in order to shave-off the compiled code to the most possible.)



Reverting to the original code :

1) re-paste over, the following, at 0xb4ac8 :
00 B5 BA F7 7F FD 00 04 00 0C 64 21 33 F2 84 F8
00 06 01 0E 13 29 01 D8 00 20 4C E0 08 1C 14 38
00 06 00 0E 13 28 01 D8 01 20 44 E0 08 1C 28 38
00 06 00 0E 09 28 01 D8 02 20 3C E0 08 1C 32 38
00 06 00 0E 09 28 01 D8 03 20 34 E0 08 1C 3C 38
00 06 00 0E 09 28 01 D8 04 20 2C E0 08 1C 46 38
00 06 00 0E 09 28 01 D8 05 20 24 E0 08 1C 50 38
00 06 00 0E 04 28 01 D8 06 20 1C E0 08 1C 55 38
00 06 00 0E 04 28 01 D8 07 20 14 E0 08 1C 5A 38
00 06 00 0E 03 28 01 D8 08 20 0C E0 08 1C 5E 38
00 06 00 0E 03 28 01 D8 09 20 04 E0 62 29 01 D0
0B 20 00 E0 0A 20 02 BC 08 47 00 00

2) revert the pointer at 0xaef08 to :
5C EF 0A 08



Ultimately, this post stands a bit short of a full-on mastered tutorial,
yet is quite more than a very advanced investigation.
Thus, I have decided to put it in the tutorial section, with the asterisk that :
"Some corner cases may not have been considered still."

And, frankly, a main motivating factor is that :
The proposed code can be quite easily patched-in and out of any mod-project,
as well as be adjusted quite finely,
both in terms of the turning hours for each period,
as well as in terms of which shifts to apply for each periods.

For all that, I concluded that it was best to share my findings and code the earliest,
for everyone to be able to take it, and build upon it, the earliest.​


Thanks for reading and feel free to play with and adjust this proposed base code.
Disfrute!
 

aptitud

Usuario mítico
Update :

Context :

As I had devised a way to, quite accurately, transcribe the day-period,
through having the default outdoor weather (0x2),
shift to either of two weather (0xa and 0xb) in the morning and night,
I decided to switch code philosophy and, instead of it being and extra graphic clue,
have the weather be the actual parameter checked to define the wild_poké_slots shift.


Hence :

A revised and more proper proposal for the code in the former post :

00 B5 BA F7 7F FD 64 21 33 F2 86 F8 01 1C 07 48
00 68 2E 30 00 78 09 28 0A D0 0A 28 0A D0 0B 28
0A D0 04 28 08 D0 00 20 07 E0 00 00 8C 5D 00 03
06 20 02 E0 04 20 00 E0 08 20 13 29 28 D3 27 29
01 D8 01 30 24 E0 31 29 01 D8 02 30 20 E0 3B 29
01 D8 03 30 1C E0 45 29 01 D8 04 30 18 E0 4F 29
01 D8 05 30 14 E0 54 29 01 D8 06 30 10 E0 59 29
01 D8 07 30 0C E0 5D 29 01 D8 08 30 08 E0 61 29
01 D8 09 30 04 E0 62 29 01 D0 0B 30 00 E0 0A 30
0B 28 00 D9 0B 38 02 BC 08 47 7A F7 11 FD 04 48
80 78 04 38 0E 28 07 D2 06 28 03 D3 02 20 F2 E7
F8 5C 00 03 0A 20 EE E7 0B 20 EC E7


push {lr}

.word 0xfd7ff7ba
mov r1, #0x64
.word 0xf886f233
mov r1, r0

ldr r0, .L843
ldr r0, [r0]
add r0, r0, #0x2e
ldrb r0, [r0]
cmp r0, #0x9
beq .LCB20000
cmp r0, #0xa
beq .LCB16206
cmp r0, #0xb
beq .LCB16207
cmp r0, #0x4
beq .LCB16207



mov r0, #0x0
b .LCB16208

.align 2, 0
.L843:
.word 0x03005d8c

.LCB20000:
mov r0, #0x6
b .LCB16208
.LCB16206:
mov r0, #0x4
b .LCB16208
.LCB16207:
mov r0, #0x8
.LCB16208:

cmp r1, #0x13
.LM60:
blo .L75
.L53:
.LM61:

cmp r1, #0x27
bhi .L55 @cond_branch
.LM62:

add r0, r0, #0x1
b .L75
.L55:
.LM63:

cmp r1, #0x31
bhi .L57 @cond_branch
.LM64:

add r0, r0, #0x2
b .L75
.L57:
.LM65:

cmp r1, #0x3b
bhi .L59 @cond_branch
.LM66:

add r0, r0, #0x3
b .L75
.L59:
.LM67:

cmp r1, #0x45
bhi .L61 @cond_branch
.LM68:

add r0, r0, #0x4
b .L75
.L61:
.LM69:

cmp r1, #0x4f
bhi .L63 @cond_branch
.LM70:

add r0, r0, #0x5
b .L75
.L63:
.LM71:

cmp r1, #0x54
bhi .L65 @cond_branch
.LM72:

add r0, r0, #0x6
b .L75
.L65:
.LM73:

cmp r1, #0x59
bhi .L67 @cond_branch
.LM74:

add r0, r0, #0x7
b .L75
.L67:
.LM75:

cmp r1, #0x5d
bhi .L69 @cond_branch
.LM76:

add r0, r0, #0x8
b .L75
.L69:
.LM77:

cmp r1, #0x61
bhi .L71 @cond_branch
.LM78:

add r0, r0, #0x9
b .L75
.L71:
.LM79:

cmp r1, #0x62
beq .L73 @cond_branch
.LM80:

add r0, r0, #0xb
b .L75
.L73:
.LM81:

add r0, r0, #0xa
.L75:
cmp r0, #0xb
bls .L890
.L76:
sub r0, r0, #0xb
.L890:

pop {r1}
bx r1



.word 0xfd11f77a
ldr r0, .L1754
ldrb r0, [r0, #0x2]
sub r0, r0, #0x4
cmp r0, #0xe
bhs .LM1122
cmp r0, #0x6
blo .LM1119
mov r0, #0x2
b .L890
.L1755:
.align 2, 0
.L1754:
.word 0x03005CF8

.LM1119:

mov r0, #0xa
b .L890
.LM1122:

mov r0, #0xb

b .L890


In practice :

Basically, only the beginning is edited, compared to the initial proposal,
in order to, as stated in the introduction,
instead of brute-force checking against the time of the day,
checking against the current weather.
And, with the code, still here at the end, unchanged,
still called by the code setting the weather 0x2,
through the same pointer change than last time,
we keep switching the regular weather, depending on the time of the day.

In the end :
a) It effectively do the same as the previous code in the cases that mattered for me.

b) Now, for the better, not all main_wild_pokémons tables are affected,
only if the map is under the chosen weather conditions and with a direct association.

Which mean, mainly, that :
grotto wild pokémons now work again as they should do ;
and given weather do not normally update without changing maps,
the only difference with gen 2 is that the shift in wild pokémons,
only change with changing maps, under normal circumstances.

-which is not a bad thing per se, so it's quite fine with me-

Anyway (for your interest) :
Here are the instructions to return the current weather value in r0 :
ldr r0, .L843
ldr r0, [r0]
add r0, r0, #0x2e
ldrb r0, [r0]

.align 2, 0
.L843:
.word 0x03005d8c

->this code can be used as-it-is, in any code you write,
the only condition being to keep the used/added ".word 0x03005d8c" address,
in the valid range, for the ldr instruction.



And, finally, with the freed code space, I could thus add a full weather+shift to handle,
as well as an additional weather to point to one of the now 3 possible shifts.

Thus here are the handled cases and pokémon slot shifts, in code order :
weather 0x9 ("diagonal fog") :
-> shift index 0x6 slots
weather 0xa ("morning") :
-> shift index 0x4 slots
weather 0xb ("night" ) :
-> shift index 0x8 slots
weather 0x4 (snowflakes then visually like weather 0xb) :
-> points the same shift as weather 0xb


Adding an additional, seemingly unused fog with its own "half table shift",
was motivated by the wish to keep a viable option
to let (some choice) caves/grottoes still have a weather-based wild_poké_shift.
(My take would be to base it around the tides system of the game,
with the weather being changed through map script instead of through a core code.)


Now, the 0x4 weather also seems unused, yet its end-filter is the same as 0xb,
so, for clarity, I have used-up the remaining code space,
to link weather 0x4 to the same shift as weather 0xb.


And, that is it :
Feel free to use this code if you like what it offers,
just paste it over, at the same address 0xb4ac8
as the previous code proposal.
(it takes exactly the same space,
and, again, the code shifting the weather with the time of the day, at the end,
stays unchanged and at the same address 0xb4b62,
thus the pointer to be edited at address 0xaef08 should, again be 62 4b 0b 08.)



Thanks for the read,
 
Arriba