shithub: pokecrystal

Download patch

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