Skip to content
Snippets Groups Projects
Commit 6b9ff5eb authored by DarkGod's avatar DarkGod
Browse files

Merge branch 'Effigy/t-engine4-wardens_call_fix'

parents f85f439c 90cdbe42
No related branches found
No related tags found
No related merge requests found
......@@ -184,6 +184,7 @@ function _M:clone(t)
return n
end
--- Automatically called by cloneFull()
local function clonerecursfull(clonetable, d, noclonecall, use_saveinstead)
if use_saveinstead and (d.__ATOMIC or d.__CLASSNAME) and d.__SAVEINSTEAD then
d = d.__SAVEINSTEAD
......@@ -215,19 +216,95 @@ local function clonerecursfull(clonetable, d, noclonecall, use_saveinstead)
return n, nb
end
--- Clones the object, all subobjects without cloning twice a subobject
-- @return the clone and the number of cloned objects
function _M:cloneFull(t)
--- Clones the object, and all subobjects without cloning a subobject twice
-- @param[type=table] self Object to be cloned.
-- @param[type=table] post_copy Optional, a table to be merged with the new object after cloning.
-- @return a reference to the clone
function _M:cloneFull(post_copy)
local clonetable = {}
local n = clonerecursfull(clonetable, self, nil, nil)
if t then
for k, e in pairs(t) do n[k] = e end
if post_copy then
for k, e in pairs(post_copy) do n[k] = e end
end
return n
-- return core.serial.cloneFull(self)
end
--- Clones the object, all subobjects without cloning twice a subobject
--- Automatically called by cloneCustom()
local function cloneCustomRecurs(clonetable, d, noclonecall, use_saveinstead, alt_nodes)
if use_saveinstead and (d.__ATOMIC or d.__CLASSNAME) and d.__SAVEINSTEAD then
d = d.__SAVEINSTEAD
if clonetable[d] then return d, 1 end
end
local nb = 0
local add
local n = {}
clonetable[d] = n
local k, e = next(d)
while k do
local skip = false
local nk_alt, ne_alt = nil, nil
if alt_nodes then
for node, alt in pairs(alt_nodes) do
if node == k or node == e then
if alt == false then
skip = true
break
elseif type(alt) == "table" then
if alt.k ~= nil and node == k then nk_alt = alt.k end
if alt.v ~= nil then ne_alt = alt.v end
break
end
end
end
end
if not skip then
local nk, ne
if nk_alt ~= nil then nk = nk_alt else nk = k end
if ne_alt ~= nil then ne = ne_alt else ne = e end
if clonetable[nk] then nk = clonetable[nk]
elseif type(nk) == "table" then nk, add = cloneCustomRecurs(clonetable, nk, noclonecall, use_saveinstead, alt_nodes) nb = nb + add
end
if clonetable[ne] then ne = clonetable[ne]
elseif type(ne) == "table" and (type(nk) ~= "string" or nk ~= "__threads") then ne, add = cloneCustomRecurs(clonetable, ne, noclonecall, use_saveinstead, alt_nodes) nb = nb + add
end
n[nk] = ne
end
k, e = next(d, k)
end
setmetatable(n, getmetatable(d))
if not noclonecall and n.cloned and (n.__ATOMIC or n.__CLASSNAME) then n:cloned(d) end
if n.__ATOMIC or n.__CLASSNAME then nb = nb + 1 end
return n, nb
end
--- Clone the object, with custom logic
-- Based on cloneFull(), with added functionality to skip/replace specified nodes.
-- @param[type=table] self Object to be cloned.
-- @param[type=table] alt_nodes Optional, these nodes will use a specified key/value on the clone instead of copying from the target.
-- @ Table keys should be the nodes to skip/replace (field name or table reference).
-- @ Each key should be set to false (to skip assignment entirely) or a table with up to two nodes:
-- @ k = a name/ref to substitute for instances of this field,
-- @ or nil to use the default name/ref as keys on the clone
-- @ v = the value to assign for instances of this node,
-- @ or nil to use the default assignment value
-- @param[type=table] post_copy Optional, a table to be merged with the new object after cloning.
-- @return a reference to the clone
function _M:cloneCustom(alt_nodes, post_copy)
local clonetable = {}
local n = cloneCustomRecurs(clonetable, self, nil, nil, alt_nodes)
if post_copy then
for k, e in pairs(post_copy) do n[k] = e end
end
return n
end
--- Clones the object, and all subobjects without cloning a subobject twice
-- Does not invoke clone methods as this is not for reloading, just for saving
-- @return the clone and the number of cloned objects
function _M:cloneForSave()
......
......@@ -274,48 +274,80 @@ checkWardenFocus = function(self)
end
-- Spell functions
makeParadoxClone = function(self, target, duration)
-- Don't clone particles or inventory on short lived clones
local restore = false
local old_particles, old_inven
--- Create a temporal clone
-- @param[type=table] self Actor doing the cloning. Not currently used.
-- @param[type=table] target Actor to be cloned.
-- @param[type=int] duration How many turns the clone lasts. Zero is allowed.
-- @param[type=table] alt_nodes Optional, these nodes will use a specified key/value on the clone instead of copying from the target.
-- @ Table keys should be the nodes to skip/replace (field name or table reference).
-- @ Each key should be set to false (to skip assignment entirely) or a table with up to two nodes:
-- @ k = a name/ref to substitute for instances of this field,
-- @ or nil to use the default name/ref as keys on the clone
-- @ v = the value to assign for instances of this node,
-- @ or nil to use the default assignent value
-- @return a reference to the clone on success, or nil on failure
makeParadoxClone = function(self, target, duration, alt_nodes)
if not target or not duration then return nil end
if duration < 0 then duration = 0 end
-- Don't copy certain properties from the target
alt_nodes = alt_nodes or {}
alt_nodes[target:getInven("INVEN")] = false -- Skip main inventory; equipped items are still copied
alt_nodes.quests = false
alt_nodes.random_escort_levels = false
alt_nodes.achievements = false
alt_nodes.achievement_data = false
alt_nodes.last_learnt_talents = false
alt_nodes.died = false
alt_nodes.died_times = false
alt_nodes.killedBy = false
alt_nodes.all_kills = false
alt_nodes.all_kills_kind = false
alt_nodes.running_fov = false
alt_nodes.running_prev = false
alt_nodes._mo = false
alt_nodes._last_mo = false
alt_nodes.add_mos = false
alt_nodes.add_displays = false
-- Don't copy some additional properties for short-lived clones
if duration == 0 then
old_particles = target.__particles
old_inventory = target.inven[target.INVEN_INVEN]
target.__particles = {}
target.inven[target.INVEN_INVEN] = nil
restore = true
alt_nodes.__particles = {v = {} }
alt_nodes.hotkey = false
alt_nodes.talents_auto = {v = {} }
alt_nodes.talents_confirm_use = false
end
-- Clone them
local m = target:cloneFull{
no_drops = true,
keep_inven_on_death = false,
faction = target.faction,
summoner = target, summoner_gain_exp=true,
summon_time = duration,
ai_target = {actor=nil},
ai = "summoned", ai_real = "tactical",
name = ""..target.name.."'s temporal clone",
desc = [[A creature from another timeline.]],
}
-- Clone the target
local m = target:cloneCustom(alt_nodes)
-- restore values if needed
if restore then
target.__particles = old_particles
target.inven[target.INVEN_INVEN] = old_inventory
end
-- Basic setup
m.dead = false
m.no_drops = true
m.keep_inven_on_death = false
m.faction = target.faction
m.summoner = target
m.summoner_gain_exp = true
m.summon_time = duration
m.ai_target = {actor = nil}
m.ai = "summoned"
m.ai_real = "tactical"
m.name = "" .. target.name .. "'s temporal clone"
m.desc = [[A creature from another timeline.]]
-- remove some values
m:removeAllMOs()
-- Remove some values
--m:removeAllMOs()
m.make_escort = nil
m.escort_quest = nil
m.on_added_to_level = nil
m.on_added = nil
m.game_ender = nil
mod.class.NPC.castAs(m)
engine.interface.ActorAI.init(m, m)
-- change some values
-- Change some values
m.exp_worth = 0
m.energy.value = 0
m.player = nil
......@@ -332,9 +364,10 @@ makeParadoxClone = function(self, target, duration)
m.seen_by = nil
m.can_talk = nil
m.clone_on_hit = nil
m.escort_quest = nil
m.unused_talents = 0
m.unused_generics = 0
m.unused_talents_types = 0
m.unused_prodigies = 0
if m.talents.T_SUMMON then m.talents.T_SUMMON = nil end
if m.talents.T_MULTIPLY then m.talents.T_MULTIPLY = nil end
......@@ -353,10 +386,10 @@ makeParadoxClone = function(self, target, duration)
m:unlearnTalentFull(t.id)
end
-- remove timed effects
-- Remove timed effects
m:removeTimedEffectsOnClone()
-- reset folds for our Warden clones
-- Reset folds for our Warden clones
for tid, cd in pairs(m.talents_cd) do
local t = m:getTalentFromId(tid)
if t.type[1]:find("^chronomancy/manifold") and m:knowTalent(tid) then
......@@ -364,9 +397,10 @@ makeParadoxClone = function(self, target, duration)
end
end
-- And finally, a bit of sanity in case anyone decides they should blow up the world..
-- A bit of sanity in case anyone decides they should blow up the world..
if m.preferred_paradox and m.preferred_paradox > 600 then m.preferred_paradox = 600 end
-- Prevent respawning
m.self_resurrect = nil
return m
......@@ -379,4 +413,4 @@ checkTimeline = function(self)
else
return true
end
end
\ No newline at end of file
end
......@@ -283,6 +283,19 @@ newTalent{
return tgts
end,
cleanupClone = function(self, t, clone)
if not self or not clone then return false end
if not clone.dead then clone:die() end
for _, ent in pairs(game.level.entities) do
-- Replace clone references in timed effects so they don't prevent GC
if ent.tmp then
for _, eff in pairs(ent.tmp) do
if eff.src and eff.src == clone then eff.src = self end
end
end
end
return true
end,
callbackOnArcheryAttack = function(self, t, target, hitted)
if hitted then
if self.turn_procs.wardens_call then
......@@ -328,6 +341,7 @@ newTalent{
end
end
end
t.cleanupClone(self, t, m)
end
end,
callbackOnMeleeAttack = function(self, t, target, hitted)
......@@ -353,7 +367,7 @@ newTalent{
self:die()
game.level.map:particleEmitter(self.x, self.y, 1, "temporal_teleport")
end
-- Find a good location for our shot
local function find_space(self, target, clone)
local poss = {}
......@@ -394,6 +408,7 @@ newTalent{
end
end
end
t.cleanupClone(self, t, m)
end
end,
info = function(self, t)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment