Cost, Target, And Operation Lifecycle
Activated effects usually move through the same script lifecycle: check whether the effect is legal, pay costs and record activation data, then resolve later against the current game state.
The important habit is to keep each callback honest. A check branch answers "can this happen?" A real activation branch spends costs, selects targets, and stores data. The operation performs the effect after the Chain reaches that Link.
Reading Card Text Into Callbacks
Modern card text gives useful punctuation clues:
| Text part | Script home |
|---|---|
Before a colon : | activation condition, timing condition, or other explicit requirement |
Before a semicolon ; | activation-time procedure, usually cost payment or target selection |
| After the semicolon, or after the colon when there is no semicolon | resolution, usually the operation callback |
This punctuation is a guide, not a substitute for legality checks. The script still has to check implicit requirements such as available Monster Zones, whether a card can be added to the hand, whether a target can be affected, or whether a mandatory part of an optional effect can be attempted.
Callback Timeline
For a typical activated effect, scripts are written around this sequence:
conditionanswers timing and event requirements.costwithchk==0answers whether the cost can be paid.targetwithchk==0answers whether the activation has a legal outcome.- The real
costbranch pays the cost. - The real
targetbranch selects targets, records target player/param data, labels, and operation info. operationresolves later and re-checks anything that can change.
Do not move cards, pay LP, remove Xyz Materials, or choose hidden information in the chk==0 branch. The engine may call that branch while only testing whether the effect is available.
Callback Signature Cheat Sheet
The callback name does not fully describe the signature. The effect's type and code decide what the engine passes. These are the common learner-facing shapes seen in local scripts:
| Callback role | Common shape | Local example |
|---|---|---|
| Activated or event condition | function s.con(e,tp,eg,ep,ev,re,r,rp) | Cyber Dragon Infinity's Chain negation condition. |
| Activated cost | function s.cost(e,tp,eg,ep,ev,re,r,rp,chk) | Red-Eyes Fusion's activation restriction cost. |
| Activated target without manual retargeting | function s.tg(e,tp,eg,ep,ev,re,r,rp,chk) | Pot of Greed's draw target function. |
| Activated card-target function | function s.tg(e,tp,eg,ep,ev,re,r,rp,chk,chkc) | Cyber Dragon Infinity's attach-material target function. |
| Activated operation | function s.op(e,tp,eg,ep,ev,re,r,rp) | Most resolving Chain effects. |
| Continuous value callback | function s.val(e,c) | c10002346.lua recalculates ATK/DEF from overlay count. |
| Special Summon procedure callbacks | Procedure-specific shapes such as spcon(e,c), sptg(...,chk,c), and spop(...,c) | Lava Golem's hand procedure. |
Treat the table as a starting map, not a universal signature list. A continuous replacement target, an equip limit, a material procedure, and an activated target function can all be installed through SetTarget, but they do not ask the same question. When a local script with the same mechanic uses an extra argument, copy the mechanic's shape and then simplify only after you understand what the argument represents.
Activation Restrictions Belong With The Activation
If an effect says the player cannot do something for the rest of the turn after activation, the check and the restriction often live in the cost callback. The local Red-Eyes Fusion script (c6172122.lua) checks whether the player has already Summoned, then registers the restriction only when the effect is really activated:
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 important split is the same as an LP cost: check first, apply later. The restriction is a real effect registered to the player, so creating it during chk==0 would leak a restriction from a mere legality probe.
Cost Can Store Resolution Data
A cost can store an integer label when the amount paid matters later. The Winged Dragon of Ra (10000010) checks whether the player can pay LP, then the real cost branch stores the paid amount and pays it:
function c10000010.atkcost(e,tp,eg,ep,ev,re,r,rp,chk)
local lp=Duel.GetLP(tp)
if chk==0 then return Duel.CheckLPCost(tp,lp-100,true) end
e:SetLabel(lp-100)
Duel.PayLPCost(tp,lp-100,true)
end
function c10000010.atkop(e,tp,eg,ep,ev,re,r,rp)
local c=e:GetHandler()
if c:IsFaceup() and c:IsRelateToEffect(e) then
local e1=Effect.CreateEffect(c)
e1:SetType(EFFECT_TYPE_SINGLE)
e1:SetProperty(EFFECT_FLAG_SINGLE_RANGE)
e1:SetRange(LOCATION_MZONE)
e1:SetCode(EFFECT_UPDATE_ATTACK)
e1:SetValue(e:GetLabel())
e1:SetReset(RESET_EVENT+RESETS_STANDARD+RESET_DISABLE)
c:RegisterEffect(e1)
end
endDuel.CheckLPCost is the read-side helper. Duel.PayLPCost belongs in the real cost branch because it actually schedules the payment.
Target Branches Can Store Choices
The target callback is often where a player chooses a mode. c10000030.lua checks two possible outcomes, asks the player which one to use, stores the choice in e:SetLabel(op), then the operation reads e:GetLabel():
function c10000030.target1(e,tp,eg,ep,ev,re,r,rp,chk,chkc)
local b1=Duel.IsExistingTarget(c10000030.filter1,tp,0,LOCATION_MZONE,1,nil)
local b2=Duel.GetLocationCount(tp,LOCATION_MZONE)>0
and Duel.IsExistingTarget(c10000030.filter2,tp,0,LOCATION_GRAVE,1,nil,e,tp)
if chk==0 then return b1 or b2 end
local op=0
if b1 and b2 then
op=Duel.SelectOption(tp,aux.Stringid(10000030,1),aux.Stringid(10000030,2))
elseif b1 then
op=Duel.SelectOption(tp,aux.Stringid(10000030,1))
else
op=Duel.SelectOption(tp,aux.Stringid(10000030,2))+1
end
e:SetLabel(op)
if op==0 then
e:SetCategory(CATEGORY_CONTROL)
Duel.SelectTarget(tp,c10000030.filter1,tp,0,LOCATION_MZONE,1,1,nil)
else
e:SetCategory(CATEGORY_SPECIAL_SUMMON)
Duel.SelectTarget(tp,c10000030.filter2,tp,0,LOCATION_GRAVE,1,1,nil,e,tp)
end
end
function c10000030.operation1(e,tp,eg,ep,ev,re,r,rp)
local tc=Duel.GetFirstTarget()
if e:GetLabel()==0 then
if tc:IsRelateToEffect(e) then
Duel.GetControl(tc,tp,PHASE_END,1)
end
else
if tc:IsRelateToEffect(e) then
Duel.SpecialSummon(tc,0,tp,tp,false,false,POS_FACEUP)
end
end
endThe operation still checks tc:IsRelateToEffect(e). A stored label remembers which branch was chosen; it does not prove the target is still usable.
Target Player And Param
For player-targeted effects, the target callback records the player and amount, then the operation reads them back from the current Chain. c10040267.lua uses this pattern for effect damage:
function c10040267.damtg(e,tp,eg,ep,ev,re,r,rp,chk)
if chk==0 then return true end
Duel.SetTargetPlayer(1-tp)
Duel.SetTargetParam(600)
Duel.SetOperationInfo(0,CATEGORY_DAMAGE,nil,0,1-tp,600)
end
function c10040267.damop(e,tp,eg,ep,ev,re,r,rp)
local p,d=Duel.GetChainInfo(0,CHAININFO_TARGET_PLAYER,CHAININFO_TARGET_PARAM)
Duel.Damage(p,d,REASON_EFFECT)
endUse SetTargetPlayer and SetTargetParam for small Chain-scoped values such as a player, damage amount, recovery amount, or draw count. Use an effect label when the data belongs to the effect object rather than to the Chain target fields.
Resolution Rechecks
Operation callbacks run after both players had a chance to respond. The field may no longer match the activation check.
| Activation data | Resolution habit |
|---|---|
| A targeted card | Read with Duel.GetFirstTarget() or target Group helpers, then check relation. |
| A selected mode | Read e:GetLabel() and still re-check cards/zones before acting. |
| Target player/param | Read Duel.GetChainInfo(0,CHAININFO_TARGET_PLAYER,CHAININFO_TARGET_PARAM). |
| A Special Summon outcome | Check zone count and summon legality again if the script can fail gracefully. |
| A card in the handler's location | Check c:IsRelateToEffect(e) or the needed location/face-up state. |
Common Mistakes
Do not pay cost in chk==0. For LP, use Duel.CheckLPCost first and Duel.PayLPCost only when chk is not 0.
Do not select targets in the check branch. Use Duel.IsExistingTarget for the check and Duel.SelectTarget for the real activation branch.
Do not assume operation data is still valid. Labels, target player fields, and operation info remember what was chosen; resolution still needs current-state checks before moving cards or applying effects.
Relevant API pages: