Skip to content

4 ‐ Understanding a card script

Naim edited this page Jan 7, 2025 · 1 revision

Let's start looking at Galaxy Charity, that has this effect:

If you control a "Galaxy" Xyz Monster: Discard 1 card; draw 2 cards, also if you activated this card, any damage your opponent takes for the rest of this turn is halved. You can only activate 1 "Galactic Charity" per turn.

Its script has the following code:

--銀河の施し
--Galactic Charity
local s,id=GetID()
function s.initial_effect(c)
	--Activate
	local e1=Effect.CreateEffect(c)
	e1:SetCategory(CATEGORY_DRAW)
	e1:SetType(EFFECT_TYPE_ACTIVATE)
	e1:SetCode(EVENT_FREE_CHAIN)
	e1:SetCountLimit(1,id,EFFECT_COUNT_CODE_OATH)
	e1:SetCondition(s.condition)
	e1:SetCost(s.cost)
	e1:SetTarget(s.target)
	e1:SetOperation(s.activate)
	c:RegisterEffect(e1)
end
s.listed_series={0x7b}
function s.cfilter(c)
	return c:IsFaceup() and c:IsSetCard(0x7b) and c:IsType(TYPE_XYZ)
end
function s.condition(e,tp,eg,ep,ev,re,r,rp)
	return Duel.IsExistingMatchingCard(s.cfilter,tp,LOCATION_MZONE,0,1,nil)
end
function s.cost(e,tp,eg,ep,ev,re,r,rp,chk)
	if chk==0 then return Duel.IsExistingMatchingCard(Card.IsDiscardable,tp,LOCATION_HAND,0,1,e:GetHandler()) end
	Duel.DiscardHand(tp,Card.IsDiscardable,1,1,REASON_COST+REASON_DISCARD,nil)
end
function s.target(e,tp,eg,ep,ev,re,r,rp,chk)
	if chk==0 then return Duel.IsPlayerCanDraw(tp,2) end
	Duel.SetTargetPlayer(tp)
	Duel.SetTargetParam(2)
	Duel.SetOperationInfo(0,CATEGORY_DRAW,nil,0,tp,2)
end
function s.activate(e,tp,eg,ep,ev,re,r,rp)
	local p,d=Duel.GetChainInfo(0,CHAININFO_TARGET_PLAYER,CHAININFO_TARGET_PARAM)
	Duel.Draw(p,d,REASON_EFFECT)
	if e:IsHasType(EFFECT_TYPE_ACTIVATE) then
		local e1=Effect.CreateEffect(e:GetHandler())
		e1:SetType(EFFECT_TYPE_FIELD)
		e1:SetCode(EFFECT_CHANGE_DAMAGE)
		e1:SetProperty(EFFECT_FLAG_PLAYER_TARGET+EFFECT_FLAG_CLIENT_HINT)
		e1:SetDescription(aux.Stringid(id,1))
		e1:SetTargetRange(0,1)
		e1:SetValue(s.val)
		e1:SetReset(RESET_PHASE+PHASE_END,1)
		Duel.RegisterEffect(e1,tp)
	end
end
function s.val(e,re,val,r,rp,rc)
	return math.floor(val/2)
end

To verify if the card can be activated, the game first checks the condition, the function called in e1:SetCondition(s.condition). s.condition is said to pass if the following line returns true:

return Duel.IsExistingMatchingCard(s.cfilter,tp,LOCATION_MZONE,0,1,nil)

This line checks if there is at least 1 card in player tp's monster zone that matches the function s.cfilter.

Next, the game checks for the cost. Cost must be paid when a card is activated and if the cost cannot be paid, the activation is not possible. For that, the function called in e1:SetCost(s.cost) is executed. First the chk variable is passed as 0 when the game tests the cost and the line

if chk==0 then return Duel.IsExistingMatchingCard(Card.IsDiscardable,tp,LOCATION_HAND,0,1,e:GetHandler()) end

is executed. In this test, if there is at least 1 card in player tp's hand that can be discarded (except Galactic Charity itself, that was obtained with e:GetHandler()) the test is true and the game knows that the cost can be paid.

The following step is to verify the activation legality ("can the player resolve this effect?"). For this, the function defined in e1:SetTarget(s.target) is executed. For the activation legality the chk parameter here is passed as 0 again and the line

if chk==0 then return Duel.IsPlayerCanDraw(tp,2) end

is executed. Here, if player tp can draw 2 cards the test returns true and the game decides that the activation is legal.

After those steps, since condition, cost and activation legality are all true, the player can activate the card. When they do, the cost function is executed once again. Now the chk parameter is no longer 0 (it is passed as 1), so the first line in that function is not executed (because if chk==0 is false) and instead the game runs the function that will make the player pay the cost (via Duel.DiscardHand).

After that, the s.target function is executed. The same situation with chk happens here: it is passed as 1 so the first line is no longer executed and the game instead only calls the functions Duel.SetTargetPlayer, Duel.SetTargetParam and Duel.SetOperationInfo.

Finally, when the effect resolves, the s.activate function is called, executing the actual effect of the card.

Other insights

After the comments, in the script shown above you have:

The definition of 2 local variables, s and id, that receive the return values from the GetID function:

local s,id=GetID()

s will be a card object, and id will be an int containing that card's passcode.

The declaration of the initial effect, with only 1 effect in this card e1:

function s.initial_effect(c)
	--Activate
	local e1=Effect.CreateEffect(c)
	...
	c:RegisterEffect(e1)
end

The first (and only) effect this card has is defined in e1. Here we have:

The category of the effect (defined via Effect.SetCategory):

e1:SetCategory(CATEGORY_DRAW)

This tells the game what the effect includes and it is also used because there are cards that need to detect such categories like Ash Blossom & Joyous Spring (detecting CATEGORY_DRAW and CATEGORY_SEARCH).

The code of the effect (defined via Effect.SetCode):

e1:SetCode(EVENT_FREE_CHAIN)

This tells the game that the card can be activated in a open game state, when no chain is resolving (because Galactic Charity is a normal Spell). Other codes might include any event that the card needs to detect (for example EVENT_SUMMON_SUCCESS)

The amount of times the effect can be used (defined via Effect.SetCountLimit):

e1:SetCountLimit(1,id,EFFECT_COUNT_CODE_OATH)

This tells the game that e1 can be activated once per turn, for the card that has id as passcode. If the EFFECT_COUNT_CODE_OATH flag was not there, then the player would be able to use the effect once per turn (in Yu-Gi-Oh, "use" counts any attempts to apply that effect, even the ones that were negated, while activate will only count the ones that didn't have their activation negated).

Examples:

  • e1:SetCountLimit(1): used for effects that say "Once per turn" (per copy of the card);
  • e1:SetCountLimit(1,id): used for effects that say "You can only use this effect of CARD_NAME once per turn";
  • e1:SetCountLimit(1,id,EFFECT_COUNT_CODE_DUEL): used for effects that say "You can only use this effect of CARD_NAME once per duel".

The condition to be able to activate the card or effect (defined via Effect.SetCondition):

e1:SetCondition(s.condition)

The cost function, that checks if the cost can be paid and also executes such cost (defined via Effect.SetCost):

e1:SetCost(s.cost)

The target function, that performs the activation legality check and also does the actions that must be done during the activation (defined via Effect.SetTarget):

e1:SetTarget(s.target)

The operation function, that executes all the steps that are done when the card or effect resolves (defined via Effect.SetOperation):

e1:SetOperation(s.activate)