@ Feebas Routine
@ by ShantyTown
@
@ You'll need to do 2 steps to insert this Feebas routine into your ROM.
@ 1. Compile and insert this file into the ROM, and remember its ROM address.
@ 2. Overwrite the 8 bytes at address 0x82b64 with these 8 bytes:
@ 00 4A 97 46 XX XX XX XX
@ (Replace "XX XX XX XX" with the ROM address of the routine you inserted in the first step.)
@
@ For example, if you inserted it at ROM address 0x800480, it would look like this:
@ 00 4A 97 46 80 04 80 08
@
@ This routine enables 1-8 fishing tiles in a single map to contain Feebas. (Defaults to 6 tiles)
@ To customize this for your ROM, make changes to the constants at the bottom of this file:
@ You must define a rectanglular sub-area of the map where Feebas can appear.
@ For example, for the pond in Celadon City, we would define the sides of the rectangular area as
@ Left side of rectangle = 0x18
@ Right side of rectangle = 0x1d
@ Top side of rectangle = 0x15
@ Bottom side of rectangle = 0x17
@
@ NOTE: If the rectangle's area is larger than 255 tiles, then some of the bottom tiles won't be able to contin Feebas.
@
.text
.align 2
.thumb
.thumb_func
main:
@ Push registers that were pushed in the original routine.
push {r4-r6, lr}
@ Check if current map is Feebas's map.
ldr r3, =(0x2031DBC) @ Address of current map id
ldrh r3, [r3] @ r3 = current map id
ldr r2, =(MAP_ID) @ r2 = Map Id for Feebas tiles
cmp r3, r2 @ compare the current map id with Feebas's map id
bne done @ jump to "done" if they weren't equal
push {r0-r2}
@ Get the player's tile coordinates.
ldr r0, =(0x3005008)
ldr r2, [r0] @ r2 = pointer to player's X/Y coordinates
ldrh r0, [r2] @ r0 = player's X coordinate
ldrh r1, [r2, #0x02] @ r1 = player's Y coordinate
@ Get the player's facing direction.
ldr r2, =(0x2036e58)
ldrb r2, [r2] @ r2 = player's facing direction (1 = down, 2 = up, 3 = left, 4 = right)
@ Get the fishing tile coordinates by getting the tile coordinates in front of the player.
cmp r2, #0x1
beq facingDown
cmp r2, #0x2
beq facingUp
cmp r2, #0x3
beq facingLeft
b facingRight
gotFishingTileCoords:
@ At this point, r0 = X, r1 = Y
bl checkFeebasTile
cmp r0, #0x0 @ If r0 != 0, then it's a Feebas tile
beq notFeebas
@ Set encounter rate.
ldr r0, .genRandom
bl linker
mov r1, #0xff
and r0, r1
cmp r0, #ENCOUNTER_RATE
bgt notFeebas
pop {r5-r7} @ pop off garbage to get the stack in a good state
@ Load Feebas id and level.
ldr r0, =(POKEMON_ID) @ Load Feebas id into r0
ldr r1, =(POKEMON_LEVEL) @ Load level 20 into r1
ldr r2, =(0x0) @ Load the fishing data slot(???) into r2
@ Jump back to original routine where it has calculated the pokemon species and level.
ldr r3, =0x08082b8e
mov pc, r3
facingDown:
@ Add 1 to the player's y coordinate.
add r1, #0x1
b gotFishingTileCoords
facingUp:
@ Subtract 1 from the player's y coordinate.
sub r1, #0x1
b gotFishingTileCoords
facingLeft:
@ Subtract 1 from the player's x coordinate.
sub r0, #0x1
b gotFishingTileCoords
facingRight:
@ Add 1 to the player's x coordinate.
add r0, #0x1
b gotFishingTileCoords
checkFeebasTile:
@ Check if the given tile coordinates are a Feebas tile
@ r0 = X, r1 = Y
@ return: r0 != 0x0 if it's a Feebas tile
@
@ Algorithm:
@ Define a rectangular area with width w and height h
@ This area is an array of length w * h
@
@ Check if the fishing tile is inside the rectangle. Else return.
@ Convert the fishing tile coordinates to an index within this array.
@ Take the secret id/trainer id and combine nybbles to form 8-bit values by sliding to the left.
@ For each of those values 'v':
@ If v % (w * h) == array index:
@ It's a Feebas tile
@
push {lr}
@ Is the fishing tile in the Feebas rectangle?
cmp r0, #RECT_LEFT
bmi notFeebasTile
cmp r0, #RECT_RIGHT
bgt notFeebasTile
cmp r1, #RECT_TOP
bmi notFeebasTile
cmp r1, #RECT_BOTTOM
bgt notFeebasTile
@ Convert tile coordinates to the array index.
sub r0, r0, #RECT_LEFT
sub r1, r1, #RECT_TOP
mov r2, #(RECT_RIGHT - RECT_LEFT + 1) @ width of rectangle
mul r1, r1, r2
add r0, r0, r1 @ r0 = array index for rectangular area
ldr r1, =(0x0300500C)
ldr r1, [r1]
add r1, r1, #0xa
ldrh r5, [r1] @ Need to load trainer id and secret id separately because they aren't word-aligned
mov r6, r5
add r1, r1, #0x2
ldrh r6, [r1]
lsl r5, r5, #0x10
add r1, r5, r6 @ Load the secret id and trainer id into r1
mov r2, #((RECT_RIGHT - RECT_LEFT + 1) * (RECT_BOTTOM - RECT_TOP + 1)) @ Rectangle's area
mov r3, #NUM_TILES
mov r5, #0xff
checkSlot:
sub r3, r3, #0x1 @ Decrement the counter
bmi notFeebasTile @ Return if the counter hit zero.
mov r4, r1
and r4, r5 @ Load lower 8 bits of r1 into r4.
lsr r1, r1, #0x4 @ Shift r1 4 bits to the right, so a new 8-bit value will be used
@ in the next loop iteration.
modulus:
@ Perform r4 % r2
sub r4, r4, r2
bmi modulusDone
b modulus
modulusDone:
add r4, r4, r2 @ Make the result positive.
cmp r4, r0
bne checkSlot
exitCheckFeebasTile:
ldr r0, =(0x1)
pop {pc}
notFeebasTile:
ldr r0, =(0x0)
pop {pc}
notFeebas:
pop {r0-r2}
done:
@ Code that was replaced by the hook in the original routine
mov r6, r0
lsl r0, r1, #0x18
lsr r0, r0, #0x18
@ Jump back to the original routine after the hook
ldr r2, =0x08082b6c
mov pc, r2
linker:
bx r0
.align 2
.genRandom:
.word 0x08044ec8 + 1
@ Customizable Constants
.set MAP_ID, 0x0603 @ [ID][Bank] (Default CELADON CITY)
.set RECT_LEFT, 0x18 @ X coordinate of left side of rectangle
.set RECT_RIGHT, 0x1d @ X coordinate of right side of rectangle
.set RECT_TOP, 0x15 @ Y coordinate of top side of rectangle
.set RECT_BOTTOM, 0x17 @ Y coordinate of bottom side of rectangle
.set NUM_TILES, 0x06 @ Number of Feebas tiles (must be between 1 and 8)
.set POKEMON_ID, 0x148 @ Pokemon Id (Feebas)
.set POKEMON_LEVEL, 0x0f @ Wild Pokemon level (15)
.set ENCOUNTER_RATE, 0xc0 @ This number is divided by 0xFF to get the encounter rate (0xc0 / 0xff = 75% encounter rate)