Commit 14515cb1 authored by Satty's avatar Satty

fix

parent d3283871
Pipeline #39131 failed with stages
in 8 minutes and 12 seconds
......@@ -24,7 +24,7 @@ function cm.initial_effect(c)
c:RegisterEffect(e1)
end
function cm.spcfilter(c)
return c:IsRace(RACE_INSECT) and c:IsAbleToDeckOrExtraAsCost()
return c:IsRace(RACE_INSECT) and c:IsAbleToDeckOrExtraAsCost() and c:IsFaceup()
end
function cm.spcon(e,c)
if c==nil then return true end
......@@ -35,7 +35,6 @@ end
function cm.spop(e,tp,eg,ep,ev,re,r,rp,c)
Duel.Hint(HINT_SELECTMSG,tp,HINTMSG_TODECK)
local g=Duel.SelectMatchingCard(tp,cm.spcfilter,tp,LOCATION_GRAVE+LOCATION_REMOVED,0,1,2,c)
Duel.ConfirmCards(1-tp,g)
Duel.ConfirmCards(tp,g)
Duel.HintSelection(g)
Duel.SendtoDeck(g,nil,2,REASON_COST)
end
\ No newline at end of file
......@@ -59,7 +59,7 @@ function cm.sfilter(c)
return c:IsSummonable(true,nil,1) and c:IsSetCard(0xcc5)
end
function cm.con(e,tp)
return (Duel.GetCurrentPhase()==PHASE_MAIN1 and Duel.GetCurrentPhase()==PHASE_MAIN2) and Duel.IsExistingMatchingCard(cm.sfilter,tp,LOCATION_HAND,0,1,nil)
return (Duel.GetCurrentPhase()==PHASE_MAIN1 or Duel.GetCurrentPhase()==PHASE_MAIN2) and Duel.IsExistingMatchingCard(cm.sfilter,tp,LOCATION_HAND,0,1,nil)
end
function cm.op1(e,tp)
Duel.Hint(HINT_SELECTMSG,tp,HINTMSG_SUMMON)
......
dofile("expansions/script/c20099997.lua")
if fuef then return end --2025/4/25
fuef = {
DebugMode = false, -- 调试模式
StepReg = false -- 每次 Reload 皆印出 Reg
}
if fuef then return end --2025/7/16
fuef = { }
fuef.__index = fuef
---------------------------------------------------------------- Standard Register
-- no cod
function fuef.I(_owner, _handler, _ignore)
if _owner == false then _handler, _owner = false end -- _owner is _handler (use in FG
return fuef:Creat("I", nil, _owner, _handler, _ignore)
end
function fuef.FG(_owner, _obj, _handler, _ignore)
-- ex: cm.e3 = fuef.FG("e1+e2")
if type(_owner) ~= "userdata" then _obj, _owner = _owner end
return fuef:Creat("F+G", nil, _owner, _handler, _ignore):OBJ(_obj)
end
-- need cod
for i, typs in ipairs({"S,F,E,S+C,F+C,E+C,F+TO,F+TF,S+TO,S+TF,X", "A,QO,QF"}) do
for _, typ in typs:ForCut("typ_reg_1") do
fuef.DebugMode = false -- 调试模式
dofile("expansions/script/c20099997.lua")
----------------------------------------------------------------
--- 建立一个 IGNITION 的 fuef
-- @param owner Card|Effect|nil 效果的持有者(若为 nil,仅建立效果表;需后续补设)
-- @param target boolean|Card|Group|player 注册目标
-- @param force boolean 是否强制注册
-- @return fuef 新创建的效果表实例
function fuef.I(owner, target, force)
local log = fudf.StartLog(0, "fuef.I", owner, target, force)
return fuef.New(log, "I", nil, owner, target, force)
end
--- 建立一个 FIELD + GRANT 的 fuef
-- @param owner Card|Effect|nil 效果的持有者(若为 obj,仅建立效果表;需后续补设)
-- @param obj Effect|fuef|string 要赋予的效果(effect object)
-- @param target boolean|Card|Group|player 注册目标
-- @param force boolean 是否强制注册
-- @return fuef 新创建的效果表实例
function fuef.FG(owner, obj, target, force)
local log = fudf.StartLog(0, "fuef.FG", owner, obj, target, force)
local is_obj = pcall(fusf.CheckTypes, owner, "string/fuef/Effect")
if is_obj then
owner, obj, target, force = nil, owner, obj, target
end
return fuef.New(log, "F+G", nil, owner, target, force):OBJ(obj)
end
--- 建立剩余需要 cod 的 fuef
for i, typs in ipairs{"S,F,E,S+C,F+C,E+C,F+TO,F+TF,S+TO,S+TF,X", "A,QO,QF"} do
for _, typ in typs:ForCut() do
local name = typ:gsub("+", "")
fuef[name] = function(_owner, _cod, _handler, _ignore)
if type(_owner) ~= "userdata" then _cod, _handler, _owner = _owner, _cod end -- _owner is cod (Noc
if i == 2 then _cod = _cod or "FC" end -- A,QO,QF
return fuef:Creat(typ, _cod, _owner, _handler, _ignore)
fuef[name] = function(owner, cod, target, force)
local log = fudf.StartLog(0, "fuef."..name, owner, cod, target, force)
local is_cod = pcall(fusf.CheckTypes, owner, "string/number")
if is_cod then
owner, cod, target, force = nil, owner, cod, target
end
if i == 2 then cod = cod or "FC" end -- A,QO,QF
return fuef.New(log, typ, cod, owner, target, force)
end
end
end
---------------------------------------------------------------- procedure Register (just Noc
function fuef.Proc_XYZ(cf, gf, min, max, ex_loc)
min, max = min or 1, max or 99
return fuef.F(EFFECT_SPSUMMON_PROC):DES("XYZ"):PRO("OE"):RAN("E"):Func("XYZ,PX_con(%1,%2,%3,%4,%5),PX_tg(%1,%2,%3,%4,%5),XyzLevelFreeOperation()",cf,gf,min,max,ex_loc)
end
function fuef.PX_g_goal(g, tp, xyzc, gf)
---------------------------------------------------------------- XYZ Proc
function fuef.ProcXyzLv(lv, cf, min, max)
fusf.CheckArgType("fuef.ProcXyzLv", 1, lv, "number")
fusf.CheckArgType("fuef.ProcXyzLv", 2, cf, "nil/function")
local lvf = fucf.MakeCardFilter("IsLv", lv)
local _cf = function(c, xyzc)
return lvf(c) and (not cf or cf(c, xyzc))
end
return fuef.ProcXyz(_cf, nil, min, max)
end
function fuef.ProcXyzAlter(cf, gf, min, max, ex_op, ex_cf)
return fuef.ProcXyz(cf, gf, min, max, ex_op, ex_cf):Lab(1)
end
function fuef.ProcXyz(cf, gf, min, max, ex_op, ex_cf)
fusf.CheckArgType("fuef.ProcXyz", 1, cf, "nil/string/function")
fusf.CheckArgType("fuef.ProcXyz", 2, gf, "nil/string/function")
local _, min = fusf.CheckArgType("fuef.ProcXyz", 3, min or 2, "number")
local _, max = fusf.CheckArgType("fuef.ProcXyz", 4, max or min, "number")
fusf.CheckArgType("fuef.ProcXyz", 5, ex_op, "nil/string/function")
fusf.CheckArgType("fuef.ProcXyz", 6, ex_cf, "nil/string/function")
local e = fuef.F(EFFECT_SPSUMMON_PROC):Des(1165):Pro("OE"):Ran("E"):Val("XYZ")
e:Con("PX_con", cf, gf, min, max, ex_op, ex_cf)
e:Tg("PX_tg", cf, gf, min, max, ex_op, ex_cf)
return e:Op(aux.XyzLevelFreeOperation())
end
function fuef.PX_goal(g, tp, xyzc, gf)
return (not gf or gf(g, tp, xyzc)) and Duel.GetLocationCountFromEx(tp, tp, g, xyzc) > 0
end
function fuef.PX_con(cf, gf, minc, maxc, ex_loc)
function fuef.PX_con(cf, gf, minc, maxc, ex_op, ex_cf)
return function(e, c, og, min, max)
if c == nil then return true end
if c:IsType(TYPE_PENDULUM) and c:IsFaceup() then return false end
local tp, minc, maxc = c:GetControler(), math.max(minc, min or minc), math.min(maxc, max or maxc)
local minc, maxc = math.max(minc, min or minc), math.min(maxc, max or maxc)
if maxc < minc then return false end
local catch = fusf.MakeFuncCatch(e)
local tp = c:GetControler()
if ex_op then
if type(ex_op) == "string" then ex_op = fusf.FindFuncCatch(catch, "ex_op", ex_op) end
if not ex_op(e, c, tp, 0) then return false end
end
if cf and type(cf) == "string" then cf = fusf.FindFuncCatch(catch, "cf", cf) end
local mg = fugf.Filter(og or fugf.Get(tp, "M"), "XyzLevelFreeFilter", {c, cf})
if ex_loc then mg = mg + fugf.GetFilter(tp, ex_loc, "XyzLevelFreeFilter", {c, cf}) end
local sg = Duel.GetMustMaterial(tp,EFFECT_MUST_BE_XMATERIAL)
if ex_cf then
local mg2 = fugf.GetFilter(tp, "A", "IsTyp/IsCanBeXyzMaterial", "S/T,%1", nil, c) - mg
if type(ex_cf) == "string" then ex_cf = fusf.FindFuncCatch(catch, "ex_cf", ex_cf) end
mg = mg + fugf.Filter(mg2, ex_cf, c)
end
local sg = Duel.GetMustMaterial(tp, EFFECT_MUST_BE_XMATERIAL)
if #mg > #(mg + sg) then return false end
Duel.SetSelectedCard(sg)
Auxiliary.GCheckAdditional = Auxiliary.TuneMagicianCheckAdditionalX(EFFECT_TUNE_MAGICIAN_X)
local res = mg:CheckSubGroup(fuef.PX_g_goal, minc, maxc, tp, c, gf)
if gf and type(gf) == "string" then gf = fusf.FindFuncCatch(catch, "gf", gf) end
local res = mg:CheckSubGroup(fuef.PX_goal, minc, maxc, tp, c, gf)
Auxiliary.GCheckAdditional = nil
return res
end
end
function fuef.PX_tg(cf, gf, minc, maxc, ex_loc)
function fuef.PX_tg(cf, gf, minc, maxc, ex_op, ex_cf)
return function(e, tp, eg, ep, ev, re, r, rp, chk, c, og, min, max)
if og and not min then return true end
minc, maxc = math.max(minc, min or minc), math.min(maxc, max or maxc)
local catch = fusf.MakeFuncCatch(e)
if cf and type(cf) == "string" then cf = fusf.FindFuncCatch(catch, "cf", cf) end
local mg = fugf.Filter(og or fugf.Get(tp,"M"), "XyzLevelFreeFilter", {c, cf})
if ex_loc then mg = mg + fugf.GetFilter(tp, ex_loc, "XyzLevelFreeFilter", {c, cf}) end
if ex_cf then
local mg2 = fugf.GetFilter(tp, "A", "IsTyp/IsCanBeXyzMaterial", "S/T,%1", nil, c) - mg
if type(ex_cf) == "string" then ex_cf = fusf.FindFuncCatch(catch, "ex_cf", ex_cf) end
mg = mg + fugf.Filter(mg2, ex_cf, c)
end
Duel.SetSelectedCard(Duel.GetMustMaterial(tp,EFFECT_MUST_BE_XMATERIAL))
Duel.Hint(HINT_SELECTMSG,tp,HINTMSG_XMATERIAL)
Auxiliary.GCheckAdditional = Auxiliary.TuneMagicianCheckAdditionalX(EFFECT_TUNE_MAGICIAN_X)
mg = mg:SelectSubGroup(tp, fuef.PX_g_goal, Duel.IsSummonCancelable(), minc, maxc, tp, c, gf)
if gf and type(gf) == "string" then gf = fusf.FindFuncCatch(catch, "gf", gf) end
mg = mg:SelectSubGroup(tp, fuef.PX_goal, Duel.IsSummonCancelable(), minc, maxc, tp, c, gf)
Auxiliary.GCheckAdditional = nil
if ex_op then
if type(ex_op) == "string" then ex_op = fusf.FindFuncCatch(catch, "ex_op", ex_op) end
ex_op(e, c, tp, 1)
end
if not mg or #mg == 0 then return false end
mg:KeepAlive()
e:SetLabelObject(mg)
return true
end
end
---------------------------------------------------------------- fuef()
function fuef:__call(_cod, _handler, _ignore)
fusf.Debug("")
fusf.Debug("__call("..fusf.ValToDebug(_cod, _handler, _ignore)..")")
-- _cod is owner (Creat and Register Noc
if type(_cod) == "userdata" then return self:CreatNoc(_cod, _handler, _ignore) end
-- Creat and Clone all key
local CE = setmetatable({ }, fuef)
local keys = "typ,cod,des,cat,pro,ran,tran,ctl,val,con,cos,tg,op,res,lab,obj,handler"
for _,_key in keys:ForCut("fuef:__call") do
CE[_key] = self[_key] or nil
---------------------------------------------------------------- local
--- 修正效果建立时的三个参数:赋予者(owner)、被赋予者(target)、是否强制注册(force)
-- 根据参数类型推断调用者的意图,并统一为标准形式返回,用于 fuef.New
-- @param owner Card/Effect/player/boolean/nil - 效果赋予者;若为 player/boolean/nil,将视为 target
-- @param target Card/Group/player/boolean/nil - 效果被赋予对象;若为 true 表示强制注册
-- @param force boolean/nil - 是否强制注册;预设为 false
-- @return owner(Card/Effect/nil), target(Card/Group/player/nil), force(boolean)
local function FixEffectAssignArgs(owner, target, force)
if owner == false or owner == 0 or owner == 1 then
owner, target = nil, owner
else
fusf.CheckTypes(owner, "nil/Card/Effect")
end
if target == true then
target, force = nil, true
elseif target ~= false then
local typ = fusf.CheckTypes(target, "nil/Card/Group/table/player")
if typ == "Card" or typ == "Group" then
target = fusf.ToGroupTable(target)
end
end
force = fusf.CheckType(force or false, "boolean")
return owner, target, force
end
--- 检查 val 是否为有效的 value 格式,用于 fuef:Func
-- @param val any 欲检查的值
-- @param val_typ string 值的型别
-- @return boolean 是否符合格式
local function MatchValFormat(val, val_typ)
if val_typ ~= "string" then return true end
if val:match("%,") then return false end
return tonumber(val) or fucs.val[val] or (val:sub(-4, -2):lower() == "val")
end
----------------------------------------------------------------
--- 建立一个新的效果表(fuef 物件),初始化基本栏位
--
-- - 若 `owner` 为 nil,仅建立效果表而不创建 Effect 实体,需后续呼叫 `:InitSetOwner(owner)` 补足。
-- - 若 `owner` 为 Effect 或 Card,则立即创建 Effect 并进行注册。
----- 建立一个新的效果表(fuef 物件),初始化基本栏位
--
-- - 若 `owner` 为 nil,仅建立效果表而不创建 Effect 实体,需后续呼叫 `:InitSetOwner(owner)` 补足。
-- - 若 `owner` 为 Effect 或 Card,则立即创建 Effect 并进行注册。
--
-- @param log 日志对象或层级(用于调试记录)
-- @param typ string|number 效果类型(支援常数名或数值,将转换为 EFFECT_TYPE_* 常数)
-- @param cod string|number|nil 效果代码(支援常数名或数值,将转换为 EVENT_* 常数,可为 nil)
-- @param owner nil|Effect|Card 效果实体的持有者(若为 nil,仅建立表,不创建实体)
-- @param target boolean|Card|Group|player 注册目标(传入至 Reg 中),若为 boolean 则视为 force
-- @param force boolean 是否强制注册,跳过效果限制检查
-- @return table fuef 物件(含 typ, cod, handler, force 等栏位)
-- @raise 若 owner 非 nil 且无法转为卡片时,建立 Effect 可能触发错误
function fuef.New(log, typ, cod, owner, target, force)
local log = fudf.StartLog(log, "fuef.New", typ, cod, owner, target, force)
local E = setmetatable({}, fuef)
E.typ = fusf.ParseConstantKey("etyp", typ)
log:Info("set self.typ : "..fusf.CutHex(E.typ))
if cod then
E.cod = fusf.ParseConstantKey("cod", cod)
log:Info("set self.cod : "..E.cod)
end
owner, target, force = FixEffectAssignArgs(owner, target, force)
E.handler = target
log:Info("set handler : "..fusf.Type(target))
E.force = force
log:Info("set force : "..tostring(force))
log:Info("has owner : "..tostring(not not owner))
if not owner then return E end
E.e = Effect.CreateEffect(fusf.ToCard(owner))
log:Info("set self.e")
E.e:SetType(E.typ)
log:Info("self.e SetType : "..fusf.CutHex(E.typ))
if E.cod then
E.e:SetCode(E.cod)
log:Info("self.e SetCode : "..E.cod)
end
-- Cover _cod
if CE.typ == EFFECT_TYPE_FIELD + EFFECT_TYPE_GRANT then
CE:OBJ(_cod)
elseif _cod then
CE.cod = fusf.Get_Constant("cod", _cod)
return E:Reg(log)
end
-- 注册 fuef 给 self.handler, 没有则注册给 owner
-- @param log 日志对象或层级(用于调试记录)
-- @return table 返回调用者 self,支持链式调用
function fuef:Reg(log, target, force)
if self.isinit then return self end
local log = fudf.StartLog(log, "fuef:Reg")
if self.handler == false then
log:Info("no RegisterEffect")
self.handler = false
return self
end
local tg_typ = fusf.CheckTypes(self.handler, "nil/table/player")
if tg_typ == "player" then
log:Info("RegisterEffect to player")
Duel.RegisterEffect(self.e, self.handler)
return self
elseif tg_typ == "nil" then
self.handler = { self.e:GetOwner() }
log:Info("set self.handler is owner")
end
-- Clone and return Clone
if self.e then
CE.e = self.e:Clone()
-- Set Key and Register Effect
return CE:SetKey():Reg(_handler, _ignore)
if self.force == nil then
self.force = false
log:Info("set self.force is false")
end
-- Clone Noc (
CE.pre = self
self.aft = CE
return CE
end
--------------------------------------------------------------------------
-- Creat and Register Effect
function fuef:Creat(_typ, _cod, _owner, _handler, _ignore)
fusf.Debug("")
fusf.Debug("Creat("..fusf.ValToDebug(fusf.CutHex(_typ), _cod, _owner, _handler, _ignore)..")")
local E = setmetatable({ }, fuef)
-- Set type and code and handler
E.typ = fusf.Get_Constant("etyp", _typ)
E.cod = fusf.Get_Constant("cod", _cod)
E.handler = _handler
-- use in no owner (Creat and Register in __call
if not _owner then return E end
-- Create Effect
E.e = Effect.CreateEffect(fusf.ToCard(_owner))
-- Set type and code
if _typ then E.e:SetType(E.typ) end
if _cod then E.e:SetCode(E.cod) end
return E:Reg(_handler, _ignore)
end
function fuef:CreatNoc(_owner, _handler, _ignore)
fusf.Debug("CreatNoc("..fusf.ValToDebug(_owner, _handler, _ignore)..")")
local rg = self.handler
local fc = rg[1]
fc:RegisterEffect(self.e, self.force)
if #rg > 1 then
self.clones = { }
for i = 2, #rg do
local e = self.e:Clone()
table.insert(self.clones, e)
local c = rg[i]
c:RegisterEffect(e, self.force)
end
end
log:Info("RegisterEffect to "..(#rg).." Card")
return self
end
--- 支援语法糖:fuef物件可直接以 () 调用以复制自身
-- 等同于 self:Clone(...)
function fuef:__call(cod, target, force)
return self:Clone(cod, target, force)
end
--- 复制当前 fuef 效果对象,可用于复制已有效果结构
-- @param cod string|number|fuef 替换的 cod, 赋予效果会将 cod 放到 OBJ
-- @param target 注册目标,同 Reg 函数
-- @param force 是否强制注册,同 Reg 函数
-- @return table 新的 fuef 对象,可链式调用
-- @raise cod 为 number 且用于 GRANT 类型时抛错;self.e 类型不正确时报错
function fuef:Clone(cod, target, force)
local log = fudf.StartLog(0, "fuef:Clone", cod, target, force)
local cod_typ = fusf.CheckArgType("Clone", 1, cod, "string/number/fuef")
local E = setmetatable({}, fuef)
log:Info("try Clone All key")
local keys = "typ,des,cat,pro,ran,tran,ctl,val,con,cos,tg,op,func,res,lab,obj,pl,handler,force"
for _,key in keys:ForCut() do
E[key] = self[key] or nil
end
log:Info("check self.typ is F+G")
if E.typ == EFFECT_TYPE_FIELD + EFFECT_TYPE_GRANT then
E:OBJ(cod)
else
E.cod = fusf.ParseConstantKey("cod", cod)
log:Info("set self.cod : "..E.cod)
end
local e_typ, e = fusf.CheckTypes(self.e, "nil/Effect")
if e_typ == "Effect" then
log:Info("Clone Effect E")
E.e = e:Clone()
local _, target, force = FixEffectAssignArgs(nil, target, force)
if target then
E.handler = target
log:Info("set E.handler : "..fusf.Type(target))
end
E.force = force
log:Info("set E.force : "..tostring(force))
E.depth = log.depth
return E:SetKey(log):Reg(log)
end
E.pre = self
self.aft = E
log:Info("set E.pre and self.aft")
return E
end
--- 初始化 fuef 链条的 Effect 实体,并设置所有字段属性
--
-- - 本方法用于延迟注册效果表(fuef),在 `initial_effect` 中设定持有者(owner)并依序初始化整条链。
-- - 链的起点是最早创建的 fuef(无 pre),终点为当前对象或后续的 aft。
-- - 每个节点会调用 `fuef.New` 创建 Effect 实体,依照字段依序设置属性(des、cat、val 等)。
-- - 所有链条节点都会生成独立实体效果,并注册(呼叫 `:Reg()`)。
--
-- ⚠ 若创建时未传入 owner,此函数为唯一可创建 Effect 实体的注册点。
--
-- @param name string 名称(用于日志打印)
-- @param owner Card|Effect 效果持有者(会被转为卡片用作 Effect.CreateEffect)
-- @param target nil|player 注册目标(用于设置 handler 字段,或 Reg 注册位置)
-- @return fuef 链尾节点(最后一个完成注册的效果表)
function fuef:InitSetOwner(name, owner, target)
local log = fudf.StartLog(1, "fuef:InitSetOwner", name, owner, target)
local tg_typ = fusf.CheckArgType("InitSetOwner", 2, target, "nil/player")
log:Info("find root")
local root = self
while root.pre do
root = root.pre
end
local keys = "des,cat,pro,ran,tran,ctl,val,con,cos,tg,op,res,lab,obj"
log:Info("set root owner")
local keys = "des,cat,pro,ran,tran,ctl,val,con,cos,tg,op,func,res,lab,obj,pl"
local last
repeat
if type(root.typ) == "table" then root.typ = root.typ[1] end
if root.handler ~= nil and not _handler then _handler = root.handler end
last = fuef:Creat(root.typ, root.cod, _owner, _handler, _ignore)
for _, key in keys:ForCut("CreatNoc") do
if root[key] then
fuef[key:upper()](last, table.unpack(root[key]))
repeat
if tg_typ == "player" then
root.handler = target
end
last = fuef.New(log, root.typ, root.cod, owner, root.handler)
last.isinit = true
for _, key in keys:ForCut() do
if root[key] then
local method = key:sub(1,1):upper()..key:sub(2)
log:Info("try call fuef:"..method)
fuef[method](last, table.unpack(root[key], 1, root[key].n))
end
end
last.isinit = nil
last:Reload(log)
root = root.aft
until not root
log:Info(name.." set owner down")
return last
end
-- Register Effect
function fuef:Reg(_handler, _ignore, _isreload)
if not (_isreload or fuef.StepReg) then
fusf.Debug("Reg("..fusf.ValToDebug(_handler, _ignore)..")")
end
-- not Reg (use in FG
if self.handler == false or _handler == false then
self.handler = false
return self
end
-- get handler and ignore
local handler, ignore = self.handler or self.e:GetOwner(), _ignore
if _handler == true then -- handler equal owner and ignore == true
ignore = true
elseif _handler then -- handler not equal owner and handler is Card, Group or tp
handler = _handler
end
-- cover handler
self.handler = handler
-- Handler is player
if type(handler) == "number" then
Duel.RegisterEffect(self.e, handler)
return self
end
-- Handler is c or g
-- get handler group
handler = fusf.ToGroup(handler)
-- check and cover ignore
if type(ignore) ~= "boolean" then ignore = self.ignore or false end -- not input _ignore
self.ignore = ignore
-- Register First Card
local fc = handler:GetFirst()
fc:RegisterEffect(self.e, ignore)
handler:RemoveCard(fc)
-- handler is group
if #handler > 0 then
-- Register other card
self.gclo = { }
for c in aux.Next(handler) do
local E = self.e:Clone()
self.gclo[#self.gclo + 1] = E
c:RegisterEffect(E, ignore)
end
end
-- 设置单个 Effect 属性(适用于非解包参数)
-- @param log 日志对象或层级(用于调试记录)
-- @param set_key Effect 的方法名,例如 "SetType"
-- @param val_key 当前对象中的字段名,例如 "typ"
-- @param val_typ 允许的类型,可以是字符串("number")或多个类型的字符串("function/number")或表
-- @return fuef 返回调用者 self,支持链式调用
function fuef:SetEffVal(log, set_key, val_key, val_typ)
local val = self[val_key]
if not val then return self end
fusf.CheckTypes(val, val_typ)
Effect[set_key](self.e, val)
log:Info(set_key)
return self
end
-- Set all Key
function fuef:SetKey()
if self.typ then self.e:SetType(self.typ) end
if self.cod then self.e:SetCode(self.cod) end
if self.des then self.e:SetDescription(self.des) end
if self.cat then self.e:SetCategory(self.cat) end
if self.pro then self.e:SetProperty(self.pro) end
if self.ran then self.e:SetRange(self.ran) end
if self.tran then self.e:SetTargetRange(table.unpack(self.tran)) end
if self.ctl then self.e:SetCountLimit(table.unpack(self.ctl)) end
if self.val then self.e:SetValue(self.val) end
if self.con then self.e:SetCondition(self.con) end
if self.cos then self.e:SetCost(self.cos) end
if self.tg then self.e:SetTarget(self.tg) end
if self.op then self.e:SetOperation(self.op) end
if self.res then self.e:SetReset(table.unpack(self.res)) end
if self.lab then self.e:SetLabel(table.unpack(self.lab)) end
if self.obj then self.e:SetLabelObject(self.obj) end
-- 设置需要 unpack 参数的 Effect 属性(适用于参数为 table 的字段)
-- @param log 日志对象或层级(用于调试记录)
-- @param set_key Effect 的方法名,例如 "SetTargetRange"
-- @param val_key 当前对象中的字段名,例如 "tran"
-- @return fuef 返回调用者 self,支持链式调用
function fuef:SetEffValUnpack(log, set_key, val_key)
local val = self[val_key]
if not val then return self end
fusf.CheckType(val, "table")
Effect[set_key](self.e, table.unpack(val))
log:Info(set_key)
return self
end
-- Reset and Creat Effect and SetKey and Reg (use in every set key final return
function fuef:Reload(de)
-- Reset self
local _owner = self.e:GetOwner()
self.e:Reset()
self.e = Effect.CreateEffect(_owner)
-- Reset if handler is group
if self.gclo then
for _, gcloe in ipairs(self.gclo) do
gcloe:Reset()
end
self.gclo = nil
end
return self:SetKey(de):Reg(nil, nil, true)
end
function fuef:IsNil(from, ...)
local res = fusf.IsNil(...)
if res then fusf.Debug("... IsNil <- "..from) end
return res
end
function fuef:PreChk(from, ...)
fusf.Debug("set "..from.." : "..fusf.ValToDebug(...))
if not self.e then --is Noc
self[from:lower()] = {...}
return false
end
return not self:IsNil("PreChk <- "..from, ...)
-- 主设置函数:设置当前对象(fuef)中所有已定义的 Effect 关键字段
-- 支持类型检查、空值跳过、调试输出
-- @param log 日志对象或层级(用于调试记录)
-- @return fuef 返回调用者 self,支持链式调用
function fuef:SetKey(log)
local log = fudf.StartLog(log, "fuef:SetKey")
self:SetEffVal(log, "SetType", "typ", "number")
self:SetEffVal(log, "SetCode", "cod", "number")
self:SetEffVal(log, "SetDescription", "des", "number")
self:SetEffVal(log, "SetCategory", "cat", "number")
self:SetEffVal(log, "SetProperty", "pro", "number")
self:SetEffVal(log, "SetRange", "ran", "number")
self:SetEffValUnpack(log, "SetTargetRange", "tran")
self:SetEffValUnpack(log, "SetCountLimit", "ctl")
self:SetEffVal(log, "SetValue", "val", "function/number/boolean")
self:SetEffVal(log, "SetCondition", "con", "function")
self:SetEffVal(log, "SetCost", "cos", "function")
self:SetEffVal(log, "SetTarget", "tg", "function")
self:SetEffVal(log, "SetOperation", "op", "function")
self:SetEffValUnpack(log, "SetReset", "res")
self:SetEffValUnpack(log, "SetLabel", "lab")
self:SetEffVal(log, "SetLabelObject", "obj", "Card/Group/Effect")
self:SetEffVal(log, "SetOwnerPlayer", "pl", "player")
return self
end
-- 输出当前 fuef 对象的字段信息,用于调试
function fuef:Info()
Debug.Message("")
Debug.Message("Info")
if self.typ then Debug.Message("typ : "..fusf.CutHex(self.typ, ", "))end
if self.typ then Debug.Message("typ : "..fusf.CutHex(self.typ))end
if self.cod then Debug.Message("cod : "..self.cod) end
if self.des then Debug.Message("des : "..(self.des//16)..", "..(self.des%16)) end
if self.cat then Debug.Message("cat : "..fusf.CutHex(self.cat, ", ")) end
if self.pro then Debug.Message("pro : "..fusf.CutHex(self.pro, ", ")) end
if self.cat then Debug.Message("cat : "..fusf.CutHex(self.cat)) end
if self.pro then Debug.Message("pro : "..fusf.CutHex(self.pro)) end
if self.ran then Debug.Message("ran : "..self.ran) end
if self.tran then Debug.Message("tran : "..table.concat(self.tran, ", ")) end
if self.ctl then Debug.Message("ctl : "..table.concat(self.ctl, ", ")) end
......@@ -248,245 +460,447 @@ function fuef:Info()
if self.cos then Debug.Message("cos : "..tostring(self.cos)) end
if self.tg then Debug.Message("tg : "..tostring(self.tg)) end
if self.op then Debug.Message("op : "..tostring(self.op)) end
if self.res then Debug.Message("res : "..fusf.CutHex(self.res[1])..", "..self.res[2], ", ") end
if self.res then Debug.Message("res : "..fusf.CutHex(self.res[1])..", "..self.res[2]) end
if self.lab then Debug.Message("lab : "..table.concat(self.lab, ", ")) end
if self.obj then Debug.Message("obj : "..aux.GetValueType(self.obj)) end
if self.handler then Debug.Message("handler : "..(handler == false and "false" or aux.GetValueType(self.handler))) end
end
----------------------------------------------------------------DES
function fuef:DES(_code, _id) -- (0), ("n"), (m), ("+1")
if not self:PreChk("DES", _code, _id) then return self end
self.des = fusf.GetDES(_code, _id, self.e:GetOwner():GetOriginalCode())
return self:Reload()
end
----------------------------------------------------------------TYP, COD, CAT and PRO
function fuef:Cons_Model(_key, _val)
if not self:PreChk(_key:upper(), _val) then return self end
local _keytype = _key == "typ" and "etyp" or _key
local val, des = fusf.Get_Constant(_keytype, _val)
self[_key] = val
if _key == "cat" and des then self.des = self.des or des end
return self:Reload()
end
function fuef:TYP(_val)
return self:Cons_Model("typ", _val)
end
function fuef:COD(_val)
return self:Cons_Model("cod", _val)
end
function fuef:CAT(_val)
return self:Cons_Model("cat", _val)
end
function fuef:PRO(_val)
return self:Cons_Model("pro", _val)
end
----------------------------------------------------------------RAN and TRAN
function fuef:RAN(_loc)
if not self:PreChk("RAN", _loc) then return self end
self.ran = fusf.Get_Loc(_loc, nil, "fuef:RAN()")
return self:Reload()
end
function fuef:TRAN(_loc1, _loc2)
if not self:PreChk("TRAN", _loc1, _loc2) then return self end
self.tran = {fusf.Get_Loc(_loc1, _loc2, "fuef:TRAN()")}
return self:Reload()
end
----------------------------------------------------------------CTL
function fuef:CTL(_count, _code, _pro) --count, code, pro
if not self:PreChk("CTL", _count, _code, _pro) then return self end
if type(_count) == "string" or _count > 99 then -- ("n+D") or (m) -> (1, "n+D") or (1, m)
_count, _code, _pro = 1, _count, _code
end
if _code == "m" then _code = self.e:GetOwner():GetOriginalCode() end
local res, ctl_val = {_code or 0, _pro or 0}, {
O = EFFECT_COUNT_CODE_OATH,
D = EFFECT_COUNT_CODE_DUEL,
C = EFFECT_COUNT_CODE_CHAIN,
}
if _pro then -- (1, n, "D")
res = {fusf.M_chk(_code), ctl_val[_pro:match("[ODC]")]}
elseif type(_code) == "string" then -- (1, "n+D")
res = _code:Cut("CTL_1", "+") -- (n), (n, d), (d)
if res[1]:match("[ODC]") then
res = {0, res[1]}
elseif res[1]:match("m") then
res[1] = self.e:GetOwner():GetOriginalCode()
else
res[1] = fusf.M_chk(tonumber(res[1]))
if self.obj then Debug.Message("obj : "..fusf.Type(self.obj)) end
if self.handler then Debug.Message("handler : "..fusf.Type(self.handler)) end
end
--- initial 检查,仅当尚未创建效果实体(self.e 为 nil)时执行初始化
-- @param log 日志对象或层级(用于调试记录)
-- @param key string 要初始化的字段名(会转为小写)
-- @param ... any 初始化值,将作为 table 存入字段
-- @return boolean 是否在 initial 调用
function fuef:InitCheck(log, key, ...)
if self.e then return false end
local log = fudf.StartLog(log, "fuef:InitCheck", key, ...)
self[key] = table.pack(...)
log:Info("set self."..key)
return true
end
--- 重置 fuef.e 并以 fuef.e 的 owner 再创建一个效果并重新应用 Key
-- 会先 Reset 原本的效果实体并重新创建,清除 clones
-- @param log 日志对象或层级(用于调试记录)
-- @return fuef 返回 self(可链式调用)
function fuef:Reload(log)
if self.isinit then return self end
local log = fudf.StartLog(log, "fuef:Reload")
log:Info("recreate Effect")
local owner = self.e:GetOwner()
self.e:Reset()
self.e = Effect.CreateEffect(owner)
if self.clones and #self.clones > 0 then
log:Info("clean self.clones")
for _, e in ipairs(self.clones) do
e:Reset()
end
if res[2] then res[2] = ctl_val[res[2]:match("[ODC]")] end
if res[2] & 0x30000000 > 0 and res[1] == 0 then res[1] = self.e:GetOwner():GetOriginalCode() end -- is O or D
self.clones = nil
end
self.ctl = {_count, res[1] + res[2]}
return self:Reload()
return self:SetKey(log):Reg(log)
end
----------------------------------------------------------------VAL, CON, COS, TG and OP
function fuef:Func(_val, _func, ...)
-- func = ("val,con,cos(v1,v2),tg,op") or ("con(v1,v2),op") or (val, "con,op(v1, v2)"), if v = %1~n then { ... } is value table
if fusf.IsNil(_val, _func) then -- nil chk
fusf.Debug("_val, _func IsNil <- Func".._from)
return self
----------------------------------------------------------------DES
--- 设置描述字段(des),支持延迟初始化
-- 若尚未创建效果实体,则记录并延后设置;否则立即设置并重载
-- @param code string|number 描述代码(将传入 ResolveDescription)
-- @param id string|number 描述 ID(将传入 ResolveDescription)
-- @return fuef 返回 self(可链式调用)
function fuef:Des(code, id)
local log = fudf.StartLog(self, "fuef:Des", code, id)
fusf.CheckEmptyArgs("Des", code, id)
if self:InitCheck(log, "des", code, id) then return self end
local m = self.e:GetOwner():GetOriginalCode()
self.des = fusf.ResolveDescription(code, id, m)
log:Info("set self.des : "..self.des)
return self:Reload(log)
end
----------------------------------------------------------------Const
--- 设定常数型栏位(如 typ、cod、cat 等),若尚未建立效果物件则先记录初始值
-- 若已存在效果物件则立即重新加载
-- @param log 日志对象或层级(用于调试记录)
-- @param key string 栏位名称(如 "typ", "cod", "cat" 等)
-- @param val any 欲设定的常数值(支援名称字串或数值)
-- @return self fuef 物件
function fuef:SetConstField(log, key, val)
local log = fudf.StartLog(log, "fuef:SetConstField", key, val)
fusf.CheckArgType(key, 1, val, "number/string")
key = key:lower()
if self:InitCheck(log, key, val) then return self end
local table_key = (key == "typ") and "etyp" or key
local val, des = fusf.ParseConstantKey(table_key, val)
self[key] = val
log:Info(("set self.%s : %s"):format(key, val))
if key == "cat" and des then
self.des = self.des or des
log:Info("set self.des : "..self.des)
end
local vals = {...}
if not (type(_val) == "string" and _val:match("%,")) then -- check _val is val
local val = { _val }
if type(_val) == "string" and _val:match("%%") then val = { _val , ... } end
self.val = val
else -- _val is _func
vals, _func = {_func, ...}, _val
return self:Reload(log)
end
--- 设定 type
function fuef:Typ(typ)
local log = fudf.StartLog(self, "fuef:Typ", typ)
return self:SetConstField(log, "Typ", typ)
end
--- 设定 code
function fuef:Cod(code)
local log = fudf.StartLog(self, "fuef:Cod", code)
return self:SetConstField(log, "Cod", code)
end
--- 设定 category
function fuef:Cat(category)
local log = fudf.StartLog(self, "fuef:Cat", category)
return self:SetConstField(log, "Cat", category)
end
--- 设定 property
function fuef:Pro(property)
local log = fudf.StartLog(self, "fuef:Pro", property)
return self:SetConstField(log, "Pro", property)
end
----------------------------------------------------------------Range
--- 设定效果适用的区域(Range)
-- @param loc string|number 区域常数或其字串名称
-- @return self fuef 物件
function fuef:Ran(loc)
local log = fudf.StartLog(self, "fuef:Ran", loc)
fusf.CheckArgType("Ran", 1, loc, "number/string")
if self:InitCheck(log, "ran", loc) then return self end
loc = fusf.GetLoc(loc)
self.ran = loc
log:Info("set self.ran : "..loc)
return self:Reload(log)
end
--- 设定效果于自己与对手区域的作用范围(TargetRange)
-- @param self_loc string|number 自己场地的区域
-- @param oppo_loc string|number 对手场地的区域
-- @return self fuef 物件
function fuef:Tran(self_loc, oppo_loc)
local log = fudf.StartLog(self, "fuef:Tran", self_loc, oppo_loc)
fusf.CheckEmptyArgs("Tran", self_loc, oppo_loc)
if self:InitCheck(log, "tran", self_loc, oppo_loc) then return self end
self_loc, oppo_loc = fusf.GetLoc(self_loc, oppo_loc)
self.tran = {self_loc, oppo_loc}
log:Info(("set self.tran : %s / %s"):format(self_loc, oppo_loc))
return self:Reload(log)
end
----------------------------------------------------------------CountLimit
--- 设定效果限制条件(Count Limit),支援多种输入格式。
-- @param count number|string 可为数字(使用次数)或字串(如 "100+O")
-- @param code number|string|nil 可为卡号、字符串(含限制类型 "O/D/C"),或留空
-- @param pro string|nil 限制类型("O"=OATH, "D"=DUEL, "C"=CHAIN)
-- @return fuef 返回自身以支援链式调用
function fuef:Ctl(count, code, pro)
local log = fudf.StartLog(self, "fuef:Ctl", count, code, pro)
fusf.CheckEmptyArgs("Ctl", count, code, pro)
if self:InitCheck(log, "ctl", count, code, pro) then return self end
local ct_typ = fusf.CheckArgType("Ctl", 1, count, "number/string")
if ct_typ == "string" or count > 99 then -- ("n+D") or (m) -> (1, "n+D") or (1, m)
count, code, pro = 1, count, code
end
-- "val,con,cos,tg,op" or "con,op" , if cant match then follow a sequence
local seqs, place, sets = {"val", "con", "cos", "tg", "op"}, 1, { }
-- "f1,f2(%1,v2),f3" -> {"f1", {"f2", vals[1], "v2"}, "f3"}, ... use in vi = %i
for i, func in fusf.ForTable(fusf.Val_Cuts(_func, table.unpack(vals))) do
local fname, fval = func, func
if type(fname) == "table" then
fname = table.remove(fval,1)
else
fval = nil
end
-- find fname can match seqs
local match = 0
for j = place, 5 do
local temp = fname:find(seqs[j])
if temp and temp > match then
place, match = j, temp
local cod_typ = fusf.CheckArgType("Ctl", 2, code, "nil/number/string")
local m = self.e:GetOwner():GetOriginalCode()
local ctl_consts = fusf.Findfucs("ctl")
local _code, _pro = 0, 0
fusf.CheckArgType("Ctl", 3, pro, "nil/string")
if pro then
_pro = fusf.FindTables(pro, ctl_consts)
end
if cod_typ == "number" then
_code = fusf.NormalizeID(code, m)
elseif cod_typ == "string" then
for _, val in code:ForCut("+") do
local match = val:match("[ODC]")
if match then
if _pro ~= 0 then
error("repeat set fuef:Ctl pro in string", 2)
end
_pro = fusf.FindTables(match, ctl_consts)
else
_code = fusf.NormalizeID(val, m)
end
end
-- cant match seqs
if match == 0 then place = i end
-- set self[key] and save key
func = seqs[place]
self[func], sets[#sets + 1] = {fname, fval}, func -- self[func] = {name, {...}} or {name}
end
if not self.e then return self end
-- self[key] = {func, ...}
if sets[1] == "val" then
table.remove(sets, 1)
local val = self.val[1]
if type(val) == "string" then
self.val = tonumber(val) or fucs.val[val] or fusf.Get_Func(self.e:GetOwner(), table.unpack(self.val))
if not self.val then fusf.Debug("val Func value is nil") end
else -- number or function
if _code == 0 and (_pro == ctl_consts["O"] or _pro == ctl_consts["D"]) then
_code = m
end
self.ctl = {count, _code + _pro}
log:Info(("set self.ctl : %s / %s"):format(count, self.ctl[2]))
return self:Reload(log)
end
----------------------------------------------------------------VAL, CON, COS, TG and OP
--- 设定函数型栏位,如 val、con、tar、op 等,可接受函数名、表达式或函数本体
-- @param log 日志对象或层级(用于调试记录)
-- @param key string 欲设定的栏位名称
-- @param func string|function|number 欲指定的函数或值
-- @param ... 可选的参数,用于传递给 ResolveFunction 或 ParseCallExprString
-- @return fuef 返回自身以支援链式调用
function fuef:SetFuncField(log, key, func, ...)
local log = fudf.StartLog(log, "fuef:SetFuncField", key, func, ...)
fusf.CheckEmptyArgs(key, func, ...)
key = key:lower()
local types = "string/function"
if key == "val" then types = types.."/number" end
local f_typ = fusf.CheckArgType(key, 1, func, types)
if self:InitCheck(log, key, func, ...) then return self end
local has_arg = select("#", ...) > 0
if f_typ == "function" then
if has_arg then func = func(...) end
self[key] = func
log:Info(("set self.%s : %s"):format(key, tostring(func)))
return self:Reload(log)
end
if key == "val" then
local val = tonumber(func) or fucs.val[func] or aux[func]
if val ~= nil then
self.val = val
log:Info("set self.val : "..tostring(val))
return self:Reload(log)
end
end
for _,set in ipairs(sets) do
local res = fusf.Get_Func(self.e:GetOwner(), table.unpack(self[set]))
if not res then fusf.Debug(set.." Func value is nil") end
self[set] = res
local f, args = func, nil
if has_arg then args = table.pack(...) end
if func:match("%(") then
f, args = fusf.ParseCallExprString(func, args)
end
return self:Reload()
end
function fuef:Func_Model(_key, _func, ...)
if not self:PreChk(_key:upper(), _func, ...) then return self end
local val_chk = _key == "val" and (tonumber(_func) or fucs.val[_func]) or nil
local vals = select("#", ...) > 0 and { ... } or nil
if type(_func) == "string" and _func:match("%(") then
_func = fusf.Val_Cuts(_func, ...)[1]
vals = _func
if type(_func) == "table" then
_func = table.remove(vals,1)
else
vals = nil
local c = self.e:GetOwner()
f = fusf.ResolveFunction(f, args, fusf.Getcm(c), c.lib)
self[key] = f
log:Info(("set self.%s : %s"):format(key, tostring(f)))
return self:Reload(log)
end
--- 设定效果的 value(数值或函数),可为 number|string|function
-- @param val number|string|function 数值或函数(或名称)
-- @param ... 可选的额外参数(若 val 是函数字串会带入)
-- @return fuef 返回自身以支援链式调用
function fuef:Val(val, ...)
local log = fudf.StartLog(self, "fuef:Val", val, ...)
return self:SetFuncField(log, "Val", val, ...)
end
--- 设定效果的 condition(条件),可为 string|function
-- @param condition function|string 条件函数或其名称
-- @param ... 可选的额外参数(若为函数字串会带入)
-- @return fuef 返回自身以支援链式调用
function fuef:Con(condition, ...)
local log = fudf.StartLog(self, "fuef:Con", condition, ...)
return self:SetFuncField(log, "Con", condition, ...)
end
--- 设定效果的 cost(代价),可为 string|function
-- @param cost function|string 条件函数或其名称
-- @param ... 可选的额外参数(若为函数字串会带入)
-- @return fuef 返回自身以支援链式调用
function fuef:Cos(cost, ...)
local log = fudf.StartLog(self, "fuef:Cos", cost, ...)
return self:SetFuncField(log, "Cos", cost, ...)
end
--- 设定效果的 target(目标),可为 string|function
-- @param target function|string 条件函数或其名称
-- @param ... 可选的额外参数(若为函数字串会带入)
-- @return fuef 返回自身以支援链式调用
function fuef:Tg(target, ...)
local log = fudf.StartLog(self, "fuef:Tg", target, ...)
return self:SetFuncField(log, "Tg", target, ...)
end
--- 设定效果的 operation(执行动作),可为 string|function
-- @param operation function|string 条件函数或其名称
-- @param ... 可选的额外参数(若为函数字串会带入)
-- @return fuef 返回自身以支援链式调用
function fuef:Op(operation, ...)
local log = fudf.StartLog(self, "fuef:Op", operation, ...)
return self:SetFuncField(log, "Op", operation, ...)
end
--- 依据传入的函数结构设定 fuef 物件的多个栏位(val/con/cos/tg/op)
-- 支援多种格式的输入,并自动解析每一段功能模块,依照顺序填入栏位
-- @param val 可能是 val 值,或实际的函数名(视情况而定)
-- @param func 字串描述或 table 结构,代表要设置的功能函数模块
-- @param ... 其他额外参数,可能为 val 的对应值,或函数模块的参数
-- @return fuef 返回自身以支援链式调用
function fuef:Func(val, func, ...)
local log = fudf.StartLog(self, "fuef:Func", val, func, ...)
fusf.CheckEmptyArgs("Func", val, func, ...)
if self:InitCheck(log, "func", val, func, ...) then return self end
local val_typ = fusf.CheckArgType(up_key, 1, val, "string/function/number")
log:Info("try match val")
local args = {...}
if MatchValFormat(val, val_typ) then
self:SetFuncField(log, "val", val, ...)
else
func, args = val, {func, ...}
end
local c = self.e:GetOwner()
local cm = fusf.Getcm(c)
local keys, key_ind = {"val", "con", "cos", "tg", "op"}, 1
local func_table = fusf.ParseCallGroupString(func, args)
if func_table.n > 5 then error("Func : invalid func len", 2) end
log:Info("try match keys")
for i, unit in fusf.ForTable(func_table) do
local fname, fargs = unit
if type(unit) == "table" then
fname, fargs = unit[1], unit[2]
end
elseif vals and #vals == 1 and type(vals[1]) == "table" then
vals = vals[1] -- 若 vals 中只有一个表,则直接解包表
log:Info(i.." : "..fname)
local f = fusf.ResolveFunction(fname, fargs, cm, c.lib)
local match = 0
for j = key_ind, 5 do
local ind = fname:find(keys[j])
if ind and ind > match then
key_ind, match = j, ind
end
end
if match == 0 then key_ind = i end
local key = keys[key_ind]
log:Info("match "..key)
self:SetFuncField(log, key, f)
end
self[_key] = val_chk or fusf.Get_Func(self.e:GetOwner(), _func, vals)
if not self[_key] then fusf.Debug(_key.." Func value is nil") end
return self:Reload()
end
function fuef:VAL(_func, ...)
return self:Func_Model("val", _func, ...)
end
function fuef:CON(_func, ...)
return self:Func_Model("con", _func, ...)
end
function fuef:COS(_func, ...)
return self:Func_Model("cos", _func, ...)
end
function fuef:TG(_func, ...)
return self:Func_Model("tg", _func, ...)
end
function fuef:OP(_func, ...)
return self:Func_Model("op", _func, ...)
log:Info("all match down")
return self
end
----------------------------------------------------------------RES
function fuef:RES(_flag, _count) -- _flag = a + b/b1/b2 + c | 1
if not self:PreChk("RES", _flag, _count) then return self end
self.res = fusf.GetRES(_flag, _count)
return self:Reload()
end
----------------------------------------------------------------LAB
function fuef:LAB(...)
if not self:PreChk("LAB", ...) then return self end
local elabs = { }
for _, labs in ipairs({...}) do
if type(labs) == "string" then
for _, lab in labs:ForCut("LAB", "+") do
elabs[#elabs + 1] = (lab == "m") and self.e:GetOwner():GetOriginalCode() or tonumber(lab)
--- 设定效果的 reset(重置条件)
-- @param flag string|number 条件描述(如 "a + b - c , 1")
-- @param count number? 可选的重置次数,若省略则从字串中解析
-- @return fuef 返回自身以支援链式调用
function fuef:Res(flag, count)
local log = fudf.StartLog(self, "fuef:Res", flag, count)
fusf.CheckEmptyArgs("Res", flag, count)
if self:InitCheck(log, "res", flag, count) then return self end
flag, count = fusf.ResolveReset(flag, count)
self.res = {flag, count}
log:Info(("set self.res : %s / %d"):format(fusf.CutHex(flag), count))
return self:Reload(log)
end
----------------------------------------------------------------LAB & OBJ
--- 设定 label 参数,支援数字、字串(可用 "+" 拆分)与特别标记 "m"
-- @param ... 可为数字、字串(如 "1+2+m")等,会转为数字阵列
-- @return fuef 返回自身,用于链式调用
function fuef:Lab(...)
local log = fudf.StartLog(self, "fuef:Lab", ...)
fusf.CheckEmptyArgs("Lab", ...)
if self:InitCheck(log, "lab", ...) then return self end
local m = self.e:GetOwner():GetOriginalCode()
local args = {}
for i, val in ipairs{...} do
local typ = fusf.CheckArgType("Lab", i, val, "string/number")
if typ == "string" then
for _, lab in val:ForCut("+") do
args[#args + 1] = (lab == "m") and m or tonumber(lab)
end
else
elabs[#elabs + 1] = labs
args[#args + 1] = val
end
end
self.lab = elabs
return self:Reload()
end
----------------------------------------------------------------OBJ
function fuef:OBJ(_val)
if not self:PreChk("OBJ", _val) then return self end
if type(_val) == "string" then -- ex: cm.e3 = fuef.FG("e1") -> obj == cm.e1
_val = fusf.Getcm(self)["es"][_val].e
end
self.obj = _val
return self:Reload(1)
end
--------------------------------------------------------------------------"Support Effect function"
function fuef.initial(_lib, _glo, _exop_func, ...)
local cm, m = GetID()
local exop_val = { ... }
cm.es, cm.lib = {}, _lib
cm.initial_effect = cm.initial_effect or function(c)
-- do ex_op
if _exop_func then
local place = 1
if type(_exop_func) ~= "table" then _exop_func = { _exop_func } end
for _, exop_func in ipairs(_exop_func) do
if type(exop_func) == "string" then
for _, func in exop_func:ForCut("fuef.initial") do
local f = fucf[func] or Card[func]
if f then
f(c, exop_val[place])
else
cm[func] = exop_val[place](c)
end
place = place + 1
end
else
exop_func(c, exop_val[place])
place = place + 1
end
end
end
local dof = function(_name, _exval)
local n = 1
while cm[_name..n] do
cm.es[_name..n] = cm[_name..n](c, _exval)
n = n + 1
end
self.lab = args
log:Info(("set self.lab : %d arg"):format(#args))
return self:Reload(log)
end
--- 设置效果的关联对象(object),可为字符串代号或实际对象
-- @param val string|Card|Group|Effect 关联对象,字符串将解析为 cm.elist 的键
-- @return fuef 返回自身,用于链式调用
-- @raise 类型不符时抛出错误
function fuef:Obj(val)
local log = fudf.StartLog(self, "fuef:Obj", val)
local typ = fusf.CheckArgType("Obj", 1, val, "string/fuef/Card/Group/Effect")
if self:InitCheck(log, "obj", val) then return self end
if typ == "string" then
local e = fusf.Getcm(self).elist[val]
if not e or not e.e then
error("Obj: elist entry for '" .. val .. "' is missing or invalid", 2)
end
dof("e") -- do e1 ~ en
dof("pe") -- do e1 ~ en in lib pre set
-- if cm[_glo] then do ge1 ~ gen
if not (_glo and not cm[_glo]) then return end
cm[_glo] = {0, 0}
dof("ge", 1)
val = fusf.CheckType(e.e, "Effect")
end
return cm, m
self.obj = val
log:Info("set self.obj : "..typ)
return self:Reload(log)
end
---------------------------------------------------------------- OwnerPlayer
--- 设置效果的 OwnerPlayer 属性为 player
-- @param p player 玩家
-- @return self fuef 物件
function fuef:Pl(p)
local log = fudf.StartLog(self, "fuef:Pl", p)
fusf.CheckArgType("Pl", 1, p, "player")
if self:InitCheck(log, "Pl", p) then return self end
self.pl = p
log:Info("set self.pl : "..p)
return self:Reload(log)
end
function fuef.tg_is_cos(e,tp,eg,ep,ev,re,r,rp,chk)
e:SetLabel(100)
if chk==0 then return true end
end
\ No newline at end of file
dofile("expansions/script/c20099998.lua")
if fucf then return end
fucf, fugf = { }, { }
dofile("expansions/script/c20099998.lua")
-------------------------------------- Group function
--- 根据卡片过滤条件构造一个群组过滤器(可回传过滤后的卡片群组或判断数量)
-- @param f string|function 过滤函数或名称,传入 MakeCardFilter 使用
-- @param v any 传入过滤函数的参数,可为单值或表
-- @param ... 可选占位参数,传给 MakeCardFilter
-- @return function function(Group, number?): Group | boolean
-- 若不提供 n,返回符合条件的子群组;
-- 若提供正整数 n,返回群组中符合条件的卡片数量是否 ≥ n;
-- 若提供负整数 -n,返回是否 ≤ -n。
function fugf.MakeGroupFilter(f, v)
return function(g, n, ...)
fusf.CheckArgType("MakeGroupFilter", 1, g, "Group")
local ok, res = pcall(fusf.CheckArgType,"MakeGroupFilter", 2, n, "nil/number")
if not ok then
error(res, 2)
end
g = g:Filter(fucf.MakeCardFilter(f, v, ...), nil)
if not n then return g end
return n > 0 and #g >= n or #g <= -n
end
end
--- 返回以 p 来看的 loc (自己以及对方区域以+分开)位置的卡片组
-- @param p number 玩家编号(必须是 0 或 1)
-- @param loc string 表示区域的字符串(如 "H+HM" 等)
-- @return Group 返回该区域内所有卡片的 Group
function fugf.Get(p, loc)
return Duel.GetFieldGroup(p, fusf.Get_Loc(loc))
fusf.CheckArgType("Get", 1, p, "player")
return Duel.GetFieldGroup(p, fusf.GetLoc(loc))
end
--- 以指定条件过滤 Group 中的卡片(支持数量判断)
-- @param g Group 要过滤的卡片群组
-- @param f string|function 用于 MakeCardFilter 的过滤函数或名称
-- @param v any 用于传入过滤器的参数(可为单值、表等)
-- @param n number|nil 可选值:
-- - 若为 nil,返回过滤后的 Group;
-- - 若为正数,判断是否有 ≥ n 张符合条件的卡片,返回 boolean;
-- - 若为负数,判断是否有 ≤ n 张符合条件的卡片,返回 boolean。
-- @param ... any 可选参数,传入 MakeCardFilter,用于额外参数或占位
-- @return Group|boolean 返回过滤后的 Group,或符合数量条件的布尔值
function fugf.Filter(g, f, v, n, ...)
return fusf.Creat_GF(f, v, ...)(g, n)
return fugf.MakeGroupFilter(f, v)(g, n, ...)
end
--- 获取并过滤指定玩家区域内的卡片
-- @param p number 玩家编号(必须是 0 或 1)
-- @param loc string 表示区域的字符串(如 "H+HM" 等)
-- @param f string|function 过滤函数或名称,传入 MakeCardFilter 使用
-- @param v any 传入过滤函数的参数(可为单值或表)
-- @param n number|nil 可选数量条件(正数:≥n,负数:≤n,nil:返回过滤后的 Group)
-- @param ... 可选额外参数,传给 MakeCardFilter
-- @return Group|boolean 返回过滤后的 Group,或是否符合数量条件
function fugf.GetFilter(p, loc, f, v, n, ...)
return fugf.Filter(fugf.Get(p, loc), f, v, n, ...)
end
function fugf.GetNoP(loc, f, v, n, ...)
local val = {...}
return function(p)
return fugf.GetFilter(p, loc, f, v, n, table.unpack(val))
--- 构造一个带默认位置与过滤条件的 Group 过滤器函数
-- @param loc string 预设区域字符串(如 "M", "H+M")
-- @param f string|function 过滤函数或名称,用于 MakeCardFilter
-- @param v any 传给过滤函数的参数
-- @param n number|nil 数量要求(正:≥n,负:≤n,nil:返回过滤后的 Group)
-- @return function(p: number, ...): Group|boolean
-- 返回一个函数,该函数接受 p 及 ... 并执行过滤
function fugf.MakeFilter(loc, f, v, n)
return function(p, ...)
return fugf.GetFilter(p, loc, f, v, n, ...)
end
end
--- 从卡片组中选择卡片(带筛选、数量控制)
-- @param p number 控制玩家编号(0或1)
-- @param g Group|string 卡片组,或为关键字字符串(如 "HAND+MONSTER")
-- @param f function|number 筛选函数或最小选择数量
-- @param v any 若 f 为函数时传入的首个额外参数
-- @param min number? 最小选择数量(若 f 为 number,此值会从 f 推出)
-- @param max number? 最大选择数量(默认与 min 相同)
-- @param ... any 筛选函数的其它额外参数
-- @return Group 返回选择的卡片组(Group 类型)
function fugf.Select(p, g, f, v, min, max, ...)
if type(g) == "string" then g = fugf.Get(p, g) end -- _g is loc
local g_typ = fusf.CheckArgType("Select", 2, g, "Group/string")
if g_typ == "string" then g = fugf.Get(p, g) end
if type(f) == "number" then -- f is min
min, max = f, v
elseif f then -- f is func
......@@ -29,136 +88,369 @@ function fugf.Select(p, g, f, v, min, max, ...)
if #g == min then return g end
return g:Select(p, min, max, nil)
end
--- 从指定卡组中选择目标卡并设定为效果目标
-- @param p number 玩家编号(0 或 1)
-- @param g Group|string 卡片群组,或表示卡片来源的字符串(会调用 fugf.Get)
-- @param f function|string|number 可选过滤函数或数量
-- - 若为 number,代表 min 数量,v 将视为 max;
-- - 若为 function/string,则视为过滤器(将用于 Filter)
-- @param v any 过滤器附加参数
-- @param min number 最小选择数量(默认 1)
-- @param max number 最大选择数量(默认等于 min)
-- @param ... 其他传给过滤器的参数
-- @return Group 被选择并设定为目标的卡片群组
function fugf.SelectTg(p, g, f, v, min, max, ...)
local g = fugf.Select(p, g, f, v, min, max, ...)
Duel.SetTargetCard(g)
return g
end
--------------------------------------"Card function" (use in initial (no return
function fucf.AddCode(c, ...)
local codes = { }
for _, _code in ipairs({...}) do
if type(_code) == "string" then
for _, cod in _code:ForCut("fucf.AddCode") do
codes[#codes + 1] = fusf.M_chk(cod)
--------------------------------------"Card function"
--- 根据函数与参数构造一个卡片过滤器(返回一个 function(Card) -> bool)
-- @param func string|function 过滤函数或其名称,支援组合逻辑与反向(如 "~IsType+IsRace")
-- @param args table|string|number 传入的参数,可为字符串(含占位符)或具体值
-- @param ... 可选的占位参数(会传给 %1、%2 等)
-- @return function 过滤器函数,接受卡片并返回布林值
function fucf.MakeCardFilter(func, args, ...)
if not func then return function(c) return true end end
local typ = fusf.CheckArgType("MakeCardFilter", 1, func, "string/function")
-- trans args
if type(args) ~= "table" then args = { args } end
local arg_table, arg_count = {}, 0
for _, arg in ipairs(args) do
if type(arg) == "string" then
for _, v in fusf.ForTable(fusf.ParseCallGroupString(arg, { ... })) do
arg_count = arg_count + 1
arg_table[arg_count] = v
end
else
codes[#codes + 1] = fusf.M_chk(_code)
arg_count = arg_count + 1
arg_table[arg_count] = arg
end
end
-- func is function
if typ == "function" then
return function(c)
fusf.CheckArgType("MakeCardFilter", 1, c, "Card")
return func(c, table.unpack(arg_table, 1, arg_count))
end
end
aux.AddCodeList(c, table.unpack(codes))
end
fucf.ReviveLimit = Card.EnableReviveLimit
--------------------------------------"Card function" (use in Filter
function fucf.Filter(c, _func, ...)
return fusf.Creat_CF(_func, {...})(c)
--return fugf.Filter(Group.FromCards(c), func, {...}, 1)
end
fucf.IsRk = fusf.IsN("GetRank")
fucf.IsLv = fusf.IsN("GetLevel")
fucf.IsRLv = fusf.IsN("GetRitualLevel")
fucf.IsLk = fusf.IsN("GetLink")
fucf.IsAtk = fusf.IsN("GetAttack")
fucf.IsDef = fusf.IsN("GetDefense")
fucf.IsSeq = fusf.IsN("GetSequence")
fucf.IsPSeq = fusf.IsN("GetPreviousSequence")
function fucf.Not(c,val)
if aux.GetValueType(val) == "Card" then
-- func is string
local func_expr = fusf.InfixToPostfix(func, ...)
local fucf, Card, aux = fucf, Card, aux
if #func_expr == 1 then -- func just one
local find_func = fusf.FindTables(func_expr[1], fucf, Card, aux)
fusf.CheckType(find_func, "function")
return function(c)
fusf.CheckArgType("MakeCardFilter", 1, c, "Card")
return find_func(c, table.unpack(arg_table, 1, arg_count))
end
end
-- func is multi
return function(c)
fusf.CheckArgType("MakeCardFilter", 1, c, "Card")
local stack, arg_ind = {}, 1
for _, func in ipairs(func_expr) do
if func == "~" then
stack[#stack] = not stack[#stack]
elseif type(func) == "string" and #func == 1 then
local valR, valL = table.remove(stack), table.remove(stack)
local Cal = {
["+"] = valL and valR,
["-"] = valL and not valR,
["/"] = valL or valR
}
local temp = Cal[func]
if temp == nil then
error("invalid operators : " .. func)
end
stack[#stack + 1] = temp
else
if type(func) == "string" then
func = fusf.FindTables(func, fucf, Card, aux)
fusf.CheckType(func, "function")
end
local arg = arg_table[arg_ind]
arg_ind = arg_ind + 1
if type(arg) ~= "table" then arg = { arg, n = 1 } end
stack[#stack + 1] = func(c, table.unpack(arg, 1, arg.n))
end
end
return table.remove(stack)
end
end
--- 判断卡片是否满足给定的过滤条件
-- @param c Card 欲判断的卡片对象
-- @param f string|function 过滤器(可为函数或其名称)
-- @param args table|string|number 传入的参数,可为字符串(含占位符)或具体值
-- @param ... 可选的占位参数(会传给 %1、%2 等)
-- @return boolean 若卡片符合条件,返回 true,否则返回 false
function fucf.Filter(c, f, v, ...)
fusf.CheckArgType("Filter", 1, c, "Card")
return fucf.MakeCardFilter(f, v, ...)(c)
end
fucf.IsRk = fusf.MakeValCheck("GetRank")
fucf.IsLv = fusf.MakeValCheck("GetLevel")
fucf.IsRLv = fusf.MakeValCheck("GetRitualLevel")
fucf.IsLk = fusf.MakeValCheck("GetLink")
fucf.IsAtk = fusf.MakeValCheck("GetAttack")
fucf.IsDef = fusf.MakeValCheck("GetDefense")
fucf.IsSeq = fusf.MakeValCheck("GetSequence")
fucf.IsPSeq = fusf.MakeValCheck("GetPreviousSequence")
--- 判断卡片 c 是否“不等于”给定的 val
-- @param c Card 需要判断的卡片对象
-- @param val Card|Effect|Group|function 参考值,可以是卡片、效果、卡片组或过滤函数
-- @return boolean 如果 c 与 val 不匹配则返回 true,否则 false
--
-- - 若 val 是 Card,则比较是否为不同卡片
-- - 若 val 是 Effect,则比较 c 是否不是该效果的持有者
-- - 若 val 是 Group,则判断 c 是否不包含在该组内
-- - 若 val 是 function,则调用该函数传入 c,结果取反
-- - 其他情况返回 false
function fucf.Not(c, val)
fusf.CheckArgType("Not", 1, c, "Card")
local typ = fusf.CheckArgType("Not", 2, val, "Card/Effect/function")
if typ == "Card" then
return c ~= val
elseif aux.GetValueType(val) == "Effect" then
elseif typ == "Effect" then
return c ~= val:GetHandler()
elseif aux.GetValueType(val) == "Group" then
return not val:IsContains(c)
elseif aux.GetValueType(val) == "function" then
elseif typ == "function" then
return not val(c)
end
return false
end
--- 判断卡片是否属于指定系列(Set)
-- @param c Card 要检查的卡片
-- @param sets number|string 系列编号或由多个系列编号组成的字串(用 "/" 分隔)
-- @return boolean 若属于任一指定系列则返回 true,否则返回 false
--
-- fucf.IsSet(c, 0xfd1) → 检查是否属于系列 0xfd1
-- fucf.IsSet(c, "fd1+fd2") → 检查是否属于 0xfd1 或 0xfd2
function fucf.IsSet(c, sets)
if type(sets) == "number" then return c:IsSetCard(sets) end
for _, set in sets:ForCut("fucf.IsSet", "/") do
fusf.CheckArgType("IsSet", 1, c, "Card")
local typ = fusf.CheckArgType("IsSet", 2, sets, "number/string")
if typ == "number" then return c:IsSetCard(sets) end
for _, set in sets:ForCut("+") do
set = tonumber(set, 16)
if set and c:IsSetCard(set) then return true end
end
return false
end
function fucf.AbleTo(c,loc)
local func = {
--- 判断卡片是否能以指定方式送去特定区域
-- @param c Card 要检查的卡片
-- @param loc string 目标区域代号,可选前缀 "*" 表示作为 cost
-- 可用区域代号:
-- "H" = 手牌(Hand)
-- "D" = 卡组(Deck)
-- "G" = 墓地(Grave)
-- "R" = 除外(Remove)
-- "E" = 额外卡组(Extra)
-- 例:"*G" 表示是否能作为 cost 被送去墓地
-- @return boolean 若能送去指定区域,则返回 true,否则返回 false
-- @raise 若 loc 非法,将抛出错误
function fucf.AbleTo(c, loc)
fusf.CheckArgType("AbleTo", 1, c, "Card")
fusf.CheckArgType("AbleTo", 2, loc, "string")
local iscos = loc:match("*")
loc = loc:match("[HDGRE]")
if not loc then error("InfixToPostfix : param 1 is invalid param", 2) end
local locs = {
["H"] = "Hand" ,
["D"] = "Deck" ,
["G"] = "Grave" ,
["R"] = "Remove",
["E"] = "Extra" ,
}
local iscos = string.sub(loc,1,1) == "*"
if iscos then loc = string.sub(loc,2) end
return Card["IsAbleTo"..func[loc]..(iscos and "AsCost" or "")](c)
return Card["IsAbleTo"..locs[loc]..(iscos and "AsCost" or "")](c)
end
function fucf.CanSp(c, e, typ, tp, nochk, nolimit, pos, totp, zone)
if not tp then tp = e:GetHandlerPlayer() end
if typ == SUMMON_TYPE_RITUAL or typ == "RI" then
--- 检查 c 是否可以特殊召唤
-- @param c Card 要特殊召唤的卡片
-- @param e Effect 用于特殊召唤的效果
-- @param typ number|string 特召方式类型(数值或缩写,如 "RI" 表示仪式召唤)
-- @param tp number 特召的执行玩家(预设为 e:GetHandlerPlayer())
-- @param pos number|string 特召时卡片的表示形式(预设为 POS_FACEUP,可用字串)
-- @param lg Card |Group|nil 检查 lg 离开后的区域数量(有的话)
-- @param nochk boolean 是否忽略召唤条件(预设视 typ 而定)
-- @param nolimit boolean 是否忽略召唤次数限制(预设视 typ 而定)
-- @param totp number 特召后控制该卡的玩家(预设为 tp)
-- @param zone number 特召位置的区域 mask(预设为 0xff,即所有主怪兽区)
-- @return boolean 是否可以被特殊召唤
function fucf.CanSp(c, e, typ, tp, pos, lg, nochk, nolimit, totp, zone)
fusf.CheckArgType("CanSp", 1, c, "Card")
if not c:IsType(TYPE_MONSTER) then return false end
fusf.CheckArgType("CanSp", 2, e, "Effect")
local _, tp = fusf.CheckArgType("CanSp", 4, tp or e:GetHandlerPlayer(), "player")
fusf.CheckArgType("CanSp", 6, lg, "nil/Card/Group")
if c:IsLocation(LOCATION_EXTRA) then
if Duel.GetMZoneCount(tp, lg) == 0 then return false end
else
if Duel.GetLocationCountFromEx(tp, tp, lg, c) == 0 then return false end
end
-- default chk
local _, nochk = fusf.CheckArgType("CanSp", 7, nochk, "nil/boolean")
local _, nolimit = fusf.CheckArgType("CanSp", 8, nolimit, "nil/boolean")
-- special default
local _, typ = fusf.CheckArgType("CanSp", 3, typ or 0, "number/string")
if typ == "RI" then
typ = SUMMON_TYPE_RITUAL
nochk = nochk or false
nolimit = nolimit or true
if nochk == nil then nochk = false end
if nolimit == nil then nolimit = true end
end
return c:IsCanBeSpecialSummoned(e, typ, tp, nochk or false, nolimit or false, pos or POS_FACEUP, totp or tp,zone or 0xff)
-- normal default
if nochk == nil then nochk = false end
if nolimit == nil then nolimit = false end
--
local _, pos = fusf.CheckArgType("CanSp", 5, pos or POS_FACEUP, "number/string")
pos = fusf.ParseConstantKey("pos", pos)
local _, totp = fusf.CheckArgType("CanSp", 9, totp or tp, "player")
local _, zone = fusf.CheckArgType("CanSp", 10, zone or 0xff, "number")
return c:IsCanBeSpecialSummoned(e, typ, tp, nochk, nolimit, pos, totp, zone)
end
function fucf.IsCode(c, _cod)
local cod
if aux.GetValueType(_cod) == "number" then
cod = {_cod}
elseif aux.GetValueType(_cod) == "string" then
cod = _cod:Cut("fucf.IsCode", "+")
elseif aux.GetValueType(_cod) == "table" then
cod = _cod
--- 判断 c 是否属于指定的卡号
-- @param c Card 要检查的卡片
-- @param cod number|string|table|Effect|Card|Group 可为单个卡号、以 "+" 分隔的字符串、卡号数组,或包含卡的 Group
-- @return boolean 是否匹配任意一个卡号
function fucf.IsCode(c, cod)
fusf.CheckArgType("IsCode", 1, c, "Card")
return c:IsCode(table.unpack(fusf.GetCodeTable(cod)))
end
--- 判断 c 是否有卡名记述
-- @param c Card 要检查的卡片
-- @param cod number|string|table|Effect|Card|Group 为单个卡号、以 "+" 分隔的字符串、卡号数组,或包含卡的 Group
-- @return boolean 是否匹配任意一个卡名记述
function fucf.HasCode(c, cod)
fusf.CheckArgType("HasCode", 1, c, "Card")
local code_list = c.card_code_list
if not code_list then return false end
for _, code in ipairs(fusf.GetCodeTable(cod)) do
if code_list[code] then return true end
end
for i,v in ipairs(cod) do
cod[i] = fusf.M_chk(tonumber(v))
return false
end
--- 判断是否能将多张装备卡(eg)装备到指定怪兽卡 c
-- @param c Card 欲装备的目标怪兽
-- @param eg Effect|Card|Group 欲装备的卡(可为单一卡片或卡片群组)
-- @param tp number? 控制玩家(预设为 c 的控制者)
-- @return boolean 是否所有卡片都能合法装备给 c
function fucf.CanEq(c, eg, tp)
fusf.CheckArgType("CanEq", 1, c, "Card")
eg = fusf.ToGroup(eg)
tp = tp or c:GetControler()
fusf.CheckArgType("CanEq", 3, tp, "player")
if Duel.GetLocationCount(tp, LOCATION_SZONE) < #eg then return false end
for ec in aux.Next(eg) do
if not fucf.CanBeEq(ec, tp, c, true) then return false end
end
return c:IsCode(table.unpack(cod))
return true
end
function fucf.HasCode(c, _cod)
if not c.card_code_list then return false end
local cod, has
if aux.GetValueType(_cod) == "number" then
cod = {_cod}
elseif aux.GetValueType(_cod) == "string" then
cod = _cod:Cut("fucf.HasCode", "+")
--- 判断卡片是否可以作为装备卡装备给指定对象
-- @param c Card 欲检查的装备卡(可为怪兽或装备魔法)
-- @param tp number? 预设视为控制装备卡的玩家,若 ec 存在则预设为 ec 的控制者
-- @param ec Card? 欲装备的目标怪兽卡
-- @param chk_loc boolean? 若为 true,跳过地区检查(例如已有预留 zone)
-- @return boolean 是否可被当作装备卡使用
function fucf.CanBeEq(c, tp, ec, chk_loc)
fusf.CheckArgType("CanBeEq", 1, c, "Card")
if ec then
fusf.CheckArgType("CanBeEq", 3, ec, "Card")
tp = tp or ec:GetControler()
end
for i,v in ipairs(cod) do
has = has or c.card_code_list[fusf.M_chk(tonumber(v))]
fusf.CheckArgType("CanBeEq", 2, tp, "player")
if not (chk_loc or Duel.GetLocationCount(tp, LOCATION_SZONE) > 0) then return false end
if c:IsLocation(LOCATION_ONFIELD) then
if not c:IsFaceup() then return false end
else
if not c:CheckUniqueOnField(tp) then return false end
end
return has
if c:IsType(TYPE_MONSTER) then
return c:IsControler(tp) or c:IsAbleToChangeControler()
elseif c:IsType(TYPE_EQUIP) then
return c:CheckEquipTarget(ec)
end
error("CanBeEq : mismatch card type", 2)
end
function fucf.CanEq(c, tp)
return c:CheckUniqueOnField(tp) and Duel.GetLocationCount(tp, LOCATION_SZONE) > 0
--- 检查卡片 c 的种类为 cod 的标识效果的数量比较 n 的结果
-- @param c Card 要检查的卡片
-- @param cod number|string 旗标代码,可为数值或字串(将会正规化)
-- @param n number 可选,若提供则检查是否具有至少 n 层该 Flag
-- @return boolean Flag count 或 count 比较 n
function fucf.IsFlag(c, cod, n)
fusf.CheckArgType("IsFlag", 1, c, "Card")
fusf.CheckArgType("IsFlag", 3, n, "number")
return fusf.GetFlag(c, cod, n)
end
function fucf.IsFlagLab(c, _cod, _lab)
return c:GetFlagEffectLabel(_cod) == _lab
--- 检查卡片 c 的种类为 cod 的标识效果的 Label 是否是 lab
-- @param c Card 要检查的卡片
-- @param cod number|string 旗标代码,可为数值或字串(将会正规化)
-- @param lab number 要比较的 Label
-- @return boolean Label 或 Label == lab
function fucf.IsFlagLab(c, cod, lab)
fusf.CheckArgType("IsFlagLab", 1, c, "Card")
fusf.CheckArgType("IsFlagLab", 3, lab, "number")
return fusf.GetFlagLabel(c, cod, lab)
end
--- 检查卡片 c 的当前控制者是否是 p
-- @param c Card 要检查的卡片
-- @param p number 要比较的控制者玩家(0 或 1)
-- @return boolean 卡片 c 的当前控制者是否是 p
function fucf.IsCon(c, p)
if type(p) == "string" then p = tonumber(p) end
fusf.CheckArgType("IsCon", 1, c, "Card")
fusf.CheckArgType("IsCon", 2, p, "player")
return c:IsControler(p)
end
fucf.InGroup = function(c, g) return g:IsContains(c) end
--- 检查卡片组 g 中是否存在卡片 c
-- @param c Card 要检查的卡片
-- @param g Group 要检查的卡片组
-- @return boolean 卡片组 g 中是否存在卡片 c
function fucf.InG(c, g)
fusf.CheckArgType("InG", 1, c, "Card")
fusf.CheckArgType("InG", 2, g, "Group")
return g:IsContains(c)
end
--- 检查卡片 c 没有受王家长眠之谷的影响,并且检查当前连锁能否被无效
-- @param c Card 要检查的卡片
-- @return boolean 卡片 c 没有受王家长眠之谷的影响,并且当前连锁能被无效
function fucf.GChk(c)
fusf.CheckArgType("GChk", 1, c, "Card")
return not c:IsHasEffect(EFFECT_NECRO_VALLEY) and Duel.IsChainDisablable(0)
end
--- 检查卡片 c 当前位置是否是 loc
-- 注:loc == M 时,怪兽召唤(广义的)之际或被无效会返回 false
-- loc == S 时,魔陷发动无效会返回false
-- @param c Card 要检查的卡片
-- @param loc string|number 区域表示
-- @return boolean 检查卡片 c 当前位置是否是 loc
function fucf.IsLoc(c, loc)
fusf.CheckArgType("IsLoc", 1, c, "Card")
loc = fusf.GetLoc(loc)
return c:IsLocation(loc)
end
--- 检查卡片 c 之前的位置是否是 loc
-- @param c Card 要检查的卡片
-- @param loc string|number 区域表示
-- @return boolean 检查卡片 c 之前的位置是否是 loc
function fucf.IsPLoc(c, loc)
fusf.CheckArgType("IsPLoc", 1, c, "Card")
loc = fusf.GetLoc(loc)
return c:IsPreviousLocation(loc)
end
fucf.TgChk = Card.IsCanBeEffectTarget
fucf.GChk = function(c) return not c:IsHasEffect(EFFECT_NECRO_VALLEY) end
fucf.IsImm = Card.IsImmuneToEffect
fucf.IsPCon = Card.IsPreviousControler
fucf.IsLoc = function(c,loc) return c:IsLocation(fusf.Get_Loc(loc)) end
fucf.IsPLoc = function(c,loc) return c:IsPreviousLocation(fusf.Get_Loc(loc)) end
fucf.IsRea = fusf.Is_Cons("GetReason", "rea")
fucf.IsTyp = fusf.Is_Cons("GetType", "typ")
fucf.IsSTyp = fusf.Is_Cons("GetSummonType", "styp")
fucf.IsOTyp = fusf.Is_Cons("GetOriginalType", "typ")
fucf.IsAtt = fusf.Is_Cons("GetAttribute", "att")
fucf.IsRac = fusf.Is_Cons("GetRace", "rac")
fucf.IsPos = fusf.Is_Cons("GetPosition", "pos", function(card_val, val) return card_val | val == val end)
fucf.IsPPos = fusf.Is_Cons("GetPreviousPosition", "pos", function(card_val, val) return card_val | val == val end)
fucf.IsRea = fusf.MakeConsCheck("GetReason", "rea", "&")
fucf.IsTyp = fusf.MakeConsCheck("GetType", "typ", "&")
fucf.IsSTyp = fusf.MakeConsCheck("GetSummonType", "styp", "&")
fucf.IsOTyp = fusf.MakeConsCheck("GetOriginalType", "typ", "&")
fucf.IsAtt = fusf.MakeConsCheck("GetAttribute", "att", "&")
fucf.IsRac = fusf.MakeConsCheck("GetRace", "rac", "&")
fucf.IsPos = fusf.MakeConsCheck("GetPosition", "pos", "|")
fucf.IsPPos = fusf.MakeConsCheck("GetPreviousPosition", "pos", "|")
--[[
---------------------------------------------------------------- procedure
function fucf.RMFilter(c, rf, e, tp, g1, g2, level_function, greater_or_equal, chk)
if rf and not rf(c, e, tp, chk) then return false end
if not fucf.Filter(rc, "IsTyp+CanSp", "RI+M", {e, SUMMON_TYPE_RITUAL, tp, false, true}) then return false end
if not fucf.Filter(rc, "IsTyp+CanSp", "RI+M", {e, "RI"}) then return false end
local g = g1:Filter(Card.IsCanBeRitualMaterial, c, c) + (g2 or Group.CreateGroup())
g = g:Filter(c.mat_filter or aux.TRUE, c, tp)
local lv = level_function(c)
......@@ -166,4 +458,5 @@ function fucf.RMFilter(c, rf, e, tp, g1, g2, level_function, greater_or_equal, c
local res = g:CheckSubGroup(Auxiliary.RitualCheck, 1, lv, tp, c, lv, greater_or_equal)
Auxiliary.GCheckAdditional = nil
return res
end
\ No newline at end of file
end
--]]
\ No newline at end of file
dofile("expansions/script/c20099999.lua")
if fusf then return end
fusf = { }
dofile("expansions/script/c20099999.lua")
fusf = {}
fudf = {}
fudf.__index = fudf
--------------------------------------"Debug function"
--- 若处于调试模式则输出函数调用信息和参数
-- @param log fudf|fuef|number log 实例/fuef 实例/层级数字
-- @param name string 函数名称
-- @param ... any 参数列表
-- @return fudf log 实例
function fudf.StartLog(log, name, ...)
if not fuef.DebugMode then return fudf end
local typ = fusf.CheckArgType("StartLog", 1, log, "number/fudf/fuef")
fusf.CheckArgType("StartLog", 2, name, "string")
local lv
if typ == "number" then
lv = log
elseif typ == "fuef" then
lv = log.isinit and 2 or 0
else
lv = log.depth
end
local indent = ("\t"):rep(lv)
local arg_str = ""
local args = {...}
local len = select("#", ...)
if len > 0 then
local out = {}
for i = 1, len do
local val = args[i]
local typ = type(val)
if typ == "userdata" then
typ = fusf.Type(val)
elseif typ == "number" or typ == "boolean" then
typ = tostring(val)
elseif typ == "string" then
typ = '"'..val..'"'
end
table.insert(out, typ)
end
arg_str = table.concat(out, ", ")
end
Debug.Message(("\t%s[CALL] : %s(%s)"):format(indent, name, arg_str))
return setmetatable({ depth = lv + 1 }, fudf)
end
--- 若处于调试模式则印出 msg
-- @param msg any 调试信息
function fudf:Info(msg)
if not fuef.DebugMode then return end
local indent = ("\t"):rep(self.depth)
Debug.Message("\t"..indent.."[INFO] : "..msg)
end
--- 若处于调试模式则印出 msg
-- @param msg any 调试信息
function fusf.Info(msg)
if not fuef.DebugMode then return end
Debug.Message("\t[INFO] : "..msg)
end
--------------------------------------"String function"
function string:Cut(_from, _cut)
_cut = _cut or "," -- default
if type(self) ~= "string" or type(_cut) ~= "string" then
local _p = (type(self) ~= "string" and "1" or "")..(type(_cut) ~= "string" and "2" or "")
Debug.Message("Invalid parameter ".._p.." in string:Cut <- ".._from)
return nil
end
local str, list, char = self.._cut, {}, ""
for unit in str:gmatch(".") do
if unit == _cut then
table.insert(list, char)
char = ""
--- 将字串以指定字元切割,保留空字串片段
-- @param cut string 分隔符,预设为逗号 ","
-- @return table 字串片段阵列,连续分隔符中间的空字串也会被保留
-- @raise 当 self 或 cut 非字串型态时抛出错误
function string:Cut(cut)
cut = cut or ","
if type(cut) ~= "string" then
error("Cut : param 1 should be string, got "..type(cut), 2)
end
local res, part = {}, ""
for char in (self .. cut):gmatch(".") do
if char == cut then
table.insert(res, part)
part = ""
else
char = char..unit
part = part .. char
end
end
return list
return res
end
function string:ForCut(_from, _cut)
_cut = _cut or "," -- default
if type(self) ~= "string" or type(_cut) ~= "string" then
local _p = (type(self) ~= "string" and "1" or "")..(type(_cut) ~= "string" and "2" or "")
Debug.Message("Invalid parameter ".._p.." in string:ForCut <- ".._from)
return nil
end
local list = self:Cut("string:ForCut <- ".._from, _cut)
--- 返回一个迭代器,遍历按指定分隔符切割后的字符串片段
-- @param cut string 分隔符,默认值为逗号 ","
-- @return function 迭代函数,每次调用返回当前索引和对应的字符串片段
-- @usage
-- for i, s in ("a,b,,c"):ForCut(",") do
-- print(i, s)
-- end
function string:ForCut(cut)
local list = self:Cut(cut)
local ind, max = 0, #list
return function()
if ind >= max then return nil end
......@@ -35,159 +101,320 @@ function string:ForCut(_from, _cut)
return ind, list[ind]
end
end
--------------------------------------"Error function"
--- 检查 from 函数的参数是否为空,若为空则报错
-- @param from string 呼叫来源,用于错误提示
-- @param ... any 任意数量的参数
-- @raise 若参数皆为空,则触发错误,提示呼叫位置
function fusf.CheckEmptyArgs(from, ...)
if not fusf.IsEmpty(...) then return end
error(from.." : Argument cannot be empty", 3)
end
--- 检查 from 函数的第 i 个参数 val 的类型是否为 typs,否则报错
-- @param from string 呼叫来源,用于错误提示
-- @param i number 参数位置(从 1 开始)
-- @param val any 要检查的值
-- @param typs string 允许的类型,类型名以 `/` 分隔(如 "string/number")
-- @return string, any 若匹配,返回其匹配的类型及原值
-- @raise 若类型不符,报错并显示位置与类型资讯
function fusf.CheckArgType(from, i, val, typs)
local is_typ, typ = pcall(fusf.CheckTypes, val, typs)
if is_typ then return typ, val end
local err_msg = "%s : param %d should be %s, got %s"
err_msg = err_msg:format(from, i, typs, fusf.Type(val))
error(err_msg, 3)
end
--------------------------------------"Support function"
function fusf.Debug(msg)
if fuef.DebugMode then Debug.Message(msg) end
end
function fusf.ValToDebug(...)
local tab, res = {...}, ""
for i = 1, select("#", ...) do
if type(tab[i]) == "userdata" then
res = res..", "..aux.GetValueType(tab[i])
else
res = res..", "..tostring(tab[i])
--- 更精确地取得物件的类型名称,支援 Card/Effect/Group/fuef 等
-- @param val 任意值
-- @return string 自定义类型名称或 Lua 原生类型名称
function fusf.Type(val)
local typ = type(val)
if typ == "userdata" then
return aux.GetValueType(val)
elseif typ == "table" then
local metatable = getmetatable(val)
if metatable == fuef then
return "fuef"
elseif metatable == fudf then
return "fudf"
end
end
return res:sub(3)
return typ
end
function fusf.Getcm(val)
local c = fusf.ToCard(val, true)
if getmetatable(val) == fuef then
c = val.e:GetOwner()
end
if not c then
Debug.Message("Cant Get cm "..tostring(val))
return { }
end
return _G["c"..val.e:GetOwner():GetCode()]
end
function fusf.CutHex(val, add)
-- 转换 typ 等并分割
if type(val) == "string" then return val end
add = add or "+"
local parts = {}
local bit = 1
while val > 0 do
if val % 2 == 1 then
table.insert(parts, string.format("0x%X", bit))
--- 验证 val 的类型是否为 typ,若为 "player" 类型则进一步检查值是否合法(0 或 1)
-- @param val any 欲检查的值
-- @param typ string 预期的类型名称(如 "Card", "string", "number", "player" 等)
-- @return any 若类型正确,返回原值
-- @raise 若类型不符,会根据 arg_ind 抛出更明确的错误(包含参数编号)
function fusf.CheckType(val, typ)
if type(typ) ~= "string" then
error(("param %d should be %s, got %s"):format(2, "string", type(typ)), 2)
end
if typ == "player" then
if val == 0 or val == 1 then
return val
else
error("invalid player index '"..val.."' (expected 0 or 1)", 3)
end
val = math.floor(val / 2)
bit = bit * 2
end
for i = 1, math.floor(#parts / 2) do
parts[i], parts[#parts - i + 1] = parts[#parts - i + 1], parts[i]
local val_type = fusf.Type(val)
if val_type == typ then return val end
error(("should be %s, got %s"):format(typ, val_type), 3)
end
--- 验证 val 的 type 是否在 typs 内(以 "/" 分隔)
-- @param val any 欲检查的值
-- @param typs string 多个类型名称组成的字符串,如 "number/string"
-- @return string, any 若匹配,返回其匹配的类型及原值
-- @raise 若不符合任何类型,会抛出详细错误信息
function fusf.CheckTypes(val, typs)
typs = fusf.CheckType(typs, "string")
local val_type = fusf.Type(val)
for _, typ in typs:ForCut("/") do
if pcall(fusf.CheckType, val, typ) then return typ, val end
end
return table.concat(parts, add)
error(("should be %s, got %s"):format(typs, val_type), 3)
end
function fusf.ToCard(val, is_owner)
local g = fusf.ToGroup(val, is_owner)
if g then return g:GetFirst() end
return false
--- 从 fucs 下指定子表中查找常量值,或返回整个表
-- @param table_key string 欲查找的子表名称(如 "cod")
-- @param key? string 欲查找的键名称(可省略,省略时返回整个子表)
-- @return any 找到的值或整个子表
-- @raise 若子表不存在、键无效,则报错
function fusf.Findfucs(table_key, key)
fusf.CheckArgType("Findfucs", 1, table_key, "string")
local const_table = fusf.CheckType(fucs[table_key], "table")
if not key then return const_table end
return fusf.FindTables(key, const_table)
end
--- 在多个表中依序查找键值
-- @param key string 欲查找的键名称
-- @param ... table 可变参数,每个都是待查找的表格
-- @return any 若找到键,返回对应的值;否则抛出错误
function fusf.FindTables(key, ...)
fusf.CheckArgType("FindTables", 1, key, "string")
for i, tab in ipairs{ ... } do
fusf.CheckArgType("FindTables", i + 1, tab, "table")
if tab[key] then return tab[key] end
end
error("unknown constant key '"..key.."' in table", 2)
end
--- 将 val 转为对应的 Group
-- @param val Card|Effect|Group 欲处理的对象
-- @param is_owner boolean 若为 Effect 时,是否取其 owner 而非 handler
-- @return Group 对应的 Group
-- @raise 若类型非 Card/Effect/Group,则抛出错误
function fusf.ToGroup(val, is_owner)
local typ = aux.GetValueType(val)
local typ = fusf.CheckArgType("ToGroup", 1, val, "Card/Effect/Group")
if typ == "Effect" then
return Group.FromCards(is_owner and val:GetOwner() or val:GetHandler())
elseif typ == "Card" then
return Group.FromCards(val)
elseif typ == "Group" then
return val
end
return false
return val -- typ == "Group"
end
--- 将 val 转为对应的卡片组 table
-- @param val Card|Effect|Group 欲处理的对象
-- @return table 对应的 table
-- @raise 若无法转换为非空 Group,则抛出错误
function fusf.ToGroupTable(val)
local g = fusf.ToGroup(val)
if #g == 0 then
error("ToGroup(" .. fusf.Type(val) .. ") is empty Group", 2)
end
local tab = {}
for c in aux.Next(g) do
table.insert(tab, c)
end
return tab
end
--- 将 val 转为对应的 Card
-- @param val Card|Effect|Group 欲处理的对象
-- @param is_owner boolean 若为 Effect 时,是否取其 owner 而非 handler
-- @return Card 对应的 Card
-- @raise 若无法转换为非空 Group,则抛出错误
function fusf.ToCard(val, is_owner)
local c = fusf.ToGroup(val, is_owner):GetFirst()
if c then return c end
error("ToGroup(" .. fusf.Type(val) .. ") is empty Group", 2)
end
function fusf.IsNil(...)
local vals = {...}
if #vals == 0 then return true end
--- 判断传入值是否为 nil, "", {}
-- @param ... 任意数量的参数
-- @return boolean 是否为 nil, "", {}
function fusf.IsEmpty(...)
local vals = { ... }
if select("#", ...) == 0 then return true end
vals = #vals == 1 and vals[1] or vals
if type(vals) == "string" then
return vals == "" -- 非空字串
return vals == ""
elseif type(vals) == "table" then
return not next(vals) -- 表有內容
end
return not vals
end
function fusf.NotNil(...)
return not fusf.IsNil(...)
end
function fusf.Get_Constant(_constable, _vals)
-- string chk
if type(_vals) ~= "string" then return _vals end
local res = 0
-- cod chk
if _constable == "cod" then
-- EVENT_CUSTOM
if _vals:match("CUS") then
_vals = _vals:sub(5)
-- owner code or number
res = EVENT_CUSTOM + fusf.M_chk(_vals)
-- EVENT_PHASE or EVENT_PHASE_START
elseif _vals:match("PH") then
for _, val in _vals:ForCut("Get_Constant_1", "+") do
local _constable = val:match("PH") and "cod" or "pha"
res = res + fucs[_constable][val]
return next(vals) == nil
end
return vals == nil
end
--- 根据 val 取得其对应的全局 cm 表
-- @param val Card|Effect|Group|fuef
-- @return table 对应的 cm 表
-- @raise 若无法取得卡片对象则抛出错误
function fusf.Getcm(val)
local typ = fusf.CheckArgType("Getcm", 1, val, "Card/Effect/Group/fuef")
local c
if typ == "fuef" then
c = fusf.CheckType(val.e, "Effect"):GetOwner()
else
c = fusf.ToCard(val, true)
end
return fusf.CheckType(_G["c" .. c:GetCode()], "table")
end
--- 规范化卡号,若小于 1000,则加上 base (20000000)
-- @param id number|string
-- @param base number 可选,若卡号 < 1000,将加上此值(默认 20000000)
-- @return number 规范化后的卡号
-- @raise 若 id 无法转为数字,或 base 非数字,将抛出错误
function fusf.NormalizeID(id, base)
id = tonumber(id)
fusf.CheckArgType("NormalizeID", 1, id, "number")
fusf.CheckArgType("NormalizeID", 2, base, "nil/number")
base = base or 20000000
return id < 1000 and id + base or id
end
--- 解析 val 并回传一个对应的卡号 table
-- @param val any 可以是 Group、Card、Effect、number、string(用 + 分隔的代码)、table 等
-- @return table<number> 包含标准化过的卡号 table
function fusf.GetCodeTable(val)
local result = {}
local to_g, g = pcall(fusf.ToGroup, val)
if to_g then
for gc in aux.Next(g) do
for _, cod in ipairs{ gc:GetCode() } do
table.insert(result, cod)
end
end
if res ~= 0 then return res end
return result
end
-- find _constable
local vals, cons, des = _vals:Cut("Get_Constant_2", "+"), fucs[_constable]
for i = #vals, 1, -1 do
local _val = vals[i]
des = fucs.des[_val] or des
res = res + cons[_val]
local code_table = {}
local typ = fusf.CheckTypes(val, "number/string/table")
if typ == "number" then
code_table = { val }
elseif typ == "string" then
code_table = val:Cut("+")
elseif typ == "table" then
code_table = val
end
return res, des
for _, cod in ipairs(code_table) do
table.insert(result, fusf.NormalizeID(cod))
end
if #result > 0 then return result end
error("GetCodeTable : get empty table", 3)
end
function fusf.Get_Loc(_loc_s, _loc_o, _from)
-- nil chk
if not fusf.NotNil(_loc_s, _loc_o) then
Debug.Message("Get_Loc <- ".._from)
return nil
end
local p_loc = {0, 0}
-- _loc_o chk
if _loc_o then p_loc[2] = _loc_o end
-- _loc_s string chk
if type(_loc_s) ~= "string" then
p_loc[1] = _loc_s
return table.unpack(p_loc)
end
-- _loc_s is string and find fucs.ran
local _res = 0
for i, loc in _loc_s:ForCut("Get_Loc", "+") do
for j = 1, #loc do
p_loc[i] = p_loc[i] + fucs.ran[loc:sub(j,j):upper()]
--- 将整数转换为 16 进制 string 表示(以 ", " 或指定符号连接)
-- @param val number|string 欲转换的数值,若为字串则直接返回
-- @return string 分解后的 16 进制表示,如 "0x1, 0x2, 0x8"
-- @raise 当参数类型不符时抛出错误
function fusf.CutHex(val)
local typ = fusf.CheckArgType("CutHex", 1, val, "string/number")
if typ == "string" then return val end
local parts, bit = {}, 1
while val > 0 do
if val % 2 == 1 then
table.insert(parts, 1, string.format("0x%X", bit))
end
val = math.floor(val / 2)
bit = bit * 2
end
return table.unpack(p_loc)
return table.concat(parts, ", ")
end
function fusf.M_chk(val) -- val : number|string
val = tonumber(val)
if val < 19999999 then return val + 20000000 end
return val
--- 转换成前后两个区域后传回(self_loc包含两个区域时以+分开)
-- @param self_loc string|number 自己的区域表示,可为形如 "HM+M" 的字串,或直接为数值
-- @param oppo_loc number|nil 对方的区域数值
-- @return number, number 自己和对方的区域值
-- @raise 当 self_loc 和 oppo_loc 同时为空时抛出错误
-- @raise 当 oppo_loc 非 number or nil 时抛出错误
-- @raise 当 self_loc 字符无法在 fucs.ran 中找到对应值时抛出错误
function fusf.GetLoc(self_loc, oppo_loc)
fusf.CheckEmptyArgs("GetLoc", self_loc, oppo_loc)
local typ = fusf.CheckArgType("GetLoc", 1, self_loc, "string/number")
fusf.CheckArgType("GetLoc", 2, oppo_loc, "nil/number")
local loc_list = { 0, 0 }
if oppo_loc then
loc_list[2] = oppo_loc
end
if typ == "number" then
loc_list[1] = self_loc
return table.unpack(loc_list)
end
for i, locs in self_loc:ForCut("+") do
for j = 1, #locs do
local ch = locs:sub(j, j):upper()
loc_list[i] = loc_list[i] + fusf.Findfucs("ran", ch)
end
end
return table.unpack(loc_list)
end
function fusf.PostFix_Trans(_str, ...)
local vals, res, temp, i = {...}, { }, { }, 1
while i <= #_str do
local ch = _str:sub(i, i)
--- 将带变量占位符的中缀表达式转换成后缀表达式(逆波兰表达式)
-- 支持字母变量、占位符 %n 替换参数、括号以及 + - / ~ 运算符
-- @param expr string 形如 "A+(B-%1)/C" 的表达式字符串
-- @param ... 传入参数,用于替换表达式中的 %n 占位符(n为数字)
-- @return table 后缀表达式数组,元素为字符串或函数
function fusf.InfixToPostfix(expr, ...)
fusf.CheckArgType("InfixToPostfix", 1, expr, "string")
if expr == "" then error("InfixToPostfix : param 1 is invalid param", 2) end
local args, res, temp, i = { ... }, {}, {}, 1
while i <= #expr do
local ch = expr:sub(i, i)
if ch:match("%a") then
_, i, ch = _str:find("(%a+)", i)
_, i, ch = expr:find("(%a+)", i)
table.insert(res, ch)
elseif ch == "%" then
_, i, ch = _str:find("(%d+)", i)
ch = vals[tonumber(ch)]
if type(ch) == "boolean" then
local b = ch
ch = function() return b end
_, i, ch = expr:find("(%d+)", i)
if not ch then
error("'%' must be followed by a number at position : " .. expr, 2)
end
table.insert(res, ch)
local val = args[tonumber(ch)]
if val == nil then
error("argument %" .. ch .. " is nil", 2)
elseif type(val) == "boolean" then
local b = val
val = function() return b end
end
table.insert(res, val)
elseif ch == "(" or ch == "~" then
table.insert(temp, ch)
elseif ch == ")" then
while #temp > 0 and temp[#temp] ~= "(" do
table.insert(res, table.remove(temp))
local found_left_paren = false
while #temp > 0 do
local op = table.remove(temp)
if op == "(" then
found_left_paren = true
break
else
table.insert(res, op)
end
end
if not found_left_paren then
error("unexpected symbol near ')' : " .. expr, 2)
end
table.remove(temp)
elseif ch == "+" or ch == "-" then
while #temp > 0 and temp[#temp] ~= "(" do
table.insert(res, table.remove(temp))
......@@ -198,6 +425,8 @@ function fusf.PostFix_Trans(_str, ...)
table.insert(res, table.remove(temp))
end
table.insert(temp, ch)
else
error("unexpected character '" .. ch .. "' : " .. expr, 2)
end
if temp[#temp] == "~" and ch:match("^[%a%)%%]") then
table.insert(res, table.remove(temp))
......@@ -205,260 +434,644 @@ function fusf.PostFix_Trans(_str, ...)
i = i + 1
end
while #temp > 0 do
table.insert(res, table.remove(temp))
local op = table.remove(temp)
if op == "(" then
error("expected ')' to close '(' : " .. expr, 2)
end
table.insert(res, op)
end
return res
end
function fusf.IsN(_func)
return function(_c, _val, _exval)
local c_val = Card[_func](_c, _exval)
if type(_val) == "string" then
local oper, _val = _val:match("[%+%-]"), _val:match("%d+")
_val = tonumber(_val)
if oper == "+" then
return c_val >= _val
elseif oper == "-" then
return c_val <= _val
end
return c_val == _val
--- 构造一个用于判断卡片某个数值大小关系的函数
-- @param getter string Card 的函数名,如 "GetLevel"
-- @return function(c, target, ...) -> boolean
function fusf.MakeValCheck(getter)
local card_func = fusf.CheckType(Card[getter], "function")
local from = "MakeValCheck("..getter
return function(c, target, ...)
fusf.CheckArgType(from, 1, c, "Card")
local typ = fusf.CheckArgType(from, 2, target, "number/string")
local card_val = fusf.CheckType(card_func(c, ...), "number")
if typ == "number" then
return target > 0 and card_val == target or card_val <= -target
end
local oper = target:match("[%+%-]")
local num = tonumber(target:match("%d+"))
if oper == "+" then
return card_val >= num
elseif oper == "-" then
return card_val <= num
end
if _val > 0 then return c_val == _val end
return c_val <= -_val -- _val = -n
return card_val == num
end
end
function fusf.Is_Cons(_func, _key, _cal)
_cal = _cal or function(card_val, val) return card_val & val == val end
return function(c, _cons)
if type(_cons) ~= "string" then return _cal(Card[_func](c), _cons) end
local res, valL, valR = { }
for _, val in ipairs(fusf.PostFix_Trans(_cons)) do
--- 构造一个用于判断卡片某个数值是否符合常量条件的函数
-- @param getter string 要呼叫的 Card 方法名称,例如 "GetType"
-- @param table_key string 常量表名称,如 "cod"、"pha"(会传给 fusf.FindTables)
-- @param cal_type string? 校验类型("&" 代表包含、"|" 代表允许并集),预设为 "|"
-- @return function(card: Card, value: string|number): boolean 回传是否符合条件
function fusf.MakeConsCheck(getter, table_key, cal_type)
local card_func = fusf.CheckType(Card[getter], "function")
local const_table = fusf.Findfucs(table_key)
cal_type = cal_type or "|"
local cal_func
if cal_type == "&" then
cal_func = function(c, target) return card_func(c) & target == target end
elseif cal_type == "|" then
cal_func = function(c, target) return card_func(c) | target == target end
end
return function(c, target)
local from = "MakeConsCheck("..getter
fusf.CheckArgType(from, 1, c, "Card")
local typ = fusf.CheckArgType(from, 2, target, "number/string")
if typ == "number" then return cal_func(c, target) end
local stack = {}
for _, val in ipairs(fusf.InfixToPostfix(target)) do
if val:match("[%-%~]") then
res[#res] = not res[#res]
stack[#stack] = not stack[#stack]
elseif val:match("[%+%/]") then
valR = table.remove(res)
valL = table.remove(res)
local valR, valL = table.remove(stack), table.remove(stack)
local Cal = {
["+"] = valL and valR,
["/"] = valL or valR
}
table.insert(res, Cal[val])
table.insert(stack, Cal[val])
else
table.insert(res, _cal(Card[_func](c), fucs[_key][val:upper()]))
local temp = fusf.FindTables(val:upper(), const_table)
table.insert(stack, cal_func(c, temp))
end
end
return res[#res]
end
end
function fusf.Get_Func(_c, _func, _val)
if type(_func) ~= "string" then return _func end
local lib = _c.lib or {}
local res = function(_func) return _func end
if _func:match("~") then
_func = _func:sub(2)
res = function(_func) return function(...) return not _func(...) end end
end
-- find cm, lib, fuef, aux
if not _val then
return res(_c[_func] or lib[_func] or fuef[_func] or aux[_func])
end
-- translate vals
for i, val in ipairs(_val) do
_val[i] = tonumber(val) or val
end
-- find cm, lib, fuef, aux
for _, Lib in ipairs({_c, lib, fuef, aux}) do
if Lib[_func] then return res(Lib[_func](table.unpack(_val))) end
end
Debug.Message("Get_Func not found : ".._func)
return nil
end
function fusf.Val_Cuts(_val, ...) -- "f1,f2(v1,v2),(v3)" -> {"f1", {"f2", "v1", "v2"}, {"v3"}}, ... use in vi = %i
local res_lst, res_ind, temp = { }, 0, { }
for _, val in _val:ForCut("Val_Cuts") do
res_ind = res_ind + 1
local is_st = val:match("%(")
local is_ed = val:match("%)")
-- is f(v1)
if is_st and is_ed then -- f(v1) -> {"f", "v1"}
res_lst[res_ind] = fusf.Val_Cuts_Table_Process(val, ...)
elseif is_st then -- f(v1,v2,v3) st f(v1
temp, res_ind = val, res_ind - 1
elseif is_ed then -- f(v1,v2,v3) ed v3) -> {"f", "v1", "v2", "v3"}
res_lst[res_ind] = fusf.Val_Cuts_Table_Process(temp..","..val, ...)
temp = ""
elseif #temp > 0 then -- f(v1,v2,v3) mid v2
temp, res_ind = temp..","..val, res_ind - 1
elseif val:match("%%") then
res_lst[res_ind] = ({...})[tonumber(val:sub(2))]
elseif val ~= "" then
res_lst[res_ind] = val
end
return stack[#stack]
end
res_lst.len = res_ind
return res_lst
end
function fusf.Val_Cuts_Table_Process(_str, ...) -- "f(%1,,3)" -> {"f", vals[1], nil, "3"}
local vals, res_lst, st = {...}, { }, _str:find("%(")
if st ~= 1 then res_lst[1] = _str:sub(1, st - 1) end -- has f
if st + 1 == #_str then return res_lst end -- "f()" -> {"f"}
_str = _str:sub(st + 1, #_str - 1)
local res_ind = #res_lst
for _, val in _str:ForCut("Val_Cuts_Table_Process") do
res_ind = res_ind + 1
if val:match("%%") then
res_lst[res_ind] = vals[tonumber(val:sub(2))]
elseif val ~= "" then
res_lst[res_ind] = val
end
--- 从常量表找寻对应 keys 合计值
-- @param table_key string 常量表名称,如 "cod", "pha"
-- @param keys number|string 欲解析的常量名,多个以 "+" 连接
-- @return number 合并的常量值, 可选返回常量描述
-- @raise 当参数类型错误或无法找到对应常量时抛出错误
function fusf.ParseConstantKey(table_key, keys)
local typ = fusf.CheckArgType("ParseConstantKey", 2, keys, "number/string")
if typ == "number" then return keys end
if keys == "" then error("ParseConstantKey : param 2 is invalid param", 2) end
local keys_list, res = keys:Cut("+"), 0
-- is special keys in cod table
if table_key == "cod" then
if keys_list[1] == "CUS" then
res = EVENT_CUSTOM + fusf.NormalizeID(keys_list[2])
-- EVENT_PHASE or EVENT_PHASE_START + PHASE_
elseif keys_list[1]:match("PH") then
for _, key in ipairs(keys_list) do
res = res + fusf.FindTables(key, fucs.cod, fucs.pha)
end
end
if res > 0 then return res end
end
res_lst.len = res_ind
return res_lst
end
function fusf.Creat_CF(_func, _val, ...)
if not _func then return function(c) return true end end
-- trans _val
if type(_val) ~= "table" then _val = { _val } end
local temp_val, v_ind = { }, 0
for _, f_val in ipairs(_val) do
if type(f_val) == "string" then
for _, val in fusf.ForTable(fusf.Val_Cuts(f_val, ...)) do
v_ind = v_ind + 1
temp_val[v_ind] = val
end
-- find table_key
local const_table = fusf.Findfucs(table_key)
local des
for i = #keys_list, 1, -1 do
local key = keys_list[i]
res = res + fusf.FindTables(key, const_table)
des = fucs.des[key] or des
end
return res, des
end
--- 查找并解析函数 func,支持反向逻辑
-- @param func string|function 函数名称或函数对象,若为字串则从 c/lib/fuef/aux 中查找,支持 "~" 前缀取反
-- @param args table? 若存在则立即调用目标函数,并传入该参数表
-- @param ... table 可选优先查找表,最后搜索 fuef, aux
-- @return function|any 若有 args,返回函数执行结果
-- @raise 若 func 为字串且无法解析为有效函数,将抛出错误
function fusf.ResolveFunction(func, args, ...)
fusf.CheckArgType("ResolveFunction", 2, args, "nil/table")
local typ = fusf.CheckArgType("ResolveFunction", 1, func, "string/function")
if typ == "function" then return func end
local fix = function(f) return f end
local f = func
if func:match("~") then
f = func:sub(2)
fix = function(f) return function(...) return not f(...) end end
end
local tabs = {...}
table.insert(tabs, fuef)
table.insert(tabs, aux)
f = fusf.FindTables(f, table.unpack(tabs))
if func == "tgoval" then
Debug.Message(type(f))
end
fusf.CheckType(f, "function")
if args then
if args.n then
f = f(table.unpack(args, 1, args.n))
else
v_ind = v_ind + 1
temp_val[v_ind] = f_val
f = f(table.unpack(args))
end
end
_val, temp_val = temp_val
-- _func is function
if type(_func) == "function" then
return function(c)
return _func(c, table.unpack(_val, 1, v_ind))
end
end
-- _func is string
_func = fusf.PostFix_Trans(_func, ...)
local fucf, Card, aux = fucf, Card, aux
if #_func == 1 then -- _func just one
_func = fucf[_func[1] ] or Card[_func[1] ] or aux[_func[1] ]
return function(c)
return _func(c, table.unpack(_val, 1, v_ind))
end
end
-- _func is multi
return function(c)
local stack, v_ind, temp_val = { }, 1
for _, func in ipairs(_func) do
if func == "~" then
stack[#stack] = not stack[#stack]
elseif type(func) == "string" and #func == 1 then
local valR, valL = table.remove(stack), table.remove(stack)
local Cal = {
["+"] = valL and valR,
["-"] = valL and not valR,
["/"] = valL or valR
}
stack[#stack + 1] = Cal[func]
return fix(f)
end
--- 将可能包含多个函数呼叫与括号参数的表达式字串解析为表结构(支援嵌套与占位符替换)
-- 如 "f1,,f2(%1,%2),(%3)" 会被解析为 { "f1", nil, { "f2", {replace[1], replace[2]} }, { replace[3] } }
-- @param expr_str string 欲解析的整体表达式,如 "f1,,f2(%1,%2),(%3)"
-- @param replace_table table 替换占位符用的参数表
-- @return table 解析结果为表结构,内含函数名与参数的混合列表,巢状呼叫以子表表示,附加栏位 n 表示数量
-- @raise 若有未关闭括号或非法占位符,将抛出错误
function fusf.ParseCallGroupString(expr_str, replace_table)
local result, length, pending = {}, 0, ""
for _, unit in expr_str:ForCut() do
length = length + 1
local is_st = unit:match("%(")
local is_ed = unit:match("%)")
-- is f(v1)
if is_st and is_ed then
local fname, args = fusf.ParseCallExprString(unit, replace_table)
if fname == "" then
result[length] = args
else
if type(func) == "string" then
func = fucf[func] or Card[func] or aux[func]
end
temp_val, v_ind = _val[v_ind], v_ind + 1
if type(temp_val) ~= "table" then temp_val = {temp_val, len = 1} end
stack[#stack + 1] = func(c, table.unpack(temp_val, 1, temp_val.len))
result[length] = {fname, args}
end
elseif is_st then
pending, length = unit, length - 1
elseif is_ed then
local fname, args = fusf.ParseCallExprString(pending .. "," .. unit, replace_table)
if fname == "" then
result[length] = args
else
result[length] = {fname, args}
end
pending = ""
elseif #pending > 0 then
pending, length = pending .. "," .. unit, length - 1
elseif unit:match("%%") then
local ind = tonumber(unit:sub(2))
if not ind or ind < 1 then
error("invalid replace_table index : " .. expr_str)
end
result[length] = replace_table[ind]
elseif unit ~= "" then
result[length] = unit
end
return table.remove(stack)
end
if pending ~= "" then
error("unclosed in ParseCallGroupString : " .. expr_str)
end
result.n = length
return result
end
--- 解析可能带有函数名称与括号参数的呼叫字串,并进行占位替换
-- 如 "Func(%1,,3)" 会被解析为 "Func",与参数表 { replace[1], nil, "3" }
-- @param expr_str string 表达式形式的呼叫字串,如 "Func(%1,,3)"
-- @param replace_table table 替换占位符用的参数表
-- @return string, table 函数名称以及对应参数表,附加栏位 len 表示参数数量
-- @raise 当占位符无效或格式错误时会报错
function fusf.ParseCallExprString(expr_str, replace_table)
local fname, start_pos = "", expr_str:find("%(")
if start_pos ~= 1 then
fname = expr_str:sub(1, start_pos - 1)
end
if start_pos + 1 == #expr_str then -- "f()" -> {"f"}
return fname, {len = 0}
end
local args_str = expr_str:sub(start_pos + 1, -2)
return fname, fusf.ParseArgsString(args_str, replace_table)
end
function fusf.Creat_GF(_func, _val, ...)
local ex_val = {...}
return function(g, n)
g = g:Filter(fusf.Creat_CF(_func, _val, table.unpack(ex_val)), nil)
if not n then return g end
return n > 0 and #g >= n or #g <= -n
--- 解析以逗号分隔的参数字串,并替换其中的占位符(如 %1, %2 等)
-- 如 "%1,,3" 会被解析为 { replace[1], nil, "3" }
-- @param args_str string 欲解析的字串,如 "%1,,3"
-- @param replace_table table 替换占位符用的参数表
-- @return table 解析后的参数 table ,附加栏位 n 表示参数数量
-- @raise 若占位符无效(非正整数)则触发错误
function fusf.ParseArgsString(args_str, replace_table)
local result, length = {}, 0
for _, arg in args_str:ForCut() do
length = length + 1
if arg:match("%%") then
local ind = tonumber(arg:sub(2))
if not ind or ind < 1 then
error("invalid replace_table index : " .. args_str)
end
result[length] = replace_table[ind]
elseif arg ~= "" then
result[length] = arg
end
end
result.n = length
return result
end
--- 遍历一个表(table),支援带 .n 栏位的自订长度
-- @param t table 欲遍历的表
-- @param n number? 若无 t.n,则使用此值作为最大索引(可选)
-- @return function 叠代器函数,返回 (index, value)
function fusf.ForTable(t, n)
local i, max = 0, t.len or n
local i, max = 0, t.n or n
return function()
if i >= max then return nil end
i = i + 1
return i, t[i]
end
end
function fusf.GetDES(_code, _id, m) -- (0), ("n"), (m), ("+1")
if _id then
if type(_code) == "number" then
_code = fusf.M_chk(_code)
else -- ("-1", 0)
_code = m + tonumber(_code)
--- 解析文字讯息代码,支持多种输入格式(数字/字符串/偏移量)
-- @param code number|string 原始代码或偏移字符串,如 "+1"、"-2"
-- @param id number? 可选的消息编号(如 Stringid(code, id) 中的 id)
-- @param m number? 可选基准卡号,用于计算相对偏移
-- @return number 返回 aux.Stringid(code, id) 的值
function fusf.ResolveDescription(code, id, m) -- (0), ("n"), (m), ("+1")
local cod_typ = fusf.CheckArgType("ResolveDescription", 1, code, "number/string")
local id_typ = fusf.CheckArgType("ResolveDescription", 2, id, "nil/number")
fusf.CheckArgType("ResolveDescription", 3, m, "number")
if id_typ == "number" then
if id < 17 then
code = fusf.NormalizeID(code, m)
end
elseif type(_code) == "number" then
if _code < 17 then -- in cdb and code is owner card code
_code, _id = m, _code
elseif cod_typ == "number" then
if code < 17 then -- in cdb and code is owner card code
code, id = m, code
elseif code > 1000 then
code, id = 0, code
else
_code, _id = fusf.M_chk(_code), 0
code, id = fusf.NormalizeID(code, m), 0
end
elseif type(_code) == "string" then
if tonumber(_code) then -- code = m +- _code
_code, _id = m + tonumber(_code), 0
else -- in fucs.des
_code, _id = 0, fucs.des[_code]
elseif cod_typ == "string" then
if tonumber(code) then -- code = m +- code
code, id = fusf.NormalizeID(code, m), 0
else -- in fucs.des
code, id = 0, fusf.CheckType(fucs.des[code], "number")
end
end
return aux.Stringid(_code, _id) -- _code * 16 + _id
return aux.Stringid(code, id) -- code * 16 + id
end
function fusf.GetRES(_flag, _count) -- _flag = a + b/b1/b2 + c | 1
if type(_flag) ~= "string" then return {_flag or 0, _count} end
if not _count then -- cut count
_flag = _flag:Cut("fusf.GetRES", "|")
_flag, _count = _flag[1], tonumber(_flag[2] or 1)
--- 解析重置条件字串,支援简单运算与预设数量
-- @param flag string|number 条件描述(如 "a + b - c , 1")
-- @param count number? 可选的重置次数,若省略则从字串中解析
-- @return table {flag: number, count: number} 重置条件与次数 table
function fusf.ResolveReset(flag, count)
local typ = fusf.CheckArgType("ResolveReset", 1, flag, "number/string")
fusf.CheckArgType("ResolveReset", 2, count, "nil/number")
if typ == "number" then return flag, count end
if not count then -- cut count
local parts = flag:Cut()
flag, count = parts[1], tonumber(parts[2] or 1)
end
local stack = { }
for _, unit in ipairs(fusf.PostFix_Trans(_flag)) do
if unit:match("[+-/]") then
local stack = {}
local res, pha = fucs.res, fucs.pha
for _, unit in ipairs(fusf.InfixToPostfix(flag)) do
if unit:match("[+-]") then
local valR, valL = table.remove(stack), table.remove(stack)
table.insert(stack, unit == "-" and valL - valR or valL | valR)
local cal = (unit == "-") and (valL - valR) or (valL | valR)
table.insert(stack, cal)
else
table.insert(stack, fucs.res[unit] or fucs.pha[unit])
table.insert(stack, fusf.FindTables(unit, res, pha))
end
end
_flag = table.remove(stack)
if _flag & 0xfff0000 > 0 then _flag = _flag | RESET_EVENT end
if _flag & 0x00003ff > 0 then _flag = _flag | RESET_PHASE end
return {_flag, _count}
flag = table.remove(stack)
if flag & 0xfff0000 > 0 then flag = flag | RESET_EVENT end
if flag & 0x00003ff > 0 then flag = flag | RESET_PHASE end
return flag, count
end
--- 建立与事件 e 绑定的函数快取区(每个效果唯一识别),供函数查找与快取使用
-- @param e Effect 效果物件,用于识别对应的快取区
-- @return table 快取表,带栏位 lib 表示可供查找函数的函式库
function fusf.MakeFuncCatch(e)
local cm = fusf.Getcm(e)
local id = tostring(e):sub(-8)
cm[id] = cm[id] or {lib = cm}
return cm[id]
end
--- 查找指定键值对应的函数,若未快取则解析后快取
-- @param catch table 快取区(来自 MakeFuncCatch)
-- @param key string 作为函数的识别名称(快取键)
-- @param func string|function 欲查找的函数或其字串表示,可带 "~" 表示反向逻辑
-- @return function 查找到的函数物件(已快取)
function fusf.FindFuncCatch(catch, key, func)
if catch[key] then return catch[key] end
catch[key] = fusf.ResolveFunction(func, nil, catch.lib)
return catch[key]
end
--------------------------------------"Other Support function"
function fusf.RegFlag(val, cod, res, pro, lab, des) -- val : Card|Effect|player(number)
cod, res, pro = fusf.M_chk(cod), fusf.GetRES(res), fusf.Get_Constant("pro", pro) or 0
if des then des, pro = fusf.GetDES(des, nil, cod), (pro or 0)|EFFECT_FLAG_CLIENT_HINT end
local typ = aux.GetValueType(val)
if typ == "Card" then
val:RegisterFlagEffect(cod, res[1], pro, res[2] or 1, lab or 0, des)
elseif typ == "Group" then
for c in aux.Next(val) do
c:RegisterFlagEffect(cod, res[1], pro, res[2] or 1, lab or 0, des)
end
elseif typ == "Effect" then
val:GetHandler():RegisterFlagEffect(cod, res[1], pro, res[2] or 1, lab or 0, des)
else
--- 为卡片、效果、玩家或卡片群组注册一个标识用效果(FlagEffect)
-- @param val Card|Group|Effect|number 目标对象,可为单张卡、卡组、效果对象,或玩家编号
-- @param cod number|string 标识效果的代码,可为数值或偏移表达式(将被规范化)
-- @param res string|number 标识的重置条件,可用表达式,如 "event1 + pha1 , 1"
-- @param pro string|number? 效果的属性(效果标志),默认值为 0,可为常量名或数值
-- @param des string|number? 效果提示描述(字符串ID),传入描述名或偏移,如 "+1"
-- @param lab number? 标签参数,可用于额外传值(如标记内容),默认 0
function fusf.RegFlag(val, cod, res, pro, des, lab)
local typ = fusf.CheckArgType("RegFlag", 1, val, "Card/Group/Effect/player")
fusf.CheckArgType("RegFlag", 6, lab, "nil/number")
cod = fusf.NormalizeID(cod) -- 规范化代码(如字符串转数值)
res = {fusf.ResolveReset(res)} -- 转换表达式或数值为 {flag, count}
pro = fusf.ParseConstantKey("pro", pro or 0) -- 效果属性,默认为 0
if des then
des = fusf.ResolveDescription(des, nil, cod)
pro = pro|EFFECT_FLAG_CLIENT_HINT
end
if typ == "player" then
Duel.RegisterFlagEffect(val, cod, res[1], pro, res[2] or 1, lab or 0)
return
end
for c in aux.Next(fusf.ToGroup(val)) do
c:RegisterFlagEffect(cod, res[1], pro, res[2] or 1, lab or 0, des)
end
end
--- 检查 val 的种类为 cod 的标识效果的数量, 有 n 则传回比较结果
-- @param val Card|Effect|number 目标对象,可为卡片、效果对象或玩家编号(0或1)
-- @param cod number|string FlagEffect 的代码,会被标准化(如偏移表达式 "+1")
-- @param n number? 可选,若指定则判断是否达到 n,负数则判断不到 -n
-- @return number|boolean 返回数量,或是否满足条件
function fusf.GetFlag(val, cod, n)
local typ = fusf.CheckArgType("GetFlag", 1, val, "Card/Effect/player")
fusf.CheckArgType("GetFlag", 3, n, "nil/number")
cod = fusf.NormalizeID(cod)
local count
if typ == "player" then
count = Duel.GetFlagEffect(val, cod)
else
count = fusf.ToCard(val):GetFlagEffect(cod)
end
if not n then return count end
return n > 0 and count >= n or count <= -n
end
--- 检查 val 的种类为 cod 的标识效果的 Label, 有 lab 則傳回是否相等
-- @param val Card|Effect|player 要查询的对象,可以是卡片、效果或玩家
-- @param cod number|string FlagEffect 的代码,会被标准化(如偏移表达式 "+1")
-- @param lab any? 可选,用于比较 label 是否等于特定值
-- @return any|boolean 若提供 lab,则回传是否相等;否则回传该 Label(或 nil)
function fusf.GetFlagLabel(val, cod, lab)
local typ = fusf.CheckArgType("GetFlagLabel", 1, val, "Card/Effect/player")
fusf.CheckArgType("GetFlagLabel", 3, lab, "nil/number")
cod = fusf.NormalizeID(cod)
local label
if typ == "player" then
label = {Duel.GetFlagEffectLabel(val, cod)}
else
label = {fusf.ToCard(val):GetFlagEffectLabel(cod)}
end
if lab then return label[1] == lab end
return table.unpack(label)
end
--- 取得成为对象的卡(若存在),并检测是否与 e 有联系
-- @param e Effect 当前效果物件
-- @param pos number|nil 过滤位置(可选,用于指定表示形式,如 POS_FACEUP)
-- @param is_imm boolean|nil 是否过滤能否被效果影响的卡片(可选)
-- @return Card|false 返回对象,若无或不满足条件则返回 false
function fusf.GetTarget(e, pos, is_imm)
local tg = fusf.GetTargets(e, pos, is_imm)
if tg then tg = tg:GetFirst() end
return tg
end
--- 取得成为对象的卡片组(若存在),并检测是否与 e 有联系
-- @param e Effect 当前效果物件
-- @param pos number|nil 过滤位置(可选,用于指定表示形式,如 POS_FACEUP)
-- @param is_imm boolean|nil 是否过滤能否被效果影响的卡片(可选)
-- @return Group|false 返回满足条件的对象卡片组,若无或都不满足条件则返回 false
function fusf.GetTargets(e, pos, is_imm)
local tg = Duel.GetChainInfo(0,CHAININFO_TARGET_CARDS):Filter(Card.IsRelateToEffect,nil,e)
if pos then tg = fugf.Filter(tg, "IsPos", pos) end
if is_imm then tg = fugf.Filter(tg, "~IsImm", e) end
if #tg == 0 then return false end
return tg
end
--- 让玩家 tp 把卡片或卡片组 eg 作为装备卡装备给卡片 c,返回值表示是否成功
-- @param e Effect 装备效果
-- @param tp number 装备的玩家,必须为 0 或 1
-- @param eg Group|Card 要装备的卡片,支持单张或群组
-- @param c nil|Card 装备目标卡片,必须为面朝上的卡
-- @return boolean 装备成功返回 true,失败返回 false
function fusf.Equip(e, tp, eg, c)
fusf.CheckArgType("Equip", 1, e, "Effect")
fusf.CheckArgType("Equip", 2, tp, "player")
local typ = fusf.CheckArgType("Equip", 3, eg, "Group/Card")
eg = fusf.ToGroup(eg)
if Duel.GetLocationCount(tp, LOCATION_SZONE) < #eg then return false end
c = fusf.CheckArgType("Equip", 4, c or e:GetHandler(), "Card")
if c:IsFacedown() then return false end
local limit = function(e, c) return c == e:GetLabelObject() end
if typ == "Card" then
local ec = eg:GetFirst()
if not Duel.Equip(tp, ec, c) then return false end
if ec:IsType(TYPE_MONSTER) then
fuef.S(e, EFFECT_EQUIP_LIMIT, ec):PRO("CD"):VAL(limit):OBJ(c):RES("STD")
end
return true
end
for ec in aux.Next(eg) do
Duel.Equip(tp, ec, c, true, true)
if ec:IsType(TYPE_MONSTER) then
fuef.S(e, EFFECT_EQUIP_LIMIT, ec):PRO("CD"):VAL(limit):OBJ(c):RES("STD")
end
end
Duel.EquipComplete()
return true
end
--- 检查计数器 id 的数量, 有 n 则传回比较结果
-- @param id number|string 计数器的 ID,会规范化
-- @param p number 玩家编号(0 或 1)
-- @param typ number|string 操作类型
-- @param n number? 可选,若指定则判断是否达到 n,负数则判断不到 -n
-- @return number|boolean 返回数量,或是否满足条件
function fusf.GetCounter(id, p, typ, n)
id = fusf.NormalizeID(id)
typ = fusf.ParseConstantKey("act", typ)
local ct = Duel.GetCustomActivityCount(id, p, typ)
if not n then return ct end
return n > 0 and ct >= n or ct <= -n
end
--- 判断目前是否处于指定的 Phase(支援逻辑运算)
-- 支援表达式:单一阶段(如 "BP")、多个阶段的 OR(如 "BP/M2")、NOT(如 "~EP")
-- @param pha number|string 阶段代码或逻辑表达式
-- @return boolean 是否处于该阶段
function fusf.IsPhase(pha)
local phase = Duel.GetCurrentPhase()
local typ = fusf.CheckArgType("IsPhase", 1, pha, "number/string")
if typ == "number" then return phase == pha end
local stack = {}
for _, val in ipairs(fusf.InfixToPostfix(pha)) do
if val:match("[%-%~]") then
stack[#stack] = not stack[#stack]
elseif val == "/" then
local valR, valL = table.remove(stack), table.remove(stack)
table.insert(stack, valL or valR)
elseif val == "BP" then
table.insert(stack, Duel.IsBattlePhase())
else
local _pha = fusf.Findfucs("pha", val)
table.insert(stack, phase == _pha)
end
end
return stack[#stack]
end
--------------------------------------------------------------------------"initial function"
--- 为卡片脚本提供统一初始化接口
-- @param lib table (可选)卡片使用的函式库
-- @param glo_key string (可选)表示是否有全域注册条件
-- @return cm 卡片表(即全局脚本的局部名 cm)
-- @return m 卡片 ID(编号)
function fusf.Initial(lib, glo_key)
local cm, m = GetID()
if cm.initial_effect then return cm, m end
local log = fudf.StartLog(0, "fusf.Initial", lib, glo_key)
log:Info(m)
fusf.CheckArgType("Initial", 1, lib, "nil/table")
fusf.CheckArgType("Initial", 2, glo_key, "nil/string")
cm.lib = lib or {}
cm.e_list = {}
cm.init_list = {}
local function apply(log, c, name, val)
log:Info("try InitSetOwner "..name)
local i = 1
local key = name .. i
while cm[key] do
local E = fusf.CheckType(cm[key], "fuef")
c.e_list[key] = E:InitSetOwner(key, c, val)
i = i + 1
key = name .. i
end
log:Info(("set %d %s"):format(i - 1, name))
end
cm.initial_effect = function(c)
local log = fudf.StartLog(0, "cm.initial_effect", c)
log:Info(m)
apply(log, c, "pe") -- pe1, pe2, ...
apply(log, c, "e") -- e1, e2, ...
log:Info("try ipairs cm.init_list")
for _, f in ipairs(cm.init_list) do
f(c)
end
log:Info("set cm.init_list down")
if not glo_key or cm[glo_key] then return end
cm[glo_key] = {0, 0}
apply(log, c, "ge", 1) -- ge1, ge2, ...
end
log:Info("set cm.initial_effect down")
return cm, m
end
--- 在卡片的 initial_effect 中插入额外初始化函数
-- @param func function 需要插入的初始化函数
-- @raise 如果 cm.initial_effect 不存在或不是函数,则报错 "INITIAL"
function fusf.InsertInitial(func, log)
log = fudf.StartLog(log or 0, "fusf.InsertInitial", func)
fusf.CheckArgType("InsertInitial", 1, func, "function")
local cm, m = GetID()
if not cm.init_list then error("You must call fusf.Initial first", 2) end
table.insert(cm.init_list, func)
end
--- 为卡片添加卡名记述 ...
-- @param ... number|string 可为卡号、字符串(如 "100+200")、或多个卡号
-- 字符串会用 "+" 切割后批量处理,转换为标准卡号格式
function fusf.AddCode(...)
local log = fudf.StartLog(0, "fusf.AddCode", ...)
fusf.CheckEmptyArgs("AddCode", ...)
local codes = { }
for i, val in ipairs{...} do
local typ = fusf.CheckArgType("AddCode", i, val, "string/number")
if typ == "string" then
for _, code in ipairs(fusf.GetCodeTable(val)) do
log:Info("add "..code)
table.insert(codes, code)
end
else
local code = fusf.NormalizeID(val)
log:Info("add "..code)
table.insert(codes, code)
end
end
local function AddFunc(c)
aux.AddCodeList(c, table.unpack(codes))
end
fusf.InsertInitial(AddFunc, log)
end
function fusf.GetFlag(val, cod) -- val : Card|Effect|player(number)
cod = fusf.M_chk(cod)
local typ = aux.GetValueType(val)
if typ == "Card" then
return val:GetFlagEffect(cod)
elseif typ == "Effect" then
return val:GetHandler():GetFlagEffect(cod)
--- 为卡片添加苏生限制
function fusf.ReviveLimit()
local log = fudf.StartLog(0, "fusf.ReviveLimit")
local function AddFunc(c)
c:EnableReviveLimit()
end
return Duel.GetFlagEffect(val, cod)
fusf.InsertInitial(AddFunc, log)
end
function fusf.Equip(e,tp,ec,c)
if not (ec and c and Duel.Equip(tp, ec, c)) then return false end
local eq = function(e, c) return c == e:GetLabelObject() end
return fuef.S(e, EFFECT_EQUIP_LIMIT, ec):PRO("CD"):VAL(eq):OBJ(c):RES("STD")
--- 添加自定义计数器
-- @param id number|string 计数器的 ID,会规范化
-- @param typ number|string 操作类型
-- @param func function|string 判断计数的过滤函数
function fusf.AddCounter(id, typ, func)
local log = fudf.StartLog(0, "fusf.AddCounter", id, typ, func)
local id = fusf.NormalizeID(id)
log:Info("NormalizeID id : "..id)
local typ = fusf.ParseConstantKey("act", typ)
log:Info("got act typ : "..fusf.CutHex(typ))
if fusf.CheckArgType("AddCounter", 3, func, "function/string") == "string" then
func = "~"..func
else
func = function(...) return not func(...) end
end
local function AddFunc(c)
func = fusf.ResolveFunction(func, nil, fusf.Getcm(c), c.lib)
Duel.AddCustomActivityCounter(id, typ, func)
end
fusf.InsertInitial(AddFunc, log)
end
\ No newline at end of file
......@@ -124,9 +124,9 @@ fucs.cod = {
RE = EVENT_REMOVE ,
MO = EVENT_MOVE ,
--召唤
PS = EVENT_SUMMON , --召唤之际(怪兽还没上场、神宣等时点)
PSP = EVENT_SPSUMMON , --特殊召唤之际
PFS = EVENT_FLIP_SUMMON , --翻转召唤之际
PS = EVENT_SUMMON , --召唤之际(怪兽还没上场、神宣等时点)
PSP = EVENT_SPSUMMON , --特殊召唤之际
PFS = EVENT_FLIP_SUMMON , --翻转召唤之际
S = EVENT_SUMMON_SUCCESS , --通常召唤成功时
SP = EVENT_SPSUMMON_SUCCESS , --特殊召唤成功时
FS = EVENT_FLIP_SUMMON_SUCCESS , --翻转召唤成功时
......@@ -150,30 +150,30 @@ fucs.cod = {
NEGS = EVENT_SUMMON_NEGATED , --召唤被无效时
NEGFS = EVENT_FLIP_SUMMON_NEGATED , --反转召唤被无效时
NEGSP = EVENT_SPSUMMON_NEGATED , --特殊召唤被无效时
NEGATK = EVENT_ATTACK_DISABLED , --攻击无效时(翻倍机会)
--连锁
CH = EVENT_CHAINING , --效果发动时
CHED= EVENT_CHAIN_SOLVED , --连锁处理结束时
----组合时点
PHS = EVENT_PHASE_START ,
--攻击
ATK = EVENT_ATTACK_ANNOUNCE , --攻击宣言时
BATK= EVENT_BE_BATTLE_TARGET , --被选为攻击对象时
--需组合 阶段时点
PH = EVENT_PHASE , --阶段结束时
PHS = EVENT_PHASE_START , --阶段开始时
--[[
EVENT_CHAIN_SOLVING =1020 --连锁处理开始时(EVENT_CHAIN_ACTIVATING之後)
EVENT_CHAIN_ACTIVATING =1021 --连锁处理准备中
EVENT_CHAIN_ACTIVATED =1023 --N/A
EVENT_CHAIN_NEGATED =1024 --连锁发动无效时(EVENT_CHAIN_ACTIVATING之後)
EVENT_CHAIN_DISABLED =1025 --连锁效果无效时
EVENT_CHAIN_END =1026 --连锁串结束时
EVENT_BECOME_TARGET =1028 --成为效果对象时
EVENT_BREAK_EFFECT =1050 --Duel.BreakEffect()被调用时
EVENT_MSET =1106 --放置怪兽时
EVENT_SSET =1107 --放置魔陷时
EVENT_DRAW =1110 --抽卡时
EVENT_DAMAGE =1111 --造成战斗/效果伤害时
EVENT_RECOVER =1112 --回复生命值时
EVENT_PREDRAW =1113 --抽卡阶段通常抽卡前
EVENT_CONTROL_CHANGED =1120 --控制权变更
EVENT_EQUIP =1121 --装备卡装备时
EVENT_ATTACK_ANNOUNCE =1130 --攻击宣言时
EVENT_BE_BATTLE_TARGET =1131 --被选为攻击对象时
EVENT_BATTLE_START =1132 --伤害步骤开始时(反转前)
EVENT_BATTLE_CONFIRM =1133 --伤害计算前(反转後)
EVENT_PRE_DAMAGE_CALCULATE =1134 --伤害计算时(羽斬)
......@@ -184,7 +184,6 @@ EVENT_BATTLED =1138 --伤害计算后(异女、同反转效果时点)
EVENT_BATTLE_DESTROYING =1139 --以战斗破坏怪兽送去墓地时(BF-苍炎之修罗)
EVENT_BATTLE_DESTROYED =1140 --被战斗破坏送去墓地时(杀人番茄等)
EVENT_DAMAGE_STEP_END =1141 --伤害步骤结束时
EVENT_ATTACK_DISABLED =1142 --攻击无效时(翻倍机会)
EVENT_BATTLE_DAMAGE =1143 --造成战斗伤害时
EVENT_TOSS_DICE =1150 --掷骰子的结果产生后
EVENT_TOSS_COIN =1151 --抛硬币的结果产生后
......@@ -194,8 +193,6 @@ EVENT_LEVEL_UP =1200 --等级上升时
EVENT_PAY_LPCOST =1201 --支付生命值时
EVENT_RETURN_TO_GRAVE =1203 --回到墓地时
EVENT_TURN_END =1210 --回合结束时
EVENT_PHASE =0x1000 --阶段结束时
EVENT_PHASE_START =0x2000 --阶段开始时
EVENT_ADD_COUNTER =0x10000 --增加指示物时
EVENT_REMOVE_COUNTER =0x20000 --去除指示物时(A指示物),Card.RemoveCounter()必須手動觸發此事件
--]]
......@@ -219,7 +216,7 @@ fucs.pro = {
CAL = EFFECT_FLAG_DAMAGE_CAL , --可以在伤害计算时发动
OP = EFFECT_FLAG_EVENT_PLAYER , --发动/处理效果的玩家为触发事件的玩家而不是卡片的持有者,如仪式魔人,万魔殿
NR = EFFECT_FLAG_NO_TURN_RESET , --发条等“这张卡在场上只能发动一次”的效果
OE = 0x40400 , --EFFECT_FLAG_CANNOT_DISABLE+EFFECT_FLAG_UNCOPYABLE(out effect)
OE = 0x40400 , --EFFECT_FLAG_CANNOT_DISABLE+EFFECT_FLAG_UNCOPYABLE(out effect)
}
--Location Variable
fucs.ran = {
......@@ -237,13 +234,19 @@ fucs.ran = {
["P"] = LOCATION_PZONE ,
["A"] = 0xff
}
--Countlimit Variable
fucs.ctl = {
O = EFFECT_COUNT_CODE_OATH,
D = EFFECT_COUNT_CODE_DUEL,
C = EFFECT_COUNT_CODE_CHAIN,
}
--Phase Variable
fucs.pha = {
DP = PHASE_DRAW , --抽卡阶段
SP = PHASE_STANDBY , --准备阶段
M1 = PHASE_MAIN1 , --主要阶段1
BPS = PHASE_BATTLE_START, --战斗阶段开始
BP = PHASE_BATTLE_STEP , --战斗步驟
BS = PHASE_BATTLE_STEP , --战斗步驟
DS = PHASE_DAMAGE , --伤害步驟
DC = PHASE_DAMAGE_CAL , --伤害计算时
BPE = PHASE_BATTLE , --战斗阶段結束
......@@ -396,5 +399,21 @@ fucs.val = {
LI = SUMMON_TYPE_LINK ,
--Summon Value --特定的召唤方式
SELF = SUMMON_VALUE_SELF ,
SYM = SUMMON_VALUE_SYNCHRO_MATERIAL
}
\ No newline at end of file
SYM = SUMMON_VALUE_SYNCHRO_MATERIAL ,
--location Value --离场重定向
H = LOCATION_HAND ,
D = LOCATION_DECK ,
G = LOCATION_GRAVE ,
R = LOCATION_REMOVED,
E = LOCATION_EXTRA ,
}
--Value Variable
fucs.act = {
S = ACTIVITY_SUMMON , --召唤(不包括通常召唤的set)
NS = ACTIVITY_NORMALSUMMON , --通常召唤(包括通常召唤的set)
SP = ACTIVITY_SPSUMMON , --特殊召唤
FS = ACTIVITY_FLIPSUMMON , --反转召唤
ATK = ACTIVITY_ATTACK , --攻击
CH = ACTIVITY_CHAIN , --发动效果
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment