Skip to content

Actor Attacks

The EVS_TakeTurn script runs when it's the actor's turn to attack. A typical attack follows this pattern: approach the target, check if the attack hits, deal damage, then return home.

Testing the target

Before dealing damage, use EnemyTestTarget to check if the attack will hit. This accounts for evasion badges and abilities.

Call(SetTargetActor,attackerID ACTOR_SELF,defenderID ACTOR_PLAYER)
Call(EnemyTestTarget,
    actorID ACTOR_SELF,outResult LVar0,damageType 0,debuffType 0,
    damageAmount 1,flagsModifier BS_FLAGS1_INCLUDE_POWER_UPS)
Switch(LVar0)
    CaseOrEq(HIT_RESULT_MISS)
    CaseOrEq(HIT_RESULT_LUCKY)
        // play miss animation, then return home
        Call(YieldTurn)
        Return
    EndCaseGroup
EndSwitch

The fifth argument (1 in this example) is the damage amount for display purposes.

Dealing damage

If the test passes, use EnemyDamageTarget to actually apply damage:

Call(SetGoalToTarget,actorID ACTOR_SELF)
Call(EnemyDamageTarget,
    actorID ACTOR_SELF,outResult LVar0,damageType 0,suppressEventFlags 0,
    debuffType 0,damageAmount 2,flagsModifier BS_FLAGS1_TRIGGER_EVENTS)

The third argument is the damage type flags — use 0 for a normal attack, or combine flags for elemental and special behavior (see below). The sixth argument (2 here) is the actual damage dealt. The result in LVar0 tells you what happened:

ResultMeaning
HIT_RESULT_HITNormal hit
HIT_RESULT_NO_DAMAGEHit but dealt 0 damage (e.g. high defense)

Simple melee attack

Here's a basic melee attack that runs up to the player, hits, and retreats:

EvtScript EVS_TakeTurn = {
    Call(UseIdleAnimation,actorID ACTOR_SELF,useIdle false)
    Call(EnableIdleScript,actorID ACTOR_SELF,mode IDLE_SCRIPT_DISABLE)
    Call(SetTargetActor,attackerID ACTOR_SELF,defenderID ACTOR_PLAYER)
    Call(UseBattleCamPreset,presetIndex BTL_CAM_ENEMY_APPROACH)
    Call(BattleCamTargetActor,actorID ACTOR_SELF)
    // run towards the player
    Call(SetAnimation,
        actorID ACTOR_SELF,partIndex PRT_MAIN,animID ANIM_Goomba_Run)
    Call(SetGoalToTarget,actorID ACTOR_SELF)
    Call(AddGoalPos,actorID ACTOR_SELF,dx 50,dy 0,dz 0)
    Call(SetActorSpeed,actorID ACTOR_SELF,moveSpeed Float(6.0))
    Call(RunToGoal,actorID ACTOR_SELF,moveTime 0,moveArcAmplitude false)
    // test if the attack hits
    Call(EnemyTestTarget,
        actorID ACTOR_SELF,outResult LVar0,damageType 0,debuffType 0,
        damageAmount 2,flagsModifier BS_FLAGS1_INCLUDE_POWER_UPS)
    Switch(LVar0)
        CaseOrEq(HIT_RESULT_MISS)
        CaseOrEq(HIT_RESULT_LUCKY)
            // missed — return home
            Call(UseBattleCamPreset,presetIndex BTL_CAM_DEFAULT)
            Call(YieldTurn)
            Call(SetAnimation,
                actorID ACTOR_SELF,partIndex PRT_MAIN,animID ANIM_Goomba_Run)
            Call(SetGoalToHome,actorID ACTOR_SELF)
            Call(SetActorSpeed,actorID ACTOR_SELF,moveSpeed Float(8.0))
            Call(RunToGoal,
                actorID ACTOR_SELF,moveTime 0,moveArcAmplitude false)
            Call(EnableIdleScript,actorID ACTOR_SELF,mode IDLE_SCRIPT_ENABLE)
            Call(UseIdleAnimation,actorID ACTOR_SELF,useIdle true)
            Return
        EndCaseGroup
    EndSwitch
    // attack connects
    Call(SetGoalToTarget,actorID ACTOR_SELF)
    Call(EnemyDamageTarget,
        actorID ACTOR_SELF,outResult LVar0,damageType 0,suppressEventFlags 0,
        debuffType 0,damageAmount 2,flagsModifier BS_FLAGS1_TRIGGER_EVENTS)
    // return home
    Call(UseBattleCamPreset,presetIndex BTL_CAM_DEFAULT)
    Wait(5)
    Call(YieldTurn)
    Call(SetAnimation,
        actorID ACTOR_SELF,partIndex PRT_MAIN,animID ANIM_Goomba_Run)
    Call(SetGoalToHome,actorID ACTOR_SELF)
    Call(SetActorSpeed,actorID ACTOR_SELF,moveSpeed Float(8.0))
    Call(RunToGoal,actorID ACTOR_SELF,moveTime 0,moveArcAmplitude false)
    Call(EnableIdleScript,actorID ACTOR_SELF,mode IDLE_SCRIPT_ENABLE)
    Call(UseIdleAnimation,actorID ACTOR_SELF,useIdle true)
    Return
    End
};

For a jump attack with arc rotation, see src/battle/actor/gloomba.c. For a dive attack pattern, see src/battle/actor/paragoomba.c.

Damage type flags

The third argument to EnemyDamageTarget is a bitmask of DAMAGE_TYPE_ flags. Use exactly one elemental flag to set the attack's element, and optionally combine it with modifier flags:

FlagEffect
DAMAGE_TYPE_NO_CONTACTRanged/projectile attack — won't trigger contact hazards like spikes or Zap Tap.
DAMAGE_TYPE_IGNORE_DEFENSEBypasses the target's defense stat.
DAMAGE_TYPE_UNBLOCKABLECannot be blocked by the player.
DAMAGE_TYPE_STATUS_ALWAYS_HITSStatus effects bypass immunity checks.

Pass 0 for a normal contact attack with no special properties.