From 219b4d778486f03dbdf5c3cec05ca1681bf947de Mon Sep 17 00:00:00 2001
From: dg <dg@51575b47-30f0-44d4-a5cc-537603b46e54>
Date: Sat, 27 Aug 2011 16:28:47 +0000
Subject: [PATCH] Temporal Devourers renamed Devourer and subtype changed to
 eldritch Temporal Dredge renmed Dredge (subtype still temporal) Worm that
 Walks and Devourers now have unique talents Devourer hit points and damage
 improved (beware the land sharks!!) Carrion Worms multiply less but have
 better resists Epidemic now checks for disease immunity when spreading
 diseases Dredge Frenzy improved a bit

git-svn-id: http://svn.net-core.org/repos/t-engine4@4271 51575b47-30f0-44d4-a5cc-537603b46e54
---
 game/modules/tome/class/Actor.lua             |   3 +
 game/modules/tome/data/damage_types.lua       |  26 +-
 .../modules/tome/data/general/npcs/horror.lua |  73 +++---
 .../modules/tome/data/general/npcs/vermin.lua |  11 +-
 ...ourer.png => horror_eldritch_devourer.png} | Bin
 ..._dredge.png => horror_temporal_dredge.png} | Bin
 .../tome/data/talents/corruptions/plague.lua  |   6 +-
 .../tome/data/talents/misc/horrors.lua        | 241 +++++++++++++++---
 game/modules/tome/data/talents/misc/npcs.lua  |  27 +-
 game/modules/tome/data/timed_effects.lua      |  97 ++++++-
 10 files changed, 397 insertions(+), 87 deletions(-)
 rename game/modules/tome/data/gfx/shockbolt/npc/{horror_temporal_temporal_devourer.png => horror_eldritch_devourer.png} (100%)
 rename game/modules/tome/data/gfx/shockbolt/npc/{horror_temporal_temporal_dredge.png => horror_temporal_dredge.png} (100%)

diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua
index 444a00cea5..461afdf5b8 100644
--- a/game/modules/tome/class/Actor.lua
+++ b/game/modules/tome/class/Actor.lua
@@ -143,6 +143,9 @@ function _M:init(t, no_default)
 	t.resists = t.resists or {}
 	t.resists_cap = t.resists_cap or { all = 100 }
 	t.resists_pen = t.resists_pen or {}
+	
+	-- Absorbs a percentage of damage
+	t.damage_affinity = t.damage_affinity or {}
 
 	-- % Increase damage
 	t.inc_damage = t.inc_damage or {}
diff --git a/game/modules/tome/data/damage_types.lua b/game/modules/tome/data/damage_types.lua
index a9832dda37..58f344c83f 100644
--- a/game/modules/tome/data/damage_types.lua
+++ b/game/modules/tome/data/damage_types.lua
@@ -133,7 +133,14 @@ setDefaultProjector(function(src, x, y, type, dam, tmp, no_martyr)
 			target:setEffect(target.EFF_SMEARED, 6, {src=src, power=smear/6})
 			dam = 0
 		end
-
+		
+		-- affinity healing, we store it to apply it after damage is resolved
+		local affinity_heal = 0
+		if target.damage_affinity then
+			local aff = (target.damage_affinity[type] or 0) / 100
+			affinity_heal = (dam * aff)
+		end
+		
 		-- Reduce damage with resistance
 		if target.resists then
 			local pen = 0
@@ -198,6 +205,12 @@ setDefaultProjector(function(src, x, y, type, dam, tmp, no_martyr)
 				target:setEffect(target.EFF_ELEMENTAL_HARMONY, 5 + math.ceil(target:attr("elemental_harmony")), {power=target:attr("elemental_harmony"), type=type})
 			end
 		end
+		
+		-- damage affinity healing
+		if not target.dead and affinity_heal > 0 then
+			target:heal(affinity_heal)
+			game.logSeen(target, "%s is healed by the %s%s#LAST# damage!", target.name:capitalize(), DamageType:get(type).text_color or "#aaaaaa#", DamageType:get(type).name)
+		end
 
 		if dam > 0 and src.__projecting_for and src.__projecting_for.project_type and (src.__projecting_for.project_type.talent_id or src.__projecting_for.project_type.talent) and src.getTalentFromId and not src.__projecting_for.talent_on_hit_done then
 			local t = src:getTalentFromId(src.__projecting_for.project_type.talent or src.__projecting_for.project_type.talent_id)
@@ -1546,17 +1559,6 @@ newDamageType{
 	end,
 }
 
--- Dredge Haste
-newDamageType{
-	name = "dredge frenzy", type = "DREDGE_FRENZY",
-	projector = function(src, x, y, type, dam)
-		local target = game.level.map(x, y, Map.ACTOR)
-		if target and target.dredge then
-			target:setEffect(target.EFF_SPEED, 3, {power=0.6})
-		end
-	end,
-}
-
 newDamageType{
 	name = "devour life", type = "DEVOUR_LIFE",
 	projector = function(src, x, y, type, dam)
diff --git a/game/modules/tome/data/general/npcs/horror.lua b/game/modules/tome/data/general/npcs/horror.lua
index a4a55676a6..82c4e22e8f 100644
--- a/game/modules/tome/data/general/npcs/horror.lua
+++ b/game/modules/tome/data/general/npcs/horror.lua
@@ -44,7 +44,8 @@ newEntity{
 
 newEntity{ base = "BASE_NPC_HORROR",
 	name = "worm that walks", color=colors.SANDY_BROWN,
-	desc = [[A maggot-filled robe with a vaguely humanoid shape.]],
+	desc = [[A bulging rotten robe seems to tear at the seams, with masses of bloated worms spilling from out and around the moving form.  Two arm-like appendages, each made up of overlapping mucous-drenched maggots, grasp tightly around the handles of bile-coated waraxes. 
+	Each swing drips pestulant fluid before it, and each droplet writhes and wriggles in the air before splashing against the ground.]],
 	level_range = {25, nil}, exp_worth = 1,
 	rarity = 5,
 	max_life = resolvers.rngavg(150,170),
@@ -52,8 +53,9 @@ newEntity{ base = "BASE_NPC_HORROR",
 	rank = 3,
 	hate_regen = 1,
 	vim_regen = 1,
-
-	ai = "tactical", ai_state = { ai_move="move_dmap", talent_in=1, },
+	
+	autolevel = "warriormage",
+	ai = "tactical", ai_state = { ai_move="move_dmap", talent_in=1, ally_compassion=0 },
 	ai_tactic = resolvers.tactic "melee",
 
 	see_invisible = 100,
@@ -61,10 +63,11 @@ newEntity{ base = "BASE_NPC_HORROR",
 	stun_immune = 1,
 	blind_immune = 1,
 	disease_immune = 1,
-	move_others=true,
-
-	resists = { [DamageType.PHYSICAL] = 50, [DamageType.BLIGHT] = 100, [DamageType.FIRE] = -50},
 
+	resists = { [DamageType.PHYSICAL] = 50, [DamageType.ACID] = 100, [DamageType.BLIGHT] = 100, [DamageType.FIRE] = -50},
+	damage_affinity = { [DamageType.BLIGHT] = 50 },	
+	no_auto_resists = true,
+	
 	body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1 },
 	resolvers.drops{chance=20, nb=1, {} },
 	resolvers.equip{
@@ -78,15 +81,15 @@ newEntity{ base = "BASE_NPC_HORROR",
 	resolvers.inscriptions(1, {"regeneration infusion"}),
 
 	resolvers.talents{
-		--[Talents.T_BONE_GRAB]={base=4, every=10},
-		[Talents.T_BLINDSIDE]={base=5, every=12},
+		[Talents.T_WORM_ROT]={base=4, every=8},
+		[Talents.T_EPIDEMIC]={base=4, every=8},
+		[Talents.T_REND]={base=4, every=8},
+		[Talents.T_ACID_STRIKE]={base=4, every=8},
+		[Talents.T_BLOODLUST]={base=4, every=8},
+		[Talents.T_RUIN]={base=4, every=8},
 		[Talents.T_CORRUPTED_STRENGTH]={base=3, every=15},
-		[Talents.T_SLASH]={base=4, every=12},
-		[Talents.T_FRENZY]={base=4, every=15},
-		[Talents.T_REND]={base=4, every=12},
-		[Talents.T_BLOODLUST]={base=3, every=12},
-		[Talents.T_RUIN]={base=2, every=12},
-		[Talents.T_SUMMON]=1,
+		
+		[Talents.T_BLINDSIDE]={base=3, every=12},
 
 		[Talents.T_WEAPON_COMBAT]={base=5, every=10, max=10},
 		[Talents.T_WEAPONS_MASTERY]={base=3, every=10, max=10},
@@ -96,14 +99,12 @@ newEntity{ base = "BASE_NPC_HORROR",
 
 	on_takehit = function(self, value, src)
 		if value >= (self.max_life * 0.1) then
-			self:forceUseTalent(self.T_SUMMON, {ignore_energy=true, ignore_cd=true})
+			local t = self:getTalentFromId(self.T_WORM_ROT)
+			t.spawn_carrion_worm(self, self, t)
+			game.logSeen(self, "#LIGHT_RED#A carrion worm mass has spawned from %s' wounds!", self.name)
 		end
 		return value
 	end,
-
-	summon = {
-		{type="vermin", subtype="worms", name="carrion worm mass", number=1, hasxp=false},
-	},
 }
 
 newEntity{ base = "BASE_NPC_HORROR",
@@ -474,11 +475,9 @@ newEntity{ base = "BASE_NPC_HORROR",
 	},
 }
 
--- Temporal Horrors
-
 newEntity{ base = "BASE_NPC_HORROR",
-	subtype = "temporal",
-	name = "temporal devourer", color=colors.CRIMSON,
+	subtype = "eldritch",
+	name = "devourer", color=colors.CRIMSON,
 	desc = "A headless, round creature with stubby legs and arms.  Its body seems to be all teeth.",
 	level_range = {10, nil}, exp_worth = 1,
 	rarity = 2,
@@ -486,17 +485,27 @@ newEntity{ base = "BASE_NPC_HORROR",
 	movement_speed = 0.8,
 	size_category = 2,
 	autolevel = "warrior",
-	max_life = resolvers.rngavg(50, 80),
-	combat_armor = 1, combat_def = 10,
-	combat = { dam=resolvers.levelup(resolvers.rngavg(20,30), 1, 1.2), atk=resolvers.rngavg(10,20), apr=5, dammod={str=1} },
+	max_life = resolvers.rngavg(80, 100),
+	life_rating = 14,
+	life_regen = 4,
+	combat_armor = 16, combat_def = 1,
+	combat = { dam=resolvers.levelup(resolvers.rngavg(25,40), 1, 0.6), atk=resolvers.rngavg(25,50), apr=25, dammod={str=1.1}, physcrit = 10 },
+	ai_state = { talent_in=2, },
 
-	resists = { [DamageType.TEMPORAL] = 5},
+	resolvers.talents{
+		[Talents.T_BLOODBATH]={base=1, every=5, max=7},
+		[Talents.T_GNASHING_TEETH]={base=1, every=5, max=7},
+		-- talents only usable while frenzied
+		[Talents.T_FRENZIED_LEAP]={base=1, every=5, max=7},
+		[Talents.T_FRENZIED_BITE]={base=1, every=5, max=7},
+	},
 
 	make_escort = {
-		{type="horror", subtype="temporal", name="temporal devourer", number=2, no_subescort=true},
+		{type="horror", subtype="eldritch", name="devourer", number=2, no_subescort=true},
 	},
 }
 
+-- temporal horrors
 newEntity{ base = "BASE_NPC_HORROR",
 	subtype = "temporal",
 	dredge = 1,
@@ -511,7 +520,7 @@ newEntity{ base = "BASE_NPC_HORROR",
 	combat_armor = 1, combat_def = 10,
 	combat = { dam=resolvers.levelup(resolvers.rngavg(15,20), 1, 1.1), atk=resolvers.rngavg(5,15), apr=5, dammod={str=1} },
 
-	resists = { [DamageType.TEMPORAL] = 5},
+	resists = { [DamageType.TEMPORAL] = 25},
 
 	resolvers.talents{
 		[Talents.T_DUST_TO_DUST]={base=1, every=7, max=5},
@@ -523,7 +532,7 @@ newEntity{ base = "BASE_NPC_HORROR",
 newEntity{ base = "BASE_NPC_HORROR",
 	subtype = "temporal",
 	dredge = 1,
-	name = "temporal dredge", color=colors.PINK,
+	name = "dredge", color=colors.PINK,
 	desc = "A hulking pink-skinned creature with long arms as thick as tree trunks.  It drags its knuckles on the ground as it lumbers toward you.",
 	level_range = {15, nil}, exp_worth = 1,
 	rarity = 2,
@@ -536,7 +545,7 @@ newEntity{ base = "BASE_NPC_HORROR",
 	combat_armor = 1, combat_def = 0,
 	combat = { dam=resolvers.levelup(resolvers.rngavg(25,150), 1, 1.2), atk=resolvers.rngavg(25,130), apr=1, dammod={str=1.1} },
 
-	resists = {all = 10, [DamageType.TEMPORAL] = 50, [DamageType.PHYSICAL] = 25},
+	resists = {all = 10, [DamageType.TEMPORAL] = 25, [DamageType.PHYSICAL] = 25},
 
 	resolvers.talents{
 		[Talents.T_STUN]={base=3, every=7, max=7},
@@ -565,7 +574,7 @@ newEntity{ base = "BASE_NPC_HORROR",
 	resists = {all = 10, [DamageType.TEMPORAL] = 50},
 
 	make_escort = {
-		{type="horror", subtype="temporal", name="temporal dredge", number=3, no_subescort=true},
+		{type="horror", subtype="temporal", name="dredge", number=3, no_subescort=true},
 	},
 
 	resolvers.inscriptions(1, {"shielding rune"}),
diff --git a/game/modules/tome/data/general/npcs/vermin.lua b/game/modules/tome/data/general/npcs/vermin.lua
index d3a6c6f16f..b01681782b 100644
--- a/game/modules/tome/data/general/npcs/vermin.lua
+++ b/game/modules/tome/data/general/npcs/vermin.lua
@@ -66,15 +66,18 @@ newEntity{ base = "BASE_NPC_WORM",
 
 newEntity{ base = "BASE_NPC_WORM",
 	name = "carrion worm mass", color=colors.SANDY_BROWN,
+	can_multiply = 2,
 	level_range = {20, nil}, exp_worth = 1,
 	rarity = 4,
 	max_life = resolvers.rngavg(5,9),
 	combat = { dam=1, atk=15, apr=100 },
-	
-	resists = {[DamageType.BLIGHT] = 100},
+
+	disease_immune = 1,
+	resists = { [DamageType.PHYSICAL] = 50, [DamageType.ACID] = 100, [DamageType.BLIGHT] = 100, [DamageType.FIRE] = -50},
+	no_auto_resists = true,
 
 	autolevel = "warriormage",
-	resolvers.talents{ [Talents.T_CRAWL_POISON]=2, [Talents.T_ROTTING_DISEASE]=4, [Talents.T_MULTIPLY]=1 },
+	resolvers.talents{ [Talents.T_CRAWL_ACID]=2, [Talents.T_ROTTING_DISEASE]=4, [Talents.T_MULTIPLY]=1 },
 	
 	on_die = function(self, src)
 		game.level.map:addEffect(self,
@@ -82,7 +85,7 @@ newEntity{ base = "BASE_NPC_WORM",
 			engine.DamageType.BLIGHT, self:getStr(90, true),
 			2,
 			5, nil,
-			engine.Entity.new{alpha=100, display='', color_br=30, color_bg=180, color_bb=60}
+			engine.Entity.new{alpha=50, display='', color_br=30, color_bg=180, color_bb=60}
 		)
 		game.logSeen(self, "%s exudes a corrupted gas as it dies.", self.name:capitalize())
 	end,
diff --git a/game/modules/tome/data/gfx/shockbolt/npc/horror_temporal_temporal_devourer.png b/game/modules/tome/data/gfx/shockbolt/npc/horror_eldritch_devourer.png
similarity index 100%
rename from game/modules/tome/data/gfx/shockbolt/npc/horror_temporal_temporal_devourer.png
rename to game/modules/tome/data/gfx/shockbolt/npc/horror_eldritch_devourer.png
diff --git a/game/modules/tome/data/gfx/shockbolt/npc/horror_temporal_temporal_dredge.png b/game/modules/tome/data/gfx/shockbolt/npc/horror_temporal_dredge.png
similarity index 100%
rename from game/modules/tome/data/gfx/shockbolt/npc/horror_temporal_temporal_dredge.png
rename to game/modules/tome/data/gfx/shockbolt/npc/horror_temporal_dredge.png
diff --git a/game/modules/tome/data/talents/corruptions/plague.lua b/game/modules/tome/data/talents/corruptions/plague.lua
index 0ef613a0d1..d8fcb47202 100644
--- a/game/modules/tome/data/talents/corruptions/plague.lua
+++ b/game/modules/tome/data/talents/corruptions/plague.lua
@@ -212,7 +212,11 @@ newTalent{
 			local disease = rng.table(diseases)
 			local params = disease.params
 			params.src = self
-			target:setEffect(disease.id, 6, {src=self, dam=disease.params.dam, str=disease.params.str, dex=disease.params.dex, con=disease.params.con, heal_factor=disease.params.heal_factor})
+			if target:checkHit(self:combatSpellpower(), target:combatSpellResist(), 0, 95, 12 - self:getTalentLevel(t)) and target:canBe("disease") then
+				target:setEffect(disease.id, 6, {src=self, dam=disease.params.dam, str=disease.params.str, dex=disease.params.dex, con=disease.params.con, heal_factor=disease.params.heal_factor, burst=disease.params.burst})
+			else
+				game.logSeen(target, "%s resists the disease!", target.name:capitalize())
+			end
 			game.level.map:particleEmitter(px, py, 1, "slime")
 		end)
 	end,
diff --git a/game/modules/tome/data/talents/misc/horrors.lua b/game/modules/tome/data/talents/misc/horrors.lua
index 313a571f07..625a6cb146 100644
--- a/game/modules/tome/data/talents/misc/horrors.lua
+++ b/game/modules/tome/data/talents/misc/horrors.lua
@@ -19,11 +19,139 @@
 
 newTalentType{ type="technique/horror", name = "horror techniques", hide = true, description = "Physical talents of the various horrors of the world." }
 newTalentType{ no_silence=true, is_spell=true, type="spell/horror", name = "horror spells", hide = true, description = "Spell talents of the various horrors of the world." }
+newTalentType{ no_silence=true, is_spell=true, type="corruption/horror", name = "horror spells", hide = true, description = "Spell talents of the various horrors of the world." }
 newTalentType{ type="other/horror", name = "horror powers", hide = true, description = "Unclassified talents of the various horrors of the world." }
 
 local oldTalent = newTalent
 local newTalent = function(t) if type(t.hide) == "nil" then t.hide = true end return oldTalent(t) end
+-- Bloated Horror Powers
+-- Ideas; Mind Shriek (confusion, plus mental damge each turn), Hallucination (Clones that die on hit if the target makes a mental save)
 
+-- Devourer Powers
+newTalent{
+	name = "Frenzied Bite",
+	type = {"technique/horror", 3},
+	points = 5,
+	cooldown = 12,
+	stamina = 24,
+	tactical = { ATTACK = 1, DISABLE = 2 },
+	message = "In a frenzy @Source@ bites at @Target@!",
+	on_pre_use = function(self, t, silent) if not self:hasEffect(self.EFF_FRENZY) then return false end return true end,
+	action = function(self, t)
+		local tg = {type="hit", range=self:getTalentRange(t)}
+		local x, y, target = self:getTarget(tg)
+		if not x or not y or not target then return nil end
+		if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end
+
+		local hit = self:attackTarget(target, nil, self:combatTalentWeaponDamage(t, 1, 1.7), true)
+		if hit then
+			if target:checkHit(self:combatAttackStr(), target:combatPhysicalResist(), 0, 95, 5) and target:canBe("cut") then
+				target:setEffect(target.EFF_DEEP_WOUND, 5, {src=self, heal_factor=self:getTalentLevel(t) * 10, power=self:combatTalentWeaponDamage(t, 1.5, 3)/5})
+			end
+		end
+		return true
+	end,
+	info = function(self, t)
+		return ([[A nasty bite that causes a deep wound, only usable in a frenzy.]])
+	end,
+}
+
+newTalent{
+	name = "Frenzied Leap", -- modified ghoulish leap, only usable while in a frenzy
+	type = {"technique/horror", 1},
+	points = 5,
+	cooldown = 5,
+	tactical = { CLOSEIN = 3 },
+	direct_hit = true,
+	message = "@Source@ leaps forward in a frenzy!",
+	range = function(self, t) return math.floor(2 + self:getTalentLevel(t)) end,
+	requires_target = true,
+	on_pre_use = function(self, t, silent) if not self:hasEffect(self.EFF_FRENZY) or self:attr("encased_in_ice") or self:attr("never_move") then return false end return true end,
+	action = function(self, t)
+		local tg = {type="hit", range=self:getTalentRange(t)}
+		local x, y, target = self:getTarget(tg)
+		if not x or not y then return nil end
+		if math.floor(core.fov.distance(self.x, self.y, x, y)) > self:getTalentRange(t) then return nil end
+
+		local l = line.new(self.x, self.y, x, y)
+		local lx, ly = l()
+		local tx, ty = lx, ly
+		lx, ly = l()
+		while lx and ly do
+			if game.level.map:checkEntity(lx, ly, Map.TERRAIN, "block_move", self) then break end
+			tx, ty = lx, ly
+			lx, ly = l()
+		end
+
+		-- Find space
+		if game.level.map:checkEntity(tx, ty, Map.TERRAIN, "block_move", self) then return nil end
+		local fx, fy = util.findFreeGrid(tx, ty, 5, true, {[Map.ACTOR]=true})
+		if not fx then
+			return
+		end
+		self:move(fx, fy, true)
+
+		return true
+	end,
+	info = function(self, t)
+		return ([[Leap toward your target.]])
+	end,
+}
+
+newTalent{
+	name = "Gnashing Teeth",
+	type = {"technique/horror", 1},
+	points = 5,
+	cooldown = 3,
+	stamina = 8,
+	message = "@Source@ tries to bite @Target@ with razor sharp teeth!",
+	requires_target = true,
+	tactical = { ATTACK = 2 },
+	do_devourer_frenzy = function(self, target, t)
+		game.logSeen(self, "The scent of blood sends the %ss into a frenzy!", self.name:capitalize())
+		-- frenzy devourerers
+		local tg = {type="ball", range=0, radius=3, selffire=true, talent=t}
+		self:project(tg, target.x, target.y, function(px, py)
+			local target = game.level.map(px, py, engine.Map.ACTOR)
+			local reapplied = false
+			if target then
+				local actor_frenzy = false
+				if target.name == "devourer" then
+					actor_frenzy = true
+				end
+				if actor_frenzy then
+					-- silence the apply message if the target already has the effect
+					for eff_id, p in pairs(target.tmp) do
+						local e = target.tempeffect_def[eff_id]
+						if e.name == "Frenzy" then
+							reapplied = true
+						end
+					end
+					target:setEffect(target.EFF_FRENZY, math.floor(2 + self:getTalentLevel(t)), {crit = self:getTalentLevel(t) * 3, power=self:getTalentLevel(t) * 0.2, dieat=self:getTalentLevel(t) * 0.2}, reapplied)
+				end
+			end
+		end)
+	end,		
+	action = function(self, t)
+		local tg = {type="hit", range=self:getTalentRange(t)}
+		local x, y, target = self:getTarget(tg)
+		if not x or not y or not target then return nil end
+		if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end
+		local hit = self:attackTarget(target, nil, self:combatTalentWeaponDamage(t, 0.5, 1), true)
+
+		if target:checkHit(self:combatAttackStr(), target:combatPhysicalResist(), 0, 95, 8 - self:getTalentLevel(t) / 2) and target:canBe("cut") then
+			target:setEffect(target.EFF_CUT, 5, {power=self:combatTalentWeaponDamage(t, 0.8, 1.5), src=self})
+			t.do_devourer_frenzy(self, target, t)
+		else
+			game.logSeen(target, "%s resists the cut!", target.name:capitalize())
+		end
+
+		return true
+	end,
+	info = function(self, t)
+		return ([[Bites the target, potentially causing it to bleed.  The blood will send the devourer into a frenzy (which in turn will frenzy nearby devourers).]])
+	end,
+}
 
 -- Nightmare Horror Powers
 newTalent{
@@ -45,7 +173,7 @@ newTalent{
 		if not x then
 			return
 		end
-
+		
 		local m = target:clone{
 			shader = "shadow_simulacrum",
 			no_drops = true,
@@ -63,7 +191,7 @@ newTalent{
 
 		mod.class.NPC.castAs(m)
 		engine.interface.ActorAI.init(m, m)
-
+		
 		m.energy.value = 0
 		m.player = nil
 		m.max_life = m.max_life / 2
@@ -75,7 +203,7 @@ newTalent{
 		m.seen_by = nil
 		m.can_talk = nil
 		m.clone_on_hit = nil
-
+			
 		-- Remove some talents
 		local tids = {}
 		for tid, _ in pairs(m.talents) do
@@ -86,10 +214,10 @@ newTalent{
 			if t.mode == "sustained" and m:isTalentActive(t.id) then m:forceUseTalent(t.id, {ignore_energy=true}) end
 			m.talents[t.id] = nil
 		end
-
+		
 		-- nil the Inner Demons effect to squelch combat log spam
 		m.tmp[m.EFF_INNER_DEMONS] = nil
-
+		
 		-- remove detrimental timed effects
 		local effs = {}
 		for eff_id, p in pairs(m.tmp) do
@@ -98,7 +226,7 @@ newTalent{
 				effs[#effs+1] = {"effect", eff_id}
 			end
 		end
-
+		
 		while #effs > 0 do
 			local eff = rng.tableRemove(effs)
 			if eff[1] == "effect" then
@@ -106,12 +234,12 @@ newTalent{
 			end
 		end
 
-
+		
 		game.zone:addEntity(game.level, m, "actor", x, y)
 		game.level.map:particleEmitter(x, y, 1, "shadow")
 
 		game.logSeen(target, "%s's Inner Demon manifests!", target.name:capitalize())
-
+		
 	end,
 	action = function(self, t)
 		local tg = {type="hit", range=self:getTalentRange(t), talent=t}
@@ -121,13 +249,13 @@ newTalent{
 		if not x or not y then return nil end
 		local target = game.level.map(x, y, Map.ACTOR)
 		if not target then return nil end
-
+		
 		if target:checkHit(self:combatSpellpower(), target:combatSpellResist(), 0, 95, 15) and target:canBe("fear") then
 			target:setEffect(target.EFF_INNER_DEMONS, t.getDuration(self, t), {src = self, chance=t.getChance(self, t), dam=t.getDamage(self, t)})
 		else
 			game.logSeen(target, "%s resists the demons!", target.name:capitalize())
 		end
-
+		
 		return true
 	end,
 	info = function(self, t)
@@ -156,13 +284,13 @@ newTalent{
 		if not x or not y then return nil end
 		local target = game.level.map(x, y, Map.ACTOR)
 		if not target then return nil end
-
+		
 		if target:checkHit(self:combatSpellpower(), target:combatSpellResist(), 0, 95, 15) and target:canBe("fear") then
 			target:setEffect(target.EFF_WAKING_NIGHTMARE, t.getDuration(self, t), {src = self, chance=t.getChance(self, t), dam=t.getDamage(self, t)})
 		else
 			game.logSeen(target, "%s resists the nightmare!", target.name:capitalize())
 		end
-
+		
 		return true
 	end,
 	info = function(self, t)
@@ -211,7 +339,8 @@ newTalent{
 		return ([[An area of effect that causes darkness damage and reduces the lite radius and darkness resistance of those within.]])
 	end,
 }
-
+-- Temporal Stalker Powers
+-- Ideas; Damage Shift >:)  Give more temporal effects, especially Warden effects, raise AP and add Temporal Damage on hit to mimic weapon folding, give invis rune
 -- Void Horror Powers
 newTalent{
 	name = "Echoes From The Void",
@@ -233,13 +362,13 @@ newTalent{
 		if not x or not y then return nil end
 		local target = game.level.map(x, y, Map.ACTOR)
 		if not target then return nil end
-
-		if target:checkHit(self:combatMindpower(), target:combatMentalResist(), 0, 95, 15) and target:canBe("fear") then
-			target:setEffect(target.EFF_VOID_ECHOES, 6, {src=self, power=t.getDamage(self, t)})
+		
+		if target:checkHit(src:combatMindpower(), target:combatMentalResist(), 0, 95, 15) and target:canBe("fear") then
+			target:setEffect(target.EFF_VOID_ECHOES, 6, {src= self, power=t.getDamage(self, t)})
 		else
 			game.logSeen(target, "%s resists the void!", target.name:capitalize())
 		end
-
+		
 		game:playSoundNear(self, "talents/arcane")
 		return true
 	end,
@@ -269,9 +398,9 @@ newTalent{
 		local _ _, tx, ty = self:canProject(tg, tx, ty)
 		target = game.level.map(tx, ty, Map.ACTOR)
 		if target == self then target = nil end
-
+		
 		if self:getTalentLevel(t) < 5 then self:setEffect(self.EFF_SUMMON_DESTABILIZATION, 500, {power=5}) end
-
+		
 		for i = 1, self:getTalentLevelRaw(t) do
 		-- Find space
 			local x, y = util.findFreeGrid(tx, ty, 5, true, {[Map.ACTOR]=true})
@@ -287,41 +416,41 @@ newTalent{
 				name = "void shard", faction = self.faction,
 				desc = [[It looks like a small hole in the fabric of spacetime.]],
 				stats = { str=22, dex=20, wil=15, con=15 },
-
-				--level_range = {self.level, self.level},
+				
+				--level_range = {self.level, self.level}, 
 				exp_worth = 0,
 				max_life = resolvers.rngavg(5,10),
 				life_rating = 2,
 				rank = 2,
 				size_category = 1,
-
+							
 				autolevel = "summoner",
-				ai = "summoned", ai_real = "dumb_talented_simple", ai_state = { talent_in=2, ai_move="move_snake" },
+				ai = "summoned", ai_real = "dumb_talented_simple", ai_state = { talent_in=2, ai_move="move_snake" },							
 				combat_armor = 1, combat_def = 1,
 				combat = { dam=resolvers.levelup(resolvers.mbonus(40, 15), 1, 1.2), atk=15, apr=15, dammod={wil=0.8}, damtype=DamageType.TEMPORAL },
 				on_melee_hit = { [DamageType.TEMPORAL] = resolvers.mbonus(20, 10), },
-
+			
 				infravision = 10,
 				no_breath = 1,
 				fear_immune = 1,
 				stun_immune = 1,
 				confusion_immune = 1,
 				silence_immune = 1,
-
+				
 				ai_target = {actor=target}
 			}
-
+			
 			m.faction = self.faction
-			m.summoner = self
+			m.summoner = self 
 			m.summoner_gain_exp=true
 			m.summon_time = t.getSummonTime(self, t)
-
+			
 			m:resolve() m:resolve(nil, true)
 			m:forceLevelup(self.level)
 			game.zone:addEntity(game.level, m, "actor", x, y)
 			game.level.map:particleEmitter(x, y, 1, "summon")
 			m:setEffect(m.EFF_TEMPORAL_DESTABILIZATION, 5, {src=self, dam=t.getDamage(self, t), explosion=self:spellCrit(t.getExplosion(self, t))})
-
+			
 		end
 		game:playSoundNear(self, "talents/spell_generic")
 		return true
@@ -329,4 +458,56 @@ newTalent{
 	info = function(self, t)
 		return ([[Summons shards of explosive doom!]])
 	end,
-}
\ No newline at end of file
+}
+
+-- Worm that Walks Powers
+newTalent{
+	name = "Worm Rot",
+	type = {"corruption/horror", 1},
+	points = 5,
+	cooldown = 8,
+	vim = 10,
+	range = 6,
+	requires_target = true,
+	tactical = { ATTACK = 2, DISABLE = 4 },
+	getBurstDamage = function(self, t) return self:combatTalentSpellDamage(t, 30, 300) end,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 5, 50) end,
+	getHealReduction = function(self, t) return self:combatTalentSpellDamage(t, 20, 70) end,
+	getDuration = function(self, t) return 4 + math.ceil(self:getTalentLevel(t)) end,
+	proj_speed = 6,
+	spawn_carrion_worm = function (self, target, t)
+		local x, y = util.findFreeGrid(target.x, target.y, 10, true, {[Map.ACTOR]=true})
+		if not x then
+			return
+		end
+		
+		local worm = {type="vermin", subtype="worms", name="carrion worm mass", number=1, hasxp=false}
+		local m = game.zone:makeEntity(game.level, "actor", worm, nil, true)
+		m:resolve()
+		m.faction = self.faction
+		
+		game.zone:addEntity(game.level, m, "actor", x, y)
+	end,		
+	action = function(self, t)
+		local tg = {type="bolt", range=self:getTalentRange(t), talent=t, display={particle="bolt_slime"}}
+		local x, y = self:getTarget(tg)
+		if not x or not y then return nil end
+		
+		self: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:checkHit(self:combatSpellpower(), target:combatSpellResist(), 0, 95, 12 - self:getTalentLevel(t)) and target:canBe("disease") then
+				target:setEffect(target.EFF_WORM_ROT, 5, {src=self, dam=t.getDamage(self, t), burst=t.getBurstDamage(self, t)})
+			else
+				game.logSeen(target, "%s resists the worm rot!", target.name:capitalize())
+			end
+			game.level.map:particleEmitter(px, py, 1, "slime")
+		end)
+		game:playSoundNear(self, "talents/slime")
+		
+		return true
+	end,
+	info = function(self, t)
+		return ([[A terrible rotting disease that removes a beneficial physical effect and deals acid and blight damage each turn.  If not cleared after a full five turn duration it will inflict extra damage and spawn a carrion worm mass.]])
+	end,
+}
diff --git a/game/modules/tome/data/talents/misc/npcs.lua b/game/modules/tome/data/talents/misc/npcs.lua
index 49f125e157..9724983766 100644
--- a/game/modules/tome/data/talents/misc/npcs.lua
+++ b/game/modules/tome/data/talents/misc/npcs.lua
@@ -1119,7 +1119,7 @@ newTalent{
 	target = function(self, t)
 		return {type="beam", range=self:getTalentRange(t), talent=t}
 	end,
-	tactical = { ATTACK = 3 },
+	tactical = { ATTACK = 2, ESCAPE = 2 },
 	action = function(self, t)
 		local tg = self:getTalentTarget(t)
 		local x, y = self:getTarget(tg)
@@ -1418,7 +1418,7 @@ newTalent{
 	points = 5,
 	cooldown = 6,
 	tactical = {
-		BUFF = 10,
+		BUFF = 4,
 	},
 	direct_hit = true,
 	range = 0,
@@ -1428,9 +1428,30 @@ newTalent{
 	end,
 	action = function(self, t)
 		local tg = self:getTalentTarget(t)
-		local grids = self:project(tg, self.x, self.y, DamageType.DREDGE_FRENZY)
+		self:project(tg, self.x, self.y, function(px, py)
+			local target = game.level.map(px, py, engine.Map.ACTOR)
+			local reapplied = false
+			if target then
+				local actor_frenzy = false
+				if target.dredge then
+					actor_frenzy = true
+				end
+				if actor_frenzy then
+					-- silence the apply message if the target already has the effect
+					for eff_id, p in pairs(target.tmp) do
+						local e = target.tempeffect_def[eff_id]
+						if e.name == "Frenzy" then
+							reapplied = true
+						end
+					end
+					target:setEffect(target.EFF_FRENZY, self:getTalentLevel(t), {crit = self:getTalentLevel(t), power=self:getTalentLevel(t) * 0.1, dieat=self:getTalentLevel(t) * 0.1}, reapplied)
+				end
+			end
+		end)
+		
 		game.level.map:particleEmitter(self.x, self.y, tg.radius, "ball_light", {radius=tg.radius})
 		game:playSoundNear(self, "talents/arcane")
+			
 		return true
 	end,
 	info = function(self, t)
diff --git a/game/modules/tome/data/timed_effects.lua b/game/modules/tome/data/timed_effects.lua
index 05339dd7a6..aaa1a959ac 100644
--- a/game/modules/tome/data/timed_effects.lua
+++ b/game/modules/tome/data/timed_effects.lua
@@ -1506,7 +1506,7 @@ newEffect{
 newEffect{
 	name = "EPIDEMIC",
 	desc = "Epidemic",
-	long_desc = function(self, eff) return ("The target is infected by a disease, doing %0.2f blight damage per turn.\nEach non-disease blight damage done to it will spread the disease."):format(eff.dam) end,
+	long_desc = function(self, eff) return ("The target is infected by a disease, doing %0.2f blight damage per turn and reducig healing received by %d%%.\nEach non-disease blight damage done to it will spread the disease."):format(eff.dam, eff.heal_factor) end,
 	type = "disease",
 	status = "detrimental",
 	parameters = {},
@@ -2744,13 +2744,10 @@ newEffect{
 			end
 		end
 
-		for i = 1, 1 do
-			if #effs == 0 then break end
+		if #effs > 0 then
 			local eff = rng.tableRemove(effs)
-
 			if eff[1] == "effect" then
 				self:removeEffect(eff[2])
-				known = true
 			end
 		end
 	end,
@@ -4531,4 +4528,94 @@ newEffect{
 			end
 		end
 	end,
+}
+
+newEffect{
+	name = "WORM_ROT",
+	desc = "Worm Rot",
+	long_desc = function(self, eff) return ("The target is infected with carrion worm larvae.  Each turn it will lose one beneficial physical effect and %0.2f blight and acid damage will be inflicted.\nAfter five turns the disease will inflict %0.2f blight damage and spawn a carrion worm mass."):format(eff.dam, eff.burst) end,
+	type = "disease",
+	status = "detrimental",
+	parameters = {},
+	on_gain = function(self, err) return "#Target# is afflicted by a terrible worm rot!" end,
+	on_lose = function(self, err) return "#Target# is free from the worm rot." end,
+	-- Damage each turn
+	on_timeout = function(self, eff)
+		self.worm_rot_timer = self.worm_rot_timer - 1
+		
+		-- disease damage
+		if self:attr("purify_disease") then 
+			self:heal(eff.dam)
+		else 
+			DamageType:get(DamageType.BLIGHT).projector(eff.src, self.x, self.y, DamageType.BLIGHT, eff.dam, {from_disease=true})
+		end
+		-- acid damage from the larvae
+		DamageType:get(DamageType.ACID).projector(eff.src, self.x, self.y, DamageType.ACID, eff.dam)
+		
+		local effs = {}
+		-- Go through all physical effects
+		for eff_id, p in pairs(self.tmp) do
+			local e = self.tempeffect_def[eff_id]
+			if e.status == "beneficial" and e.type == "physical" then
+				effs[#effs+1] = {"effect", eff_id}
+			end
+		end
+		-- remove a random physical effect
+		if #effs > 0 then
+			local eff = rng.tableRemove(effs)
+			if eff[1] == "effect" then
+				self:removeEffect(eff[2])
+			end
+		end
+		
+		-- burst and spawn a worm mass
+		if self.worm_rot_timer == 0 then
+			DamageType:get(DamageType.BLIGHT).projector(eff.src, self.x, self.y, DamageType.BLIGHT, eff.burst, {from_disease=true})
+			local t = eff.src:getTalentFromId(eff.src.T_WORM_ROT)
+			t.spawn_carrion_worm(eff.src, self, t)
+			game.logSeen(self, "#LIGHT_RED#A carrion worm mass bursts out of %s!", self.name:capitalize())
+			self:removeEffect(self.EFF_WORM_ROT)
+		end
+	end,
+	activate = function(self, eff)
+		self.worm_rot_timer = 5
+	end,
+	deactivate = function(self, eff)
+		self.worm_rot_timer = nil
+	end,
+}
+
+newEffect{
+	name = "FRENZY",
+	desc = "Frenzy",
+	long_desc = function(self, eff) return ("Increases global action speed by %d%% and physical crit by %d%%.\nAdditionally the target will continue to fight until it's hit points reach -%d%%."):format(eff.power * 100, eff.crit, eff.dieat * 100) end,
+	type = "physical",
+	status = "beneficial",
+	parameters = { power=0.1 },
+	on_gain = function(self, err) return "#Target# goes into a killing frenzy.", "+Frenzy" end,
+	on_lose = function(self, err) return "#Target# calms down.", "-Frenzy" end,
+	on_merge = function(self, old_eff, new_eff)
+		-- use on merge so reapplied frenzy doesn't kill off creatures with negative life
+		old_eff.dur = new_eff.dur
+		old_eff.power = new_eff.power
+		old_eff.crit = new_eff.crit
+		return old_eff
+	end,
+	activate = function(self, eff)
+		eff.tmpid = self:addTemporaryValue("global_speed", eff.power)
+		eff.critid = self:addTemporaryValue("combat_physcrit", eff.crit)
+		eff.dieatid = self:addTemporaryValue("die_at", -self.max_life * eff.dieat)
+	end,
+	deactivate = function(self, eff)
+		-- check negative life first incase the creature has healing
+		if self.life <= 0 then
+			local sx, sy = game.level.map:getTileToScreen(self.x, self.y)
+			game.flyers:add(sx, sy, 30, (rng.range(0,2)-1) * 0.5, rng.float(-2.5, -1.5), "Falls dead!", {255,0,255})
+			game.logSeen(self, "%s dies when it's frenzy ends!", self.name:capitalize())
+			self:die(self)
+		end
+		self:removeTemporaryValue("global_speed", eff.tmpid)
+		self:removeTemporaryValue("combat_physcrit", eff.critid)
+		self:removeTemporaryValue("die_at", eff.dieatid)
+	end,
 }
\ No newline at end of file
-- 
GitLab