- 82
- Posts
- 7
- Years
- California
- Seen May 20, 2020
Berry System in FireRed! Now with RTC
Technical Information:
System Mechanics:
The system works identical to that of DPPT. It uses DNS's RTC but should work fine with SQ's RTC.
The beauty of the system is that it uses only temporary variables and derives their values from the RAM. Meaning you can go ahead and use those variables for anything, without having to save them for just the tree. Incase you are interested here's how the data is stored and the variable's significance:
System Algorithm:
The current system has 5 phases, each phase is 12 hours; feel free to change this to whatever you want. You can Increase or decrease this time by going to the "getTreeData" routine, finding my comment in all caps and changing the constant. There is also another "Change" label. So if more than 3 days has passed, it will automatically set the stage to 5(picking berries). If you change the phases to 24 hrs, this needs to be changed. Make sure to add 1 for safety reasons. I discovered if I had it set to 2 and you planted in the evening of the 10th and you came back the morning of the 12th, it would go into stage 5 when it should be 4. But right now its set to the same as DPPT so I see no reason to change it.
Link to DPPT berry system: https://bulbapedia.bulbagarden.net/wiki/Berry#Formula
Tutorial:
First of all, before you implement my system you need to insert:
JPAN's save block hack: https://www.pokecommunity.com/posts/6993092/
You will then need to install some RTC. Note: If you use DNS, go to the second to last page of the thread and get endrifts patch. PATCH THE DNS PROGRAM NOT YOUR ROM. This patch fixes dns compatibility with mgba. If you don't do this, dns will only work on vba and not no$gba or Mgba. If you use sq's rtc, it will only work on VBA. These are bugs with rtc and emulators, not my system.
Insert the following routines into freespace.
bagHack:
getTreeData:
.text
.align 2
.thumb
.thumb_func
Main: @loads tree data in table and var 8005
Push {r0-r4, lr}
Ldr r0, .var_800F @Level script bypass check
Ldrb r0, [r0]
cmp r0, #0x7E @Checks if baghack is active
beq end
ldr r0, =(0x20370BC)@get tree data
ldrh r0, [r0]
ldr r1, .SaveBlockLocation
mov r2, #0x6
mul r0, r0, r2
add r0, r0, r1 @r0 contains offset to tree data
Mov r3, r0 @moves offset of tree data to r3
Ldrb r0, [r0] @loads stage/water byte
ldr r1, .var_8005 @loads values in vars
@r0 contains tree stage, watered and amount watered byte
@r3 contains offset to treedata
DecryptBitShifts: @stage decryption
Lsr r2, r0, #0x5 @puts stage value into r2
Strh r2, [r1] @stores stage into 8005
Add r1, r1, #0x2 @moves to var 8006
@water amount decryption
Lsl r2, r0, #0x1B @put water amount to top bits
Lsr r2, r2, #0x1C @water amount lowest bits
Strh r2, [r1] @stores water amount into 8006
Add r1, r1, #0x2 @moves to var 8007
@watered this stage
Lsl r2, r0, #0x1F @raise to top bits
Lsr r2, r2, #0x1F @lower to bottom bits
Strh r2, [r1] @stores byte into 8007
Add r1, r1, #0x2 @moves r1 to var 8008
Add r3, r3, #0x1 @moves r3 to berry I'd
@store berry id in 8008
Ldrb r0, [r3] @load berry id from ram
Strh r0, [r1] @stores byte into 8008
Add r1, r1, #0x2 @moves r1 to 8009
Add r3, r3, #0x1 @moves r3 to time planted
@store time planted into 8009
ldrb r0, [r3] @load first byte if time planted
Strh r0, [r1] @stores halfword in 8009
add r1, r1, #0x2 @moves r1 to 800A
add r3, r3, #0x1 @moves r3 to month planted
@store month into 800A
ldrb r0, [r3] @loads month
Strh r0, [r1] @stores halfword in 8009
add r1, r1, #0x2 @moves r1 to 800B
add r3, r3, #0x1 @moves r3 to day planted
@stores day into 800B
ldrb r0, [r3]
strh r0, [r1] @stores second byte time planted
CheckForZero: @checks if stage is zero
ldr r1, .var_8005
ldrh r1, [R1]
cmp r1, #0x0
beq end @skip time check if stage is 0
CheckMonthDay: @check time passed #check for demcember
ldr r0, .Clock
ldrb r3, [r0, #0x3] @Holds current month
Ldr r1, .var_800A @loads month planted
Ldrh r1, [r1]
Mov r2, r1
cmp r1, r3 @checks for month switch
bne MonthSwitched
b CheckDay
MonthSwitched:
@CHECK FOR HOW MANY DAYS
sub r1, r3, r1 @minus current from month planted
cmp r1, #0x1 @is month change larger than 1
beq OneMonthSwitch
add r1, r1, #0x0C @checking for dec to jan
cmp r1, #0x1
beq OneMonthSwitch
B SetStageTo5
OneMonthSwitch:
Ldrb r0, [r0, #0x4] @holds current day
Ldr r2, .var_800B @loads day planted offset
ldrh r2, [r2] @loads day planted
Sub r0, r0, r2 @minus current from planted day
Add r0, r0, #0x1E @add 30 for month switch
B CheckDayBranch
CheckDay:
Ldrb r0, [r0, #0x4] @holds current day
Ldr r2, .var_800B @loads day planted
ldrh r2, [r2] @loads day planted
Sub r0, r0, r2 @minus current from planted day
CheckDayBranch:
Cmp r0, #0x3 @CHANGE IF YOU WANT A DIFFERENT AMOUNT OF DAYS REFERENCE #001 (In explaination on PC)
Bge SetStageTo5 @if more than 3 days, set stage to 5
@day change can only be 1 or 2
CheckHoursPassed: @figure out how much time in hours has passed - you can use all regs
@check time passed and update
ldr r1, .Clock
ldrb r2, [r1, #0x6] @hours
ldr r3, .var_8009 @Loads var
ldrb r3, [r3]
sub r2, r2, r3 @Minus planted time from current
Push {r3}
mov r3, #0x18
Mul r0, r0, r3 @turn days into hours
pop {r3}
Add r2, r2, r0 @How many hours has passed
BeginLoop:
mov r3, #0x0
mov r1, #0x0C @CHANGE THIS TO CHANGE TIME BETWEEN PHASES
mov r0, r1
loop: @figuring out which stage youre in
cmp r2, r1
blo addToStage
add r1, r1, r0 @CHANGE THIS TO CHANGE TIME BETWEEN PHASES
add r3, r3, #0x1
cmp r3, #0x5
bge SetStageTo5 @Still Need to test
b loop
SetStageTo5:
ldr r2, .var_8005 @Stores stage in 8005
mov r3, #0x5
strh r3, [r2]
b ResetWatering
addToStage:
add r3, r3, #0x1
ldr r2, .var_8005 @Stores stage in 8005
ldrh r1, [r2]
cmp r1, r3
beq end
strb r3, [r2]
ResetWatering:
ldr r2, .var_8007 @Stores 0 in 8007 to reset Watered Already
mov r3, #0x0
strh r3, [r2]
end:
pop {r0-r4, pc}
.align 2
.SaveBlockLocation: .word 0x203C000
.var_8005: .word 0x020370C2
.var_8007: .word 0x020370C6
.var_8009: .word 0x020370CA
.var_800A: .word 0x020370CC
.var_800B: .word 0x020370CE
.var_800F: .word 0x020370D2
.Clock: .word 0x0300553C
Compiled:
setTreeData:
.text
.align 2
.thumb
.thumb_func
Main: @Sets tree data in table
Push {r0-r4, lr}
Ldr r0, .var_800F @Level script bypass check
Ldrb r0, [r0]
cmp r0, #0x7E @Checks if baghack is active
beq End
ldr r0, =(0x20370BC)@get tree data
ldrh r0, [r0]
ldr r1, .SaveBlockLocation
mov r2, #0x6
mul r0, r0, r2
add r4, r0, r1 @r4 contains offset to tree data
StageByteStorage:
ldr r1, .var_8005 @loads offsets to vars
ldr r2, .var_8006
ldr r3, .var_8007
Ldrb r1, [r1] @loads vars bytes
Ldrb r2, [r2]
Ldrb r3, [r3]
Placebits:
lsl r1, r1, #0x5 @put stage to top 3 bits
lsl r2, r2, #0x1 @put watered amount to 4th bit
orr r1, r1, r2 @combine stage and watered this stage
orr r1, r1, r3 @combine ^ with watered amount
strb r1, [r4] @stores mashed byte in first slot
add r4, r4, #0x1 @moves to next slot in ram
@store berry id in 8008
Ldr r1, .var_8008
Ldrb r0, [r1] @load berry id from var 8008
Strb r0, [r4] @stores byte into ram
Add r1, r1, #0x2 @moves r1 to 8009
Add r4, r4, #0x1 @moves r3 to time planted
@store time planted into 8009
ldrb r0, [r1] @load first byte if time planted
Strb r0, [r4]
add r1, r1, #0x2 @moves r1 to 800A
add r4, r4, #0x1 @moves r3 to month planted
@store month into 800A
ldrb r0, [r1] @loads month
Strb r0, [r4]
add r1, r1, #0x2 @moves r1 to 800B
add r4, r4, #0x1 @moves r3 to day planted
@stores day into 800B
ldrb r0, [r1]
strb r0, [r4]
End:
Pop {r0-r4, pc}
.align 2
.SaveBlockLocation: .word 0x203C000
.var_8005: .word 0x020370C2
.var_8006: .word 0x020370C4
.var_8007: .word 0x020370C6
.var_8008: .word 0x020370C8
.var_8009: .word 0x020370CA
.var_800A: .word 0x020370CC
.var_800B: .word 0x020370CE
.var_800F: .word 0x020370D2
Compiled:
getYeild:
@((max - min) x (water - 1) + rand) / 4 + min
Main:
Push {r0-r4, lr}
bl RandomBL
mov r3, r0 @put random value in r3 for later
ldr r0, .var_8000
ldr r1, .var_8001
ldr r2, .var_8007
ldrh r0, [r0] @Load max
ldrh r1, [r1] @Load min
ldrh r2, [r2] @load water amount
Equation:
sub r1, r0, r1 @Gap between max and min in r1
cmp r2, #0x0
beq Skip
sub r2, r2, #0x1 @water minus 1
Skip:
mov r4, r1
mul r4, r4, r2
ModularDivide:
mov r0, r3 @put random value in r0
bl UmodsiBL @modular divsion RAND
add r0, r0, r4
mov r1, #0x4
bl DivideBL
ldr r1, .var_8001
ldrh r1, [r1] @Load min
add r0, r0, r1
Store:
ldr r1, .var_800D
strh r0, [r1]
End:
Pop {r0-r4, pc}
RandomBL:
ldr r2, .Random
bx r2
UmodsiBL:
ldr r2, .Umodsi
bx r2
DivideBL:
ldr r2, .Divide
bx r2
.align 2
.var_8000: .word 0x020370B8
.var_8001: .word 0x020370BA
.var_8007: .word 0x020370C6
.var_800D: .word 0x020370D0
.Random: .word 0x08044EC9
.Umodsi: .word 0x081E4685
.Divide: .word 0x081E4019
Compiled:
setTime:
.align 2
.thumb
.thumb_func
Main:
push {r0-r3, lr}
ldr r0, .Clock
ldr r1, .var_8009
LoadandStore:
@loads and stores hour in 8009
ldrb r2, [r0, #0x6] @Holds current hour
strb r2, [r1]
add r1, r1, #0x2 @moves to var 800a
@loads and stores hour
ldrb r2, [r0, #0x3] @Holds current month
strb r2, [r1]
add r1, r1, #0x2 @moves to var 800b
@loads and stores day
ldrb r2, [r0, #0x4] @Holds current day
strb r2, [r1]
End:
pop {r0-r3, pc}
.align 2
.align 2
.var_8009: .word 0x020370CA
.Clock: .word 0x0300553C
.align 2
.thumb
.thumb_func
Main:
push {r0-r3, lr}
ldr r0, .Clock
ldr r1, .var_8009
LoadandStore:
@loads and stores hour in 8009
ldrb r2, [r0, #0x6] @Holds current hour
strb r2, [r1]
add r1, r1, #0x2 @moves to var 800a
@loads and stores hour
ldrb r2, [r0, #0x3] @Holds current month
strb r2, [r1]
add r1, r1, #0x2 @moves to var 800b
@loads and stores day
ldrb r2, [r0, #0x4] @Holds current day
strb r2, [r1]
End:
pop {r0-r3, pc}
.align 2
.align 2
.var_8009: .word 0x020370CA
.Clock: .word 0x0300553C
Compiled:
Now, once you've compiled those routines into free space you will need to insert their pointers (not in reverse hex) into this script. Remember to add 1 to the pointer.
I've made sure to add comments everywhere there is a callasm command to one of my routines. You'll also notice that the names I call these routines are identical to the names I gave the spoilers. You need to match the call to the pointer to the name of the routine!
XSE script:
#org @HeavyScript
lock
copyvar 0x8002 LASTRESULT
copyvar 0x8011 0x8002
callasm 0x8900031 'GetTreeData
compare 0x8005 0x0
if 0x1 goto @NonePlanted
compare 0x8005 0x1
if 0x1 goto @Fresh
compare 0x8005 0x2
if 0x1 goto @Sprouting
compare 0x8005 0x3
if 0x1 goto @Growing
compare 0x8005 0x4
if 0x1 goto @Blooming
callasm 0x89001B1 'GetYield
buffernumber 0x0 LASTRESULT
bufferitem 0x1 0x8008
additem 0x8008 0x800D
msgbox @Received MSG_NORMAL
setvar 0x8005 0x0
setvar 0x8006 0x0
setvar 0x8007 0x0
setvar 0x8008 0x0
setvar 0x8009 0x0
setvar 0x800A 0x0
setvar 0x800B 0x0
callasm 0x8900131 'SetTreeData
msgbox @Returned MSG_NORMAL
release
end
#org @NonePlanted
msgbox @None MSG_YESNO
compare LASTRESULT 0x0
if 0x1 goto @end
fadescreen 0x1
setvar 0x800F 0x7E
setvar 0x800E 0x0
writebytetooffset 0x0 0x203AD02
callasm 0x810A67D
waitmsg
setvar 0x800F 0x0
compare 0x800E 0x0
if 0x1 goto @end
compare 0x800E 0x84
if 0x3 goto @LaughAtPlayer
compare 0x800E 0xB0
if 0x4 goto @LaughAtPlayer
copyvar 0x8008 0x800E
bufferitem 0x0 0x8008
msgbox @addedberry MSG_NORMAL
setvar 0x8005 0x1
setvar 0x8006 0x0
setvar 0x8007 0x0
copyvar 0x8002 0x8011
callasm 0x8900221 'SetTime
callasm 0x8900131 'SetTreeData
removeitem 0x8008 0x1
release
end
#org @Fresh
bufferitem 0x0 0x8008
msgbox @NewPlant MSG_NORMAL
compare 0x8007 0x0
if 0x3 goto @WaterPlant
msgbox @WateredAlready MSG_NORMAL
release
end
#org @Sprouting
bufferitem 0x0 0x8008
msgbox @Sprouted MSG_NORMAL
compare 0x8007 0x0
if 0x3 goto @WaterPlant
msgbox @WateredAlready MSG_NORMAL
release
end
#org @Growing
bufferitem 0x0 0x8008
msgbox @Tall MSG_NORMAL
compare 0x8007 0x0
if 0x3 goto @WaterPlant
msgbox @WateredAlready MSG_NORMAL
release
end
#org @Blooming
bufferitem 0x0 0x8008
msgbox @Bloom MSG_NORMAL
compare 0x8007 0x0
if 0x3 goto @WaterPlant
msgbox @WateredAlready MSG_NORMAL
release
end
#org @end
release
end
#org @LaughAtPlayer
msgbox @WrongItem MSG_NORMAL
release
end
#org @WaterPlant
checkitem 0x1 0x1 'Checks if you have masterball Change to what your watering item
if 0x0 goto @end
removeitem 0x1 0x1 'Change masterball
msgbox @WantWater MSG_YESNO
compare LASTRESULT 0x0
if 0x1 goto @end
msgbox @FinishedWatering MSG_NORMAL
addvar 0x8006 0x1
addvar 0x8007 0x1
copyvar LASTRESULT 0x8005
callasm 0x8900131 'SetTreeData
release
end
'---------
' Strings
'---------
#org @Received
= [player] found [buffer1] [buffer2]!
#org @Returned
= The soil returned to it's soft and\nloamy state.
#org @None
= The soil is soft here[.]\nPlant a berry?
#org @addedberry
= You planted a [buffer1] here.
#org @NewPlant
= A [buffer1] was planted here.
#org @WateredAlready
= It's been watered already.
#org @Sprouted
= The [buffer1] has sprouted.
#org @Tall
= The [buffer1] plant\nis growing bigger.
#org @Bloom
= The [buffer1] plant is in bloom!
#org @WantWater
= The plant can use some water[.]\nWould you like to water it?
#org @FinishedWatering
= You finished watering!
#org @WrongItem
= Silly child[.]\nYou can't plant that
Usage:
After you've compiled the script, write down the offset of it as well. You will need to remember it.
Here is how you'll be using the script:
Every tree should have a unique Tree ID. So if you want two trees, one can be 0x0, and the other can be 0x1. You can theoretically go up to FFFF or 65,535 trees with the current system but we don't have enough ram for that. I think FF is a fine amount. Set the tree ID to lastresult and then jump the script that handles the complicated stuff.
Getting Sprites to Load:
Everyone can thank DrFuji for this.
Insert this level script into every level you have berry trees on:
#dynamic 0x880000
#org @start // Uses a 0x5 type level script
setvar 0x4011 0x1 // ID of first berry tree on map
setvar 0x8003 0x2 // ID of last berry tree on map
setvar 0x8004 0x10 // Start of the berry tree OWs event numbers in A-Map
goto @Main
#org @Main
copyvar 0x8002 0x4011
callasm 0x8900031 `get tree data
callasm 0x8800131 `set tree data
compare 0x8005 0x0
if 0x1 goto @Nothing
compare 0x8005 0x1
if 0x1 goto @Sprout
compare 0x8005 0x2
if 0x1 goto @Growing
compare 0x8005 0x3
if 0x1 goto @Flowering
spritebehave 0x8004 0x4D // Makes the OW hold the VS seeker animation
goto @Next
#org @Next
comparevars 0x8003 0x4011 // Checks if the current berry tree ID is the last one on the map
if 0x1 goto @Ending
addvar 0x4011 0x1
addvar 0x8004 0x1
goto @Main // Loops back to @Main
#org @Nothing
movesprite2 0x8004 0xFF 0xFF // Moves the OW far offscreen if there is no growth
goto @Next
#org @Sprout
spritebehave 0x8004 0x0 // Makes the OW face down
goto @Next
#org @Growing
spritebehave 0x8004 0x7 // Makes the OW face up
goto @Next
#org @Flowering
spritebehave 0x8004 0x9 // Makes the OW face left
goto @Next
#org @Ending
end
Now once you do that....a tutorial will be put here later!
Notes:
- Watering item is a masterball, change it to your watering item
- Where you see "SAVEBLOCK" in each routine, change that to a spot in ram that your tree data table will be located. Every tree is 6 bytes of ram.
- YOU MUST MOVE BERRIES INTO THE ITEM POUCH
Why?
I tried for a few weeks search for a way to open and close the berry pouch from the Overworld and came up empty handed. I did find 5 different functions to open the pouch but all bugged in one way or another when called via callasm.
Future Updates:
- Move berries bag into berry pouch when/if we find a way to do so
Credits:
Jpan - Save Block Hack
FBI - Making the base of the system
ShinyQuagsire - Bag Hack
Xizqu - Finishing FBI's system
Skeli - Helping me
Akamethebulbasaur - Helping me
DrFuji - Level Script
![[PokeCommunity.com] [Alpha] [FR] Berry System V2 [PokeCommunity.com] [Alpha] [FR] Berry System V2](https://media.giphy.com/media/3GBXIjDhUN7jGHLlUR/giphy.gif)
Technical Information:
Spoiler:
System Mechanics:
The system works identical to that of DPPT. It uses DNS's RTC but should work fine with SQ's RTC.
The beauty of the system is that it uses only temporary variables and derives their values from the RAM. Meaning you can go ahead and use those variables for anything, without having to save them for just the tree. Incase you are interested here's how the data is stored and the variable's significance:
Code:
General variables:
Var 8005 - Stage
Var 8006 - Watered amount
Var 8007 - Watered this stage
Var 8008 - Berry id
Var 8009 - Time planted
Var 800A - Month Planted
Var 800B - Day Planted
RAM Data storage:
[Current Stage, Watered Amount, Watered this Stage: 1 byte]
[Berry ID: 1 byte]
[Hour Planted: 1 byte]
[Month Planted: 1 byte]
[Day Planted: 1 bytes]
System Algorithm:
The current system has 5 phases, each phase is 12 hours; feel free to change this to whatever you want. You can Increase or decrease this time by going to the "getTreeData" routine, finding my comment in all caps and changing the constant. There is also another "Change" label. So if more than 3 days has passed, it will automatically set the stage to 5(picking berries). If you change the phases to 24 hrs, this needs to be changed. Make sure to add 1 for safety reasons. I discovered if I had it set to 2 and you planted in the evening of the 10th and you came back the morning of the 12th, it would go into stage 5 when it should be 4. But right now its set to the same as DPPT so I see no reason to change it.
Link to DPPT berry system: https://bulbapedia.bulbagarden.net/wiki/Berry#Formula
Tutorial:
First of all, before you implement my system you need to insert:
JPAN's save block hack: https://www.pokecommunity.com/posts/6993092/
You will then need to install some RTC. Note: If you use DNS, go to the second to last page of the thread and get endrifts patch. PATCH THE DNS PROGRAM NOT YOUR ROM. This patch fixes dns compatibility with mgba. If you don't do this, dns will only work on vba and not no$gba or Mgba. If you use sq's rtc, it will only work on VBA. These are bugs with rtc and emulators, not my system.
Insert the following routines into freespace.
bagHack:
Spoiler:
@Forces the bag to exit instead of opening the menu.
@Insert bx r3 at (18 47) 0813DB18, and the pointer to this routine (+1) at 0813DB30
.thumb
.thumb_func
.global BagHack
main:
ldr r1, var_800F
ldrh r1, [r1]
cmp r1, #0x7E
beq skip
ldr r1, var_800E
strh r0, [r1]
ldr r1, return
bx r1
skip:
ldr r1, var_800E
strh r0, [r1]
ldr r1, skipaddr
bx r1
.align 2
var_800F: .word 0x020370D2
var_800E: .word 0x0203AD30
skipaddr: .word 0x08109065
return: .word 0x081090B9
@Insert bx r3 at (18 47) 0813DB18, and the pointer to this routine (+1) at 0813DB30
.thumb
.thumb_func
.global BagHack
main:
ldr r1, var_800F
ldrh r1, [r1]
cmp r1, #0x7E
beq skip
ldr r1, var_800E
strh r0, [r1]
ldr r1, return
bx r1
skip:
ldr r1, var_800E
strh r0, [r1]
ldr r1, skipaddr
bx r1
.align 2
var_800F: .word 0x020370D2
var_800E: .word 0x0203AD30
skipaddr: .word 0x08109065
return: .word 0x081090B9
getTreeData:
Spoiler:
.text
.align 2
.thumb
.thumb_func
Main: @loads tree data in table and var 8005
Push {r0-r4, lr}
Ldr r0, .var_800F @Level script bypass check
Ldrb r0, [r0]
cmp r0, #0x7E @Checks if baghack is active
beq end
ldr r0, =(0x20370BC)@get tree data
ldrh r0, [r0]
ldr r1, .SaveBlockLocation
mov r2, #0x6
mul r0, r0, r2
add r0, r0, r1 @r0 contains offset to tree data
Mov r3, r0 @moves offset of tree data to r3
Ldrb r0, [r0] @loads stage/water byte
ldr r1, .var_8005 @loads values in vars
@r0 contains tree stage, watered and amount watered byte
@r3 contains offset to treedata
DecryptBitShifts: @stage decryption
Lsr r2, r0, #0x5 @puts stage value into r2
Strh r2, [r1] @stores stage into 8005
Add r1, r1, #0x2 @moves to var 8006
@water amount decryption
Lsl r2, r0, #0x1B @put water amount to top bits
Lsr r2, r2, #0x1C @water amount lowest bits
Strh r2, [r1] @stores water amount into 8006
Add r1, r1, #0x2 @moves to var 8007
@watered this stage
Lsl r2, r0, #0x1F @raise to top bits
Lsr r2, r2, #0x1F @lower to bottom bits
Strh r2, [r1] @stores byte into 8007
Add r1, r1, #0x2 @moves r1 to var 8008
Add r3, r3, #0x1 @moves r3 to berry I'd
@store berry id in 8008
Ldrb r0, [r3] @load berry id from ram
Strh r0, [r1] @stores byte into 8008
Add r1, r1, #0x2 @moves r1 to 8009
Add r3, r3, #0x1 @moves r3 to time planted
@store time planted into 8009
ldrb r0, [r3] @load first byte if time planted
Strh r0, [r1] @stores halfword in 8009
add r1, r1, #0x2 @moves r1 to 800A
add r3, r3, #0x1 @moves r3 to month planted
@store month into 800A
ldrb r0, [r3] @loads month
Strh r0, [r1] @stores halfword in 8009
add r1, r1, #0x2 @moves r1 to 800B
add r3, r3, #0x1 @moves r3 to day planted
@stores day into 800B
ldrb r0, [r3]
strh r0, [r1] @stores second byte time planted
CheckForZero: @checks if stage is zero
ldr r1, .var_8005
ldrh r1, [R1]
cmp r1, #0x0
beq end @skip time check if stage is 0
CheckMonthDay: @check time passed #check for demcember
ldr r0, .Clock
ldrb r3, [r0, #0x3] @Holds current month
Ldr r1, .var_800A @loads month planted
Ldrh r1, [r1]
Mov r2, r1
cmp r1, r3 @checks for month switch
bne MonthSwitched
b CheckDay
MonthSwitched:
@CHECK FOR HOW MANY DAYS
sub r1, r3, r1 @minus current from month planted
cmp r1, #0x1 @is month change larger than 1
beq OneMonthSwitch
add r1, r1, #0x0C @checking for dec to jan
cmp r1, #0x1
beq OneMonthSwitch
B SetStageTo5
OneMonthSwitch:
Ldrb r0, [r0, #0x4] @holds current day
Ldr r2, .var_800B @loads day planted offset
ldrh r2, [r2] @loads day planted
Sub r0, r0, r2 @minus current from planted day
Add r0, r0, #0x1E @add 30 for month switch
B CheckDayBranch
CheckDay:
Ldrb r0, [r0, #0x4] @holds current day
Ldr r2, .var_800B @loads day planted
ldrh r2, [r2] @loads day planted
Sub r0, r0, r2 @minus current from planted day
CheckDayBranch:
Cmp r0, #0x3 @CHANGE IF YOU WANT A DIFFERENT AMOUNT OF DAYS REFERENCE #001 (In explaination on PC)
Bge SetStageTo5 @if more than 3 days, set stage to 5
@day change can only be 1 or 2
CheckHoursPassed: @figure out how much time in hours has passed - you can use all regs
@check time passed and update
ldr r1, .Clock
ldrb r2, [r1, #0x6] @hours
ldr r3, .var_8009 @Loads var
ldrb r3, [r3]
sub r2, r2, r3 @Minus planted time from current
Push {r3}
mov r3, #0x18
Mul r0, r0, r3 @turn days into hours
pop {r3}
Add r2, r2, r0 @How many hours has passed
BeginLoop:
mov r3, #0x0
mov r1, #0x0C @CHANGE THIS TO CHANGE TIME BETWEEN PHASES
mov r0, r1
loop: @figuring out which stage youre in
cmp r2, r1
blo addToStage
add r1, r1, r0 @CHANGE THIS TO CHANGE TIME BETWEEN PHASES
add r3, r3, #0x1
cmp r3, #0x5
bge SetStageTo5 @Still Need to test
b loop
SetStageTo5:
ldr r2, .var_8005 @Stores stage in 8005
mov r3, #0x5
strh r3, [r2]
b ResetWatering
addToStage:
add r3, r3, #0x1
ldr r2, .var_8005 @Stores stage in 8005
ldrh r1, [r2]
cmp r1, r3
beq end
strb r3, [r2]
ResetWatering:
ldr r2, .var_8007 @Stores 0 in 8007 to reset Watered Already
mov r3, #0x0
strh r3, [r2]
end:
pop {r0-r4, pc}
.align 2
.SaveBlockLocation: .word 0x203C000
.var_8005: .word 0x020370C2
.var_8007: .word 0x020370C6
.var_8009: .word 0x020370CA
.var_800A: .word 0x020370CC
.var_800B: .word 0x020370CE
.var_800F: .word 0x020370D2
.Clock: .word 0x0300553C
Compiled:
Code:
1F B5 3A 48 00 78 7E 28 62 D0 3A 48 00 88 31 49 06 22 50 43 40 18 03 1C 00 78 2F 49 42 09 0A 80 02 31 C2 06 12 0F 0A 80 02 31 C2 07 D2 0F 0A 80 02 31 01 33 18 78 08 80 02 31 01 33 18 78 08 80 02 31 01 33 18 78 08 80 02 31 01 33 18 78 08 80 21 49 09 88 00 29 3B D0 25 48 C3 78 21 49 09 88 0A 1C 99 42 00 D1 0C E0 59 1A 01 29 03 D0 0C 31 01 29 00 D0 1F E0 00 79 1B 4A 12 88 80 1A 1E 30 03 E0 00 79 18 4A 12 88 80 1A 03 28 13 DA 18 49 8A 79 13 4B 1B 78 D2 1A 08 B4 18 23 58 43 08 BC 12 18 00 23 0C 21 08 1C 8A 42 08 D3 09 18 01 33 05 2B 00 DA F8 E7 08 4A 05 23 13 80 05 E0 01 33 05 4A 11 88 99 42 03 D0 13 70 04 4A 00 23 13 80 1F BD C0 46 00 C0 03 02 C2 70 03 02 C6 70 03 02 CA 70 03 02 CC 70 03 02 CE 70 03 02 D2 70 03 02 3C 55 00 03 BC 70 03 02
setTreeData:
Spoiler:
.text
.align 2
.thumb
.thumb_func
Main: @Sets tree data in table
Push {r0-r4, lr}
Ldr r0, .var_800F @Level script bypass check
Ldrb r0, [r0]
cmp r0, #0x7E @Checks if baghack is active
beq End
ldr r0, =(0x20370BC)@get tree data
ldrh r0, [r0]
ldr r1, .SaveBlockLocation
mov r2, #0x6
mul r0, r0, r2
add r4, r0, r1 @r4 contains offset to tree data
StageByteStorage:
ldr r1, .var_8005 @loads offsets to vars
ldr r2, .var_8006
ldr r3, .var_8007
Ldrb r1, [r1] @loads vars bytes
Ldrb r2, [r2]
Ldrb r3, [r3]
Placebits:
lsl r1, r1, #0x5 @put stage to top 3 bits
lsl r2, r2, #0x1 @put watered amount to 4th bit
orr r1, r1, r2 @combine stage and watered this stage
orr r1, r1, r3 @combine ^ with watered amount
strb r1, [r4] @stores mashed byte in first slot
add r4, r4, #0x1 @moves to next slot in ram
@store berry id in 8008
Ldr r1, .var_8008
Ldrb r0, [r1] @load berry id from var 8008
Strb r0, [r4] @stores byte into ram
Add r1, r1, #0x2 @moves r1 to 8009
Add r4, r4, #0x1 @moves r3 to time planted
@store time planted into 8009
ldrb r0, [r1] @load first byte if time planted
Strb r0, [r4]
add r1, r1, #0x2 @moves r1 to 800A
add r4, r4, #0x1 @moves r3 to month planted
@store month into 800A
ldrb r0, [r1] @loads month
Strb r0, [r4]
add r1, r1, #0x2 @moves r1 to 800B
add r4, r4, #0x1 @moves r3 to day planted
@stores day into 800B
ldrb r0, [r1]
strb r0, [r4]
End:
Pop {r0-r4, pc}
.align 2
.SaveBlockLocation: .word 0x203C000
.var_8005: .word 0x020370C2
.var_8006: .word 0x020370C4
.var_8007: .word 0x020370C6
.var_8008: .word 0x020370C8
.var_8009: .word 0x020370CA
.var_800A: .word 0x020370CC
.var_800B: .word 0x020370CE
.var_800F: .word 0x020370D2
Compiled:
Code:
1F B5 1B 48 00 78 7E 28 20 D0 1A 48 00 88 10 49 06 22 50 43 44 18 0F 49 0F 4A 10 4B 09 78 12 78 1B 78 49 01 52 00 11 43 19 43 21 70 01 34 0C 49 08 78 20 70 02 31 01 34 08 78 20 70 02 31 01 34 08 78 20 70 02 31 01 34 08 78 20 70 1F BD C0 46 00 C0 03 02 C2 70 03 02 C4 70 03 02 C6 70 03 02 C8 70 03 02 CA 70 03 02 CC 70 03 02 CE 70 03 02 D2 70 03 02 BC 70 03 02
getYeild:
Spoiler:
@((max - min) x (water - 1) + rand) / 4 + min
Main:
Push {r0-r4, lr}
bl RandomBL
mov r3, r0 @put random value in r3 for later
ldr r0, .var_8000
ldr r1, .var_8001
ldr r2, .var_8007
ldrh r0, [r0] @Load max
ldrh r1, [r1] @Load min
ldrh r2, [r2] @load water amount
Equation:
sub r1, r0, r1 @Gap between max and min in r1
cmp r2, #0x0
beq Skip
sub r2, r2, #0x1 @water minus 1
Skip:
mov r4, r1
mul r4, r4, r2
ModularDivide:
mov r0, r3 @put random value in r0
bl UmodsiBL @modular divsion RAND
add r0, r0, r4
mov r1, #0x4
bl DivideBL
ldr r1, .var_8001
ldrh r1, [r1] @Load min
add r0, r0, r1
Store:
ldr r1, .var_800D
strh r0, [r1]
End:
Pop {r0-r4, pc}
RandomBL:
ldr r2, .Random
bx r2
UmodsiBL:
ldr r2, .Umodsi
bx r2
DivideBL:
ldr r2, .Divide
bx r2
.align 2
.var_8000: .word 0x020370B8
.var_8001: .word 0x020370BA
.var_8007: .word 0x020370C6
.var_800D: .word 0x020370D0
.Random: .word 0x08044EC9
.Umodsi: .word 0x081E4685
.Divide: .word 0x081E4019
Compiled:
Code:
1F B5 00 F0 1A F8 03 1C 0F 48 10 49 10 4A 00 88 09 88 12 88 41 1A 00 2A 00 D0 01 3A 0C 1C 54 43 18 1C 00 F0 0C F8 00 19 04 21 00 F0 0A F8 07 49 09 88 40 18 07 49 08 80 1F BD 07 4A 10 47 07 4A 10 47 07 4A 10 47 C0 46 B8 70 03 02 BA 70 03 02 C6 70 03 02 D0 70 03 02 C9 4E 04 08 85 46 1E 08 19 40 1E 08
setTime:
Spoiler:
.align 2
.thumb
.thumb_func
Main:
push {r0-r3, lr}
ldr r0, .Clock
ldr r1, .var_8009
LoadandStore:
@loads and stores hour in 8009
ldrb r2, [r0, #0x6] @Holds current hour
strb r2, [r1]
add r1, r1, #0x2 @moves to var 800a
@loads and stores hour
ldrb r2, [r0, #0x3] @Holds current month
strb r2, [r1]
add r1, r1, #0x2 @moves to var 800b
@loads and stores day
ldrb r2, [r0, #0x4] @Holds current day
strb r2, [r1]
End:
pop {r0-r3, pc}
.align 2
.align 2
.var_8009: .word 0x020370CA
.Clock: .word 0x0300553C
.align 2
.thumb
.thumb_func
Main:
push {r0-r3, lr}
ldr r0, .Clock
ldr r1, .var_8009
LoadandStore:
@loads and stores hour in 8009
ldrb r2, [r0, #0x6] @Holds current hour
strb r2, [r1]
add r1, r1, #0x2 @moves to var 800a
@loads and stores hour
ldrb r2, [r0, #0x3] @Holds current month
strb r2, [r1]
add r1, r1, #0x2 @moves to var 800b
@loads and stores day
ldrb r2, [r0, #0x4] @Holds current day
strb r2, [r1]
End:
pop {r0-r3, pc}
.align 2
.align 2
.var_8009: .word 0x020370CA
.Clock: .word 0x0300553C
Compiled:
Code:
0F B5 06 48 04 49 82 79 0A 70 02 31 C2 78 0A 70 02 31 02 79 0A 70 0F BD CA 70 03 02 3C 55 00 03
Now, once you've compiled those routines into free space you will need to insert their pointers (not in reverse hex) into this script. Remember to add 1 to the pointer.
I've made sure to add comments everywhere there is a callasm command to one of my routines. You'll also notice that the names I call these routines are identical to the names I gave the spoilers. You need to match the call to the pointer to the name of the routine!
XSE script:
Spoiler:
#org @HeavyScript
lock
copyvar 0x8002 LASTRESULT
copyvar 0x8011 0x8002
callasm 0x8900031 'GetTreeData
compare 0x8005 0x0
if 0x1 goto @NonePlanted
compare 0x8005 0x1
if 0x1 goto @Fresh
compare 0x8005 0x2
if 0x1 goto @Sprouting
compare 0x8005 0x3
if 0x1 goto @Growing
compare 0x8005 0x4
if 0x1 goto @Blooming
callasm 0x89001B1 'GetYield
buffernumber 0x0 LASTRESULT
bufferitem 0x1 0x8008
additem 0x8008 0x800D
msgbox @Received MSG_NORMAL
setvar 0x8005 0x0
setvar 0x8006 0x0
setvar 0x8007 0x0
setvar 0x8008 0x0
setvar 0x8009 0x0
setvar 0x800A 0x0
setvar 0x800B 0x0
callasm 0x8900131 'SetTreeData
msgbox @Returned MSG_NORMAL
release
end
#org @NonePlanted
msgbox @None MSG_YESNO
compare LASTRESULT 0x0
if 0x1 goto @end
fadescreen 0x1
setvar 0x800F 0x7E
setvar 0x800E 0x0
writebytetooffset 0x0 0x203AD02
callasm 0x810A67D
waitmsg
setvar 0x800F 0x0
compare 0x800E 0x0
if 0x1 goto @end
compare 0x800E 0x84
if 0x3 goto @LaughAtPlayer
compare 0x800E 0xB0
if 0x4 goto @LaughAtPlayer
copyvar 0x8008 0x800E
bufferitem 0x0 0x8008
msgbox @addedberry MSG_NORMAL
setvar 0x8005 0x1
setvar 0x8006 0x0
setvar 0x8007 0x0
copyvar 0x8002 0x8011
callasm 0x8900221 'SetTime
callasm 0x8900131 'SetTreeData
removeitem 0x8008 0x1
release
end
#org @Fresh
bufferitem 0x0 0x8008
msgbox @NewPlant MSG_NORMAL
compare 0x8007 0x0
if 0x3 goto @WaterPlant
msgbox @WateredAlready MSG_NORMAL
release
end
#org @Sprouting
bufferitem 0x0 0x8008
msgbox @Sprouted MSG_NORMAL
compare 0x8007 0x0
if 0x3 goto @WaterPlant
msgbox @WateredAlready MSG_NORMAL
release
end
#org @Growing
bufferitem 0x0 0x8008
msgbox @Tall MSG_NORMAL
compare 0x8007 0x0
if 0x3 goto @WaterPlant
msgbox @WateredAlready MSG_NORMAL
release
end
#org @Blooming
bufferitem 0x0 0x8008
msgbox @Bloom MSG_NORMAL
compare 0x8007 0x0
if 0x3 goto @WaterPlant
msgbox @WateredAlready MSG_NORMAL
release
end
#org @end
release
end
#org @LaughAtPlayer
msgbox @WrongItem MSG_NORMAL
release
end
#org @WaterPlant
checkitem 0x1 0x1 'Checks if you have masterball Change to what your watering item
if 0x0 goto @end
removeitem 0x1 0x1 'Change masterball
msgbox @WantWater MSG_YESNO
compare LASTRESULT 0x0
if 0x1 goto @end
msgbox @FinishedWatering MSG_NORMAL
addvar 0x8006 0x1
addvar 0x8007 0x1
copyvar LASTRESULT 0x8005
callasm 0x8900131 'SetTreeData
release
end
'---------
' Strings
'---------
#org @Received
= [player] found [buffer1] [buffer2]!
#org @Returned
= The soil returned to it's soft and\nloamy state.
#org @None
= The soil is soft here[.]\nPlant a berry?
#org @addedberry
= You planted a [buffer1] here.
#org @NewPlant
= A [buffer1] was planted here.
#org @WateredAlready
= It's been watered already.
#org @Sprouted
= The [buffer1] has sprouted.
#org @Tall
= The [buffer1] plant\nis growing bigger.
#org @Bloom
= The [buffer1] plant is in bloom!
#org @WantWater
= The plant can use some water[.]\nWould you like to water it?
#org @FinishedWatering
= You finished watering!
#org @WrongItem
= Silly child[.]\nYou can't plant that
Usage:
After you've compiled the script, write down the offset of it as well. You will need to remember it.
Here is how you'll be using the script:
Code:
#dyn 0x740000
#org @start
setvar 0x8000 0x[max berry yield]
setvar 0x8001 0x[min berry yield]
setvar LASTRESULT 0x[Tree ID] 'Start at 00
jump 0x8[Offset where you compiled the above script]
Every tree should have a unique Tree ID. So if you want two trees, one can be 0x0, and the other can be 0x1. You can theoretically go up to FFFF or 65,535 trees with the current system but we don't have enough ram for that. I think FF is a fine amount. Set the tree ID to lastresult and then jump the script that handles the complicated stuff.
Getting Sprites to Load:
Everyone can thank DrFuji for this.
Spoiler:
Or tell him he sucks
Insert this level script into every level you have berry trees on:
Spoiler:
#dynamic 0x880000
#org @start // Uses a 0x5 type level script
setvar 0x4011 0x1 // ID of first berry tree on map
setvar 0x8003 0x2 // ID of last berry tree on map
setvar 0x8004 0x10 // Start of the berry tree OWs event numbers in A-Map
goto @Main
#org @Main
copyvar 0x8002 0x4011
callasm 0x8900031 `get tree data
callasm 0x8800131 `set tree data
compare 0x8005 0x0
if 0x1 goto @Nothing
compare 0x8005 0x1
if 0x1 goto @Sprout
compare 0x8005 0x2
if 0x1 goto @Growing
compare 0x8005 0x3
if 0x1 goto @Flowering
spritebehave 0x8004 0x4D // Makes the OW hold the VS seeker animation
goto @Next
#org @Next
comparevars 0x8003 0x4011 // Checks if the current berry tree ID is the last one on the map
if 0x1 goto @Ending
addvar 0x4011 0x1
addvar 0x8004 0x1
goto @Main // Loops back to @Main
#org @Nothing
movesprite2 0x8004 0xFF 0xFF // Moves the OW far offscreen if there is no growth
goto @Next
#org @Sprout
spritebehave 0x8004 0x0 // Makes the OW face down
goto @Next
#org @Growing
spritebehave 0x8004 0x7 // Makes the OW face up
goto @Next
#org @Flowering
spritebehave 0x8004 0x9 // Makes the OW face left
goto @Next
#org @Ending
end
Now once you do that....a tutorial will be put here later!
Notes:
- Watering item is a masterball, change it to your watering item
- Where you see "SAVEBLOCK" in each routine, change that to a spot in ram that your tree data table will be located. Every tree is 6 bytes of ram.
- YOU MUST MOVE BERRIES INTO THE ITEM POUCH
Why?
Spoiler:
I tried for a few weeks search for a way to open and close the berry pouch from the Overworld and came up empty handed. I did find 5 different functions to open the pouch but all bugged in one way or another when called via callasm.
Future Updates:
- Move berries bag into berry pouch when/if we find a way to do so
Credits:
Spoiler:
Jpan - Save Block Hack
FBI - Making the base of the system
ShinyQuagsire - Bag Hack
Xizqu - Finishing FBI's system
Skeli - Helping me
Akamethebulbasaur - Helping me
DrFuji - Level Script
Last edited: