From b7fe42c2991f14cf7e9a2839e0c105da13d12ee6 Mon Sep 17 00:00:00 2001
From: Hachem_Muche <Hachem_Muche@stanfordalumni.org>
Date: Thu, 26 Feb 2015 15:25:46 -0800
Subject: [PATCH] Updated on use descriptions for several World Artifacts and
 added some log messages.

Changed Umbraphage's damage calculation to actually care about it's charge level (slight nerf).
Fixed a bug with Butcher that could cause its rampage not to activate on damage.  Now benefits from Charm Mastery.
Stormlash's AOE effect is correctly centered on the target, rather than the user.

DamageType.LITE now reduces the unlit value of grids with darkness if can't remove them. (Fixes a possible abusable bug with Umbraphage.)
Updated the tooltip for the Invisibility effect.
---
 game/modules/tome/data/damage_types.lua       |   5 +-
 .../data/general/objects/world-artifacts.lua  | 427 +++++++++++-------
 .../tome/data/timed_effects/magical.lua       |   2 +-
 3 files changed, 268 insertions(+), 166 deletions(-)

diff --git a/game/modules/tome/data/damage_types.lua b/game/modules/tome/data/damage_types.lua
index a85db7ff06..db8d0a781e 100644
--- a/game/modules/tome/data/damage_types.lua
+++ b/game/modules/tome/data/damage_types.lua
@@ -909,13 +909,12 @@ newDamageType{
 	projector = function(src, x, y, type, dam, state)
 		state = state or {}
 		useImplicitCrit(src, state)
-		-- Dont lit magically unlit grids
+		-- Counter magical unlite level before lighting grids
 		local g = game.level.map(x, y, Map.TERRAIN+1)
 		if g and g.unlit then
 			if g.unlit <= dam then game.level.map:remove(x, y, Map.TERRAIN+1)
-			else return end
+			else g.unlit = g.unlit - dam return end -- Lite wears down darkness
 		end
-
 		game.level.map.lites(x, y, true)
 	end,
 }
diff --git a/game/modules/tome/data/general/objects/world-artifacts.lua b/game/modules/tome/data/general/objects/world-artifacts.lua
index fd5ba3a450..5f9146614d 100644
--- a/game/modules/tome/data/general/objects/world-artifacts.lua
+++ b/game/modules/tome/data/general/objects/world-artifacts.lua
@@ -130,13 +130,22 @@ newEntity{ base = "BASE_RING",
 	material_level = 2,
 
 	max_power = 60, power_regen = 1,
-	use_power = { name = "summon a tidal wave", power = 60,
+	use_power = {
+		name = function(self, who)
+			local dam = self.use_power.damage(self, who)
+			return ("summon a radius %d tidal wave that expands slowly over %d turns, dealing %0.2f cold and %0.2f physical damage (based on Willpower) each turn and knocking opponents back"):
+			format(self.use_power:radius(who), self.use_power.duration(self, who), who:damDesc(engine.DamageType.COLD, dam/2), who:damDesc(engine.DamageType.PHYSICAL, dam/2)) 
+		end,
+		power = 60,
+		radius = function(self, who) return 1 end,
+		duration = function(self, who) return 7 end,
+		damage = function(self, who) return who:combatStatScale("wil", 15, 40) end,
 		use = function(self, who)
 			local duration = 7
 			local radius = 1
-			local dam = 20
+			local dam = self.use_power.damage(self, who)
 			-- Add a lasting map effect
-			game.level.map:addEffect(who,
+			local wave = game.level.map:addEffect(who,
 				who.x, who.y, duration,
 				engine.DamageType.WAVE, {dam=dam, x=who.x, y=who.y},
 				radius,
@@ -148,7 +157,8 @@ newEntity{ base = "BASE_RING",
 				end,
 				false
 			)
-			game.logSeen(who, "%s brandishes the %s, calling forth the might of the oceans!", who.name:capitalize(), self:getName())
+			wave.name = "tidal wave"
+			game.logSeen(who, "%s brandishes %s, calling forth the might of the oceans!", who.name:capitalize(), self:getName({no_add_name = true}))
 			return {id=true, used=true}
 		end
 	},
@@ -282,10 +292,13 @@ newEntity{ base = "BASE_LITE",
 	cost = 200,
 
 	max_power = 15, power_regen = 1,
-	use_power = { name = "call light", power = 10,
+	use_power = {
+		name = function(self, who) return ("call light (%d power, based on Willpower)"):format(self.use_power.litepower(self, who)) end,
+		power = 10,
+		litepower = function(self, who) return who:combatStatScale("wil", 50, 150) end,
 		use = function(self, who)
-			who:project({type="ball", range=0, radius=20}, who.x, who.y, engine.DamageType.LITE, 100)
-			game.logSeen(who, "%s brandishes the %s and banishes all shadows!", who.name:capitalize(), self:getName())
+			who:project({type="ball", range=0, radius=20}, who.x, who.y, engine.DamageType.LITE, self.use_power.litepower(self, who))
+			game.logSeen(who, "%s brandishes %s %s and banishes all shadows!", who.name:capitalize(), who:his_her(), self:getName())
 			return {id=true, used=true}
 		end
 	},
@@ -313,7 +326,7 @@ This star is the culmination of their craft. Light radiates from its ever-shifti
 	cost = 400,
 
 	max_power = 30, power_regen = 1,
-	use_power = { name = "map surroundings", power = 30,
+	use_power = { name = "map surroundings within range 20", power = 30,
 		use = function(self, who)
 			who:magicMap(20)
 			game.logSeen(who, "%s brandishes the %s which radiates in all directions!", who.name:capitalize(), self:getName())
@@ -398,9 +411,12 @@ newEntity{ base = "BASE_LEATHER_BOOT",
 	},
 
 	max_power = 50, power_regen = 1,
-	use_power = { name = "boost speed", power = 50,
+	use_power = {
+		name = function(self, who) return ("boost speed by %d%% (based on Cunning)"):format(100 * self.use_power.speedboost(self, who)) end,
+		power = 50,
+		speedboost = function(self, who) return math.min(0.20 + who:getCun() / 200, 0.7) end,
 		use = function(self, who)
-			who:setEffect(who.EFF_SPEED, 8, {power=math.min(0.20 + who:getCun() / 200, 0.7)})
+			who:setEffect(who.EFF_SPEED, 8, {power=self.use_power:speedboost(who)})
 			return {id=true, used=true}
 		end
 	},
@@ -606,7 +622,7 @@ newEntity{
 		lite = -2,
 	},
 	max_power = 100, power_regen = 1,
-	use_power = { name = "summon spiders", power = 80, use = function(self, who)
+	use_power = { name = "summon up to 2 spiders", power = 80, use = function(self, who)
 		if not who:canBe("summon") then game.logPlayer(who, "You cannot summon; you are suppressed!") return end
 
 		local NPC = require "mod.class.NPC"
@@ -2061,34 +2077,41 @@ newEntity{ base = "BASE_SHIELD",
 		disease_immune = 0.6,
 	},
 	max_power = 40, power_regen = 1,
-	use_power = { name = "purge diseases and increase your resistances", power = 24,
-	use = function(self, who)
-		local target = who
-		local effs = {}
-		local known = false
+	use_power = {
+		name = function(self, who)
+			local effpower = self.use_power.effpower(self, who)
+			return ("purge up to %d diseases (based on Willpower) and gain disease immunity, %d%% blight resistance, and %d spell save for 5 turns"):format(self.use_power.nbdisease(self, who), effpower, effpower)
+		end,
+		power = 24,
+		nbdisease = function(self, who) return math.floor(who:combatStatScale("wil", 3, 5, "log")) end, -- There really aren't that many different disease you can have at one time.
+		effpower = function(self, who) return 20 end,
+		use = function(self, who)
+			local target = who
+			local effs = {}
+			local known = false
 
-		who:setEffect(who.EFF_PURGE_BLIGHT, 5, {power=20})
+			who:setEffect(who.EFF_PURGE_BLIGHT, 5, {power=self.use_power.effpower(self, who)})
 
-			-- Go through all spell effects
-		for eff_id, p in pairs(target.tmp) do
-			local e = target.tempeffect_def[eff_id]
-			if e.subtype.disease then
-				effs[#effs+1] = {"effect", eff_id}
+				-- Go through all spell effects
+			for eff_id, p in pairs(target.tmp) do
+				local e = target.tempeffect_def[eff_id]
+				if e.subtype.disease then
+					effs[#effs+1] = {"effect", eff_id}
+				end
 			end
-		end
 
-		for i = 1, 3 + math.floor(who:getWil() / 10) do
-			if #effs == 0 then break end
-			local eff = rng.tableRemove(effs)
+			for i = 1, self.use_power.nbdisease(self, who) do
+				if #effs == 0 then break end
+				local eff = rng.tableRemove(effs)
 
-			if eff[1] == "effect" then
-				target:removeEffect(eff[2])
-				known = true
+				if eff[1] == "effect" then
+					target:removeEffect(eff[2])
+					known = true
+				end
 			end
-		end
-		game.logSeen(who, "%s is purged of diseases!", who.name:capitalize())
-		return {id=true, used=true}
-	end,
+			game.logSeen(who, "%s is purged of diseases!", who.name:capitalize())
+			return {id=true, used=true}
+		end,
 	},
 	on_wear = function(self, who)
 		if who:attr("forbid_arcane") then
@@ -2807,23 +2830,31 @@ newEntity{ base = "BASE_WHIP",
 		inc_damage={ [DamageType.LIGHTNING] = 10, },
 	},
 	max_power = 10, power_regen = 1,
-	use_power = { name = "strike an enemy in range 3, releasing a burst of lightning", power = 10,
+	use_power = {
+		name = function(self, who)
+			local dam = who:damDesc(engine.DamageType.LIGHTNING, self.use_power.damage(self, who))
+			return ("strike an enemy within range %d (for 100%% weapon damage as lightning) and release a radius %d burst of electricity dealing %0.2f to %0.2f lightning damage (based on Magic and Dexterity)"):format(self.use_power.range, self.use_power.radius, dam/3, dam)
+		end,
+		power = 10,
+		range = 3,
+		radius = 1,
+		damage = function(self, who) return 20 + who:getMag()/2 + who:getDex()/3 end,
 		use = function(self, who)
-			local dam = 20 + who:getMag()/2 + who:getDex()/3
-			local tg = {type="bolt", range=3}
-			local blast = {type="ball", range=0, radius=1, selffire=false}
+			local dam = self.use_power.damage(self, who)
+			local tg = {type="bolt", range=self.use_power.range}
 			local x, y = who:getTarget(tg)
 			if not x or not y then return nil end
 			local _ _, x, y = who:canProject(tg, x, y)
 			local target = game.level.map(x, y, engine.Map.ACTOR)
 			if not target then return end
+			local blast = {type="ball", start_x = target.x, start_y = target.y, range=0, radius=self.use_power.radius, selffire=false}
 			who:attackTarget(target, engine.DamageType.LIGHTNING, 1, true)
 			local _ _, x, y = who:canProject(tg, x, y)
 			game.level.map:particleEmitter(who.x, who.y, math.max(math.abs(x-who.x), math.abs(y-who.y)), "lightning", {tx=x-who.x, ty=y-who.y})
 			who:project(blast, x, y, engine.DamageType.LIGHTNING, rng.avg(dam / 3, dam, 3))
 			game.level.map:particleEmitter(x, y, radius, "ball_lightning", {radius=blast.radius})
 			game:playSoundNear(self, "talents/lightning")
-			who:logCombat(target, "#Source# strikes #Target#, sending out an arc of lightning!")
+			who:logCombat(target, "#Source# strikes #Target# with %s %s, sending out an arc of lightning!", who:his_her(), self:getName({no_add_name = true}))
 			return {id=true, used=true}
 		end
 	},
@@ -2854,7 +2885,7 @@ newEntity{ base = "BASE_WHIP",
 		talent_on_hit = { [Talents.T_MINDLASH] = {level=1, chance=18} },
 	},
 	max_power = 10, power_regen = 1,
-	use_power = { name = "strike all targets in a line", power = 10,
+	use_power = { name = "strike all targets in a line (for 100%% weapon damage as mind)", power = 10,
 		use = function(self, who)
 			local tg = {type="beam", range=4}
 			local x, y = who:getTarget(tg)
@@ -2903,35 +2934,39 @@ newEntity{ base = "BASE_GREATSWORD",
 		inc_stats = { [Stats.STAT_STR] = 5, [Stats.STAT_CUN] = 3 },
 	},
 	max_power = 25, power_regen = 1,
-	use_power = {name="accelerate burns, instantly inflicting 125% of all burn damage", power = 10, --wherein Pure copies Catalepsy
-	use=function(combat, who, target)
-		local tg = {type="ball", range=5, radius=1, selffire=false}
-		local x, y = who:getTarget(tg)
-		if not x or not y then return nil end
-
-		local source = nil
-		who:project(tg, x, y, function(px, py)
-			local target = game.level.map(px, py, engine.Map.ACTOR)
-			if not target then return end
+	use_power = { --slightly less obviously Pure's copy of Catalepsy
+		name = function(self, who) return ("accelerate burning effects on all creatures in a radius %d ball within range %d, consuming them to instantly inflict 125%% of all remaining burn damage"):format(self.use_power.radius(self, who), self.use_power.range(self, who)) end,
+		power = 10,
+		range = function(self, who) return 5 end,
+		radius = function(self, who) return 1 end,
+		use=function(self, who, target)
+			local tg = {type="ball", range=self.use_power.range(self, who), radius=self.use_power.radius(self, who), selffire=false}
+			local x, y = who:getTarget(tg)
+			if not x or not y then return nil end
 
-			-- List all diseases, I mean, burns
-			local burns = {}
-			for eff_id, p in pairs(target.tmp) do
-				local e = target.tempeffect_def[eff_id]
-				if e.subtype.fire and p.power and e.status == "detrimental" then
-					burns[#burns+1] = {id=eff_id, params=p}
+			local source = nil
+			who:project(tg, x, y, function(px, py)
+				local target = game.level.map(px, py, engine.Map.ACTOR)
+				if not target then return end
+
+				-- List all diseases, I mean, burns
+				local burns = {}
+				for eff_id, p in pairs(target.tmp) do
+					local e = target.tempeffect_def[eff_id]
+					if e.subtype.fire and p.power and e.status == "detrimental" then
+						burns[#burns+1] = {id=eff_id, params=p}
+					end
 				end
-			end
-			-- Make them EXPLODE !!!
-			for i, d in ipairs(burns) do
-				target:removeEffect(d.id)
-				engine.DamageType:get(engine.DamageType.FIRE).projector(who, px, py, engine.DamageType.FIRE, d.params.power * d.params.dur * 1.25)
-			end
-			game.level.map:particleEmitter(target.x, target.y, 1, "ball_fire", {radius=1})
-		end)
-		game:playSoundNear(who, "talents/fireflash")
-		return {id=true, used=true}
-	end},
+				-- Make them EXPLODE !!!
+				for i, d in ipairs(burns) do
+					target:removeEffect(d.id)
+					engine.DamageType:get(engine.DamageType.FIRE).projector(who, px, py, engine.DamageType.FIRE, d.params.power * d.params.dur * 1.25)
+				end
+				game.level.map:particleEmitter(target.x, target.y, 1, "ball_fire", {radius=1})
+			end)
+			game:playSoundNear(who, "talents/fireflash")
+			return {id=true, used=true}
+		end},
 }
 
 newEntity{ base = "BASE_CLOTH_ARMOR",
@@ -2956,13 +2991,20 @@ newEntity{ base = "BASE_CLOTH_ARMOR",
 		resists={[DamageType.PHYSICAL] = 12, [DamageType.ACID] = 15,},
 	},
 	max_power = 10, power_regen = 1,
-	use_power = { name = "send out a beam of kinetic energy", power = 10,
+	use_power = {
+		name = function(self, who)
+			local dam = who:damDesc(engine.DamageType.PHYSICAL, self.use_power.damage(self, who))
+			return ("send out a range %d beam of kinetic energy, dealing %0.2f to %0.2f physical damage (based on Willpower and Cunning) with knockback"):format(self.use_power.range, 0.8*dam, dam) end,
+		power = 10,
+		damage = function(self, who) return 15 + who:getWil()/3 + who:getCun()/3 end,
+		range =5,
 		use = function(self, who)
-			local dam = 15 + who:getWil()/3 + who:getCun()/3
-			local tg = {type="beam", range=5}
+			local dam = self.use_power.damage(self, who)
+			local tg = {type="beam", range=self.use_power.range}
 			local x, y = who:getTarget(tg)
 			if not x or not y then return nil end
 			who:project(tg, x, y, engine.DamageType.MINDKNOCKBACK, who:mindCrit(rng.avg(0.8*dam, dam)))
+			game.logSeen(who, "%s focuses a beam of kinetic energy from of %s %s!", who.name:capitalize(), who:his_her(), self:getName({no_add_name = true}))
 			game.level.map:particleEmitter(who.x, who.y, tg.radius, "matter_beam", {tx=x-who.x, ty=y-who.y})
 			return {id=true, used=true}
 		end
@@ -3452,50 +3494,57 @@ newEntity{ base = "BASE_GAUNTLETS",
 				talent_on_hit = { [Talents.T_DESTROY_MAGIC] = {level=5, chance=100}, [Talents.T_MANA_CLASH] = {level=3, chance=15}, [Talents.T_AURA_OF_SILENCE] = {level=1, chance=10} },
 			},
 		}
-		self.use_power.name = "destroy magic in a radius 5 cone"
-		self.use_power.power = 100
-		self.use_power.use= function(self,who)
-			local tg = {type="cone", range=0, radius=5}
-			local x, y = who:getTarget(tg)
-			if not x or not y then return nil end
-			who:project(tg, x, y, function(px, py)
-				local target = game.level.map(px, py, engine.Map.ACTOR)
-				if not target then return end
-				target:setEffect(target.EFF_SPELL_DISRUPTION, 10, {src=who, power = 50, max = 75, apply_power=who:combatMindpower()})
-				for i = 1, 2 do
-					local effs = {}
-					-- Go through all spell effects
-					for eff_id, p in pairs(target.tmp) do
-						local e = target.tempeffect_def[eff_id]
-						if e.type == "magical" then
-							effs[#effs+1] = {"effect", eff_id}
+		self.use_power = {
+			name = function(self, who)
+				dam = who:damDesc(engine.DamageType.ARCANE, self.use_power.unnaturaldam(self, who))
+				return ("attempt to destroy all magic effects and sustains on creatures in a radius %d cone (unnatural creaters are additionally dealt %0.2f arcane damage and stunned)"):format(self.use_power.radius, dam)
+			end,
+			power = 100,
+			unnaturaldam = function(self, who) return 100+who:combatMindpower() end,
+			radius = 5,
+			use= function(self,who)
+				local tg = {type="cone", range=0, radius=self.use_power.radius}
+				local x, y = who:getTarget(tg)
+				if not x or not y then return nil end
+				game.logSeen(who, "%s FIRST unleashes antimagic forces from %s %s!", who.name:capitalize(), who:his_her(), self:getName({no_add_name = true}))
+				who:project(tg, x, y, function(px, py)
+					local target = game.level.map(px, py, engine.Map.ACTOR)
+					if not target then return end
+					target:setEffect(target.EFF_SPELL_DISRUPTION, 10, {src=who, power = 50, max = 75, apply_power=who:combatMindpower()})
+					for i = 1, 2 do
+						local effs = {}
+						-- Go through all spell effects
+						for eff_id, p in pairs(target.tmp) do
+							local e = target.tempeffect_def[eff_id]
+							if e.type == "magical" then
+								effs[#effs+1] = {"effect", eff_id}
+							end
 						end
-					end
-					-- Go through all sustained spells
-					for tid, act in pairs(target.sustain_talents) do
-						if act then
-							local talent = target:getTalentFromId(tid)
-							if talent.is_spell then effs[#effs+1] = {"talent", tid} end
+						-- Go through all sustained spells
+						for tid, act in pairs(target.sustain_talents) do
+							if act then
+								local talent = target:getTalentFromId(tid)
+								if talent.is_spell then effs[#effs+1] = {"talent", tid} end
+							end
 						end
-					end
-					local eff = rng.tableRemove(effs)
-					if eff then
-						if eff[1] == "effect" then
-						target:removeEffect(eff[2])
-						else
-							target:forceUseTalent(eff[2], {ignore_energy=true})
+						local eff = rng.tableRemove(effs)
+						if eff then
+							if eff[1] == "effect" then
+							target:removeEffect(eff[2])
+							else
+								target:forceUseTalent(eff[2], {ignore_energy=true})
+							end
 						end
 					end
-				end
-				if target.undead or target.construct then
-					who:project({type="hit"}, target.x, target.y, engine.DamageType.ARCANE,100+who:combatMindpower())
-					if target:canBe("stun") then target:setEffect(target.EFF_STUNNED, 10, {apply_power=who:combatMindpower()}) end
-					game.logSeen(who, "%s's animating magic is disrupted by the burst of power!", who.name:capitalize())
-				end
-			end, nil, {type="slime"})
-			game:playSoundNear(who, "talents/breath")
-			return {id=true, used=true}
-		end
+					if target.undead or target.construct then
+						who:project({type="hit"}, target.x, target.y, engine.DamageType.ARCANE, self.use_power.unnaturaldam(self, who))
+						if target:canBe("stun") then target:setEffect(target.EFF_STUNNED, 10, {apply_power=who:combatMindpower()}) end
+						game.logSeen(target, "%s's animating magic is disrupted by the burst of power!", target.name:capitalize())
+					end
+				end, nil, {type="slime"})
+				game:playSoundNear(who, "talents/breath")
+				return {id=true, used=true}
+			end}
 		end
 
 		who:onWear(self, inven_id, true)
@@ -3596,14 +3645,22 @@ newEntity{ base = "BASE_SHIELD",
 		inc_stats = { [Stats.STAT_WIL] = 5, [Stats.STAT_CUN] = 3, },
 	},
 	max_power = 30, power_regen = 1,
-	use_power = { name = "send out a beam of light", power = 12,
+	use_power = {
+		name = function(self, who)
+			local dam = who:damDesc(engine.DamageType.LIGHT, self.use_power.damage(self, who))
+			return ("send out a range %d beam, lighting its path and dealing %0.2f to %0.2f light damage (based on Willpower and Cunning)"):format(self.use_power.range, 0.8*dam, dam)
+		end,
+		power = 12,
+		damage = function(self, who) return 20 + who:getWil()/3 + who:getCun()/3 end,
+		range = 7,
 		use = function(self, who)
-			local dam = 20 + who:getWil()/3 + who:getCun()/3
-			local tg = {type="beam", range=7}
+			local dam = self.use_power.damage(self, who)
+			local tg = {type="beam", range=self.use_power.range}
 			local x, y = who:getTarget(tg)
 			if not x or not y then return nil end
 			
 			who:project(tg, x, y, engine.DamageType.LITE_LIGHT, who:mindCrit(rng.avg(0.8*dam, dam)))
+			game.logSeen(who, "%s's %s flashes!", who.name:capitalize(), self:getName({no_add_name = true}))
 			game.level.map:particleEmitter(who.x, who.y, tg.radius, "light_beam", {tx=x-who.x, ty=y-who.y})
 			return {id=true, used=true}
 		end
@@ -3908,7 +3965,7 @@ newEntity{ base = "BASE_LITE", --Thanks Frumple!
 	material_level=3,
 	sentient=true,
 	charge = 0,
-	special_desc = function(self) return "Absorbs all darkness in its light radius." end,
+	special_desc = function(self) return ("Absorbs all darkness (power %d, based on Willpower and Cunning) within its light radius, increasing in brightness. (current charge %d)."):format(self.worn_by and self.use_power.litepower(self, self.worn_by) or 100, self.charge) end,
 	on_wear = function(self, who)
 		self.worn_by = who
 	end,
@@ -3924,23 +3981,20 @@ newEntity{ base = "BASE_LITE", --Thanks Frumple!
 		if game.level and not game.level:hasEntity(self.worn_by) and not self.worn_by.player then self.worn_by = nil return end
 		if self.worn_by:attr("dead") then return end
 		
-		
 		who:project({type="ball", range=0, radius=self.wielder.lite}, who.x, who.y, function(px, py) -- The main event!
 			local is_lit = game.level.map.lites(px, py)
 			if is_lit then return end
 			
 			if not self.max_charge then
-			
 				self.charge = self.charge + 1
-				
 				if self.charge == 200 then
+					self.charge = 300 -- Power boost when fully charged :)
 					self.max_charge=true
-					game.logPlayer(who, "Umbraphage is fully powered!")
+					game.logPlayer(who, "#ORCHID#Umbraphage is fully powered!")
 				end
-			
 			end
 		end)
-		who:project({type="ball", range=0, radius=self.wielder.lite}, who.x, who.y, engine.DamageType.LITE, 100) -- Light the space!
+		who:project({type="ball", range=0, radius=self.wielder.lite}, who.x, who.y, engine.DamageType.LITE, self.use_power.litepower(self, who)) -- Light the space!
 		if (5 + math.floor(self.charge/20)) > self.wielder.lite and self.wielder.lite < 10 then
 			local p = self.power
 			who:onTakeoff(self, who.INVEN_LITE, true)
@@ -3965,16 +4019,26 @@ newEntity{ base = "BASE_LITE", --Thanks Frumple!
 		}
 	},
 	max_power = 10, power_regen = 1,
-	use_power = { name = "release the absorbed darkness", power = 10,
+	use_power = {
+		name = function(self, who)
+			local dam = who:damDesc(engine.DamageType.DARKNESS, self.use_power.damage(self, who))
+			return ("release absorbed darkness in a %d radius cone with a %d%% chance to blind (based on lite radius), dealing %0.2f darkness damage (based on Mindpower and charge)"):format(self.use_power.radius(self, who), self.use_power.blindchance(self, who), dam)
+		end,
+		power = 10,
+		damage = function(self, who) return 15 + 3*who:combatMindpower() + math.floor(self.charge/4) end, -- Damage is based on charge
+		-- radius of cone and chance to blind depend on lite radius of the artifact
+		radius = function(self, who) return self.wielder.lite end,
+		blindchance = function(self, who) return self.wielder.lite*10 end,
+
+		litepower = function(self, who) return who:combatStatScale(who:getWil(70)+who:getCun(30), 50, 150) end,
 		use = function(self, who)
-			if self.max_charge then self.charge=300 end -- Power boost if you fully charged :)
-			local dam = (15 + who:combatMindpower()) * 4+math.floor(self.charge/50) -- Damage is based on charge
-			local tg = {type="cone", range=0, radius=self.wielder.lite} -- Radius of Cone is based on lite radius of the artifact
+			local dam = self.use_power.damage(self, who)
+			local tg = {type="cone", range=0, radius=self.use_power.radius(self, who)}
 			local x, y = who:getTarget(tg)
 			if not x or not y then return nil end
-			
+			game.logSeen(who, "%s triggers %s %s, unleashing a torrent of shadows!", who.name:capitalize(), who:his_her(), self:getName())
 			who:project(tg, x, y, engine.DamageType.DARKNESS, who:mindCrit(dam)) -- FIRE!
-			who:project(tg, x, y, engine.DamageType.RANDOM_BLIND, self.wielder.lite*10) -- FIRE!
+			who:project(tg, x, y, engine.DamageType.RANDOM_BLIND, self.use_power.blindchance(self, who)) -- blind
 			game.level.map:particleEmitter(who.x, who.y, tg.radius, "breath_dark", {radius=tg.radius, tx=x-who.x, ty=y-who.y})
 			self.max_charge=nil -- Reset charge.
 			self.charge=0
@@ -4088,7 +4152,7 @@ newEntity{ base = "BASE_TOOL_MISC",
 		combat_mindpower=8,
 	},
 		max_power = 35, power_regen = 1,
-	use_power = { name = "call an antimagic pillar, but silence yourself", power = 35,
+	use_power = { name = "call an antimagic pillar for 15 turns, but silence yourself for 5 turns", power = 35,
 		use = function(self, who)
 			local x, y = util.findFreeGrid(who.x, who.y, 5, true, {[engine.Map.ACTOR]=true})
 			if not x then
@@ -4213,12 +4277,21 @@ newEntity{ base = "BASE_TOOL_MISC",
 		combat_spellpower=10,
 	},
 	max_power = 40, power_regen = 1,
-	use_power = { name = "release a burst of void energy", power = 20,
+	use_power = {
+		name = function(self, who)
+			local dam = self.use_power.damage(self, who)/2
+			return ("release a radius %d burst of void energy at up to range %d, dealing %0.2f temporal and %0.2f darkness damage (based on Magic)"):format(self.use_power.radius, self.use_power.range, who:damDesc(engine.DamageType.TEMPORAL, dam), who:damDesc(engine.DamageType.DARKNESS, dam))
+		end,
+		power = 20,
+		damage = function(self, who) return 200 + who:getMag() * 2 end,
+		range = 5,
+		radius = 2,
 		use = function(self, who)
-			local tg = {type="ball", range=5, radius=2}
+			local tg = {type="ball", range=self.use_power.range, radius=self.use_power.radius}
 			local x, y = who:getTarget(tg)
 			if not x or not y then return nil end
-			who:project(tg, x, y, engine.DamageType.VOID, 200 + who:getMag() * 2)
+			who:project(tg, x, y, engine.DamageType.VOID, self.use_power.damage(self, who))
+			game.logSeen(who, "%s siphons space and time into %s %s!", who.name:capitalize(), who:his_her(), self:getName({no_add_name = true}))
 			game.level.map:particleEmitter(x, y, tg.radius, "shadow_flash", {radius=tg.radius, tx=x, ty=y})
 			return {id=true, used=true}
 		end
@@ -4741,9 +4814,12 @@ newEntity{ base = "BASE_LIGHT_ARMOR", --Thanks SageAcrin!
  		},
 	},
 	max_power = 50, power_regen = 1,
-	use_power = { name = "turn yourself invisible for 10 turns", power = 50,
+		use_power = {
+		name = function(self, who) return ("turn yourself invisible (power %d, based on Cunning and Magic) for 10 turns"):format(self.use_power.invispower(self, who)) end,
+		power = 50,
+		invispower = function(self, who) return 10+who:getCun()/6+who:getMag()/6 end,
 		use = function(self, who)
-			who:setEffect(who.EFF_INVISIBILITY, 10, {power=10+who:getCun()/6+who:getMag()/6, penalty=0.5, regen=true})
+			who:setEffect(who.EFF_INVISIBILITY, 10, {power=self.use_power.invispower(self, who), penalty=0.5, regen=true})
 			return {id=true, used=true}
 		end
 	},
@@ -4813,15 +4889,19 @@ newEntity{ base = "BASE_TOOL_MISC", --Thanks Alex!
 		flat_damage_armor = {all=0},
 	},
 	max_power = 20, power_regen = 1,
-	use_power = { name = "flip the hourglass", power = 20,
+	use_power = {
+		name = function(self, who) return ("flip the hourglass (sands currently flowing towards %s)"):format(self.direction > 0 and "stability" or "entropy") end,
+		power = 20,
 		use = function(self, who)
+			local power = self.power
 			self.direction = self.direction * -1
 			self.finished = false
 			who:onTakeoff(self, who.INVEN_TOOL, true)
 			self.wielder.inc_damage.all = 0
 			self.wielder.flat_damage_armor.all = 0
 			who:onWear(self, who.INVEN_TOOL, true)
-			game.logPlayer(who, "#GOLD#The sands slowly begin falling in the other direction.")
+			game.logPlayer(who, "#GOLD#The sands slowly begin falling towards %s.", self.direction > 0 and "stability" or "entropy")
+			self.power = power
 		end
 	},
 	on_wear = function(self, who)
@@ -4839,6 +4919,7 @@ newEntity{ base = "BASE_TOOL_MISC", --Thanks Alex!
 		local who = self.worn_by
 		local direction=self.direction
 		if self.finished == true then return end
+		local power = self.power
 		who:onTakeoff(self, who.INVEN_TOOL, true)
 		
 		self.wielder.resists.all = self.wielder.resists.all + direction * 3
@@ -4859,6 +4940,7 @@ newEntity{ base = "BASE_TOOL_MISC", --Thanks Alex!
 		end
 		
 		who:onWear(self, who.INVEN_TOOL, true)
+		self.power = power
 	end,
 }
 
@@ -5388,7 +5470,7 @@ newEntity{ base = "BASE_LEATHER_CAP",
 		psi_on_crit=6,
 	},
 	max_power = 30, power_regen = 1,
-	use_power = { name = "reveal the surrounding area", power = 30,
+	use_power = { name = "reveal the surrounding area (range 20)", power = 30,
 		use = function(self, who)
 			who:magicMap(20)
 			game.logSeen(who, "%s has a sudden vision!", who.name:capitalize())
@@ -5716,15 +5798,17 @@ newEntity{ base = "BASE_LONGSWORD",
 	rarity = 250,
 	cost = 300,
 	material_level = 5,
-	sentient=true,
 	running=false,
-	special_desc = function(self) return ("Enter Rampage if HP falls under 20%% (Shared 30 turn cooldown)") end,
+	special_desc = function(self)
+		local maxp = self:min_power_to_trigger()
+		return ("Enter Rampage if health falls below 20%%%s"):format(self.power < maxp and (" (cooling down: %d turns)"):format(maxp - self.power) or "") 
+	end,
 	combat = {
 		dam = 48,
 		apr = 12,
 		physcrit = 10,
 		dammod = {str=1},
-		special_on_hit = {desc="Attempt to devour a low HP enemy, striking again and possibly killing instantly.", fct=function(combat, who, target)
+		special_on_hit = {desc="Attempt to devour a low HP enemy, striking again and possibly killing it instantly.", fct=function(combat, who, target)
 			local Talents = require "engine.interface.ActorTalents"
 			local o, item, inven_id = who:findInAllInventoriesBy("define_as", "BUTCHER")
 			if not o or not who:getInven(inven_id).worn then return end
@@ -5736,11 +5820,12 @@ newEntity{ base = "BASE_LONGSWORD",
 			end
 			o.running=false
 		end},
-		special_on_kill = {desc="Enter a Rampage (Shared 30 turn cooldown).", fct=function(combat, who, target)
+		special_on_kill = {desc="Enter a Rampage (Shared cooldown).", fct=function(combat, who, target)
+			if who:hasEffect(who.EFF_RAMPAGE) then return end
 			local Talents = require "engine.interface.ActorTalents"
 			local o, item, inven_id = who:findInAllInventoriesBy("define_as", "BUTCHER")
 			if not o or not who:getInven(inven_id).worn then return end
-			if o.power < o.max_power then return end
+			if o.power < o:min_power_to_trigger() then return end
 			who:forceUseTalent(Talents.T_RAMPAGE, {ignore_cd=true, ignore_energy=true, force_level=2, ignore_ressources=true})
 			o.power = 0
 		end},
@@ -5754,24 +5839,24 @@ newEntity{ base = "BASE_LONGSWORD",
 		combat_atk = 18,
 	},
 	max_power = 30, power_regen = 1,
-	use_power = { name = "", power = 30, hidden = true, use = function(self, who) return end},
+	min_power_to_trigger = function(self) return self.max_power * (self.worn_by and (100 - (self.worn_by:attr("use_object_cooldown_reduce") or 0))/100 or 1) end, -- special handling of the Charm Mastery attribute
 	on_wear = function(self, who)
 		self.worn_by = who
 	end,
 	on_takeoff = function(self)
 		self.worn_by = nil
 	end,
-	act = function(self)
-		self:useEnergy()
-		self:regenPower()
-		if not self.worn_by then return end
-		local who=self.worn_by
-		if game.level and not game.level:hasEntity(who) and not who.player then self.worn_by = nil return end
-		if who.life/who.max_life < 0.2 and self.power == self.max_power then
-			local Talents = require "engine.interface.ActorTalents"
-			who:forceUseTalent(Talents.T_RAMPAGE, {ignore_cd=true, ignore_energy=true, force_level=2, ignore_ressources=true})
-			self.power=0
-		end
+	callbackOnTakeDamage = function(self, who, src, x, y, type, dam, state) -- Trigger Rampage
+		if not self.worn_by or self.power < self:min_power_to_trigger() then return end
+		if (who.life - dam)/who.max_life >=0.2 then return end
+		game:onTickEnd(function() -- make sure all damage has been resolved
+			if who.life/who.max_life < 0.2 and not who:hasEffect(who.EFF_RAMPAGE) then
+				local Talents = require "engine.interface.ActorTalents"
+				who:forceUseTalent(Talents.T_RAMPAGE, {ignore_cd=true, ignore_energy=true, force_level=2, ignore_ressources=true})
+				self.power=0
+				end
+			end
+		)
 	end,
 }
 
@@ -6157,18 +6242,30 @@ In the case of opponents who weren't alone, he had to improvise.]],
 	},
 	talent_on_spell = { {chance=10, talent="T_RETHREAD", level = 2} },
 	max_power = 32, power_regen = 1,
-	use_power = { name = "deal temporal damage to summons, and if they survive, remove them from time", power = 32,
+	use_power = {
+		name = function(self, who)
+			local Talents = require "engine.interface.ActorTalents"
+			local tal = Talents:getTalentFromId(Talents.T_TIME_SKIP)
+			local oldlevel = who.talents[Talents.T_TIME_SKIP]
+			who.talents[Talents.T_TIME_SKIP] = 2 --Set up to calculate damage accurately
+			local dam = who:damDesc(engine.DamageType.TEMPORAL, who:callTalent(Talents.T_TIME_SKIP, "getDamage", who, tal))
+			who.talents[Talents.T_TIME_SKIP] = oldlevel
+			return ("inflict %0.2f temporal damage (based on Spellpower and Paradox, if any) on summons, removing any that survive from time, in a radius %d ball out to range %d"):format(dam, self.use_power.radius, self.use_power.range)
+		end,
+		power = 32,
+		range =5,
+		radius = 2,
 		use = function(self, who)
 			local Talents = require "engine.interface.ActorTalents"
 			local tg = {type="ball", range=5, radius=2}
 			local x, y = who:getTarget(tg)
 			if not x or not y then return nil end
 			who:project(tg, x, y, function(px, py) 
-			local target = game.level.map(px, py, engine.Map.ACTOR)
-			if not target then return end
-			if target.summoner then
-			who:forceUseTalent(Talents.T_TIME_SKIP, {ignore_cd=true, ignore_energy=true, force_target=target, force_level=2, ignore_ressources=true})
-			end
+				local target = game.level.map(px, py, engine.Map.ACTOR)
+				if not target then return end
+				if target.summoner then -- consider making this work on low rank critters also
+					who:forceUseTalent(Talents.T_TIME_SKIP, {ignore_cd=true, ignore_energy=true, force_target=target, force_level=2, ignore_ressources=true})
+				end
 			end)
 			return {id=true, used=true}
 		end
@@ -6692,9 +6789,12 @@ newEntity{ base = "BASE_GREATSWORD",
 		amplify_sun_beam = 15,
 	},
 	max_power = 30, power_regen = 1,
-	use_power = { name = "strike with your weapon as 100% light damage, up to 4 spaces away, healing for 50% of the damage dealt", power = 30,
+	use_power = {
+		name = function(self, who) return ("attack everything in a line out to range %d, dealing 100%% weapon damage (as light), and healing for 50%% of the damage dealt"):format(self.use_power.range) end,
+		power = 30,
+		range = 4,
 		use = function(self, who)
-			local tg = {type="beam", range=4}
+			local tg = {type="beam", range=self.use_power.range}
 			local x, y = who:getTarget(tg)
 			if not x or not y then return nil end
 			local _ _, x, y = who:canProject(tg, x, y)
@@ -6735,7 +6835,7 @@ newEntity{ base = "BASE_MASSIVE_ARMOR",
 		speed_resist=1,
 	},
 	max_power = 25, power_regen = 1,
-	use_power = { name = "slow all units within 5 spaces (including yourself) by 40%", power = 25,
+	use_power = { name = "slow the movement speed of all creatures within 5 spaces (including yourself) by 40%", power = 25,
 		use = function(self, who)
 			who:project({type="ball", range=0, radius=5}, who.x, who.y, function(px, py)
 				local target = game.level.map(px, py, engine.Map.ACTOR)
@@ -7320,9 +7420,12 @@ newEntity{ base = "BASE_MACE",
 		},
 	},
 	max_power = 60, power_regen = 1,
-	use_power = { name = "strike a target at range for an automatic critical hit as lightning damage", power = 60,
+	use_power = {
+		name = function(self, who) return ("strike a target at up to range %d for an automatic critical hit as lightning damage"):format(self.use_power.range) end,
+		power = 60,
+		range = 10,
 		use = function(self, who)
-			local tg = {type="hit", range=10}
+			local tg = {type="hit", range=self.use_power.range}
 			local x, y = who:getTarget(tg)
 			if not x or not y then return nil end
 			local _ _, x, y = who:canProject(tg, x, y)
diff --git a/game/modules/tome/data/timed_effects/magical.lua b/game/modules/tome/data/timed_effects/magical.lua
index 675d1398c2..1af79eae0b 100644
--- a/game/modules/tome/data/timed_effects/magical.lua
+++ b/game/modules/tome/data/timed_effects/magical.lua
@@ -264,7 +264,7 @@ newEffect{
 newEffect{
 	name = "INVISIBILITY", image = "effects/invisibility.png",
 	desc = "Invisibility",
-	long_desc = function(self, eff) return ("Improves/gives invisibility (power %d)."):format(eff.power) end,
+	long_desc = function(self, eff) return ("Improves/gives invisibility (power %d), reducing damage dealt by %d%%%s."):format(eff.power, eff.penalty*100, eff.regen and " and preventing healing and life regeneration" or "") end,
 	type = "magical",
 	subtype = { phantasm=true },
 	status = "beneficial",
-- 
GitLab