Activation Legality
Activation legality is the part of a script that answers: "May this effect be activated right now?"
Most of that work happens in condition, cost, and target. The important habit is to separate legality checks from resolution. A legal activation can still resolve without doing anything if its target disappears or a required zone closes.
For the prompt and response-window side of the same topic, see Chain Timing And Activation Windows.
The Legality Flow
For an activated effect, the engine checks roughly this shape:
- Is the effect type/timing available?
- Is the handler in the right place?
- Does
conditionreturn true? - Can the
costbe paid withchk==0? - Does
targetreturn true withchk==0? - If activated, pay cost and select targets.
- Later, resolve the operation.
This is why target and cost callbacks must be careful: the check branch is not the same as the real activation branch.
chk==0
In target and cost functions, chk==0 means the engine is asking if activation is legal. Do not pay costs or move cards in this branch.
function s.target(e,tp,eg,ep,ev,re,r,rp,chk)
if chk==0 then
return Duel.IsExistingMatchingCard(s.filter,tp,LOCATION_MZONE,0,1,nil)
end
Duel.SetOperationInfo(0,CATEGORY_DESTROY,nil,1,tp,LOCATION_MZONE)
endThe non-check branch is where you select targets, announce operation info, or store labels that the operation will read.
Common Legality Questions
| Question | Typical check |
|---|---|
| Is the handler in the right place? | SetRange, c:IsLocation(...) |
| Is the phase right? | Duel.IsMainPhase(), Duel.IsBattlePhase(), Duel.IsPhase(...) |
| Is a card available? | Duel.IsExistingMatchingCard(...) |
| Can the card be targeted? | Duel.IsExistingTarget(...) and the chkc branch |
| Can the player perform the action? | Duel.IsPlayerCanDraw(...), Duel.GetLocationCount(...) |
| Can the cost be paid? | Duel.CheckLPCost(...), Duel.CheckReleaseGroup(...) |
| Can the monster be Special Summoned? | c:IsCanBeSpecialSummoned(...), Duel.GetLocationCount(...) |
| Is the effect once per turn? | SetCountLimit(...) |
Explicit vs Implicit Requirements
Card text usually gives explicit requirements before the colon. Those are good condition/cost/target checks. Some legality requirements are implicit in the action instead:
| Intended action | Implicit legality check |
|---|---|
| Draw cards. | Duel.IsPlayerCanDraw(...) |
| Special Summon. | Duel.GetLocationCount(...) and Card.IsCanBeSpecialSummoned(...) |
| Add to hand. | a filter with Card.IsAbleToHand or the relevant able-to helper |
| Banish. | a filter with Card.IsAbleToRemove or Card.IsAbleToRemoveAsCost |
| Equip. | an available target plus legal equip state/checks |
| Inflict damage or recover LP. | target player/amount can be announced, and any card-dependent amount has a legal source |
Do not put every implicit action check into condition. Conditions should answer timing and event questions. Action availability usually belongs in target or cost, because those callbacks already have the chk==0 check branch.
What Is Not A Condition
Some phrases in card text are handled by effect setup instead of a Condition callback.
| Text or rule shape | Usual script home |
|---|---|
| "Once per turn", "once per Duel", shared once-per-turn wording | Effect.SetCountLimit |
| "(Quick Effect)" | Effect.SetType with EFFECT_TYPE_QUICK_O or EFFECT_TYPE_QUICK_F |
| "during the Damage Step" or "even during damage calculation" | Effect.SetProperty with Damage Step flags |
| costs and activation restrictions | the cost callback, using chk==0 for checks and the real branch for applying restrictions |
The Condition callback still handles fine timing details. For example, an ATK/DEF modifier that is legal in the Damage Step but not after damage calculation can pair EFFECT_FLAG_DAMAGE_STEP with a phase check:
e1:SetProperty(EFFECT_FLAG_DAMAGE_STEP)
e1:SetCondition(aux.dscon)In the local utility.lua, aux.dscon returns true outside the Damage Phase and during the Damage Phase before Duel.IsDamageCalculated becomes true. Effects that are also legal during damage calculation use EFFECT_FLAG_DAMAGE_CAL as well, and should still keep any card-specific timing checks in their condition.
Damage Step Legality
Damage Step scripting is a common source of accidental legality bugs. The setup flags open the timing window; the condition decides whether this specific effect should be allowed at the current point inside that window.
Cyber Dragon Infinity (c10443957.lua) shows the shape for a negation Quick Effect:
e3:SetType(EFFECT_TYPE_QUICK_O)
e3:SetCode(EVENT_CHAINING)
e3:SetProperty(EFFECT_FLAG_DAMAGE_STEP+EFFECT_FLAG_DAMAGE_CAL)
e3:SetCondition(c10443957.discon)
function c10443957.discon(e,tp,eg,ep,ev,re,r,rp)
return not e:GetHandler():IsStatus(STATUS_BATTLE_DESTROYED)
and Duel.IsChainNegatable(ev)
endThe flags say the effect may be offered in the Damage Step and during damage calculation. The condition still rejects a handler already marked with STATUS_BATTLE_DESTROYED and checks whether the current Chain Link can be negated with Duel.IsChainNegatable. That pattern is local-script-backed; the exact real-card timing still depends on the effect's official text and rulings.
Costs Can Apply Restrictions
Some effects have an activation restriction that must be checked before activation and then applied immediately once the effect is activated. Red-Eyes Fusion (c6172122.lua) is a local example:
function c6172122.cost(e,tp,eg,ep,ev,re,r,rp,chk)
if chk==0 then
return Duel.GetActivityCount(tp,ACTIVITY_SUMMON)==0
and Duel.GetActivityCount(tp,ACTIVITY_SPSUMMON)==0
end
local e1=Effect.CreateEffect(e:GetHandler())
e1:SetType(EFFECT_TYPE_FIELD)
e1:SetCode(EFFECT_CANNOT_SUMMON)
e1:SetProperty(EFFECT_FLAG_PLAYER_TARGET+EFFECT_FLAG_OATH)
e1:SetTargetRange(1,0)
e1:SetReset(RESET_PHASE+PHASE_END)
Duel.RegisterEffect(e1,tp)
endThe chk==0 branch only asks whether the player already Summoned this turn. The real branch registers the oath-style restriction. Do not register that restriction during chk==0; the engine may call the check branch without the player actually activating the effect.
Optional Effects Should Have A Legal Outcome
Optional activated effects should normally be unavailable if they cannot make a legal attempt. For example, a search effect should check that a matching card exists and can be added to the hand.
function s.thfilter(c)
return c:IsSetCard(0x123) and c:IsAbleToHand()
end
function s.thtg(e,tp,eg,ep,ev,re,r,rp,chk)
if chk==0 then
return Duel.IsExistingMatchingCard(s.thfilter,tp,LOCATION_DECK,0,1,nil)
end
Duel.SetOperationInfo(0,CATEGORY_TOHAND,nil,1,tp,LOCATION_DECK)
endThe operation may still fail if the Deck changes before resolution. Legality only proves that the activation was valid when chosen.
When an effect will free a zone as part of its cost or procedure, count that freed zone. Local scripts commonly use helpers such as Duel.GetMZoneCount when the cards to leave the Monster Zone are already known. A plain Duel.GetLocationCount check can be too strict for effects that Tribute, release, or move their own monster before placing another card.
Mandatory Parts And Optional Follow-Ups
For optional activated effects, the chk==0 branch should prove that the effect has a legal mandatory outcome. It should not demand every optional follow-up choice that the player may decline later.
Use the card text as a script outline:
| Text shape | Legality habit |
|---|---|
| "Do A, then do B." | Check that A and B can be legally attempted if both are mandatory. |
| "Do A, and if you do, you can do B." | Check A for activation; check B later only if the player chooses to try it. |
| "Choose 1 of these effects" | Check that at least one mode is legal, then offer only legal modes. |
| "Do A, then apply B if possible" | Check A; let B resolve as much as the current state allows. |
Local modal effects show this pattern clearly. c10000030.lua computes two possible modes, returns b1 or b2 in the chk==0 branch, then only offers modes that are actually legal. The target branch stores the chosen mode with e:SetLabel(op), and the operation reads that label while still rechecking the target and zone state.
Mandatory Effects Still Resolve Carefully
Mandatory trigger effects normally activate when their event happens, even if some later action may no longer be possible. Their check branch is often broad:
function s.mandtarg(e,tp,eg,ep,ev,re,r,rp,chk)
if chk==0 then return true end
Duel.SetOperationInfo(0,CATEGORY_DAMAGE,nil,0,1-tp,500)
endThe operation still checks the current state before applying an action. A mandatory trigger is not permission to skip resolution checks.
Costs are different. Even mandatory effects cannot activate if their cost cannot be paid. The broad if chk==0 then return true end habit belongs to the target branch of many mandatory effects, not to a cost branch that actually has LP, material, discard, release, or activity requirements.
Target Rechecks With chkc
Targeting effects often include a chkc branch. It tells the client whether a manually chosen card is a legal target for this effect.
function s.target(e,tp,eg,ep,ev,re,r,rp,chk,chkc)
if chkc then
return chkc:IsControler(tp) and chkc:IsLocation(LOCATION_MZONE)
and s.filter(chkc)
end
if chk==0 then
return Duel.IsExistingTarget(s.filter,tp,LOCATION_MZONE,0,1,nil)
end
Duel.SelectTarget(tp,s.filter,tp,LOCATION_MZONE,0,1,1,nil)
endThe chkc branch should match the same target rules used by Duel.SelectTarget.
Resolution Is Separate
Legal activation does not guarantee successful resolution. Cards may move, be flipped face-down, become unaffected, or stop being related to the effect.
local tc=Duel.GetFirstTarget()
if tc and tc:IsRelateToEffect(e) then
Duel.Destroy(tc,REASON_EFFECT)
endUse activation checks to make the effect legal. Use resolution checks to keep it correct after the game state changes.
Common Mistakes
| Mistake | Safer pattern |
|---|---|
Paying cost during chk==0. | Only return whether the cost can be paid, then pay later. |
Using SelectMatchingCard for an effect that targets. | Use SelectTarget and relation checks. |
| Forgetting zone checks for Special Summons or setting cards. | Check Duel.GetLocationCount or the relevant summon legality function. |
| Assuming a target is still valid at resolution. | Check tc and tc:IsRelateToEffect(e). |
| Announcing a category that the operation does not perform. | Match SetCategory and SetOperationInfo to the real action. |
Relevant API pages: