ref: 7d55a3909698d069f002c8f8297b7eb164361e59
parent: 01f48a54e88bc42ad47a3b33752c80fb19be4774
author: yenatch <[email protected]>
date: Thu Jan 10 21:18:34 EST 2013
comment LoadEnemyMon and related fns; add RNG fns
--- a/constants.asm
+++ b/constants.asm
@@ -1599,6 +1599,12 @@
FRIDAY EQU $05
SATURDAY EQU $06
+; times of day
+MORN EQU 0
+DAY EQU 1
+NITE EQU 2
+DARKNESS EQU 3
+
; trainer groups
FALKNER EQU $01
WHITNEY EQU $02
@@ -3247,17 +3253,15 @@
SPECIAL_BEASTSCHECK EQU $0096
SPECIAL_MONCHECK EQU $0097
-; battle scripts
-BATTLE_FILLPP EQU $05
-BATTLE_FILLSTATS EQU $0C
+; predefs
+PREDEF_FILLPP EQU $05
+PREDEF_FILLSTATS EQU $0C
+PREDEF_FILLMOVES EQU $1B
+PREDEF_GETUNOWNLETTER EQU $2D
-BATTLE_FILLMOVES EQU $1B
-BATTLE_GETUNOWNLETTER EQU $2D
-
-
-; vars
+; script vars
NUM_VARS EQU $1b
VAR_MOVEMENT EQU $08
@@ -3282,6 +3286,9 @@
MOVE_ACC EQU 4
MOVE_PP EQU 5
MOVE_CHANCE EQU 6
+
+; base stats
+BASE_STATS_LENGTH EQU 32
; stat constants
NUM_STATS EQU 6
--- a/main.asm
+++ b/main.asm
@@ -1364,8 +1364,60 @@
ret
; 0x2ead
-INCBIN "baserom.gbc",$2ead,$2fb1-$2ead
+INCBIN "baserom.gbc", $2ead, $2f8c - $2ead
+RNG: ; 2f8c
+; Two random numbers are generated by adding and subtracting
+; the divider to the respective values every time it's called.
+
+; The divider is a value that increments at a rate of 16384Hz.
+; For comparison, the Game Boy operates at a clock speed of 4.2MHz.
+
+; Additionally, an equivalent function is called every frame.
+
+; output:
+; a: rand2
+; ffe1: rand1
+; ffe2: rand2
+
+ push bc
+; Added value
+ ld a, [$ff04] ; divider
+ ld b, a
+ ld a, [$ffe1]
+ adc b
+ ld [$ffe1], a
+; Subtracted value
+ ld a, [$ff04] ; divider
+ ld b, a
+ ld a, [$ffe2]
+ sbc b
+ ld [$ffe2], a
+ pop bc
+ ret
+; 2f9f
+
+FarBattleRNG: ; 2f9f
+; BattleRNG lives in another bank.
+; It handles all RNG calls in the battle engine,
+; allowing link battles to remain in sync using a shared PRNG.
+
+; Save bank
+ ld a, [$ff9d] ; bank
+ push af
+; Bankswitch
+ ld a, BANK(BattleRNG)
+ rst $10
+ call BattleRNG
+; Restore bank
+ ld [$cfb6], a
+ pop af
+ rst $10
+ ld a, [$cfb6]
+ ret
+; 2fb1
+
+
Function2fb1: ; 2fb1
push bc
ld c, a
@@ -1878,8 +1930,72 @@
ret
; 0x3487
-INCBIN "baserom.gbc",$3487,$38a2 - $3487
+INCBIN "baserom.gbc",$3487,$3856 - $3487
+GetBaseStats: ; 3856
+ push bc
+ push de
+ push hl
+
+; Save bank
+ ld a, [$ff9d]
+ push af
+; Bankswitch
+ ld a, BANK(BaseStats)
+ rst $10
+
+; Egg doesn't have base stats
+ ld a, [CurBattleSpecies]
+ cp EGG
+ jr z, .egg
+
+; Get base stats
+ dec a
+ ld bc, BASE_STATS_LENGTH
+ ld hl, BaseStats
+ call AddNTimes
+ ld de, CurBaseStats
+ ld bc, BASE_STATS_LENGTH
+ call CopyBytes
+ jr .end
+
+.egg
+; ????
+ ld de, $7d9c
+
+; Sprite dimensions
+ ld b, $55
+ ld hl, $d247
+ ld [hl], b
+
+; ????
+ ld hl, $d248
+ ld [hl], e
+ inc hl
+ ld [hl], d
+ inc hl
+ ld [hl], e
+ inc hl
+ ld [hl], d
+ jr .end
+
+.end
+; Replace Pokedex # with species
+ ld a, [CurBattleSpecies]
+ ld [CurBaseStats], a
+
+; Restore bank
+ pop af
+ rst $10
+
+ pop hl
+ pop de
+ pop bc
+ ret
+; 389c
+
+INCBIN "baserom.gbc",$389c,$38a2 - $389c
+
GetNick: ; 38a2
; get the nickname of a partymon
; write nick to StringBuffer1
@@ -1985,21 +2101,25 @@
ret
; 0x3917
-Function3917: ; 3917
+GetPartyParamLocation: ; 3917
+; Get the location of parameter a from CurPartyMon in hl
push bc
- ld hl, $dcdf
+ ld hl, PartyMons
ld c, a
ld b, $00
add hl, bc
- ld a, [$d109]
- call Function3927
+ ld a, [CurPartyMon]
+ call GetPartyLocation
pop bc
ret
; 3927
-Function3927: ; 3927
-; a is typically [$d109]
- ld bc, $0030
+GetPartyLocation: ; 3927
+; Add the length of a PartyMon struct to hl a times
+; input:
+; a: partymon #
+; hl: partymon struct
+ ld bc, $0030 ; PARTYMON_LENGTH
jp AddNTimes
; 392d
@@ -12501,53 +12621,100 @@
INCBIN "baserom.gbc",$3ddc8,$3e8eb - $3ddc8
-Function3e8eb: ; 3e8eb
-;part of battle init
+LoadEnemyMon: ; 3e8eb
+; Initialize enemy monster parameters
+; To do this we pull the species from TempEnemyMonSpecies
+
+; Notes:
+; FarBattleRNG is used to ensure sync between Game Boys
+
+; Clear the whole EnemyMon struct
xor a
ld hl, EnemyMonSpecies
ld bc, $0027
call ByteFill
+
+; We don't need to be here if we're in a link battle
ld a, [InLinkBattle]
and a
jp nz, $5abd
- ld a, [$cfc0]
+
+ ld a, [$cfc0] ; ????
bit 0, a
jp nz, $5abd
+
+; Make sure everything knows what species we're working with
ld a, [TempEnemyMonSpecies]
ld [EnemyMonSpecies], a
- ld [$cf60], a
- ld [$d108], a
- call $3856
- ld a, [$d22d]
+ ld [CurBattleSpecies], a
+ ld [CurPartySpecies], a
+
+; Grab the base stats for this species
+ call GetBaseStats
+
+
+; Let's get the item:
+
+; Is the item predetermined?
+ ld a, [IsInBattle]
dec a
- jr z, .asm_3e925
- ld a, [$d109]
- ld hl, $d289
- call Function3927
+ jr z, .WildItem
+
+; If we're in a trainer battle, the item is in the party struct
+ ld a, [CurPartyMon]
+ ld hl, OTPartyMon1Item
+ call GetPartyLocation ; bc = PartyMon[CurPartyMon] - PartyMons
ld a, [hl]
- jr .asm_3e945
-.asm_3e925
+ jr .UpdateItem
+
+
+.WildItem
+; In a wild battle, we pull from the item slots in base stats
+
+; Force Item1
+; Used for Ho-Oh, Lugia and Snorlax encounters
ld a, [BattleType]
- cp a, $0a
- ld a, [$d241]
- jr z, .asm_3e945
- call $2f9f
- cp a, $c0
- ld a, $00
- jr c, .asm_3e945
- call $2f9f
- cp a, $14
- ld a, [$d241]
- jr nc, .asm_3e945
- ld a, [$d242]
-.asm_3e945
+ cp BATTLETYPE_FORCEITEM
+ ld a, [$d241] ; BufferMonItem1
+ jr z, .UpdateItem
+
+; Failing that, it's all up to chance
+; Effective chances:
+; 75% None
+; 23% Item1
+; 2% Item2
+
+; 25% chance of getting an item
+ call FarBattleRNG
+ cp a, $c0 ; $c0/$100 = 75%
+ ld a, NO_ITEM
+ jr c, .UpdateItem
+
+; From there, an 8% chance for Item2
+ call FarBattleRNG
+ cp a, $14 ; 8% of 25% = 2% Item2
+ ld a, [$d241] ; BaseStatsItem1
+ jr nc, .UpdateItem
+ ld a, [$d242] ; BaseStatsItem2
+
+
+.UpdateItem
ld [EnemyMonItem], a
- ld a, [$d22d]
+
+
+; Initialize DVs
+
+; If we're in a trainer battle, DVs are predetermined
+ ld a, [IsInBattle]
and a
- jr z, .asm_3e963
+ jr z, .InitDVs
+
+; ????
ld a, [$c671]
bit 3, a
- jr z, .asm_3e963
+ jr z, .InitDVs
+
+; Unknown
ld hl, $c6f2
ld de, EnemyMonDVs
ld a, [hli]
@@ -12555,161 +12722,276 @@
inc de
ld a, [hl]
ld [de], a
- jp .asm_3ea1a
-.asm_3e963
- ld a, $09
- ld hl, $70c4
- rst $08
- ld a, [$d22d]
+ jp .Happiness
+
+
+.InitDVs
+
+; Trainer DVs
+
+; All trainers have preset DVs, determined by class
+; See GetTrainerDVs for more on that
+ callba GetTrainerDVs
+; These are the DVs we'll use if we're actually in a trainer battle
+ ld a, [IsInBattle]
dec a
- jr nz, .asm_3e9a8
+ jr nz, .UpdateDVs
+
+
+; Wild DVs
+; Here's where the fun starts
+
+; Roaming monsters (Entei, Raikou) work differently
+; They have their own structs, which are shorter than normal
ld a, [BattleType]
- cp a, $05
- jr nz, .asm_3e996
- call $7a01
+ cp a, BATTLETYPE_ROAMING
+ jr nz, .NotRoaming
+
+; Grab HP
+ call GetRoamMonHP
ld a, [hl]
+; Check if the HP has been initialized
and a
+; We'll do something with the result in a minute
push af
- call $7a19
+
+; Grab DVs
+ call GetRoamMonDVs
inc hl
ld a, [hld]
ld c, a
ld b, [hl]
+
+; Get back the result of our check
pop af
- jr nz, .asm_3e9a8
- call $7a19
+; If the RoamMon struct has already been initialized, we're done
+ jr nz, .UpdateDVs
+
+; If it hasn't, we need to initialize the DVs
+; (HP is initialized at the end of the battle)
+ call GetRoamMonDVs
inc hl
- call $2f9f
+ call FarBattleRNG
ld [hld], a
ld c, a
- call $2f9f
+ call FarBattleRNG
ld [hl], a
ld b, a
- jr .asm_3e9a8
-.asm_3e996
- cp a, $07
- jr nz, .asm_3e9a0
- ld b, $ea
- ld c, $aa
- jr .asm_3e9a8
-.asm_3e9a0
- call $2f9f
+; We're done with DVs
+ jr .UpdateDVs
+
+
+.NotRoaming
+; Register a contains BattleType
+
+; Forced shiny battle type
+; Used by Red Gyarados at Lake of Rage
+ cp a, BATTLETYPE_SHINY
+ jr nz, .GenerateDVs
+
+ ld b, ATKDEFDV_SHINY ; $ea
+ ld c, SPDSPCDV_SHINY ; $aa
+ jr .UpdateDVs
+
+.GenerateDVs
+; Generate new random DVs
+ call FarBattleRNG
ld b, a
- call $2f9f
+ call FarBattleRNG
ld c, a
-.asm_3e9a8
+
+.UpdateDVs
+; Input DVs in register bc
ld hl, EnemyMonDVs
ld a, b
ld [hli], a
ld [hl], c
- ld a, [$d22d]
+
+
+; We've still got more to do if we're dealing with a wild monster
+ ld a, [IsInBattle]
dec a
- jr nz, .asm_3ea1a
+ jr nz, .Happiness
+
+
+; Species-specfic:
+
+
+; Unown
ld a, [TempEnemyMonSpecies]
cp a, UNOWN
- jr nz, .notunown
+ jr nz, .Magikarp
+
+; Get letter based on DVs
ld hl, EnemyMonDVs
- ld a, $2d
- call $2d83
+ ld a, PREDEF_GETUNOWNLETTER
+ call Predef
+; Can't use any letters that haven't been unlocked
+; If combined with forced shiny battletype, causes an infinite loop
call CheckUnownLetter
- jr c, .asm_3e9a0
-.notunown
+ jr c, .GenerateDVs ; try again
+
+
+.Magikarp
+; Skimming this part recommended
+
ld a, [TempEnemyMonSpecies]
cp a, MAGIKARP
- jr nz, .asm_3ea1a
+ jr nz, .Happiness
+
+; Get Magikarp's length
ld de, EnemyMonDVs
ld bc, PlayerID
- ld hl, CalcMagikarpLength
- ld a, BANK(CalcMagikarpLength)
- rst $08
- ld a, [$d1ea] ; Magikarp's length
- cp a, $06
- jr nz, .asm_3e9fe
- call $2f8c
- cp a, $0c
- jr c, .asm_3e9fe
- ld a, [$d1eb]
+ callab CalcMagikarpLength
+
+; We're clear if the length is < 1536
+ ld a, [MagikarpLengthHi]
+ cp a, $06 ; $600 = 1536
+ jr nz, .CheckMagikarpArea
+
+; 5% chance of skipping size checks
+ call RNG
+ cp a, $0c ; / $100
+ jr c, .CheckMagikarpArea
+; Try again if > 1614
+ ld a, [MagikarpLengthLo]
cp a, $50
- jr nc, .asm_3e9a0
- call $2f8c
- cp a, $32
- jr c, .asm_3e9fe
- ld a, [$d1eb]
+ jr nc, .GenerateDVs
+
+; 20% chance of skipping this check
+ call RNG
+ cp a, $32 ; / $100
+ jr c, .CheckMagikarpArea
+; Try again if > 1598
+ ld a, [MagikarpLengthLo]
cp a, $40
- jr nc, .asm_3e9a0
-.asm_3e9fe
- ld a, [$dcb5]
- cp a, $09
- jr z, .asm_3ea1a
- ld a, [$dcb6]
- cp a, $06
- jr z, .asm_3ea1a
- call $2f8c
- cp a, $64
- jr c, .asm_3ea1a
- ld a, [$d1ea]
- cp a, $04
- jr c, .asm_3e9a0
-.asm_3ea1a
- ld a, $46
+ jr nc, .GenerateDVs
+
+.CheckMagikarpArea
+; The z checks are supposed to be nz
+; Instead, all maps in GROUP_LAKE_OF_RAGE (mahogany area)
+; and routes 20 and 44 are treated as Lake of Rage
+
+; This also means Lake of Rage Magikarp can be smaller than ones
+; caught elsewhere rather than the other way around
+
+; Intended behavior enforces a minimum size at Lake of Rage
+; The real behavior prevents size flooring in the Lake of Rage area
+ ld a, [MapGroup]
+ cp a, GROUP_LAKE_OF_RAGE
+ jr z, .Happiness
+ ld a, [MapNumber]
+ cp a, MAP_LAKE_OF_RAGE
+ jr z, .Happiness
+; 40% chance of not flooring
+ call RNG
+ cp a, $64 ; / $100
+ jr c, .Happiness
+; Floor at length 1024
+ ld a, [MagikarpLengthHi]
+ cp a, $04 ; $400 = 1024
+ jr c, .GenerateDVs ; try again
+
+
+; Finally done with DVs
+
+.Happiness
+; Set happiness
+ ld a, 70 ; BASE_HAPPINESS
ld [EnemyMonHappiness], a
- ld a, [$d143]
+; Set level
+ ld a, [CurPartyLevel]
ld [EnemyMonLevel], a
+; Fill stats
ld de, EnemyMonMaxHP
ld b, $00
- ld hl, $d201
- ld a, $0c
- call $2d83
- ld a, [$d22d]
- cp a, $02
- jr z, .asm_3ea74
+ ld hl, $d201 ; ?
+ ld a, PREDEF_FILLSTATS
+ call Predef
+
+; If we're in a trainer battle,
+; get the rest of the parameters from the party struct
+ ld a, [IsInBattle]
+ cp a, TRAINER_BATTLE
+ jr z, .OpponentParty
+
+; If we're in a wild battle, check wild-specific stuff
and a
- jr z, .asm_3ea44
+ jr z, .TreeMon
+
+; ????
ld a, [$c671]
bit 3, a
- jp nz, .asm_3ea90
-.asm_3ea44
+ jp nz, .Moves
+
+.TreeMon
+; If we're headbutting trees, some monsters enter battle asleep
call CheckSleepingTreeMon
- ld a, $07
- jr c, .asm_3ea4c
+ ld a, 7 ; Asleep for 7 turns
+ jr c, .UpdateStatus
+; Otherwise, no status
xor a
-.asm_3ea4c
+
+.UpdateStatus
ld hl, EnemyMonStatus
ld [hli], a
+
+; Unused byte
xor a
ld [hli], a
+
+; Full HP...
ld a, [EnemyMonMaxHPHi]
ld [hli], a
ld a, [EnemyMonMaxHPLo]
ld [hl], a
+
+; ...unless it's a RoamMon
ld a, [BattleType]
- cp a, $05
- jr nz, .asm_3ea90
- call $7a01
+ cp a, BATTLETYPE_ROAMING
+ jr nz, .Moves
+
+; Grab HP
+ call GetRoamMonHP
ld a, [hl]
+; Check if it's been initialized again
and a
- jr z, .asm_3ea6e
+ jr z, .InitRoamHP
+; Update from the struct if it has
ld a, [hl]
ld [EnemyMonHPLo], a
- jr .asm_3ea90
-.asm_3ea6e
+ jr .Moves
+
+.InitRoamHP
+; HP only uses the lo byte in the RoamMon struct since
+; Raikou/Entei/Suicune will have < 256 hp at level 40
ld a, [EnemyMonHPLo]
ld [hl], a
- jr .asm_3ea90
-.asm_3ea74
- ld hl, $d2ab
- ld a, [$d109]
- call Function3927
+ jr .Moves
+
+
+.OpponentParty
+; Get HP from the party struct
+ ld hl, (PartyMon1CurHP + 1) - PartyMon1 + OTPartyMon1
+ ld a, [CurPartyMon]
+ call GetPartyLocation
ld a, [hld]
ld [EnemyMonHPLo], a
ld a, [hld]
ld [EnemyMonHPHi], a
- ld a, [$d109]
- ld [$c663], a
+
+; Make sure everything knows which monster the opponent is using
+ ld a, [CurPartyMon]
+ ld [CurOTMon], a
+
+; Get status from the party struct
dec hl
- ld a, [hl]
+ ld a, [hl] ; OTPartyMonStatus
ld [EnemyMonStatus], a
-.asm_3ea90
+
+
+.Moves
+; ????
ld hl, $d23d
ld de, $d224
ld a, [hli]
@@ -12717,17 +12999,23 @@
inc de
ld a, [hl]
ld [de], a
+
+; Get moves
ld de, EnemyMonMoves
- ld a, [$d22d]
- cp a, $02
- jr nz, .asm_3eab6
+; Are we in a trainer battle?
+ ld a, [IsInBattle]
+ cp a, TRAINER_BATTLE
+ jr nz, .WildMoves
+; Then copy moves from the party struct
ld hl, OTPartyMon1Moves
- ld a, [$d109]
- call Function3927
- ld bc, $0004
+ ld a, [CurPartyMon]
+ call GetPartyLocation
+ ld bc, NUM_MOVES
call CopyBytes
- jr .asm_3eac5
-.asm_3eab6
+ jr .PP
+
+.WildMoves
+; Clear EnemyMonMoves
xor a
ld h, d
ld l, e
@@ -12735,86 +13023,115 @@
ld [hli], a
ld [hli], a
ld [hl], a
+; Make sure the predef knows this isn't a partymon
ld [$d1ea], a
- ld a, $1b
- call $2d83
-.asm_3eac5
- ld a, [$d22d]
- cp a, $02
- jr z, .asm_3ead9
+; Fill moves based on level
+ ld a, PREDEF_FILLMOVES
+ call Predef
+
+.PP
+; Trainer battle?
+ ld a, [IsInBattle]
+ cp a, TRAINER_BATTLE
+ jr z, .TrainerPP
+
+; Fill wild PP
ld hl, EnemyMonMoves
ld de, EnemyMonPP
- ld a, $05
- call $2d83
- jr .asm_3eaeb
-.asm_3ead9
- ld hl, $d29f
- ld a, [$d109]
- call Function3927
+ ld a, PREDEF_FILLPP
+ call Predef
+ jr .Finish
+
+.TrainerPP
+; Copy PP from the party struct
+ ld hl, OTPartyMon1PP
+ ld a, [CurPartyMon]
+ call GetPartyLocation
ld de, EnemyMonPP
- ld bc, $0004
+ ld bc, NUM_MOVES
call CopyBytes
-.asm_3eaeb
+
+.Finish
+; ????
ld hl, $d237
ld de, $d226
- ld b, $05
-.asm_3eaf3
+ ld b, 5 ; # bytes to copy
+; Copy $d237-a to $d226-9
+.loop
ld a, [hli]
ld [de], a
inc de
dec b
- jr nz, .asm_3eaf3
+ jr nz, .loop
+; Copy $d23f to $d22a
ld a, [$d23f]
ld [de], a
inc de
+; Copy $d240 to $d22b
ld a, [$d240]
ld [de], a
+; copy TempEnemyMonSpecies to $d265
ld a, [TempEnemyMonSpecies]
ld [$d265], a
+; ????
call $343b
- ld a, [$d22d]
+; If wild, we're done
+ ld a, [IsInBattle]
and a
ret z
- ld hl, $d073
- ld de, $c616
- ld bc, $000b
+; Update enemy nick
+ ld hl, StringBuffer1
+ ld de, EnemyMonNick
+ ld bc, PKMN_NAME_LENGTH
call CopyBytes
+; ????
ld a, [TempEnemyMonSpecies]
dec a
ld c, a
ld b, $01
ld hl, $deb9
- ld a, $03
- call $2d83
+ ld a, $03 ; PREDEF_
+ call Predef
+; Fill EnemyMon stats
ld hl, EnemyMonAtk
ld de, $c6c1
- ld bc, $000a
+ ld bc, 2*(NUM_STATS-1) ; 2 bytes for each non-HP stat
call CopyBytes
+; We're done
ret
; 3eb38
+
CheckSleepingTreeMon: ; 3eb38
+; Return carry if species is in the list
+; for the current time of day
+
+; Don't do anything if this isn't a tree encounter
ld a, [BattleType]
- cp a, $08 ; headbutt
- jr nz, .notsleeping
- ld hl, SleepingTreeMonMornTable
+ cp a, BATTLETYPE_TREE
+ jr nz, .NotSleeping
+
+; Get list for the time of day
+ ld hl, .Morn
ld a, [TimeOfDay]
- cp a, $01 ; day
- jr c, .check
- ld hl, SleepingTreeMonDayTable
- jr z, .check
- ld hl, SleepingTreeMonNiteTable
-.check
+ cp a, DAY
+ jr c, .Check
+ ld hl, .Day
+ jr z, .Check
+ ld hl, .Nite
+
+.Check
ld a, [TempEnemyMonSpecies]
- ld de, $0001
+ ld de, 1 ; length of species id
call IsInArray
+; If it's a match, the opponent is asleep
ret c
-.notsleeping
+
+.NotSleeping
and a
ret
-; 3eb5d
-SleepingTreeMonNiteTable: ; 3eb5d
+.Nite
db CATERPIE
db METAPOD
db BUTTERFREE
@@ -12827,9 +13144,8 @@
db LEDYBA
db AIPOM
db $ff ; end
-; 3eb69
-SleepingTreeMonDayTable: ; 3eb69
+.Day
db VENONAT
db HOOTHOOT
db NOCTOWL
@@ -12836,9 +13152,8 @@
db SPINARAK
db HERACROSS
db $ff ; end
-; 3eb6f
-SleepingTreeMonMornTable ; 3eb6f
+.Morn
db VENONAT
db HOOTHOOT
db NOCTOWL
@@ -12847,19 +13162,23 @@
db $ff ; end
; 3eb75
+
CheckUnownLetter: ; 3eb75
-; returns carry if not a valid letter
- ld a, [$def3]
+; Return carry if the Unown letter hasn't been unlocked yet
+ ld a, [$def3] ; UnownLetter
ld c, a
ld de, $0000
-.asm_3eb7c
- srl c ; bit 0 off?
- jr nc, .asm_3eb96
- ld hl, UnownLetterPointerTable
+.loop
+; Has this set been unlocked?
+ srl c
+ jr nc, .next
+; Check out the set
+ ld hl, .LetterSets
add hl, de
ld a, [hli]
ld h, [hl]
ld l, a
+; Is our letter in the set?
push de
ld a, [$d234]
ld de, $0001
@@ -12867,52 +13186,165 @@
call IsInArray
pop bc
pop de
- jr c, .end
-.asm_3eb96
+ jr c, .Match
+.next
+; Next set
inc e
inc e
ld a, e
- cp a, $08 ; has the end of the table been reached?
- jr c, .asm_3eb7c
+; Gone past the end of the table?
+ cp a, 4*2 ; 4 sets with 2-byte pointers
+ jr c, .loop
+
+; Didn't find the letter (not unlocked)
scf
ret
-.end
+
+.Match
+; Valid letter
and a
ret
-UnownLetterPointerTable: ; 3eba1
- dw UnownLetterTable
- dw UnownLetterTable2
- dw UnownLetterTable3
- dw UnownLetterTable4
-; 3eba9
+.LetterSets
+ dw .Set1
+ dw .Set2
+ dw .Set3
+ dw .Set4
-UnownLetterTable: ; 3eba9
+.Set1
; A B C D E F G H I J K
db $01, $02, $03, $04, $05, $06, $07, $08, $09, $0a, $0b
- db $ff
-; 3ebb5
+ db $ff ; end
-UnownLetterTable2: ; 3ebb5
+.Set2
; L M N O P Q R
db $0c, $0d, $0e, $0f, $10, $11, $12
- db $ff
-; 3ebbd
+ db $ff ; end
-UnownLetterTable3: ; 3ebbd
+.Set3
; S T U V W
db $13, $14, $15, $16, $17
- db $ff
-; 3ebc3
+ db $ff ; end
-UnownLetterTable4: ; 3ebc3
+.Set4
; X Y Z
db $18, $19, $1a
- db $ff
-;3ebc7
+ db $ff ; end
+; 3ebc7
-INCBIN "baserom.gbc",$3ebc7,$3fc8b - $3ebc7
+INCBIN "baserom.gbc", $3ebc7, $3edd8 - $3ebc7
+BattleRNG: ; 3edd8
+; If the normal RNG is used in a link battle it'll desync.
+; To circumvent this a shared PRNG is used instead.
+
+; But if we're in a non-link battle we're safe to use it
+ ld a, [InLinkBattle]
+ and a
+ jp z, RNG
+
+; The PRNG operates in streams of 8 values
+; The reasons for this are unknown
+
+; Which value are we trying to pull?
+ push hl
+ push bc
+ ld a, [LinkBattleRNCount]
+ ld c, a
+ ld b, $0
+ ld hl, LinkBattleRNs
+ add hl, bc
+ inc a
+ ld [LinkBattleRNCount], a
+
+; If we haven't hit the end yet, we're good
+ cp 9 ; Exclude last value. See the closing comment
+ ld a, [hl]
+ pop bc
+ pop hl
+ ret c
+
+
+; If we have, we have to generate new pseudorandom data
+; Instead of having multiple PRNGs, ten seeds are used
+ push hl
+ push bc
+ push af
+
+; Reset count to 0
+ xor a
+ ld [LinkBattleRNCount], a
+ ld hl, LinkBattleRNs
+ ld b, 10 ; number of seeds
+
+; Generate next number in the sequence for each seed
+; The algorithm takes the form *5 + 1 % 256
+.loop
+ ; get last #
+ ld a, [hl]
+
+ ; a * 5 + 1
+ ld c, a
+ add a
+ add a
+ add c
+ inc a
+
+ ; update #
+ ld [hli], a
+ dec b
+ jr nz, .loop
+
+; This has the side effect of pulling the last value first,
+; then wrapping around. As a result, when we check to see if
+; we've reached the end, we have to take this into account.
+ pop af
+ pop bc
+ pop hl
+ ret
+; 3ee0f
+
+INCBIN "baserom.gbc", $3ee0f, $3fa01 - $3ee0f
+
+GetRoamMonHP: ; 3fa01
+; output: hl = RoamMonCurHP
+ ld a, [TempEnemyMonSpecies]
+ ld b, a
+ ld a, [RoamMon1Species]
+ cp b
+ ld hl, RoamMon1CurHP
+ ret z
+ ld a, [RoamMon2Species]
+ cp b
+ ld hl, RoamMon2CurHP
+ ret z
+; remnant of the GS function
+; we know this will be $00 because it's never initialized
+ ld hl, RoamMon3CurHP
+ ret
+; 3fa19
+
+GetRoamMonDVs: ; 3fa19
+; output: hl = RoamMonDVs
+ ld a, [TempEnemyMonSpecies]
+ ld b, a
+ ld a, [RoamMon1Species]
+ cp b
+ ld hl, RoamMon1DVs
+ ret z
+ ld a, [RoamMon2Species]
+ cp b
+ ld hl, RoamMon2DVs
+ ret z
+; remnant of the GS function
+; we know this will be $0000 because it's never initialized
+ ld hl, RoamMon3DVs
+ ret
+; 3fa31
+
+
+INCBIN "baserom.gbc", $3fa31, $3fc8b - $3fa31
+
; I have no clue what most of this does
BattleStartMessage:
@@ -18123,6 +18555,9 @@
db "DARK@"
INCBIN "baserom.gbc",$50A28, $51424 - $50A28
+
+
+BaseStats:
BulbasaurBaseStats: ; 0x51424
db BULBASAUR ; 001
--- a/wram.asm
+++ b/wram.asm
@@ -539,7 +539,7 @@
BattleScriptBufferLocHi: ; c6b3
ds 1
- ds 24
+ ds 25
PlayerStatLevels: ; c6cc
; 07 neutral