summaryrefslogtreecommitdiff
path: root/engine
diff options
context:
space:
mode:
authorentrpntr <entrpntr@gmail.com>2020-05-01 02:18:33 -0400
committerentrpntr <entrpntr@gmail.com>2020-05-01 02:18:33 -0400
commit2bc79616c484472021ca95c56f759f4e4ddb6d6d (patch)
treee49a97d22eb529e0be89d2ee8f5d8a1270559430 /engine
parent03bc01bf4efea42661600089af1a8ff787a093a5 (diff)
Add engine/overworld/wildmons.asm (without data files).
Diffstat (limited to 'engine')
-rw-r--r--engine/overworld/wildmons.asm971
1 files changed, 971 insertions, 0 deletions
diff --git a/engine/overworld/wildmons.asm b/engine/overworld/wildmons.asm
new file mode 100644
index 00000000..4be921fd
--- /dev/null
+++ b/engine/overworld/wildmons.asm
@@ -0,0 +1,971 @@
+LoadWildMonData:
+ call _GrassWildmonLookup
+ jr c, .copy
+ ld hl, wMornEncounterRate
+ xor a
+ ld [hli], a
+ ld [hli], a
+ ld [hl], a
+ jr .done_copy
+
+.copy
+ inc hl
+ inc hl
+ ld de, wMornEncounterRate
+ ld bc, 3
+ call CopyBytes
+.done_copy
+ call _WaterWildmonLookup
+ ld a, 0
+ jr nc, .no_copy
+ inc hl
+ inc hl
+ ld a, [hl]
+.no_copy
+ ld [wWaterEncounterRate], a
+ ret
+
+FindNest:
+; Parameters:
+; e: 0 = Johto, 1 = Kanto
+; wNamedObjectIndexBuffer: species
+ hlcoord 0, 0
+ ld bc, SCREEN_WIDTH * SCREEN_HEIGHT
+ xor a
+ call ByteFill
+ ld a, e
+ and a
+ jr nz, .kanto
+ decoord 0, 0
+ ld hl, JohtoGrassWildMons
+ call .FindGrass
+ ld hl, JohtoWaterWildMons
+ call .FindWater
+ call .RoamMon1
+ call .RoamMon2
+ call .RoamMon3
+ ret
+
+.kanto
+ decoord 0, 0
+ ld hl, KantoGrassWildMons
+ call .FindGrass
+ ld hl, KantoWaterWildMons
+ jp .FindWater
+
+.FindGrass:
+ ld a, [hl]
+ cp -1
+ ret z
+ push hl
+ ld a, [hli]
+ ld b, a
+ ld a, [hli]
+ ld c, a
+ inc hl
+ inc hl
+ inc hl
+ ld a, NUM_GRASSMON * 3
+ call .SearchMapForMon
+ jr nc, .next_grass
+ ld [de], a
+ inc de
+
+.next_grass
+ pop hl
+ ld bc, GRASS_WILDDATA_LENGTH
+ add hl, bc
+ jr .FindGrass
+
+.FindWater:
+ ld a, [hl]
+ cp -1
+ ret z
+ push hl
+ ld a, [hli]
+ ld b, a
+ ld a, [hli]
+ ld c, a
+ inc hl
+ ld a, NUM_WATERMON
+ call .SearchMapForMon
+ jr nc, .next_water
+ ld [de], a
+ inc de
+
+.next_water
+ pop hl
+ ld bc, WATER_WILDDATA_LENGTH
+ add hl, bc
+ jr .FindWater
+
+.SearchMapForMon:
+ inc hl
+.ScanMapLoop:
+ push af
+ ld a, [wNamedObjectIndexBuffer]
+ cp [hl]
+ jr z, .found
+ inc hl
+ inc hl
+ pop af
+ dec a
+ jr nz, .ScanMapLoop
+ and a
+ ret
+
+.found
+ pop af
+ jp .AppendNest
+
+.AppendNest:
+ push de
+ call GetWorldMapLocation
+ ld c, a
+ hlcoord 0, 0
+ ld de, SCREEN_WIDTH * SCREEN_HEIGHT
+.AppendNestLoop:
+ ld a, [hli]
+ cp c
+ jr z, .found_nest
+ dec de
+ ld a, e
+ or d
+ jr nz, .AppendNestLoop
+ ld a, c
+ pop de
+ scf
+ ret
+
+.found_nest
+ pop de
+ and a
+ ret
+
+.RoamMon1:
+ ld a, [wRoamMon1Species]
+ ld b, a
+ ld a, [wNamedObjectIndexBuffer]
+ cp b
+ ret nz
+ ld a, [wRoamMon1MapGroup]
+ ld b, a
+ ld a, [wRoamMon1MapNumber]
+ ld c, a
+ call .AppendNest
+ ret nc
+ ld [de], a
+ inc de
+ ret
+
+.RoamMon2:
+ ld a, [wRoamMon2Species]
+ ld b, a
+ ld a, [wNamedObjectIndexBuffer]
+ cp b
+ ret nz
+ ld a, [wRoamMon2MapGroup]
+ ld b, a
+ ld a, [wRoamMon2MapNumber]
+ ld c, a
+ call .AppendNest
+ ret nc
+ ld [de], a
+ inc de
+ ret
+
+.RoamMon3:
+ ld a, [wRoamMon3Species]
+ ld b, a
+ ld a, [wNamedObjectIndexBuffer]
+ cp b
+ ret nz
+ ld a, [wRoamMon3MapGroup]
+ ld b, a
+ ld a, [wRoamMon3MapNumber]
+ ld c, a
+ call .AppendNest
+ ret nc
+ ld [de], a
+ inc de
+ ret
+
+TryWildEncounter::
+; Try to trigger a wild encounter.
+ call .EncounterRate
+ jr nc, .no_battle
+ call ChooseWildEncounter
+ jr nz, .no_battle
+ call CheckRepelEffect
+ jr nc, .no_battle
+ xor a
+ ret
+
+.no_battle
+ xor a ; BATTLETYPE_NORMAL
+ ld [wTempWildMonSpecies], a
+ ld [wBattleType], a
+ ld a, 1
+ and a
+ ret
+
+.EncounterRate:
+ call GetMapEncounterRate
+ call ApplyMusicEffectOnEncounterRate
+ call ApplyCleanseTagEffectOnEncounterRate
+ call Random
+ cp b
+ ret
+
+GetMapEncounterRate:
+ ld hl, wMornEncounterRate
+ call CheckOnWater
+ ld a, wWaterEncounterRate - wMornEncounterRate
+ jr z, .ok
+ ld a, [wTimeOfDay]
+.ok
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ld b, [hl]
+ ret
+
+ApplyMusicEffectOnEncounterRate::
+; Pokemon March and Ruins of Alph signal double encounter rate.
+; Pokemon Lullaby halves encounter rate.
+ ld a, [wMapMusic]
+ cp MUSIC_POKEMON_MARCH
+ jr z, .double
+ cp MUSIC_RUINS_OF_ALPH_RADIO
+ jr z, .double
+ cp MUSIC_POKEMON_LULLABY
+ ret nz
+ srl b
+ ret
+
+.double
+ sla b
+ ret
+
+ApplyCleanseTagEffectOnEncounterRate::
+; Cleanse Tag halves encounter rate.
+ ld hl, wPartyMon1Item
+ ld de, PARTYMON_STRUCT_LENGTH
+ ld a, [wPartyCount]
+ ld c, a
+.loop
+ ld a, [hl]
+ cp CLEANSE_TAG
+ jr z, .cleansetag
+ add hl, de
+ dec c
+ jr nz, .loop
+ ret
+
+.cleansetag
+ srl b
+ ret
+
+ChooseWildEncounter:
+ call LoadWildMonDataPointer
+ jp nc, .nowildbattle
+ call CheckEncounterRoamMon
+ jp c, .startwildbattle
+
+ inc hl
+ inc hl
+ inc hl
+ call CheckOnWater
+ ld de, WaterMonProbTable
+ jr z, .watermon
+ inc hl
+ inc hl
+ ld a, [wTimeOfDay]
+ ld bc, NUM_GRASSMON * 2
+ call AddNTimes
+ ld de, GrassMonProbTable
+
+.watermon
+; hl contains the pointer to the wild mon data, let's save that to the stack
+ push hl
+.randomloop
+ call Random
+ cp 100
+ jr nc, .randomloop
+ inc a ; 1 <= a <= 100
+ ld b, a
+ ld h, d
+ ld l, e
+; This next loop chooses which mon to load up.
+.prob_bracket_loop
+ ld a, [hli]
+ cp b
+ jr nc, .got_it
+ inc hl
+ jr .prob_bracket_loop
+
+.got_it
+ ld c, [hl]
+ ld b, 0
+ pop hl
+ add hl, bc ; this selects our mon
+ ld a, [hli]
+ ld b, a
+; If the Pokemon is encountered by surfing, we need to give the levels some variety.
+ call CheckOnWater
+ jr nz, .ok
+; Check if we buff the wild mon, and by how much.
+ call Random
+ cp 35 percent
+ jr c, .ok
+ inc b
+ cp 65 percent
+ jr c, .ok
+ inc b
+ cp 85 percent
+ jr c, .ok
+ inc b
+ cp 95 percent
+ jr c, .ok
+ inc b
+; Store the level
+.ok
+ ld a, b
+ ld [wCurPartyLevel], a
+ ld b, [hl]
+ ; ld a, b
+ call ValidateTempWildMonSpecies
+ jr c, .nowildbattle
+
+ ld a, b ; This is in the wrong place.
+ cp UNOWN
+ jr nz, .done
+
+ ld a, [wUnlockedUnowns]
+ and a
+ jr z, .nowildbattle
+
+.done
+ jr .loadwildmon
+
+.nowildbattle
+ ld a, 1
+ and a
+ ret
+
+.loadwildmon
+ ld a, b
+ ld [wTempWildMonSpecies], a
+
+.startwildbattle
+ xor a
+ ret
+
+INCLUDE "data/wild/probabilities.asm"
+
+CheckRepelEffect::
+; If there is no active Repel, there's no need to be here.
+ ld a, [wRepelEffect]
+ and a
+ jr z, .encounter
+; Get the first Pokemon in your party that isn't fainted.
+ ld hl, wPartyMon1HP
+ ld bc, PARTYMON_STRUCT_LENGTH - 1
+.loop
+ ld a, [hli]
+ or [hl]
+ jr nz, .ok
+ add hl, bc
+ jr .loop
+
+.ok
+; to PartyMonLevel
+rept 4
+ dec hl
+endr
+
+ ld a, [wCurPartyLevel]
+ cp [hl]
+ jr nc, .encounter
+ and a
+ ret
+
+.encounter
+ scf
+ ret
+
+LoadWildMonDataPointer:
+ call CheckOnWater
+ jr z, _WaterWildmonLookup
+
+_GrassWildmonLookup:
+ ld hl, SwarmGrassWildMons
+ ld bc, GRASS_WILDDATA_LENGTH
+ call _SwarmWildmonCheck
+ ret c
+ ld hl, JohtoGrassWildMons
+ ld de, KantoGrassWildMons
+ call _JohtoWildmonCheck
+ ld bc, GRASS_WILDDATA_LENGTH
+ jr _NormalWildmonOK
+
+_WaterWildmonLookup:
+ ld hl, SwarmWaterWildMons
+ ld bc, WATER_WILDDATA_LENGTH
+ call _SwarmWildmonCheck
+ ret c
+ ld hl, JohtoWaterWildMons
+ ld de, KantoWaterWildMons
+ call _JohtoWildmonCheck
+ ld bc, WATER_WILDDATA_LENGTH
+ jr _NormalWildmonOK
+
+_JohtoWildmonCheck:
+ call IsInJohto
+ and a
+ ret z
+ ld h, d
+ ld l, e
+ ret
+
+_SwarmWildmonCheck:
+ call CopyCurrMapDE
+ ld a, [wDunsparceMapGroup]
+ cp d
+ jr nz, _NoSwarmWildmon
+ ld a, [wDunsparceMapNumber]
+ cp e
+ jr nz, _NoSwarmWildmon
+ call LookUpWildmonsForMapDE
+ jr nc, _NoSwarmWildmon
+ scf
+ ret
+
+_NoSwarmWildmon:
+ and a
+ ret
+
+_NormalWildmonOK:
+ call CopyCurrMapDE
+ jr LookUpWildmonsForMapDE
+
+CopyCurrMapDE:
+ ld a, [wMapGroup]
+ ld d, a
+ ld a, [wMapNumber]
+ ld e, a
+ ret
+
+LookUpWildmonsForMapDE:
+.loop
+ push hl
+ ld a, [hl]
+ inc a
+ jr z, .nope
+ ld a, d
+ cp [hl]
+ jr nz, .next
+ inc hl
+ ld a, e
+ cp [hl]
+ jr z, .yup
+
+.next
+ pop hl
+ add hl, bc
+ jr .loop
+
+.nope
+ pop hl
+ and a
+ ret
+
+.yup
+ pop hl
+ scf
+ ret
+
+InitRoamMons:
+; initialize wRoamMon structs
+
+; species
+ ld a, RAIKOU
+ ld [wRoamMon1Species], a
+ ld a, ENTEI
+ ld [wRoamMon2Species], a
+ ld a, SUICUNE
+ ld [wRoamMon3Species], a
+
+; level
+ ld a, 40
+ ld [wRoamMon1Level], a
+ ld [wRoamMon2Level], a
+ ld [wRoamMon3Level], a
+
+; raikou starting map
+ ld a, GROUP_ROUTE_42
+ ld [wRoamMon1MapGroup], a
+ ld a, MAP_ROUTE_42
+ ld [wRoamMon1MapNumber], a
+
+; entei starting map
+ ld a, GROUP_ROUTE_37
+ ld [wRoamMon2MapGroup], a
+ ld a, MAP_ROUTE_37
+ ld [wRoamMon2MapNumber], a
+
+; suicune starting map
+ ld a, GROUP_ROUTE_38
+ ld [wRoamMon3MapGroup], a
+ ld a, MAP_ROUTE_38
+ ld [wRoamMon3MapNumber], a
+
+; hp
+ xor a ; generate new stats
+ ld [wRoamMon1HP], a
+ ld [wRoamMon2HP], a
+ ld [wRoamMon3HP], a
+
+ ret
+
+CheckEncounterRoamMon:
+ push hl
+; Don't trigger an encounter if we're on water.
+ call CheckOnWater
+ jr z, .DontEncounterRoamMon
+; Load the current map group and number to de
+ call CopyCurrMapDE
+; Randomly select a beast.
+ call Random
+ cp 100 ; 25/64 chance
+ jr nc, .DontEncounterRoamMon
+ and %00000011 ; Of that, a 3/4 chance. Running total: 75/256, or around 29.3%.
+ jr z, .DontEncounterRoamMon
+ dec a ; 1/3 chance that it's Entei, 1/3 chance that it's Raikou, 1/3 chance that it's Suicune
+; Compare its current location with yours
+ ld hl, wRoamMon1MapGroup
+ ld c, a
+ ld b, 0
+ ld a, 7 ; length of the roam_struct
+ call AddNTimes
+ ld a, d
+ cp [hl]
+ jr nz, .DontEncounterRoamMon
+ inc hl
+ ld a, e
+ cp [hl]
+ jr nz, .DontEncounterRoamMon
+; We've decided to take on a beast, so stage its information for battle.
+ dec hl
+ dec hl
+ dec hl
+ ld a, [hli]
+ ld [wTempWildMonSpecies], a
+ ld a, [hl]
+ ld [wCurPartyLevel], a
+ ld a, BATTLETYPE_ROAMING
+ ld [wBattleType], a
+
+ pop hl
+ scf
+ ret
+
+.DontEncounterRoamMon:
+ pop hl
+ and a
+ ret
+
+UpdateRoamMons:
+ ld a, [wRoamMon1MapGroup]
+ cp GROUP_N_A
+ jr z, .SkipRaikou
+ ld b, a
+ ld a, [wRoamMon1MapNumber]
+ ld c, a
+ call .Update
+ ld a, b
+ ld [wRoamMon1MapGroup], a
+ ld a, c
+ ld [wRoamMon1MapNumber], a
+
+.SkipRaikou:
+ ld a, [wRoamMon2MapGroup]
+ cp GROUP_N_A
+ jr z, .SkipEntei
+ ld b, a
+ ld a, [wRoamMon2MapNumber]
+ ld c, a
+ call .Update
+ ld a, b
+ ld [wRoamMon2MapGroup], a
+ ld a, c
+ ld [wRoamMon2MapNumber], a
+
+.SkipEntei:
+ ld a, [wRoamMon3MapGroup]
+ cp GROUP_N_A
+ jr z, .Finished
+ ld b, a
+ ld a, [wRoamMon3MapNumber]
+ ld c, a
+ call .Update
+ ld a, b
+ ld [wRoamMon3MapGroup], a
+ ld a, c
+ ld [wRoamMon3MapNumber], a
+
+.Finished:
+ jp _BackUpMapIndices
+
+.Update:
+ ld hl, RoamMaps
+.loop
+; Are we at the end of the table?
+ ld a, [hl]
+ cp -1
+ ret z
+; Is this the correct entry?
+ ld a, b
+ cp [hl]
+ jr nz, .next
+ inc hl
+ ld a, c
+ cp [hl]
+ jr z, .yes
+; We don't have the correct entry yet, so let's continue. A 0 terminates each entry.
+.next
+ ld a, [hli]
+ and a
+ jr nz, .next
+ jr .loop
+
+; We have the correct entry now, so let's choose a random map from it.
+.yes
+ inc hl
+ ld d, h
+ ld e, l
+.update_loop
+ ld h, d
+ ld l, e
+; Choose which map to warp to.
+ call Random
+ and %00011111 ; 1/8n chance it moves to a completely random map, where n is the number of roaming connections from the current map.
+ jr z, JumpRoamMon
+ and %11
+ cp [hl]
+ jr nc, .update_loop ; invalid index, try again
+ inc hl
+ ld c, a
+ ld b, 0
+ add hl, bc
+ add hl, bc
+ ld a, [wRoamMons_LastMapGroup]
+ cp [hl]
+ jr nz, .done
+ inc hl
+ ld a, [wRoamMons_LastMapNumber]
+ cp [hl]
+ jr z, .update_loop
+ dec hl
+
+.done
+ ld a, [hli]
+ ld b, a
+ ld c, [hl]
+ ret
+
+JumpRoamMons:
+ ld a, [wRoamMon1MapGroup]
+ cp GROUP_N_A
+ jr z, .SkipRaikou
+ call JumpRoamMon
+ ld a, b
+ ld [wRoamMon1MapGroup], a
+ ld a, c
+ ld [wRoamMon1MapNumber], a
+
+.SkipRaikou:
+ ld a, [wRoamMon2MapGroup]
+ cp GROUP_N_A
+ jr z, .SkipEntei
+ call JumpRoamMon
+ ld a, b
+ ld [wRoamMon2MapGroup], a
+ ld a, c
+ ld [wRoamMon2MapNumber], a
+
+.SkipEntei:
+ ld a, [wRoamMon3MapGroup]
+ cp GROUP_N_A
+ jr z, .Finished
+ call JumpRoamMon
+ ld a, b
+ ld [wRoamMon3MapGroup], a
+ ld a, c
+ ld [wRoamMon3MapNumber], a
+
+.Finished:
+ jp _BackUpMapIndices
+
+JumpRoamMon:
+.loop
+ ld hl, RoamMaps
+.innerloop1 ; This loop happens to be unnecessary.
+ call Random ; Choose a random number.
+ maskbits NUM_ROAMMON_MAPS ; Mask the number to limit it between 0 and 15.
+ cp NUM_ROAMMON_MAPS ; If the number is not less than 16, try again.
+ jr nc, .innerloop1 ; I'm sure you can guess why this check is bogus.
+ inc a
+ ld b, a
+.innerloop2 ; Loop to get hl to the address of the chosen roam map.
+ dec b
+ jr z, .ok
+.innerloop3 ; Loop to skip the current roam map, which is terminated by a 0.
+ ld a, [hli]
+ and a
+ jr nz, .innerloop3
+ jr .innerloop2
+; Check to see if the selected map is the one the player is currently in. If so, try again.
+.ok
+ ld a, [wMapGroup]
+ cp [hl]
+ jr nz, .done
+ inc hl
+ ld a, [wMapNumber]
+ cp [hl]
+ jr z, .loop
+ dec hl
+; Return the map group and number in bc.
+.done
+ ld a, [hli]
+ ld b, a
+ ld c, [hl]
+ ret
+
+_BackUpMapIndices:
+ ld a, [wRoamMons_CurMapNumber]
+ ld [wRoamMons_LastMapNumber], a
+ ld a, [wRoamMons_CurMapGroup]
+ ld [wRoamMons_LastMapGroup], a
+ ld a, [wMapNumber]
+ ld [wRoamMons_CurMapNumber], a
+ ld a, [wMapGroup]
+ ld [wRoamMons_CurMapGroup], a
+ ret
+
+INCLUDE "data/wild/roammon_maps.asm"
+
+ValidateTempWildMonSpecies:
+; Due to a development oversight, this function is called with the wild Pokemon's level, not its species, in a.
+ and a
+ jr z, .nowildmon ; = 0
+ cp NUM_POKEMON + 1 ; 252
+ jr nc, .nowildmon ; >= 252
+ and a ; 1 <= Species <= 251
+ ret
+
+.nowildmon
+ scf
+ ret
+
+; Finds a rare wild Pokemon in the route of the trainer calling, then checks if it's been Seen already.
+; The trainer will then tell you about the Pokemon if you haven't seen it.
+RandomUnseenWildMon:
+ farcall GetCallerLocation
+ ld d, b
+ ld e, c
+ ld hl, JohtoGrassWildMons
+ ld bc, GRASS_WILDDATA_LENGTH
+ call LookUpWildmonsForMapDE
+ jr c, .GetGrassmon
+ ld hl, KantoGrassWildMons
+ call LookUpWildmonsForMapDE
+ jr nc, .done
+
+.GetGrassmon:
+ push hl
+ ld bc, 5 + 4 * 2 ; Location of the level of the 5th wild Pokemon in that map
+ add hl, bc
+ ld a, [wTimeOfDay]
+ ld bc, NUM_GRASSMON * 2
+ call AddNTimes
+.randloop1
+ call Random
+ and %11
+ jr z, .randloop1
+ dec a
+ ld c, a
+ ld b, 0
+ add hl, bc
+ add hl, bc
+; We now have the pointer to one of the last (rarest) three wild Pokemon found in that area.
+ inc hl
+ ld c, [hl] ; Contains the species index of this rare Pokemon
+ pop hl
+ ld de, 5 + 0 * 2
+ add hl, de
+ inc hl ; Species index of the most common Pokemon on that route
+ ld b, 4
+.loop2
+ ld a, [hli]
+ cp c ; Compare this most common Pokemon with the rare one stored in c.
+ jr z, .done
+ inc hl
+ dec b
+ jr nz, .loop2
+; This Pokemon truly is rare.
+ push bc
+ dec c
+ ld a, c
+ call CheckSeenMon
+ pop bc
+ jr nz, .done
+; Since we haven't seen it, have the caller tell us about it.
+ ld de, wStringBuffer1
+ call CopyName1
+ ld a, c
+ ld [wNamedObjectIndexBuffer], a
+ call GetPokemonName
+ ld hl, .JustSawSomeRareMonText
+ call PrintText
+ xor a
+ ld [wScriptVar], a
+ ret
+
+.done
+ ld a, $1
+ ld [wScriptVar], a
+ ret
+
+.JustSawSomeRareMonText:
+ text_far _JustSawSomeRareMonText
+ text_end
+
+RandomPhoneWildMon:
+ farcall GetCallerLocation
+ ld d, b
+ ld e, c
+ ld hl, JohtoGrassWildMons
+ ld bc, GRASS_WILDDATA_LENGTH
+ call LookUpWildmonsForMapDE
+ jr c, .ok
+ ld hl, KantoGrassWildMons
+ call LookUpWildmonsForMapDE
+
+.ok
+ ld bc, 5 + 0 * 2
+ add hl, bc
+ ld a, [wTimeOfDay]
+ inc a
+ ld bc, NUM_GRASSMON * 2
+.loop
+ dec a
+ jr z, .done
+ add hl, bc
+ jr .loop
+
+.done
+ call Random
+ and %11
+ ld c, a
+ ld b, 0
+ add hl, bc
+ add hl, bc
+ inc hl
+ ld a, [hl]
+ ld [wNamedObjectIndexBuffer], a
+ call GetPokemonName
+ ld hl, wStringBuffer1
+ ld de, wStringBuffer4
+ ld bc, MON_NAME_LENGTH
+ jp CopyBytes
+
+RandomPhoneMon:
+; Get a random monster owned by the trainer who's calling.
+ farcall GetCallerLocation
+ ld hl, TrainerGroups
+ ld a, d
+ dec a
+ ld c, a
+ ld b, 0
+ add hl, bc
+ add hl, bc
+ ld a, BANK(TrainerGroups)
+ call GetFarHalfword
+
+.skip_trainer
+ dec e
+ jr z, .skipped
+.skip
+ ld a, BANK(Trainers)
+ call GetFarByte
+ inc hl
+ cp -1
+ jr nz, .skip
+ jr .skip_trainer
+.skipped
+
+.skip_name
+ ld a, BANK(Trainers)
+ call GetFarByte
+ inc hl
+ cp "@"
+ jr nz, .skip_name
+
+ ld a, BANK(Trainers)
+ call GetFarByte
+ inc hl
+ ld bc, 2 ; level, species
+ cp TRAINERTYPE_NORMAL
+ jr z, .got_mon_length
+ ld bc, 2 + NUM_MOVES ; level, species, moves
+ cp TRAINERTYPE_MOVES
+ jr z, .got_mon_length
+ ld bc, 2 + 1 ; level, species, item
+ cp TRAINERTYPE_ITEM
+ jr z, .got_mon_length
+ ; TRAINERTYPE_ITEM_MOVES
+ ld bc, 2 + 1 + NUM_MOVES ; level, species, item, moves
+.got_mon_length
+
+ ld e, 0
+ push hl
+.count_mon
+ inc e
+ add hl, bc
+ ld a, BANK(Trainers)
+ call GetFarByte
+ cp -1
+ jr nz, .count_mon
+ pop hl
+
+.rand
+ call Random
+ maskbits PARTY_LENGTH
+ cp e
+ jr nc, .rand
+
+ inc a
+.get_mon
+ dec a
+ jr z, .got_mon
+ add hl, bc
+ jr .get_mon
+.got_mon
+
+ inc hl ; species
+ ld a, BANK(Trainers)
+ call GetFarByte
+ ld [wNamedObjectIndexBuffer], a
+ call GetPokemonName
+ ld hl, wStringBuffer1
+ ld de, wStringBuffer4
+ ld bc, MON_NAME_LENGTH
+ jp CopyBytes
+
+;INCLUDE "data/wild/johto_grass.asm"
+;INCLUDE "data/wild/johto_water.asm"
+;INCLUDE "data/wild/kanto_grass.asm"
+;INCLUDE "data/wild/kanto_water.asm"
+;INCLUDE "data/wild/swarm_grass.asm"
+;INCLUDE "data/wild/swarm_water.asm"