Use explicit SRAM sentinel for catch-flag initialization

This commit is contained in:
robjlyons 2026-02-10 16:45:09 +00:00
parent bc2354dd66
commit ad5a0ebea2
9 changed files with 273 additions and 54 deletions

View File

@ -13,6 +13,14 @@ _PokemonFaintedText::
line "fainted!"
done
_PokemonRanAwayBadTrainingText::
text_ram wNameBuffer
text_start
line "ran away due"
cont "to your bad"
cont "training!"
prompt
_PlayerBlackedOutText::
text "<PLAYER> is out of"
line "useable #MON!"

View File

@ -218,6 +218,13 @@ _ThrowBallAtTrainerMonText2::
text "Don't be a thief!"
prompt
_ThrowBallAtUsedEncounterText::
text "You have already"
line "tried here. Try"
cont "a different"
cont "route."
prompt
_NoCyclingAllowedHereText::
text "No cycling"
next "allowed here."

View File

@ -137,6 +137,7 @@ StartBattle:
ld [wPartyGainExpFlags], a
ld [wPartyFoughtCurrentEnemyFlags], a
ld [wActionResultOrTookBattleTurn], a
ld [wWildEncounterCanCatch], a
inc a
ld [wFirstMonsNotOutYet], a
ld hl, wEnemyMon1HP
@ -152,6 +153,26 @@ StartBattle:
.foundFirstAliveEnemyMon
ld a, d
ld [wSerialExchangeNybbleReceiveData], a
ld a, [wIsInBattle]
dec a ; is it a wild battle?
jr nz, .skipWildCatchInit
ld a, [wBattleType]
and a ; is it a normal battle?
jr nz, .skipWildCatchInit
call EnsureEncounterCatchFlagsInitialized
call EnableEncounterCatchSRAM_Battle
ld a, [wCurMap]
ld c, a
ld b, FLAG_TEST
ld hl, sMapEncounterCatchFlags
predef FlagActionPredef
call DisableEncounterCatchSRAM_Battle
ld a, c
and a
jr z, .skipWildCatchInit
ld a, 1
ld [wWildEncounterCanCatch], a
.skipWildCatchInit
ld a, [wIsInBattle]
dec a ; is it a trainer battle?
call nz, EnemySendOutFirstMon ; if it is a trainer battle, send out enemy mon
@ -261,6 +282,7 @@ StartBattle:
; wild mon or link battle enemy ran from battle
EnemyRan:
call MarkWildEncounterCatchUsed
call LoadScreenTilesFromBuffer1
ld a, [wLinkState]
cp LINK_STATE_BATTLING
@ -312,10 +334,16 @@ MainInBattleLoop:
jr nz, .selectEnemyMove ; if so, jump
; the player is neither thrashing about nor charging for an attack
call DisplayBattleMenu ; show battle menu
ret c ; return if player ran from battle
jr nc, .didNotRun
call MarkWildEncounterCatchUsed
ret ; return if player ran from battle
.didNotRun
ld a, [wEscapedFromBattle]
and a
ret nz ; return if pokedoll was used to escape from battle
jr z, .continueAfterEscapeCheck
call MarkWildEncounterCatchUsed
ret ; return if pokedoll was used to escape from battle
.continueAfterEscapeCheck
ld a, [wBattleMonStatus]
and (1 << FRZ) | SLP_MASK
jr nz, .selectEnemyMove ; if so, jump
@ -719,6 +747,7 @@ HandleEnemyMonFainted:
call nz, DrawPlayerHUDAndHPBar ; if battle mon HP is not zero, draw player HD and HP bar
ld a, [wIsInBattle]
dec a
call z, MarkWildEncounterCatchUsed
ret z ; return if it's a wild battle
call AnyEnemyPokemonAliveCheck
jp z, TrainerBattleVictory
@ -927,6 +956,12 @@ ReplaceFaintedEnemyMon:
ret
TrainerBattleVictory:
ld a, [wCurOpponent]
cp OPP_RIVAL1
jr nz, .notRival1Battle
xor a
ld [wWildEncounterCanCatch], a
.notRival1Battle
call EndLowHealthAlarm
ld b, MUSIC_DEFEATED_GYM_LEADER
ld a, [wGymLeaderNo]
@ -1077,15 +1112,103 @@ RemoveFaintedPlayerMon:
jr nc, .carelessTrainer ; if so, punish the player for being careless, as they shouldn't be fighting a very high leveled trainer with such a level difference
.regularFaint
farcall_ModifyPikachuHappiness PIKAHAPPY_FAINTED
ret
jr .handleRanAway
.carelessTrainer
farcall_ModifyPikachuHappiness PIKAHAPPY_CARELESSTRAINER
; fall through
.handleRanAway
call HandleFaintedPlayerMonRanAway
ret
PlayerMonFaintedText:
text_far _PlayerMonFaintedText
text_end
PlayerMonRanAwayBadTrainingText:
text_far _PokemonRanAwayBadTrainingText
text_end
HandleFaintedPlayerMonRanAway:
ld a, [wPartyCount]
cp 1
ret z ; don't remove the final party mon to avoid invalid 0-mon party state
ld a, [wPlayerMonNumber]
ld [wWhichPokemon], a
ld hl, wPartyMonNicks
call GetPartyMonName
ld hl, PlayerMonRanAwayBadTrainingText
call PrintText
xor a
ld [wRemoveMonFromBox], a
call RemovePokemon
ld a, [wPartyCount]
and a
ret z
dec a
ld b, a
ld a, [wPlayerMonNumber]
cp b
jr c, .done
ld a, b
ld [wPlayerMonNumber], a
.done
ret
MarkWildEncounterCatchUsed:
ld a, [wBattleType]
and a
ret nz
ld a, [wIsInBattle]
dec a
ret nz
ld a, [wWildEncounterCanCatch]
and a
ret z
ld a, [wCurMap]
ld c, a
call EnableEncounterCatchSRAM_Battle
ld b, FLAG_RESET
ld hl, sMapEncounterCatchFlags
predef FlagActionPredef
call DisableEncounterCatchSRAM_Battle
xor a
ld [wWildEncounterCanCatch], a
ret
EnsureEncounterCatchFlagsInitialized:
call EnableEncounterCatchSRAM_Battle
ld a, [sMapEncounterCatchFlagsInitialized]
cp $a5
jr z, .done ; explicit initialization marker
ld hl, sMapEncounterCatchFlags
ld b, (NUM_MAPS + 7) / 8
ld a, $ff
.fillLoop
ld [hli], a
dec b
jr nz, .fillLoop
ld a, $a5
ld [sMapEncounterCatchFlagsInitialized], a
.done
call DisableEncounterCatchSRAM_Battle
ret
EnableEncounterCatchSRAM_Battle:
ld a, BMODE_ADVANCED
ld [rBMODE], a
ld a, RAMG_SRAM_ENABLE
ld [rRAMG], a
ld a, BANK(sMapEncounterCatchFlags)
ld [rRAMB], a
ret
DisableEncounterCatchSRAM_Battle:
ld a, BMODE_SIMPLE
ld [rBMODE], a
ASSERT RAMG_SRAM_DISABLE == BMODE_SIMPLE
ld [rRAMG], a
ret
; asks if you want to use next mon
; stores whether you ran in C flag
DoUseNextMonDialogue:
@ -1169,6 +1292,7 @@ ChooseNextMon:
; called when player is out of usable mons.
; prints appropriate lose message, sets carry flag if player blacked out (special case for initial rival fight)
HandlePlayerBlackOut:
call MarkWildEncounterCatchUsed
ld a, [wLinkState]
cp LINK_STATE_BATTLING
jr z, .notRival1Battle
@ -1183,6 +1307,8 @@ HandlePlayerBlackOut:
call DelayFrames
ld hl, Rival1WinText
call PrintText
xor a
ld [wWildEncounterCanCatch], a
ld a, [wCurMap]
cp OAKS_LAB
ret z ; starter battle in oak's lab: don't black out
@ -1411,9 +1537,7 @@ EnemySendOutFirstMon:
ld a, [wLinkState]
cp LINK_STATE_BATTLING
jr z, .next4
ld a, [wOptions]
bit BIT_BATTLE_SHIFT, a
jr nz, .next4
jr .next4 ; battle style is forced to SET
ld hl, TrainerAboutToUseText
call PrintText
hlcoord 0, 7

View File

@ -68,8 +68,10 @@ ApplyOutOfBattlePoisonDamage:
callfar PlayPikachuSoundClip
callfar_ModifyPikachuHappiness PIKAHAPPY_PSNFNT
.curMonNotPlayerPikachu
call HandlePoisonFaintedMonRanAway
pop de
pop hl
jr .restartAfterRemoval
.nextMon
inc hl
inc hl
@ -84,7 +86,28 @@ ApplyOutOfBattlePoisonDamage:
ld hl, wWhichPokemon
inc [hl]
pop hl
jr .applyDamageLoop
jp .applyDamageLoop
.restartAfterRemoval
ld a, [wPartyCount]
and a
jr z, .applyDamageLoopDone
ld a, [wWhichPokemon]
ld b, a
ld a, [wPartyCount]
cp b
jr z, .applyDamageLoopDone
ld hl, wPartySpecies
ld a, [wWhichPokemon]
ld c, a
ld b, 0
add hl, bc
ld d, h
ld e, l
ld hl, wPartyMon1Status
ld bc, PARTYMON_STRUCT_LENGTH
ld a, [wWhichPokemon]
call AddNTimes
jp .applyDamageLoop
.applyDamageLoopDone
ld hl, wPartyMon1Status
ld a, [wPartyCount]
@ -125,6 +148,34 @@ ApplyOutOfBattlePoisonDamage:
ld [wOutOfBattleBlackout], a
ret
PoisonFaintedMonRanAwayText:
text_far _PokemonRanAwayBadTrainingText
text_end
HandlePoisonFaintedMonRanAway:
ld a, [wPartyCount]
cp 1
ret z ; don't remove the final party mon to avoid invalid 0-mon party state
ld hl, wPartyMonNicks
call GetPartyMonName
ld hl, PoisonFaintedMonRanAwayText
call PrintText
xor a
ld [wRemoveMonFromBox], a
call RemovePokemon
ld a, [wPartyCount]
and a
ret z
dec a
ld b, a
ld a, [wWhichPokemon]
cp b
jr c, .done
ld a, b
ld [wWhichPokemon], a
.done
ret
Func_c4c7:
ld a, [wStepCounter]
and a

View File

@ -112,6 +112,14 @@ ItemUseBall:
dec a
jp nz, ThrowBallAtTrainerMon
ld a, [wBattleType]
and a
jr nz, .skipEncounterLimitCheck
ld a, [wWildEncounterCanCatch]
and a
jp z, ThrowBallAtUsedEncounter
.skipEncounterLimitCheck
; If this is for the old man battle, skip checking if the party & box are full.
ld a, [wBattleType]
cp BATTLE_TYPE_OLD_MAN
@ -525,6 +533,7 @@ ItemUseBall:
ld [wCapturedMonSpecies], a
ld [wCurPartySpecies], a
ld [wPokedexNum], a
call MarkWildEncounterCatchUsedItem
ld a, [wBattleType]
cp BATTLE_TYPE_OLD_MAN ; is this the old man battle?
jp z, .oldManCaughtMon ; if so, don't give the player the caught Pokémon
@ -2579,6 +2588,17 @@ ThrowBallAtTrainerMon:
call PrintText
jr RemoveUsedItem
ThrowBallAtUsedEncounter:
call RunDefaultPaletteCommand
call LoadScreenTilesFromBuffer1 ; restore saved screen
call Delay3
ld a, TOSS_ANIM
ld [wAnimationID], a
predef MoveAnimation ; do animation
ld hl, ThrowBallAtUsedEncounterText
call PrintText
jr RemoveUsedItem
NoCyclingAllowedHere:
ld hl, NoCyclingAllowedHereText
jr ItemUseFailed
@ -2615,6 +2635,47 @@ ThrowBallAtTrainerMonText2:
text_far _ThrowBallAtTrainerMonText2
text_end
ThrowBallAtUsedEncounterText:
text_far _ThrowBallAtUsedEncounterText
text_end
MarkWildEncounterCatchUsedItem:
ld a, [wBattleType]
and a
ret nz
ld a, [wIsInBattle]
dec a
ret nz
ld a, [wWildEncounterCanCatch]
and a
ret z
call EnableEncounterCatchSRAM_Item
ld a, [wCurMap]
ld c, a
ld b, FLAG_RESET
ld hl, sMapEncounterCatchFlags
predef FlagActionPredef
call DisableEncounterCatchSRAM_Item
xor a
ld [wWildEncounterCanCatch], a
ret
EnableEncounterCatchSRAM_Item:
ld a, BMODE_ADVANCED
ld [rBMODE], a
ld a, RAMG_SRAM_ENABLE
ld [rRAMG], a
ld a, BANK(sMapEncounterCatchFlags)
ld [rRAMB], a
ret
DisableEncounterCatchSRAM_Item:
ld a, BMODE_SIMPLE
ld [rBMODE], a
ASSERT RAMG_SRAM_DISABLE == BMODE_SIMPLE
ld [rRAMG], a
ret
NoCyclingAllowedHereText:
text_far _NoCyclingAllowedHereText
text_end

View File

@ -10,17 +10,8 @@ AskName:
ld a, [wCurPartySpecies]
ld [wNamedObjectIndex], a
call GetMonName
ld hl, DoYouWantToNicknameText
call PrintText
hlcoord 14, 7
lb bc, 8, 15
ld a, TWO_OPTION_MENU
ld [wTextBoxID], a
call DisplayTextBoxID
pop hl
ld a, [wCurrentMenuItem]
and a
jr nz, .declinedNickname
.enterNickname
ld a, [wUpdateSpritesEnabled]
push af
xor a
@ -40,13 +31,8 @@ AskName:
ld [wUpdateSpritesEnabled], a
ld a, [wStringBuffer]
cp '@'
ret nz
.declinedNickname
ld d, h
ld e, l
ld hl, wNameBuffer
ld bc, NAME_LENGTH
jp CopyData
jr z, .enterNickname ; force a nickname (can't skip with empty input)
ret
DoYouWantToNicknameText:
text_far _DoYouWantToNicknameText

View File

@ -175,38 +175,15 @@ OptionsMenu_BattleAnimations:
.Off: db "OFF@"
OptionsMenu_BattleStyle:
ldh a, [hJoy5]
and PAD_LEFT | PAD_RIGHT
jr nz, .buttonPressed
ld a, [wOptions]
and 1 << BIT_BATTLE_SHIFT
jr .nothingPressed
.buttonPressed
ld a, [wOptions]
xor 1 << BIT_BATTLE_SHIFT
or 1 << BIT_BATTLE_SHIFT ; force SET
ld [wOptions], a
.nothingPressed
ld bc, 0
sla a
sla a
rl c
ld hl, .Strings
add hl, bc
add hl, bc
ld e, [hl]
inc hl
ld d, [hl]
ld de, .Set
hlcoord 14, 6
call PlaceString
and a ; clear carry flag
ret
.Strings:
dw .Shift
dw .Set
.Shift: db "SHIFT@"
.Set: db "SET @"

View File

@ -23,6 +23,10 @@ sTileAnimations:: db
sGameDataEnd::
sMainDataCheckSum:: db
; Flags for whether the first wild encounter catch opportunity was used per map.
sMapEncounterCatchFlags:: flag_array NUM_MAPS
sMapEncounterCatchFlagsInitialized:: db
; The PC boxes will not fit into one SRAM bank,
; so they use multiple SECTIONs

View File

@ -1427,10 +1427,10 @@ wCurOpponent:: db
wBattleType:: db
; bits 0-6: Effectiveness
; $0 = immune
; $5 = not very effective
; $a = neutral
; $14 = super-effective
; $0 = immune
; $5 = not very effective
; $a = neutral
; $14 = super-effective
; bit 7: STAB
wDamageMultipliers:: db
@ -2345,7 +2345,8 @@ wLastBlackoutMap:: db
wDestinationMap:: db
; initialized to $ff, but nothing ever reads it
wUnusedPlayerDataByte:: db
wUnusedPlayerDataByte::
wWildEncounterCanCatch:: db
; used to store the tile in front of the boulder when trying to push a boulder
; also used to store the result of the collision check ($ff for a collision and $00 for no collision)