Skip to content

Choices Within Attacks

Sha0den edited this page Oct 23, 2024 · 2 revisions

In this tutorial, we will learn how to create something with nearly infinite potential: the ability to select an attack, and then select one of two options which alter that attack. This is useful for making attacks which require more thinking than usual, as well as making them more exciting in general.

For this tutorial's example, we will refer to Flareon's Flare Blitz attack in Pokemon TCG Neo, as it is a simple example. This attack allows the player to choose to use the attack with no effect or to have the Attacking Pokémon do 30 more damage to the Defending Pokémon and then do 30 damage to itself. This means that Flareon's Flare Blitz is capable of either doing 30 damage, or 60 damage but with 30 recoil.

  1. First, we will be making a text definition. (check here for a tutorial on adding new texts):
FlareBlitzYesNoText:
	text "Do 30 more damage, but Recoil?"
	line "            Yes     No"
	done

This is the text that will show up on the attack menu after the player selects Flare Blitz. The yes and no options are spaced exactly where the cursors will be during the selection process.

  1. Next, we will put the effect commands in src/engine/duel/effect_commands.asm. You can of course rename then to whatever you want, but in this example, because Flareon is the only Pokémon in Neo that uses this particular modal option, the effect functions are named after it.
; Yes/No: Do 30 more damage in exchange for 30 recoil damage
CanDo30ButRecoilEffectCommands:
	dbw EFFECTCMDTYPE_BEFORE_DAMAGE, FlareonDamageEffect
	dbw EFFECTCMDTYPE_REQUIRE_SELECTION, PlayerYesNoFlareonEffect
	dbw EFFECTCMDTYPE_AFTER_DAMAGE, FlareonRecoilEffect
	dbw EFFECTCMDTYPE_AI_SELECTION, FlareonFB_AISelectEffect
	dbw EFFECTCMDTYPE_AI, FlareonFB_AIEffect 
	db  $00
  1. Now, we will go into src/engine/duel/effect_functions.asm and create the effect functions for the player, which are the first three in the above effect commands.
; output:
;	[hTemp_ffa0] = whichever option the Player selected (0 = "Yes", 1 = "No")
PlayerYesNoFlareonEffect:
	bank1call DrawDuelMainScene ; draws a screen for the player to select from
	ldtx hl, FlareonYesNoText ; loads the text we made in step 1
	call TwoItemHorizontalMenu ; defines that this text contains 2 menu items (yes/no)
	ldh a, [hKeysHeld] ; loads the player's input
	and B_BUTTON
	jr nz, PlayerYesNoFlareonEffect ; this forces the player to select either "Yes" or "No", can't exit
	ldh a, [hCurMenuItem] ; stores the result in a
	ldh [hTemp_ffa0], a ; loads what the player selected into hTemp_ffa0
	ret

; preserves all registers except af
; input:
;	[hTemp_ffa0] != 0:  immediately return without applying the effect
FlareonDamageEffect:
	ldh a, [hTemp_ffa0]
	or a
	ret nz ; return if "Yes" wasn't selected
	; otherwise, load the correct animation and increase the damage by 30
	ld a, ATK_ANIM_FLARE_BLITZ
	ld [wLoadedAttackAnimation], a
	ld a, 30
	jp AddToDamage

; input:
;	[hTemp_ffa0] != 0:  immediately return without applying the effect
FlareonRecoilEffect:
	ldh a, [hTemp_ffa0]
	or a
	ret nz ; return if "Yes" wasn't selected
	; otherwise, the Attacking Pokémon does 30 damage to itself
	ld a, 30
	jp DealRecoilDamageToSelf

That's it for the Player! But the AI still doesn't understand the effect. We will have to give the AI its own logic.

  1. How you do the AI logic will vary a lot depending on what the effect of your choice will be, and what you want the AI to prioritize. In this particular example, we will tell the AI to always select "Yes" if the Defending Pokémon's HP is above 30 and "No" if its HP is between 0 and 30. This is just a simple logic so that the AI will try to KO the Defending Pokémon as much as possible, but not resort to self damage if the base damage is enough to KO the Defending Pokémon. Again, it is possible in this example to have the AI always choose "Yes" even at the risk of self KO, or the opposite and always select "No".

Go back to src/engine/duel/effect_functions.asm

; preserves bc and hl
FlareonFB_AIEffect:
	ld a, 30
	lb de, 30, 60 ; The AI now knows it will do either 30 or 60 damage.
	jp SetExpectedAIDamage

; preserves bc and de
; output:
;	[hTemp_ffa0] = whichever option the AI selected (0 = "Yes", 1 = "No")
FlareonFB_AISelectEffect:
	ld a,  DUELVARS_ARENA_CARD_HP
	call GetNonTurnDuelistVariable
	; a = Defending Pokémon's current HP
	cp 31
	jr nc, .more_than_30_hp
	; base damage can already KO
	ld a, 1 ; "No"
	ldh [hTemp_ffa0], a
	ret

.more_than_30_hp
	xor a ; "Yes"
	ldh [hTemp_ffa0], a
	ret

And now the AI understands the attack! Of course, you can program more logic than this, but in this tutorial, the above will suffice.