Skip to content

Functions That Move Cards Across Locations ​

Movement functions are where many scripting mistakes become visible. The target or selected group should be legal before activation and still valid at resolution.

Common Movement Functions ​

FunctionCommon destination or action
Duel.SendtoHandAdd cards to the hand.
Duel.SendtoDeckReturn cards to the Deck or Extra Deck.
Duel.SendtoGraveSend cards to the GY.
Duel.RemoveBanish cards.
Duel.DestroyDestroy cards.
Duel.ReleaseTribute or release cards.
Duel.DiscardHandDiscard cards from the hand.
Duel.SpecialSummonSpecial Summon a monster.
Duel.SSetSet a Spell/Trap.
Duel.MSetSet a monster.
Duel.EquipEquip one card to another.
Duel.MoveToFieldMove a card to a field location.
Duel.ReturnToFieldReturn a temporarily banished card to the field.
Duel.OverlayAttach cards as Xyz Materials.
Duel.SendtoExtraPPut a Pendulum Card face-up in the Extra Deck.
Duel.GetControlChange control of cards.

Some outside examples use Duel.RemoveCards for banishing cards. That function is not present in the local MDPro4/YGOPro sources checked for Sacred Book. Use local Duel.Remove instead.

Movement Router ​

Before choosing a function, identify what the card text is really doing.

Text shapeFirst questionUsual family
"add to the hand", "send to the GY", "banish", "shuffle into the Deck"Which destination is named?Duel.SendtoHand, Duel.SendtoGrave, Duel.Remove, Duel.SendtoDeck
"destroy"Does the text specifically destroy?Duel.Destroy
"Tribute", "release"Is it a cost, effect, or Summon procedure?Duel.Release with the matching reason
"discard"Is it a discard or just send from hand to GY?Duel.DiscardHand or Duel.SendtoGrave with discard reason
"Special Summon"Which player summons it, from where, in what position?Duel.SpecialSummon
"equip it to..."Which card becomes the Equip Card?Duel.Equip plus equip-limit effects
"place in the Spell & Trap Zone" or "place in the Field Zone"Is it being moved onto the field as a card type?Duel.MoveToField plus type-changing effects when needed
"take control"How long does control last?Duel.GetControl with reset data

This keeps "move this card somewhere" from becoming one oversized habit. The destination, reason, and whether the action places a card onto the field decide the function.

Announce The Movement ​

Duel.SetOperationInfo tells the engine and client what the Chain Link is expected to do. Local define comments explicitly call out movement categories such as CATEGORY_SPECIAL_SUMMON, CATEGORY_TOHAND, and CATEGORY_TODECK, and local scripts use the same pattern for the common movement families.

Action being announcedCommon categoryTypical operation info shape
Add to handCATEGORY_TOHANDDuel.SetOperationInfo(0,CATEGORY_TOHAND,nil,1,tp,LOCATION_DECK)
Send to the GYCATEGORY_TOGRAVEDuel.SetOperationInfo(0,CATEGORY_TOGRAVE,nil,1,tp,LOCATION_DECK)
Return to Deck/Extra DeckCATEGORY_TODECK or CATEGORY_TOEXTRADuel.SetOperationInfo(0,CATEGORY_TODECK,g,#g,0,0)
BanishCATEGORY_REMOVEDuel.SetOperationInfo(0,CATEGORY_REMOVE,g,#g,0,0)
Tribute/release by effectCATEGORY_RELEASEDuel.SetOperationInfo(0,CATEGORY_RELEASE,g,#g,0,0)
Special SummonCATEGORY_SPECIAL_SUMMONDuel.SetOperationInfo(0,CATEGORY_SPECIAL_SUMMON,nil,1,tp,LOCATION_GRAVE)
Change controlCATEGORY_CONTROLDuel.SetOperationInfo(0,CATEGORY_CONTROL,g,1,0,0)
Equip this cardCATEGORY_EQUIPDuel.SetOperationInfo(0,CATEGORY_EQUIP,e:GetHandler(),1,0,0)

Be careful with CATEGORY_EQUIP: the local constant.lua comment describes it as the category for equipping the effect's own card. When an effect equips other cards from another location, local scripts often announce the source movement, such as CATEGORY_LEAVE_GRAVE, and perform the actual equip in the operation after all zone and legality checks pass.

Two Movement Families ​

Most movement mistakes come from treating every destination as the same kind of action. Split them into two families before writing the filter.

FamilyExamplesWhat the activation branch usually proves
Destination or removal actionDuel.SendtoHand, Duel.SendtoGrave, Duel.SendtoDeck, Duel.Remove, Duel.Release, Duel.DiscardHand, Duel.DestroyThe card can legally leave its current place for that action, usually with a matching Card.IsAbleTo*, Card.IsReleasable*, or Card.IsDiscardable check.
Field placement or field control actionDuel.SpecialSummon, Duel.SSet, Duel.MSet, Duel.Equip, Duel.MoveToField, Duel.ReturnToField, Duel.GetControlThere is a usable zone and the card can legally exist there, using checks such as Duel.GetLocationCount, Duel.GetLocationCountFromEx, Card.IsCanBeSpecialSummoned, Card.IsSSetable, Card.IsMSetable, not c:IsForbidden(), Card.CheckUniqueOnField, or Card.IsControlerCanBeChanged.

The second family changes the field layout, so it is more sensitive to Chain changes. A free Monster Zone, Spell & Trap Zone, Field Zone replacement, or equip target can disappear before resolution. Local scripts commonly check the zone in chk==0, then check again in the operation before moving the card.

Check The Card Can Move ​

Movement filters should use the matching "able to" helper when one exists.

ActionCommon filter check
Add to handc:IsAbleToHand()
Send to GYc:IsAbleToGrave() or c:IsAbleToGraveAsCost()
Return to Deckc:IsAbleToDeck() or c:IsAbleToExtra() depending on the effect
Return to Deck/Extra Deck as costc:IsAbleToDeckAsCost(), c:IsAbleToExtraAsCost(), or c:IsAbleToDeckOrExtraAsCost()
Banishc:IsAbleToRemove() or c:IsAbleToRemoveAsCost()
Tribute/releasec:IsReleasableByEffect() for effects, c:IsReleasable() for costs/procedures
Discardc:IsDiscardable(REASON_EFFECT) for effects, c:IsDiscardable(REASON_COST) or c:IsDiscardable() for costs
Set Spell/Trapc:IsSSetable()
Set monsterc:IsMSetable(...)
Special Summonc:IsCanBeSpecialSummoned(...)
Equiptarget is face-up and the Equip Card can legally equip to it
Attach as Xyz Materialc:IsCanOverlay(player) when that local check applies
Change controlc:IsControlerCanBeChanged(...)

Cost filters should use cost-specific helpers where available. A card that can be sent to the GY by effect is not automatically legal to send as cost.

Destroying is a special case: many effects can be legally activated even if the chosen card may not actually be destroyed by the resolving effect. Use Card.IsDestructable only when the card text or local pattern specifically needs that pre-check.

Targeted Card Pattern ​

lua
function s.operation(e,tp,eg,ep,ev,re,r,rp)
    local tc=Duel.GetFirstTarget()
    if tc and tc:IsRelateToEffect(e) then
        Duel.SendtoHand(tc,nil,REASON_EFFECT)
    end
end

If the operation has one official target, Duel.GetFirstTarget() is usually the clearest read. Always check relation before moving the target.

Multi-Target Pattern ​

lua
function s.operation(e,tp,eg,ep,ev,re,r,rp)
    local g=Duel.GetTargetsRelateToChain()
    if #g>0 then
        Duel.Destroy(g,REASON_EFFECT)
    end
end

For multi-target effects, prefer a relation-aware group helper or filter the target group yourself.

lua
local tg=Duel.GetChainInfo(0,CHAININFO_TARGET_CARDS)
local rg=tg:Filter(Card.IsRelateToEffect,nil,e)

Non-Targeting Selection Pattern ​

Non-targeting effects usually select during operation, not during the target callback.

lua
function s.operation(e,tp,eg,ep,ev,re,r,rp)
    Duel.Hint(HINT_SELECTMSG,tp,HINTMSG_REMOVE)
    local g=Duel.SelectMatchingCard(tp,s.filter,tp,LOCATION_GRAVE,0,1,1,nil)
    if #g>0 then
        Duel.Remove(g,POS_FACEUP,REASON_EFFECT)
    end
end

Use this when card text says "choose" or performs a selection without targeting.

Location-To-Location Actions ​

Many movement functions can move cards from any legal starting location, but the script still needs to choose the right timing and legality checks.

Source situationTypical call
A card in Deck/GY/banished should go to hand.Duel.SendtoHand(g,nil,REASON_EFFECT)
A card should go to the GY without being destroyed.Duel.SendtoGrave(g,REASON_EFFECT)
A card should be banished face-up.Duel.Remove(g,POS_FACEUP,REASON_EFFECT)
A card should be Tributed/released.Duel.Release(g,REASON_EFFECT) or Duel.Release(g,REASON_COST)
A card should be discarded.Duel.SendtoGrave(g,REASON_DISCARD+REASON_EFFECT) or Duel.DiscardHand(...)
A monster should be Special Summoned.Duel.SpecialSummon(tc,0,tp,tp,false,false,POS_FACEUP)
A Spell/Trap should be Set.Duel.SSet(tp,tc)
A card should become an Equip Card.Duel.Equip(tp,eqc,tc)
A card should become a Continuous Spell/Trap.Duel.MoveToField(...) plus EFFECT_CHANGE_TYPE when needed
A temporarily banished card should return.Duel.ReturnToField(tc) after an earlier temporary Duel.Remove flow
A Pendulum Card should go face-up to the Extra Deck.Duel.SendtoExtraP(tc,tp,REASON_EFFECT)
A card should change controller.Duel.GetControl(tc,tp,reset_phase,reset_count)

If an action places a card onto the field, check available zones before activation and again during resolution if the Chain can change them.

Local scripts show the same routing in different card types:

  • c1003840.lua releases monsters, Special Summons the handler, then returns a card to the Deck with Duel.SendtoDeck.
  • c10204849.lua uses Duel.Equip for an effect that turns a card into an Equip Card.
  • c1050355.lua uses Duel.MoveToField when a card is placed into the Field Zone.
  • c99742859.lua uses Duel.SpecialSummonStep and Duel.SpecialSummonComplete when two monsters are Summoned in one grouped process.

Special Summon Movement ​

Special Summon checks usually mirror the later summon call.

lua
function s.spfilter(c,e,tp)
    return c:IsCanBeSpecialSummoned(e,0,tp,false,false,POS_FACEUP,tp)
end

function s.sptg(e,tp,eg,ep,ev,re,r,rp,chk)
    if chk==0 then
        return Duel.GetLocationCount(tp,LOCATION_MZONE)>0
            and Duel.IsExistingMatchingCard(s.spfilter,tp,LOCATION_GRAVE,0,1,nil,e,tp)
    end
    Duel.SetOperationInfo(0,CATEGORY_SPECIAL_SUMMON,nil,1,tp,LOCATION_GRAVE)
end

Check zones and summon legality before activation. During resolution, check the zone again if the effect depends on it.

Equip and MoveToField ​

Equip effects often move a card to the Spell & Trap Zone, then register equip effects and equip limits.

lua
local tc=Duel.GetFirstTarget()
if c:IsRelateToEffect(e) and tc and tc:IsRelateToEffect(e) then
    Duel.Equip(tp,c,tc)
end

Duel.MoveToField is used when a card becomes a Continuous Spell/Trap, is put in a Field Zone, or otherwise moves to a specific field location.

For the full equip flow, see Equip Spells and Equip Cards.

Temporary Banish and ReturnToField ​

Duel.ReturnToField is for cards that were temporarily banished, not a generic "put this card back" helper. The earlier banish should include REASON_TEMPORARY, then a later registered effect returns the same stored card.

Local c10960419.lua uses that shape:

lua
if Duel.Remove(tc,0,REASON_EFFECT+REASON_TEMPORARY)~=0 then
    -- register the delayed return watcher and store tc on it
end

The delayed operation then returns the stored card:

lua
function s.retop(e,tp,eg,ep,ev,re,r,rp)
    local tc=e:GetLabelObject()
    Duel.ReturnToField(tc)
end

Do not replace a normal Special Summon, Set, or MoveToField action with ReturnToField. Use it when the script has a real temporary-banish lifecycle to close.

Local Scripts To Read ​

These local scripts are compact examples of different movement families:

ScriptMovement pattern
c1003840.luaReleases cards as cost, Special Summons from the Deck, then returns a card to the Deck.
c1050355.luaReplaces a Field Spell, sends the old card to the GY by rule, then moves the new card into the Field Zone.
c10960419.luaTemporarily banishes a card with REASON_TEMPORARY and later returns it with Duel.ReturnToField.
c99742859.luaUses Duel.SpecialSummonStep/Duel.SpecialSummonComplete for grouped Summons, then destroys a target group.
c10204849.luaCombines Special Summon and equip movement in one resolving effect.

Change Control ​

Changing control uses Duel.GetControl in this local API, despite the name reading like a getter.

lua
function s.ctltg(e,tp,eg,ep,ev,re,r,rp,chk,chkc)
    if chkc then return chkc:IsControler(1-tp) and chkc:IsLocation(LOCATION_MZONE)
        and chkc:IsControlerCanBeChanged() end
    if chk==0 then
        return Duel.IsExistingTarget(Card.IsControlerCanBeChanged,tp,0,LOCATION_MZONE,1,nil)
    end
    Duel.Hint(HINT_SELECTMSG,tp,HINTMSG_CONTROL)
    local g=Duel.SelectTarget(tp,Card.IsControlerCanBeChanged,tp,0,LOCATION_MZONE,1,1,nil)
    Duel.SetOperationInfo(0,CATEGORY_CONTROL,g,1,0,0)
end

Do not confuse IsControlerCanBeChanged with IsAbleToChangeControler. The former checks whether a control-change action can be applied; the latter is used by some movement-like actions where controler/possession changes as part of another operation.

Return Values ​

Many movement functions return how many cards moved or whether the action succeeded. Local scripts often use that return value before applying the next part of an effect.

lua
if Duel.SpecialSummon(c,0,tp,tp,false,false,POS_FACEUP)>0 then
    Duel.GetControl(tc,tp)
end

Do not assume the intended card moved. Relation checks, immunity, replacement effects, and zone changes can all affect the final result.

For a complete example that inspects Duel.GetOperatedGroup, checks the actual destination, and offers a conditional draw, continue to operation results and follow-up effects.

Reason Matters ​

The reason flag tells the engine why the move happened: REASON_EFFECT, REASON_COST, REASON_RELEASE, REASON_MATERIAL, and similar constants affect triggers and replacement effects.

lua
Duel.SendtoGrave(g,REASON_EFFECT)
Duel.Release(g,REASON_COST)
Duel.SendtoDeck(mat,nil,SEQ_DECKSHUFFLE,REASON_EFFECT+REASON_MATERIAL+REASON_FUSION)

Use the reason that matches the card text and mechanic.

Useful API pages: