Commit d97d98da authored by Vury Leo's avatar Vury Leo

Add 究極竜魔導師

parent 2afce688
...@@ -2,6 +2,33 @@ ...@@ -2,6 +2,33 @@
local s,id,o=GetID() local s,id,o=GetID()
function s.initial_effect(c) function s.initial_effect(c)
aux.AddMaterialCodeList(c,23995346) aux.AddMaterialCodeList(c,23995346)
-- common Ritual‐monster filter: 「カオス」儀式モンスター
local ritual_filter=function(mc,tc)
if not mc:IsAllTypes(TYPE_MONSTER|TYPE_RITUAL) then return false end
return mc:IsFusionSetCard(0xcf)
end
Fusion.AddFusionProcedure(c,{
variants = {
-- Variant A: 1 × 青眼の究極竜 + 1 × 「カオス」儀式モンスター
{
slots = {
Fusion.Slot.Code(23995346),
Fusion.Slot.Filter(ritual_filter),
}
},
-- Variant B: 3 × 「ブルーアイズ」モンスター + 1 × 「カオス」儀式モンスター
{
slots = {
Fusion.Slot.Group({
min=3,
max=3,
filter=function(mc,tc) return mc:IsFusionSetCard(0xdd) end,
}),
Fusion.Slot.Filter(ritual_filter),
}
}
}
})
c:EnableReviveLimit() c:EnableReviveLimit()
--summon procedure --summon procedure
local e0=Effect.CreateEffect(c) local e0=Effect.CreateEffect(c)
...@@ -10,13 +37,13 @@ function s.initial_effect(c) ...@@ -10,13 +37,13 @@ function s.initial_effect(c)
e0:SetCode(EFFECT_SPSUMMON_CONDITION) e0:SetCode(EFFECT_SPSUMMON_CONDITION)
e0:SetValue(aux.fuslimit) e0:SetValue(aux.fuslimit)
c:RegisterEffect(e0) c:RegisterEffect(e0)
local e1=Effect.CreateEffect(c) -- local e1=Effect.CreateEffect(c)
e1:SetType(EFFECT_TYPE_SINGLE) -- e1:SetType(EFFECT_TYPE_SINGLE)
e1:SetProperty(EFFECT_FLAG_CANNOT_DISABLE+EFFECT_FLAG_UNCOPYABLE) -- e1:SetProperty(EFFECT_FLAG_CANNOT_DISABLE+EFFECT_FLAG_UNCOPYABLE)
e1:SetCode(EFFECT_FUSION_MATERIAL) -- e1:SetCode(EFFECT_FUSION_MATERIAL)
e1:SetCondition(s.fcondition) -- e1:SetCondition(s.fcondition)
e1:SetOperation(s.foperation) -- e1:SetOperation(s.foperation)
c:RegisterEffect(e1) -- c:RegisterEffect(e1)
--negate --negate
local e2=Effect.CreateEffect(c) local e2=Effect.CreateEffect(c)
e2:SetDescription(aux.Stringid(id,1)) e2:SetDescription(aux.Stringid(id,1))
......
...@@ -11,7 +11,14 @@ function s.initial_effect(c) ...@@ -11,7 +11,14 @@ function s.initial_effect(c)
{ [0xff]=FusionSpell.FUSION_OPERATION_SHUFFLE } { [0xff]=FusionSpell.FUSION_OPERATION_SHUFFLE }
}, },
stage_x_operation=s.stage_x_operation, stage_x_operation=s.stage_x_operation,
locked_codes={89631139,23995346} locked_codes=function(tc)
if aux.IsMaterialListCode(tc,89631139)==true then
return {89631139}
elseif aux.IsMaterialListCode(tc,23995346)==true then
return {23995346}
end
return {}
end
}) })
e1:SetCategory(CATEGORY_SPECIAL_SUMMON+CATEGORY_FUSION_SUMMON+CATEGORY_GRAVE_ACTION) e1:SetCategory(CATEGORY_SPECIAL_SUMMON+CATEGORY_FUSION_SUMMON+CATEGORY_GRAVE_ACTION)
e1:SetHintTiming(0,TIMINGS_CHECK_MONSTER+TIMING_MAIN_END) e1:SetHintTiming(0,TIMINGS_CHECK_MONSTER+TIMING_MAIN_END)
......
...@@ -2255,7 +2255,7 @@ FusionSpell = {} ...@@ -2255,7 +2255,7 @@ FusionSpell = {}
--- Whether skip the location count check, default false, used for 叛逆の堕天使, only works for target function --- Whether skip the location count check, default false, used for 叛逆の堕天使, only works for target function
--- @field skip_location_count_check? boolean --- @field skip_location_count_check? boolean
--- Locked code, material code that can not use substitutes --- Locked code, material code that can not use substitutes
--- @field locked_codes? integer[] --- @field locked_codes? (integer[]|fun(tc:Card):integer[])
--- Add LOCATION_EXTRA and opponent mzone to EFFECT_EXTRA_FUSION_MATERIAL list, remove once core updated --- Add LOCATION_EXTRA and opponent mzone to EFFECT_EXTRA_FUSION_MATERIAL list, remove once core updated
...@@ -2407,7 +2407,7 @@ end ...@@ -2407,7 +2407,7 @@ end
---@param fusion_spell_matfilter FUSION_SPELL_MATFILTER_FUNCTION a material must pass this to be legal as material come from fusion spell ---@param fusion_spell_matfilter FUSION_SPELL_MATFILTER_FUNCTION a material must pass this to be legal as material come from fusion spell
---@param skip_summon_check boolean whether skip the IsCanBeSpecialSummoned check, for クロック・リザード ---@param skip_summon_check boolean whether skip the IsCanBeSpecialSummoned check, for クロック・リザード
---@param skip_location_count_check boolean Whether skip the location count check, default false, used for 叛逆の堕天使 ---@param skip_location_count_check boolean Whether skip the location count check, default false, used for 叛逆の堕天使
---@param locked_codes integer[] Locked code, material code that can not use substitutes, used for 烙印融合 ---@param locked_codes (integer[]|fun(tc:Card):integer[]) Locked code, material code that can not use substitutes, used for 烙印融合
function FusionSpell.GetSummonTarget( function FusionSpell.GetSummonTarget(
fusfilter, fusfilter,
matfilter, matfilter,
...@@ -2460,7 +2460,7 @@ function FusionSpell.GetSummonTarget( ...@@ -2460,7 +2460,7 @@ function FusionSpell.GetSummonTarget(
end end
-- --- check for chain material targets -- --- check for chain material targets
if sumtype&SUMMON_TYPE_FUSION~=0 then if sumtype&SUMMON_TYPE_FUSION~=0 then
local ce_sg=FusionSpell.IsExistsChainMaterialSummonTargets(e,tp,fusfilter,matfilter,additional_fcheck,additional_fgoalcheck,fuslocation,sumtype,sumpos,gc,skip_summon_check,skip_location_count_check) local ce_sg=FusionSpell.IsExistsChainMaterialSummonTargets(e,tp,fusfilter,matfilter,additional_fcheck,additional_fgoalcheck,fuslocation,sumtype,sumpos,gc,skip_summon_check,skip_location_count_check,locked_codes)
if ce_sg==true then if ce_sg==true then
return true return true
end end
...@@ -2488,7 +2488,7 @@ end ...@@ -2488,7 +2488,7 @@ end
---@param gc fun(e:Effect):Card|nil Function that returns a card that must be included in the fusion materials ---@param gc fun(e:Effect):Card|nil Function that returns a card that must be included in the fusion materials
---@param fusion_spell_matfilter FUSION_SPELL_MATFILTER_FUNCTION a material must pass this to be legal as material come from fusion spell ---@param fusion_spell_matfilter FUSION_SPELL_MATFILTER_FUNCTION a material must pass this to be legal as material come from fusion spell
---@param skip_summon_check boolean whether skip the IsCanBeSpecialSummoned check, for クロック・リザード ---@param skip_summon_check boolean whether skip the IsCanBeSpecialSummoned check, for クロック・リザード
---@param locked_codes integer[] Locked code, material code that can not use substitutes, used for 烙印融合 ---@param locked_codes (integer[]|fun(tc:Card):integer[]) Locked code, material code that can not use substitutes, used for 烙印融合
function FusionSpell.GetSummonOperation( function FusionSpell.GetSummonOperation(
fusfilter, fusfilter,
matfilter, matfilter,
...@@ -2597,7 +2597,11 @@ function FusionSpell.GetSummonOperation( ...@@ -2597,7 +2597,11 @@ function FusionSpell.GetSummonOperation(
e, e,
mat_operation_code_map) mat_operation_code_map)
aux.FGoalCheckAdditional=FusionSpell.GetFusionSpellFGoalCheckAdditionalFunction(additional_fgoalcheck,tp,tc,pre_select_mat_location,e) aux.FGoalCheckAdditional=FusionSpell.GetFusionSpellFGoalCheckAdditionalFunction(additional_fgoalcheck,tp,tc,pre_select_mat_location,e)
Fusion.LockedCodes=locked_codes if type(locked_codes)=='function' then
Fusion.LockedCodes=locked_codes(tc)
else
Fusion.LockedCodes=locked_codes
end
materials=Duel.SelectFusionMaterial(tp,tc,mg,gc(e),tp) materials=Duel.SelectFusionMaterial(tp,tc,mg,gc(e),tp)
aux.FCheckAdditional=nil aux.FCheckAdditional=nil
aux.FGoalCheckAdditional=nil aux.FGoalCheckAdditional=nil
...@@ -2610,7 +2614,11 @@ function FusionSpell.GetSummonOperation( ...@@ -2610,7 +2614,11 @@ function FusionSpell.GetSummonOperation(
assert(#chain_mg>0, "we are trying to apply a chain material, but it has no possible material") assert(#chain_mg>0, "we are trying to apply a chain material, but it has no possible material")
aux.FCheckAdditional=FusionSpell.GetFusionSpellFCheckAdditionalFunctionForChainMaterial(additional_fcheck,e) aux.FCheckAdditional=FusionSpell.GetFusionSpellFCheckAdditionalFunctionForChainMaterial(additional_fcheck,e)
aux.FGoalCheckAdditional=FusionSpell.GetFusionSpellFGoalCheckAdditionalFunctionForChainMaterial(additional_fgoalcheck,e) aux.FGoalCheckAdditional=FusionSpell.GetFusionSpellFGoalCheckAdditionalFunctionForChainMaterial(additional_fgoalcheck,e)
Fusion.LockedCodes=locked_codes if type(locked_codes)=='function' then
Fusion.LockedCodes=locked_codes(tc)
else
Fusion.LockedCodes=locked_codes
end
materials=Duel.SelectFusionMaterial(tp,tc,chain_mg,gc(e),tp) materials=Duel.SelectFusionMaterial(tp,tc,chain_mg,gc(e),tp)
aux.FCheckAdditional=nil aux.FCheckAdditional=nil
aux.FGoalCheckAdditional=nil aux.FGoalCheckAdditional=nil
...@@ -2879,7 +2887,7 @@ end ...@@ -2879,7 +2887,7 @@ end
---@param fusion_spell_matfilter fun(c:Card):boolean a material must pass this to be legal as material come from fusion spell ---@param fusion_spell_matfilter fun(c:Card):boolean a material must pass this to be legal as material come from fusion spell
---@param skip_summon_check boolean whether skip the IsCanBeSpecialSummoned check, for クロック・リザード ---@param skip_summon_check boolean whether skip the IsCanBeSpecialSummoned check, for クロック・リザード
---@param skip_location_count_check boolean Whether skip the location count check, default false, used for 叛逆の堕天使 ---@param skip_location_count_check boolean Whether skip the location count check, default false, used for 叛逆の堕天使
---@param locked_codes integer[] Locked code, material code that can not use substitutes, used for 烙印融合 ---@param locked_codes (integer[]|fun(tc:Card):integer[]) Locked code, material code that can not use substitutes, used for 烙印融合
function FusionSpell.SummonTargetFilter( function FusionSpell.SummonTargetFilter(
c, c,
fusfilter, fusfilter,
...@@ -2932,7 +2940,11 @@ function FusionSpell.SummonTargetFilter( ...@@ -2932,7 +2940,11 @@ function FusionSpell.SummonTargetFilter(
e, e,
mat_operation_code_map) mat_operation_code_map)
aux.FGoalCheckAdditional=FusionSpell.GetFusionSpellFGoalCheckAdditionalFunction(additional_fgoalcheck,tp,c,pre_select_mat_location,e) aux.FGoalCheckAdditional=FusionSpell.GetFusionSpellFGoalCheckAdditionalFunction(additional_fgoalcheck,tp,c,pre_select_mat_location,e)
Fusion.LockedCodes=locked_codes if type(locked_codes)=='function' then
Fusion.LockedCodes=locked_codes(c)
else
Fusion.LockedCodes=locked_codes
end
local chkf=FusionSpell.GetCheckFieldPlayer(tp,skip_location_count_check) local chkf=FusionSpell.GetCheckFieldPlayer(tp,skip_location_count_check)
local res=c:CheckFusionMaterial(mg,gc(e),chkf) local res=c:CheckFusionMaterial(mg,gc(e),chkf)
aux.FCheckAdditional=nil aux.FCheckAdditional=nil
...@@ -2973,8 +2985,9 @@ end ...@@ -2973,8 +2985,9 @@ end
---@param gc fun(e:Effect):Card|nil ---@param gc fun(e:Effect):Card|nil
---@param skip_summon_check boolean whether skip the IsCanBeSpecialSummoned check, for クロック・リザード ---@param skip_summon_check boolean whether skip the IsCanBeSpecialSummoned check, for クロック・リザード
---@param skip_location_count_check boolean Whether skip the location count check, default false, used for 叛逆の堕天使 ---@param skip_location_count_check boolean Whether skip the location count check, default false, used for 叛逆の堕天使
---@param locked_codes (integer[]|fun(tc:Card):integer[]) Locked code, material code that can not use substitutes, used for 烙印融合
---@return boolean res return whether there is a valid target for any chain material effect ---@return boolean res return whether there is a valid target for any chain material effect
function FusionSpell.IsExistsChainMaterialSummonTargets(e,tp,fusfilter,matfilter,additional_fcheck,additional_fgoalcheck,fuslocation,sumtype,sumpos,gc,skip_summon_check,skip_location_count_check) function FusionSpell.IsExistsChainMaterialSummonTargets(e,tp,fusfilter,matfilter,additional_fcheck,additional_fgoalcheck,fuslocation,sumtype,sumpos,gc,skip_summon_check,skip_location_count_check,locked_codes)
local chain_material_effects={Duel.IsPlayerAffectedByEffect(tp,EFFECT_CHAIN_MATERIAL)} local chain_material_effects={Duel.IsPlayerAffectedByEffect(tp,EFFECT_CHAIN_MATERIAL)}
---@type {[Effect]:Group} ---@type {[Effect]:Group}
for _,ce in ipairs(chain_material_effects) do for _,ce in ipairs(chain_material_effects) do
...@@ -2984,7 +2997,7 @@ function FusionSpell.IsExistsChainMaterialSummonTargets(e,tp,fusfilter,matfilter ...@@ -2984,7 +2997,7 @@ function FusionSpell.IsExistsChainMaterialSummonTargets(e,tp,fusfilter,matfilter
if #chain_mg>0 then if #chain_mg>0 then
local ce_fusfilter=ce:GetValue() local ce_fusfilter=ce:GetValue()
local res=Duel.IsExistingMatchingCard(function(c) local res=Duel.IsExistingMatchingCard(function(c)
return FusionSpell.ChainMaterialSummonTargetFilter(c,aux.AND(ce_fusfilter,fusfilter or aux.TRUE),e,tp,chain_mg,additional_fcheck,additional_fgoalcheck,sumtype,sumpos,gc,skip_summon_check,skip_location_count_check) return FusionSpell.ChainMaterialSummonTargetFilter(c,aux.AND(ce_fusfilter,fusfilter or aux.TRUE),e,tp,chain_mg,additional_fcheck,additional_fgoalcheck,sumtype,sumpos,gc,skip_summon_check,skip_location_count_check,locked_codes)
end, end,
tp,fuslocation,0,1,nil) tp,fuslocation,0,1,nil)
if res==true then if res==true then
...@@ -3000,6 +3013,7 @@ end ...@@ -3000,6 +3013,7 @@ end
---@param gc fun(e:Effect):Card|nil ---@param gc fun(e:Effect):Card|nil
---@param skip_summon_check boolean whether skip the IsCanBeSpecialSummoned check, for クロック・リザード ---@param skip_summon_check boolean whether skip the IsCanBeSpecialSummoned check, for クロック・リザード
---@param skip_location_count_check boolean Whether skip the location count check, default false, used for 叛逆の堕天使 ---@param skip_location_count_check boolean Whether skip the location count check, default false, used for 叛逆の堕天使
---@param locked_codes (integer[]|fun(tc:Card):integer[]) Locked code, material code that can not use substitutes, used for 烙印融合
---@return boolean result Whether c could be fusion summoned by this chain material effect ---@return boolean result Whether c could be fusion summoned by this chain material effect
function FusionSpell.ChainMaterialSummonTargetFilter(c,fusfilter,e,tp,mg,additional_fcheck,additional_fgoalcheck,sumtype,sumpos,gc,skip_summon_check,skip_location_count_check,locked_codes) function FusionSpell.ChainMaterialSummonTargetFilter(c,fusfilter,e,tp,mg,additional_fcheck,additional_fgoalcheck,sumtype,sumpos,gc,skip_summon_check,skip_location_count_check,locked_codes)
if not c:IsType(TYPE_FUSION) or fusfilter(c,e,tp)==false then if not c:IsType(TYPE_FUSION) or fusfilter(c,e,tp)==false then
...@@ -3012,7 +3026,11 @@ function FusionSpell.ChainMaterialSummonTargetFilter(c,fusfilter,e,tp,mg,additio ...@@ -3012,7 +3026,11 @@ function FusionSpell.ChainMaterialSummonTargetFilter(c,fusfilter,e,tp,mg,additio
end end
aux.FCheckAdditional=FusionSpell.GetFusionSpellFCheckAdditionalFunctionForChainMaterial(additional_fcheck,e) aux.FCheckAdditional=FusionSpell.GetFusionSpellFCheckAdditionalFunctionForChainMaterial(additional_fcheck,e)
aux.FGoalCheckAdditional=FusionSpell.GetFusionSpellFGoalCheckAdditionalFunctionForChainMaterial(additional_fgoalcheck,e) aux.FGoalCheckAdditional=FusionSpell.GetFusionSpellFGoalCheckAdditionalFunctionForChainMaterial(additional_fgoalcheck,e)
Fusion.LockedCodes=locked_codes if type(locked_codes)=='function' then
Fusion.LockedCodes=locked_codes(c)
else
Fusion.LockedCodes=locked_codes
end
local chkf=FusionSpell.GetCheckFieldPlayer(tp,skip_location_count_check) local chkf=FusionSpell.GetCheckFieldPlayer(tp,skip_location_count_check)
local res=c:CheckFusionMaterial(mg,gc(e),chkf) local res=c:CheckFusionMaterial(mg,gc(e),chkf)
aux.FCheckAdditional=nil aux.FCheckAdditional=nil
...@@ -3400,57 +3418,61 @@ Fusion={} ...@@ -3400,57 +3418,61 @@ Fusion={}
Fusion.LockedCodes=nil Fusion.LockedCodes=nil
function Fusion.AddFusionProcedure(c,opts) -- Refactored Fusion.AddFusionProcedure using the above helpers
local slots=opts.slots function Fusion.AddFusionProcedure(c, opts)
local mat_filter=opts.mat_filter or nil local patterns=Fusion.BuildPatterns(opts)
local fgoalcheck=opts.fgoalcheck or aux.TRUE --=== Metadata registration ===--
-- Register metadata if c:IsStatus(STATUS_COPYING_EFFECT) then return end
if c:IsStatus(STATUS_COPYING_EFFECT) then return end -- collect all fusion codes and compute global min/max across patterns
local mat={} local code_set={}
local min_req=0 local global_min,global_max=math.huge,0
local max_req=0 for _,pat in ipairs(patterns) do
for _,slot in ipairs(slots) do local pmin,pmax=0,0
if slot.match_code then for _,slot in ipairs(pat.slots) do
mat[slot.match_code]=true if slot.match_code then
min_req=min_req+1 code_set[slot.match_code]=true
max_req=max_req+1 pmin=pmin+1
elseif slot.match_codes then pmax=pmax+1
for _,code in ipairs(slot.match_codes) do elseif slot.match_codes then
mat[code]=true for _,cd in ipairs(slot.match_codes) do
end code_set[cd]=true
min_req=min_req+1 end
max_req=max_req+1 pmin=pmin+1
elseif slot.filter then pmax=pmax+1
local prev=slot.filter elseif slot.group then
slot.filter=function(mc,p_tc) return prev(mc,p_tc) and not mc:IsHasEffect(6205579) end pmin=pmin+slot.group.min
min_req=min_req+1 pmax=pmax+slot.group.max
max_req=max_req+1 else
elseif slot.group then -- generic filter-slot
local prev=slot.group.filter pmin=pmin+1
slot.group.filter=function(mc,p_tc) return prev(mc,p_tc) and not mc:IsHasEffect(6205579) end pmax=pmax+1
min_req=min_req+slot.group.min end
max_req=max_req+slot.group.max end
end global_min=math.min(global_min,pmin)
end global_max=math.max(global_max,pmax)
local mt=getmetatable(c) end
if mt.material==nil then
mt.material=mat local mt=getmetatable(c)
end if mt.material==nil then
if mt.material_count==nil then mt.material=code_set
mt.material_count={min_req,max_req} end
end if mt.material_count==nil then
for index,_ in pairs(mat) do mt.material_count={global_min,global_max}
Auxiliary.AddCodeList(c,index) end
end for cd,_ in pairs(code_set) do
-- Register effect Auxiliary.AddCodeList(c,cd)
local e=Effect.CreateEffect(c) end
e:SetType(EFFECT_TYPE_SINGLE)
e:SetProperty(EFFECT_FLAG_CANNOT_DISABLE+EFFECT_FLAG_UNCOPYABLE) --=== Effect registration ===--
e:SetCode(EFFECT_FUSION_MATERIAL) local e = Effect.CreateEffect(c)
e:SetCondition(Fusion.FusionCondition(c,slots,mat_filter,fgoalcheck,true)) e:SetType(EFFECT_TYPE_SINGLE)
e:SetOperation(Fusion.FusionOperation(c,slots,mat_filter,fgoalcheck)) e:SetProperty(EFFECT_FLAG_CANNOT_DISABLE+EFFECT_FLAG_UNCOPYABLE)
e:SetDescription(1379) --- 启用扩展卡包调试模式 e:SetCode(EFFECT_FUSION_MATERIAL)
c:RegisterEffect(e)
e:SetCondition(Fusion.MultiCondition(c,patterns,true))
e:SetOperation(Fusion.MultiOperation(c,patterns))
e:SetDescription(1379)
c:RegisterEffect(e)
end end
---@param allow_extras boolean whether to allow extra materials for checking propose ---@param allow_extras boolean whether to allow extra materials for checking propose
...@@ -3503,7 +3525,6 @@ function Fusion.FusionCondition(tc,slots,mat_filter,fgoalcheck,allow_extras,sele ...@@ -3503,7 +3525,6 @@ function Fusion.FusionCondition(tc,slots,mat_filter,fgoalcheck,allow_extras,sele
for idx,slot in ipairs(single_slots) do for idx,slot in ipairs(single_slots) do
-- lock slot if its match_code equals code -- lock slot if its match_code equals code
if slot.match_code==code then if slot.match_code==code then
Debug.Message(string.format("locked %d, slot %d",code,idx))
locked_slots[idx]=true locked_slots[idx]=true
break break
end end
...@@ -3773,92 +3794,6 @@ function Fusion.MatchSlot(mc,slot,tc) ...@@ -3773,92 +3794,6 @@ function Fusion.MatchSlot(mc,slot,tc)
return false,false return false,false
end end
--- Generator: operation from named slots
---@param slots table[]
function Fusion.FusionOperation(tc,slots,mat_filter,fgoalcheck)
return function(e,tp,eg,ep,ev,re,rp,gc,chkf)
if mat_filter then
local pool=eg
pool=Group.CreateGroup()
for mc in aux.Next(eg) do
if mat_filter(mc,e:GetHandler()) then
pool:AddCard(mc)
end
end
eg=pool
end
-- strict final check (no extras)
local strict_cond=Fusion.FusionCondition(tc,slots,mat_filter,fgoalcheck,false)
-- precompute minimum materials required
local min_req=0
local max_req=0
for _,slot in ipairs(slots) do
if slot.group then
min_req=min_req+slot.group.min
max_req=max_req+slot.group.max
else
min_req=min_req+1
max_req=max_req+1
end
end
max_req=math.min(max_req,eg:GetCount())
-- prompt player
Duel.Hint(HINT_SELECTMSG, tp, HINTMSG_FMATERIAL)
local sg=Group.CreateGroup()
while true do
-- can we finish right now?
local finishable=(sg:GetCount()>=min_req) and strict_cond(e,sg,gc,chkf)
-- build “addable” via exhaustive mapping + completion test
local addable=Group.CreateGroup()
local candidates={}
for mc in aux.Next(eg) do
if not sg:IsContains(mc) then
table.insert(candidates,mc)
end
end
for _,mc in ipairs(candidates) do
sg:AddCard(mc)
if Fusion.CanCompleteFromMappings(e,sg,eg,slots,mat_filter,fgoalcheck,tc,gc,chkf) then
addable:AddCard(mc)
end
sg:RemoveCard(mc)
end
if addable:GetCount()==0 then
assert(finishable==true)
Duel.SetFusionMaterial(sg)
return
end
-- UI: let the player pick or finalize
local picked=Group.SelectUnselect(addable,sg,tp,finishable,true,min_req,max_req)
if not picked then
if finishable then
Duel.SetFusionMaterial(sg)
return -- user clicked “OK”
else
Duel.SetFusionMaterial(Group.CreateGroup())
return -- user clicked “Cancel” on invalid selection
end
end
assert(picked~=nil)
-- toggle selection
if sg:IsContains(picked) then
sg:RemoveCard(picked)
else
sg:AddCard(picked)
end
end
end
end
-- Slot constructors -- Slot constructors
Fusion.Slot={} Fusion.Slot={}
--- Create a slot for a single code (default allow_sub = true) --- Create a slot for a single code (default allow_sub = true)
...@@ -3950,7 +3885,11 @@ function Fusion.FindAllMappings(cards_sel,slots,tc) ...@@ -3950,7 +3885,11 @@ function Fusion.FindAllMappings(cards_sel,slots,tc)
local all_maps={} local all_maps={}
local mapping={} -- mapping[idx_in_cards_sel] = slot_index local mapping={} -- mapping[idx_in_cards_sel] = slot_index
local function dfs(idx) local function dfs(idx,sub_count)
-- prune: no more than 1 substitute
if sub_count>1 then
return
end
if idx>#cards_sel then if idx>#cards_sel then
-- Before recording, run unique_by on each group‐slot that has one -- Before recording, run unique_by on each group‐slot that has one
for i,slot in ipairs(slots) do for i,slot in ipairs(slots) do
...@@ -3988,14 +3927,17 @@ function Fusion.FindAllMappings(cards_sel,slots,tc) ...@@ -3988,14 +3927,17 @@ function Fusion.FindAllMappings(cards_sel,slots,tc)
if not slot.group and not filled[i] then if not slot.group and not filled[i] then
local match,is_sub=Fusion.MatchSlot(mc,slot,tc) local match,is_sub=Fusion.MatchSlot(mc,slot,tc)
if match then if match then
-- skip substitute if slot is locked -- skip if substitute and this slot is locked
if not (is_sub and locked_slots[i]) then if not (is_sub and locked_slots[i]) then
any_match=true local new_sub=sub_count+(is_sub and 1 or 0)
filled[i]=true if new_sub<=1 then
mapping[idx]=i any_match=true
dfs(idx+1) filled[i]=true
filled[i]=false mapping[idx]=i
mapping[idx]=nil dfs(idx+1,new_sub)
filled[i]=false
mapping[idx]=nil
end
end end
end end
end end
...@@ -4050,7 +3992,7 @@ function Fusion.FindAllMappings(cards_sel,slots,tc) ...@@ -4050,7 +3992,7 @@ function Fusion.FindAllMappings(cards_sel,slots,tc)
any_match=true any_match=true
group_assigned[i]=assigned + 1 group_assigned[i]=assigned + 1
mapping[idx]=i mapping[idx]=i
dfs(idx+1) dfs(idx+1,sub_count)
group_assigned[i]=assigned group_assigned[i]=assigned
mapping[idx]=nil mapping[idx]=nil
end end
...@@ -4065,7 +4007,7 @@ function Fusion.FindAllMappings(cards_sel,slots,tc) ...@@ -4065,7 +4007,7 @@ function Fusion.FindAllMappings(cards_sel,slots,tc)
end end
end end
dfs(1) dfs(1,0)
return all_maps return all_maps
end end
...@@ -4366,3 +4308,168 @@ function Fusion.MakeExcludeFilter(orig_filter,key_func,used_keys) ...@@ -4366,3 +4308,168 @@ function Fusion.MakeExcludeFilter(orig_filter,key_func,used_keys)
return true return true
end end
end end
-- Helper: build patterns list (single or variants)
function Fusion.BuildPatterns(opts)
local patterns={}
if opts.variants then
for _, v in ipairs(opts.variants) do
table.insert(patterns,{
slots=v.slots,
mat_filter=v.mat_filter,
fgoalcheck=v.fgoalcheck or aux.TRUE,
})
end
else
table.insert(patterns,{
slots=opts.slots,
mat_filter=opts.mat_filter,
fgoalcheck=opts.fgoalcheck or aux.TRUE,
})
end
return patterns
end
-- Helper: combine multiple patterns into one condition function (no goto)
function Fusion.MultiCondition(tc,patterns,allow_extras)
return function(e,g,gc,chkf)
if not g then return false end
local locked=Fusion.LockedCodes
for _, pat in ipairs(patterns) do
-- skip patterns that don't include all locked codes
local ok=true
if locked then
for _,code in ipairs(locked) do
if not Fusion.PatternIncludesCode(pat,code) then
ok=false
break
end
end
end
if ok then
local cond=Fusion.FusionCondition(tc,pat.slots,pat.mat_filter,pat.fgoalcheck,allow_extras)
if cond(e,g,gc,chkf) then
return true
end
end
end
return false
end
end
-- Helper: compute the minimal and maximal material counts across patterns
function Fusion.PatternsMinMax(patterns)
local min_req=math.huge
local max_req=0
for _, pat in ipairs(patterns) do
local lo,hi=0,0
for _, slot in ipairs(pat.slots) do
if slot.group then
lo=lo+slot.group.min
hi=hi+slot.group.max
else
lo=lo+1
hi=hi+1
end
end
min_req=math.min(min_req,lo)
max_req=math.max(max_req,hi)
end
return min_req,max_req
end
-- Helper: combine patterns into one operation function
function Fusion.MultiOperation(tc, patterns)
return function(e,tp,eg,ep,ev,re,rp,gc,chkf)
-- filter patterns by any locked codes, once per operation
local locked=Fusion.LockedCodes
local active_patterns={}
if locked then
for _,pat in ipairs(patterns) do
local ok=true
for _, code in ipairs(locked) do
if not Fusion.PatternIncludesCode(pat,code) then
ok=false
break
end
end
if ok then table.insert(active_patterns,pat) end
end
else
-- no locked codes: all patterns active
active_patterns=patterns
end
-- compute min/max over active patterns
local min_req,max_req=Fusion.PatternsMinMax(active_patterns)
max_req=math.min(max_req,eg:GetCount())
-- strict condition uses only active patterns
local strict_cond=Fusion.MultiCondition(tc,active_patterns,false)
Duel.Hint(HINT_SELECTMSG, tp, HINTMSG_FMATERIAL)
local sg=Group.CreateGroup()
--- arraify eg
local eg_arr={}
for mc in aux.Next(eg) do
table.insert(eg_arr,mc)
end
while true do
local finishable=sg:GetCount()>=min_req and strict_cond(e, sg, gc, chkf)
local addable=Group.CreateGroup()
for _,mc in ipairs(eg_arr) do
if not sg:IsContains(mc) then
sg:AddCard(mc)
-- completion test against active patterns
for _,pat in ipairs(active_patterns) do
if Fusion.CanCompleteFromMappings(e,sg,eg,pat.slots,pat.mat_filter,pat.fgoalcheck,tc,gc,chkf) then
addable:AddCard(mc)
break
end
end
sg:RemoveCard(mc)
end
end
if addable:GetCount()==0 then
assert(finishable)
Duel.SetFusionMaterial(sg)
return
end
local picked=Group.SelectUnselect(addable,sg,tp,finishable,true,min_req,max_req)
if not picked then
if finishable then
Duel.SetFusionMaterial(sg)
else
Duel.SetFusionMaterial(Group.CreateGroup())
end
return
end
if sg:IsContains(picked) then
sg:RemoveCard(picked)
else
sg:AddCard(picked)
end
end
end
end
-- Helper: detect if a pattern has a slot for a given code
function Fusion.PatternIncludesCode(pat,code)
for _, slot in ipairs(pat.slots) do
if slot.match_code==code then
return true
end
if slot.match_codes then
for _, c in ipairs(slot.match_codes) do
if c==code then return true end
end
end
end
return false
end
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