Skip to content

Remove AI artificial delay

ElectroDeoxys edited this page Aug 3, 2024 · 6 revisions

The AI opponent has an artificial 60 frame delay when it is processing actions for the turn. Reducing this delay is easily done by changing the delay value in AIMakeDecision:

AIMakeDecision:
	ldh [hOppActionTableIndex], a
	ld hl, wSkipDuelistIsThinkingDelay
	ld a, [hl]
	ld [hl], $0
	or a
	jr nz, .skip_delay
.delay_loop
	call DoFrame
	ld a, [wVBlankCounter]
	cp 60 ; CHANGE HERE
	jr c, .delay_loop

.skip_delay
	...

On the other hand, if you want to remove this functionality completely then read on.

Contents

  1. Remove delay logic
  2. Disallow opponent attack on first turn

1. Remove delay logic

Remove delay loop in AIMakeDecision. Edit src/engine/duel/core.asm:

 AIMakeDecision:
 	ldh [hOppActionTableIndex], a
-	ld hl, wSkipDuelistIsThinkingDelay
-	ld a, [hl]
-	ld [hl], $0
-	or a
-	jr nz, .skip_delay
-.delay_loop
-	call DoFrame
-	ld a, [wVBlankCounter]
-	cp 60
-	jr c, .delay_loop
-
-.skip_delay
-	ldh a, [hOppActionTableIndex]
 	ld hl, wOpponentTurnEnded
 	ld [hl], 0
 	ld hl, OppActionTable

2. Remove rest of logic regarding the delay

Now there is some code that dynamically switches off the delay loop through wSkipDuelistIsThinkingDelay, which is now redundant. We can safely remove these portions of code. Edit src/engine/duel/core.asm again:

 DuelMainInterface:
 	...

 	; DUELIST_TYPE_AI_OPP
 	xor a
 	ld [wVBlankCounter], a
-	ld [wSkipDuelistIsThinkingDelay], a
 	ldtx hl, DuelistIsThinkingText
 	call DrawWideTextBox_PrintTextNoDelay
 	call AIDoAction_Turn
 AIMakeDecision:
 	...

 	ld a, [wDuelFinished]
 	ld hl, wOpponentTurnEnded
 	or [hl]
-	jr nz, .turn_ended
-	ld a, [wSkipDuelistIsThinkingDelay]
-	or a
-	ret nz
-	ld [wVBlankCounter], a
-	ldtx hl, DuelistIsThinkingText
-	call DrawWideTextBox_PrintTextNoDelay
-	or a
-	ret
-
-.turn_ended
+	ret z
 	scf
 	ret
 OppAction_PlayTrainerCard:
 	call LoadNonPokemonCardEffectCommands
 	call DisplayUsedTrainerCardDetailScreen
 	call PrintUsedTrainerCardDescription
-	call ExchangeRNG
-	ld a, $01
-	ld [wSkipDuelistIsThinkingDelay], a
-	ret
+	jp ExchangeRNG
 OppAction_BeginUseAttack:
 	...
 	ld e, a
 	call CopyAttackDataAndDamage_FromDeckIndex
 	call UpdateArenaCardIDsAndClearTwoTurnDuelVars
-	ld a, $01
-	ld [wSkipDuelistIsThinkingDelay], a
 	call CheckSandAttackOrSmokescreenSubstatus
 	jr c, .has_status
 	ld a, DUELVARS_ARENA_CARD_STATUS
 OppAction_UseAttack:
 	...
 	call PrintPokemonsAttackText
 	call WaitForWideTextBoxInput
-	call ExchangeRNG
-	ld a, $01
-	ld [wSkipDuelistIsThinkingDelay], a
-	ret
+	jp ExchangeRNG
 .confusion_damage
 	call HandleConfusionDamageToSelf
 OppAction_UsePokemonPower:
 	...
 	ld [wTxRam2_b + 1], a
 	ldtx hl, WillUseThePokemonPowerText
 	call DrawWideTextBox_WaitForInput_Bank1
-	call ExchangeRNG
-	ld a, $01
-	ld [wSkipDuelistIsThinkingDelay], a
-	ret
+	jp ExchangeRNG
 
 ; execute the EFFECTCMDTYPE_BEFORE_DAMAGE command of the used Pokemon Power
 OppAction_ExecutePokemonPowerEffect:
 	call Func_7415
 	ld a, EFFECTCMDTYPE_BEFORE_DAMAGE
-	call TryExecuteEffectCommandFunction
-	ld a, $01
-	ld [wSkipDuelistIsThinkingDelay], a
-	ret
+	jp TryExecuteEffectCommandFunction
 
 ; execute the EFFECTCMDTYPE_AFTER_DAMAGE command of the used Pokemon Power
 OppAction_6b15:
 	ld a, EFFECTCMDTYPE_AFTER_DAMAGE
-	call TryExecuteEffectCommandFunction
-	ld a, $01
-	ld [wSkipDuelistIsThinkingDelay], a
-	ret
+	jp TryExecuteEffectCommandFunction
 
 OppAction_DrawDuelMainScene:
 	call DrawDuelMainScene
 OppAction_TossCoinATimes:
 	call SerialRecv8Bytes
-	call TossCoinATimes
-	ld a, $01
-	ld [wSkipDuelistIsThinkingDelay], a
-	ret
+	jp TossCoinATimes
 
 OppAction_6b30:
 	ldh a, [hWhoseTurn]

Although we switched off the delay loop, wSkipDuelistIsThinkingDelay is still used for Link duels, so it cannot be completely removed.

Now we are set, you'll find that the AI is much more snappy with its actions and the duel flows more fluidly.