Split battle/ into data/ and engine/ components

This commit is contained in:
Remy Oukaour
2017-12-26 17:47:05 -05:00
parent 2f98c2032f
commit b5417fafec
170 changed files with 1790 additions and 1799 deletions

882
engine/battle/ai/items.asm Normal file
View File

@@ -0,0 +1,882 @@
AI_SwitchOrTryItem: ; 38000
and a
ld a, [wBattleMode]
dec a
ret z
ld a, [wLinkMode]
and a
ret nz
farcall CheckEnemyLockedIn
ret nz
ld a, [PlayerSubStatus5]
bit SUBSTATUS_CANT_RUN, a
jr nz, DontSwitch
ld a, [wEnemyWrapCount]
and a
jr nz, DontSwitch
ld hl, TrainerClassAttributes + TRNATTR_AI_ITEM_SWITCH
ld a, [InBattleTowerBattle] ; Load always the first TrainerClass for BattleTower-Trainers
and a
jr nz, .ok
ld a, [TrainerClass]
dec a
ld bc, NUM_TRAINER_ATTRIBUTES
call AddNTimes
.ok
bit SWITCH_OFTEN_F, [hl]
jp nz, SwitchOften
bit SWITCH_RARELY_F, [hl]
jp nz, SwitchRarely
bit SWITCH_SOMETIMES_F, [hl]
jp nz, SwitchSometimes
; fallthrough
DontSwitch: ; 38041
call AI_TryItem
ret
; 38045
SwitchOften: ; 38045
callfar CheckAbleToSwitch
ld a, [wEnemySwitchMonParam]
and $f0
jp z, DontSwitch
cp $10
jr nz, .not_10
call Random
cp 1 + 50 percent
jr c, .switch
jp DontSwitch
.not_10
cp $20
jr nz, .not_20
call Random
cp -1 + 79 percent
jr c, .switch
jp DontSwitch
.not_20
; $30
call Random
cp 4 percent
jp c, DontSwitch
.switch
ld a, [wEnemySwitchMonParam]
and $f
inc a
; In register 'a' is the number (1-6) of the Pkmn to switch to
ld [wEnemySwitchMonIndex], a
jp AI_TrySwitch
; 38083
SwitchRarely: ; 38083
callfar CheckAbleToSwitch
ld a, [wEnemySwitchMonParam]
and $f0
jp z, DontSwitch
cp $10
jr nz, .not_10
call Random
cp 8 percent
jr c, .switch
jp DontSwitch
.not_10
cp $20
jr nz, .not_20
call Random
cp 12 percent
jr c, .switch
jp DontSwitch
.not_20
; $30
call Random
cp -1 + 79 percent
jp c, DontSwitch
.switch
ld a, [wEnemySwitchMonParam]
and $f
inc a
ld [wEnemySwitchMonIndex], a
jp AI_TrySwitch
; 380c1
SwitchSometimes: ; 380c1
callfar CheckAbleToSwitch
ld a, [wEnemySwitchMonParam]
and $f0
jp z, DontSwitch
cp $10
jr nz, .not_10
call Random
cp -1 + 20 percent
jr c, .switch
jp DontSwitch
.not_10
cp $20
jr nz, .not_20
call Random
cp 1 + 50 percent
jr c, .switch
jp DontSwitch
.not_20
; $30
call Random
cp -1 + 20 percent
jp c, DontSwitch
.switch
ld a, [wEnemySwitchMonParam]
and $f
inc a
ld [wEnemySwitchMonIndex], a
jp AI_TrySwitch
; 380ff
CheckSubstatusCantRun: ; 380ff
ld a, [EnemySubStatus5]
bit SUBSTATUS_CANT_RUN, a
ret
; 38105
AI_TryItem: ; 38105
; items are not allowed in the BattleTower
ld a, [InBattleTowerBattle]
and a
ret nz
ld a, [wEnemyTrainerItem1]
ld b, a
ld a, [wEnemyTrainerItem2]
or b
ret z
call .IsHighestLevel
ret nc
ld a, [TrainerClass]
dec a
ld hl, TrainerClassAttributes + TRNATTR_AI_ITEM_SWITCH
ld bc, NUM_TRAINER_ATTRIBUTES
call AddNTimes
ld b, h
ld c, l
ld hl, AI_Items
ld de, wEnemyTrainerItem1
.loop
ld a, [hl]
and a
inc a
ret z
ld a, [de]
cp [hl]
jr z, .has_item
inc de
ld a, [de]
cp [hl]
jr z, .has_item
dec de
inc hl
inc hl
inc hl
jr .loop
.has_item
inc hl
push hl
push de
ld de, .callback
push de
ld a, [hli]
ld h, [hl]
ld l, a
jp hl
.callback
pop de
pop hl
inc hl
inc hl
jr c, .loop
.used_item
xor a
ld [de], a
inc a
ld [wEnemyGoesFirst], a
ld hl, EnemySubStatus3
res SUBSTATUS_BIDE, [hl]
xor a
ld [EnemyFuryCutterCount], a
ld [EnemyProtectCount], a
ld [wEnemyRageCounter], a
ld hl, EnemySubStatus4
res SUBSTATUS_RAGE, [hl]
xor a
ld [LastEnemyCounterMove], a
scf
ret
.IsHighestLevel: ; 38170
ld a, [OTPartyCount]
ld d, a
ld e, 0
ld hl, OTPartyMon1Level
ld bc, PARTYMON_STRUCT_LENGTH
.next
ld a, [hl]
cp e
jr c, .ok
ld e, a
.ok
add hl, bc
dec d
jr nz, .next
ld a, [CurOTMon]
ld hl, OTPartyMon1Level
call AddNTimes
ld a, [hl]
cp e
jr nc, .yes
.no
and a
ret
.yes
scf
ret
; 38196
AI_Items: ; 39196
dbw FULL_RESTORE, .FullRestore
dbw MAX_POTION, .MaxPotion
dbw HYPER_POTION, .HyperPotion
dbw SUPER_POTION, .SuperPotion
dbw POTION, .Potion
dbw X_ACCURACY, .XAccuracy
dbw FULL_HEAL, .FullHeal
dbw GUARD_SPEC, .GuardSpec
dbw DIRE_HIT, .DireHit
dbw X_ATTACK, .XAttack
dbw X_DEFEND, .XDefend
dbw X_SPEED, .XSpeed
dbw X_SPECIAL, .XSpecial
db $ff
; 381be
.FullHeal: ; 381be
call .Status
jp c, .DontUse
call EnemyUsedFullHeal
jp .Use
; 381ca
.Status: ; 381ca (e:41ca)
ld a, [EnemyMonStatus]
and a
jp z, .DontUse
ld a, [bc]
bit CONTEXT_USE_F, a
jr nz, .StatusCheckContext
ld a, [bc]
bit ALWAYS_USE_F, a
jp nz, .Use
call Random
cp -1 + 20 percent
jp c, .Use
jp .DontUse
.StatusCheckContext:
ld a, [EnemySubStatus5]
bit SUBSTATUS_TOXIC, a
jr z, .FailToxicCheck
ld a, [EnemyToxicCount]
cp 4
jr c, .FailToxicCheck
call Random
cp 1 + 50 percent
jp c, .Use
.FailToxicCheck:
ld a, [EnemyMonStatus]
and 1 << FRZ | SLP
jp z, .DontUse
jp .Use
; 38208
.FullRestore: ; 38208
call .HealItem
jp nc, .UseFullRestore
ld a, [bc]
bit CONTEXT_USE_F, a
jp z, .DontUse
call .Status
jp c, .DontUse
.UseFullRestore:
call EnemyUsedFullRestore
jp .Use
; 38220
.MaxPotion: ; 38220
call .HealItem
jp c, .DontUse
call EnemyUsedMaxPotion
jp .Use
.HealItem: ; 3822c (e:422c)
ld a, [bc]
bit CONTEXT_USE_F, a
jr nz, .CheckHalfOrQuarterHP
callfar AICheckEnemyHalfHP
jp c, .DontUse
ld a, [bc]
bit UNKNOWN_USE_F, a
jp nz, .CheckQuarterHP
callfar AICheckEnemyQuarterHP
jp nc, .UseHealItem
call Random
cp 1 + 50 percent
jp c, .UseHealItem
jp .DontUse
.CheckQuarterHP: ; 38254 (e:4254)
callfar AICheckEnemyQuarterHP
jp c, .DontUse
call Random
cp -1 + 20 percent
jp c, .DontUse
jr .UseHealItem
.CheckHalfOrQuarterHP: ; 38267 (e:4267)
callfar AICheckEnemyHalfHP
jp c, .DontUse
callfar AICheckEnemyQuarterHP
jp nc, .UseHealItem
call Random
cp -1 + 20 percent
jp nc, .DontUse
.UseHealItem: ; 38281 (e:4281)
jp .Use
; 38284
.HyperPotion: ; 38284
call .HealItem
jp c, .DontUse
ld b, 200
call EnemyUsedHyperPotion
jp .Use
; 38292 (e:4292)
.SuperPotion: ; 38292
call .HealItem
jp c, .DontUse
ld b, 50
call EnemyUsedSuperPotion
jp .Use
; 382a0
.Potion: ; 382a0
call .HealItem
jp c, .DontUse
ld b, 20
call EnemyUsedPotion
jp .Use
; 382ae
.asm_382ae ; This appears to be unused
callfar AICheckEnemyMaxHP
jr c, .dont_use
push bc
ld de, EnemyMonMaxHP + 1
ld hl, EnemyMonHP + 1
ld a, [de]
sub [hl]
jr z, .check_40_percent
dec hl
dec de
ld c, a
sbc [hl]
and a
jr nz, .check_40_percent
ld a, c
cp b
jp c, .check_50_percent
callfar AICheckEnemyQuarterHP
jr c, .check_40_percent
.check_50_percent
pop bc
ld a, [bc]
bit UNKNOWN_USE_F, a
jp z, .Use
call Random
cp 1 + 50 percent
jp c, .Use
.dont_use
jp .DontUse
.check_40_percent
pop bc
ld a, [bc]
bit UNKNOWN_USE_F, a
jp z, .DontUse
call Random
cp 1 + 39 percent
jp c, .Use
jp .DontUse
; 382f9
.XAccuracy: ; 382f9
call .XItem
jp c, .DontUse
call EnemyUsedXAccuracy
jp .Use
; 38305
.GuardSpec: ; 38305
call .XItem
jp c, .DontUse
call EnemyUsedGuardSpec
jp .Use
; 38311
.DireHit: ; 38311
call .XItem
jp c, .DontUse
call EnemyUsedDireHit
jp .Use
; 3831d (e:431d)
.XAttack: ; 3831d
call .XItem
jp c, .DontUse
call EnemyUsedXAttack
jp .Use
; 38329
.XDefend: ; 38329
call .XItem
jp c, .DontUse
call EnemyUsedXDefend
jp .Use
; 38335
.XSpeed: ; 38335
call .XItem
jp c, .DontUse
call EnemyUsedXSpeed
jp .Use
; 38341
.XSpecial: ; 38341
call .XItem
jp c, .DontUse
call EnemyUsedXSpecial
jp .Use
; 3834d
.XItem: ; 3834d (e:434d)
ld a, [EnemyTurnsTaken]
and a
jr nz, .notfirstturnout
ld a, [bc]
bit ALWAYS_USE_F, a
jp nz, .Use
call Random
cp 1 + 50 percent
jp c, .DontUse
ld a, [bc]
bit CONTEXT_USE_F, a
jp nz, .Use
call Random
cp 1 + 50 percent
jp c, .DontUse
jp .Use
.notfirstturnout
ld a, [bc]
bit ALWAYS_USE_F, a
jp z, .DontUse
call Random
cp -1 + 20 percent
jp nc, .DontUse
jp .Use
.DontUse:
scf
ret
.Use:
and a
ret
AIUpdateHUD: ; 38387
call UpdateEnemyMonInParty
farcall UpdateEnemyHUD
ld a, $1
ld [hBGMapMode], a
ld hl, wEnemyItemState
dec [hl]
scf
ret
; 3839a
AIUsedItemSound: ; 3839a
push de
ld de, SFX_FULL_HEAL
call PlaySFX
pop de
ret
; 383a3
EnemyUsedFullHeal: ; 383a3 (e:43a3)
call AIUsedItemSound
call AI_HealStatus
ld a, FULL_HEAL
jp PrintText_UsedItemOn_AND_AIUpdateHUD
EnemyUsedMaxPotion: ; 383ae (e:43ae)
ld a, MAX_POTION
ld [CurEnemyItem], a
jr FullRestoreContinue
EnemyUsedFullRestore: ; 383b5 (e:43b5)
call AI_HealStatus
ld a, FULL_RESTORE
ld [CurEnemyItem], a
ld hl, EnemySubStatus3
res SUBSTATUS_CONFUSED, [hl]
xor a
ld [EnemyConfuseCount], a
FullRestoreContinue: ; 383c6
ld de, wCurHPAnimOldHP
ld hl, EnemyMonHP + 1
ld a, [hld]
ld [de], a
inc de
ld a, [hl]
ld [de], a
inc de
ld hl, EnemyMonMaxHP + 1
ld a, [hld]
ld [de], a
inc de
ld [wCurHPAnimMaxHP], a
ld [EnemyMonHP + 1], a
ld a, [hl]
ld [de], a
ld [wCurHPAnimMaxHP + 1], a
ld [EnemyMonHP], a
jr EnemyPotionFinish
; 383e8 (e:43e8)
EnemyUsedPotion: ; 383e8
ld a, POTION
ld b, 20
jr EnemyPotionContinue
EnemyUsedSuperPotion: ; 383ee
ld a, SUPER_POTION
ld b, 50
jr EnemyPotionContinue
EnemyUsedHyperPotion: ; 383f4 (e:43f4)
ld a, HYPER_POTION
ld b, 200
EnemyPotionContinue: ; 383f8
ld [CurEnemyItem], a
ld hl, EnemyMonHP + 1
ld a, [hl]
ld [wCurHPAnimOldHP], a
add b
ld [hld], a
ld [wCurHPAnimNewHP], a
ld a, [hl]
ld [wCurHPAnimOldHP + 1], a
ld [wCurHPAnimNewHP + 1], a
jr nc, .ok
inc a
ld [hl], a
ld [wCurHPAnimNewHP + 1], a
.ok
inc hl
ld a, [hld]
ld b, a
ld de, EnemyMonMaxHP + 1
ld a, [de]
dec de
ld [wCurHPAnimMaxHP], a
sub b
ld a, [hli]
ld b, a
ld a, [de]
ld [wCurHPAnimMaxHP + 1], a
sbc b
jr nc, EnemyPotionFinish
inc de
ld a, [de]
dec de
ld [hld], a
ld [wCurHPAnimNewHP], a
ld a, [de]
ld [hl], a
ld [wCurHPAnimNewHP + 1], a
EnemyPotionFinish: ; 38436
call PrintText_UsedItemOn
hlcoord 2, 2
xor a
ld [wWhichHPBar], a
call AIUsedItemSound
predef AnimateHPBar
jp AIUpdateHUD
AI_TrySwitch: ; 3844b
; Determine whether the AI can switch based on how many Pokemon are still alive.
; If it can switch, it will.
ld a, [OTPartyCount]
ld c, a
ld hl, OTPartyMon1HP
ld d, 0
.SwitchLoop:
ld a, [hli]
ld b, a
ld a, [hld]
or b
jr z, .fainted
inc d
.fainted
push bc
ld bc, PARTYMON_STRUCT_LENGTH
add hl, bc
pop bc
dec c
jr nz, .SwitchLoop
ld a, d
cp 2
jp nc, AI_Switch
and a
ret
; 3846c
AI_Switch: ; 3846c
ld a, $1
ld [wEnemyIsSwitching], a
ld [wEnemyGoesFirst], a
ld hl, EnemySubStatus4
res SUBSTATUS_RAGE, [hl]
xor a
ld [hBattleTurn], a
callfar PursuitSwitch
push af
ld a, [CurOTMon]
ld hl, OTPartyMon1Status
ld bc, PARTYMON_STRUCT_LENGTH
call AddNTimes
ld d, h
ld e, l
ld hl, EnemyMonStatus
ld bc, MON_MAXHP - MON_STATUS
call CopyBytes
pop af
jr c, .skiptext
ld hl, TextJump_EnemyWithdrew
call PrintText
.skiptext
ld a, 1
ld [wBattleHasJustStarted], a
callfar NewEnemyMonStatus
callfar ResetEnemyStatLevels
ld hl, PlayerSubStatus1
res SUBSTATUS_IN_LOVE, [hl]
farcall EnemySwitch
farcall ResetBattleParticipants
xor a
ld [wBattleHasJustStarted], a
ld a, [wLinkMode]
and a
ret nz
scf
ret
; 384d0
TextJump_EnemyWithdrew: ; 384d0
text_jump Text_EnemyWithdrew
db "@"
; 384d5
Function384d5: ; This appears to be unused
call AIUsedItemSound
call AI_HealStatus
ld a, FULL_HEAL_RED ; X_SPEED
jp PrintText_UsedItemOn_AND_AIUpdateHUD
; 384e0
AI_HealStatus: ; 384e0
ld a, [CurOTMon]
ld hl, OTPartyMon1Status
ld bc, PARTYMON_STRUCT_LENGTH
call AddNTimes
xor a
ld [hl], a
ld [EnemyMonStatus], a
; Bug: this should reset SUBSTATUS_NIGHTMARE too
; Uncomment the lines below to fix
; ld hl, EnemySubStatus1
; res SUBSTATUS_NIGHTMARE, [hl]
ld hl, EnemySubStatus5
res SUBSTATUS_TOXIC, [hl]
ret
; 384f7
EnemyUsedXAccuracy: ; 384f7
call AIUsedItemSound
ld hl, EnemySubStatus4
set SUBSTATUS_X_ACCURACY, [hl]
ld a, X_ACCURACY
jp PrintText_UsedItemOn_AND_AIUpdateHUD
; 38504
EnemyUsedGuardSpec: ; 38504
call AIUsedItemSound
ld hl, EnemySubStatus4
set SUBSTATUS_MIST, [hl]
ld a, GUARD_SPEC
jp PrintText_UsedItemOn_AND_AIUpdateHUD
; 38511
EnemyUsedDireHit: ; 38511
call AIUsedItemSound
ld hl, EnemySubStatus4
set SUBSTATUS_FOCUS_ENERGY, [hl]
ld a, DIRE_HIT
jp PrintText_UsedItemOn_AND_AIUpdateHUD
; 3851e
Function3851e: ; This appears to be unused
ld [hDivisor], a
ld hl, EnemyMonMaxHP
ld a, [hli]
ld [hDividend], a
ld a, [hl]
ld [hDividend + 1], a
ld b, 2
call Divide
ld a, [hQuotient + 2]
ld c, a
ld a, [hQuotient + 1]
ld b, a
ld hl, EnemyMonHP + 1
ld a, [hld]
ld e, a
ld a, [hl]
ld d, a
ld a, d
sub b
ret nz
ld a, e
sub c
ret
; 38541
EnemyUsedXAttack: ; 38541
ld b, ATTACK
ld a, X_ATTACK
jr EnemyUsedXItem
; 38547
EnemyUsedXDefend: ; 38547
ld b, DEFENSE
ld a, X_DEFEND
jr EnemyUsedXItem
; 3854d
EnemyUsedXSpeed: ; 3854d
ld b, SPEED
ld a, X_SPEED
jr EnemyUsedXItem
; 38553
EnemyUsedXSpecial: ; 38553
ld b, SP_ATTACK
ld a, X_SPECIAL
; Parameter
; a = ITEM_CONSTANT
; b = BATTLE_CONSTANT (ATTACK, DEFENSE, SPEED, SP_ATTACK, SP_DEFENSE, ACCURACY, EVASION)
EnemyUsedXItem:
ld [CurEnemyItem], a
push bc
call PrintText_UsedItemOn
pop bc
farcall CheckIfStatCanBeRaised
jp AIUpdateHUD
; 38568
; Parameter
; a = ITEM_CONSTANT
PrintText_UsedItemOn_AND_AIUpdateHUD: ; 38568
ld [CurEnemyItem], a
call PrintText_UsedItemOn
jp AIUpdateHUD
; 38571
PrintText_UsedItemOn: ; 38571
ld a, [CurEnemyItem]
ld [wd265], a
call GetItemName
ld hl, StringBuffer1
ld de, wMonOrItemNameBuffer
ld bc, ITEM_NAME_LENGTH
call CopyBytes
ld hl, TextJump_EnemyUsedOn
jp PrintText
; 3858c
TextJump_EnemyUsedOn: ; 3858c
text_jump Text_EnemyUsedOn
db "@"
; 38591

221
engine/battle/ai/move.asm Executable file
View File

@@ -0,0 +1,221 @@
AIChooseMove: ; 440ce
; Score each move in EnemyMonMoves starting from Buffer1. Lower is better.
; Pick the move with the lowest score.
; Wildmons attack at random.
ld a, [wBattleMode]
dec a
ret z
ld a, [wLinkMode]
and a
ret nz
; No use picking a move if there's no choice.
farcall CheckEnemyLockedIn
ret nz
; The default score is 20. Unusable moves are given a score of 80.
ld a, 20
ld hl, Buffer1
ld [hli], a
ld [hli], a
ld [hli], a
ld [hl], a
; Don't pick disabled moves.
ld a, [EnemyDisabledMove]
and a
jr z, .CheckPP
ld hl, EnemyMonMoves
ld c, 0
.CheckDisabledMove:
cp [hl]
jr z, .ScoreDisabledMove
inc c
inc hl
jr .CheckDisabledMove
.ScoreDisabledMove:
ld hl, Buffer1
ld b, 0
add hl, bc
ld [hl], 80
; Don't pick moves with 0 PP.
.CheckPP:
ld hl, Buffer1 - 1
ld de, EnemyMonPP
ld b, 0
.CheckMovePP:
inc b
ld a, b
cp EnemyMonMovesEnd - EnemyMonMoves + 1
jr z, .ApplyLayers
inc hl
ld a, [de]
inc de
and $3f
jr nz, .CheckMovePP
ld [hl], 80
jr .CheckMovePP
; Apply AI scoring layers depending on the trainer class.
.ApplyLayers:
ld hl, TrainerClassAttributes + TRNATTR_AI_MOVE_WEIGHTS
; If we have a battle in BattleTower just load the Attributes of the first TrainerClass (Falkner)
; so we have always the same AI, regardless of the loaded class of trainer
ld a, [InBattleTowerBattle]
bit 0, a
jr nz, .battle_tower_skip
ld a, [TrainerClass]
dec a
ld bc, 7 ; Trainer2AI - Trainer1AI
call AddNTimes
.battle_tower_skip
lb bc, CHECK_FLAG, 0
push bc
push hl
.CheckLayer:
pop hl
pop bc
ld a, c
cp 16 ; up to 16 scoring layers
jr z, .DecrementScores
push bc
ld d, BANK(TrainerClassAttributes)
predef FlagPredef
ld d, c
pop bc
inc c
push bc
push hl
ld a, d
and a
jr z, .CheckLayer
ld hl, AIScoringPointers
dec c
ld b, 0
add hl, bc
add hl, bc
ld a, [hli]
ld h, [hl]
ld l, a
ld a, BANK(AIScoring)
call FarCall_hl
jr .CheckLayer
; Decrement the scores of all moves one by one until one reaches 0.
.DecrementScores:
ld hl, Buffer1
ld de, EnemyMonMoves
ld c, EnemyMonMovesEnd - EnemyMonMoves
.DecrementNextScore:
; If the enemy has no moves, this will infinite.
ld a, [de]
inc de
and a
jr z, .DecrementScores
; We are done whenever a score reaches 0
dec [hl]
jr z, .PickLowestScoreMoves
; If we just decremented the fourth move's score, go back to the first move
inc hl
dec c
jr z, .DecrementScores
jr .DecrementNextScore
; In order to avoid bias towards the moves located first in memory, increment the scores
; that were decremented one more time than the rest (in case there was a tie).
; This means that the minimum score will be 1.
.PickLowestScoreMoves:
ld a, c
.move_loop
inc [hl]
dec hl
inc a
cp NUM_MOVES + 1
jr nz, .move_loop
ld hl, Buffer1
ld de, EnemyMonMoves
ld c, NUM_MOVES
; Give a score of 0 to a blank move
.loop2
ld a, [de]
and a
jr nz, .skip_load
ld [hl], a
; Disregard the move if its score is not 1
.skip_load
ld a, [hl]
dec a
jr z, .keep
xor a
ld [hli], a
jr .after_toss
.keep
ld a, [de]
ld [hli], a
.after_toss
inc de
dec c
jr nz, .loop2
; Randomly choose one of the moves with a score of 1
.ChooseMove:
ld hl, Buffer1
call Random
and 3
ld c, a
ld b, 0
add hl, bc
ld a, [hl]
and a
jr z, .ChooseMove
ld [CurEnemyMove], a
ld a, c
ld [CurEnemyMoveNum], a
ret
; 441af
AIScoringPointers: ; 441af
dw AI_Basic
dw AI_Setup
dw AI_Types
dw AI_Offensive
dw AI_Smart
dw AI_Opportunist
dw AI_Aggressive
dw AI_Cautious
dw AI_Status
dw AI_Risky
dw AI_None
dw AI_None
dw AI_None
dw AI_None
dw AI_None
dw AI_None
; 441cf

198
engine/battle/ai/redundant.asm Executable file
View File

@@ -0,0 +1,198 @@
AI_Redundant: ; 2c41a
; Check if move effect c will fail because it's already been used.
; Return z if the move is a good choice.
; Return nz if the move is a bad choice.
ld a, c
ld de, 3
ld hl, .Moves
call IsInArray
jp nc, .NotRedundant
inc hl
ld a, [hli]
ld h, [hl]
ld l, a
jp hl
.Moves: ; 2c42c
dbw EFFECT_DREAM_EATER, .DreamEater
dbw EFFECT_HEAL, .Heal
dbw EFFECT_LIGHT_SCREEN, .LightScreen
dbw EFFECT_MIST, .Mist
dbw EFFECT_FOCUS_ENERGY, .FocusEnergy
dbw EFFECT_CONFUSE, .Confuse
dbw EFFECT_TRANSFORM, .Transform
dbw EFFECT_REFLECT, .Reflect
dbw EFFECT_SUBSTITUTE, .Substitute
dbw EFFECT_LEECH_SEED, .LeechSeed
dbw EFFECT_DISABLE, .Disable
dbw EFFECT_ENCORE, .Encore
dbw EFFECT_SNORE, .Snore
dbw EFFECT_SLEEP_TALK, .SleepTalk
dbw EFFECT_MEAN_LOOK, .MeanLook
dbw EFFECT_NIGHTMARE, .Nightmare
dbw EFFECT_SPIKES, .Spikes
dbw EFFECT_FORESIGHT, .Foresight
dbw EFFECT_PERISH_SONG, .PerishSong
dbw EFFECT_SANDSTORM, .Sandstorm
dbw EFFECT_ATTRACT, .Attract
dbw EFFECT_SAFEGUARD, .Safeguard
dbw EFFECT_RAIN_DANCE, .RainDance
dbw EFFECT_SUNNY_DAY, .SunnyDay
dbw EFFECT_TELEPORT, .Teleport
dbw EFFECT_MORNING_SUN, .MorningSun
dbw EFFECT_SYNTHESIS, .Synthesis
dbw EFFECT_MOONLIGHT, .Moonlight
dbw EFFECT_SWAGGER, .Swagger
dbw EFFECT_FUTURE_SIGHT, .FutureSight
db -1
.LightScreen: ; 2c487
ld a, [EnemyScreens]
bit SCREENS_LIGHT_SCREEN, a
ret
.Mist: ; 2c48d
ld a, [EnemySubStatus4]
bit SUBSTATUS_MIST, a
ret
.FocusEnergy: ; 2c493
ld a, [EnemySubStatus4]
bit SUBSTATUS_FOCUS_ENERGY, a
ret
.Confuse: ; 2c499
ld a, [PlayerSubStatus3]
bit SUBSTATUS_CONFUSED, a
ret nz
ld a, [PlayerScreens]
bit SCREENS_SAFEGUARD, a
ret
.Transform: ; 2c4a5
ld a, [EnemySubStatus5]
bit SUBSTATUS_TRANSFORMED, a
ret
.Reflect: ; 2c4ab
ld a, [EnemyScreens]
bit SCREENS_REFLECT, a
ret
.Substitute: ; 2c4b1
ld a, [EnemySubStatus4]
bit SUBSTATUS_SUBSTITUTE, a
ret
.LeechSeed: ; 2c4b7
ld a, [PlayerSubStatus4]
bit SUBSTATUS_LEECH_SEED, a
ret
.Disable: ; 2c4bd
ld a, [PlayerDisableCount]
and a
ret
.Encore: ; 2c4c2
ld a, [PlayerSubStatus5]
bit SUBSTATUS_ENCORED, a
ret
.Snore:
.SleepTalk: ; 2c4c8
ld a, [EnemyMonStatus]
and SLP
jr z, .Redundant
jr .NotRedundant
.MeanLook: ; 2c4d1
ld a, [EnemySubStatus5]
bit SUBSTATUS_CANT_RUN, a
ret
.Nightmare: ; 2c4d7
ld a, [BattleMonStatus]
and a
jr z, .Redundant
ld a, [PlayerSubStatus1]
bit SUBSTATUS_NIGHTMARE, a
ret
.Spikes: ; 2c4e3
ld a, [PlayerScreens]
bit SCREENS_SPIKES, a
ret
.Foresight: ; 2c4e9
ld a, [PlayerSubStatus1]
bit SUBSTATUS_IDENTIFIED, a
ret
.PerishSong: ; 2c4ef
ld a, [PlayerSubStatus1]
bit SUBSTATUS_PERISH, a
ret
.Sandstorm: ; 2c4f5
ld a, [Weather]
cp WEATHER_SANDSTORM
jr z, .Redundant
jr .NotRedundant
.Attract: ; 2c4fe
farcall CheckOppositeGender
jr c, .Redundant
ld a, [PlayerSubStatus1]
bit SUBSTATUS_IN_LOVE, a
ret
.Safeguard: ; 2c50c
ld a, [EnemyScreens]
bit SCREENS_SAFEGUARD, a
ret
.RainDance: ; 2c512
ld a, [Weather]
cp WEATHER_RAIN
jr z, .Redundant
jr .NotRedundant
.SunnyDay: ; 2c51b
ld a, [Weather]
cp WEATHER_SUN
jr z, .Redundant
jr .NotRedundant
.DreamEater: ; 2c524
ld a, [BattleMonStatus]
and SLP
jr z, .Redundant
jr .NotRedundant
.Swagger: ; 2c52d
ld a, [PlayerSubStatus3]
bit SUBSTATUS_CONFUSED, a
ret
.FutureSight: ; 2c533
ld a, [EnemyScreens]
bit 5, a
ret
.Heal:
.MorningSun:
.Synthesis:
.Moonlight: ; 2c539
farcall AICheckEnemyMaxHP
jr nc, .NotRedundant
.Teleport:
.Redundant: ; 2c541
ld a, 1
and a
ret
.NotRedundant: ; 2c545
xor a
ret

3598
engine/battle/ai/scoring.asm Normal file

File diff suppressed because it is too large Load Diff

672
engine/battle/ai/switch.asm Executable file
View File

@@ -0,0 +1,672 @@
CheckPlayerMoveTypeMatchups: ; 3484e
; Check how well the moves you've already used
; fare against the enemy's Pokemon. Used to
; score a potential switch.
push hl
push de
push bc
ld a, 10
ld [wEnemyAISwitchScore], a
ld hl, PlayerUsedMoves
ld a, [hl]
and a
jr z, .unknown_moves
ld d, NUM_MOVES
ld e, 0
.loop
ld a, [hli]
and a
jr z, .exit
push hl
dec a
ld hl, Moves + MOVE_POWER
call GetMoveAttr
and a
jr z, .next
inc hl
call GetMoveByte
ld hl, EnemyMonType
call CheckTypeMatchup
ld a, [wTypeMatchup]
cp 10 + 1 ; 1.0 + 0.1
jr nc, .super_effective
and a
jr z, .next
cp 10 ; 1.0
jr nc, .neutral
.not_very_effective
ld a, e
cp 1 ; 0.1
jr nc, .next
ld e, 1
jr .next
.neutral
ld e, 2
jr .next
.super_effective
call .DecreaseScore
pop hl
jr .done
.next
pop hl
dec d
jr nz, .loop
.exit
ld a, e
cp 2
jr z, .done
call .IncreaseScore
ld a, e
and a
jr nz, .done
call .IncreaseScore
jr .done
.unknown_moves
ld a, [BattleMonType1]
ld b, a
ld hl, EnemyMonType1
call CheckTypeMatchup
ld a, [wTypeMatchup]
cp 10 + 1 ; 1.0 + 0.1
jr c, .ok
call .DecreaseScore
.ok
ld a, [BattleMonType2]
cp b
jr z, .ok2
call CheckTypeMatchup
ld a, [wTypeMatchup]
cp 10 + 1 ; 1.0 + 0.1
jr c, .ok2
call .DecreaseScore
.ok2
.done
call .CheckEnemyMoveMatchups
pop bc
pop de
pop hl
ret
; 348de
.CheckEnemyMoveMatchups: ; 348de
ld de, EnemyMonMoves
ld b, NUM_MOVES + 1
ld c, 0
ld a, [wTypeMatchup]
push af
.loop2
dec b
jr z, .exit2
ld a, [de]
and a
jr z, .exit2
inc de
dec a
ld hl, Moves + MOVE_POWER
call GetMoveAttr
and a
jr z, .loop2
inc hl
call GetMoveByte
ld hl, BattleMonType1
call CheckTypeMatchup
ld a, [wTypeMatchup]
; immune
and a
jr z, .loop2
; not very effective
inc c
cp 10
jr c, .loop2
; neutral
inc c
inc c
inc c
inc c
inc c
cp 10
jr z, .loop2
; super effective
ld c, 100
jr .loop2
.exit2
pop af
ld [wTypeMatchup], a
ld a, c
and a
jr z, .doubledown ; double down
cp 5
jr c, .DecreaseScore ; down
cp 100
ret c
jr .IncreaseScore ; up
.doubledown
call .DecreaseScore
.DecreaseScore: ; 34931
ld a, [wEnemyAISwitchScore]
dec a
ld [wEnemyAISwitchScore], a
ret
; 34939
.IncreaseScore: ; 34939
ld a, [wEnemyAISwitchScore]
inc a
ld [wEnemyAISwitchScore], a
ret
; 34941
CheckAbleToSwitch: ; 34941
xor a
ld [wEnemySwitchMonParam], a
call FindAliveEnemyMons
ret c
ld a, [EnemySubStatus1]
bit SUBSTATUS_PERISH, a
jr z, .no_perish
ld a, [EnemyPerishCount]
cp 1
jr nz, .no_perish
; Perish count is 1
call FindAliveEnemyMons
call FindEnemyMonsWithAtLeastQuarterMaxHP
call FindEnemyMonsThatResistPlayer
call FindAliveEnemyMonsWithASuperEffectiveMove
ld a, e
cp 2
jr nz, .not_2
ld a, [wEnemyAISwitchScore]
add $30 ; maximum chance
ld [wEnemySwitchMonParam], a
ret
.not_2
call FindAliveEnemyMons
sla c
sla c
ld b, $ff
.loop1
inc b
sla c
jr nc, .loop1
ld a, b
add $30 ; maximum chance
ld [wEnemySwitchMonParam], a
ret
.no_perish
call CheckPlayerMoveTypeMatchups
ld a, [wEnemyAISwitchScore]
cp 11
ret nc
ld a, [LastPlayerCounterMove]
and a
jr z, .no_last_counter_move
call FindEnemyMonsImmuneToLastCounterMove
ld a, [wEnemyAISwitchScore]
and a
jr z, .no_last_counter_move
ld c, a
call FindEnemyMonsWithASuperEffectiveMove
ld a, [wEnemyAISwitchScore]
cp $ff
ret z
ld b, a
ld a, e
cp 2
jr z, .not_2_again
call CheckPlayerMoveTypeMatchups
ld a, [wEnemyAISwitchScore]
cp 10
ret nc
ld a, b
add $10
ld [wEnemySwitchMonParam], a
ret
.not_2_again
ld c, $10
call CheckPlayerMoveTypeMatchups
ld a, [wEnemyAISwitchScore]
cp 10
jr nc, .okay
ld c, $20
.okay
ld a, b
add c
ld [wEnemySwitchMonParam], a
ret
.no_last_counter_move
call CheckPlayerMoveTypeMatchups
ld a, [wEnemyAISwitchScore]
cp 10
ret nc
call FindAliveEnemyMons
call FindEnemyMonsWithAtLeastQuarterMaxHP
call FindEnemyMonsThatResistPlayer
call FindAliveEnemyMonsWithASuperEffectiveMove
ld a, e
cp $2
ret nz
ld a, [wEnemyAISwitchScore]
add $10
ld [wEnemySwitchMonParam], a
ret
; 349f4
FindAliveEnemyMons: ; 349f4
ld a, [OTPartyCount]
cp 2
jr c, .only_one
ld d, a
ld e, 0
ld b, 1 << (PARTY_LENGTH - 1)
ld c, 0
ld hl, OTPartyMon1HP
.loop
ld a, [CurOTMon]
cp e
jr z, .next
push bc
ld b, [hl]
inc hl
ld a, [hld]
or b
pop bc
jr z, .next
ld a, c
or b
ld c, a
.next
srl b
push bc
ld bc, PARTYMON_STRUCT_LENGTH
add hl, bc
pop bc
inc e
dec d
jr nz, .loop
ld a, c
and a
jr nz, .more_than_one
.only_one
scf
ret
.more_than_one
and a
ret
; 34a2a
FindEnemyMonsImmuneToLastCounterMove: ; 34a2a
ld hl, OTPartyMon1
ld a, [OTPartyCount]
ld b, a
ld c, 1 << (PARTY_LENGTH - 1)
ld d, 0
xor a
ld [wEnemyAISwitchScore], a
.loop
ld a, [CurOTMon]
cp d
push hl
jr z, .next
push hl
push bc
; If the Pokemon has at least 1 HP...
ld bc, MON_HP
add hl, bc
pop bc
ld a, [hli]
or [hl]
pop hl
jr z, .next
ld a, [hl]
ld [CurSpecies], a
call GetBaseData
; the player's last move is damaging...
ld a, [LastPlayerCounterMove]
dec a
ld hl, Moves + MOVE_POWER
call GetMoveAttr
and a
jr z, .next
; and the Pokemon is immune to it...
inc hl
call GetMoveByte
ld hl, BaseType
call CheckTypeMatchup
ld a, [wTypeMatchup]
and a
jr nz, .next
; ... encourage that Pokemon.
ld a, [wEnemyAISwitchScore]
or c
ld [wEnemyAISwitchScore], a
.next
pop hl
dec b
ret z
push bc
ld bc, PARTYMON_STRUCT_LENGTH
add hl, bc
pop bc
inc d
srl c
jr .loop
; 34a85
FindAliveEnemyMonsWithASuperEffectiveMove: ; 34a85
push bc
ld a, [OTPartyCount]
ld e, a
ld hl, OTPartyMon1HP
ld b, 1 << (PARTY_LENGTH - 1)
ld c, 0
.loop
ld a, [hli]
or [hl]
jr z, .next
ld a, b
or c
ld c, a
.next
srl b
push bc
ld bc, PartyMon2HP - (PartyMon1HP + 1)
add hl, bc
pop bc
dec e
jr nz, .loop
ld a, c
pop bc
and c
ld c, a
FindEnemyMonsWithASuperEffectiveMove: ; 34aa7
ld a, -1
ld [wEnemyAISwitchScore], a
ld hl, OTPartyMon1Moves
ld b, 1 << (PARTY_LENGTH - 1)
ld d, 0
ld e, 0
.loop
ld a, b
and c
jr z, .next
push hl
push bc
; for move on mon:
ld b, NUM_MOVES
ld c, 0
.loop3
; if move is None: break
ld a, [hli]
and a
push hl
jr z, .break3
; if move has no power: continue
dec a
ld hl, Moves + MOVE_POWER
call GetMoveAttr
and a
jr z, .nope
; check type matchups
inc hl
call GetMoveByte
ld hl, BattleMonType1
call CheckTypeMatchup
; if immune or not very effective: continue
ld a, [wTypeMatchup]
cp 10
jr c, .nope
; if neutral: load 1 and continue
ld e, 1
cp 10 + 1
jr c, .nope
; if super-effective: load 2 and break
ld e, 2
jr .break3
.nope
pop hl
dec b
jr nz, .loop3
jr .done
.break3
pop hl
.done
ld a, e
pop bc
pop hl
cp 2
jr z, .done2 ; at least one move is super-effective
cp 1
jr nz, .next ; no move does more than half damage
; encourage this pokemon
ld a, d
or b
ld d, a
jr .next ; such a long jump
.next
; next pokemon?
push bc
ld bc, PARTYMON_STRUCT_LENGTH
add hl, bc
pop bc
srl b
jr nc, .loop
; if no pokemon has a super-effective move: return
ld a, d
ld b, a
and a
ret z
.done2
; convert the bit flag to an int and return
push bc
sla b
sla b
ld c, $ff
.loop2
inc c
sla b
jr nc, .loop2
ld a, c
ld [wEnemyAISwitchScore], a
pop bc
ret
; 34b20
FindEnemyMonsThatResistPlayer: ; 34b20
push bc
ld hl, OTPartySpecies
ld b, 1 << (PARTY_LENGTH - 1)
ld c, 0
.loop
ld a, [hli]
cp $ff
jr z, .done
push hl
ld [CurSpecies], a
call GetBaseData
ld a, [LastPlayerCounterMove]
and a
jr z, .skip_move
dec a
ld hl, Moves + MOVE_POWER
call GetMoveAttr
and a
jr z, .skip_move
inc hl
call GetMoveByte
jr .check_type
.skip_move
ld a, [BattleMonType1]
ld hl, BaseType
call CheckTypeMatchup
ld a, [wTypeMatchup]
cp 10 + 1
jr nc, .dont_choose_mon
ld a, [BattleMonType2]
.check_type
ld hl, BaseType
call CheckTypeMatchup
ld a, [wTypeMatchup]
cp 10 + 1
jr nc, .dont_choose_mon
ld a, b
or c
ld c, a
.dont_choose_mon
srl b
pop hl
jr .loop
.done
ld a, c
pop bc
and c
ld c, a
ret
; 34b77
FindEnemyMonsWithAtLeastQuarterMaxHP: ; 34b77
push bc
ld de, OTPartySpecies
ld b, 1 << (PARTY_LENGTH - 1)
ld c, 0
ld hl, OTPartyMon1HP
.loop
ld a, [de]
inc de
cp $ff
jr z, .done
push hl
push bc
ld b, [hl]
inc hl
ld c, [hl]
inc hl
inc hl
; hl = MaxHP + 1
; bc = [CurHP] * 4
srl c
rl b
srl c
rl b
; if bc >= [hl], encourage
ld a, [hld]
cp c
ld a, [hl]
sbc b
pop bc
jr nc, .next
ld a, b
or c
ld c, a
.next
srl b
pop hl
push bc
ld bc, PARTYMON_STRUCT_LENGTH
add hl, bc
pop bc
jr .loop
.done
ld a, c
pop bc
and c
ld c, a
ret
; 34bb1

435
engine/battle/anim_hp_bar.asm Executable file
View File

@@ -0,0 +1,435 @@
_AnimateHPBar: ; d627
call .IsMaximumMoreThan48Pixels
jr c, .MoreThan48Pixels
call .ComputePixels
.ShortAnimLoop:
push bc
push hl
call ShortAnim_UpdateVariables
pop hl
pop bc
push af
push bc
push hl
call ShortHPBarAnim_UpdateTiles
call HPBarAnim_BGMapUpdate
pop hl
pop bc
pop af
jr nc, .ShortAnimLoop
ret
.MoreThan48Pixels:
call .ComputePixels
.LongAnimLoop:
push bc
push hl
call LongAnim_UpdateVariables
pop hl
pop bc
ret c
push af
push bc
push hl
call LongHPBarAnim_UpdateTiles
call HPBarAnim_BGMapUpdate
pop hl
pop bc
pop af
jr nc, .LongAnimLoop
ret
; d65f
.IsMaximumMoreThan48Pixels: ; d65f
ld a, [wCurHPAnimMaxHP + 1]
and a
jr nz, .player
ld a, [wCurHPAnimMaxHP]
cp 6 * 8
jr nc, .player
and a
ret
.player
scf
ret
; d670
.ComputePixels: ; d670
push hl
ld hl, wCurHPAnimMaxHP
ld a, [hli]
ld e, a
ld a, [hli]
ld d, a
ld a, [hli]
ld c, a
ld a, [hli]
ld b, a
pop hl
call ComputeHPBarPixels
ld a, e
ld [wCurHPBarPixels], a
ld a, [wCurHPAnimNewHP]
ld c, a
ld a, [wCurHPAnimNewHP + 1]
ld b, a
ld a, [wCurHPAnimMaxHP]
ld e, a
ld a, [wCurHPAnimMaxHP + 1]
ld d, a
call ComputeHPBarPixels
ld a, e
ld [wNewHPBarPixels], a
push hl
ld hl, wCurHPAnimOldHP
ld a, [hli]
ld c, a
ld a, [hli]
ld b, a
ld a, [hli]
ld e, a
ld a, [hli]
ld d, a
pop hl
ld a, e
sub c
ld e, a
ld a, d
sbc b
ld d, a
jr c, .negative
ld a, [wCurHPAnimOldHP]
ld [wCurHPAnimLowHP], a
ld a, [wCurHPAnimNewHP]
ld [wCurHPAnimHighHP], a
ld bc, 1
jr .got_direction
.negative
ld a, [wCurHPAnimOldHP]
ld [wCurHPAnimHighHP], a
ld a, [wCurHPAnimNewHP]
ld [wCurHPAnimLowHP], a
ld a, e
xor $ff
inc a
ld e, a
ld a, d
xor $ff
ld d, a
ld bc, -1
.got_direction
ld a, d
ld [wCurHPAnimDeltaHP], a
ld a, e
ld [wCurHPAnimDeltaHP + 1], a
ret
; d6e2
ShortAnim_UpdateVariables: ; d6e2
ld hl, wCurHPBarPixels
ld a, [wNewHPBarPixels]
cp [hl]
jr nz, .not_finished
scf
ret
.not_finished
ld a, c
add [hl]
ld [hl], a
call ShortHPBar_CalcPixelFrame
and a
ret
; d6f5
LongAnim_UpdateVariables: ; d6f5
.loop
ld hl, wCurHPAnimOldHP
ld a, [hli]
ld e, a
ld a, [hli]
ld d, a
ld a, e
cp [hl]
jr nz, .next
inc hl
ld a, d
cp [hl]
jr nz, .next
scf
ret
.next
ld l, e
ld h, d
add hl, bc
ld a, l
ld [wCurHPAnimOldHP], a
ld a, h
ld [wCurHPAnimOldHP + 1], a
push hl
push de
push bc
ld hl, wCurHPAnimMaxHP
ld a, [hli]
ld e, a
ld a, [hli]
ld d, a
ld a, [hli]
ld c, a
ld a, [hli]
ld b, a
; This routine is buggy. The result from ComputeHPBarPixels is stored
; in e. However, the pop de opcode deletes this result before it is even
; used. The game then proceeds as though it never deleted that output.
; To fix, uncomment the line below.
call ComputeHPBarPixels
; ld a, e
pop bc
pop de
pop hl
ld a, e ; Comment or delete this line to fix the above bug.
ld hl, wCurHPBarPixels
cp [hl]
jr z, .loop
ld [hl], a
and a
ret
; d730
ShortHPBarAnim_UpdateTiles: ; d730
call HPBarAnim_UpdateHPRemaining
ld d, $6
ld a, [wWhichHPBar]
and $1
ld b, a
ld a, [wCurHPBarPixels]
ld e, a
ld c, a
push de
call HPBarAnim_RedrawHPBar
pop de
call HPBarAnim_PaletteUpdate
ret
; d749
LongHPBarAnim_UpdateTiles: ; d749
call HPBarAnim_UpdateHPRemaining
ld a, [wCurHPAnimOldHP]
ld c, a
ld a, [wCurHPAnimOldHP + 1]
ld b, a
ld a, [wCurHPAnimMaxHP]
ld e, a
ld a, [wCurHPAnimMaxHP + 1]
ld d, a
call ComputeHPBarPixels
ld c, e
ld d, $6
ld a, [wWhichHPBar]
and $1
ld b, a
push de
call HPBarAnim_RedrawHPBar
pop de
call HPBarAnim_PaletteUpdate
ret
; d771
HPBarAnim_RedrawHPBar: ; d771
ld a, [wWhichHPBar]
cp $2
jr nz, .skip
ld a, 2 * SCREEN_WIDTH
add l
ld l, a
ld a, 0
adc h
ld h, a
.skip
call DrawBattleHPBar
ret
; d784
HPBarAnim_UpdateHPRemaining: ; d784
ld a, [wWhichHPBar]
and a
ret z
cp $1
jr z, .load_15
ld de, SCREEN_WIDTH + 2
jr .loaded_de
.load_15
ld de, SCREEN_WIDTH + 1
.loaded_de
push hl
add hl, de
ld a, " "
ld [hli], a
ld [hli], a
ld [hld], a
dec hl
ld a, [wCurHPAnimOldHP]
ld [StringBuffer2 + 1], a
ld a, [wCurHPAnimOldHP + 1]
ld [StringBuffer2], a
ld de, StringBuffer2
lb bc, 2, 3
call PrintNum
pop hl
ret
; d7b4
HPBarAnim_PaletteUpdate: ; d7b4
ld a, [hCGB]
and a
ret z
ld hl, wCurHPAnimPal
call SetHPPal
ld a, [wCurHPAnimPal]
ld c, a
farcall ApplyHPBarPals
ret
; d7c9
HPBarAnim_BGMapUpdate: ; d7c9
ld a, [hCGB]
and a
jr nz, .cgb
call DelayFrame
call DelayFrame
ret
.cgb
ld a, [wWhichHPBar]
and a
jr z, .load_0
cp $1
jr z, .load_1
ld a, [CurPartyMon]
cp $3
jr nc, .bottom_half_of_screen
ld c, $0
jr .got_third
.bottom_half_of_screen
ld c, $1
.got_third
push af
cp $2
jr z, .skip_delay
cp $5
jr z, .skip_delay
ld a, $2
ld [hBGMapMode], a
ld a, c
ld [hBGMapThird], a
call DelayFrame
.skip_delay
ld a, $1
ld [hBGMapMode], a
ld a, c
ld [hBGMapThird], a
call DelayFrame
pop af
cp $2
jr z, .two_frames
cp $5
jr z, .two_frames
ret
.two_frames
inc c
ld a, $2
ld [hBGMapMode], a
ld a, c
ld [hBGMapThird], a
call DelayFrame
ld a, $1
ld [hBGMapMode], a
ld a, c
ld [hBGMapThird], a
call DelayFrame
ret
.load_0
ld c, $0
jr .finish
.load_1
ld c, $1
.finish
call DelayFrame
ld a, c
ld [hBGMapThird], a
call DelayFrame
ret
; d839
ShortHPBar_CalcPixelFrame: ; d839
ld a, [wCurHPAnimMaxHP]
ld c, a
ld b, 0
ld hl, 0
ld a, [wCurHPBarPixels]
cp 6 * 8
jr nc, .return_max
and a
jr z, .return_zero
call AddNTimes
ld b, 0
; This routine is buggy. If [wCurHPAnimMaxHP] * [wCurHPBarPixels] is divisible
; by 48, the loop runs one extra time. To fix, uncomment the line below.
.loop
ld a, l
sub 6 * 8
ld l, a
ld a, h
sbc $0
ld h, a
; jr z, .done
jr c, .done
inc b
jr .loop
.done
push bc
ld bc, $80
add hl, bc
pop bc
ld a, l
sub 6 * 8
ld l, a
ld a, h
sbc $0
ld h, a
jr c, .no_carry
inc b
.no_carry
ld a, [wCurHPAnimLowHP]
cp b
jr nc, .finish
ld a, [wCurHPAnimHighHP]
cp b
jr c, .finish
ld a, b
.finish
ld [wCurHPAnimOldHP], a
ret
.return_zero
xor a
ld [wCurHPAnimOldHP], a
ret
.return_max
ld a, [wCurHPAnimMaxHP]
ld [wCurHPAnimOldHP], a
ret
; d88c

View File

@@ -0,0 +1,873 @@
Predef_StartBattle: ; 8c20f
call .InitGFX
ld a, [rBGP]
ld [wBGP], a
ld a, [rOBP0]
ld [wOBP0], a
ld a, [rOBP1]
ld [wOBP1], a
call DelayFrame
ld hl, hVBlank
ld a, [hl]
push af
ld [hl], $1
.loop
ld a, [wJumptableIndex]
bit 7, a
jr nz, .done
call FlashyTransitionToBattle
call DelayFrame
jr .loop
.done
ld a, [rSVBK]
push af
ld a, $5
ld [rSVBK], a
ld hl, UnknBGPals
ld bc, 8 palettes
xor a
call ByteFill
pop af
ld [rSVBK], a
ld a, %11111111
ld [wBGP], a
call DmgToCgbBGPals
call DelayFrame
xor a
ld [hLCDCPointer], a
ld [hLYOverrideStart], a
ld [hLYOverrideEnd], a
ld [hSCY], a
ld a, $1
ld [rSVBK], a
pop af
ld [hVBlank], a
call DelayFrame
ret
; 8c26d
.InitGFX: ; 8c26d
ld a, [wLinkMode]
cp LINK_MOBILE
jr z, .mobile
farcall ReanchorBGMap_NoOAMUpdate
call UpdateSprites
call DelayFrame
call .NonMobile_LoadPokeballTiles
call BattleStart_CopyTilemapAtOnce
jr .resume
.mobile
call LoadTrainerBattlePokeballTiles
.resume
ld a, SCREEN_HEIGHT_PX
ld [hWY], a
call DelayFrame
xor a
ld [hBGMapMode], a
ld hl, wJumptableIndex
xor a
ld [hli], a
ld [hli], a
ld [hl], a
call WipeLYOverrides
ret
; 8c2a0
.NonMobile_LoadPokeballTiles: ; 8c2a0
call LoadTrainerBattlePokeballTiles
hlbgcoord 0, 0
call ConvertTrainerBattlePokeballTilesTo2bpp
ret
; 8c2aa
LoadTrainerBattlePokeballTiles:
; Load the tiles used in the Pokeball Graphic that fills the screen
; at the start of every Trainer battle.
ld de, TrainerBattlePokeballTiles
ld hl, VTiles1 tile $7e
ld b, BANK(TrainerBattlePokeballTiles)
ld c, 2
call Request2bpp
ld a, [rVBK]
push af
ld a, $1
ld [rVBK], a
ld de, TrainerBattlePokeballTiles
ld hl, VTiles4 tile $7e
ld b, BANK(TrainerBattlePokeballTiles)
ld c, 2
call Request2bpp
pop af
ld [rVBK], a
ret
; 8c2cf
ConvertTrainerBattlePokeballTilesTo2bpp: ; 8c2cf
ld a, [rSVBK]
push af
ld a, $6
ld [rSVBK], a
push hl
ld hl, wDecompressScratch
ld bc, $28 tiles
.loop
ld [hl], -1
inc hl
dec bc
ld a, c
or b
jr nz, .loop
pop hl
ld de, wDecompressScratch
ld b, BANK(ConvertTrainerBattlePokeballTilesTo2bpp) ; BANK(@)
ld c, $28
call Request2bpp
pop af
ld [rSVBK], a
ret
; 8c2f4
TrainerBattlePokeballTiles: ; 8c2f4
INCBIN "gfx/overworld/trainer_battle_pokeball_tiles.2bpp"
FlashyTransitionToBattle: ; 8c314
jumptable .dw, wJumptableIndex
; 8c323
.dw ; 8c323 (23:4323)
dw StartTrainerBattle_DetermineWhichAnimation ; 00
; Animation 1: cave
dw StartTrainerBattle_LoadPokeBallGraphics ; 01
dw StartTrainerBattle_SetUpBGMap ; 02
dw StartTrainerBattle_Flash ; 03
dw StartTrainerBattle_Flash ; 04
dw StartTrainerBattle_Flash ; 05
dw StartTrainerBattle_NextScene ; 06
dw StartTrainerBattle_SetUpForWavyOutro ; 07
dw StartTrainerBattle_SineWave ; 08
; Animation 2: cave, stronger
dw StartTrainerBattle_LoadPokeBallGraphics ; 09
dw StartTrainerBattle_SetUpBGMap ; 0a
dw StartTrainerBattle_Flash ; 0b
dw StartTrainerBattle_Flash ; 0c
dw StartTrainerBattle_Flash ; 0d
dw StartTrainerBattle_NextScene ; 0e
; There is no setup for this one
dw StartTrainerBattle_ZoomToBlack ; 0f
; Animation 3: no cave
dw StartTrainerBattle_LoadPokeBallGraphics ; 10
dw StartTrainerBattle_SetUpBGMap ; 11
dw StartTrainerBattle_Flash ; 12
dw StartTrainerBattle_Flash ; 13
dw StartTrainerBattle_Flash ; 14
dw StartTrainerBattle_NextScene ; 15
dw StartTrainerBattle_SetUpForSpinOutro ; 16
dw StartTrainerBattle_SpinToBlack ; 17
; Animation 4: no cave, stronger
dw StartTrainerBattle_LoadPokeBallGraphics ; 18
dw StartTrainerBattle_SetUpBGMap ; 19
dw StartTrainerBattle_Flash ; 1a
dw StartTrainerBattle_Flash ; 1b
dw StartTrainerBattle_Flash ; 1c
dw StartTrainerBattle_NextScene ; 1d
dw StartTrainerBattle_SetUpForRandomScatterOutro ; 1e
dw StartTrainerBattle_SpeckleToBlack ; 1f
; All animations jump to here.
dw StartTrainerBattle_Finish ; 20
StartTrainerBattle_DetermineWhichAnimation: ; 8c365 (23:4365)
; The screen flashes a different number of times depending on the level of
; your lead Pokemon relative to the opponent's.
; BUG: BattleMonLevel and EnemyMonLevel are not set at this point, so whatever
; values happen to be there will determine the animation.
ld de, 0
ld a, [BattleMonLevel]
add 3
ld hl, EnemyMonLevel
cp [hl]
jr nc, .okay
set 0, e
.okay
ld a, [wEnvironment]
cp CAVE
jr z, .okay2
cp ENVIRONMENT_5
jr z, .okay2
cp DUNGEON
jr z, .okay2
set 1, e
.okay2
ld hl, .StartingPoints
add hl, de
ld a, [hl]
ld [wJumptableIndex], a
ret
; 8c38f (23:438f)
.StartingPoints: ; 8c38f
db 1, 9
db 16, 24
; 8c393
StartTrainerBattle_Finish: ; 8c393 (23:4393)
call ClearSprites
ld a, $80
ld [wJumptableIndex], a
ret
StartTrainerBattle_NextScene: ; 8c39c (23:439c)
ld hl, wJumptableIndex
inc [hl]
ret
StartTrainerBattle_SetUpBGMap: ; 8c3a1 (23:43a1)
call StartTrainerBattle_NextScene
xor a
ld [wcf64], a
ld [hBGMapMode], a
ret
StartTrainerBattle_Flash: ; 8c3ab (23:43ab)
call .DoFlashAnimation
ret nc
call StartTrainerBattle_NextScene
ret
.DoFlashAnimation: ; 8c3b3 (23:43b3)
ld a, [wTimeOfDayPalset]
cp %11111111 ; dark cave
jr z, .done
ld hl, wcf64
ld a, [hl]
inc [hl]
srl a
ld e, a
ld d, 0
ld hl, .pals
add hl, de
ld a, [hl]
cp %00000001
jr z, .done
ld [wBGP], a
call DmgToCgbBGPals
and a
ret
.done
xor a
ld [wcf64], a
scf
ret
; 8c3db (23:43db)
.pals ; 8c3db
db %11111001 ; 3321
db %11111110 ; 3332
db %11111111 ; 3333
db %11111110 ; 3332
db %11111001 ; 3321
db %11100100 ; 3210
db %10010000 ; 2100
db %01000000 ; 1000
db %00000000 ; 0000
db %01000000 ; 1000
db %10010000 ; 2100
db %11100100 ; 3210
db %00000001 ; 0001
; 8c3e8
StartTrainerBattle_SetUpForWavyOutro: ; 8c3e8 (23:43e8)
farcall Function5602
ld a, $5 ; BANK(LYOverrides)
ld [rSVBK], a
call StartTrainerBattle_NextScene
ld a, rSCX - $ff00
ld [hLCDCPointer], a
xor a
ld [hLYOverrideStart], a
ld a, $90
ld [hLYOverrideEnd], a
xor a
ld [wcf64], a
ld [wcf65], a
ret
StartTrainerBattle_SineWave: ; 8c408 (23:4408)
ld a, [wcf64]
cp $60
jr nc, .end
call .DoSineWave
ret
.end
ld a, $20
ld [wJumptableIndex], a
ret
.DoSineWave: ; 8c419 (23:4419)
ld hl, wcf65
ld a, [hl]
inc [hl]
ld hl, wcf64
ld d, [hl]
add [hl]
ld [hl], a
ld a, LYOverridesEnd - LYOverrides
ld bc, LYOverrides
ld e, $0
.loop
push af
push de
ld a, e
call StartTrainerBattle_DrawSineWave
ld [bc], a
inc bc
pop de
ld a, e
add $2
ld e, a
pop af
dec a
jr nz, .loop
ret
StartTrainerBattle_SetUpForSpinOutro: ; 8c43d (23:443d)
farcall Function5602
ld a, $5 ; BANK(LYOverrides)
ld [rSVBK], a
call StartTrainerBattle_NextScene
xor a
ld [wcf64], a
ret
StartTrainerBattle_SpinToBlack: ; 8c44f (23:444f)
xor a
ld [hBGMapMode], a
ld a, [wcf64]
ld e, a
ld d, 0
ld hl, .spintable
rept 5
add hl, de
endr
ld a, [hli]
cp -1
jr z, .end
ld [wcf65], a
call .load
ld a, $1
ld [hBGMapMode], a
call DelayFrame
call DelayFrame
ld hl, wcf64
inc [hl]
ret
.end
ld a, $1
ld [hBGMapMode], a
call DelayFrame
call DelayFrame
call DelayFrame
xor a
ld [hBGMapMode], a
ld a, $20
ld [wJumptableIndex], a
ret
; 8c490 (23:4490)
; quadrants
const_def
const UPPER_LEFT
const UPPER_RIGHT
const LOWER_LEFT
const LOWER_RIGHT
.spintable ; 8c490
spintable_entry: MACRO
db \1
dw .wedge\2
dwcoord \3, \4
ENDM
spintable_entry UPPER_LEFT, 1, 1, 6
spintable_entry UPPER_LEFT, 2, 0, 3
spintable_entry UPPER_LEFT, 3, 1, 0
spintable_entry UPPER_LEFT, 4, 5, 0
spintable_entry UPPER_LEFT, 5, 9, 0
spintable_entry UPPER_RIGHT, 5, 10, 0
spintable_entry UPPER_RIGHT, 4, 14, 0
spintable_entry UPPER_RIGHT, 3, 18, 0
spintable_entry UPPER_RIGHT, 2, 19, 3
spintable_entry UPPER_RIGHT, 1, 18, 6
spintable_entry LOWER_RIGHT, 1, 18, 11
spintable_entry LOWER_RIGHT, 2, 19, 14
spintable_entry LOWER_RIGHT, 3, 18, 17
spintable_entry LOWER_RIGHT, 4, 14, 17
spintable_entry LOWER_RIGHT, 5, 10, 17
spintable_entry LOWER_LEFT, 5, 9, 17
spintable_entry LOWER_LEFT, 4, 5, 17
spintable_entry LOWER_LEFT, 3, 1, 17
spintable_entry LOWER_LEFT, 2, 0, 14
spintable_entry LOWER_LEFT, 1, 1, 11
db -1
; 8c4f5
.load ; 8c4f5 (23:44f5)
ld a, [hli]
ld e, a
ld a, [hli]
ld d, a
ld a, [hli]
ld h, [hl]
ld l, a
.loop
push hl
ld a, [de]
ld c, a
inc de
.loop1
ld [hl], $ff
ld a, [wcf65]
bit 0, a
jr z, .leftside
inc hl
jr .okay1
.leftside
dec hl
.okay1
dec c
jr nz, .loop1
pop hl
ld a, [wcf65]
bit 1, a
ld bc, SCREEN_WIDTH
jr z, .upper
ld bc, -SCREEN_WIDTH
.upper
add hl, bc
ld a, [de]
inc de
cp -1
ret z
and a
jr z, .loop
ld c, a
.loop2
ld a, [wcf65]
bit 0, a
jr z, .leftside2
dec hl
jr .okay2
.leftside2
inc hl
.okay2
dec c
jr nz, .loop2
jr .loop
; 8c538 (23:4538)
.wedge1 db 2, 3, 5, 4, 9, -1
.wedge2 db 1, 1, 2, 2, 4, 2, 4, 2, 3, -1
.wedge3 db 2, 1, 3, 1, 4, 1, 4, 1, 4, 1, 3, 1, 2, 1, 1, 1, 1, -1
.wedge4 db 4, 1, 4, 0, 3, 1, 3, 0, 2, 1, 2, 0, 1, -1
.wedge5 db 4, 0, 3, 0, 3, 0, 2, 0, 2, 0, 1, 0, 1, 0, 1, -1
; 8c578
StartTrainerBattle_SetUpForRandomScatterOutro: ; 8c578 (23:4578)
farcall Function5602
ld a, $5 ; BANK(LYOverrides)
ld [rSVBK], a
call StartTrainerBattle_NextScene
ld a, $10
ld [wcf64], a
ld a, $1
ld [hBGMapMode], a
ret
StartTrainerBattle_SpeckleToBlack: ; 8c58f (23:458f)
ld hl, wcf64
ld a, [hl]
and a
jr z, .done
dec [hl]
ld c, $c
.loop
push bc
call .BlackOutRandomTile
pop bc
dec c
jr nz, .loop
ret
.done
ld a, $1
ld [hBGMapMode], a
call DelayFrame
call DelayFrame
call DelayFrame
xor a
ld [hBGMapMode], a
ld a, $20
ld [wJumptableIndex], a
ret
.BlackOutRandomTile: ; 8c5b8 (23:45b8)
.y_loop
call Random
cp SCREEN_HEIGHT
jr nc, .y_loop
ld b, a
.x_loop
call Random
cp SCREEN_WIDTH
jr nc, .x_loop
ld c, a
hlcoord 0, -1
ld de, SCREEN_WIDTH
inc b
.row_loop
add hl, de
dec b
jr nz, .row_loop
add hl, bc
; If the tile has already been blacked out,
; sample a new tile
ld a, [hl]
cp $ff
jr z, .y_loop
ld [hl], $ff
ret
StartTrainerBattle_LoadPokeBallGraphics: ; 8c5dc (23:45dc)
ld a, [OtherTrainerClass]
and a
jp z, .nextscene ; don't need to be here if wild
xor a
ld [hBGMapMode], a
hlcoord 0, 0, AttrMap
ld bc, SCREEN_HEIGHT * SCREEN_WIDTH
inc b
inc c
jr .enter_loop_midway
.loop
; set all pals to 7
ld a, [hl]
or %00000111
ld [hli], a
.enter_loop_midway
dec c
jr nz, .loop
dec b
jr nz, .loop
call .loadpokeballgfx ; ld a, [OtherTrainerClass] \ ld de, PokeBallTransition \ ret
hlcoord 2, 1
ld b, SCREEN_WIDTH - 4
.loop2
push hl
ld c, 2
.loop3
push hl
ld a, [de]
inc de
.loop4
; Loading is done bit by bit
and a
jr z, .done
sla a
jr nc, .no_load
ld [hl], $fe
.no_load
inc hl
jr .loop4
.done
pop hl
push bc
ld bc, (SCREEN_WIDTH - 4) / 2
add hl, bc
pop bc
dec c
jr nz, .loop3
pop hl
push bc
ld bc, SCREEN_WIDTH
add hl, bc
pop bc
dec b
jr nz, .loop2
ld a, [hCGB]
and a
jr nz, .cgb
ld a, $1
ld [hBGMapMode], a
call DelayFrame
call DelayFrame
jr .nextscene
.cgb
ld hl, .daypals
ld a, [TimeOfDayPal]
and (1 << 2) - 1
cp 3
jr nz, .daytime
ld hl, .nightpals
.daytime
ld a, [rSVBK]
push af
ld a, $5 ; WRAM5 = palettes
ld [rSVBK], a
call .copypals
push hl
ld de, UnknBGPals palette PAL_BG_TEXT
ld bc, 1 palettes
call CopyBytes
pop hl
ld de, BGPals palette PAL_BG_TEXT
ld bc, 1 palettes
call CopyBytes
pop af
ld [rSVBK], a
ld a, $1
ld [hCGBPalUpdate], a
call DelayFrame
call BattleStart_CopyTilemapAtOnce
.nextscene ; 8c673 (23:4673)
call StartTrainerBattle_NextScene
ret
.copypals ; 8c677 (23:4677)
ld de, UnknBGPals palette PAL_BG_TEXT
call .copy
ld de, BGPals palette PAL_BG_TEXT
call .copy
ld de, UnknOBPals palette PAL_OW_TREE
call .copy
ld de, OBPals palette PAL_OW_TREE
call .copy
ld de, UnknOBPals palette PAL_OW_ROCK
call .copy
ld de, OBPals palette PAL_OW_ROCK
.copy ; 8c698 (23:4698)
push hl
ld bc, 1 palettes
call CopyBytes
pop hl
ret
; 8c6a1 (23:46a1)
.daypals ; 8c6a1
RGB 31, 18, 29
RGB 31, 11, 15
RGB 31, 05, 05
RGB 07, 07, 07
; 8c6a9
.nightpals ; 8c6a9
RGB 31, 18, 29
RGB 31, 05, 05
RGB 31, 05, 05
RGB 31, 05, 05
.loadpokeballgfx
ld a, [OtherTrainerClass]
ld de, PokeBallTransition
ret
PokeBallTransition:
db %00000011, %11000000
db %00001111, %11110000
db %00111100, %00111100
db %00110000, %00001100
db %01100000, %00000110
db %01100011, %11000110
db %11000110, %01100011
db %11111100, %00111111
db %11111100, %00111111
db %11000110, %01100011
db %01100011, %11000110
db %01100000, %00000110
db %00110000, %00001100
db %00111100, %00111100
db %00001111, %11110000
db %00000011, %11000000
WipeLYOverrides: ; 8c6d8
ld a, [rSVBK]
push af
ld a, $5
ld [rSVBK], a
ld hl, LYOverrides
call .wipe
ld hl, LYOverridesBackup
call .wipe
pop af
ld [rSVBK], a
ret
; 8c6ef
.wipe ; 8c6ef
xor a
ld c, SCREEN_HEIGHT_PX
.loop
ld [hli], a
dec c
jr nz, .loop
ret
; 8c6f7
StartTrainerBattle_DrawSineWave: ; 8c6f7 (23:46f7)
and (1 << 6) - 1
cp 1 << 5
jr nc, .okay
call .DoSineWave
ld a, h
ret
.okay
and (1 << 5) - 1
call .DoSineWave
ld a, h
xor -1 ; cpl
inc a
ret
.DoSineWave: ; 8c70c (23:470c)
ld e, a
ld a, d
ld d, 0
ld hl, .sinewave
add hl, de
add hl, de
ld e, [hl]
inc hl
ld d, [hl]
ld hl, 0
.loop
srl a
jr nc, .skip
add hl, de
.skip
sla e
rl d
and a
jr nz, .loop
ret
; 8c728 (23:4728)
.sinewave ; 8c728
sine_wave $100
; 8c768
StartTrainerBattle_ZoomToBlack: ; 8c768 (23:4768)
farcall Function5602
ld de, .boxes
.loop
ld a, [de]
cp -1
jr z, .done
inc de
ld c, a
ld a, [de]
inc de
ld b, a
ld a, [de]
inc de
ld l, a
ld a, [de]
inc de
ld h, a
xor a
ld [hBGMapMode], a
call .Copy
call WaitBGMap
jr .loop
.done
ld a, $20
ld [wJumptableIndex], a
ret
; 8c792 (23:4792)
.boxes ; 8c792
zoombox: macro
; width, height, start y, start x
db \1, \2
dwcoord \3, \4
endm
zoombox 4, 2, 8, 8
zoombox 6, 4, 7, 7
zoombox 8, 6, 6, 6
zoombox 10, 8, 5, 5
zoombox 12, 10, 4, 4
zoombox 14, 12, 3, 3
zoombox 16, 14, 2, 2
zoombox 18, 16, 1, 1
zoombox 20, 18, 0, 0
db -1
; 8c7b7
.Copy: ; 8c7b7 (23:47b7)
ld a, $ff
.row
push bc
push hl
.col
ld [hli], a
dec c
jr nz, .col
pop hl
ld bc, SCREEN_WIDTH
add hl, bc
pop bc
dec b
jr nz, .row
ret
; 8c7c9 (23:47c9)
Function8c7c9:
; XXX
ld a, $1
ld [hBGMapMode], a
call WaitBGMap
xor a
ld [hBGMapMode], a
ret

9511
engine/battle/core.asm Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,79 @@
BattleCommand_Attract: ; 377ce
; attract
ld a, [AttackMissed]
and a
jr nz, .failed
call CheckOppositeGender
jr c, .failed
call CheckHiddenOpponent
jr nz, .failed
ld a, BATTLE_VARS_SUBSTATUS1_OPP
call GetBattleVarAddr
bit SUBSTATUS_IN_LOVE, [hl]
jr nz, .failed
set SUBSTATUS_IN_LOVE, [hl]
call AnimateCurrentMove
; 'fell in love!'
ld hl, FellInLoveText
jp StdBattleTextBox
.failed
jp FailAttract
; 377f5
CheckOppositeGender: ; 377f5
ld a, MON_SPECIES
call BattlePartyAttr
ld a, [hl]
ld [CurPartySpecies], a
ld a, [CurBattleMon]
ld [CurPartyMon], a
xor a
ld [MonType], a
farcall GetGender
jr c, .genderless_samegender
ld b, 1
jr nz, .got_gender
dec b
.got_gender
push bc
ld a, [TempEnemyMonSpecies]
ld [CurPartySpecies], a
ld hl, EnemyMonDVs
ld a, [EnemySubStatus5]
bit SUBSTATUS_TRANSFORMED, a
jr z, .not_transformed
ld hl, wEnemyBackupDVs
.not_transformed
ld a, [hli]
ld [TempMonDVs], a
ld a, [hl]
ld [TempMonDVs + 1], a
ld a, 3
ld [MonType], a
farcall GetGender
pop bc
jr c, .genderless_samegender
ld a, 1
jr nz, .got_enemy_gender
dec a
.got_enemy_gender
xor b
jr z, .genderless_samegender
and a
ret
.genderless_samegender
scf
ret
; 3784b

View File

@@ -0,0 +1,97 @@
BattleCommand_Curse: ; 37588
; curse
ld de, BattleMonType1
ld bc, PlayerStatLevels
ld a, [hBattleTurn]
and a
jr z, .go
ld de, EnemyMonType1
ld bc, EnemyStatLevels
.go
; Curse is different for Ghost-types.
ld a, [de]
cp GHOST
jr z, .ghost
inc de
ld a, [de]
cp GHOST
jr z, .ghost
; If no stats can be increased, don't.
; Attack
ld a, [bc]
cp MAX_STAT_LEVEL
jr c, .raise
; Defense
inc bc
ld a, [bc]
cp MAX_STAT_LEVEL
jr nc, .cantraise
.raise
; Raise Attack and Defense, and lower Speed.
ld a, $1
ld [wKickCounter], a
call AnimateCurrentMove
ld a, SPEED
call LowerStat
call BattleCommand_SwitchTurn
call BattleCommand_StatDownMessage
call ResetMiss
call BattleCommand_SwitchTurn
call BattleCommand_AttackUp
call BattleCommand_StatUpMessage
call ResetMiss
call BattleCommand_DefenseUp
jp BattleCommand_StatUpMessage
.ghost
; Cut HP in half and put a curse on the opponent.
call CheckHiddenOpponent
jr nz, .failed
call CheckSubstituteOpp
jr nz, .failed
ld a, BATTLE_VARS_SUBSTATUS1_OPP
call GetBattleVarAddr
bit SUBSTATUS_CURSE, [hl]
jr nz, .failed
set SUBSTATUS_CURSE, [hl]
call AnimateCurrentMove
ld hl, GetHalfMaxHP
call CallBattleCore
ld hl, SubtractHPFromUser
call CallBattleCore
call UpdateUserInParty
ld hl, PutACurseText
jp StdBattleTextBox
.failed
call AnimateFailedMove
jp PrintButItFailed
.cantraise
; Can't raise either stat.
ld b, ABILITY + 1
call GetStatName
call AnimateFailedMove
ld hl, WontRiseAnymoreText
jp StdBattleTextBox
; 37618

View File

@@ -0,0 +1,17 @@
BattleCommand_Endure: ; 3766f
; endure
; Endure shares code with Protect. See protect.asm.
call ProtectChance
ret c
ld a, BATTLE_VARS_SUBSTATUS1
call GetBattleVarAddr
set SUBSTATUS_ENDURE, [hl]
call AnimateCurrentMove
ld hl, BracedItselfText
jp StdBattleTextBox
; 37683

View File

@@ -0,0 +1,23 @@
BattleCommand_Foresight: ; 376a0
; foresight
ld a, [AttackMissed]
and a
jr nz, .failed
call CheckHiddenOpponent
jr nz, .failed
ld a, BATTLE_VARS_SUBSTATUS1_OPP
call GetBattleVarAddr
bit SUBSTATUS_IDENTIFIED, [hl]
jr nz, .failed
set SUBSTATUS_IDENTIFIED, [hl]
call AnimateCurrentMove
ld hl, IdentifiedText
jp StdBattleTextBox
.failed
jp FailForesight
; 376c2

View File

@@ -0,0 +1,61 @@
BattleCommand_Metronome: ; 37418
; metronome
call ClearLastMove
call CheckUserIsCharging
jr nz, .asm_3742b
ld a, [wKickCounter]
push af
call BattleCommand_LowerSub
pop af
ld [wKickCounter], a
.asm_3742b
call LoadMoveAnim
.GetMove:
call BattleRandom
; No invalid moves.
cp NUM_ATTACKS + 1
jr nc, .GetMove
; None of the moves in MetronomeExcepts.
push af
ld de, 1
ld hl, MetronomeExcepts
call IsInArray
pop bc
jr c, .GetMove
; No moves the user already has.
ld a, b
call CheckUserMove
jr z, .GetMove
ld a, BATTLE_VARS_MOVE
call GetBattleVarAddr
ld [hl], b
call UpdateMoveData
jp ResetTurn
; 37454
MetronomeExcepts: ; 37454
db NO_MOVE
db METRONOME
db STRUGGLE
db SKETCH
db MIMIC
db COUNTER
db MIRROR_COAT
db PROTECT
db DETECT
db ENDURE
db DESTINY_BOND
db SLEEP_TALK
db THIEF
db -1
; 37462

View File

@@ -0,0 +1,52 @@
BattleCommand_MirrorMove: ; 373c9
; mirrormove
call ClearLastMove
ld a, BATTLE_VARS_MOVE
call GetBattleVarAddr
ld a, BATTLE_VARS_LAST_COUNTER_MOVE_OPP
call GetBattleVar
and a
jr z, .failed
call CheckUserMove
jr nz, .use
.failed
call AnimateFailedMove
ld hl, MirrorMoveFailedText
call StdBattleTextBox
jp EndMoveEffect
.use
ld a, b
ld [hl], a
ld [wd265], a
push af
ld a, BATTLE_VARS_MOVE_ANIM
call GetBattleVarAddr
ld d, h
ld e, l
pop af
dec a
call GetMoveData
call GetMoveName
call CopyName1
call CheckUserIsCharging
jr nz, .done
ld a, [wKickCounter]
push af
call BattleCommand_LowerSub
pop af
ld [wKickCounter], a
.done
call BattleCommand_MoveDelay
jp ResetTurn
; 37418

View File

@@ -0,0 +1,38 @@
BattleCommand_Nightmare: ; 37536
; nightmare
; Can't hit an absent opponent.
call CheckHiddenOpponent
jr nz, .failed
; Can't hit a substitute.
call CheckSubstituteOpp
jr nz, .failed
; Only works on a sleeping opponent.
ld a, BATTLE_VARS_STATUS_OPP
call GetBattleVarAddr
and SLP
jr z, .failed
; Bail if the opponent is already having a nightmare.
ld a, BATTLE_VARS_SUBSTATUS1_OPP
call GetBattleVarAddr
bit SUBSTATUS_NIGHTMARE, [hl]
jr nz, .failed
; Otherwise give the opponent a nightmare.
set SUBSTATUS_NIGHTMARE, [hl]
call AnimateCurrentMove
ld hl, StartedNightmareText
jp StdBattleTextBox
.failed
call AnimateFailedMove
jp PrintButItFailed
; 37563

View File

@@ -0,0 +1,40 @@
BattleCommand_PerishSong: ; 376c2
; perishsong
ld hl, PlayerSubStatus1
ld de, EnemySubStatus1
bit SUBSTATUS_PERISH, [hl]
jr z, .ok
ld a, [de]
bit SUBSTATUS_PERISH, a
jr nz, .failed
.ok
bit SUBSTATUS_PERISH, [hl]
jr nz, .enemy
set SUBSTATUS_PERISH, [hl]
ld a, 4
ld [PlayerPerishCount], a
.enemy
ld a, [de]
bit SUBSTATUS_PERISH, a
jr nz, .done
set SUBSTATUS_PERISH, a
ld [de], a
ld a, 4
ld [EnemyPerishCount], a
.done
call AnimateCurrentMove
ld hl, StartPerishText
jp StdBattleTextBox
.failed
call AnimateFailedMove
jp PrintButItFailed
; 376f8

View File

@@ -0,0 +1,93 @@
BattleCommand_Present: ; 37874
; present
ld a, [wLinkMode]
cp LINK_COLOSSEUM
jr z, .colosseum_skippush
push bc
push de
.colosseum_skippush
call BattleCommand_Stab
ld a, [wLinkMode]
cp LINK_COLOSSEUM
jr z, .colosseum_skippop
pop de
pop bc
.colosseum_skippop
ld a, [wTypeMatchup]
and a
jp z, AnimateFailedMove
ld a, [AttackMissed]
and a
jp nz, AnimateFailedMove
push bc
call BattleRandom
ld b, a
ld hl, .PresentPower
ld c, 0
.next
ld a, [hli]
cp $ff
jr z, .heal_effect ; 378a4 $11
cp b
jr nc, .got_power ; 378a7 $4
inc c
inc hl
jr .next ; 378ab $f4
.got_power
ld a, c
ld [wPresentPower], a
call AnimateCurrentMoveEitherSide
ld d, [hl]
pop bc
ret
.heal_effect
pop bc
ld a, $3
ld [wPresentPower], a
call AnimateCurrentMove
call BattleCommand_SwitchTurn
ld hl, AICheckPlayerMaxHP
ld a, [hBattleTurn]
and a
jr z, .got_hp_fn_pointer ; 378c9 $3
ld hl, AICheckEnemyMaxHP
.got_hp_fn_pointer
ld a, BANK(AICheckPlayerMaxHP)
rst FarCall
jr c, .already_fully_healed ; 378d1 $20
ld hl, GetQuarterMaxHP
call CallBattleCore
call BattleCommand_SwitchTurn
ld hl, RestoreHP
call CallBattleCore
call BattleCommand_SwitchTurn
ld hl, RegainedHealthText
call StdBattleTextBox
call BattleCommand_SwitchTurn
call UpdateOpponentInParty
jr .do_animation ; 378f1 $11
.already_fully_healed
call BattleCommand_SwitchTurn
call _CheckBattleScene
jr nc, .do_animation ; 378f9 $9
call AnimateFailedMove
ld hl, RefusedGiftText
call StdBattleTextBox
.do_animation
jp EndMoveEffect
.PresentPower:
db 40 percent, 40
db 70 percent + 1, 80
db 80 percent, 120
db $ff
; 3790e

View File

@@ -0,0 +1,80 @@
BattleCommand_Protect: ; 37618
; protect
call ProtectChance
ret c
ld a, BATTLE_VARS_SUBSTATUS1
call GetBattleVarAddr
set SUBSTATUS_PROTECT, [hl]
call AnimateCurrentMove
ld hl, ProtectedItselfText
jp StdBattleTextBox
; 3762c
ProtectChance: ; 3762c
ld de, PlayerProtectCount
ld a, [hBattleTurn]
and a
jr z, .asm_37637
ld de, EnemyProtectCount
.asm_37637
call CheckOpponentWentFirst
jr nz, .failed
; Can't have a substitute.
ld a, BATTLE_VARS_SUBSTATUS4
call GetBattleVar
bit SUBSTATUS_SUBSTITUTE, a
jr nz, .failed
; Halve the chance of a successful Protect for each consecutive use.
ld b, $ff
ld a, [de]
ld c, a
.loop
ld a, c
and a
jr z, .done
dec c
srl b
ld a, b
and a
jr nz, .loop
jr .failed
.done
.rand
call BattleRandom
and a
jr z, .rand
dec a
cp b
jr nc, .failed
; Another consecutive Protect use.
ld a, [de]
inc a
ld [de], a
and a
ret
.failed
xor a
ld [de], a
call AnimateFailedMove
call PrintButItFailed
scf
ret
; 3766f

View File

@@ -0,0 +1,99 @@
MAX_ROLLOUT_COUNT EQU 5
BattleCommand_CheckCurl: ; 37718
; checkcurl
ld de, PlayerRolloutCount
ld a, [hBattleTurn]
and a
jr z, .ok
ld de, EnemyRolloutCount
.ok
ld a, BATTLE_VARS_SUBSTATUS1
call GetBattleVar
bit SUBSTATUS_ROLLOUT, a
jr z, .reset
ld b, $4 ; doturn
jp SkipToBattleCommand
.reset
xor a
ld [de], a
ret
; 37734
BattleCommand_RolloutPower: ; 37734
; rolloutpower
ld a, BATTLE_VARS_STATUS
call GetBattleVar
and SLP
ret nz
ld hl, PlayerRolloutCount
ld a, [hBattleTurn]
and a
jr z, .got_rollout_count
ld hl, EnemyRolloutCount
.got_rollout_count
ld a, [hl]
and a
jr nz, .skip_set_rampage
ld a, 1
ld [wSomeoneIsRampaging], a
.skip_set_rampage
ld a, [AttackMissed]
and a
jr z, .hit
ld a, BATTLE_VARS_SUBSTATUS1
call GetBattleVarAddr
res 6, [hl]
ret
.hit
inc [hl]
ld a, [hl]
ld b, a
cp MAX_ROLLOUT_COUNT
jr c, .not_done_with_rollout
ld a, BATTLE_VARS_SUBSTATUS1
call GetBattleVarAddr
res SUBSTATUS_ROLLOUT, [hl]
jr .done_with_substatus_flag
.not_done_with_rollout
ld a, BATTLE_VARS_SUBSTATUS1
call GetBattleVarAddr
set SUBSTATUS_ROLLOUT, [hl]
.done_with_substatus_flag
ld a, BATTLE_VARS_SUBSTATUS2
call GetBattleVar
bit SUBSTATUS_CURLED, a
jr z, .not_curled
inc b
.not_curled
.loop
dec b
jr z, .done_damage
ld hl, CurDamage + 1
sla [hl]
dec hl
rl [hl]
jr nc, .loop
ld a, $ff
ld [hli], a
ld [hl], a
.done_damage
ret
; 37791

View File

@@ -0,0 +1,19 @@
BattleCommand_StartSandstorm: ; 376f8
; startsandstorm
ld a, [Weather]
cp WEATHER_SANDSTORM
jr z, .failed
ld a, WEATHER_SANDSTORM
ld [Weather], a
ld a, 5
ld [WeatherCount], a
call AnimateCurrentMove
ld hl, SandstormBrewedText
jp StdBattleTextBox
.failed
call AnimateFailedMove
jp PrintButItFailed
; 37718

View File

@@ -0,0 +1,27 @@
BattleCommand_Spikes: ; 37683
; spikes
ld hl, EnemyScreens
ld a, [hBattleTurn]
and a
jr z, .asm_3768e
ld hl, PlayerScreens
.asm_3768e
; Fails if spikes are already down!
bit SCREENS_SPIKES, [hl]
jr nz, .failed
; Nothing else stops it from working.
set SCREENS_SPIKES, [hl]
call AnimateCurrentMove
ld hl, SpikesText
jp StdBattleTextBox
.failed
jp FailSpikes
; 376a0

View File

@@ -0,0 +1,116 @@
BattleCommand_Thief: ; 37492
; thief
ld a, [hBattleTurn]
and a
jr nz, .enemy
; The player needs to be able to steal an item.
call .playeritem
ld a, [hl]
and a
ret nz
; The enemy needs to have an item to steal.
call .enemyitem
ld a, [hl]
and a
ret z
; Can't steal mail.
ld [wd265], a
ld d, a
farcall ItemIsMail
ret c
ld a, [EffectFailed]
and a
ret nz
ld a, [wLinkMode]
and a
jr z, .stealenemyitem
ld a, [wBattleMode]
dec a
ret z
.stealenemyitem
call .enemyitem
xor a
ld [hl], a
ld [de], a
call .playeritem
ld a, [wd265]
ld [hl], a
ld [de], a
jr .stole
.enemy
; The enemy can't already have an item.
call .enemyitem
ld a, [hl]
and a
ret nz
; The player must have an item to steal.
call .playeritem
ld a, [hl]
and a
ret z
; Can't steal mail!
ld [wd265], a
ld d, a
farcall ItemIsMail
ret c
ld a, [EffectFailed]
and a
ret nz
; If the enemy steals your item,
; it's gone for good if you don't get it back.
call .playeritem
xor a
ld [hl], a
ld [de], a
call .enemyitem
ld a, [wd265]
ld [hl], a
ld [de], a
.stole
call GetItemName
ld hl, StoleText
jp StdBattleTextBox
.playeritem
ld a, 1
call BattlePartyAttr
ld d, h
ld e, l
ld hl, BattleMonItem
ret
.enemyitem
ld a, 1
call OTPartyAttr
ld d, h
ld e, l
ld hl, EnemyMonItem
ret
; 37517

View File

@@ -0,0 +1,141 @@
BattleCommand_Transform: ; 371cd
; transform
call ClearLastMove
ld a, BATTLE_VARS_SUBSTATUS5_OPP
call GetBattleVarAddr
bit SUBSTATUS_TRANSFORMED, [hl]
jp nz, BattleEffect_ButItFailed
call CheckHiddenOpponent
jp nz, BattleEffect_ButItFailed
xor a
ld [wNumHits], a
ld [FXAnimID + 1], a
ld a, $1
ld [wKickCounter], a
ld a, BATTLE_VARS_SUBSTATUS4
call GetBattleVarAddr
bit SUBSTATUS_SUBSTITUTE, [hl]
push af
jr z, .mimic_substitute
call CheckUserIsCharging
jr nz, .mimic_substitute
ld a, SUBSTITUTE
call LoadAnim
.mimic_substitute
ld a, BATTLE_VARS_SUBSTATUS5
call GetBattleVarAddr
set SUBSTATUS_TRANSFORMED, [hl]
call ResetActorDisable
ld hl, BattleMonSpecies
ld de, EnemyMonSpecies
ld a, [hBattleTurn]
and a
jr nz, .got_mon_species
ld hl, EnemyMonSpecies
ld de, BattleMonSpecies
xor a
ld [CurMoveNum], a
.got_mon_species
push hl
ld a, [hli]
ld [de], a
inc hl
inc de
inc de
ld bc, NUM_MOVES
call CopyBytes
ld a, [hBattleTurn]
and a
jr z, .mimic_enemy_backup
ld a, [de]
ld [wEnemyBackupDVs], a
inc de
ld a, [de]
ld [wEnemyBackupDVs + 1], a
dec de
.mimic_enemy_backup
; copy DVs
ld a, [hli]
ld [de], a
inc de
ld a, [hli]
ld [de], a
inc de
; move pointer to stats
ld bc, BattleMonStats - BattleMonPP
add hl, bc
push hl
ld h, d
ld l, e
add hl, bc
ld d, h
ld e, l
pop hl
ld bc, BattleMonStructEnd - BattleMonStats
call CopyBytes
; init the power points
ld bc, BattleMonMoves - BattleMonStructEnd
add hl, bc
push de
ld d, h
ld e, l
pop hl
ld bc, BattleMonPP - BattleMonStructEnd
add hl, bc
ld b, NUM_MOVES
.pp_loop
ld a, [de]
inc de
and a
jr z, .done_move
cp SKETCH
ld a, 1
jr z, .done_move
ld a, 5
.done_move
ld [hli], a
dec b
jr nz, .pp_loop
pop hl
ld a, [hl]
ld [wNamedObjectIndexBuffer], a
call GetPokemonName
ld hl, EnemyStats
ld de, PlayerStats
ld bc, 2 * 5
call BattleSideCopy
ld hl, EnemyStatLevels
ld de, PlayerStatLevels
ld bc, 8
call BattleSideCopy
call _CheckBattleScene
jr c, .mimic_anims
ld a, [hBattleTurn]
and a
ld a, [wPlayerMinimized]
jr z, .got_byte
ld a, [wEnemyMinimized]
.got_byte
and a
jr nz, .mimic_anims
call LoadMoveAnim
jr .after_anim
.mimic_anims
call BattleCommand_MoveDelay
call BattleCommand_RaiseSubNoAnim
.after_anim
xor a
ld [wNumHits], a
ld [FXAnimID + 1], a
ld a, $2
ld [wKickCounter], a
pop af
ld a, SUBSTITUTE
call nz, LoadAnim
ld hl, TransformedText
jp StdBattleTextBox
; 372c6

View File

@@ -0,0 +1,111 @@
HiddenPowerDamage: ; fbced
; Override Hidden Power's type and power based on the user's DVs.
ld hl, BattleMonDVs
ld a, [hBattleTurn]
and a
jr z, .got_dvs
ld hl, EnemyMonDVs
.got_dvs
; Power:
; Take the top bit from each stat
; Attack
ld a, [hl]
swap a
and 8
; Defense
ld b, a
ld a, [hli]
and 8
srl a
or b
; Speed
ld b, a
ld a, [hl]
swap a
and 8
srl a
srl a
or b
; Special
ld b, a
ld a, [hl]
and 8
srl a
srl a
srl a
or b
; Multiply by 5
ld b, a
add a
add a
add b
; Add Special & 3
ld b, a
ld a, [hld]
and 3
add b
; Divide by 2 and add 30 + 1
srl a
add 30
inc a
ld d, a
; Type:
; Def & 3
ld a, [hl]
and 3
ld b, a
; + (Atk & 3) << 2
ld a, [hl]
and 3 << 4
swap a
add a
add a
or b
; Skip Normal
inc a
; Skip Bird
cp BIRD
jr c, .done
inc a
; Skip unused types
cp UNUSED_TYPES
jr c, .done
add SPECIAL - UNUSED_TYPES
.done
; Overwrite the current move type.
push af
ld a, BATTLE_VARS_MOVE_TYPE
call GetBattleVarAddr
pop af
ld [hl], a
; Get the rest of the damage formula variables
; based on the new type, but keep base power.
ld a, d
push af
farcall BattleCommand_DamageStats ; damagestats
pop af
ld d, a
ret
; fbd54

162
engine/battle/link_result.asm Executable file
View File

@@ -0,0 +1,162 @@
DetermineLinkBattleResult: ; 2b930
farcall UpdateEnemyMonInParty
ld hl, PartyMon1HP
call .CountMonsRemaining
push bc
ld hl, OTPartyMon1HP
call .CountMonsRemaining
ld a, c
pop bc
cp c
jr z, .even_number_of_mons_remaining
jr c, .defeat
jr .victory
.even_number_of_mons_remaining
call .BothSides_CheckNumberMonsAtFullHealth
jr z, .drawn
ld a, e
cp $1
jr z, .victory
cp $2
jr z, .defeat
ld hl, PartyMon1HP
call .CalcPercentHPRemaining
push de
ld hl, OTPartyMon1HP
call .CalcPercentHPRemaining
pop hl
ld a, d
cp h
jr c, .victory
jr z, .compare_lo
jr .defeat
.compare_lo
ld a, e
cp l
jr z, .drawn
jr nc, .defeat
.victory
ld a, [wBattleResult]
and $f0
ld [wBattleResult], a
ret
.defeat
ld a, [wBattleResult]
and $f0
add $1
ld [wBattleResult], a
ret
.drawn
ld a, [wBattleResult]
and $f0
add $2
ld [wBattleResult], a
ret
.CountMonsRemaining: ; 2b995
ld c, 0
ld b, 3
ld de, PARTYMON_STRUCT_LENGTH - 1
.loop
ld a, [hli]
or [hl]
jr nz, .not_fainted
inc c
.not_fainted
add hl, de
dec b
jr nz, .loop
ret
.CalcPercentHPRemaining: ; 2b9a6
ld de, 0
ld c, $3
.loop2
ld a, [hli]
or [hl]
jr z, .next
dec hl
xor a
ld [hDividend + 0], a
ld a, [hli]
ld [hDividend + 1], a
ld a, [hli]
ld [hDividend + 2], a
xor a
ld [hDividend + 3], a
ld a, [hli]
ld b, a
ld a, [hld]
srl b
rr a
srl b
rr a
ld [hDivisor], a
ld b, $4
call Divide
ld a, [hQuotient + 2]
add e
ld e, a
ld a, [hQuotient + 1]
adc d
ld d, a
dec hl
.next
push de
ld de, $2f
add hl, de
pop de
dec c
jr nz, .loop2
ret
.BothSides_CheckNumberMonsAtFullHealth: ; 2b9e1
ld hl, PartyMon1HP
call .CheckFaintedOrFullHealth
jr nz, .finish ; we have a pokemon that's neither fainted nor at full health
ld hl, OTPartyMon1HP
call .CheckFaintedOrFullHealth
ld e, $1
ret
.finish
ld hl, OTPartyMon1HP
call .CheckFaintedOrFullHealth
ld e, $0
ret nz ; we both have pokemon that are neither fainted nor at full health
ld e, $2
ld a, $1
and a
ret
.CheckFaintedOrFullHealth: ; 2ba01
ld d, 3
.loop3
ld a, [hli]
ld b, a
ld a, [hli]
ld c, a
or b
jr z, .fainted_or_full_health
ld a, [hli]
cp b
ret nz
ld a, [hld]
cp c
ret nz
.fainted_or_full_health
push de
ld de, PARTYMON_STRUCT_LENGTH - 2
add hl, de
pop de
dec d
jr nz, .loop3
ret

118
engine/battle/menu.asm Executable file
View File

@@ -0,0 +1,118 @@
LoadBattleMenu: ; 24ef2
ld hl, BattleMenuDataHeader
call LoadMenuDataHeader
ld a, [wBattleMenuCursorBuffer]
ld [wMenuCursorBuffer], a
call InterpretBattleMenu
ld a, [wMenuCursorBuffer]
ld [wBattleMenuCursorBuffer], a
call ExitMenu
ret
; 24f0b
SafariBattleMenu: ; 24f0b
; untranslated
ld hl, MenuDataHeader_0x24f4e
call LoadMenuDataHeader
jr Function24f19
; 24f13
ContestBattleMenu: ; 24f13
ld hl, MenuDataHeader_0x24f89
call LoadMenuDataHeader
; 24f19
Function24f19: ; 24f19
ld a, [wBattleMenuCursorBuffer]
ld [wMenuCursorBuffer], a
call _2DMenu
ld a, [wMenuCursorBuffer]
ld [wBattleMenuCursorBuffer], a
call ExitMenu
ret
; 24f2c
BattleMenuDataHeader: ; 24f2c
db $40 ; flags
db 12, 08 ; start coords
db 17, 19 ; end coords
dw MenuData_0x24f34
db 1 ; default option
; 24f34
MenuData_0x24f34: ; 0x24f34
db $81 ; flags
dn 2, 2 ; rows, columns
db 6 ; spacing
dba Strings24f3d
dbw BANK(MenuData_0x24f34), 0
; 0x24f3d
Strings24f3d: ; 0x24f3d
db "FIGHT@"
db "<PKMN>@"
db "PACK@"
db "RUN@"
; 24f4e
MenuDataHeader_0x24f4e: ; 24f4e
db $40 ; flags
db 12, 00 ; start coords
db 17, 19 ; end coords
dw MenuData_0x24f56
db 1 ; default option
; 24f56
MenuData_0x24f56: ; 24f56
db $81 ; flags
dn 2, 2 ; rows, columns
db 11 ; spacing
dba Strings24f5f
dba Function24f7c
; 24f5f
Strings24f5f: ; 24f5f
db "サファりボール× @" ; "SAFARI BALL× @"
db "エサをなげる@" ; "THROW BAIT"
db "いしをなげる@" ; "THROW ROCK"
db "にげる@" ; "RUN"
; 24f7c
Function24f7c: ; 24f7c
hlcoord 17, 13
ld de, wSafariBallsRemaining
lb bc, PRINTNUM_LEADINGZEROS | 1, 2
call PrintNum
ret
; 24f89
MenuDataHeader_0x24f89: ; 24f89
db $40 ; flags
db 12, 02 ; start coords
db 17, 19 ; end coords
dw MenuData_0x24f91
db 1 ; default option
; 24f91
MenuData_0x24f91: ; 24f91
db $81 ; flags
dn 2, 2 ; rows, columns
db 12 ; spacing
dba Strings24f9a
dba Function24fb2
; 24f9a
Strings24f9a: ; 24f9a
db "FIGHT@"
db "<PKMN>", "@"
db "PARKBALL× @"
db "RUN@"
; 24fb2
Function24fb2: ; 24fb2
hlcoord 13, 16
ld de, wParkBallsRemaining
lb bc, PRINTNUM_LEADINGZEROS | 1, 2
call PrintNum
ret
; 24fbf

257
engine/battle/misc.asm Normal file
View File

@@ -0,0 +1,257 @@
_DisappearUser: ; fbd54
xor a
ld [hBGMapMode], a
ld a, [hBattleTurn]
and a
jr z, .player
call GetEnemyFrontpicCoords
jr .okay
.player
call GetPlayerBackpicCoords
.okay
call ClearBox
jr FinishAppearDisappearUser
_AppearUserRaiseSub: ; fbd69 (3e:7d69)
farcall BattleCommand_RaiseSubNoAnim
jr AppearUser
_AppearUserLowerSub: ; fbd71 (3e:7d71)
farcall BattleCommand_LowerSubNoAnim
AppearUser: ; fbd77 (3e:7d77)
xor a
ld [hBGMapMode], a
ld a, [hBattleTurn]
and a
jr z, .player
call GetEnemyFrontpicCoords
xor a
jr .okay
.player
call GetPlayerBackpicCoords
ld a, $31
.okay
ld [hGraphicStartTile], a
predef PlaceGraphic
FinishAppearDisappearUser: ; fbd91 (3e:7d91)
ld a, $1
ld [hBGMapMode], a
ret
GetEnemyFrontpicCoords: ; fbd96 (3e:7d96)
hlcoord 12, 0
lb bc, 7, 7
ret
GetPlayerBackpicCoords: ; fbd9d (3e:7d9d)
hlcoord 2, 6
lb bc, 6, 6
ret
DoWeatherModifiers: ; fbda4
ld de, .WeatherTypeModifiers
ld a, [Weather]
ld b, a
ld a, [wd265] ; move type
ld c, a
.CheckWeatherType:
ld a, [de]
inc de
cp $ff
jr z, .done_weather_types
cp b
jr nz, .NextWeatherType
ld a, [de]
cp c
jr z, .ApplyModifier
.NextWeatherType:
inc de
inc de
jr .CheckWeatherType
.done_weather_types
ld de, .WeatherMoveModifiers
ld a, BATTLE_VARS_MOVE_EFFECT
call GetBattleVar
ld c, a
.CheckWeatherMove:
ld a, [de]
inc de
cp $ff
jr z, .done
cp b
jr nz, .NextWeatherMove
ld a, [de]
cp c
jr z, .ApplyModifier
.NextWeatherMove:
inc de
inc de
jr .CheckWeatherMove
.ApplyModifier:
xor a
ld [hMultiplicand + 0], a
ld hl, CurDamage
ld a, [hli]
ld [hMultiplicand + 1], a
ld a, [hl]
ld [hMultiplicand + 2], a
inc de
ld a, [de]
ld [hMultiplier], a
call Multiply
ld a, 10
ld [hDivisor], a
ld b, $4
call Divide
ld a, [hQuotient + 0]
and a
ld bc, -1
jr nz, .Update
ld a, [hQuotient + 1]
ld b, a
ld a, [hQuotient + 2]
ld c, a
or b
jr nz, .Update
ld bc, 1
.Update:
ld a, b
ld [CurDamage], a
ld a, c
ld [CurDamage + 1], a
.done
ret
.WeatherTypeModifiers:
db WEATHER_RAIN, WATER, 15
db WEATHER_RAIN, FIRE, 05
db WEATHER_SUN, FIRE, 15
db WEATHER_SUN, WATER, 05
db $ff
.WeatherMoveModifiers:
db WEATHER_RAIN, EFFECT_SOLARBEAM, 05
db $ff
; fbe24
DoBadgeTypeBoosts: ; fbe24
ld a, [wLinkMode]
and a
ret nz
ld a, [InBattleTowerBattle]
and a
ret nz
ld a, [hBattleTurn]
and a
ret nz
push de
push bc
ld hl, .BadgeTypes
ld a, [KantoBadges]
ld b, a
ld a, [JohtoBadges]
ld c, a
.CheckBadge:
ld a, [hl]
cp $ff
jr z, .done
srl b
rr c
jr nc, .NextBadge
ld a, [wd265] ; move type
cp [hl]
jr z, .ApplyBoost
.NextBadge:
inc hl
jr .CheckBadge
.ApplyBoost:
ld a, [CurDamage]
ld h, a
ld d, a
ld a, [CurDamage + 1]
ld l, a
ld e, a
srl d
rr e
srl d
rr e
srl d
rr e
ld a, e
or d
jr nz, .done_min
ld e, 1
.done_min
add hl, de
jr nc, .Update
ld hl, $ffff
.Update:
ld a, h
ld [CurDamage], a
ld a, l
ld [CurDamage + 1], a
.done
pop bc
pop de
ret
.BadgeTypes:
db FLYING ; zephyrbadge
db BUG ; hivebadge
db NORMAL ; plainbadge
db GHOST ; fogbadge
db STEEL ; mineralbadge
db FIGHTING ; stormbadge
db ICE ; glacierbadge
db DRAGON ; risingbadge
db ROCK ; boulderbadge
db WATER ; cascadebadge
db ELECTRIC ; thunderbadge
db GRASS ; rainbowbadge
db POISON ; soulbadge
db PSYCHIC ; marshbadge
db FIRE ; volcanobadge
db GROUND ; earthbadge
db $ff
; fbe91

View File

@@ -0,0 +1,64 @@
GetTrainerClassName: ; 3952d
ld hl, RivalName
ld a, c
cp RIVAL1
jr z, .rival
ld [CurSpecies], a
ld a, TRAINER_NAME
ld [wNamedObjectTypeBuffer], a
call GetName
ld de, StringBuffer1
ret
.rival
ld de, StringBuffer1
push de
ld bc, NAME_LENGTH
call CopyBytes
pop de
ret
GetOTName: ; 39550
ld hl, OTPlayerName
ld a, [wLinkMode]
and a
jr nz, .ok
ld hl, RivalName
ld a, c
cp RIVAL1
jr z, .ok
ld [CurSpecies], a
ld a, TRAINER_NAME
ld [wNamedObjectTypeBuffer], a
call GetName
ld hl, StringBuffer1
.ok
ld bc, TRAINER_CLASS_NAME_LENGTH
ld de, OTClassName
push de
call CopyBytes
pop de
ret
GetTrainerAttributes: ; 3957b
ld a, [TrainerClass]
ld c, a
call GetOTName
ld a, [TrainerClass]
dec a
ld hl, TrainerClassAttributes + TRNATTR_ITEM1
ld bc, NUM_TRAINER_ATTRIBUTES
call AddNTimes
ld de, wEnemyTrainerItem1
ld a, [hli]
ld [de], a
inc de
ld a, [hli]
ld [de], a
ld a, [hl]
ld [wEnemyTrainerBaseReward], a
ret

View File

@@ -0,0 +1,398 @@
ReadTrainerParty: ; 39771
ld a, [InBattleTowerBattle]
bit 0, a
ret nz
ld a, [wLinkMode]
and a
ret nz
ld hl, OTPartyCount
xor a
ld [hli], a
dec a
ld [hl], a
ld hl, OTPartyMons
ld bc, OTPartyMonsEnd - OTPartyMons
xor a
call ByteFill
ld a, [OtherTrainerClass]
cp CAL
jr nz, .not_cal2
ld a, [OtherTrainerID]
cp CAL2
jr z, .cal2
ld a, [OtherTrainerClass]
.not_cal2
dec a
ld c, a
ld b, 0
ld hl, TrainerGroups
add hl, bc
add hl, bc
ld a, [hli]
ld h, [hl]
ld l, a
ld a, [OtherTrainerID]
ld b, a
.skip_trainer
dec b
jr z, .got_trainer
.loop
ld a, [hli]
cp $ff
jr nz, .loop
jr .skip_trainer
.got_trainer
.skip_name
ld a, [hli]
cp "@"
jr nz, .skip_name
ld a, [hli]
ld c, a
ld b, 0
ld d, h
ld e, l
ld hl, TrainerTypes
add hl, bc
add hl, bc
ld a, [hli]
ld h, [hl]
ld l, a
ld bc, .done
push bc
jp hl
.done
jp ComputeTrainerReward
.cal2
ld a, BANK(sMysteryGiftTrainer)
call GetSRAMBank
ld de, sMysteryGiftTrainer
call TrainerType2
call CloseSRAM
jr .done
; 397e3
TrainerTypes: ; 397e3
dw TrainerType1 ; level, species
dw TrainerType2 ; level, species, moves
dw TrainerType3 ; level, species, item
dw TrainerType4 ; level, species, item, moves
; 397eb
TrainerType1: ; 397eb
; normal (level, species)
ld h, d
ld l, e
.loop
ld a, [hli]
cp $ff
ret z
ld [CurPartyLevel], a
ld a, [hli]
ld [CurPartySpecies], a
ld a, OTPARTYMON
ld [MonType], a
push hl
predef TryAddMonToParty
pop hl
jr .loop
; 39806
TrainerType2: ; 39806
; moves
ld h, d
ld l, e
.loop
ld a, [hli]
cp $ff
ret z
ld [CurPartyLevel], a
ld a, [hli]
ld [CurPartySpecies], a
ld a, OTPARTYMON
ld [MonType], a
push hl
predef TryAddMonToParty
ld a, [OTPartyCount]
dec a
ld hl, OTPartyMon1Moves
ld bc, PARTYMON_STRUCT_LENGTH
call AddNTimes
ld d, h
ld e, l
pop hl
ld b, NUM_MOVES
.copy_moves
ld a, [hli]
ld [de], a
inc de
dec b
jr nz, .copy_moves
push hl
ld a, [OTPartyCount]
dec a
ld hl, OTPartyMon1Species
ld bc, PARTYMON_STRUCT_LENGTH
call AddNTimes
ld d, h
ld e, l
ld hl, MON_PP
add hl, de
push hl
ld hl, MON_MOVES
add hl, de
pop de
ld b, NUM_MOVES
.copy_pp
ld a, [hli]
and a
jr z, .copied_pp
push hl
push bc
dec a
ld hl, Moves + MOVE_PP
ld bc, MOVE_LENGTH
call AddNTimes
ld a, BANK(Moves)
call GetFarByte
pop bc
pop hl
ld [de], a
inc de
dec b
jr nz, .copy_pp
.copied_pp
pop hl
jr .loop
; 39871
TrainerType3: ; 39871
; item
ld h, d
ld l, e
.loop
ld a, [hli]
cp $ff
ret z
ld [CurPartyLevel], a
ld a, [hli]
ld [CurPartySpecies], a
ld a, OTPARTYMON
ld [MonType], a
push hl
predef TryAddMonToParty
ld a, [OTPartyCount]
dec a
ld hl, OTPartyMon1Item
ld bc, PARTYMON_STRUCT_LENGTH
call AddNTimes
ld d, h
ld e, l
pop hl
ld a, [hli]
ld [de], a
jr .loop
; 3989d (e:589d)
TrainerType4: ; 3989d
; item + moves
ld h, d
ld l, e
.loop
ld a, [hli]
cp $ff
ret z
ld [CurPartyLevel], a
ld a, [hli]
ld [CurPartySpecies], a
ld a, OTPARTYMON
ld [MonType], a
push hl
predef TryAddMonToParty
ld a, [OTPartyCount]
dec a
ld hl, OTPartyMon1Item
ld bc, PARTYMON_STRUCT_LENGTH
call AddNTimes
ld d, h
ld e, l
pop hl
ld a, [hli]
ld [de], a
push hl
ld a, [OTPartyCount]
dec a
ld hl, OTPartyMon1Moves
ld bc, PARTYMON_STRUCT_LENGTH
call AddNTimes
ld d, h
ld e, l
pop hl
ld b, NUM_MOVES
.copy_moves
ld a, [hli]
ld [de], a
inc de
dec b
jr nz, .copy_moves
push hl
ld a, [OTPartyCount]
dec a
ld hl, OTPartyMon1
ld bc, PARTYMON_STRUCT_LENGTH
call AddNTimes
ld d, h
ld e, l
ld hl, MON_PP
add hl, de
push hl
ld hl, MON_MOVES
add hl, de
pop de
ld b, NUM_MOVES
.copy_pp
ld a, [hli]
and a
jr z, .copied_pp
push hl
push bc
dec a
ld hl, Moves + MOVE_PP
ld bc, MOVE_LENGTH
call AddNTimes
ld a, BANK(Moves)
call GetFarByte
pop bc
pop hl
ld [de], a
inc de
dec b
jr nz, .copy_pp
.copied_pp
pop hl
jr .loop
; 3991b
ComputeTrainerReward: ; 3991b (e:591b)
ld hl, hProduct
xor a
ld [hli], a
ld [hli], a
ld [hli], a
ld a, [wEnemyTrainerBaseReward]
ld [hli], a
ld a, [CurPartyLevel]
ld [hl], a
call Multiply
ld hl, wBattleReward
xor a
ld [hli], a
ld a, [hProduct + 2]
ld [hli], a
ld a, [hProduct + 3]
ld [hl], a
ret
Battle_GetTrainerName:: ; 39939
ld a, [InBattleTowerBattle]
bit 0, a
ld hl, OTPlayerName
jp nz, CopyTrainerName
ld a, [OtherTrainerID]
ld b, a
ld a, [OtherTrainerClass]
ld c, a
GetTrainerName:: ; 3994c
ld a, c
cp CAL
jr nz, .not_cal2
ld a, BANK(sMysteryGiftTrainerHouseFlag)
call GetSRAMBank
ld a, [sMysteryGiftTrainerHouseFlag]
and a
call CloseSRAM
jr z, .not_cal2
ld a, BANK(sMysteryGiftPartnerName)
call GetSRAMBank
ld hl, sMysteryGiftPartnerName
call CopyTrainerName
jp CloseSRAM
.not_cal2
dec c
push bc
ld b, 0
ld hl, TrainerGroups
add hl, bc
add hl, bc
ld a, [hli]
ld h, [hl]
ld l, a
pop bc
.loop
dec b
jr z, CopyTrainerName
.skip
ld a, [hli]
cp $ff
jr nz, .skip
jr .loop
CopyTrainerName: ; 39984
ld de, StringBuffer1
push de
ld bc, NAME_LENGTH
call CopyBytes
pop de
ret
; 39990
Function39990: ; 39990
; This function is useless.
ld de, StringBuffer1
push de
ld bc, NAME_LENGTH
pop de
ret
; 39999

104
engine/battle/sliding_intro.asm Executable file
View File

@@ -0,0 +1,104 @@
BattleIntroSlidingPics: ; 4e980
ld a, [rSVBK]
push af
ld a, $5
ld [rSVBK], a
call .subfunction1
ld a, rSCX - $ff00
ld [hLCDCPointer], a
call .subfunction2
xor a
ld [hLCDCPointer], a
pop af
ld [rSVBK], a
ret
; 4e998
.subfunction1 ; 4e998
call .subfunction4
ld a, $90
ld [hSCX], a
ld a, %11100100
call DmgToCgbBGPals
lb de, %11100100, %11100100
call DmgToCgbObjPals
ret
; 4e9ab
.subfunction2 ; 4e9ab
ld d, $90
ld e, $72
ld a, $48
inc a
.loop1
push af
.loop2
ld a, [rLY]
cp $60
jr c, .loop2
ld a, d
ld [hSCX], a
call .subfunction5
inc e
inc e
dec d
dec d
pop af
push af
cp $1
jr z, .skip1
push de
call .subfunction3
pop de
.skip1
call DelayFrame
pop af
dec a
jr nz, .loop1
ret
; 4e9d6
.subfunction3 ; 4e9d6
ld hl, Sprites + 1 ; x pixel
ld c, $12 ; 18
ld de, $4
.loop3
dec [hl]
dec [hl]
add hl, de
dec c
jr nz, .loop3
ret
; 4e9e5
.subfunction4 ; 4e9e5
ld hl, LYOverrides
ld a, $90
ld bc, SCREEN_HEIGHT_PX
call ByteFill
ret
; 4e9f1
.subfunction5 ; 4e9f1
ld hl, LYOverrides
ld a, d
ld c, $3e ; 62
.loop4
ld [hli], a
dec c
jr nz, .loop4
ld a, e
ld c, $22 ; 34
.loop5
ld [hli], a
dec c
jr nz, .loop5
xor a
ld c, $30 ; 48
.loop6
ld [hli], a
dec c
jr nz, .loop6
ret
; 4ea0a

View File

@@ -0,0 +1,197 @@
ShowLinkBattleParticipants: ; 2ee18
; If we're not in a communications room,
; we don't need to be here.
ld a, [wLinkMode]
and a
ret z
farcall _ShowLinkBattleParticipants
ld c, 150
call DelayFrames
call ClearTileMap
call ClearSprites
ret
FindFirstAliveMonAndStartBattle: ; 2ee2f
xor a
ld [hMapAnims], a
call DelayFrame
ld b, 6
ld hl, PartyMon1HP
ld de, PARTYMON_STRUCT_LENGTH - 1
.loop
ld a, [hli]
or [hl]
jr nz, .okay
add hl, de
dec b
jr nz, .loop
.okay
ld de, MON_LEVEL - MON_HP
add hl, de
ld a, [hl]
ld [BattleMonLevel], a
predef Predef_StartBattle
farcall _LoadBattleFontsHPBar
ld a, 1
ld [hBGMapMode], a
call ClearSprites
call ClearTileMap
xor a
ld [hBGMapMode], a
ld [hWY], a
ld [rWY], a
ld [hMapAnims], a
ret
PlayBattleMusic: ; 2ee6c
push hl
push de
push bc
xor a
ld [MusicFade], a
ld de, MUSIC_NONE
call PlayMusic
call DelayFrame
call MaxVolume
ld a, [BattleType]
cp BATTLETYPE_SUICUNE
ld de, MUSIC_SUICUNE_BATTLE
jp z, .done
cp BATTLETYPE_ROAMING
jp z, .done
; Are we fighting a trainer?
ld a, [OtherTrainerClass]
and a
jr nz, .trainermusic
farcall RegionCheck
ld a, e
and a
jr nz, .kantowild
ld de, MUSIC_JOHTO_WILD_BATTLE
ld a, [TimeOfDay]
cp NITE_F
jr nz, .done
ld de, MUSIC_JOHTO_WILD_BATTLE_NIGHT
jr .done
.kantowild
ld de, MUSIC_KANTO_WILD_BATTLE
jr .done
.trainermusic
ld de, MUSIC_CHAMPION_BATTLE
cp CHAMPION
jr z, .done
cp RED
jr z, .done
; really, they should have included admins and scientists here too...
ld de, MUSIC_ROCKET_BATTLE
cp GRUNTM
jr z, .done
cp GRUNTF
jr z, .done
ld de, MUSIC_KANTO_GYM_LEADER_BATTLE
farcall IsKantoGymLeader
jr c, .done
ld de, MUSIC_JOHTO_GYM_LEADER_BATTLE
farcall IsJohtoGymLeader
jr c, .done
ld de, MUSIC_RIVAL_BATTLE
ld a, [OtherTrainerClass]
cp RIVAL1
jr z, .done
cp RIVAL2
jr nz, .othertrainer
ld a, [OtherTrainerID]
cp RIVAL2_2_CHIKORITA ; Rival in Indigo Plateau
jr c, .done
ld de, MUSIC_CHAMPION_BATTLE
jr .done
.othertrainer
ld a, [wLinkMode]
and a
jr nz, .johtotrainer
farcall RegionCheck
ld a, e
and a
jr nz, .kantotrainer
.johtotrainer
ld de, MUSIC_JOHTO_TRAINER_BATTLE
jr .done
.kantotrainer
ld de, MUSIC_KANTO_TRAINER_BATTLE
.done
call PlayMusic
pop bc
pop de
pop hl
ret
ClearBattleRAM: ; 2ef18
xor a
ld [wPlayerAction], a
ld [wBattleResult], a
ld hl, wPartyMenuCursor
ld [hli], a
ld [hli], a
ld [hli], a
ld [hl], a
ld [wMenuScrollPosition], a
ld [CriticalHit], a
ld [BattleMonSpecies], a
ld [wBattleParticipantsNotFainted], a
ld [CurBattleMon], a
ld [wForcedSwitch], a
ld [TimeOfDayPal], a
ld [PlayerTurnsTaken], a
ld [EnemyTurnsTaken], a
ld [EvolvableFlags], a
ld hl, PlayerHPPal
ld [hli], a
ld [hl], a
ld hl, BattleMonDVs
ld [hli], a
ld [hl], a
ld hl, EnemyMonDVs
ld [hli], a
ld [hl], a
; Clear the entire BattleMons area
ld hl, wBattle
ld bc, wBattleEnd - wBattle
xor a
call ByteFill
callfar ResetEnemyStatLevels
call ClearWindowData
ld hl, hBGMapAddress
xor a ; LOW(VBGMap0)
ld [hli], a
ld [hl], HIGH(VBGMap0)
ret

269
engine/battle/trainer_huds.asm Executable file
View File

@@ -0,0 +1,269 @@
BattleStart_TrainerHuds: ; 2c000
ld a, $e4
ld [rOBP0], a
call LoadBallIconGFX
call ShowPlayerMonsRemaining
ld a, [wBattleMode]
dec a
ret z
jp ShowOTTrainerMonsRemaining
; 2c012
EnemySwitch_TrainerHud: ; 2c012
ld a, $e4
ld [rOBP0], a
call LoadBallIconGFX
jp ShowOTTrainerMonsRemaining
; 2c01c
ShowPlayerMonsRemaining: ; 2c01c
call DrawPlayerPartyIconHUDBorder
ld hl, PartyMon1HP
ld de, PartyCount
call StageBallTilesData
; ldpixel wPlaceBallsX, 12, 12
ld a, 12 * 8
ld hl, wPlaceBallsX
ld [hli], a
ld [hl], a
ld a, 8
ld [wPlaceBallsDirection], a
ld hl, Sprites
jp LoadTrainerHudOAM
; 2c03a
ShowOTTrainerMonsRemaining: ; 2c03a
call DrawEnemyHUDBorder
ld hl, OTPartyMon1HP
ld de, OTPartyCount
call StageBallTilesData
; ldpixel wPlaceBallsX, 9, 4
ld hl, wPlaceBallsX
ld a, 9 * 8
ld [hli], a
ld [hl], 4 * 8
ld a, -8
ld [wPlaceBallsDirection], a
ld hl, Sprites + PARTY_LENGTH * 4
jp LoadTrainerHudOAM
; 2c059
StageBallTilesData: ; 2c059
ld a, [de]
push af
ld de, Buffer1
ld c, PARTY_LENGTH
ld a, $34 ; empty slot
.loop1
ld [de], a
inc de
dec c
jr nz, .loop1
pop af
ld de, Buffer1
.loop2
push af
call .GetHUDTile
inc de
pop af
dec a
jr nz, .loop2
ret
; 2c075
.GetHUDTile: ; 2c075
ld a, [hli]
and a
jr nz, .got_hp
ld a, [hl]
and a
ld b, $33 ; fainted
jr z, .fainted
.got_hp
dec hl
dec hl
dec hl
ld a, [hl]
and a
ld b, $32 ; statused
jr nz, .load
dec b ; normal
jr .load
.fainted
dec hl
dec hl
dec hl
.load
ld a, b
ld [de], a
ld bc, PARTYMON_STRUCT_LENGTH + MON_HP - MON_STATUS
add hl, bc
ret
; 2c095
DrawPlayerHUDBorder: ; 2c095
ld hl, .tiles
ld de, wTrainerHUDTiles
ld bc, 4
call CopyBytes
hlcoord 18, 10
ld de, -1 ; start on right
jr PlaceHUDBorderTiles
.tiles
db $73 ; right side
db $77 ; bottom right
db $6f ; bottom left
db $76 ; bottom side
; 2c0ad
DrawPlayerPartyIconHUDBorder: ; 2c0ad
ld hl, .tiles
ld de, wTrainerHUDTiles
ld bc, 4
call CopyBytes
hlcoord 18, 10
ld de, -1 ; start on right
jr PlaceHUDBorderTiles
.tiles
db $73 ; right side
db $5c ; bottom right
db $6f ; bottom left
db $76 ; bottom side
; 2c0c5
DrawEnemyHUDBorder: ; 2c0c5
ld hl, .tiles
ld de, wTrainerHUDTiles
ld bc, 4
call CopyBytes
hlcoord 1, 2
ld de, 1 ; start on left
call PlaceHUDBorderTiles
ld a, [wBattleMode]
dec a
ret nz
ld a, [TempEnemyMonSpecies]
dec a
call CheckCaughtMon
ret z
hlcoord 1, 1
ld [hl], $5d
ret
.tiles
db $6d ; left side
db $74 ; bottom left
db $78 ; bottom right
db $76 ; bottom side
; 2c0f1
PlaceHUDBorderTiles: ; 2c0f1
ld a, [wTrainerHUDTiles]
ld [hl], a
ld bc, SCREEN_WIDTH
add hl, bc
ld a, [StartFlypoint]
ld [hl], a
ld b, $8
.loop
add hl, de
ld a, [MovementBuffer]
ld [hl], a
dec b
jr nz, .loop
add hl, de
ld a, [EndFlypoint]
ld [hl], a
ret
; 2c10d
LinkBattle_TrainerHuds: ; 2c10d
call LoadBallIconGFX
ld hl, PartyMon1HP
ld de, PartyCount
call StageBallTilesData
ld hl, wPlaceBallsX
ld a, 10 * 8
ld [hli], a
ld [hl], 8 * 8
ld a, $8
ld [wPlaceBallsDirection], a
ld hl, Sprites
call LoadTrainerHudOAM
ld hl, OTPartyMon1HP
ld de, OTPartyCount
call StageBallTilesData
ld hl, wPlaceBallsX
ld a, 10 * 8
ld [hli], a
ld [hl], 13 * 8
ld hl, Sprites + PARTY_LENGTH * 4
jp LoadTrainerHudOAM
; 2c143
LoadTrainerHudOAM: ; 2c143
ld de, Buffer1
ld c, PARTY_LENGTH
.loop
ld a, [wPlaceBallsY]
ld [hli], a
ld a, [wPlaceBallsX]
ld [hli], a
ld a, [de]
ld [hli], a
ld a, $3
ld [hli], a
ld a, [wPlaceBallsX]
ld b, a
ld a, [wPlaceBallsDirection]
add b
ld [wPlaceBallsX], a
inc de
dec c
jr nz, .loop
ret
; 2c165
LoadBallIconGFX: ; 2c165
ld de, .gfx
ld hl, VTiles0 tile $31
lb bc, BANK(LoadBallIconGFX), 4
call Get2bpp_2
ret
; 2c172
.gfx ; 2c172
INCBIN "gfx/battle/balls.2bpp"
; 2c1b2
_ShowLinkBattleParticipants: ; 2c1b2
call ClearBGPalettes
call LoadFontsExtra
hlcoord 2, 3
ld b, 9
ld c, 14
call TextBox
hlcoord 4, 5
ld de, PlayerName
call PlaceString
hlcoord 4, 10
ld de, OTPlayerName
call PlaceString
hlcoord 9, 8
ld a, "<BOLD_V>"
ld [hli], a
ld [hl], "<BOLD_S>"
farcall LinkBattle_TrainerHuds ; no need to farcall
ld b, SCGB_DIPLOMA
call GetSGBLayout
call SetPalettes
ld a, $e4
ld [rOBP0], a
ret
; 2c1ef

View File

@@ -0,0 +1,31 @@
TalkToTrainerScript:: ; 0xbe66a
faceplayer
trainerflagaction CHECK_FLAG
iftrue AlreadyBeatenTrainerScript
loadmemtrainer
encountermusic
jump StartBattleWithMapTrainerScript
SeenByTrainerScript:: ; 0xbe675
loadmemtrainer
encountermusic
showemote EMOTE_SHOCK, LAST_TALKED, 30
callasm TrainerWalkToPlayer
applymovement2 MovementBuffer
writeobjectxy LAST_TALKED
faceobject PLAYER, LAST_TALKED
jump StartBattleWithMapTrainerScript
StartBattleWithMapTrainerScript: ; 0xbe68a
opentext
trainertext $0
waitbutton
closetext
loadmemtrainer
startbattle
reloadmapafterbattle
trainerflagaction SET_FLAG
loadvar wRunningTrainerBattleScript, -1
AlreadyBeatenTrainerScript:
scripttalkafter

338
engine/battle/used_move_text.asm Executable file
View File

@@ -0,0 +1,338 @@
DisplayUsedMoveText: ; 105db0
; battle command 03
ld hl, UsedMoveText
call BattleTextBox
jp WaitBGMap
; 105db9
UsedMoveText: ; 105db9
; this is a stream of text and asm from 105db9 to 105ef6
text_jump _ActorNameText
start_asm
ld a, [hBattleTurn]
and a
jr nz, .start
ld a, [wPlayerMoveStruct + MOVE_ANIM]
call UpdateUsedMoves
.start
ld a, BATTLE_VARS_LAST_MOVE
call GetBattleVarAddr
ld d, h
ld e, l
ld a, BATTLE_VARS_LAST_COUNTER_MOVE
call GetBattleVarAddr
ld a, BATTLE_VARS_MOVE_ANIM
call GetBattleVar
ld [wd265], a
push hl
farcall CheckUserIsCharging
pop hl
jr nz, .grammar
; update last move
ld a, [wd265]
ld [hl], a
ld [de], a
.grammar
call GetMoveGrammar
; wd265 now contains MoveGrammar
; everything except 'instead' made redundant in localization
; check obedience
ld a, [AlreadyDisobeyed]
and a
ld hl, UsedMove2Text
ret nz
; check move grammar
ld a, [wd265]
cp $3
ld hl, UsedMove2Text
ret c
ld hl, UsedMove1Text
ret
; 105e04
UsedMove1Text: ; 105e04
text_jump _UsedMove1Text
start_asm
jr UsedMoveText_CheckObedience
; 105e0b
UsedMove2Text: ; 105e0b
text_jump _UsedMove2Text
start_asm
UsedMoveText_CheckObedience: ; 105e10
; check obedience
ld a, [AlreadyDisobeyed]
and a
jr z, .GetMoveNameText
; print "instead,"
ld hl, .UsedInsteadText
ret
; 105e1a
.UsedInsteadText:
text_jump _UsedInsteadText
start_asm
.GetMoveNameText:
ld hl, MoveNameText
ret
; 105e23
MoveNameText: ; 105e23
text_jump _MoveNameText
start_asm
; get start address
ld hl, .endusedmovetexts
; get move id
ld a, [wd265]
; 2-byte pointer
add a
; seek
push bc
ld b, $0
ld c, a
add hl, bc
pop bc
; get pointer to usedmovetext ender
ld a, [hli]
ld h, [hl]
ld l, a
ret
; 105e39
.endusedmovetexts ; 105e39
dw EndUsedMove1Text
dw EndUsedMove2Text
dw EndUsedMove3Text
dw EndUsedMove4Text
dw EndUsedMove5Text
; 105e43
EndUsedMove1Text: ; 105e43
text_jump _EndUsedMove1Text
db "@"
; 105e48
EndUsedMove2Text: ; 105e48
text_jump _EndUsedMove2Text
db "@"
; 105e4d
EndUsedMove3Text: ; 105e4d
text_jump _EndUsedMove3Text
db "@"
; 105e52
EndUsedMove4Text: ; 105e52
text_jump _EndUsedMove4Text
db "@"
; 105e57
EndUsedMove5Text: ; 105e57
text_jump _EndUsedMove5Text
db "@"
; 105e5c
GetMoveGrammar: ; 105e5c
; store move grammar type in wd265
push bc
; c = move id
ld a, [wd265]
ld c, a
ld b, $0
; read grammar table
ld hl, MoveGrammar
.loop
ld a, [hli]
; end of table?
cp $ff
jr z, .end
; match?
cp c
jr z, .end
; advance grammar type at $00
and a
jr nz, .loop
; next grammar type
inc b
jr .loop
.end
; wd265 now contains move grammar
ld a, b
ld [wd265], a
; we're done
pop bc
ret
; 105e7a
MoveGrammar: ; 105e7a
; made redundant in localization
; each move is given an identifier for what usedmovetext to use (0-4):
; 0
db SWORDS_DANCE
db GROWTH
db STRENGTH
db HARDEN
db MINIMIZE
db SMOKESCREEN
db WITHDRAW
db DEFENSE_CURL
db EGG_BOMB
db SMOG
db BONE_CLUB
db FLASH
db SPLASH
db ACID_ARMOR
db BONEMERANG
db REST
db SHARPEN
db SUBSTITUTE
db MIND_READER
db SNORE
db PROTECT
db SPIKES
db ENDURE
db ROLLOUT
db SWAGGER
db SLEEP_TALK
db HIDDEN_POWER
db PSYCH_UP
db EXTREMESPEED
db 0 ; end set
; 1
db RECOVER
db TELEPORT
db BIDE
db SELFDESTRUCT
db AMNESIA
db FLAIL
db 0 ; end set
; 2
db MEDITATE
db AGILITY
db MIMIC
db DOUBLE_TEAM
db BARRAGE
db TRANSFORM
db STRUGGLE
db SCARY_FACE
db 0 ; end set
; 3
db POUND
db SCRATCH
db VICEGRIP
db WING_ATTACK
db FLY
db BIND
db SLAM
db HORN_ATTACK
db WRAP
db THRASH
db TAIL_WHIP
db LEER
db BITE
db GROWL
db ROAR
db SING
db PECK
db ABSORB
db STRING_SHOT
db EARTHQUAKE
db FISSURE
db DIG
db TOXIC
db SCREECH
db METRONOME
db LICK
db CLAMP
db CONSTRICT
db POISON_GAS
db BUBBLE
db SLASH
db SPIDER_WEB
db NIGHTMARE
db CURSE
db FORESIGHT
db CHARM
db ATTRACT
db ROCK_SMASH
db 0 ; end set
; all other moves = 4
db $ff ; end
; 105ed0
UpdateUsedMoves: ; 105ed0
; append move a to PlayerUsedMoves unless it has already been used
push bc
; start of list
ld hl, PlayerUsedMoves
; get move id
ld b, a
; next count
ld c, NUM_MOVES
.loop
; get move from the list
ld a, [hli]
; not used yet?
and a
jr z, .add
; already used?
cp b
jr z, .quit
; next byte
dec c
jr nz, .loop
; if the list is full and the move hasn't already been used
; shift the list back one byte, deleting the first move used
; this can occur with struggle or a new learned move
ld hl, PlayerUsedMoves + 1
; 1 = 2
ld a, [hld]
ld [hli], a
; 2 = 3
inc hl
ld a, [hld]
ld [hli], a
; 3 = 4
inc hl
ld a, [hld]
ld [hl], a
; 4 = new move
ld a, b
ld [PlayerUsedMoves + 3], a
jr .quit
.add
; go back to the byte we just inced from
dec hl
; add the new move
ld [hl], b
.quit
; list updated
pop bc
ret
; 105ef6