From 0e15bf654bd477f216bcac5923dd0682eb9527ba Mon Sep 17 00:00:00 2001
From: dg <dg@51575b47-30f0-44d4-a5cc-537603b46e54>
Date: Mon, 25 Apr 2011 01:22:00 +0000
Subject: [PATCH] Overhauled Frozen effect: You now get to act every turns but
 can not teleport, heal or move. The ice now has HP which can be damaged to
 make it go away. All your attacks can only damage the ice (not creatures) and
 creature attacks damage you for half their value and the ice for half

git-svn-id: http://svn.net-core.org/repos/t-engine4@3271 51575b47-30f0-44d4-a5cc-537603b46e54
---
 .../default/engine/interface/ActorProject.lua | 28 ++++++++++--------
 game/modules/tome/class/Actor.lua             | 29 ++++++++++++++++++-
 game/modules/tome/class/Game.lua              |  5 ++--
 game/modules/tome/class/NPC.lua               |  8 +++++
 game/modules/tome/class/Player.lua            |  6 +++-
 game/modules/tome/class/interface/Combat.lua  |  4 +--
 game/modules/tome/data/damage_types.lua       | 20 +++++++++++--
 .../tome/data/talents/spells/water.lua        |  5 ++--
 game/modules/tome/data/timed_effects.lua      | 19 ++++++++++--
 9 files changed, 98 insertions(+), 26 deletions(-)

diff --git a/game/engines/default/engine/interface/ActorProject.lua b/game/engines/default/engine/interface/ActorProject.lua
index 28cab2af18..63cb1adcbd 100644
--- a/game/engines/default/engine/interface/ActorProject.lua
+++ b/game/engines/default/engine/interface/ActorProject.lua
@@ -126,6 +126,8 @@ function _M:project(t, x, y, damtype, dam, particles)
 		return
 	end
 
+	self:check("on_project_grids", grids)
+
 	-- Now project on each grid, one type
 	local tmp = {}
 	local stop = false
@@ -292,11 +294,11 @@ function _M:projectDoStop(typ, tg, damtype, dam, particles, lx, ly, tmp, rx, ry)
 
 	if typ.ball and typ.ball > 0 then
 		core.fov.calc_circle(
-			rx, 
-			ry, 
-			game.level.map.w, 
-			game.level.map.h, 
-			typ.ball, 
+			rx,
+			ry,
+			game.level.map.w,
+			game.level.map.h,
+			typ.ball,
 			function(_, px, py)
 				if typ.block_radius and typ:block_radius(px, py) then return true end
 			end,
@@ -310,13 +312,13 @@ function _M:projectDoStop(typ, tg, damtype, dam, particles, lx, ly, tmp, rx, ry)
 		local initial_dir = lx and util.getDir(lx, ly, x, y) or 5
 		local dir_angle = math.deg(math.atan2(ly - typ.source_actor.y, lx - typ.source_actor.x))
 		core.fov.calc_beam_any_angle(
-			rx, 
-			ry, 
-			game.level.map.w, 
-			game.level.map.h, 
-			typ.cone, 
-			dir_angle, 
-			typ.cone_angle, 
+			rx,
+			ry,
+			game.level.map.w,
+			game.level.map.h,
+			typ.cone,
+			dir_angle,
+			typ.cone_angle,
 			function(_, px, py)
 				if typ.block_radius and typ:block_radius(px, py) then return true end
 			end,
@@ -331,6 +333,8 @@ function _M:projectDoStop(typ, tg, damtype, dam, particles, lx, ly, tmp, rx, ry)
 		addGrid(lx, ly)
 	end
 
+	self:check("on_project_grids", grids)
+
 	for px, ys in pairs(grids) do
 		for py, _ in pairs(ys) do
 			if self:projectDoAct(typ, tg, damtype, dam, particles, px, py, tmp) then break end
diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua
index 28c6effee3..0a5f5b48e8 100644
--- a/game/modules/tome/class/Actor.lua
+++ b/game/modules/tome/class/Actor.lua
@@ -354,7 +354,6 @@ function _M:act()
 			game.logSeen(self, "%s temporarily fights the stun.", self.name:capitalize())
 		end
 	end
-	if self:attr("encased_in_ice") then self.energy.value = 0 end
 	if self:attr("stoned") then self.energy.value = 0 end
 	if self:attr("dazed") then self.energy.value = 0 end
 	if self:attr("time_stun") then self.energy.value = 0 end
@@ -580,6 +579,7 @@ end
 --- Blink through walls
 function _M:probabilityTravel(x, y, dist)
 	if game.zone.wilderness then return true end
+	if self:attr("encased_in_ice") then return end
 
 	local dirx, diry = x - self.x, y - self.y
 	local tx, ty = x, y
@@ -605,6 +605,7 @@ end
 -- @param min_dist the minimum radius of of the effect, will never teleport closer. Defaults to 0 if not set
 -- @return true if the teleport worked
 function _M:teleportRandom(x, y, dist, min_dist)
+	if self:attr("encased_in_ice") then return end
 	if game.level.data.no_teleport_south and y + dist > self.y then
 		y = self.y - dist
 	end
@@ -671,6 +672,8 @@ end
 --- What is our reaction toward the target
 -- This can modify faction reaction using specific actor to actor reactions
 function _M:reactionToward(target, no_reflection)
+	if target == self and self:attr("encased_in_ice") then return -100 end
+
 	local v = engine.Actor.reactionToward(self, target)
 
 	if self.reaction_actor and self.reaction_actor[target.unique or target.name] then v = v + self.reaction_actor[target.unique or target.name] end
@@ -789,6 +792,10 @@ function _M:tooltip(x, y, seen_by)
 	ts:add({"color", 0, 255, 255}, ("Level: %d"):format(self.level), {"color", "WHITE"}, true)
 	ts:add(("Exp: %d/%d"):format(self.exp, self:getExpChart(self.level+1) or "---"), true)
 	ts:add({"color", 255, 0, 0}, ("HP: %d (%d%%)"):format(self.life, self.life * 100 / self.max_life), {"color", "WHITE"}, true)
+	if self:attr("encased_in_ice") then
+		local eff = self:hasEffect(self.EFF_FROZEN)
+		ts:add({"color", 0, 255, 128}, ("Iceblock: %d"):format(eff.hp), {"color", "WHITE"}, true)
+	end
 	ts:add(("Stats: %d / %d / %d / %d / %d / %d"):format(self:getStr(), self:getDex(), self:getCon(), self:getMag(), self:getWil(), self:getCun()), true)
 	ts:add("Resists: ", table.concat(resists, ','), true)
 	ts:add("Armour/Defense: ", tostring(math.floor(self:combatArmor())), ' / ', tostring(math.floor(self:combatDefense())), true)
@@ -828,6 +835,9 @@ function _M:onHeal(value, src)
 	if self:hasEffect(self.EFF_UNSTOPPABLE) then
 		return 0
 	end
+	if self:attr("encased_in_ice") then
+		return 0
+	end
 	value = value * util.bound((self.healing_factor or 1), 0, 2.5)
 	print("[HEALING]", self.uid, self.name, "for", value)
 	return value
@@ -1054,6 +1064,14 @@ function _M:onTakeHit(value, src)
 		game.logSeen(self, "%s shatters into pieces!", self.name:capitalize())
 	end
 
+	-- Frozen: absorb some damage into the iceblock
+	if self:attr("encased_in_ice") then
+		local eff = self:hasEffect(self.EFF_FROZEN)
+		value = value / 2
+		eff.hp = eff.hp - value
+		if eff.hp < 0 then self:removeEffect(self.EFF_FROZEN) end
+	end
+
 	-- Adds hate
 	if self:knowTalent(self.T_HATE_POOL) then
 		local hateGain = 0
@@ -2459,6 +2477,15 @@ function _M:on_projectile_target(x, y, p)
 	end
 end
 
+--- Called when we have acquired grids
+function _M:on_project_grids(grids)
+	if self:attr("encased_in_ice") then
+		-- Only hit yourself
+		while next(grids) do grids[next(grids)] = nil end
+		grids[self.x] = {[self.y]=true}
+	end
+end
+
 --- Call when added to a level
 -- Used to make escorts and such
 function _M:addedToLevel(level, x, y)
diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua
index 05b488cb19..1d22892605 100644
--- a/game/modules/tome/class/Game.lua
+++ b/game/modules/tome/class/Game.lua
@@ -922,8 +922,9 @@ function _M:setupCommands()
 		end end,
 		[{"_g","ctrl"}] = function() if config.settings.cheat then
 --			self.state:debugRandomZone()
-			local m = game.zone:makeEntity(game.level, "actor", {random_boss=true}, nil, true)
-			if m then game.zone:addEntity(game.level, m, "actor", game.player.x, game.player.y + 1) end
+--			local m = game.zone:makeEntity(game.level, "actor", {random_boss=true}, nil, true)
+--			if m then game.zone:addEntity(game.level, m, "actor", game.player.x, game.player.y + 1) end
+			self.player:setEffect(self.player.EFF_FROZEN, 10, {})
 		end end,
 	}
 
diff --git a/game/modules/tome/class/NPC.lua b/game/modules/tome/class/NPC.lua
index f19e498033..b938276185 100644
--- a/game/modules/tome/class/NPC.lua
+++ b/game/modules/tome/class/NPC.lua
@@ -227,6 +227,14 @@ function _M:tooltip(x, y, seen_by)
 	return str
 end
 
+function _M:getTarget(typ)
+	if self:attr("encased_in_ice") then
+		return self.x, self.y, self
+	else
+		return mod.class.Actor.getTarget(self, typ)
+	end
+end
+
 --- Make emotes appear in the log too
 function _M:setEmote(e)
 	game.logSeen(self, "%s says: '%s'", self.name:capitalize(), e.text)
diff --git a/game/modules/tome/class/Player.lua b/game/modules/tome/class/Player.lua
index 4258c89493..4cd5b4b703 100644
--- a/game/modules/tome/class/Player.lua
+++ b/game/modules/tome/class/Player.lua
@@ -463,7 +463,11 @@ end
 
 --- Tries to get a target from the user
 function _M:getTarget(typ)
-	return game:targetGetForPlayer(typ)
+	if self:attr("encased_in_ice") then
+		return self.x, self.y, self
+	else
+		return game:targetGetForPlayer(typ)
+	end
 end
 
 --- Sets the current target
diff --git a/game/modules/tome/class/interface/Combat.lua b/game/modules/tome/class/interface/Combat.lua
index 5864630dae..c6cc5114f4 100644
--- a/game/modules/tome/class/interface/Combat.lua
+++ b/game/modules/tome/class/interface/Combat.lua
@@ -493,12 +493,12 @@ function _M:combatDefense()
 		local t = self:getTalentFromId(self.T_STEADY_MIND)
 		add = add + t.getDefense(self, t)
 	end
-	return self.combat_def + (self:getDex() - 10) * 0.35 + add + (self:getLck() - 50) * 0.4
+	return math.max(0, self.combat_def + (self:getDex() - 10) * 0.35 + add + (self:getLck() - 50) * 0.4)
 end
 
 --- Gets the defense ranged
 function _M:combatDefenseRanged()
-	return self:combatDefense() + (self.combat_def_ranged or 0)
+	return math.max(0, self:combatDefense() + (self.combat_def_ranged or 0))
 end
 
 --- Gets the armor
diff --git a/game/modules/tome/data/damage_types.lua b/game/modules/tome/data/damage_types.lua
index 183f504cf3..5f683445d8 100644
--- a/game/modules/tome/data/damage_types.lua
+++ b/game/modules/tome/data/damage_types.lua
@@ -44,6 +44,20 @@ setDefaultProjector(function(src, x, y, type, dam, tmp, no_martyr)
 			dam = dam + (dam * inc / 100)
 		end
 
+		-- Blast the iceblock
+		if src.attr and src:attr("encased_in_ice") then
+			local eff = src:hasEffect(src.EFF_FROZEN)
+			eff.hp = eff.hp - dam
+			local srcname = src.x and src.y and game.level.map.seens(src.x, src.y) and src.name:capitalize() or "Something"
+			if eff.hp < 0 then
+				game.logSeen(src, "%s forces the iceblock to shatter.", src.name:capitalize())
+				src:removeEffect(src.EFF_FROZEN)
+			else
+				game:delayedLogDamage(src, {name="Iceblock", x=src.x, y=src.y}, dam, ("%s%d %s#LAST#"):format(DamageType:get(type).text_color or "#aaaaaa#", math.ceil(dam), DamageType:get(type).name))
+			end
+			return 0
+		end
+
 		-- dark vision increases damage done in the dark
 		if src.knowTalent and src:knowTalent(src.T_DARK_VISION) then
 			local t = src:getTalentFromId(src.T_DARK_VISION)
@@ -461,7 +475,7 @@ newDamageType{
 	projector = function(src, x, y, type, dam)
 		local realdam = DamageType:get(DamageType.COLD).projector(src, x, y, DamageType.COLD, dam)
 		if rng.percent(25) then
-			DamageType:get(DamageType.FREEZE).projector(src, x, y, DamageType.FREEZE, 2)
+			DamageType:get(DamageType.FREEZE).projector(src, x, y, DamageType.FREEZE, {dur=2, hp=70+dam*1.5})
 		end
 		return realdam
 	end,
@@ -490,7 +504,7 @@ newDamageType{
 			-- Freeze it, if we pass the test
 			local sx, sy = game.level.map:getTileToScreen(x, y)
 			if target:checkHit(src:combatSpellpower(), target:combatSpellResist(), 0, 95, 15) and target:canBe("stun") then
-				target:setEffect(target.EFF_FROZEN, dam, {src=src})
+				target:setEffect(target.EFF_FROZEN, dam.dur, {hp=dam.hp * 1.5})
 
 				game.flyers:add(sx, sy, 30, (rng.range(0,2)-1) * 0.5, -3, "Frozen!", {0,255,155})
 			else
@@ -1228,7 +1242,7 @@ newDamageType{
 			-- Freeze it, if we pass the test
 			local sx, sy = game.level.map:getTileToScreen(x, y)
 			if target:checkHit(src:combatMindpower(), target:combatPhysicalResist(), 0, 95, 15) and target:canBe("stun") then
-				target:setEffect(target.EFF_FROZEN, dam, {src=src})
+				target:setEffect(target.EFF_FROZEN, dam, {hp=70 + src:combatMindpower() * 10})
 			else
 				game.logSeen(target, "%s resists!", target.name:capitalize())
 			end
diff --git a/game/modules/tome/data/talents/spells/water.lua b/game/modules/tome/data/talents/spells/water.lua
index 51086a3c56..6b5fdd7fd1 100644
--- a/game/modules/tome/data/talents/spells/water.lua
+++ b/game/modules/tome/data/talents/spells/water.lua
@@ -79,8 +79,9 @@ newTalent{
 		local tg = {type="hit", range=self:getTalentRange(t), talent=t}
 		local x, y = self:getTarget(tg)
 		if not x or not y then return nil end
-		self:project(tg, x, y, DamageType.COLD, self:spellCrit(t.getDamage(self, t)), {type="freeze"})
-		self:project(tg, x, y, DamageType.FREEZE, 3)
+		local dam = self:spellCrit(t.getDamage(self, t))
+		self:project(tg, x, y, DamageType.COLD, dam, {type="freeze"})
+		self:project(tg, x, y, DamageType.FREEZE, {dur=3, hp=70 + dam * 1.5})
 		game:playSoundNear(self, "talents/ice")
 		return true
 	end,
diff --git a/game/modules/tome/data/timed_effects.lua b/game/modules/tome/data/timed_effects.lua
index c0e8a73420..723a1e3f7f 100644
--- a/game/modules/tome/data/timed_effects.lua
+++ b/game/modules/tome/data/timed_effects.lua
@@ -21,6 +21,8 @@ local Stats = require "engine.interface.ActorStats"
 local Particles = require "engine.Particles"
 local Entity = require "engine.Entity"
 local Chat = require "engine.Chat"
+local Map = require "engine.Map"
+local Level = require "engine.Level"
 
 newEffect{
 	name = "INFUSION_COOLDOWN",
@@ -258,7 +260,7 @@ newEffect{
 newEffect{
 	name = "FROZEN",
 	desc = "Frozen",
-	long_desc = function(self, eff) return "The target is encased in ice, completely unable to act. The ice increases all your resistances by 20%." end,
+	long_desc = function(self, eff) return ("The target is encased in ice. All damage done to you will be split in half, a part absorbed by the ice. Your defense is nullified while in the ice and you may only attack the ice. %d HP on the iceblock remaining."):format(eff.hp) end,
 	type = "magical",
 	status = "detrimental",
 	parameters = {},
@@ -279,21 +281,32 @@ newEffect{
 		if self._mo then self._mo:invalidate() self._mo = nil end
 		game.level.map:updateMap(self.x, self.y)
 
+		eff.hp = eff.hp or 100
 		eff.tmpid = self:addTemporaryValue("encased_in_ice", 1)
+		eff.moveid = self:addTemporaryValue("never_move", 1)
 		eff.frozid = self:addTemporaryValue("frozen", 1)
+		eff.defid = self:addTemporaryValue("combat_def", -1000)
+		eff.rdefid = self:addTemporaryValue("combat_def_ranged", -1000)
 		eff.dur = self:updateEffectDuration(eff.dur, "freeze")
-		eff.resistsid = self:addTemporaryValue("resists", {all=20})
+
+		self:setTarget(self)
+	end,
+	on_timeout = function(self, eff)
+		self:setTarget(self)
 	end,
 	deactivate = function(self, eff)
 		self:removeTemporaryValue("encased_in_ice", eff.tmpid)
+		self:removeTemporaryValue("never_move", eff.moveid)
 		self:removeTemporaryValue("frozen", eff.frozid)
-		self:removeTemporaryValue("resists", eff.resistsid)
+		self:removeTemporaryValue("combat_def", eff.defid)
+		self:removeTemporaryValue("combat_def_ranged", eff.rdefid)
 		self.color_r = eff.old_r
 		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
 		game.level.map:updateMap(self.x, self.y)
+		self:setTarget(nil)
 	end,
 }
 
-- 
GitLab