CardFilter
subtype of function
A function used to filter Card
s.
Functions that use this type as parameter
Functions that use this type as parameter
Group.Filter
- Filters aGroup
.Link.AddProcedure
- Adds a Link Procedure to a card.
Signature
function(Card c
, any ...
) → bool
function(c,...)
Parameters
Card
c
The
Card
to check.any
...
Additional arguments, usually passed by the function using the
CardFilter
.
Returns
A CardFilter
is a function that takes a Card
as its first parameter and returns a boolean (either true
or false
). A simple example would be:
function s.lvfilter(c)
return c:IsMonster() and c:IsLevel(4)
end
This returns true
if c
is a Level 4 monster.
Using filters
Filters are mostly used with certain Duel
and Group
functions that collect or check for Card
s that fulfill the filter (in other words, Card
s that make the function return true
). Those functions will call the filter passed to them for every card they are concerned with.
For example, we can use the s.lvfilter
we have earlier to check if there are Card
s that fulfill it in certain locations. The following expression will be true
if there is at least 1 Level 4 monster in either player's GY:
Duel.IsExistingMatchingCard(s.lvfilter,tp,LOCATION_GRAVE,LOCATION_GRAVE,1,nil)
We can also make a player select Card
s that fulfill a CardFilter
. Here, a player will select 1 Level 4 monster in their hand, and the selected Card
will be stored in a Group
called g
:
local g=Duel.SelectMatchingCard(tp,s.lvfilter,LOCATION_HAND,0,1,1,nil)
Additional CardFilter
Arguments
Sometimes, filters need additional details other than the Card
they're checking. For example, we may need to write a filter for a face-up monster with ATK higher than the activating player's LP - a value that changes throughout the duel. This is where having additional filter arguments comes in.
Filter functions always take a Card
as their first parameter (usually named c
by convention), but they can take any amount of arguments after that. For the earlier example, we can create a filter that has an additional parameter called minatk
. We then check inside the filter if c
's ATK is greater than or equal to minatk
.
function s.atkfilter(c,minatk)
return c:IsFaceup() and c:IsAttackAbove(minatk)
end
Now, for the filter to know what minatk
is, it needs to be passed to it. This part is important. Otherwise, minatk
would just be nil
. Functions that use filters can take additional arguments. These functions will pass the additional arguments they receive (usually after the "exception" argument) to the filter they're using, and they become additional arguments of that filter.
To illustrate, say we have an effect with an activation condition that says If you control a face-up monster with ATK higher than your LP:. For this, we can use the s.atkfilter
earlier like so:
function s.condition(e,tp,eg,ep,ev,re,r,rp)
local lp=Duel.GetLP(tp)
return Duel.IsExistingMatchingCard(s.atkfilter,tp,LOCATION_MZONE,0,1,nil,lp)
end
Here, we first obtained tp
's LP and assigned it to the variable named lp
. Then, we passed that lp
as an extra argument to Duel.IsExistingMatchingCard
. Note that it counts as an extra argument since it comes after the "exception" argument, which is nil
in this case.
Internally, Duel.IsExistingMatchingCard
will call s.atkfilter
using lp
as an extra argument. This will correspond to the minatk
parameter of the filter. In other words, the first extra argument passed to Duel.IsExistingMatchingCard
(called lp
) becomes the first argument passed to s.atkfilter
(called minatk
).
Nesting CardFilter
functions
There are also cases where a filter needs additional information that depends on another Card
that fulfills a separate CardFilter
. A simple example is an effect that needs to banish 1 monster from a player's hand, and send another monster from that player's Deck to the GY with the same level as the first monster.
This means that the second monster's level will depend on the first monster's level. Naturally, we would need two CardFilter
s for this, but more importantly, the first monster's filter will need to call Duel.IsExistingMatchingCard
inside it using the second monster's filter.
--filter for the first monster (the card to be banished)
function s.rmfilter(c,tp)
return c:IsMonster() and c:HasLevel() and c:IsAbleToRemove()
and Duel.IsExistingMatchingCard(s.tgfilter,tp,LOCATION_DECK,0,1,nil,c:GetLevel())
end
--filter for the second monster (the card to be sent to the GY)
function s.tgfilter(c,lv)
return c:IsMonster() and c:IsAbleToGrave() and c:IsLevel(lv)
end
Here, s.rmfilter
is fulfilled if c
is a monster that has a level and can be banished, and that there exists a monster in tp
's Deck which satisfies s.tgfilter
. It passes c:GetLevel()
(the monster's level) as an additional argument to the Duel.IsExistingMatchingCard
call, which will in turn pass it to s.tgfilter
as its additional argument called lv
.
s.tgfilter
will then be fulfilled by monsters that can be sent to the GY and has a level equal to the passed lv
. The following legality check can be used to see if we have a valid monster to banish (such that we'll also have a valid monster to send to the GY):
if chk==0 then return Duel.IsExistingMatchingCard(s.rmfilter,tp,LOCATION_HAND,0,1,nil,tp) end
Essentially, we're using a function that uses a filter that uses a function that uses a filter. Hence, "nested". In theory, there's no limit to how deep such nesting could go (ignoring technical stuff like memory, performance, and stack size), but in practice, most cards that require nesting filters will only need single nesting. For a card that uses nesting filters two levels deep, you can check out Small World.