Skip to content
Snippets Groups Projects
hymns.lua 21.8 KiB
Newer Older
dg's avatar
dg committed
-- ToME - Tales of Maj'Eyal
DarkGod's avatar
DarkGod committed
-- Copyright (C) 2009 - 2019 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

--- general tactical function accounting for Hymn Acolyte and Hymn Incantor
--	uses t.base_tactical and t.adept_deac_tactical
local hymn_tactical = function(self, t, aitarget)
	local bt_type, tacs = type(t.base_tactical)
	if bt_type == "function" then
		tacs = t.base_tactical(self, t, aitarget)
	elseif bt_type == "table" then
		tacs = table.clone(t.base_tactical)
	end
	tacs = tacs or {}
	local lvl = self:getTalentLevel(self.T_HYMN_INCANTOR)
	if lvl > 0 then
		tacs.buff = (tacs.buff or 0) + 2*lvl/(lvl + 5)
	end
	lvl = self:getTalentLevel(self.T_HYMN_ADEPT)
	if lvl > 0 and self:isTalentActive(t.id) then
		if t.adept_deac_tactical then
			table.mergeAdd(tacs, t.adept_deac_tactical)
		end
		tacs.special = (tacs.special or 0) + 0.01 -- forces some "inertia" in toggling hymns
	end
	return tacs
end

	name = "Hymn of Shadows",
	type = {"celestial/hymns-hymns", 1},
	hide = true,
	require = divi_req1,
	points = 5,
	sustain_negative = 20,
dg's avatar
dg committed
	dont_provide_pool = true,
	tactical = hymn_tactical,
	base_tactical = { buff=1, defend=0.5, escape=1 , closein=1},
	adept_deac_tactical = {escape = -0.5, closein = -0.5}, -- adept tactic adjustments when deactivating (negated)
	range = 0,
Chris Davidson's avatar
Chris Davidson committed
	moveSpeed = function(self, t) return self:combatTalentSpellDamage(t, 20, 50) end,
	castSpeed = function(self, t) return self:combatTalentSpellDamage(t, 7, 20) end,
	callbackOnActBase = function(self, t)
DarkGod's avatar
DarkGod committed
		if self:isTalentActive(self.T_HYMN_NOCTURNALIST) then
HousePet's avatar
HousePet committed
			local t2 = self:getTalentFromId(self.T_HYMN_NOCTURNALIST)
			t2.do_beams(self, t2)
		end
	end,
	sustain_slots = 'celestial_hymn',
	activate = function(self, t)
		game:playSoundNear(self, "talents/spell_generic2")
		local ret = {}
		self:talentTemporaryValue(ret, "movement_speed", t.moveSpeed(self, t)/100)
		self:talentTemporaryValue(ret, "combat_spellspeed", t.castSpeed(self, t)/100)
		ret.particle = self:addParticles(Particles.new("darkness_shield", 1))
HousePet's avatar
HousePet committed
		if self:knowTalent(self.T_HYMN_INCANTOR) then
			local t2 = self:getTalentFromId(self.T_HYMN_INCANTOR)
			self:talentTemporaryValue(ret, "on_melee_hit", {[DamageType.DARKNESS]=t2.getDamageOnMeleeHit(self, t2)})
			self:talentTemporaryValue(ret, "inc_damage", {[DamageType.DARKNESS] = t2.getDarkDamageIncrease(self, t2)})
		end
HousePet's avatar
HousePet committed
		if self:knowTalent(self.T_HYMN_ADEPT) then
			local t2 = self:getTalentFromId(self.T_HYMN_ADEPT)
			self:talentTemporaryValue(ret, "infravision", t2.getBonusInfravision(self, t2))
		end
DarkGod's avatar
DarkGod committed
		if self:isTalentActive(self.T_HYMN_NOCTURNALIST) then
HousePet's avatar
HousePet committed
			local t2 = self:getTalentFromId(self.T_HYMN_NOCTURNALIST)
		return ret
	end,
	deactivate = function(self, t, p)
		self:removeParticles(p.particle)
		if self.turn_procs.resetting_talents then return true end
HousePet's avatar
HousePet committed
		if self:knowTalent(self.T_HYMN_ADEPT) then
			local t2 = self:getTalentFromId(self.T_HYMN_ADEPT)
			game:onTickEnd(function() self:setEffect(self.EFF_WILD_SPEED, 1, {power=t2.getSpeed(self, t2), no_talents=0}) end)
		return true
	end,
	info = function(self, t)
DarkGod's avatar
DarkGod committed
		return ([[Chant the glory of the Moons, gaining the agility of shadows.
Chris Davidson's avatar
Chris Davidson committed
		This increases your movement speed by %d%% and your spell speed by %d%%.
		You may only have one Hymn active at once.
dg's avatar
dg committed
		The effects will increase with your Spellpower.]]):
Otowa Kotori's avatar
Otowa Kotori committed
		tformat(t.moveSpeed(self, t), t.castSpeed(self, t))
	name = "Hymn of Detection",
	type = {"celestial/hymns-hymns", 1},
	hide = true,
	require = divi_req1,
	sustain_negative = 20,
dg's avatar
dg committed
	dont_provide_pool = true,
	tactical = hymn_tactical,
	adept_deac_tactical = {escape = -0.5}, -- adept tactic adjustments when deactivating (negated)
	base_tactical = function(self, t, aitarget) -- buff tactic up to +3 depending on how much the talent helps to see a stealthed/invisible target or a target that is out of sense range
		local buff, max_buff = 0, 4
DarkGod's avatar
DarkGod committed
		if not aitarget or not self.aiSeeTargetPos then -- out of combat, stay vigilant for enemies
			buff = max_buff
		else
			if self:attr("blind") or self:attr("esp_all") then return end -- target automatically seen or unseen
			local ax, ay = self:aiSeeTargetPos(aitarget)
			local dist = core.fov.distance(self.x, self.y, ax, ay)
			if dist <= self.sight then -- target within sight range
				local is_active = self:isTalentActive(t.id)
				local see, see_chance = self:canSeeNoCache(aitarget)
DarkGod's avatar
DarkGod committed
				if see_chance and see_chance < 100 then -- detecting hidden is useful (rules out esp, etc.)
					local tgt_inv = aitarget:attr("invisible")
					if tgt_inv then -- buff value increases depending on relative invisibility detection
						local base_det = self:combatSeeInvisible() - (is_active and self.compute_vals[is_active.invis] or 0)
						if base_det <= 0 then return {buff = max_buff} end -- cannot see at all with no detection ability
						local need_det = tgt_inv - base_det
						buff = buff + math.max(0, max_buff*(0.5 + need_det/(math.abs(need_det) + 20))) -- adds 0 (need_det = -20) to max_buff (need_det = +20)
					end
					local tgt_stealth = aitarget:attr("stealth")
					if tgt_stealth then -- buff value increases depending on relative stealth detection
						tgt_stealth = tgt_stealth + (aitarget:attr("inc_stealth") or 0)
						local base_det = is_active and is_active.base_see_stealth or self:combatSeeStealth()
						local need_det = tgt_stealth - base_det
						buff = buff + math.max(0, max_buff*(0.5 + need_det/(math.abs(need_det) + 20))) -- adds 0 (need_det = -20) to max_buff (need_det = +20)
					end
				end
			end
		end
		buff=math.min(buff, max_buff)
		return {buff=buff + 0.25, special = buff/2}
	range = 0,
	sustain_slots = 'celestial_hymn',
	getSeeInvisible = function(self, t) return self:combatTalentSpellDamage(t, 2, 25) end,
	getSeeStealth = function(self, t) return self:combatTalentSpellDamage(t, 2, 25) end,
	critPower = function(self, t) return self:combatTalentSpellDamage(t, 10, 50) end,
	callbackOnActBase = function(self, t)
DarkGod's avatar
DarkGod committed
		if self:isTalentActive(self.T_HYMN_NOCTURNALIST) then
HousePet's avatar
HousePet committed
			local t2 = self:getTalentFromId(self.T_HYMN_NOCTURNALIST)
			t2.do_beams(self, t2)
		end
	end,
	activate = function(self, t)
		game:playSoundNear(self, "talents/spell_generic2")
--		base_see_stealth = self:combatSeeStealth(),
		local ret = {}
		self:talentTemporaryValue(ret, "see_invisible", t.getSeeInvisible(self, t))
		self:talentTemporaryValue(ret, "see_stealth", t.getSeeStealth(self, t))
		self:talentTemporaryValue(ret, "blindfight", 1)
		self:talentTemporaryValue(ret, "combat_critical_power", t.critPower(self, t))
		ret.particle = self:addParticles(Particles.new("darkness_shield", 1))
HousePet's avatar
HousePet committed
		if self:knowTalent(self.T_HYMN_INCANTOR) then
			local t2 = self:getTalentFromId(self.T_HYMN_INCANTOR)
			self:talentTemporaryValue(ret, "on_melee_hit", {[DamageType.DARKNESS]=t2.getDamageOnMeleeHit(self, t2)})
			self:talentTemporaryValue(ret, "inc_damage", {[DamageType.DARKNESS] = t2.getDarkDamageIncrease(self, t2)})
		end
HousePet's avatar
HousePet committed
		if self:knowTalent(self.T_HYMN_ADEPT) then
			local t2 = self:getTalentFromId(self.T_HYMN_ADEPT)
			self:talentTemporaryValue(ret, "infravision", t2.getBonusInfravision(self, t2))
		end
DarkGod's avatar
DarkGod committed
		if self:isTalentActive(self.T_HYMN_NOCTURNALIST) then
HousePet's avatar
HousePet committed
			local t2 = self:getTalentFromId(self.T_HYMN_NOCTURNALIST)
		return ret
	end,
	deactivate = function(self, t, p)
		self:removeParticles(p.particle)
		if self.turn_procs.resetting_talents then return true end
HousePet's avatar
HousePet committed
		if self:knowTalent(self.T_HYMN_ADEPT) then
			local t2 = self:getTalentFromId(self.T_HYMN_ADEPT)
			self:setEffect(self.EFF_INVISIBILITY, t2.invisDur(self, t2), {power=t2.invisPower(self, t2), penalty=0.4})
		end
		return true
	end,
	info = function(self, t)
		local invis = t.getSeeInvisible(self, t)
		local stealth = t.getSeeStealth(self, t)
		return ([[Chant the glory of the Moons, granting you stealth detection (+%d power), and invisibility detection (+%d power).
		You may also attack creatures you cannot see without penalty and your critical hits do %d%% more damage.
		You may only have one Hymn active at once.
		The stealth and invisibility detection will increase with your Spellpower.]]):
Otowa Kotori's avatar
Otowa Kotori committed
		tformat(stealth, invis, t.critPower(self, t))
	name = "Hymn of Perseverance",
	type = {"celestial/hymns-hymns",1},
	hide = true,
	require = divi_req1,
	sustain_negative = 20,
dg's avatar
dg committed
	dont_provide_pool = true,
	tactical = hymn_tactical,
	base_tactical = {defend=1}, -- could check base immunities here
	adept_deac_tactical = {defend = -0.5}, -- adept tactic adjustments when deactivating (negated)
	getImmunities = function(self, t) return self:combatTalentLimit(t, 1, 0.2, 0.45) end, -- Limit < 100%
	callbackOnActBase = function(self, t)
DarkGod's avatar
DarkGod committed
		if self:isTalentActive(self.T_HYMN_NOCTURNALIST) then
HousePet's avatar
HousePet committed
			local t2 = self:getTalentFromId(self.T_HYMN_NOCTURNALIST)
			t2.do_beams(self, t2)
		end
	end,
	sustain_slots = 'celestial_hymn',
	activate = function(self, t)
		game:playSoundNear(self, "talents/spell_generic2")
		local ret = {}
		self:talentTemporaryValue(ret, "stun_immune", t.getImmunities(self, t))
		self:talentTemporaryValue(ret, "confusion_immune", t.getImmunities(self, t))
		self:talentTemporaryValue(ret, "blind_immune", t.getImmunities(self, t))
		ret.particle = self:addParticles(Particles.new("darkness_shield", 1))
HousePet's avatar
HousePet committed
		if self:knowTalent(self.T_HYMN_INCANTOR) then
			local t2 = self:getTalentFromId(self.T_HYMN_INCANTOR)
			self:talentTemporaryValue(ret, "on_melee_hit", {[DamageType.DARKNESS]=t2.getDamageOnMeleeHit(self, t2)})
			self:talentTemporaryValue(ret, "inc_damage", {[DamageType.DARKNESS] = t2.getDarkDamageIncrease(self, t2)})
		end
HousePet's avatar
HousePet committed
		if self:knowTalent(self.T_HYMN_ADEPT) then
			local t2 = self:getTalentFromId(self.T_HYMN_ADEPT)
			self:talentTemporaryValue(ret, "infravision", t2.getBonusInfravision(self, t2))
		end
DarkGod's avatar
DarkGod committed
		if self:isTalentActive(self.T_HYMN_NOCTURNALIST) then
HousePet's avatar
HousePet committed
			local t2 = self:getTalentFromId(self.T_HYMN_NOCTURNALIST)
		return ret
	end,
	deactivate = function(self, t, p)
		self:removeParticles(p.particle)
		if self.turn_procs.resetting_talents then return true end

HousePet's avatar
HousePet committed
		if self:knowTalent(self.T_HYMN_ADEPT) then
			local t2 = self:getTalentFromId(self.T_HYMN_ADEPT)
			self:setEffect(self.EFF_DAMAGE_SHIELD, t2.shieldDur(self, t2), {power=t2.shieldPower(self, t2)})
		end
		return true
	end,
	info = function(self, t)
dg's avatar
dg committed
		local immunities = t.getImmunities(self, t)
DarkGod's avatar
DarkGod committed
		return ([[Chant the glory of the Moons, granting you %d%% stun, blindness and confusion resistance.
		You may only have one Hymn active at once.]]):
Otowa Kotori's avatar
Otowa Kotori committed
		tformat(100 * (immunities))
-- Depreciated, but retained for compatability.
	name = "Hymn of Moonlight",
	type = {"celestial/hymns-hymns",1},
	mode = "sustained",
	require = divi_req4,
	points = 5,
	sustain_negative = 20,
dg's avatar
dg committed
	dont_provide_pool = true,
	tactical = { ATTACKAREA = { DARKNESS = 1 },
		NEGATIVE = function(self, t, target)
			return t.getNegativeDrain(self, t)/15 -- negated for foes
		end
	},
dg's avatar
dg committed
	range = 5,
dg's avatar
dg committed
	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 7, 80) end,
	getTargetCount = function(self, t) return math.floor(self:combatTalentScale(t, 1, 5)) end,
	getNegativeDrain = function(self, t) return self:combatTalentLimit(t, 0, 7, 3) end, -- Limit > 0, no regen at high levels
	target = function(self, t) -- for AI only
		 return {type="ball", friendlyfire=false, friendlyblock=false, radius=self:getTalentRange(t), range=0, talent=t}
	callbackOnActBase = function(self, t)
		local drain = t.getNegativeDrain(self, t)
		if self:getNegative() < drain then return end
		local grids = core.fov.circle_grids(self.x, self.y, 5, true)
		for x, yy in pairs(grids) do for y, _ in pairs(grids[x]) do
			local a = game.level.map(x, y, Map.ACTOR)
			if a and self:reactionToward(a) < 0 then
				tgts[#tgts+1] = a
			end
		end end

		local drain = t.getNegativeDrain(self, t)

		-- Randomly take targets
		local tg = {type="hit", range=self:getTalentRange(t), talent=t}
dg's avatar
dg committed
		for i = 1, t.getTargetCount(self, t) do
			if #tgts <= 0 then break end
			if self:getNegative() - 1 < drain then break end
			local a, id = rng.table(tgts)
			table.remove(tgts, id)

dg's avatar
dg committed
			self:project(tg, a.x, a.y, DamageType.DARKNESS, rng.avg(1, self:spellCrit(t.getDamage(self, t)), 3))
			game.level.map:particleEmitter(self.x, self.y, math.max(math.abs(a.x-self.x), math.abs(a.y-self.y)), "shadow_beam", {tx=a.x-self.x, ty=a.y-self.y})
			game:playSoundNear(self, "talents/spell_generic")
			self:incNegative(-drain)
	sustain_slots = 'celestial_hymn',
	activate = function(self, t)
		game:playSoundNear(self, "talents/spell_generic")
Otowa Kotori's avatar
Otowa Kotori committed
		game.logSeen(self, "#DARK_GREY#A shroud of shadow dances around %s!", self:getName())
		return { name=("%s's %s"):tformat(self:getName():capitalize(), t.name)
		}
	end,
	deactivate = function(self, t, p)
Otowa Kotori's avatar
Otowa Kotori committed
		game.logSeen(self, "#DARK_GREY#The shroud of shadows around %s disappears.", self:getName())
		return true
	end,
	info = function(self, t)
dg's avatar
dg committed
		local targetcount = t.getTargetCount(self, t)
		local damage = t.getDamage(self, t)
		local drain = t.getNegativeDrain(self, t)
DarkGod's avatar
DarkGod committed
		return ([[Chant the glory of the Moons, conjuring a shroud of dancing shadows that follows you as long as this spell is active.
dg's avatar
dg committed
		Each turn, a shadowy beam will hit up to %d of your foes within radius 5 for 1 to %0.2f damage.
		This powerful spell will drain %0.1f negative energy for each beam; no beam will fire if your negative energy is too low.
DarkGod's avatar
DarkGod committed
		You may only have one Hymn active at once.
dg's avatar
dg committed
		The damage will increase with your Spellpower.]]):
Otowa Kotori's avatar
Otowa Kotori committed
		tformat(targetcount, damDesc(self, DamageType.DARKNESS, damage), drain)

newTalent{
	name = "Hymn Acolyte",
	type = {"celestial/hymns", 1},
	require = divi_req1,
	points = 5,
	mode = "passive",
Hachem_Muche's avatar
Hachem_Muche committed
	negative = 0, -- forces learning of Negative pool
	passives = function(self, t)
		self:setTalentTypeMastery("celestial/hymns-hymns", self:getTalentMastery(t))
	end,
	on_learn = function(self, t)
		self:learnTalent(self.T_HYMN_OF_SHADOWS, true, nil, {no_unlearn=true})
		self:learnTalent(self.T_HYMN_OF_DETECTION, true, nil, {no_unlearn=true})
		self:learnTalent(self.T_HYMN_OF_PERSEVERANCE, true, nil, {no_unlearn=true})
	end,
	on_unlearn = function(self, t)
		self:unlearnTalent(self.T_HYMN_OF_SHADOWS)
		self:unlearnTalent(self.T_HYMN_OF_DETECTION)
		self:unlearnTalent(self.T_HYMN_OF_PERSEVERANCE)
	end,
	info = function(self, t)
		local ret = ""
		local old1 = self.talents[self.T_HYMN_OF_SHADOWS]
		local old2 = self.talents[self.T_HYMN_OF_DETECTION]
		local old3 = self.talents[self.T_HYMN_OF_PERSEVERANCE]
		self.talents[self.T_HYMN_OF_SHADOWS] = (self.talents[t.id] or 0)
		self.talents[self.T_HYMN_OF_DETECTION] = (self.talents[t.id] or 0)
		self.talents[self.T_HYMN_OF_PERSEVERANCE] = (self.talents[t.id] or 0)
		pcall(function() -- Be very paranoid, even if some addon or whatever manage to make that crash, we still restore values
			local t1 = self:getTalentFromId(self.T_HYMN_OF_SHADOWS)
			local t2 = self:getTalentFromId(self.T_HYMN_OF_DETECTION)
			local t3 = self:getTalentFromId(self.T_HYMN_OF_PERSEVERANCE)
			ret = ([[You have learned to sing the praises of the Moons, in the form of three defensive Hymns:

Chris Davidson's avatar
Chris Davidson committed
Hymn of Shadows: Increases your movement speed by %d%% and your spell casting speed by %d%%.

Hymn of Detection: Increases your ability to see stealthy creatures by %d and invisible creatures by %d, and increases your critical power by %d%%.

Hymn of Perseverance: Increases your resistance to stun, confusion and blinding by %d%%.

You may only have one Hymn active at a time.]]):
Otowa Kotori's avatar
Otowa Kotori committed
			tformat(t1.moveSpeed(self, t1), t1.castSpeed(self, t1), t2.getSeeStealth(self, t2), t2.getSeeInvisible(self, t2), t2.critPower(self, t2), t3.getImmunities(self, t3)*100)
		end)
		self.talents[self.T_HYMN_OF_SHADOWS] = old1
		self.talents[self.T_HYMN_OF_DETECTION] = old2
		self.talents[self.T_HYMN_OF_PERSEVERANCE] = old3
		return ret
	end,
}

newTalent{
	name = "Hymn Incantor",
DarkGod's avatar
DarkGod committed
	type = {"celestial/hymns", 2},
	require = divi_req2,
	points = 5,
	mode = "passive",
Chris Davidson's avatar
Chris Davidson committed
	getDamageOnMeleeHit = function(self, t) return self:combatTalentSpellDamage(t, 10, 30) end,
DarkGod's avatar
DarkGod committed
	getDarkDamageIncrease = function(self, t) return self:combatTalentSpellDamage(t, 10, 30) end,
	info = function(self, t)
		return ([[Your Hymns now focus darkness near you, which increases your darkness damage by %d%% and does %0.2f darkness damage to anyone who hits you in melee.
Otowa Kotori's avatar
Otowa Kotori committed
		These values scale with your Spellpower.]]):tformat(t.getDarkDamageIncrease(self, t), damDesc(self, DamageType.DARKNESS, t.getDamageOnMeleeHit(self, t)))
	end,
}

-- Remember that Hymns can be swapped instantly.
newTalent{
	name = "Hymn Adept",
DarkGod's avatar
DarkGod committed
	type = {"celestial/hymns", 3},
	require = divi_req3,
	points = 5,
	mode = "passive",
	getBonusInfravision = function(self, t) return math.floor(self:combatTalentScale(t, 0.75, 3.5, 0.75)) end,
	getSpeed = function(self, t) return self:combatTalentSpellDamage(t, 300, 600) end,
	shieldDur = function(self, t) return self:combatTalentSpellDamage(t, 5, 10) end,
DarkGod's avatar
DarkGod committed
	shieldPower = function(self, t) return self:combatTalentSpellDamage(t, 50, 500) end,
	invisDur = function(self, t) return self:combatTalentSpellDamage(t, 5, 10) end,
	invisPower = function(self, t) return math.ceil(self:combatTalentSpellDamage(t, 20, 30)) end,
	info = function(self, t)
		return ([[Your skill in Hymns now improves your sight in darkness, increasing your infravision radius by %d.
		Also, when you end a Hymn, you will gain a buff of a type based on which Hymn you ended.
		Hymn of Shadows increases your movement speed by %d%% for one turn.
		Hymn of Detection makes you invisible (power %d) for %d turns.
Otowa Kotori's avatar
Otowa Kotori committed
		Hymn of Perseverance grants a damage shield (power %d) for %d turns.]]):tformat(t.getBonusInfravision(self, t), t.getSpeed(self, t), t.invisPower(self, t), t.invisDur(self, t), self:getShieldAmount(t.shieldPower(self, t)), self:getShieldDuration(t.shieldDur(self, t)))
	end,
}

newTalent{
	name = "Hymn Nocturnalist",
DarkGod's avatar
DarkGod committed
	type = {"celestial/hymns", 4},
	require = divi_req4,
	points = 5,
DarkGod's avatar
DarkGod committed
	mode = "sustained",
	range = 5,
	tactical = { SELF = {NEGATIVE = 0.5}, -- this assumes a Hymn is active
		ATTACKAREA = { DARKNESS = 1 },
		DISABLE = { blind = 0.5},
		NEGATIVE = function(self, t, target)
			return t.getNegativeDrain(self, t)/15 -- negated for foes
		end
	},
	target = function(self, t) -- for AI only
		 return {type="ball", friendlyfire=false, friendlyblock=false, radius=self:getTalentRange(t), range=0, talent=t}
	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 1, 50) end,
	getTargetCount = function(self, t) return math.floor(self:combatTalentScale(t, 1, 5)) end,
	getNegativeDrain = function(self, t) return 5 end,
	do_beams = function(self, t)
		if self:getNegative() < t.getNegativeDrain(self, t) then return end

		local tgts = {}
		local grids = core.fov.circle_grids(self.x, self.y, 5, true)
		for x, yy in pairs(grids) do for y, _ in pairs(grids[x]) do
			local a = game.level.map(x, y, Map.ACTOR)
			if a and self:reactionToward(a) < 0 then
				tgts[#tgts+1] = a
			end
		end end

		if #tgts <= 0 then return end
		local drain = t.getNegativeDrain(self, t)
		self:incNegative(-drain)  -- Make sure to pay before Corona procs
		local dam = self:spellCrit(t.getDamage(self, t))

		-- Randomly take targets
		local tg = {type="hit", range=self:getTalentRange(t), talent=t}
		for i = 1, t.getTargetCount(self, t) do
			if #tgts <= 0 then break end
			local a, id = rng.table(tgts)
			table.remove(tgts, id)

			self:project(tg, a.x, a.y, DamageType.DARKNESS_BLIND, dam)
			game.level.map:particleEmitter(self.x, self.y, math.max(math.abs(a.x-self.x), math.abs(a.y-self.y)), "shadow_beam", {tx=a.x-self.x, ty=a.y-self.y})
			game:playSoundNear(self, "talents/spell_generic")
		end
	end,
DarkGod's avatar
DarkGod committed
	activate = function(self, t)
		game:onTickEnd(function()
			self.turn_procs.resetting_talents = true
			local hymn_id = self.sustain_slots and self.sustain_slots.celestial_hymn
			if self:isTalentActive(hymn_id) then
				self:forceUseTalent(hymn_id, {ignore_energy=true, ignore_cd=true, no_talent_fail=true, silent=true})
				self:forceUseTalent(hymn_id, {ignore_energy=true, ignore_cd=true, no_talent_fail=true, silent=true})
			end
			self.turn_procs.resetting_talents = nil
		end)
DarkGod's avatar
DarkGod committed
		return {}
	end,
	deactivate = function(self, t, p)
		game:onTickEnd(function()
			self.turn_procs.resetting_talents = true
			local hymn_id = self.sustain_slots and self.sustain_slots.celestial_hymn
			if self:isTalentActive(hymn_id) then
				self:forceUseTalent(hymn_id, {ignore_energy=true, ignore_cd=true, no_talent_fail=true, silent=true})
				self:forceUseTalent(hymn_id, {ignore_energy=true, ignore_cd=true, no_talent_fail=true, silent=true})
			end
			self.turn_procs.resetting_talents = nil
		end)
DarkGod's avatar
DarkGod committed
		return true
	end,
	info = function(self, t)
		return ([[Your passion for singing the praises of the Moons reaches its zenith.
		Your Hymns now fire shadowy beams that will hit up to %d of your foes within radius 5 for %0.2f damage, with a 25%% chance of blinding.
		This powerful effect will drain %0.1f negative energy each time it fires at at least 1 target; no beam will fire if your negative energy is too low.
Otowa Kotori's avatar
Otowa Kotori committed
		These values scale with your Spellpower.]]):tformat(t.getTargetCount(self, t), damDesc(self, DamageType.DARKNESS, t.getDamage(self, t)), t.getNegativeDrain(self, t))