pokecrystal/engine/wildmons.asm

1011 lines
16 KiB
NASM
Executable File

LoadWildMonData: ; 29ff8
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: ; 2a01f
; 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
ret
.kanto
decoord 0, 0
ld hl, KantoGrassWildMons
call .FindGrass
ld hl, KantoWaterWildMons
jp .FindWater
; 2a052
.FindGrass: ; 2a052
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
; 2a06e
.FindWater: ; 2a06e
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, 3
call .SearchMapForMon
jr nc, .next_water
ld [de], a
inc de
.next_water
pop hl
ld bc, 3 * 3
add hl, bc
jr .FindWater
; 2a088
.SearchMapForMon: ; 2a088
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
; 2a09c
.AppendNest: ; 2a09c
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
; 2a0b7
.RoamMon1: ; 2a0b7
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
; 2a0cf
.RoamMon2: ; 2a0cf
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
; 2a0e7
TryWildEncounter:: ; 2a0e7
; 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 [TempWildMonSpecies], a
ld [BattleType], a
ld a, 1
and a
ret
; 2a103
.EncounterRate: ; 2a103
call GetMapEncounterRate
call ApplyMusicEffectOnEncounterRate
call ApplyCleanseTagEffectOnEncounterRate
call Random
cp b
ret
; 2a111
GetMapEncounterRate: ; 2a111
ld hl, wMornEncounterRate
call CheckOnWater
ld a, wWaterEncounterRate - wMornEncounterRate
jr z, .ok
ld a, [TimeOfDay]
.ok
ld c, a
ld b, 0
add hl, bc
ld b, [hl]
ret
; 2a124
ApplyMusicEffectOnEncounterRate:: ; 2a124
; 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
; 2a138
ApplyCleanseTagEffectOnEncounterRate:: ; 2a138
; Cleanse Tag halves encounter rate.
ld hl, PartyMon1Item
ld de, PARTYMON_STRUCT_LENGTH
ld a, [PartyCount]
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
; 2a14f
ChooseWildEncounter: ; 2a14f
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, [TimeOfDay]
ld bc, $e
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 [CurPartyLevel], 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 [TempWildMonSpecies], a
.startwildbattle
xor a
ret
; 2a1cb
INCLUDE "data/wild/probabilities.asm"
CheckRepelEffect:: ; 2a1df
; 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, PartyMon1HP
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, [CurPartyLevel]
cp [hl]
jr nc, .encounter
and a
ret
.encounter
scf
ret
; 2a200
LoadWildMonDataPointer: ; 2a200
call CheckOnWater
jr z, _WaterWildmonLookup
_GrassWildmonLookup: ; 2a205
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: ; 2a21d
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
push hl
ld hl, wSwarmFlags
bit SWARMFLAGS_DUNSPARCE_SWARM_F, [hl]
pop hl
jr z, .CheckYanma
ld a, [wDunsparceMapGroup]
cp d
jr nz, .CheckYanma
ld a, [wDunsparceMapNumber]
cp e
jr nz, .CheckYanma
call LookUpWildmonsForMapDE
jr nc, _NoSwarmWildmon
scf
ret
.CheckYanma:
push hl
ld hl, wSwarmFlags
bit SWARMFLAGS_YANMA_SWARM_F, [hl]
pop hl
jr z, _NoSwarmWildmon
ld a, [wYanmaMapGroup]
cp d
jr nz, _NoSwarmWildmon
ld a, [wYanmaMapNumber]
cp e
jr nz, _NoSwarmWildmon
call LookUpWildmonsForMapDE
jr nc, _NoSwarmWildmon
scf
ret
_NoSwarmWildmon
and a
ret
_NormalWildmonOK
call CopyCurrMapDE
jr LookUpWildmonsForMapDE
; 2a27f
CopyCurrMapDE: ; 2a27f
ld a, [MapGroup]
ld d, a
ld a, [MapNumber]
ld e, a
ret
; 2a288
LookUpWildmonsForMapDE: ; 2a288
.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
; 2a2a0
Special_InitRoamMons: ; 2a2a0
; 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
; 2a2ce
CheckEncounterRoamMon: ; 2a2ce
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
; 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 [TempWildMonSpecies], a
ld a, [hl]
ld [CurPartyLevel], a
ld a, BATTLETYPE_ROAMING
ld [BattleType], a
pop hl
scf
ret
.DontEncounterRoamMon:
pop hl
and a
ret
; 2a30d
UpdateRoamMons: ; 2a30d
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, .SkipSuicune
ld b, a
ld a, [wRoamMon3MapNumber]
ld c, a
call .Update
ld a, b
ld [wRoamMon3MapGroup], a
ld a, c
ld [wRoamMon3MapNumber], a
.SkipSuicune:
jp _BackUpMapIndices
; 2a355
.Update: ; 2a355
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: ; 2a394
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, .SkipSuicune
call JumpRoamMon
ld a, b
ld [wRoamMon3MapGroup], a
ld a, c
ld [wRoamMon3MapNumber], a
.SkipSuicune:
jp _BackUpMapIndices
JumpRoamMon: ; 2a3cd
.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, [MapGroup]
cp [hl]
jr nz, .done
inc hl
ld a, [MapNumber]
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
; 2a3f6
_BackUpMapIndices: ; 2a3f6
ld a, [wRoamMons_CurrentMapNumber]
ld [wRoamMons_LastMapNumber], a
ld a, [wRoamMons_CurrentMapGroup]
ld [wRoamMons_LastMapGroup], a
ld a, [MapNumber]
ld [wRoamMons_CurrentMapNumber], a
ld a, [MapGroup]
ld [wRoamMons_CurrentMapGroup], a
ret
; 2a40f
INCLUDE "data/wild/roammon_maps.asm"
ValidateTempWildMonSpecies: ; 2a4a0
; 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
; 2a4ab
; 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.
Special_RandomUnseenWildMon: ; 2a4ab
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, [TimeOfDay]
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, StringBuffer1
call CopyName1
ld a, c
ld [wNamedObjectIndexBuffer], a
call GetPokemonName
ld hl, .SawRareMonText
call PrintText
xor a
ld [ScriptVar], a
ret
.done
ld a, $1
ld [ScriptVar], a
ret
.SawRareMonText:
; I just saw some rare @ in @ . I'll call you if I see another rare #MON, OK?
text_jump UnknownText_0x1bd34b
db "@"
; 0x2a51f
Special_RandomPhoneWildMon: ; 2a51f
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, [TimeOfDay]
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, StringBuffer1
ld de, StringBuffer4
ld bc, MON_NAME_LENGTH
jp CopyBytes
; 2a567
Special_RandomPhoneMon: ; 2a567
; 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, StringBuffer1
ld de, StringBuffer4
ld bc, MON_NAME_LENGTH
jp CopyBytes
; 2a5e9
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"