From b17716fa5efcd480ee6ae647bf8c9bf684e27726 Mon Sep 17 00:00:00 2001
From: orange <iwasrobots@hotmail.com>
Date: Wed, 27 Mar 2019 00:40:53 +0700
Subject: [PATCH] no message

---
 game/modules/tome/class/Actor.lua             |  65 +++
 game/modules/tome/data/damage_types.lua       |  11 +
 .../tome/data/talents/celestial/eclipse.lua   |  79 +--
 .../data/talents/celestial/glyphs-old.lua     | 427 ++++++++++++++
 .../tome/data/talents/celestial/glyphs.lua    | 528 ++++++++++--------
 .../tome/data/talents/celestial/star-fury.lua |   2 +-
 .../tome/data/talents/celestial/sunlight.lua  |  54 +-
 .../tome/data/timed_effects/magical.lua       |  92 +++
 8 files changed, 934 insertions(+), 324 deletions(-)
 create mode 100644 game/modules/tome/data/talents/celestial/glyphs-old.lua

diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua
index 80a6f1f5a8..1b4299d6aa 100644
--- a/game/modules/tome/class/Actor.lua
+++ b/game/modules/tome/class/Actor.lua
@@ -5275,7 +5275,72 @@ function _M:getVim()
 		return previous_getVim(self)
 	end
 end
+--[[
+--darkest light negative gain
+local previous_incPositive = _M.incPositive
+function _M:incPositive(v)
+	if v > 0 and self:attr("darkest_light_mastery") then
+		local cost = math.abs(v)
+		self:incNegative(cost)
+		return previous_incPositive(self)
+	end
+end
+]]
 
+--darkest light cost switch vim template
+--Overwrite to set up darkest light
+local previous_incNegative = _M.incNegative
+function _M:incNegative(v)
+	if v < 0 and self:attr("darkest_light_mastery") then
+		local cost = math.abs(v)
+		if self.negative - cost < 0 then
+			local poscost = (cost - (self.negative or 0))
+			self:incNegative(-self.negative or 0)
+			self.positive = self.positive - poscost
+		else
+			return previous_incNegative(self, v)
+		end
+	else
+		return previous_incNegative(self, v)
+	end
+end
+
+-- Overwrite getVim to set up darkest light
+local previous_getNegative = _M.getNegative
+function _M:getNegative()
+	if self:attr("darkest_light_mastery") and self.on_preuse_checking_resources then
+		return math.max(self.negative, self.positive)
+	else
+		return previous_getNegative(self)
+	end
+end
+--[[
+
+--darkest light cost switch
+
+local previous_incNegative = _M.incNegative
+function _M:incNegative(v)
+	if v < 0 and self:attr("darkest_light_mastery") then
+		local cost = math.abs(v)
+		if self.negative - cost < 0 then
+			self:incPositive(cost)
+		else
+			return previous_incNegative(self, v)
+		end
+	else
+		return previous_incNegative(self, v)
+	end
+end
+
+local previous_getNegative = _M.getNegative
+function _M:getPositive()
+	if self:attr("darkest_light_mastery") and self.on_preuse_checking_resources then
+		return math.max(self.positive, self.negative)
+	else
+		return previous_getNegative(self)
+	end
+end
+]]
 -- Feedback Pseudo-Resource Functions
 function _M:getFeedback()
 	if self.psionic_feedback then
diff --git a/game/modules/tome/data/damage_types.lua b/game/modules/tome/data/damage_types.lua
index b4e2308b55..7e2a55faf7 100644
--- a/game/modules/tome/data/damage_types.lua
+++ b/game/modules/tome/data/damage_types.lua
@@ -89,6 +89,17 @@ setDefaultProjector(function(src, x, y, type, dam, state)
 		return dam
 	end
 
+	if src:attr("darkest_light_mastery") and type == "LIGHT" then
+		local ndam = dam * src.darkest_light_mastery
+		dam = dam - ndam
+		local old = src.darkest_light_mastery
+		src.darkest_light_mastery = nil
+		add_dam = DamageType:get(DamageType.DARKNESS).projector(src, x, y, DamageType.DARKNESS, ndam, state)
+		src.darkest_light_mastery = old
+		if dam <= 0 then return add_dam end
+	end
+		
+
 	local source_talent = 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 src:getTalentFromId(src.__projecting_for.project_type.talent or src.__projecting_for.project_type.talent_id)
 
 	local terrain = game.level.map(x, y, Map.TERRAIN)
diff --git a/game/modules/tome/data/talents/celestial/eclipse.lua b/game/modules/tome/data/talents/celestial/eclipse.lua
index dd7f3b211e..78905ab43c 100644
--- a/game/modules/tome/data/talents/celestial/eclipse.lua
+++ b/game/modules/tome/data/talents/celestial/eclipse.lua
@@ -136,77 +136,22 @@ newTalent{
 newTalent{
 	name = "Darkest Light",
 	type = {"celestial/eclipse", 4},
-	mode = "sustained",
 	require = divi_req4,
 	points = 5,
-	cooldown = 30,
-	sustain_negative = 10,
-	no_npc_use = true,
-	tactical = { DEFEND = 2, ESCAPE = 2 },
-	getInvisibilityPower = function(self, t) return self:combatScale(self:getCun() * self:getTalentLevel(t), 5, 0, 38.33, 500) end,
-	getEnergyConvert = function(self, t) return math.max(0, 6 - self:getTalentLevelRaw(t)) end,
-	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 10, 100) end,
-	getRadius = function(self, t) return math.floor(self:combatTalentScale(t, 2.5, 4.5)) end,
-	callbackOnActBase = function(self, t)
-		if self.positive > self.negative then
-			self:forceUseTalent(t.id, {ignore_energy=true})
-			game.logSeen(self, "%s's darkness can no longer hold back the light!", self.name:capitalize())
-		end
-	end,
-	activate = function(self, t)
-		local timer = t.getEnergyConvert(self, t)
-		game:playSoundNear(self, "talents/heal")
-		local ret = {
-			invisible = self:addTemporaryValue("invisible", t.getInvisibilityPower(self, t)),
-			invisible_damage_penalty = self:addTemporaryValue("invisible_damage_penalty", 0.5),
-			fill = self:addTemporaryValue("positive_regen_ref", -timer),
-			drain = self:addTemporaryValue("negative_regen_ref", timer),
-			pstop = self:addTemporaryValue("positive_at_rest_disable", 1),
-			nstop = self:addTemporaryValue("negative_at_rest_disable", 1),
-		}
-		if not self.shader then
-			ret.set_shader = true
-			self.shader = "invis_edge"
-			self.shader_args = {color1={1,1,0,1}, color2={0,0,0,1}}
-			self:removeAllMOs()
-			game.level.map:updateMap(self.x, self.y)
-		end
-		ret.particle = self:addParticles(Particles.new("circle", 1, {shader=true, toback=true, oversize=1.7, a=155, appear=8, speed=0, img="darkest_light", radius=0}))
-		self:resetCanSeeCacheOf()
-		return ret
-	end,
-	deactivate = function(self, t, p)
-		if p.set_shader then
-			self.shader = nil
-			self:removeAllMOs()
-			game.level.map:updateMap(self.x, self.y)
-		end
-		self:removeTemporaryValue("invisible", p.invisible)
-		self:removeTemporaryValue("invisible_damage_penalty", p.invisible_damage_penalty)
-		self:removeTemporaryValue("positive_regen_ref", p.fill)
-		self:removeTemporaryValue("negative_regen_ref", p.drain)
-		self:removeTemporaryValue("positive_at_rest_disable", p.pstop)
-		self:removeTemporaryValue("negative_at_rest_disable", p.nstop)
-		self:removeParticles(p.particle)
-		local tg = {type="ball", range=0, selffire=true, radius= t.getRadius(self, t), talent=t}
-		self:project(tg, self.x, self.y, DamageType.LITE, 1)
-		tg.selffire = false
-		local grids = self:project(tg, self.x, self.y, DamageType.LIGHT, self:spellCrit(t.getDamage(self, t) + self.positive))
-		game.level.map:particleEmitter(self.x, self.y, tg.radius, "sunburst", {radius=tg.radius, grids=grids, tx=self.x, ty=self.y, max_alpha=80})
+	cooldown = 25,
+	tactical = { BUFF = 2 },
+	positive = 15,
+	negative = -15,
+	getDuration = function(self, t) return self:combatTalentSpellDamage(t, 5, 12) end,
+	getConversion = function(self, t) return math.min(1, self:combatTalentScale(t, 0.15, 1)) end,
+	action = function(self, t)
+		self:setEffect(self.EFF_DARKEST_LIGHT, t.getDuration(self, t), {conversion=t.getConversion(self, t)})
 		game:playSoundNear(self, "talents/flame")
-		self.positive = 0
-		self:resetCanSeeCacheOf()
 		return true
 	end,
 	info = function(self, t)
-		local invisibilitypower = t.getInvisibilityPower(self, t)
-		local convert = t.getEnergyConvert(self, t)
-		local damage = t.getDamage(self, t)
-		local radius = t.getRadius(self, t)
-		return ([[This powerful spell grants you %d bonus invisibility, but converts %d negative energy into positive energy each turn.  Once your positive energy exceeds your negative energy, or you deactivate the talent, the effect ends in an explosion of light, converting all of your positive energy into damage and inflicting an additional %0.2f damage to everything in a radius of %d.
-		As you become invisible, you fade out of phase with reality; all your damage is reduced by 50%%.
-		You may not cast Twilight while this spell is active, and you should take off your light source; otherwise, others will spot you with ease.
-		The invisibility bonus will increase with your Cunning, and the explosion damage will increase with your Spellpower.]]):
-		format(invisibilitypower, convert, damDesc(self, DamageType.LIGHT, damage), radius)
+		return ([[Call upon the power of the moon to darken the sun's light.
+		For %d turns, %d%% of your light damage is converted to darkness damage and you may use positive in place of negative to cast spells if you don't have enough negative.]]):
+		format(t.getDuration(self, t), t.getConversion(self, t) * 100)
 	end,
-}
+}
\ No newline at end of file
diff --git a/game/modules/tome/data/talents/celestial/glyphs-old.lua b/game/modules/tome/data/talents/celestial/glyphs-old.lua
new file mode 100644
index 0000000000..1749818c00
--- /dev/null
+++ b/game/modules/tome/data/talents/celestial/glyphs-old.lua
@@ -0,0 +1,427 @@
+
+
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009 - 2018 Nicolas Casalini
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+--
+-- Nicolas Casalini "DarkGod"
+-- darkgod@te4.org
+
+local Trap = require "mod.class.Trap"
+
+newTalent{
+	name = "Glyph of Explosion",
+	type = {"celestial/glyphs", 3},
+	require = divi_req_high3,
+	random_ego = "attack",
+	points = 5,
+	cooldown = 20,
+	positive = -10,
+	no_energy = true,
+	tactical = { ATTACKAREA = {LIGHT = 2} },
+--	requires_target = true,
+	range = function(self, t) return math.floor(self:combatTalentScale(t, 1, 5, "log")) end,
+	radius = function() return 1 end,
+	direct_hit = true,
+	tactical = { ATTACKAREA = { ACID = 2 }, DISABLE = { knockback = 1 } },
+	target = function(self, t) return {type="ball", radius=self:getTalentRadius(t), range=self:getTalentRange(t), talent=t} end,
+	getDamage = function(self, t) return 15 + self:combatSpellpower(0.15) * self:combatTalentScale(t, 1.5, 5) end,
+	getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 6, 10)) end, -- Duration of glyph
+	trapPower = function(self,t) return math.max(1,self:combatScale(self:getTalentLevel(t) * self:getMag(15, true), 0, 0, 75, 75)) end,
+	getNb = function(self, t) return 9 end,
+	action = function(self, t)
+		local tg = self:getTalentTarget(t)
+		local x, y, target = self:getTarget(tg)
+		if not x or not y then return nil end
+		local _ _, x, y = self:canProject(tg, x, y)
+		if game.level.map(x, y, Map.TRAP) then game.logPlayer(self, "You somehow fail to set the corrosive seed.") return nil end
+		
+		local grids = {}
+		self:project(tg, x, y, function(px, py)
+			if not ((px == x and py == y) or game.level.map:checkEntity(px, py, Map.TERRAIN, "block_move") or game.level.map(px, py, Map.TRAP)) then grids[#grids+1] = {x=px, y=py} end
+		end)
+		local dam = self:spellCrit(t.getDamage(self, t))
+		for i = 1, t.getNb(self, t) do
+			local spot = i == 1 and {x=x, y=y} or rng.tableRemove(grids)
+			if not spot then break end
+			local trap = Trap.new{
+				name = "glyph of explosion",
+				type = "elemental", id_by_type=true, unided_name = "trap",
+				display = '^', color=colors.GOLD, image = "trap/trap_glyph_explosion_02_64.png",
+				faction = self.faction,
+				dam = dam,
+				desc = function(self)
+					return ([[Explodes (radius 1) for %d light damage.]]):format(engine.interface.ActorTalents.damDesc(self, engine.DamageType.LIGHT, self.dam))
+				end,
+				canTrigger = function(self, x, y, who)
+					if who:reactionToward(self.summoner) < 0 then return mod.class.Trap.canTrigger(self, x, y, who) end
+					return false
+				end,
+				triggered = function(self, x, y, who)
+					self:project({type="ball", x=x,y=y, radius=1}, x, y, engine.DamageType.LIGHT, self.dam, {type="light"})
+					game.level.map:particleEmitter(x, y, 1, "sunburst", {radius=1, x=x, y=y})
+					return true
+				end,
+				temporary = t.getDuration(self, t),
+				x = tx, y = ty,
+				disarm_power = math.floor(t.trapPower(self,t) * 0.8),
+				detect_power = math.floor(t.trapPower(self,t) * 0.8),
+				inc_damage = table.clone(self.inc_damage or {}, true),
+				resists_pen = table.clone(self.resists_pen or {}, true),
+				canAct = false,
+				energy = {value=0},
+				act = function(self)
+					self:useEnergy()
+					self.temporary = self.temporary - 1
+					if self.temporary <= 0 then
+						if game.level.map(self.x, self.y, engine.Map.TRAP) == self then game.level.map:remove(self.x, self.y, engine.Map.TRAP) end
+						game.level:removeEntity(self)
+					end
+				end,
+				summoner = self,
+				summoner_gain_exp = true,
+			}
+			trap:identify(true)
+			trap:resolve() trap:resolve(nil, true)
+			trap:setKnown(self, true)
+			game.level:addEntity(trap)
+			game.zone:addEntity(game.level, trap, "trap", spot.x, spot.y)
+			game.level.map:particleEmitter(spot.x, spot.y, 1, "summon")
+		end
+
+		return true
+	end,
+	info = function(self, t)
+		return ([[test glyph]]):
+		format()
+	end,
+}
+
+
+newTalent{
+	name = "Glyph of Paralysis",
+	type = {"celestial/glyphs", 1},
+	require = divi_req_high1,
+	random_ego = "attack",
+	points = 5,
+	cooldown = 20,
+	positive = -10,
+	no_energy = true,
+--	requires_target = true,
+	tactical = { DISABLE = {stun = 1.5} },
+	range = function(self, t) return math.floor(self:combatTalentScale(t, 1, 5, "log")) end,
+	getDazeDuration = function(self, t) return math.floor(self:combatTalentScale(t, 4, 8)) end,
+	getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 6, 10)) end, -- Duration of glyph
+	trapPower = function(self,t) return math.max(1,self:combatScale(self:getTalentLevel(t) * self:getMag(15, true), 0, 0, 75, 75)) end, -- Used to determine detection and disarm power, about 75 at level 50
+	target = function(self, t) return {type="hit", nowarning=true, range=self:getTalentRange(t), nolock=true, talent=t} end,
+	action = function(self, t)
+		local tg = self:getTalentTarget(t)
+		local tx, ty = self:trapGetGrid(t, tg)
+		if not (tx and ty) then return end
+		
+		local dam = self:spellCrit(t.getDazeDuration(self, t))
+		local trap = Trap.new{
+			name = "glyph of paralysis",
+			type = "elemental", id_by_type=true, unided_name = "trap",
+			display = '^', color=colors.GOLD, image = "trap/trap_glyph_paralysis_01_64.png",
+			faction = self.faction,
+			desc = function(self)
+				return ([[Dazes for %d turns.]]):format(self.dam)
+			end,
+			dam = dam,
+			canTrigger = function(self, x, y, who)
+				if who:reactionToward(self.summoner) < 0 then return mod.class.Trap.canTrigger(self, x, y, who) end
+				return false
+			end,
+			triggered = function(self, x, y, who)
+				if who:canBe("stun") then
+					who:setEffect(who.EFF_DAZED, self.dam, {})
+				end
+				return true
+			end,
+			temporary = t.getDuration(self, t),
+			x = tx, y = ty,
+			disarm_power = math.floor(t.trapPower(self,t)),
+			detect_power = math.floor(t.trapPower(self,t) * 0.8),
+			canAct = false,
+			energy = {value=0},
+			act = function(self)
+				self:useEnergy()
+				self.temporary = self.temporary - 1
+				if self.temporary <= 0 then
+					if game.level.map(self.x, self.y, engine.Map.TRAP) == self then game.level.map:remove(self.x, self.y, engine.Map.TRAP) end
+					game.level:removeEntity(self)
+				end
+			end,
+			summoner = self,
+			summoner_gain_exp = true,
+		}
+		game.level:addEntity(trap)
+		trap:identify(true)
+		trap:setKnown(self, true)
+		game.zone:addEntity(game.level, trap, "trap", tx, ty)
+		game:playSoundNear(self, "talents/heal")
+		return true
+	end,
+	info = function(self, t)
+		local dazeduration = t.getDazeDuration(self, t)
+		local duration = t.getDuration(self, t)
+		return ([[You bind light in a glyph on the floor. All enemies walking over the glyph will be dazed for %d turns.
+		The glyph is a hidden trap (%d detection and %d disarm power based on your Magic) and lasts for %d turns.]]):format(dazeduration, t.trapPower(self,t)*0.8, t.trapPower(self,t), duration)
+	end,
+}
+
+newTalent{
+	name = "Glyph of Repulsion",
+	type = {"celestial/glyphs", 2},
+	require = divi_req_high2,
+	random_ego = "attack",
+	points = 5,
+	positive = -10,
+	cooldown = 20,
+	no_energy = true,
+	tactical = { ATTACK = {PHYSICAL = 1}, CLOSEIN = {knockback = 1.5}, ESCAPE = {knockback = 1.5} },
+--	requires_target = true,
+	range = function(self, t) return math.floor(self:combatTalentScale(t, 1, 5, "log")) end,
+	getDamage = function(self, t) return 15 + self:combatSpellpower(0.12) * self:combatTalentScale(t, 1.5, 5) end,
+	getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 6, 10)) end, -- Duration of glyph
+	trapPower = function(self,t) return math.max(1,self:combatScale(self:getTalentLevel(t) * self:getMag(15, true), 0, 0, 75, 75)) end, -- Used to determine detection and disarm power, about 75 at level 50
+	target = function(self, t) return {type="hit", nowarning=true, range=self:getTalentRange(t), nolock=true, talent=t} end,
+	action = function(self, t)
+		local tg = self:getTalentTarget(t)
+		local tx, ty = self:trapGetGrid(t, tg)
+		if not (tx and ty) then return end
+
+		local dam = self:spellCrit(t.getDamage(self, t))
+		local sp = self:combatSpellpower()
+		local trap = Trap.new{
+			name = "glyph of repulsion",
+			type = "elemental", id_by_type=true, unided_name = "trap",
+			display = '^', color=colors.GOLD, image = "trap/trap_glyph_repulsion_01_64.png",
+			faction = self.faction,
+			dam = dam,
+			desc = function(self)
+				return ([[Deals %d physical damage, knocking the target back.]]):format(engine.interface.ActorTalents.damDesc(self, engine.DamageType.PHYSICAL, self.dam))
+			end,
+			canTrigger = function(self, x, y, who)
+				if who:reactionToward(self.summoner) < 0 then return mod.class.Trap.canTrigger(self, x, y, who) end
+				return false
+			end,
+			triggered = function(self, x, y, who)
+				local ox, oy = self.x, self.y
+				local dir = util.getDir(who.x, who.y, who.old_x, who.old_y)
+				self.x, self.y = util.coordAddDir(self.x, self.y, dir)
+				self:project({type="hit",x=x,y=y}, x, y, engine.DamageType.SPELLKNOCKBACK, self.dam)
+				self.x, self.y = ox, oy
+				return true
+			end,
+			temporary = t.getDuration(self, t),
+			x = tx, y = ty,
+			disarm_power = math.floor(t.trapPower(self,t)),
+			detect_power = math.floor(t.trapPower(self,t) * 0.8),
+			inc_damage = table.clone(self.inc_damage or {}, true),
+			resists_pen = table.clone(self.resists_pen or {}, true),
+			canAct = false,
+			energy = {value=0},
+			combatSpellpower = function(self) return self.sp end, sp = sp,
+			act = function(self)
+				self:useEnergy()
+				self.temporary = self.temporary - 1
+				if self.temporary <= 0 then
+					if game.level.map(self.x, self.y, engine.Map.TRAP) == self then game.level.map:remove(self.x, self.y, engine.Map.TRAP) end
+					game.level:removeEntity(self)
+				end
+			end,
+			summoner = self,
+			summoner_gain_exp = true,
+		}
+		game.level:addEntity(trap)
+		trap:identify(true)
+		trap:setKnown(self, true)
+		game.zone:addEntity(game.level, trap, "trap", tx, ty)
+		game:playSoundNear(self, "talents/heal")
+		return true
+	end,
+	info = function(self, t)
+		local damage = t.getDamage(self, t)
+		local duration = t.getDuration(self, t)
+		return ([[You bind light in a glyph on the floor. All enemies walking over the glyph will be hit by a blast that does %0.2f physical damage and knocks them back.
+		The glyph is a hidden trap (%d detection and %d disarm power based on your Magic) and lasts for %d turns.
+		The damage will increase with your Spellpower.]]):
+		format(damDesc(self, DamageType.PHYSICAL, damage), t.trapPower(self, t)*0.8, t.trapPower(self, t), duration)
+	end,
+}
+--[[
+newTalent{
+	name = "Glyph of Explosion",
+	type = {"celestial/glyphs", 3},
+	require = divi_req_high3,
+	random_ego = "attack",
+	points = 5,
+	cooldown = 20,
+	positive = -10,
+	no_energy = true,
+	tactical = { ATTACKAREA = {LIGHT = 2} },
+--	requires_target = true,
+	range = function(self, t) return math.floor(self:combatTalentScale(t, 1, 5, "log")) end,
+	getDamage = function(self, t) return 15 + self:combatSpellpower(0.15) * self:combatTalentScale(t, 1.5, 5) end,
+	getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 6, 10)) end, -- Duration of glyph
+	trapPower = function(self,t) return math.max(1,self:combatScale(self:getTalentLevel(t) * self:getMag(15, true), 0, 0, 75, 75)) end, -- Used to determine detection and disarm power, about 75 at level 50
+	target = function(self, t) return {type="ball", radius=1, nowarning=true, range=self:getTalentRange(t), nolock=true, talent=t} end,
+	action = function(self, t)
+		local tg = self:getTalentTarget(t)
+		local x, y, target = self:getTarget(tg)
+		if not (x and y) then return end
+		local _ _, x, y = self:canProject(tg, x, y)
+		local dam = self:spellCrit(t.getDamage(self, t))
+		
+		local grids = {}
+		self:project(tg, x, y, function(px, py)
+			if not ((px == x and py == y) or game.level.map:checkEntity(px, py, Map.TERRAIN, "block_move") or game.level.map(px, py, Map.TRAP)) then grids[#grids+1] = {x=px, y=py} end
+		end)
+		local glyphgrids = math.abs(#grids)
+		for i = 1, 9 do
+			local spot = i == 1 and {x=x, y=y} or rng.tableRemove(grids)
+			if not spot then break end
+		
+		local trap = Trap.new{
+			name = "glyph of explosion",
+			type = "elemental", id_by_type=true, unided_name = "trap",
+			display = '^', color=colors.GOLD, image = "trap/trap_glyph_explosion_02_64.png",
+			faction = self.faction,
+			dam = dam,
+			desc = function(self)
+				return ([[Explodes (radius 1) for %d light damage.]]
+--[[			):format(engine.interface.ActorTalents.damDesc(self, engine.DamageType.LIGHT, self.dam))
+			end,
+			canTrigger = function(self, x, y, who)
+				if who:reactionToward(self.summoner) < 0 then return mod.class.Trap.canTrigger(self, x, y, who) end
+				return false
+			end,
+			triggered = function(self, x, y, who)
+				self:project({type="ball", x=x,y=y, radius=1}, x, y, engine.DamageType.LIGHT, self.dam, {type="light"})
+				game.level.map:particleEmitter(x, y, 1, "sunburst", {radius=1, x=x, y=y})
+				return true
+			end,
+			temporary = t.getDuration(self, t),
+			x = tx, y = ty,
+			disarm_power = math.floor(t.trapPower(self,t) * 0.8),
+			detect_power = math.floor(t.trapPower(self,t) * 0.8),
+			inc_damage = table.clone(self.inc_damage or {}, true),
+			resists_pen = table.clone(self.resists_pen or {}, true),
+			canAct = false,
+			energy = {value=0},
+			act = function(self)
+				self:useEnergy()
+				self.temporary = self.temporary - 1
+				if self.temporary <= 0 then
+					if game.level.map(self.x, self.y, engine.Map.TRAP) == self then game.level.map:remove(self.x, self.y, engine.Map.TRAP) end
+					game.level:removeEntity(self)
+				end
+			end,
+			summoner = self,
+			summoner_gain_exp = true,
+		}
+		game.level:addEntity(trap)
+		trap:identify(true)
+		trap:setKnown(self, true)
+		game.zone:addEntity(game.level, trap, "trap", spot.x, spot.y)
+		game:playSoundNear(self, "talents/heal")
+		return true end
+	end,
+	info = function(self, t)
+		local damage = t.getDamage(self, t)
+		local duration = t.getDuration(self, t)
+		return ([[You bind light in a glyph on the floor. All enemies walking over the glyph will trigger an explosion of light that does %0.2f damage to everyone within 1 tile.
+		The glyph is a hidden trap (%d detection and %d disarm power based on your Magic) and lasts for %d turns.
+		The damage will increase with your Spellpower.]]
+--[[	):
+		format(damDesc(self, DamageType.LIGHT, damage), t.trapPower(self, t)*0.8, t.trapPower(self, t)*0.8, duration)
+	end,
+}
+]]
+
+newTalent{
+	name = "Glyph of Fatigue",
+	type = {"celestial/glyphs", 4},
+	require = divi_req_high4,
+	random_ego = "attack",
+	points = 5,
+	cooldown = 20,
+	positive = -10,
+	no_energy = true,
+	tactical = { DISABLE = {slow = 1.5} },
+--	requires_target = true,
+	range = function(self, t) return math.floor(self:combatTalentScale(t, 1, 5, "log")) end,
+	getSlow = function(self, t) return self:combatTalentLimit(t, 1, 0.20, 0.35) end, -- Limit <100% slow
+	getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 6, 10)) end, -- Duration of glyph
+	trapPower = function(self,t) return math.max(1,self:combatScale(self:getTalentLevel(t) * self:getMag(15, true), 0, 0, 75, 75)) end, -- Used to determine detection and disarm power, about 75 at level 50
+	target = function(self, t) return {type="hit", nowarning=true, range=self:getTalentRange(t), nolock=true, talent=t} end,
+	action = function(self, t)
+		local tg = self:getTalentTarget(t)
+		local tx, ty = self:trapGetGrid(t, tg)
+		if not (tx and ty) then return end
+
+		local dam = t.getSlow(self, t)
+		local trap = Trap.new{
+			name = "glyph of fatigue",
+			type = "elemental", id_by_type=true, unided_name = "trap",
+			display = '^', color=colors.GOLD, image = "trap/trap_glyph_fatigue_01_64.png",
+			faction = self.faction,
+			dam = dam,
+			desc = function(self)
+				return ([[Slows (%d%%) for 5 turns.]]):format(self.dam*100)
+			end,
+			canTrigger = function(self, x, y, who)
+				if who:reactionToward(self.summoner) < 0 then return mod.class.Trap.canTrigger(self, x, y, who) end
+				return false
+			end,
+			triggered = function(self, x, y, who)
+				who:setEffect(who.EFF_SLOW, 5, {power=self.dam})
+				return true
+			end,
+			temporary = t.getDuration(self, t),
+			x = tx, y = ty,
+			disarm_power = math.floor(t.trapPower(self,t)),
+			detect_power = math.floor(t.trapPower(self,t)),
+			canAct = false,
+			energy = {value=0},
+			act = function(self)
+				self:useEnergy()
+				self.temporary = self.temporary - 1
+				if self.temporary <= 0 then
+					if game.level.map(self.x, self.y, engine.Map.TRAP) == self then game.level.map:remove(self.x, self.y, engine.Map.TRAP) end
+					game.level:removeEntity(self)
+				end
+			end,
+			summoner = self,
+			summoner_gain_exp = true,
+		}
+		game.level:addEntity(trap)
+		trap:identify(true)
+		trap:setKnown(self, true)
+		game.zone:addEntity(game.level, trap, "trap", tx, ty)
+		game:playSoundNear(self, "talents/heal")
+		return true
+	end,
+	info = function(self, t)
+		local slow = t.getSlow(self, t)
+		local duration = t.getDuration(self, t)
+		return ([[You bind light in a glyph on the floor. All enemies walking over the glyph will be slowed by %d%% for 5 turns.
+		The glyph is a hidden trap (%d detection and %d disarm power based on your Magic) and lasts for %d turns.]]):
+		format(100 * slow, t.trapPower(self, t), t.trapPower(self, t), duration)
+	end,
+}
+
diff --git a/game/modules/tome/data/talents/celestial/glyphs.lua b/game/modules/tome/data/talents/celestial/glyphs.lua
index b3d0329979..ca4d289d24 100644
--- a/game/modules/tome/data/talents/celestial/glyphs.lua
+++ b/game/modules/tome/data/talents/celestial/glyphs.lua
@@ -1,24 +1,7 @@
--- ToME - Tales of Maj'Eyal
--- Copyright (C) 2009 - 2018 Nicolas Casalini
---
--- This program is free software: you can redistribute it and/or modify
--- it under the terms of the GNU General Public License as published by
--- the Free Software Foundation, either version 3 of the License, or
--- (at your option) any later version.
---
--- This program is distributed in the hope that it will be useful,
--- but WITHOUT ANY WARRANTY; without even the implied warranty of
--- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--- GNU General Public License for more details.
---
--- You should have received a copy of the GNU General Public License
--- along with this program.  If not, see <http://www.gnu.org/licenses/>.
---
--- Nicolas Casalini "DarkGod"
--- darkgod@te4.org
-
 local Trap = require "mod.class.Trap"
 
+
+
 newTalent{
 	name = "Glyph of Paralysis",
 	type = {"celestial/glyphs", 1},
@@ -28,151 +11,193 @@ newTalent{
 	cooldown = 20,
 	positive = -10,
 	no_energy = true,
+	tactical = { ATTACKAREA = {LIGHT = 2} },
 --	requires_target = true,
-	tactical = { DISABLE = {stun = 1.5} },
 	range = function(self, t) return math.floor(self:combatTalentScale(t, 1, 5, "log")) end,
-	getDazeDuration = function(self, t) return math.floor(self:combatTalentScale(t, 4, 8)) end,
+	radius = function() return 1 end,
+	direct_hit = true,
+	target = function(self, t) return {type="ball", radius=self:getTalentRadius(t), range=self:getTalentRange(t), talent=t} end,
+	getDamage = function(self, t) return 15 + self:combatSpellpower(0.15) * self:combatTalentScale(t, 1.5, 5) end,
 	getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 6, 10)) end, -- Duration of glyph
-	trapPower = function(self,t) return math.max(1,self:combatScale(self:getTalentLevel(t) * self:getMag(15, true), 0, 0, 75, 75)) end, -- Used to determine detection and disarm power, about 75 at level 50
-	target = function(self, t) return {type="hit", nowarning=true, range=self:getTalentRange(t), nolock=true, talent=t} end,
+	trapPower = function(self,t) return math.max(1,self:combatScale(self:getTalentLevel(t) * self:getMag(15, true), 0, 0, 75, 75)) end,
+	getNb = function(self, t) return 9 end,
 	action = function(self, t)
 		local tg = self:getTalentTarget(t)
-		local tx, ty = self:trapGetGrid(t, tg)
-		if not (tx and ty) then return end
+		local x, y, target = self:getTarget(tg)
+		if not x or not y then return nil end
+		local _ _, x, y = self:canProject(tg, x, y)
+		if game.level.map(x, y, Map.TRAP) then game.logPlayer(self, "You somehow fail to set the corrosive seed.") return nil end
 		
-		local dam = self:spellCrit(t.getDazeDuration(self, t))
-		local trap = Trap.new{
-			name = "glyph of paralysis",
-			type = "elemental", id_by_type=true, unided_name = "trap",
-			display = '^', color=colors.GOLD, image = "trap/trap_glyph_paralysis_01_64.png",
-			faction = self.faction,
-			desc = function(self)
-				return ([[Dazes for %d turns.]]):format(self.dam)
-			end,
-			dam = dam,
-			canTrigger = function(self, x, y, who)
-				if who:reactionToward(self.summoner) < 0 then return mod.class.Trap.canTrigger(self, x, y, who) end
-				return false
-			end,
-			triggered = function(self, x, y, who)
-				if who:canBe("stun") then
-					who:setEffect(who.EFF_DAZED, self.dam, {})
-				end
-				return true
-			end,
-			temporary = t.getDuration(self, t),
-			x = tx, y = ty,
-			disarm_power = math.floor(t.trapPower(self,t)),
-			detect_power = math.floor(t.trapPower(self,t) * 0.8),
-			canAct = false,
-			energy = {value=0},
-			act = function(self)
-				self:useEnergy()
-				self.temporary = self.temporary - 1
-				if self.temporary <= 0 then
-					if game.level.map(self.x, self.y, engine.Map.TRAP) == self then game.level.map:remove(self.x, self.y, engine.Map.TRAP) end
-					game.level:removeEntity(self)
-				end
-			end,
-			summoner = self,
-			summoner_gain_exp = true,
-		}
-		game.level:addEntity(trap)
-		trap:identify(true)
-		trap:setKnown(self, true)
-		game.zone:addEntity(game.level, trap, "trap", tx, ty)
-		game:playSoundNear(self, "talents/heal")
+		local grids = {}
+		self:project(tg, x, y, function(px, py)
+			if not ((px == x and py == y) or game.level.map:checkEntity(px, py, Map.TERRAIN, "block_move") or game.level.map(px, py, Map.TRAP)) then grids[#grids+1] = {x=px, y=py} end
+		end)
+		local dam = self:spellCrit(t.getDamage(self, t))
+		local slowPower = t.getSlow(self, t)
+		for i = 1, t.getNb(self, t) do
+			local spot = i == 1 and {x=x, y=y} or rng.tableRemove(grids)
+			if not spot then break end
+			local trap = Trap.new{
+				name = "glyph of paralysis",
+				type = "elemental", id_by_type=true, unided_name = "trap",
+				display = '^', color=colors.GOLD, image = "trap/trap_glyph_explosion_02_64.png",
+				faction = self.faction,
+				dam = dam,
+				pinDur = pinDur,
+				desc = function(self)
+					return ([[Deals %d light damage and pins for %d turns.]]):format(engine.interface.ActorTalents.damDesc(self, engine.DamageType.LIGHT, self.dam), self.pinDur)
+				end,
+				canTrigger = function(self, x, y, who)
+					if who:reactionToward(self.summoner) < 0 then return mod.class.Trap.canTrigger(self, x, y, who) end
+					return false
+				end,
+				triggered = function(self, x, y, who)
+					who:setEffect(who.EFF_PPIN, self.pinDur)
+					self:project({type="hit",x=x,y=y}, x, y, engine.DamageType.LIGHT, self.dam)
+					return true
+				end,
+				temporary = t.getDuration(self, t),
+				x = tx, y = ty,
+				disarm_power = math.floor(t.trapPower(self,t)),
+				detect_power = math.floor(t.trapPower(self,t)),
+				canAct = false,
+				energy = {value=0},
+				act = function(self)
+					self:useEnergy()
+					self.temporary = self.temporary - 1
+					if self.temporary <= 0 then
+						if game.level.map(self.x, self.y, engine.Map.TRAP) == self then game.level.map:remove(self.x, self.y, engine.Map.TRAP) end
+						game.level:removeEntity(self)
+					end
+				end,
+				summoner = self,
+				summoner_gain_exp = true,
+			}
+			trap:identify(true)
+			trap:resolve() trap:resolve(nil, true)
+			trap:setKnown(self, true)
+			game.level:addEntity(trap)
+			game.zone:addEntity(game.level, trap, "trap", spot.x, spot.y)
+			game.level.map:particleEmitter(spot.x, spot.y, 1, "summon")
+		end
+
 		return true
 	end,
 	info = function(self, t)
-		local dazeduration = t.getDazeDuration(self, t)
+		local pinPin = t.getPin(self, t)
+		local dam = t.getDamage(self, t)
 		local duration = t.getDuration(self, t)
-		return ([[You bind light in a glyph on the floor. All enemies walking over the glyph will be dazed for %d turns.
-		The glyph is a hidden trap (%d detection and %d disarm power based on your Magic) and lasts for %d turns.]]):format(dazeduration, t.trapPower(self,t)*0.8, t.trapPower(self,t), duration)
+		return ([[Bind light in glyphs on the floor. All enemies walking over the glyphs will struck by a paralysing light, dealing %d light damage and pinning them for %d turns.
+		
+		The glyph is a hidden trap (%d detection and %d disarm power) and lasts for 6, 7, 9, 10, 11 turns.
+		
+		The damage will increase with Spellpower. The detection and disarm power increases with Magic.]]):
+		format(slow, damDesc(self, DamageType.LIGHT, damage), t.trapPower(self, t), t.trapPower(self, t), duration)
 	end,
 }
 
+
+
+
+
 newTalent{
-	name = "Glyph of Repulsion",
+	name = "Glyph of Fatigue",
 	type = {"celestial/glyphs", 2},
 	require = divi_req_high2,
 	random_ego = "attack",
 	points = 5,
-	positive = -10,
 	cooldown = 20,
+	positive = -10,
 	no_energy = true,
-	tactical = { ATTACK = {PHYSICAL = 1}, CLOSEIN = {knockback = 1.5}, ESCAPE = {knockback = 1.5} },
+	tactical = { ATTACKAREA = {DARK = 2} },
 --	requires_target = true,
 	range = function(self, t) return math.floor(self:combatTalentScale(t, 1, 5, "log")) end,
-	getDamage = function(self, t) return 15 + self:combatSpellpower(0.12) * self:combatTalentScale(t, 1.5, 5) end,
+	radius = function() return 1 end,
+	direct_hit = true,
+	target = function(self, t) return {type="ball", radius=self:getTalentRadius(t), range=self:getTalentRange(t), talent=t} end,
+	getDamage = function(self, t) return 15 + self:combatSpellpower(0.15) * self:combatTalentScale(t, 1.5, 5) end,
+	getSlow = function(self, t) return self:combatTalentLimit(t, 1, 0.20, 0.35) end,
 	getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 6, 10)) end, -- Duration of glyph
-	trapPower = function(self,t) return math.max(1,self:combatScale(self:getTalentLevel(t) * self:getMag(15, true), 0, 0, 75, 75)) end, -- Used to determine detection and disarm power, about 75 at level 50
-	target = function(self, t) return {type="hit", nowarning=true, range=self:getTalentRange(t), nolock=true, talent=t} end,
+	trapPower = function(self,t) return math.max(1,self:combatScale(self:getTalentLevel(t) * self:getMag(15, true), 0, 0, 75, 75)) end,
+	getNb = function(self, t) return 9 end,
 	action = function(self, t)
 		local tg = self:getTalentTarget(t)
-		local tx, ty = self:trapGetGrid(t, tg)
-		if not (tx and ty) then return end
-
+		local x, y, target = self:getTarget(tg)
+		if not x or not y then return nil end
+		local _ _, x, y = self:canProject(tg, x, y)
+		if game.level.map(x, y, Map.TRAP) then game.logPlayer(self, "You somehow fail to set the corrosive seed.") return nil end
+		
+		local grids = {}
+		self:project(tg, x, y, function(px, py)
+			if not ((px == x and py == y) or game.level.map:checkEntity(px, py, Map.TERRAIN, "block_move") or game.level.map(px, py, Map.TRAP)) then grids[#grids+1] = {x=px, y=py} end
+		end)
 		local dam = self:spellCrit(t.getDamage(self, t))
-		local sp = self:combatSpellpower()
-		local trap = Trap.new{
-			name = "glyph of repulsion",
-			type = "elemental", id_by_type=true, unided_name = "trap",
-			display = '^', color=colors.GOLD, image = "trap/trap_glyph_repulsion_01_64.png",
-			faction = self.faction,
-			dam = dam,
-			desc = function(self)
-				return ([[Deals %d physical damage, knocking the target back.]]):format(engine.interface.ActorTalents.damDesc(self, engine.DamageType.PHYSICAL, self.dam))
-			end,
-			canTrigger = function(self, x, y, who)
-				if who:reactionToward(self.summoner) < 0 then return mod.class.Trap.canTrigger(self, x, y, who) end
-				return false
-			end,
-			triggered = function(self, x, y, who)
-				local ox, oy = self.x, self.y
-				local dir = util.getDir(who.x, who.y, who.old_x, who.old_y)
-				self.x, self.y = util.coordAddDir(self.x, self.y, dir)
-				self:project({type="hit",x=x,y=y}, x, y, engine.DamageType.SPELLKNOCKBACK, self.dam)
-				self.x, self.y = ox, oy
-				return true
-			end,
-			temporary = t.getDuration(self, t),
-			x = tx, y = ty,
-			disarm_power = math.floor(t.trapPower(self,t)),
-			detect_power = math.floor(t.trapPower(self,t) * 0.8),
-			inc_damage = table.clone(self.inc_damage or {}, true),
-			resists_pen = table.clone(self.resists_pen or {}, true),
-			canAct = false,
-			energy = {value=0},
-			combatSpellpower = function(self) return self.sp end, sp = sp,
-			act = function(self)
-				self:useEnergy()
-				self.temporary = self.temporary - 1
-				if self.temporary <= 0 then
-					if game.level.map(self.x, self.y, engine.Map.TRAP) == self then game.level.map:remove(self.x, self.y, engine.Map.TRAP) end
-					game.level:removeEntity(self)
-				end
-			end,
-			summoner = self,
-			summoner_gain_exp = true,
-		}
-		game.level:addEntity(trap)
-		trap:identify(true)
-		trap:setKnown(self, true)
-		game.zone:addEntity(game.level, trap, "trap", tx, ty)
-		game:playSoundNear(self, "talents/heal")
+		local slowPower = t.getSlow(self, t)
+		for i = 1, t.getNb(self, t) do
+			local spot = i == 1 and {x=x, y=y} or rng.tableRemove(grids)
+			if not spot then break end
+			local trap = Trap.new{
+				name = "glyph of fatigue",
+				type = "elemental", id_by_type=true, unided_name = "trap",
+				display = '^', color=colors.GOLD, image = "trap/trap_glyph_fatigue_01_64.png",
+				faction = self.faction,
+				dam = dam,
+				slowPower = slowPower,
+				desc = function(self)
+					return ([[Slows (%d%%) for 5 turns.]]):format(self.slowPower*100, engine.interface.ActorTalents.damDesc(self, engine.DamageType.DARKNESS, self.dam))
+				end,
+				canTrigger = function(self, x, y, who)
+					if who:reactionToward(self.summoner) < 0 then return mod.class.Trap.canTrigger(self, x, y, who) end
+					return false
+				end,
+				triggered = function(self, x, y, who)
+					who:setEffect(who.EFF_CELESTIAL_FATIGUE, 5, {power=self.slowPower})
+					return true
+				end,
+				temporary = t.getDuration(self, t),
+				x = tx, y = ty,
+				disarm_power = math.floor(t.trapPower(self,t)),
+				detect_power = math.floor(t.trapPower(self,t)),
+				canAct = false,
+				energy = {value=0},
+				act = function(self)
+					self:useEnergy()
+					self.temporary = self.temporary - 1
+					if self.temporary <= 0 then
+						if game.level.map(self.x, self.y, engine.Map.TRAP) == self then game.level.map:remove(self.x, self.y, engine.Map.TRAP) end
+						game.level:removeEntity(self)
+					end
+				end,
+				summoner = self,
+				summoner_gain_exp = true,
+			}
+			trap:identify(true)
+			trap:resolve() trap:resolve(nil, true)
+			trap:setKnown(self, true)
+			game.level:addEntity(trap)
+			game.zone:addEntity(game.level, trap, "trap", spot.x, spot.y)
+			game.level.map:particleEmitter(spot.x, spot.y, 1, "summon")
+		end
+
 		return true
 	end,
 	info = function(self, t)
-		local damage = t.getDamage(self, t)
+		local slow = t.getSlow(self, t)
+		local dam = t.getDamage(self, t)
 		local duration = t.getDuration(self, t)
-		return ([[You bind light in a glyph on the floor. All enemies walking over the glyph will be hit by a blast that does %0.2f physical damage and knocks them back.
-		The glyph is a hidden trap (%d detection and %d disarm power based on your Magic) and lasts for %d turns.
-		The damage will increase with your Spellpower.]]):
-		format(damDesc(self, DamageType.PHYSICAL, damage), t.trapPower(self, t)*0.8, t.trapPower(self, t), duration)
+		return ([[Bind darkness in glyphs on the floor. All enemies walking over the glyphs will afflicted with a fatiguing darkness for 5 turns, slowing (%d%%) them and dealing %d darkness damage when they use a talent.
+		
+		The glyph is a hidden trap (%d detection and %d disarm power) and lasts for 6, 7, 9, 10, 11 turns.
+		
+		The damage will increase with Spellpower. The detection and disarm power increases with Magic.]]):
+		format(slow, damDesc(self, DamageType.DARKNESS, damage), t.trapPower(self, t), t.trapPower(self, t), duration)
 	end,
 }
 
+
+
+
+
 newTalent{
 	name = "Glyph of Explosion",
 	type = {"celestial/glyphs", 3},
@@ -185,138 +210,151 @@ newTalent{
 	tactical = { ATTACKAREA = {LIGHT = 2} },
 --	requires_target = true,
 	range = function(self, t) return math.floor(self:combatTalentScale(t, 1, 5, "log")) end,
+	radius = function() return 1 end,
+	direct_hit = true,
+	target = function(self, t) return {type="ball", radius=self:getTalentRadius(t), range=self:getTalentRange(t), talent=t} end,
 	getDamage = function(self, t) return 15 + self:combatSpellpower(0.15) * self:combatTalentScale(t, 1.5, 5) end,
 	getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 6, 10)) end, -- Duration of glyph
-	trapPower = function(self,t) return math.max(1,self:combatScale(self:getTalentLevel(t) * self:getMag(15, true), 0, 0, 75, 75)) end, -- Used to determine detection and disarm power, about 75 at level 50
-	target = function(self, t) return {type="ball", radius=1, nowarning=true, range=self:getTalentRange(t), nolock=true, talent=t} end,
+	trapPower = function(self,t) return math.max(1,self:combatScale(self:getTalentLevel(t) * self:getMag(15, true), 0, 0, 75, 75)) end,
+	getNb = function(self, t) return 9 end,
 	action = function(self, t)
 		local tg = self:getTalentTarget(t)
-		local tx, ty = self:trapGetGrid(t, tg)
-		if not (tx and ty) then return end
-
+		local x, y, target = self:getTarget(tg)
+		if not x or not y then return nil end
+		local _ _, x, y = self:canProject(tg, x, y)
+		if game.level.map(x, y, Map.TRAP) then game.logPlayer(self, "You somehow fail to set the corrosive seed.") return nil end
+		
+		local grids = {}
+		self:project(tg, x, y, function(px, py)
+			if not ((px == x and py == y) or game.level.map:checkEntity(px, py, Map.TERRAIN, "block_move") or game.level.map(px, py, Map.TRAP)) then grids[#grids+1] = {x=px, y=py} end
+		end)
 		local dam = self:spellCrit(t.getDamage(self, t))
-		local trap = Trap.new{
-			name = "glyph of explosion",
-			type = "elemental", id_by_type=true, unided_name = "trap",
-			display = '^', color=colors.GOLD, image = "trap/trap_glyph_explosion_02_64.png",
-			faction = self.faction,
-			dam = dam,
-			desc = function(self)
-				return ([[Explodes (radius 1) for %d light damage.]]):format(engine.interface.ActorTalents.damDesc(self, engine.DamageType.LIGHT, self.dam))
-			end,
-			canTrigger = function(self, x, y, who)
-				if who:reactionToward(self.summoner) < 0 then return mod.class.Trap.canTrigger(self, x, y, who) end
-				return false
-			end,
-			triggered = function(self, x, y, who)
-				self:project({type="ball", x=x,y=y, radius=1}, x, y, engine.DamageType.LIGHT, self.dam, {type="light"})
-				game.level.map:particleEmitter(x, y, 1, "sunburst", {radius=1, tx=x, ty=y})
-				return true
-			end,
-			temporary = t.getDuration(self, t),
-			x = tx, y = ty,
-			disarm_power = math.floor(t.trapPower(self,t) * 0.8),
-			detect_power = math.floor(t.trapPower(self,t) * 0.8),
-			inc_damage = table.clone(self.inc_damage or {}, true),
-			resists_pen = table.clone(self.resists_pen or {}, true),
-			canAct = false,
-			energy = {value=0},
-			act = function(self)
-				self:useEnergy()
-				self.temporary = self.temporary - 1
-				if self.temporary <= 0 then
-					if game.level.map(self.x, self.y, engine.Map.TRAP) == self then game.level.map:remove(self.x, self.y, engine.Map.TRAP) end
-					game.level:removeEntity(self)
-				end
-			end,
-			summoner = self,
-			summoner_gain_exp = true,
-		}
-		game.level:addEntity(trap)
-		trap:identify(true)
-		trap:setKnown(self, true)
-		game.zone:addEntity(game.level, trap, "trap", tx, ty)
-		game:playSoundNear(self, "talents/heal")
+		for i = 1, t.getNb(self, t) do
+			local spot = i == 1 and {x=x, y=y} or rng.tableRemove(grids)
+			if not spot then break end
+			local trap = Trap.new{
+				name = "glyph of explosion",
+				type = "elemental", id_by_type=true, unided_name = "trap",
+				display = '^', color=colors.GOLD, image = "trap/trap_glyph_explosion_02_64.png",
+				faction = self.faction,
+				dam = dam,
+				desc = function(self)
+					return ([[Explodes (radius 1), knocking back and dealing %d light and %d darkness damage.]]):format(engine.interface.ActorTalents.damDesc(self, engine.DamageType.LIGHT, self.dam), engine.interface.ActorTalents.damDesc(self, engine.DamageType.DARKNESS, self.dam))
+				end,
+				canTrigger = function(self, x, y, who)
+					if who:reactionToward(self.summoner) < 0 then return mod.class.Trap.canTrigger(self, x, y, who) end
+					return false
+				end,
+				triggered = function(self, x, y, who)
+					self:project({type="ball", x=x,y=y, radius=1}, x, y, engine.DamageType.LIGHT, self.dam, {type="light"})
+					game.level.map:particleEmitter(x, y, 1, "sunburst", {radius=1, x=x, y=y})
+					self:project({type="ball", x=x,y=y, radius=1}, x, y, engine.DamageType.DARKKNOCKBACK, self.dam, {type="light"})
+					game.level.map:particleEmitter(x, y, 1, "shadow_flash", {radius=1, x=x, y=y})
+					
+					if self.summoner:knowTalent(self.summoner.T_DIVINE_GLYPHS) then
+						local dg = self.summoner:getTalentFromId(self.summoner.T_DIVINE_GLYPHS)
+						local maxStacks = dg.getMaxStacks(self.summoner, dg)
+						--self.summoner:setEffect(self.summoner.EFF_DIVINE_GLYPHS, 6, {maxStacks=maxStacks})
+					end
+						
+					return true
+				end,
+				temporary = t.getDuration(self, t),
+				x = tx, y = ty,
+				disarm_power = math.floor(t.trapPower(self,t) * 0.8),
+				detect_power = math.floor(t.trapPower(self,t) * 0.8),
+				inc_damage = table.clone(self.inc_damage or {}, true),
+				resists_pen = table.clone(self.resists_pen or {}, true),
+				canAct = false,
+				energy = {value=0},
+				act = function(self)
+					self:useEnergy()
+					self.temporary = self.temporary - 1
+					if self.temporary <= 0 then
+						if game.level.map(self.x, self.y, engine.Map.TRAP) == self then game.level.map:remove(self.x, self.y, engine.Map.TRAP) end
+						game.level:removeEntity(self)
+					end
+				end,
+				summoner = self,
+				summoner_gain_exp = true,
+			}
+			trap:identify(true)
+			trap:resolve() trap:resolve(nil, true)
+			trap:setKnown(self, true)
+			game.level:addEntity(trap)
+			game.zone:addEntity(game.level, trap, "trap", spot.x, spot.y)
+			game.level.map:particleEmitter(spot.x, spot.y, 1, "summon")
+		end
+
+		if self:knowTalent(self.T_DIVINE_GLYPHS) then
+			local dg = self:getTalentFromId(self.T_DIVINE_GLYPHS)
+			local dam = dam * dg.getGlyphPower(self, dg)
+			local maxStacks = dg.getMaxStacks(self, dg)
+			
+			self:project({type="ball", x=x,y=y, radius=1}, x, y, engine.DamageType.LIGHT, dam, {type="light"})
+			game.level.map:particleEmitter(x, y, 1, "sunburst", {radius=1, x=x, y=y})
+			self:project({type="ball", x=x,y=y, radius=1}, x, y, engine.DamageType.DARKKNOCKBACK, dam, {type="light"})
+			game.level.map:particleEmitter(x, y, 1, "shadow_flash", {radius=1, x=x, y=y})
+			
+			self:setEffect(self.EFF_DIVINE_GLYPHS, 6, {maxStacks=maxStacks, explosionStacks=1})
+		end
+
 		return true
 	end,
 	info = function(self, t)
 		local damage = t.getDamage(self, t)
 		local duration = t.getDuration(self, t)
-		return ([[You bind light in a glyph on the floor. All enemies walking over the glyph will trigger an explosion of light that does %0.2f damage to everyone within 1 tile.
-		The glyph is a hidden trap (%d detection and %d disarm power based on your Magic) and lasts for %d turns.
-		The damage will increase with your Spellpower.]]):
-		format(damDesc(self, DamageType.LIGHT, damage), t.trapPower(self, t)*0.8, t.trapPower(self, t)*0.8, duration)
+		return ([[Bind light and darkness in glyphs on the floor. All enemies walking over the glyphs will trigger a twilight explosion that knocks back and deals %d light and %d darkness damage.
+		
+		The glyph is a hidden trap (%d detection and %d disarm power) and lasts for 6, 7, 9, 10, 11 turns.
+		
+		The damage will increase with Spellpower. The detection and disarm power increases with Magic.]]):
+		format(damDesc(self, DamageType.LIGHT, damage), damDesc(self, DamageType.DARKNESS, damage), t.trapPower(self, t), t.trapPower(self, t), duration)
 	end,
 }
 
+
+
+
+
 newTalent{
-	name = "Glyph of Fatigue",
+	name = "Divine Glyphs",
 	type = {"celestial/glyphs", 4},
 	require = divi_req_high4,
 	random_ego = "attack",
 	points = 5,
-	cooldown = 20,
-	positive = -10,
-	no_energy = true,
-	tactical = { DISABLE = {slow = 1.5} },
---	requires_target = true,
-	range = function(self, t) return math.floor(self:combatTalentScale(t, 1, 5, "log")) end,
-	getSlow = function(self, t) return self:combatTalentLimit(t, 1, 0.20, 0.35) end, -- Limit <100% slow
-	getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 6, 10)) end, -- Duration of glyph
-	trapPower = function(self,t) return math.max(1,self:combatScale(self:getTalentLevel(t) * self:getMag(15, true), 0, 0, 75, 75)) end, -- Used to determine detection and disarm power, about 75 at level 50
-	target = function(self, t) return {type="hit", nowarning=true, range=self:getTalentRange(t), nolock=true, talent=t} end,
-	action = function(self, t)
-		local tg = self:getTalentTarget(t)
-		local tx, ty = self:trapGetGrid(t, tg)
-		if not (tx and ty) then return end
-
-		local dam = t.getSlow(self, t)
-		local trap = Trap.new{
-			name = "glyph of fatigue",
-			type = "elemental", id_by_type=true, unided_name = "trap",
-			display = '^', color=colors.GOLD, image = "trap/trap_glyph_fatigue_01_64.png",
-			faction = self.faction,
-			dam = dam,
-			desc = function(self)
-				return ([[Slows (%d%%) for 5 turns.]]):format(self.dam*100)
-			end,
-			canTrigger = function(self, x, y, who)
-				if who:reactionToward(self.summoner) < 0 then return mod.class.Trap.canTrigger(self, x, y, who) end
-				return false
-			end,
-			triggered = function(self, x, y, who)
-				who:setEffect(who.EFF_SLOW, 5, {power=self.dam})
-				return true
-			end,
-			temporary = t.getDuration(self, t),
-			x = tx, y = ty,
-			disarm_power = math.floor(t.trapPower(self,t)),
-			detect_power = math.floor(t.trapPower(self,t)),
-			canAct = false,
-			energy = {value=0},
-			act = function(self)
-				self:useEnergy()
-				self.temporary = self.temporary - 1
-				if self.temporary <= 0 then
-					if game.level.map(self.x, self.y, engine.Map.TRAP) == self then game.level.map:remove(self.x, self.y, engine.Map.TRAP) end
-					game.level:removeEntity(self)
-				end
-			end,
-			summoner = self,
-			summoner_gain_exp = true,
-		}
-		game.level:addEntity(trap)
-		trap:identify(true)
-		trap:setKnown(self, true)
-		game.zone:addEntity(game.level, trap, "trap", tx, ty)
-		game:playSoundNear(self, "talents/heal")
-		return true
-	end,
+	mode = "passive",
+	getGlyphPower = function(self, t) return self:combatTalentScale(t, 0.6, 0.1, 0.5) end,
+	getMaxStacks = function(self, t) return self:combatTalentScale(t, 3, 6) end,
+	getTurns = function(self, t) return 6 - self:getTalentLevelRaw() end,
+	
+	
 	info = function(self, t)
-		local slow = t.getSlow(self, t)
-		local duration = t.getDuration(self, t)
-		return ([[You bind light in a glyph on the floor. All enemies walking over the glyph will be slowed by %d%% for 5 turns.
-		The glyph is a hidden trap (%d detection and %d disarm power based on your Magic) and lasts for %d turns.]]):
-		format(100 * slow, t.trapPower(self, t), t.trapPower(self, t), duration)
+		return ([[When you bind a glyph you will also trigger its effect %d%% power (this only happens once every %d turns).
+		Additionally, every time a glyph triggers you will gain a beneficial effect, stacking up to %d times.
+		Glyph of Paralysis: +4% to light resistance and affinity.
+		Glyph of Fatigue: +4% to darkness resistance and affinity.
+		Glyph of Explosion: +4% to light and darkness damage.]]):
+		format(t.getGlyphPower(self, t), t.getTurns(self, t), t.getMaxStacks(self, t))
 	end,
 }
+
+--[[
+		--check for talent and turn limit
+		if self:knowTalent(self.T_DIVINE_GLYPHS) and not self.turn_procs.divine_glyphs then
+			local dg = self:getTalentFromId(self.T_DIVINE_GLYPHS)
+			local dam = dam * dg.getGlyphPower(self, dg)
+			local maxStacks = dg.getMaxStacks(self, dg)
+			local glyphTurns = dg.getTurns(self, dg)
+			
+			--do the glyph effect
+			self:project({type="ball", x=x,y=y, radius=1}, x, y, engine.DamageType.LIGHT, dam, {type="light"})
+			game.level.map:particleEmitter(x, y, 1, "sunburst", {radius=1, x=x, y=y})
+			self:project({type="ball", x=x,y=y, radius=1}, x, y, engine.DamageType.DARKKNOCKBACK, dam, {type="light"})
+			game.level.map:particleEmitter(x, y, 1, "shadow_flash", {radius=1, x=x, y=y})
+			--set self buff manually
+			self:setEffect(self.EFF_DIVINE_GLYPHS, 6, {maxStacks=maxStacks})
+			--cooldown
+			self.turn_procs.divine_glyphs = glyphTurns
+		end
+]]
\ No newline at end of file
diff --git a/game/modules/tome/data/talents/celestial/star-fury.lua b/game/modules/tome/data/talents/celestial/star-fury.lua
index cb54f18c77..bd1a345bcf 100644
--- a/game/modules/tome/data/talents/celestial/star-fury.lua
+++ b/game/modules/tome/data/talents/celestial/star-fury.lua
@@ -113,7 +113,7 @@ newTalent{
 	positive = -10,
 	tactical = { ATTACKAREA = {LIGHT = 1, DARKNESS = 1} },
 	range = 0,
-	radius = 2,
+	radius = 4,
 	direct_hit = true,
 	target = function(self, t)
 		return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), talent=t, selffire=false}
diff --git a/game/modules/tome/data/talents/celestial/sunlight.lua b/game/modules/tome/data/talents/celestial/sunlight.lua
index ca98cf47c7..d3101a8208 100644
--- a/game/modules/tome/data/talents/celestial/sunlight.lua
+++ b/game/modules/tome/data/talents/celestial/sunlight.lua
@@ -129,7 +129,9 @@ newTalent{
 		local tg = self:getTalentTarget(t)
 		local x, y = self:getTarget(tg)
 		if not x or not y then return nil end
-		self:project(tg, x, y, DamageType.FIRE, self:spellCrit(t.getDamage(self, t)))
+		local dam = self:spellCrit(t.getDamage(self, t))
+		self:project(tg, x, y, DamageType.LIGHT, dam)
+		self:project(tg, x, y, DamageType.FIREBURN, dam)
 		local _ _, x, y = self:canProject(tg, x, y)
 		game.level.map:particleEmitter(self.x, self.y, math.max(math.abs(x-self.x), math.abs(y-self.y)), "light_beam", {tx=x-self.x, ty=y-self.y})
 
@@ -151,28 +153,58 @@ newTalent{
 	points = 5,
 	random_ego = "attack",
 	cooldown = 15,
-	positive = -20,
+	positive = 15,
 	tactical = { ATTACKAREA = {LIGHT = 2} },
 	range = 0,
-	radius = 3,
+	radius = 6,
 	direct_hit = true,
 	target = function(self, t)
 		return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), friendlyfire=false, talent=t}
 	end,
-	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 10, 160) end,
+	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 10, 200) end,
+	getDotDamage = function(self, t) return self:combatTalentSpellDamage(t, 10, 50) end,
+	getConversion = function(self, t) return math.min(100, self:combatTalentScale(t, 15, 100)) end,
+	
+	getDuration = function(self, t) return 6 end,
+	
 	action = function(self, t)
 		local tg = self:getTalentTarget(t)
-		local grids = self:project(tg, self.x, self.y, DamageType.LIGHT, self:spellCrit(t.getDamage(self, t)))
-
-		game.level.map:particleEmitter(self.x, self.y, tg.radius, "sunburst", {radius=tg.radius, grids=grids, tx=self.x, ty=self.y})
-
+		self:project(tg, self.x, self.y, function(px, py)
+			local target = game.level.map(px, py, Map.ACTOR)
+			if not target then return end
+			
+			DamageType:get(DamageType.LIGHT).projector(self, px, py, DamageType.LIGHT, self:spellCrit(t.getDamage(self, t)))
+			
+			if not target:hasEffect(target.EFF_SUNBURST) then target:setEffect(target.EFF_SUNBURST, t.getDuration(self, t), {src=self, dotDam=t.getDotDamage(self, t), conversion=t.getConversion(self, t)}) end
+		end)
+		
+		if self:hasEffect(self.EFF_DARKEST_LIGHT) then
+			game.level.map:particleEmitter(self.x, self.y, tg.radius, "shadow_flash", {radius=tg.radius, grids=grids, tx=self.x, ty=self.y})
+		else
+			game.level.map:particleEmitter(self.x, self.y, tg.radius, "sunburst", {radius=tg.radius, grids=grids, tx=self.x, ty=self.y})
+		end
 		game:playSoundNear(self, "talents/fireflash")
 		return true
 	end,
+
 	info = function(self, t)
-		local radius = self:getTalentRadius(t)
 		local damage = t.getDamage(self, t)
-		return ([[Conjures a furious burst of Sunlight, dealing %0.2f light damage to all around you in a radius of %d.
-		The damage done will increase with your Spellpower.]]):format(damDesc(self, DamageType.LIGHT, damage), radius)
+		local radius = self:getTalentRadius(t)
+		local dotDamage = t.getDotDamage(self, t)
+		local conversion = t.getConversion(self, t)
+		local duration = t.getDuration(self, t)
+		if self:hasEffect(self.EFF_DARKEST_LIGHT) then
+			sbDescTT = "starlight"
+			sbDamTypeTT = "#GREY#Darkness#LAST#"
+			sbDamTypeForm = DARKNESS
+		else
+			sbDescTT = "sunlight"
+			sbDamTypeTT = "#YELLOW#Light#LAST#"
+			sbDamTypeForm = LIGHT
+		end
+		return ([[Conjures a furious burst of %s, dealing %d %s damage to all within radius %d.
+		Those affected will be afflicted with a searing %s, dealing %d %s damage every turn and converting %d%% of all their damage to %s for %d turns.
+		If darkest light is active, the damage and conversion will be darkness.
+		The damage will increase with your Spellpower.]]):format(sbDescTT, damDesc(self, DamageType.sbDamTypeTT, damage), sbDamTypeTT, radius, sbDescTT, dotDamage, sbDamTypeTT, conversion, sbDamTypeTT, duration)
 	end,
 }
diff --git a/game/modules/tome/data/timed_effects/magical.lua b/game/modules/tome/data/timed_effects/magical.lua
index a4ff1e6a4b..b306755233 100644
--- a/game/modules/tome/data/timed_effects/magical.lua
+++ b/game/modules/tome/data/timed_effects/magical.lua
@@ -1387,6 +1387,98 @@ newEffect{
 	end,
 }
 
+newEffect{
+	name = "DARKEST_LIGHT", image = "talents/darkest_light.png",
+	desc = "Darkest Light",
+	long_desc = function(self, eff) return ("%d%% of the target's light damage is being converted to darkness damage and they may suplement positive for negative."):format(eff.conversion * 100) end,
+	type = "magical",
+	subtype = { darkness=true, light=true},
+	status = "beneficial",
+	parameters = {},
+	activate = function(self, eff)
+		self:effectTemporaryValue(eff, "darkest_light_mastery", eff.conversion)
+		if core.shader.active() then
+			eff.particle1 = self:addParticles(Particles.new("shader_ring_rotating", 1, {rotation=0, radius=0.8, img="runicshield_yellow"}, {type="lightningshield", time_factor=3000, noup=1.0}))
+			eff.particle1.toback = true
+			eff.particle2 = self:addParticles(Particles.new("shader_ring_rotating", 1, {rotation=0, radius=0.8, img="runicshield_dark"}, {type="lightningshield", time_factor=3000, noup=1.0}))
+		end
+	end,
+	deactivate = function(self, eff)
+		self:removeParticles(eff.particle1)
+		self:removeParticles(eff.particle2)
+	end,
+}
+
+newEffect{
+	name = "DIVINE_GLYPHS", image = "talents/glyph_of_repulsion.png",
+	desc = "Divine Glyphs",
+	long_desc = function(self, eff)
+		pGlyph = math.min(eff.maxStacks, eff.paraStacks or 0)*4
+		fGlyph = math.min(eff.maxStacks, eff.fatigueStacks or 0)*4
+		eGlyph = math.min(eff.maxStacks, eff.explosionStacks or 0)*4
+		if pGlyph > 0 then paralysis = (" +%d%% light resistance and affinitity."):format(pGlyph) end else paralysis = "" end
+		if fGlyph > 0 then fatigue = (" +%d%% darkness resistance and affinitity."):format(fGlyph) end else fatigue = "" end
+		if eGlyph > 0 then explosion = (" +%d%% light and darkness damage."):format(eGlyph) end else explosion = "" end
+		return ("A divine glyph recently triggered.%s%s%s"):format(paralysis, fatigue, explosion)
+	end,
+	type = "magical",
+	subtype = {light=true, darkness=true},
+	status = "detrimental",
+	paramters ={},
+	activate = function(self, eff)
+		local pGlyph = math.min(eff.maxStacks, eff.paraStacks or 0)*4
+		local fGlyph = math.min(eff.maxStacks, eff.fatigueStacks or 0)*4
+		local eGlyph = math.min(eff.maxStacks, eff.explosionStacks or 0)*4
+		self:effectTemporaryValue(eff, "damage_affinity", {[DamageType.LIGHT]=pGlyph, [DamageType.DARKNESS]=fGlyph})
+		self:effectTemporaryValue(eff, "resists", {[DamageType.LIGHT]=pGlyph, [DamageType.DARKNESS]=fGlyph})
+		self:effectTemporaryValue(eff, "inc_damage", {[DamageType.LIGHT]=eGlyph, [DamageType.DARKNESS]=eGlyph})
+	end,
+	on_merge = function(self, eff)
+		old_eff.paraStacks = old_eff.paraStacks + new_eff.paraStacks
+		old_eff.fatigueStacks = old_eff.fatigueStacks + new_eff.fatigueStacks
+		old_eff.explosionStacks = old_eff.explosionStacks + new_eff.explosionStacks
+		old_eff.dur = new_eff.dur
+		return old_eff
+	end,
+}
+
+newEffect{
+	name = "SUNBURST", image = "talents/sunburst.png",
+	desc = "Sunburst",
+	long_desc = function(self, eff)
+		if eff.src:hasEffect(eff.src.EFF_DARKEST_LIGHT) then darklight = "darkness" else darklight = "light" end
+		return ("The target is taking %d %s damage every turn and has %d%% of their damage converted to %s."):format(eff.dotDam, darklight, eff.conversion, darklight) end,
+	type = "magical",
+	subtype = {light=true, darkness=true},
+	status = "detrimental",
+	parameters = {},
+	activate = function(self, eff)
+		if eff.src:hasEffect(eff.src.EFF_DARKEST_LIGHT) then
+			self:effectTemporaryValue(eff, "all_damage_convert", DamageType.DARKNESS)
+			self:effectTemporaryValue(eff, "all_damage_convert_percent", eff.conversion)
+			if core.shader.active() then
+				eff.particle = self:addParticles(Particles.new("shader_ring_rotating", 1, {rotation=0.5, a=0.4, radius=0.8, img="healdark"}))
+			end
+		else
+			self:effectTemporaryValue(eff, "all_damage_convert", DamageType.LIGHT)
+			self:effectTemporaryValue(eff, "all_damage_convert_percent", eff.conversion)
+			if core.shader.active() then
+				eff.particle = self:addParticles(Particles.new("shader_ring_rotating", 1, {rotation=0.5, a=0.4, radius=0.8, img="healcelestial"}))
+			end
+		end
+	end,
+	deactivate = function(self, eff)
+		self:removeParticles(eff.particle)
+	end,
+	on_timeout = function(self, eff)
+		if eff.src:hasEffect(eff.src.EFF_DARKEST_LIGHT) then
+			DamageType:get(DamageType.DARKNESS).projector(eff.src, self.x, self.y, DamageType.DARKNESS, eff.dotDam)
+		else
+			DamageType:get(DamageType.LIGHT).projector(eff.src, self.x, self.y, DamageType.LIGHT, eff.dotDam)
+		end
+	end,
+}
+
 -- Circles
 newEffect{
 	name = "SANCTITY", image = "talents/circle_of_sanctity.png",
-- 
GitLab