From 1a12541512e4f90a7a4f442eb16eaf71fc0e40b5 Mon Sep 17 00:00:00 2001
From: dg <dg@51575b47-30f0-44d4-a5cc-537603b46e54>
Date: Sun, 29 Jul 2012 15:02:51 +0000
Subject: [PATCH] Particle effects will always appear on top of actors, even
 for moddable tiles actors or for tall actors

git-svn-id: http://svn.net-core.org/repos/t-engine4@5386 51575b47-30f0-44d4-a5cc-537603b46e54
---
 game/engines/default/engine/Actor.lua         | 24 +++++-
 game/engines/default/engine/Entity.lua        | 16 ++--
 game/engines/default/engine/Map.lua           |  4 +-
 game/engines/default/engine/class.lua         |  1 +
 game/modules/tome/class/Actor.lua             | 84 ++++++++++++-------
 game/modules/tome/class/NicerTiles.lua        |  1 +
 game/modules/tome/class/Object.lua            | 22 ++---
 game/modules/tome/data/quests/escort-duty.lua |  2 +-
 .../modules/tome/data/quests/paradoxology.lua |  2 +-
 .../data/talents/gifts/summon-utility.lua     |  2 +-
 .../tome/data/timed_effects/physical.lua      |  6 +-
 game/modules/tome/data/zones/arena/zone.lua   |  2 +-
 .../data/zones/crypt-kryl-feijan/npcs.lua     |  2 +-
 .../tome/data/zones/high-peak/grids.lua       |  2 +-
 .../tome/data/zones/shadow-crypt/npcs.lua     |  2 +-
 15 files changed, 111 insertions(+), 61 deletions(-)

diff --git a/game/engines/default/engine/Actor.lua b/game/engines/default/engine/Actor.lua
index 9bd7a22720..4a830e6c2a 100644
--- a/game/engines/default/engine/Actor.lua
+++ b/game/engines/default/engine/Actor.lua
@@ -98,7 +98,7 @@ function _M:defineDisplayCallback()
 	local f_enemy = nil
 	local f_neutral = nil
 
-	self._mo:displayCallback(function(x, y, w, h)
+	local function particles(x, y, w, h)
 		local e
 		for i = 1, #ps do
 			e = ps[i]
@@ -107,7 +107,9 @@ function _M:defineDisplayCallback()
 			else self:removeParticles(e)
 			end
 		end
+	end
 
+	local function tactical(x, y, w, h)
 		-- Tactical info
 		if game.level and game.level.map.view_faction then
 			local map = game.level.map
@@ -138,8 +140,24 @@ function _M:defineDisplayCallback()
 				end
 			end
 		end
-		return true
-	end)
+	end
+
+	if self._mo == self._last_mo then
+		self._mo:displayCallback(function(x, y, w, h)
+			tactical(x, y, w, h)
+			particles(x, y, w, h)
+			return true
+		end)
+	else
+		self._mo:displayCallback(function(x, y, w, h)
+			tactical(x, y, w, h)
+			return true
+		end)
+		self._last_mo:displayCallback(function(x, y, w, h)
+			particles(x, y, w, h)
+			return true
+		end)
+	end
 end
 
 --- Moves an actor on the map
diff --git a/game/engines/default/engine/Entity.lua b/game/engines/default/engine/Entity.lua
index e9539e50ff..6c582f1b4e 100644
--- a/game/engines/default/engine/Entity.lua
+++ b/game/engines/default/engine/Entity.lua
@@ -248,7 +248,7 @@ function _M:makeMapObject(tiles, idx)
 		if not self.add_displays or not self.add_displays[idx-1] then return nil end
 		return self.add_displays[idx-1]:makeMapObject(tiles, 1)
 	else
-		if self._mo and self._mo:isValid() then return self._mo, self.z end
+		if self._mo and self._mo:isValid() then return self._mo, self.z, self._last_mo end
 	end
 
 	-- Create the map object with 1 + additional textures
@@ -264,7 +264,7 @@ function _M:makeMapObject(tiles, idx)
 		self:check("display_scale") or 1
 	)
 
-	self:defineDisplayCallback()
+	local last_mo = self._mo
 
 	-- Setup tint
 	self._mo:tint(self.tint_r or 1, self.tint_g or 1, self.tint_b or 1)
@@ -294,6 +294,7 @@ function _M:makeMapObject(tiles, idx)
 			end
 			cmo:chain(mo)
 			cmo = mo
+			last_mo = mo
 		end
 	end
 
@@ -315,7 +316,7 @@ function _M:makeMapObject(tiles, idx)
 		if shad.shad then self._mo:shader(shad.shad) end
 	end
 
-	return self._mo, self.z
+	return self._mo, self.z, last_mo
 end
 
 --- Get all "map objects" representing this entity
@@ -325,20 +326,25 @@ end
 function _M:getMapObjects(tiles, mos, z)
 	local i = -1
 	local nextz = 0
-	local mo, dz
+	local mo, dz, lm
+	local last_mo
 	repeat
 		i = i + 1
-		mo, dz = self:makeMapObject(tiles, 1+i)
+		mo, dz, lm = self:makeMapObject(tiles, 1+i)
 		if mo then
 			if dz then mos[dz] = mo
 			else mos[z + nextz] = mo nextz = nextz + 1 end
+			last_mo = lm
 		end
 	until not mo
+	self._last_mo = last_mo
+	self:defineDisplayCallback()
 end
 
 function _M:removeAllMOs(no_invalidate)
 	if self._mo and not no_invalidate then self._mo:invalidate() end
 	self._mo = nil
+	self._last_mo = nil
 
 	if not self.add_displays then return end
 	for i = 1, #self.add_displays do
diff --git a/game/engines/default/engine/Map.lua b/game/engines/default/engine/Map.lua
index 91ad799023..2ea72fbc6e 100644
--- a/game/engines/default/engine/Map.lua
+++ b/game/engines/default/engine/Map.lua
@@ -346,11 +346,13 @@ function _M:close()
 			if e and e._mo then
 				e._mo:invalidate()
 				e._mo = nil
+				e._last_mo = nil
 			end
 			if e and e.add_displays then for i, se in ipairs(e.add_displays) do
 				if se._mo then
 					se._mo:invalidate()
 					se._mo = nil
+					se._last_mo = nil
 				end
 			end end
 			if e then e:closeParticles() end
@@ -768,7 +770,7 @@ function _M:moveViewSurround(x, y, marginx, marginy, ignore_padding)
 		elseif self.my + self.viewport.mheight - marginy - self.viewport_padding_2 <= y then
 			self.my = y - self.viewport.mheight + marginy + self.viewport_padding_2
 			self.changed = true
-		end	
+		end
 	end
 --[[
 	if self.mx + marginx >= x or self.mx + self.viewport.mwidth - marginx <= x then
diff --git a/game/engines/default/engine/class.lua b/game/engines/default/engine/class.lua
index 8f6dba8dea..769a6945d9 100644
--- a/game/engines/default/engine/class.lua
+++ b/game/engines/default/engine/class.lua
@@ -262,6 +262,7 @@ function _M:save(filter, allow)
 		filter.new = true
 		filter._no_save_fields = true
 		filter._mo = true
+		filter._last_mo = true
 		filter._mo_final = true
 		filter._hooks = true
 	else
diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua
index 7400c153ca..2b60589ca5 100644
--- a/game/modules/tome/class/Actor.lua
+++ b/game/modules/tome/class/Actor.lua
@@ -165,7 +165,7 @@ function _M:init(t, no_default)
 
 	t.flat_damage_armor = t.flat_damage_armor or {}
 	t.flat_damage_cap = t.flat_damage_cap or {}
-	
+
 	-- Default regen
 	t.air_regen = t.air_regen or 3
 	t.mana_regen = t.mana_regen or 0.5
@@ -380,7 +380,7 @@ function _M:actBase()
 			self:forceUseTalent(self.T_MIND_LINK, {ignore_energy=true})
 		end
 	end
-		
+
 	-- Cooldown talents
 	if not self:attr("no_talents_cooldown") then self:cooldownTalents() end
 	-- Regen resources
@@ -398,7 +398,7 @@ function _M:actBase()
 	end
 
 	self:regenResources()
-	
+
 	-- update psionic feedback
 	if self:getFeedback() > 0 then
 		local decay = self:getFeedbackDecay()
@@ -493,7 +493,7 @@ function _M:actBase()
 				t.doMindStorm(self, t, p)
 			end
 		end
-		
+
 		self:triggerHook{"Actor:actBase:Effects"}
 	end
 
@@ -784,7 +784,7 @@ function _M:defineDisplayCallback()
 	local f_neutral = nil
 	local ichat = nil
 
-	self._mo:displayCallback(function(x, y, w, h, zoom, on_map)
+	local function tactical(x, y, w, h, zoom, on_map)
 		-- Tactical info
 		if game.level and game.always_target then
 			-- Tactical life info
@@ -849,7 +849,9 @@ function _M:defineDisplayCallback()
 				end
 			end
 		end
+	end
 
+	local function particles(x, y, w, h, zoom, on_map)
 		-- Chat
 		if game.level and self.can_talk then
 			local map = game.level.map
@@ -861,16 +863,33 @@ function _M:defineDisplayCallback()
 		end
 
 		local e
+		local dy = 0
+		if h > w then dy = (h - w) / 2 end
 		for i = 1, #ps do
 			e = ps[i]
 			e:checkDisplay()
-			if e.ps:isAlive() then e.ps:toScreen(x + w / 2, y + h / 2, true, w / (game.level and game.level.map.tile_w or w))
+			if e.ps:isAlive() then e.ps:toScreen(x + w / 2, y + dy + h / 2, true, w / (game.level and game.level.map.tile_w or w))
 			else self:removeParticles(e)
 			end
 		end
+	end
 
-		return true
-	end)
+	if self._mo == self._last_mo then
+		self._mo:displayCallback(function(x, y, w, h, zoom, on_map)
+			tactical(x, y, w, h, zoom, on_map)
+			particles(x, y, w, h, zoom, on_map)
+			return true
+		end)
+	else
+		self._mo:displayCallback(function(x, y, w, h, zoom, on_map)
+			tactical(x, y, w, h, zoom, on_map)
+			return true
+		end)
+		self._last_mo:displayCallback(function(x, y, w, h, zoom, on_map)
+			particles(x, y, w, h, zoom, on_map)
+			return true
+		end)
+	end
 end
 
 function _M:move(x, y, force)
@@ -946,7 +965,7 @@ function _M:move(x, y, force)
 	if moved and self:isTalentActive(self.T_BODY_OF_STONE) then
 		self:forceUseTalent(self.T_BODY_OF_STONE, {ignore_energy=true})
 	end
-	
+
 	if moved then
 		self:breakPsionicChannel()
 	end
@@ -1306,7 +1325,7 @@ function _M:tooltip(x, y, seen_by)
 
 	local resists = {}
 	for t, v in pairs(self.resists) do
-		if v ~= 0 then 
+		if v ~= 0 then
 			if t ~= "all" then v = self:combatGetResist(t) end
 			resists[#resists+1] = string.format("%d%% %s", v, t == "all" and "all" or DamageType:get(t).name)
 		end
@@ -1367,7 +1386,7 @@ end
 function _M:regenLife()
 	if self.life_regen and not self:attr("no_life_regen") then
 		local regen = self.life_regen * util.bound((self.healing_factor or 1), 0, 2.5)
-		
+
 		-- Solipsism
 		if self:knowTalent(self.T_SOLIPSISM) then
 			local t = self:getTalentFromId(self.T_SOLIPSISM)
@@ -1379,9 +1398,9 @@ function _M:regenLife()
 				regen = regen - psi_increase
 			end
 		end
-		
+
 		self.life = util.bound(self.life + regen, self.die_at, self.max_life)
-		
+
 		-- Blood Lock
 		if self:attr("blood_lock") then
 			self.life = util.bound(self.life, self.die_at, self:attr("blood_lock"))
@@ -1441,7 +1460,7 @@ function _M:onHeal(value, src)
 		self:incPsi(psi_increase)
 		value = value - psi_increase
 	end
-	
+
 	-- Must be last!
 	if self:attr("blood_lock") then
 		if self.life + value > self:attr("blood_lock") then
@@ -1498,7 +1517,7 @@ function _M:onTakeHit(value, src)
 		local t = self:getTalentFromId(self.T_DISMISSAL)
 		value = t.doDismissalOnHit(self, value, src, t)
 	end
-		
+
 	if self:attr("retribution") then
 	-- Absorb damage into the retribution
 		if value / 2 <= self.retribution_absorb then
@@ -1628,13 +1647,13 @@ function _M:onTakeHit(value, src)
 			t.explode(self, t, dam)
 		end
 	end
-	
+
 	if self:isTalentActive(self.T_BONE_SHIELD) then
 		local t = self:getTalentFromId(self.T_BONE_SHIELD)
 		t.absorb(self, t, self:isTalentActive(self.T_BONE_SHIELD))
 		value = 0
 	end
-	
+
 	if self.knowTalent and (self:knowTalent(self.T_SEETHE) or self:knowTalent(self.T_GRIM_RESOLVE)) then
 		if not self:hasEffect(self.EFF_CURSED_FORM) then
 			self:setEffect(self.EFF_CURSED_FORM, 1, { increase=0 })
@@ -1677,7 +1696,7 @@ function _M:onTakeHit(value, src)
 			eff.begone = game.turn
 		end
 	end
-	
+
 	-- Feedback pool: Stores damage as energy to use later
 	if self:getMaxFeedback() > 0 and src ~= self and src ~= self.summoner then
 		local ratio = 0.5
@@ -1714,7 +1733,7 @@ function _M:onTakeHit(value, src)
 			self:removeEffect(self.EFF_RESONANCE_FIELD)
 		end
 	end
-	
+
 	-- Reduce sleep durations
 	if self:attr("sleep") then
 		local effs = {}
@@ -1737,7 +1756,7 @@ function _M:onTakeHit(value, src)
 			end
 		end
 	end
-		
+
 	-- Solipsism
 	if self:knowTalent(self.T_SOLIPSISM) then
 		local t = self:getTalentFromId(self.T_SOLIPSISM)
@@ -2198,9 +2217,9 @@ function _M:die(src, death_note)
 			t.spawn_ghoul(p.src, self, t)
 		end
 	end
-	
+
 	if src and self:attr("sleep") and src.isTalentActive and src:isTalentActive(src.T_NIGHT_TERROR) then
-		local t = src:getTalentFromId(src.T_NIGHT_TERROR) 
+		local t = src:getTalentFromId(src.T_NIGHT_TERROR)
 		t.summonNightTerror(src, self, t)
 	end
 
@@ -2599,13 +2618,13 @@ function _M:onWear(o, bypass_set)
 		self:attr("spellpower_reduction", 1)
 		self:attr("spell_failure", (o.material_level or 1) * 10)
 	end
-	
+
 	-- Apply Psychometry
 	if self:knowTalent(self.T_PSYCHOMETRY) then
 		local t = self:getTalentFromId(self.T_PSYCHOMETRY)
 		t.updatePsychometryCount(self, t)
 	end
-	
+
 	-- Learn Talent
 	if o.wielder and o.wielder.learn_talent then
 		for tid, level in pairs(o.wielder.learn_talent) do
@@ -2674,7 +2693,7 @@ function _M:onTakeoff(o, bypass_set)
 		self:attr("spellpower_reduction", -1)
 		self:attr("spell_failure", -(o.material_level or 1) * 10)
 	end
-	
+
 	-- Apply Psychometry
 	if self:knowTalent(self.T_PSYCHOMETRY) then
 		local t = self:getTalentFromId(self.T_PSYCHOMETRY)
@@ -3101,13 +3120,13 @@ function _M:incMaxFeedback(v, set)
 	if not self.psionic_feedback then
 		self.psionic_feedback = 0
 	end
-	
+
 	if not set then
 		self.psionic_feedback_max = (self.psionic_feedback_max or 0) + v
 	else
 		self.psionic_feedback_max = v
 	end
-	
+
 	-- auto unlearn feedback if below 0
 	if self.psionic_feedback_max <= 0 then
 		self.psionic_feedback = nil
@@ -3123,7 +3142,7 @@ function _M:getFeedbackDecay()
 		return 0
 	end
 end
-	
+
 
 --- Called before a talent is used
 -- Check the actor can cast it
@@ -3610,9 +3629,12 @@ function _M:getTalentFullDescription(t, addlevel, config)
 	local d = tstring{}
 
 	d:add({"color",0x6f,0xff,0x83}, "Effective talent level: ", {"color",0x00,0xFF,0x00}, ("%.1f"):format(self:getTalentLevel(t)), true)
-	if t.mode == "passive" then d:add({"color",0x6f,0xff,0x83}, "Use mode: ", {"color",0x00,0xFF,0x00}, "Passive", true)
-	elseif t.mode == "sustained" then d:add({"color",0x6f,0xff,0x83}, "Use mode: ", {"color",0x00,0xFF,0x00}, "Sustained", true)
-	else d:add({"color",0x6f,0xff,0x83}, "Use mode: ", {"color",0x00,0xFF,0x00}, "Activated", true)
+
+	if not config.ignore_mode then
+		if t.mode == "passive" then d:add({"color",0x6f,0xff,0x83}, "Use mode: ", {"color",0x00,0xFF,0x00}, "Passive", true)
+		elseif t.mode == "sustained" then d:add({"color",0x6f,0xff,0x83}, "Use mode: ", {"color",0x00,0xFF,0x00}, "Sustained", true)
+		else d:add({"color",0x6f,0xff,0x83}, "Use mode: ", {"color",0x00,0xFF,0x00}, "Activated", true)
+		end
 	end
 
 	if config.custom then
diff --git a/game/modules/tome/class/NicerTiles.lua b/game/modules/tome/class/NicerTiles.lua
index 7a20006d14..da5373be4c 100644
--- a/game/modules/tome/class/NicerTiles.lua
+++ b/game/modules/tome/class/NicerTiles.lua
@@ -135,6 +135,7 @@ function _M:replaceAll(level)
 					end
 					if e.add_mos_shader then gd.shader = e.add_mos_shader end
 					gd._mo = nil
+					gd._last_mo = nil
 				end
 				if e.add_displays then
 					g.add_displays = g.add_displays or {}
diff --git a/game/modules/tome/class/Object.lua b/game/modules/tome/class/Object.lua
index c7de5dec32..6a26597f99 100644
--- a/game/modules/tome/class/Object.lua
+++ b/game/modules/tome/class/Object.lua
@@ -529,7 +529,7 @@ function _M:getTextualDesc(compare_with)
 		if special ~= "" then
 			desc:add(found and {"color","WHITE"} or {"color","GREEN"}, "Special effect when this weapon crits: "..special, {"color","LAST"}, true)
 		end
-		
+
 		local special = ""
 		if combat.special_on_kill then
 			special = combat.special_on_kill.desc
@@ -957,17 +957,17 @@ function _M:getTextualDesc(compare_with)
 
 		compare_fields(w, compare_with, field, "nature_summon_max", "%+d", "Max wilder summons: ")
 		compare_fields(w, compare_with, field, "nature_summon_regen", "%+.2f", "Life regen bonus (wilder-summons): ")
-		
+
 		compare_fields(w, compare_with, field, "slow_projectiles", "%+d%%", "Slows Projectiles: ")
 
 		if w.undead then
 			desc:add("The wearer is treated as an undead.", true)
 		end
-		
+
 		if w.demon then
 			desc:add("The wearer is treated as a demon.", true)
 		end
-		
+
 		if w.blind then
 			desc:add("The wearer is blinded.", true)
 		end
@@ -975,7 +975,7 @@ function _M:getTextualDesc(compare_with)
 		if w.blind_fight then
 			desc:add({"color", "YELLOW"}, "Blind-Fight:", {"color", "LAST"}, "This item allows the wearer to attack unseen targets without any penalties.", true)
 		end
-		
+
 		if w.quick_weapon_swap then
 			desc:add({"color", "YELLOW"}, "Quick Weapon Swap:", {"color", "LAST"}, "This item allows the wearer to swap to their secondary weapon without spending a turn.", true)
 		end
@@ -1137,7 +1137,7 @@ function _M:getUseDesc()
 		ret = tstring{{"color","YELLOW"}, ("It can be used to %s."):format(self.use_simple.name), {"color","LAST"}}
 	elseif self.use_talent then
 		local t = game.player:getTalentFromId(self.use_talent.id)
-		local desc = game.player:getTalentFullDescription(t, nil, {force_level=self.use_talent.level, ignore_cd=true, ignore_ressources=true, ignore_use_time=true, custom=self.use_talent.power and tstring{{"color",0x6f,0xff,0x83}, "Power cost: ", {"color",0x7f,0xff,0xd4},("%d out of %d/%d."):format(self.use_talent.power, self.power, self.max_power)}})
+		local desc = game.player:getTalentFullDescription(t, nil, {force_level=self.use_talent.level, ignore_cd=true, ignore_ressources=true, ignore_use_time=true, ignore_mode=true, custom=self.use_talent.power and tstring{{"color",0x6f,0xff,0x83}, "Power cost: ", {"color",0x7f,0xff,0xd4},("%d out of %d/%d."):format(self.use_talent.power, self.power, self.max_power)}})
 		if self.talent_cooldown then
 			ret = tstring{{"color","YELLOW"}, "It can be used to activate talent ", t.name,", placing all other charms into a ", tostring(math.floor(self.use_talent.power)) ," cooldown :", {"color","LAST"}, true}
 		else
@@ -1303,12 +1303,12 @@ function _M:getPriceFlags()
 		if w.combat_physspeed then price = price + w.combat_physspeed * -200 end
 		if w.combat_spellpower then price = price + w.combat_spellpower * 0.8 end
 		if w.combat_spellcrit then price = price + w.combat_spellcrit * 0.4 end
-		
+
 		--shooter attributes
 		if w.ammo_regen then price = price + w.ammo_regen * 10 end
 		if w.ammo_reload_speed then price = price + w.ammo_reload_speed *10 end
 		if w.travel_speed then price = price +w.travel_speed * 10 end
-				
+
 		--miscellaneous attributes
 		if w.inc_stealth then price = price + w.inc_stealth * 1 end
 		if w.see_invisible then price = price + w.see_invisible * 0.2 end
@@ -1330,12 +1330,12 @@ function _M:getPriceFlags()
 		if w.damage_shield_penetrate then price = price + w.damage_shield_penetrate * 1 end
 		if w.spellsurge_on_crit then price = price + w.spellsurge_on_crit * 5 end
 		if w.quick_weapon_swap then price = price + w.quick_weapon_swap * 50 end
-		
+
 		--on teleport abilities
 		if w.resist_all_on_teleport then price = price + w.resist_all_on_teleport * 4 end
 		if w.defense_on_teleport then price = price + w.defense_on_teleport * 3 end
 		if w.effect_reduction_on_teleport then price = price + w.effect_reduction_on_teleport * 2 end
-		
+
 		--resists
 		if w.resists then for t, v in pairs(w.resists) do price = price + v * 0.15 end end
 
@@ -1360,7 +1360,7 @@ function _M:getPriceFlags()
 		if w.ranged_project then for t, v in pairs(w.ranged_project) do price = price + v * 0.7 end end
 		if w.burst_on_hit then for t, v in pairs(w.burst_on_hit) do price = price + v * 0.8 end end
 		if w.burst_on_crit then for t, v in pairs(w.burst_on_crit) do price = price + v * 0.8 end end
-		
+
 		--damage conversion
 		if w.convert_damage then for t, v in pairs(w.convert_damage) do price = price + v * 1 end end
 
diff --git a/game/modules/tome/data/quests/escort-duty.lua b/game/modules/tome/data/quests/escort-duty.lua
index d880f898f6..1f9b766442 100644
--- a/game/modules/tome/data/quests/escort-duty.lua
+++ b/game/modules/tome/data/quests/escort-duty.lua
@@ -368,7 +368,7 @@ on_grant = function(self, who)
 		who:removed()
 		game.party:removeMember(who, true)
 	end
-	g._mo = nil
+	g:removeAllMOs()
 
 	g:resolve() g:resolve(nil, true)
 	game.zone:addEntity(game.level, g, "terrain", gx, gy)
diff --git a/game/modules/tome/data/quests/paradoxology.lua b/game/modules/tome/data/quests/paradoxology.lua
index 0d23d88048..63074ce9df 100644
--- a/game/modules/tome/data/quests/paradoxology.lua
+++ b/game/modules/tome/data/quests/paradoxology.lua
@@ -46,7 +46,7 @@ generate = function(self, player, x, y)
 	a.rank = 4
 	a.name = a.name.." the Paradox Mage"
 	a.color_r = 250 a.color_g = 50 a.color_b = 250
-	a._mo:invalidate()
+	a:removeAllMOs()
 	a.ai = "tactical"
 	a.ai_state = {talent_in=1}
 	a.faction = "enemies"
diff --git a/game/modules/tome/data/talents/gifts/summon-utility.lua b/game/modules/tome/data/talents/gifts/summon-utility.lua
index 5fa19b9b1c..a7ef09729f 100644
--- a/game/modules/tome/data/talents/gifts/summon-utility.lua
+++ b/game/modules/tome/data/talents/gifts/summon-utility.lua
@@ -37,7 +37,7 @@ newTalent{
 		return true
 	end,
 	info = function(self, t)
-		return ([[Forces hostile foes to attack you.]])
+		return ([[Forces all hostile foes in radius %d to attack you.]]):format(self:getTalentRadius(t))
 	end,
 }
 
diff --git a/game/modules/tome/data/timed_effects/physical.lua b/game/modules/tome/data/timed_effects/physical.lua
index c8985fa829..d913d9fdb6 100644
--- a/game/modules/tome/data/timed_effects/physical.lua
+++ b/game/modules/tome/data/timed_effects/physical.lua
@@ -581,7 +581,7 @@ newEffect{
 			self.add_displays = { Entity.new{image='npc/iceblock.png', display=' ', display_on_seen=true } }
 			eff.added_display = true
 		end
-		if self._mo then self._mo:invalidate() self._mo = nil end
+		self:removeAllMOs()
 		game.level.map:updateMap(self.x, self.y)
 
 		eff.hp = eff.hp or 100
@@ -610,7 +610,7 @@ newEffect{
 		self.color_g = eff.old_g
 		self.color_b = eff.old_b
 		if eff.added_display then self.add_displays = nil end
-		if self._mo then self._mo:invalidate() self._mo = nil end
+		self:removeAllMOs()
 		game.level.map:updateMap(self.x, self.y)
 		self:setTarget(nil)
 	end,
@@ -1790,7 +1790,7 @@ newEffect{
 	long_desc = function(self, eff)
 		local ravaged = "each turn."
 		if eff.ravage then ravaged = "and is losing one physical effect turn." end
-		return ("The target is being ravaged by distortion, taking %0.2f physical damage %s"):format(eff.dam, ravaged) 
+		return ("The target is being ravaged by distortion, taking %0.2f physical damage %s"):format(eff.dam, ravaged)
 	end,
 	type = "physical",
 	subtype = { distortion=true },
diff --git a/game/modules/tome/data/zones/arena/zone.lua b/game/modules/tome/data/zones/arena/zone.lua
index db1ed34afd..8bb450a9ce 100644
--- a/game/modules/tome/data/zones/arena/zone.lua
+++ b/game/modules/tome/data/zones/arena/zone.lua
@@ -213,7 +213,7 @@ return {
 				master.color_r = 255
 				master.color_g = 0
 				master.color_b = 255
-				master._mo:invalidate()
+				master:removeAllMOs()
 				master.ai = "tactical"
 				master.ai_state = {talent_in=1, ai_move="move_astar"}
 				master.faction="enemies"
diff --git a/game/modules/tome/data/zones/crypt-kryl-feijan/npcs.lua b/game/modules/tome/data/zones/crypt-kryl-feijan/npcs.lua
index 59623ee52d..67f8abd4c2 100644
--- a/game/modules/tome/data/zones/crypt-kryl-feijan/npcs.lua
+++ b/game/modules/tome/data/zones/crypt-kryl-feijan/npcs.lua
@@ -160,7 +160,7 @@ newEntity{ define_as = "ACOLYTE",
 				melinda:removeEffect(melinda.EFF_TIME_PRISON)
 				melinda.display_w = nil
 				melinda.image = "npc/woman_redhair_naked.png"
-				if melinda._mo then melinda._mo:invalidate() melinda._mo = nil end
+				melinda:removeAllMOs()
 				game.level.map:updateMap(melinda.x, melinda.y)
 				require("engine.ui.Dialog"):simpleLongPopup("Melinda", "The woman seems to be freed from her bonds.\nShe stumbles on her feet, her naked body still dripping in blood. 'Please get me out of here!'", 400)
 			end
diff --git a/game/modules/tome/data/zones/high-peak/grids.lua b/game/modules/tome/data/zones/high-peak/grids.lua
index 695fde3de5..2b006679ff 100644
--- a/game/modules/tome/data/zones/high-peak/grids.lua
+++ b/game/modules/tome/data/zones/high-peak/grids.lua
@@ -119,7 +119,7 @@ local invocation_close = function(self, who)
 	g.color_r = colors.WHITE.r
 	g.color_g = colors.WHITE.g
 	g.color_b = colors.WHITE.b
-	if g._mo then g._mo:invalidate() g._mo = nil end
+	g:removeAllMOs()
 	game.level.map:updateMap(spot.x, spot.y)
 	who:setQuestStatus("high-peak", engine.Quest.COMPLETED, "closed-portal-"..self.summon)
 end
diff --git a/game/modules/tome/data/zones/shadow-crypt/npcs.lua b/game/modules/tome/data/zones/shadow-crypt/npcs.lua
index 8b776394ab..d6020a0895 100644
--- a/game/modules/tome/data/zones/shadow-crypt/npcs.lua
+++ b/game/modules/tome/data/zones/shadow-crypt/npcs.lua
@@ -84,7 +84,7 @@ newEntity{ base="BASE_NPC_ORC_RAK_SHOR", define_as = "CULTIST_RAK_SHOR",
 			a.name = "Doomed Shade of "..a.name
 			a.killer_message = "but nobody knew why #sex# suddenly became evil"
 			a.color_r = 150 a.color_g = 150 a.color_b = 150
-			a._mo:invalidate()
+			a:removeAllMOs()
 			a.ai = "tactical"
 			a.puuid = nil
 			a.ai_state = {talent_in=1}
-- 
GitLab