This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This tutorial will help you to set up turn flags. To better understand the concept of flags, you should know that every byte is made up of 8 bits. Usually, those bits are combined to represent a number between 0 and 255, but each of those bits can also be used separately as flags to record variables with only two values. More specifically, turn flags can be used to track whether a certain action has occurred during the current turn of a duel and/or prevent players from performing that action (excepting anything that references a specific play area Pokémon, as that would require using the DUELVARS_ARENA_CARD_FLAGS). Most notably, these turn flags can be used to implement once per turn restrictions on any number of actions, such as retreating, playing a Trainer/Supporter card, or even evolving a Pokémon. The first of those examples will actually be covered during this tutorial. Limiting retreating to just once per turn was one of the earliest rule changes in the Pokémon TCG (added with EX Ruby & Sapphire), and its likely something that most romhacks will want to include.
Contents
- Designate a WRAM Byte for Once Per Turn Flags
- Implement a Once Per Turn Restriction for Retreating
- Potential Card Effects:
1. Designate a WRAM Byte for Once Per Turn Flags
Normally, we would label an unused byte in src/wram.asm, but in this instance, it makes more sense to repurpose one of the wram bytes that already acts like a once per turn flag. wConfusionRetreatCheckWasUnsuccessful is a good option. It's already set up as a boolean, meaning that there are only 2 possible values for the byte (TRUE/FALSE), and it's reset at the start of each turn by InitVariablesToBeginTurn. wAlreadyPlayedEnergy would also work, but it's referenced a lot more than wConfusionRetreatCheckWasUnsuccessful in the AI files, so converting it into a flag would require more adjustments to the engine code. We'll also be using our new retreat flag during step 2.
Warning: If you end up using a different wram byte for turn flags, make sure that it's listed in DuelDataToSave, which can be found in src/engine/duel/core.asm. Otherwise, the flags won't work properly after resetting the game and selecting the "Continue Duel" option.
wAlreadyPlayedEnergy:: ; cc0b
ds $1
-; set to TRUE if the confusion check coin toss in AttemptRetreat is tails
-wConfusionRetreatCheckWasUnsuccessful:: ; cc0c
+; a series of bit flags that are reset each turn in a duel
+; see wOncePerTurnFlags constants
+wOncePerTurnFlags:: ; cc0c
ds $1
; DUELIST_TYPE_* of the turn holder
wDuelistType:: ; cc0d
Next, we'll need to set up the initial constants for our reallocated byte, so add the following to src/constants/duel_constants.asm.
DEF DUEL_WIN EQU $0
DEF DUEL_LOSS EQU $1
+; wOncePerTurnFlags constants
+DEF UNABLE_TO_RETREAT_THIS_TURN_F EQU 0
+
+DEF UNABLE_TO_RETREAT_THIS_TURN EQU 1 << UNABLE_TO_RETREAT_THIS_TURN_F
+
; wPlayerDuelVariables or wOpponentDuelVariables constants
DEF DUELVARS_CARD_LOCATIONS EQUS "LOW(wPlayerCardLocations)" ; 00
DEF DUELVARS_PRIZE_CARDS EQUS "LOW(wPlayerPrizeCards)" ; 3c
So far, we're only using the one flag (bit 0). That leaves seven more bits (1-7) that can still be used for other turn flags. The constant with "_F" represents the value of the entire byte with only that flag set. In ascending order, the hexadecimal values for each of the bits being set by themselves are: $01, $02, $04, $08, $10, $20, $40, and $80. You could also use those values to define the secondary constants, but writing it the way that we did will allow us to change the bit in the first constant without having to update the second.
Now, we need to update all of the code that referenced the original wram byte. The former retreat code only cared if wConfusionRetreatCheckWasUnsuccessful was greater than zero, so we should also zero out any unrelated flags that we might add to wOncePerTurnFlags when making a comparison; this can be achieved by replacing the or a instruction with and UNABLE_TO_RETREAT_THIS_TURN. There's one section to change in src/engine/duel/ai/retreat.asm...
; determine AI score for retreating
; return carry if AI decides to retreat
AIDecideWhetherToRetreat:
- ld a, [wConfusionRetreatCheckWasUnsuccessful]
- or a
+ ld a, [wOncePerTurnFlags]
+ and UNABLE_TO_RETREAT_THIS_TURN
jp nz, .no_carry
xor a
ld [wAIPlayEnergyCardForRetreat], a
...
... and a few more in src/engine/duel/core.asm.
DuelMenu_Retreat:
ld a, DUELVARS_ARENA_CARD_STATUS
call GetTurnDuelistVariable
and CNF_SLP_PRZ
cp CONFUSED
ldh [hTemp_ffa0], a
jr nz, .not_confused
- ld a, [wConfusionRetreatCheckWasUnsuccessful]
- or a
+ ld a, [wOncePerTurnFlags]
+ and UNABLE_TO_RETREAT_THIS_TURN
jr nz, .unable_due_to_confusion
call CheckAbleToRetreat
jr c, .unable_to_retreat
...
The edits to AttemptRetreat will be a little different since we need to set/reset the new flag rather than change the value of the entire byte. Fortunately, this function doesn't care about preserving the hl register.
AttemptRetreat:
call DiscardRetreatCostCards
ldh a, [hTemp_ffa0]
and CNF_SLP_PRZ
cp CONFUSED
jr nz, .success
ldtx de, ConfusionCheckRetreatText
call TossCoin
jr c, .success
- ld a, TRUE
- ld [wConfusionRetreatCheckWasUnsuccessful], a
+ ld hl, wOncePerTurnFlags
+ set UNABLE_TO_RETREAT_THIS_TURN_F, [hl]
scf
ret
.success
ldh a, [hTempPlayAreaLocation_ffa1]
ld e, a
call SwapArenaWithBenchPokemon
- xor a ; FALSE
- ld [wConfusionRetreatCheckWasUnsuccessful], a
+ ld hl, wOncePerTurnFlags
+ res UNABLE_TO_RETREAT_THIS_TURN_F, [hl]
ret
The final reference is in the function that resets variables at the start of each turn. You may notice that there are 2 other wram bytes being reset. wAlreadyPlayedEnergy, which we mentioned earlier, and wGotHeadsFromSandAttackOrSmokescreenCheck also function as booleans, so they could safely be added to wOncePerTurnFlags if you want to use those bytes for some other purpose. You just need to copy the above steps and change all of the references to the original bytes. Depending on the scope of your game, you might even want to create a second byte for turn flags (e.g. wMiscTurnFlags). If you do, remember to add any new flag bytes to DuelDataToSave; although, both wGotHeadsFromSandAttackOrSmokescreenCheck and wAlreadyPlayedEnergy are already on the list.
InitVariablesToBeginTurn:
xor a
ld [wAlreadyPlayedEnergy], a
- ld [wConfusionRetreatCheckWasUnsuccessful], a
+ ld [wOncePerTurnFlags], a
ld [wGotHeadsFromSandAttackOrSmokescreenCheck], a
ldh a, [hWhoseTurn]
ld [wWhoseTurn], a
ret
2. Implement a Once Per Turn Restriction for Retreating
The bulk of the retreat code for the duel engine can be found in src/engine/duel/core.asm. We'll at least need to modify DuelMenu_Retreat and AttemptRetreat, which were both already edited during Step 1, but I also recommend some minor refactoring of DuelMenu_Retreat, which will move the once per turn flag check to CheckAbleToRetreat. We'll start with the changes to DuelMenu_Retreat.
DuelMenu_Retreat:
+ call CheckAbleToRetreat
+ jr c, .unable_to_retreat
ld a, DUELVARS_ARENA_CARD_STATUS
call GetTurnDuelistVariable
and CNF_SLP_PRZ
cp CONFUSED
ldh [hTemp_ffa0], a
jr nz, .not_confused
- ld a, [wOncePerTurnFlags]
- and UNABLE_TO_RETREAT_THIS_TURN
- jr nz, .unable_due_to_confusion
- call CheckAbleToRetreat
- jr c, .unable_to_retreat
call DisplayRetreatScreen
jr c, .done
ldtx hl, SelectPkmnOnBenchToSwitchWithActiveText
call DrawWideTextBox_WaitForInput
call OpenPlayAreaScreenForSelection
jr c, .done
ld [wBenchSelectedPokemon], a
ld a, [wBenchSelectedPokemon] ; unnecessary
ldh [hTempPlayAreaLocation_ffa1], a
ld a, OPPACTION_ATTEMPT_RETREAT
call SetOppAction_SerialSendDuelData
call AttemptRetreat
jr nc, .done
+ ; retreat unsuccessful due to confusion
call DrawDuelMainScene
-
-.unable_due_to_confusion
ldtx hl, UnableToRetreatText
+.unable_to_retreat
call DrawWideTextBox_WaitForInput
jp PrintDuelMenuAndHandleInput
.not_confused
; note that the energy cards are discarded (DiscardRetreatCostCards), then returned
; (ReturnRetreatCostCardsToArena), then discarded again for good (AttemptRetreat).
; It's done this way so that the retreating Pokemon is listed with its energies updated
; when the Play Area screen is shown to select the Pokemon to switch to. The reason why
; AttemptRetreat is responsible for discarding the energy cards is because, if the
; Pokemon is confused, it may not be able to retreat, so they cannot be discarded earlier.
- call CheckAbleToRetreat
- jr c, .unable_to_retreat
call DisplayRetreatScreen
jr c, .done
call DiscardRetreatCostCards
ldtx hl, SelectPkmnOnBenchToSwitchWithActiveText
call DrawWideTextBox_WaitForInput
call OpenPlayAreaScreenForSelection
ld [wBenchSelectedPokemon], a
ldh [hTempPlayAreaLocation_ffa1], a
push af
call ReturnRetreatCostCardsToArena
pop af
jp c, DuelMainInterface
ld a, OPPACTION_ATTEMPT_RETREAT
call SetOppAction_SerialSendDuelData
call AttemptRetreat
.done
jp DuelMainInterface
-
-.unable_to_retreat
- call DrawWideTextBox_WaitForInput
- jp PrintDuelMenuAndHandleInput
Now, scroll down to CheckAbleToRetreat, and add in the check for the once per turn retreat flag. We'll also give it its own notification text.
; check if the turn holder's arena Pokemon is unable to retreat due to
; some status condition or due the bench containing no alive Pokemon.
; return carry if unable, nc if able.
CheckAbleToRetreat:
+ ld a, [wOncePerTurnFlags]
+ and UNABLE_TO_RETREAT_THIS_TURN
+ ldtx hl, MayOnlyRetreatOncePerTurnText
+ jr nz, .done
call CheckUnableToRetreatDueToEffect
ret c
call CheckIfActiveCardParalyzedOrAsleep
ret c
call HasAlivePokemonInBench
jr c, .unable_to_retreat
ld a, DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
call GetCardIDFromDeckIndex
call LoadCardDataToBuffer1_FromCardID
ld a, [wLoadedCard1Type]
cp TYPE_TRAINER
jr z, .unable_to_retreat
call CheckIfEnoughEnergiesToRetreat
jr c, .not_enough_energies
or a
ret
.not_enough_energies
ld a, [wEnergyCardsRequiredToRetreat]
ld l, a
ld h, $00
call LoadTxRam3
ldtx hl, EnergyCardsRequiredToRetreatText
jr .done
.unable_to_retreat
ldtx hl, UnableToRetreatText
.done
scf
ret
The final function is located much farther down in the file. The following modifications to AttemptRetreat might seem complicated, but a lot of that is simply removing unnecessary code. The only real change is that the once per turn retreat flag now gets set regardless of whether or not the retreat was successful.
; discard retreat cost energy cards and attempt retreat of the arena card.
; return carry if unable to retreat this turn due to unsuccessful confusion check
; if successful, the retreated card is replaced with a bench Pokemon card
AttemptRetreat:
call DiscardRetreatCostCards
ldh a, [hTemp_ffa0]
and CNF_SLP_PRZ
cp CONFUSED
jr nz, .success
ldtx de, ConfusionCheckRetreatText
call TossCoin
- jr c, .success
- ld hl, wOncePerTurnFlags
- set UNABLE_TO_RETREAT_THIS_TURN_F, [hl]
- scf
- ret
+ ccf
+ jr c, .prevent_further_retreating
.success
ldh a, [hTempPlayAreaLocation_ffa1]
ld e, a
call SwapArenaWithBenchPokemon
+.prevent_further_retreating
ld hl, wOncePerTurnFlags
- res UNABLE_TO_RETREAT_THIS_TURN_F, [hl]
+ set UNABLE_TO_RETREAT_THIS_TURN_F, [hl]
ret
Before moving on, make sure you define the text that we referenced in CheckAbleToRetreat. You can either replace one of the unused texts or add it to any of the text files that isn't yet full. Feel free to review the How to add a new text tutorial if you're still unfamiliar with the process.
MayOnlyRetreatOncePerTurnText:
text "You may only retreat once per turn."
done
The only other thing that you would normally need to consider is the AI, but in this instance, that isn't actually necessary. The AI already limits itself to only retreating once per turn, so you can stop here if you want. Although, I recommend getting rid of the redundant variable to free up the following byte in src/wram.asm.
wAIAttackIsNonDamaging:: ; ce02
ds $1
-; whether AI already retreated this turn or not.
-; - $0 has not retreated;
-; - $1 has retreated.
-wAIRetreatedThisTurn:: ; ce03
+; unused wram byte
ds $1
; used by AI to store information of VenusaurLv67
; while handling Energy Trans logic.
wAIVenusaurLv67DeckIndex:: ; ce04
You'll also need to delete the reference in src/engine/duel/ai/init.asm...
InitAITurnVars:
...
ld [wPreviousAIFlags], a
ld [wAITriedAttack], a
ld [wcddc], a
- ld [wAIRetreatedThisTurn], a
; checks if the Player used an attack last turn
; and if it was the second attack of their card.
ld a, [wPlayerAttackingAttackIndex]
...
... and all of the references in src/engine/duel/ai/decks/general.asm. The first few lines can simply be deleted since the once per turn retreat flag is already checked at the beginning of AIDecideWhetherToRetreat, but you'll have to set UNABLE_TO_RETREAT_THIS_TURN_F somewhere in the function to prevent the AI from retreating more than once each turn. If you keep the original timing that occurs before checking for Switch, then the AI will only switch their Active Pokémon once each turn, either by retreating or by using the Trainer card Switch. The alternative would be to set the flag after the jr nz, .used_switch line to only apply the limit after retreating and not after using a Switch card. The second option is a better application of the rules of the game, but we'll go with the first since that's how the AI logic was originally constructed. Technically, the AI only tries to switch their Active Pokémon a second time if they played a Professor Oak card, so this decision isn't terribly important.
AIProcessRetreat:
- ld a, [wAIRetreatedThisTurn]
- or a
- ret nz ; return, already retreated this turn
-
call AIDecideWhetherToRetreat
ret nc ; return if not retreating
call AIDecideBenchPokemonToSwitchTo
ret c ; return if no Bench Pokemon
; store Play Area to retreat to and
-; set wAIRetreatedThisTurn to true
+; set flag to prevent retreating again this turn
ld [wAIPlayAreaCardToSwitch], a
- ld a, TRUE
- ld [wAIRetreatedThisTurn], a
+ ld hl, wOncePerTurnFlags
+ set UNABLE_TO_RETREAT_THIS_TURN_F, [hl]
; if AI can use Switch from hand, use it instead...
ld a, AI_TRAINER_CARD_PHASE_09
call AIProcessHandTrainerCards
ld a, AI_TRAINER_CARD_PHASE_09
call AIProcessHandTrainerCards
ld a, [wPreviousAIFlags]
and AI_FLAG_USED_SWITCH
jr nz, .used_switch
; ... else try retreating normally.
ld a, [wAIPlayAreaCardToSwitch]
call AITryToRetreat
ret
3. Potential Card Effects
Only Allow a Specific Card or Effect to Be Used Once Each Turn
One of the simplest card effects you could create would probably be for an overpowered Trainer card that prevented any other copy of that card from being played during the same turn, sort of like a weaker Supporter effect. It's worth noting that this type of restriction could also be used to limit a strong Pokémon Power, like the ones found on the Legendary Cards, but our example will be for a Trainer card.
First, you would update the wOncePerTurnFlags constants in src/constants/duel_constants.asm. Replace "CARD_NAME" with whatever the Trainer card's name happens to be (e.g. DOWSING_MACHINE), and the 7 in "EQU 7" could be any unused bit (0-7).
DEF PLAYED_CARD_NAME_THIS_TURN_F EQU 7
DEF PLAYED_CARD_NAME_THIS_TURN EQU 1 << PLAYED_CARD_NAME_F
Next, open src/engine/duel/effect_commands.asm. You would need at least the following two effect commands. Like before, replace "CardName" with the name of the Trainer card in the following effect commands/functions.
CardNameEffectCommands:
dbw EFFECTCMDTYPE_INITIAL_EFFECT_1, CardName_PreviouslyPlayedCheck
dbw EFFECTCMDTYPE_BEFORE_DAMAGE, CardNameEffect
db $00
Finally, define the above functions in src/engine/duel/effect_functions.asm. Depending on the actual effect of the Trainer card, you might need to merge the provided contents for CardName_PreviouslyPlayedCheck with other checks that would prevent the card from being played. Also, the "..." in CardName_Effect is where the core effect of the Trainer card would go; although, any player selection aspects would probably go in another effect function/command. Remember to define the notification text as well (AlreadyPlayedCardNameText); see the How to add a new text tutorial for more details.
; returns carry if another copy of this card was already played this turn
CardName_PreviouslyPlayedCheck:
ld a, [wOncePerTurnFlags]
and PLAYED_CARD_NAME_THIS_TURN
ret z
ldtx hl, AlreadyPlayedCardNameText
scf
ret
CardName_Effect:
ld hl, wOncePerTurnFlags
set PLAYED_CARD_NAME_THIS_TURN_F, [hl]
...
ret
Forbid Player from Attacking or Using Trainers for the Rest of the Turn
You could also use turn flags to keep the player from performing certain actions, like with the secondary effects of Geeta, which prevents the player from attacking, or PokéGear/Professor Elm/Time Capsule, which all prevent other Trainer cards from being used during the same turn. These examples from the TCG are all Trainer cards, but you could just as easily apply this type of effect to a Pokémon Power.
First, you would update the wOncePerTurnFlags constants in src/constants/duel_constants.asm. The numbers for "EQU 6" and "EQU 7" can be any unused bits (0-7).
DEF UNABLE_TO_ATTACK_THIS_TURN_F EQU 6
DEF UNABLE_TO_USE_TRAINERS_THIS_TURN_F EQU 7
DEF UNABLE_TO_ATTACK_THIS_TURN EQU 1 << UNABLE_TO_ATTACK_THIS_TURN_F
DEF UNABLE_TO_USE_TRAINERS_THIS_TURN EQU 1 << UNABLE_TO_USE_TRAINERS_THIS_TURN_F
Either of these flags would be set by the main effect function for the Trainer card or Pokémon Power with the restriction, just like with the previous example. That's usually the function linked with EFFECTCMDTYPE_BEFORE_DAMAGE. Just open src/engine/duel/effect_functions.asm, and add the appropriate 2 lines to the beginning of your effect function.
ld hl, wOncePerTurnFlags
set UNABLE_TO_ATTACK_THIS_TURN_F, [hl]
ld hl, wOncePerTurnFlags
set UNABLE_TO_USE_TRAINERS_THIS_TURN_F, [hl]
Most prevention effects are handled by functions in src/home/substatus.asm. Modify HandleCantAttackSubstatus if you're adding UNABLE_TO_ATTACK_THIS_TURN_F...
-; return carry if the turn holder's arena Pokemon is under a condition that makes
-; it unable to attack. also return in hl the text id to be displayed
+; return carry if an effect is preventing the turn holder's Active Pokémon from attacking
+; with text ID for corresponding notification text in hl.
+; preserves bc and de
HandleCantAttackSubstatus::
+ ld a, [wOncePerTurnFlags]
+ bit UNABLE_TO_ATTACK_THIS_TURN_F, a
+ ldtx hl, UnableToAttackThisTurnText
+ jr nz, .return_with_cant_attack
ld a, DUELVARS_ARENA_CARD_SUBSTATUS2
call GetTurnDuelistVariable
or a
ret z
ldtx hl, UnableToAttackDueToTailWagText
cp SUBSTATUS2_TAIL_WAG
jr z, .return_with_cant_attack
ldtx hl, UnableToAttackDueToLeerText
cp SUBSTATUS2_LEER
jr z, .return_with_cant_attack
ldtx hl, UnableToAttackDueToBoneAttackText
cp SUBSTATUS2_BONE_ATTACK
jr z, .return_with_cant_attack
or a
ret
.return_with_cant_attack
scf
ret
... and CheckCantUseTrainerDueToEffect if you're adding UNABLE_TO_USE_TRAINERS_THIS_TURN_F.
-; return carry if the turn holder is affected by Headache and trainer cards can't be used
+; return carry if an effect is preventing the turn holder from playing Trainer cards
+; with text ID for corresponding notification text in hl.
+; preserves bc and de
CheckCantUseTrainerDueToEffect::
+ ld a, [wOncePerTurnFlags]
+ bit UNABLE_TO_USE_TRAINERS_THIS_TURN_F, a
+ ldtx hl, UnableToUseAnotherTrainerThisTurnText
+ jr nz, .set_carry
ld a, DUELVARS_ARENA_CARD_SUBSTATUS3
call GetTurnDuelistVariable
or a
bit SUBSTATUS3_HEADACHE_F, [hl]
ret z
ldtx hl, UnableToUseTrainerDueToHeadacheText
+.set_carry
scf
ret
The final step is to define the newly referenced text(s). See the How to add a new text tutorial for more details. If your flag is used for several different effects, then you'll have to write something more generic, like these:
UnableToAttackThisTurnText:
text "You are no longer able to attack."
line "this turn."
done
UnableToUseAnotherTrainerThisTurnText:
text "You can't play any other Trainer"
line "cards for the rest of the turn."
done
However, if the flag is only tied to a single effect, then you might consider adjusting the labels and contents of the text to reference the source of the restriction, like so:
UnableToAttackDueToGeetaText:
text "You are unable to attack"
line "due to the effect of Geeta."
done
UnableToUseTrainerDueToProfessorElmText:
text "You can't play any other Trainer"
line "cards after using Professor Elm."
done
Increased Attack Damage if a Specific Card Was Played That Turn
Quite a few Pokémon cards in the TCG possess attacks which deal increased damage if a specific card (generally a Supporter) was played during the same turn. These can be implemented rather easily using a turn flag. Let's see if we can recreate the Tactful Tangling attack on Tangela (Scarlet & Violet—151).
First, you would update the wOncePerTurnFlags constants in src/constants/duel_constants.asm. Like with the previous examples, the 7 in "EQU 7" could be any unused bit (0-7).
DEF PLAYED_ERIKAS_INVITATION_THIS_TURN_F EQU 7
DEF PLAYED_ERIKAS_INVITATION_THIS_TURN EQU 1 << PLAYED_ERIKAS_INVITATION_THIS_TURN_F
Next, open src/engine/duel/effect_commands.asm, and add the following effect.
TangelaTactfulTanglingEffectCommands:
dbw EFFECTCMDTYPE_BEFORE_DAMAGE, TactfulTanglingEffect
dbw EFFECTCMDTYPE_AI, TactfulTangling_AIEffect
db $00
Then, define the above functions in src/engine/duel/effect_functions.asm. We'll implement the attack effect exactly as it's written, but you should probably consider lowering the damage boost if you're keeping HP values similar to what's already in the original game.
; increases attack damage by 60 if Erika's Invitation was played this turn
TactfulTanglingEffect:
ld a, [wOncePerTurnFlags]
and PLAYED_ERIKAS_INVITATION_THIS_TURN
ret z ; cancel effect if Erika's Invitation wasn't played this turn
ld a, 60
jp AddToDamage
TactfulTangling_AIEffect:
call TactfulTanglingEffect
jp SetDefiniteAIDamage
You'll also need to set the new turn flag in the main effect function for the Erika's Invitation card (listed after EFFECTCMDTYPE_BEFORE_DAMAGE in effect commands). You would obviously need to add the card to the game first. Suffice it to say that this is beyond the parameters of the current example, but if you would like to do so, then you would most likely be able to base its effect commands/functions off of the ones for Pokémon Flute. After creating the card, you would add the following lines to the beginning of its final effect function.
ErikasInvitation_PlaceInPlayEffect:
+ ld hl, wOncePerTurnFlags
+ set PLAYED_ERIKAS_INVITATION_THIS_TURN_F, [hl]
...
ret
Finally, create the attack data for Tactful Tangling in src/data/cards.asm...
; attack 1
energy GRASS, 1 ; energies
tx TactfulTanglingName ; name
tx TactfulTanglingDescription ; description
dw NONE ; description (cont)
db 10 ; damage
db DAMAGE_PLUS ; category
dw TangelaTactfulTanglingEffectCommands ; effect commands
db NONE ; flags 1
db NONE ; flags 2
db NONE ; flags 3
db 0
db ATK_ANIM_WHIP ; animation
...and then define the texts for the attack name and description. See the How to add a new text tutorial for more details.
TactfulTanglingName:
text "Tactful Tangling"
done
TactfulTanglingDescription:
text "If you played Erika’s Invitation"
line "from your hand during this turn,"
line "this attack does 60 more damage."
done