From 7b5ace1499d31843432b632396de99125bac872f Mon Sep 17 00:00:00 2001
From: dg <dg@51575b47-30f0-44d4-a5cc-537603b46e54>
Date: Mon, 17 Jan 2011 00:49:41 +0000
Subject: [PATCH] fix

git-svn-id: http://svn.net-core.org/repos/t-engine4@2405 51575b47-30f0-44d4-a5cc-537603b46e54
---
 game/modules/tome/class/Actor.lua             |   2 +
 .../modules/tome/data/general/objects/gem.lua |   4 +-
 .../tome/data/talents/psionic/absorption.lua  | 648 ++++++-------
 .../talents/psionic/augmented-mobility.lua    | 408 ++++-----
 .../psionic/finer-energy-manipulations.lua    | 280 +++---
 .../tome/data/talents/psionic/focus.lua       | 328 +++----
 .../tome/data/talents/psionic/projection.lua  | 853 +++++++++---------
 .../data/talents/psionic/psi-fighting.lua     | 332 +++----
 .../tome/data/talents/psionic/voracity.lua    | 386 ++++----
 9 files changed, 1623 insertions(+), 1618 deletions(-)

diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua
index c4ee68bdfc..cb877eb63d 100644
--- a/game/modules/tome/class/Actor.lua
+++ b/game/modules/tome/class/Actor.lua
@@ -214,6 +214,8 @@ function _M:act()
 			local t = self:getTalentFromId(tid)
 			if (t.sustain_mana and self.mana < 1) or (t.sustain_stamina and self.stamina < 1) or (t.sustain_psi and self.psi < 1) then
 				self:forceUseTalent(tid, {ignore_energy=true})
+			elseif (t.sustain_psi and self.psi < 1) and t.remove_on_zero then
+				self:forceUseTalent(tid, {ignore_energy=true})
 			end
 		end
 	end
diff --git a/game/modules/tome/data/general/objects/gem.lua b/game/modules/tome/data/general/objects/gem.lua
index 61ff9d339d..4e0010c18e 100644
--- a/game/modules/tome/data/general/objects/gem.lua
+++ b/game/modules/tome/data/general/objects/gem.lua
@@ -63,11 +63,11 @@ local function newGem(name, image, cost, rarity, color, min_level, max_level, ti
 	}
 end
 
-newGem("Diamond",	"object/diamond.png",5,	18,	"white",	40,	50, 5, 70,
+newGem("Diamond",	"object/diamond.png",5,		18,	"white",	40,	50, 5, 70,
 	{ inc_stats = { [Stats.STAT_STR] = 5, [Stats.STAT_DEX] = 5, [Stats.STAT_MAG] = 5, [Stats.STAT_WIL] = 5, [Stats.STAT_CUN] = 5, [Stats.STAT_CON] = 5, } },
 	{ power=25 }
 )
-newGem("Pearl",	"object/pearl.png",	5,	18,	"white",	40,	50, 5, 70,
+newGem("Pearl",	"object/pearl.png",	5,		18,	"white",	40,	50, 5, 70,
 	{ resists = {all=10} },
 	{ splash={type="LITE", dam=1} }
 )
diff --git a/game/modules/tome/data/talents/psionic/absorption.lua b/game/modules/tome/data/talents/psionic/absorption.lua
index 0bae68b8ef..61ec2f9f61 100644
--- a/game/modules/tome/data/talents/psionic/absorption.lua
+++ b/game/modules/tome/data/talents/psionic/absorption.lua
@@ -1,324 +1,324 @@
--- ToME - Tales of Maj'Eyal
--- Copyright (C) 2009, 2010 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 function getGemLevel(self)
-		local gem_level = 0
-		if not self:getInven("PSIONIC_FOCUS")[1] then return gem_level end
-		local tk_item = self:getInven("PSIONIC_FOCUS")[1]
-		if tk_item.type == "gem" then
-			gem_level = tk_item.material_level
-		else
-			gem_level = 0
-		end
-		return gem_level
-end
-
-local function getShieldStrength(self, t)
-	local add = 0
-	if self:knowTalent(self.T_FOCUSED_CHANNELING) then
-		add = getGemLevel(self)*(1 + 0.1*(self:getTalentLevel(self.T_FOCUSED_CHANNELING) or 0))
-	end
-	--return 2 + (1+ self:getWil(8))*self:getTalentLevel(t) + add
-	return self:combatTalentIntervalDamage(t, "wil", 3, 60) + add
-end
-
-local function getSpikeStrength(self, t)
-	local ss = getShieldStrength(self, t)
-	return  75*self:getTalentLevel(t) + ss*math.sqrt(ss)
-end
-
-newTalent{
-	name = "Kinetic Shield",
-	type = {"psionic/absorption", 1},
-	require = psi_absorb,
-	mode = "sustained", no_sustain_autoreset = true,
-	points = 5,
-	sustain_psi = 30,
-	cooldown = function(self, t)
-		return 20 - 2*(self:getTalentLevelRaw(self.T_ABSORPTION_MASTERY) or 0)
-	end,
-	range = 10,
-
-	--called when damage gets absorbed by kinetic shield
-	ks_on_damage = function(self, t, damtype, dam)
-		local mast = 20 - (2*self:getTalentLevel(self.T_ABSORPTION_MASTERY) or 0) - 0.4*getGemLevel(self)
-		local total_dam = dam
-		local absorbable_dam = 0.01*(60 + math.min(self:getCun(50), 40))* total_dam
-		local guaranteed_dam = total_dam - absorbable_dam
-		dam = absorbable_dam
-		if damtype ~= DamageType.PHYSICAL and damtype ~= DamageType.ACID then return total_dam end
-
-		if dam <= self.kinetic_shield then
-			self:incPsi(2 + dam/mast)
-			dam = 0
-		else
-			self:incPsi(2 + self.kinetic_shield/mast)
-			dam = dam - self.kinetic_shield
-		end
-
-		return dam + guaranteed_dam
-	end,
-
-
-	activate = function(self, t)
-		game:playSoundNear(self, "talents/heal")
-		local s_str = getShieldStrength(self, t)
-		return {
-			am = self:addTemporaryValue("kinetic_shield", s_str),
-		}
-	end,
-	deactivate = function(self, t, p)
-		local spike_str = getSpikeStrength(self, t)
-		self:removeTemporaryValue("kinetic_shield", p.am)
-		self:setEffect(self.EFF_KINSPIKE_SHIELD, 5, {power=spike_str})
-		return true
-	end,
-
-	--called when damage gets absorbed by kinetic shield spike
-	kss_on_damage = function(self, t, damtype, dam)
-		local mast = 20 - (2*self:getTalentLevel(self.T_ABSORPTION_MASTERY) or 0) - 0.4*getGemLevel(self)
-		local total_dam = dam
-		local absorbable_dam = 0.01*(60 + math.min(self:getCun(50), 40))* total_dam
-		local guaranteed_dam = total_dam - absorbable_dam
-		dam = absorbable_dam
-
-		if damtype == DamageType.PHYSICAL or damtype == DamageType.ACID then
-			-- Absorb damage into the shield
-			if dam <= self.kinspike_shield_absorb then
-				self.kinspike_shield_absorb = self.kinspike_shield_absorb - dam
-				self:incPsi(2 + dam/mast)
-				dam = 0
-			else
-				self:incPsi(2 + self.kinspike_shield_absorb/mast)
-				dam = dam - self.kinspike_shield_absorb
-				self.kinspike_shield_absorb = 0
-			end
-
-			if self.kinspike_shield_absorb <= 0 then
-				game.logPlayer(self, "Your spiked kinetic shield crumbles under the damage!")
-				self:removeEffect(self.EFF_KINSPIKE_SHIELD)
-			end
-			return dam + guaranteed_dam
-		else
-			return total_dam
-		end
-	end,
-
-	info = function(self, t)
-		local s_str = getShieldStrength(self, t)
-		local spike_str = getSpikeStrength(self, t)
-		local mast = 20 - (2*self:getTalentLevel(self.T_ABSORPTION_MASTERY) or 0) - 0.4*getGemLevel(self)
-		local absorb = 60 + math.min(self:getCun(50), 40)
-		return ([[Surround yourself with a shield that will absorb at most %d physical or acid damage per attack. Deactivating the shield spikes it up to a temporary (five turns) %d point shield. The effect will increase with your Willpower stat.
-		Every time your shield absorbs damage, you gain two points of energy plus an additional point for every %d points of damage absorbed.
-		%d%% of any given attack is subject to absorption by this shield. The rest gets through as normal. Improve this by increasing Cunning.]]):
-		format(s_str, spike_str, mast, absorb)
-	end,
-}
-
-
-
-newTalent{
-	name = "Thermal Shield",
-	type = {"psionic/absorption", 1},
-	require = psi_absorb,
-	mode = "sustained", no_sustain_autoreset = true,
-	points = 5,
-	sustain_psi = 30,
-	cooldown = function(self, t)
-		return 20 - 2*(self:getTalentLevelRaw(self.T_ABSORPTION_MASTERY) or 0)
-	end,
-	range = 10,
-
-	--called when damage gets absorbed by thermal shield
-	ts_on_damage = function(self, t, damtype, dam)
-		local mast = 20 - (2*self:getTalentLevel(self.T_ABSORPTION_MASTERY) or 0) - 0.4*getGemLevel(self)
-		local total_dam = dam
-		local absorbable_dam = 0.01*(60 + math.min(self:getCun(50), 40))* total_dam
-		local guaranteed_dam = total_dam - absorbable_dam
-		dam = absorbable_dam
-		if damtype ~= DamageType.FIRE and damtype ~= DamageType.COLD then return total_dam end
-
-		if dam <= self.thermal_shield then
-			self:incPsi(2 + dam/mast)
-			dam = 0
-		else
-			self:incPsi(2 + self.thermal_shield/mast)
-			dam = dam - self.thermal_shield
-		end
-		return dam + guaranteed_dam
-	end,
-
-
-	activate = function(self, t)
-		game:playSoundNear(self, "talents/heal")
-		local s_str = getShieldStrength(self, t)
-		return {
-			am = self:addTemporaryValue("thermal_shield", s_str),
-		}
-	end,
-	deactivate = function(self, t, p)
-		local spike_str = getSpikeStrength(self, t)
-		self:removeTemporaryValue("thermal_shield", p.am)
-		self:setEffect(self.EFF_THERMSPIKE_SHIELD, 5, {power=spike_str})
-		return true
-	end,
-
-	--called when damage gets absorbed by thermal shield spike
-	tss_on_damage = function(self, t, damtype, dam)
-		local mast = 20 - (2*self:getTalentLevel(self.T_ABSORPTION_MASTERY) or 0) - 0.4*getGemLevel(self)
-		local total_dam = dam
-		local absorbable_dam = 0.01*(60 + math.min(self:getCun(50), 40))* total_dam
-		local guaranteed_dam = total_dam - absorbable_dam
-		dam = absorbable_dam
-
-		if damtype == DamageType.FIRE or damtype == DamageType.COLD then
-			-- Absorb damage into the shield
-			if dam <= self.thermspike_shield_absorb then
-				self.thermspike_shield_absorb = self.thermspike_shield_absorb - dam
-				self:incPsi(2 + dam/mast)
-				dam = 0
-			else
-				self:incPsi(2 + self.thermspike_shield_absorb/mast)
-				dam = dam - self.thermspike_shield_absorb
-				self.thermspike_shield_absorb = 0
-			end
-
-			if self.thermspike_shield_absorb <= 0 then
-				game.logPlayer(self, "Your spiked thermal shield crumbles under the damage!")
-				self:removeEffect(self.EFF_THERMSPIKE_SHIELD)
-			end
-			return dam + guaranteed_dam
-		else
-			return total_dam
-		end
-	end,
-
-	info = function(self, t)
-		local s_str = getShieldStrength(self, t)
-		local spike_str = getSpikeStrength(self, t)
-		local mast = 20 - (2*self:getTalentLevel(self.T_ABSORPTION_MASTERY) or 0) - 0.4*getGemLevel(self)
-		local absorb = 60 + math.min(self:getCun(50), 40)
-		return ([[Surround yourself with a shield that will absorb at most %d thermal damage per attack. Deactivating the shield spikes it up to a temporary (five turns) %d point shield. The effect will increase with your Willpower stat.
-		Every time your shield absorbs damage, you gain two points of energy plus an additional point for every %d points of damage absorbed.
-		%d%% of any given attack is subject to absorption by this shield. The rest gets through as normal. Improve this by increasing Cunning.]]):
-		format(s_str, spike_str, mast, absorb)
-	end,
-}
-
-newTalent{
-	name = "Charged Shield",
-	type = {"psionic/absorption", 1},
-	require = psi_absorb,
-	mode = "sustained", no_sustain_autoreset = true,
-	points = 5,
-	sustain_psi = 30,
-	cooldown = function(self, t)
-		return 20 - 2*(self:getTalentLevelRaw(self.T_ABSORPTION_MASTERY) or 0)
-	end,
-	range = 10,
-
-	--called when damage gets absorbed by charged shield
-	cs_on_damage = function(self, t, damtype, dam)
-		local mast = 20 - (2*self:getTalentLevel(self.T_ABSORPTION_MASTERY) or 0) - 0.4*getGemLevel(self)
-		local total_dam = dam
-		local absorbable_dam = 0.01*(60 + math.min(self:getCun(50), 40))* total_dam
-		local guaranteed_dam = total_dam - absorbable_dam
-		dam = absorbable_dam
-		if damtype ~= DamageType.LIGHTNING and damtype ~= DamageType.BLIGHT then return total_dam end
-
-		if dam <= self.charged_shield then
-			self:incPsi(2 + dam/mast)
-			dam = 0
-		else
-			self:incPsi(2 + self.charged_shield/mast)
-			dam = dam - self.charged_shield
-		end
-		return dam + guaranteed_dam
-	end,
-
-
-	activate = function(self, t)
-		game:playSoundNear(self, "talents/heal")
-		local s_str = getShieldStrength(self, t)
-		return {
-			am = self:addTemporaryValue("charged_shield", s_str),
-		}
-	end,
-	deactivate = function(self, t, p)
-		local spike_str = getSpikeStrength(self, t)
-		self:removeTemporaryValue("charged_shield", p.am)
-		self:setEffect(self.EFF_CHARGESPIKE_SHIELD, 5, {power=spike_str})
-		return true
-	end,
-
-	--called when damage gets absorbed by charged shield spike
-	css_on_damage = function(self, t, damtype, dam)
-		local mast = 20 - (2*self:getTalentLevel(self.T_ABSORPTION_MASTERY) or 0) - 0.4*getGemLevel(self)
-		local total_dam = dam
-		local absorbable_dam = 0.01*(60 + math.min(self:getCun(50), 40))* total_dam
-		local guaranteed_dam = total_dam - absorbable_dam
-		dam = absorbable_dam
-		if damtype == DamageType.LIGHTNING or damtype == DamageType.BLIGHT then
-			-- Absorb damage into the shield
-			if dam <= self.chargespike_shield_absorb then
-				self.chargespike_shield_absorb = self.chargespike_shield_absorb - dam
-				self:incPsi(2 + dam/mast)
-				dam = 0
-			else
-				self:incPsi(2 + self.chargespike_shield_absorb/mast)
-				dam = dam - self.chargespike_shield_absorb
-				self.chargespike_shield_absorb = 0
-			end
-
-			if self.chargespike_shield_absorb <= 0 then
-				game.logPlayer(self, "Your spiked charged shield crumbles under the damage!")
-				self:removeEffect(self.EFF_CHARGESPIKE_SHIELD)
-			end
-			return dam + guaranteed_dam
-		else
-			return total_dam
-		end
-	end,
-
-	info = function(self, t)
-		local s_str = getShieldStrength(self, t)
-		local spike_str = getSpikeStrength(self, t)
-		local mast = 20 - (2*self:getTalentLevel(self.T_ABSORPTION_MASTERY) or 0) - 0.4*getGemLevel(self)
-		local absorb = 60 + math.min(self:getCun(50), 40)
-		return ([[Surround yourself with a shield that will absorb at most %d lightning or blight damage per attack. Deactivating the shield spikes it up to a temporary (five turns) %d point shield. The effect will increase with your Willpower stat.
-		Every time your shield absorbs damage, you gain two points of energy plus an additional point for every %d points of damage absorbed.
-		%d%% of any given attack is subject to absorption by this shield. The rest gets through as normal. Improve this by increasing Cunning.]]):
-		format(s_str, spike_str, mast, absorb)
-	end,
-}
-
-newTalent{
-	name = "Absorption Mastery",
-	type = {"psionic/absorption", 4},
-	require = psi_wil_req2,
-	points = 5,
-	mode = "passive",
-	info = function(self, t)
-		local cooldown = 2*self:getTalentLevelRaw(t)
-		local mast = 2*self:getTalentLevel(t)
-		return ([[Your expertise in the art of energy absorption grows. Shield cooldowns are all reduced by %d turns, and the amount of damage absorption required to gain a point of energy is reduced by %0.2f.]]):
-		format(cooldown, mast)
-	end,
-}
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009, 2010 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 function getGemLevel(self)
+		local gem_level = 0
+		if not self:getInven("PSIONIC_FOCUS")[1] then return gem_level end
+		local tk_item = self:getInven("PSIONIC_FOCUS")[1]
+		if tk_item.type == "gem" then
+			gem_level = tk_item.material_level
+		else
+			gem_level = 0
+		end
+		return gem_level
+end
+
+local function getShieldStrength(self, t)
+	local add = 0
+	if self:knowTalent(self.T_FOCUSED_CHANNELING) then
+		add = getGemLevel(self)*(1 + 0.1*(self:getTalentLevel(self.T_FOCUSED_CHANNELING) or 0))
+	end
+	--return 2 + (1+ self:getWil(8))*self:getTalentLevel(t) + add
+	return self:combatTalentIntervalDamage(t, "wil", 3, 60) + add
+end
+
+local function getSpikeStrength(self, t)
+	local ss = getShieldStrength(self, t)
+	return  75*self:getTalentLevel(t) + ss*math.sqrt(ss)
+end
+
+newTalent{
+	name = "Kinetic Shield",
+	type = {"psionic/absorption", 1},
+	require = psi_absorb,
+	mode = "sustained", no_sustain_autoreset = true,
+	points = 5,
+	sustain_psi = 30,
+	cooldown = function(self, t)
+		return 20 - 2*(self:getTalentLevelRaw(self.T_ABSORPTION_MASTERY) or 0)
+	end,
+	range = 10,
+
+	--called when damage gets absorbed by kinetic shield
+	ks_on_damage = function(self, t, damtype, dam)
+		local mast = 20 - (2*self:getTalentLevel(self.T_ABSORPTION_MASTERY) or 0) - 0.4*getGemLevel(self)
+		local total_dam = dam
+		local absorbable_dam = 0.01*(60 + math.min(self:getCun(50), 40))* total_dam
+		local guaranteed_dam = total_dam - absorbable_dam
+		dam = absorbable_dam
+		if damtype ~= DamageType.PHYSICAL and damtype ~= DamageType.ACID then return total_dam end
+
+		if dam <= self.kinetic_shield then
+			self:incPsi(2 + dam/mast)
+			dam = 0
+		else
+			self:incPsi(2 + self.kinetic_shield/mast)
+			dam = dam - self.kinetic_shield
+		end
+
+		return dam + guaranteed_dam
+	end,
+
+
+	activate = function(self, t)
+		game:playSoundNear(self, "talents/heal")
+		local s_str = getShieldStrength(self, t)
+		return {
+			am = self:addTemporaryValue("kinetic_shield", s_str),
+		}
+	end,
+	deactivate = function(self, t, p)
+		local spike_str = getSpikeStrength(self, t)
+		self:removeTemporaryValue("kinetic_shield", p.am)
+		self:setEffect(self.EFF_KINSPIKE_SHIELD, 5, {power=spike_str})
+		return true
+	end,
+
+	--called when damage gets absorbed by kinetic shield spike
+	kss_on_damage = function(self, t, damtype, dam)
+		local mast = 20 - (2*self:getTalentLevel(self.T_ABSORPTION_MASTERY) or 0) - 0.4*getGemLevel(self)
+		local total_dam = dam
+		local absorbable_dam = 0.01*(60 + math.min(self:getCun(50), 40))* total_dam
+		local guaranteed_dam = total_dam - absorbable_dam
+		dam = absorbable_dam
+
+		if damtype == DamageType.PHYSICAL or damtype == DamageType.ACID then
+			-- Absorb damage into the shield
+			if dam <= self.kinspike_shield_absorb then
+				self.kinspike_shield_absorb = self.kinspike_shield_absorb - dam
+				self:incPsi(2 + dam/mast)
+				dam = 0
+			else
+				self:incPsi(2 + self.kinspike_shield_absorb/mast)
+				dam = dam - self.kinspike_shield_absorb
+				self.kinspike_shield_absorb = 0
+			end
+
+			if self.kinspike_shield_absorb <= 0 then
+				game.logPlayer(self, "Your spiked kinetic shield crumbles under the damage!")
+				self:removeEffect(self.EFF_KINSPIKE_SHIELD)
+			end
+			return dam + guaranteed_dam
+		else
+			return total_dam
+		end
+	end,
+
+	info = function(self, t)
+		local s_str = getShieldStrength(self, t)
+		local spike_str = getSpikeStrength(self, t)
+		local mast = 20 - (2*self:getTalentLevel(self.T_ABSORPTION_MASTERY) or 0) - 0.4*getGemLevel(self)
+		local absorb = 60 + math.min(self:getCun(50), 40)
+		return ([[Surround yourself with a shield that will absorb at most %d physical or acid damage per attack. Deactivating the shield spikes it up to a temporary (five turns) %d point shield. The effect will increase with your Willpower stat.
+		Every time your shield absorbs damage, you gain two points of energy plus an additional point for every %d points of damage absorbed.
+		%d%% of any given attack is subject to absorption by this shield. The rest gets through as normal. Improve this by increasing Cunning.]]):
+		format(s_str, spike_str, mast, absorb)
+	end,
+}
+
+
+
+newTalent{
+	name = "Thermal Shield",
+	type = {"psionic/absorption", 1},
+	require = psi_absorb,
+	mode = "sustained", no_sustain_autoreset = true,
+	points = 5,
+	sustain_psi = 30,
+	cooldown = function(self, t)
+		return 20 - 2*(self:getTalentLevelRaw(self.T_ABSORPTION_MASTERY) or 0)
+	end,
+	range = 10,
+
+	--called when damage gets absorbed by thermal shield
+	ts_on_damage = function(self, t, damtype, dam)
+		local mast = 20 - (2*self:getTalentLevel(self.T_ABSORPTION_MASTERY) or 0) - 0.4*getGemLevel(self)
+		local total_dam = dam
+		local absorbable_dam = 0.01*(60 + math.min(self:getCun(50), 40))* total_dam
+		local guaranteed_dam = total_dam - absorbable_dam
+		dam = absorbable_dam
+		if damtype ~= DamageType.FIRE and damtype ~= DamageType.COLD then return total_dam end
+
+		if dam <= self.thermal_shield then
+			self:incPsi(2 + dam/mast)
+			dam = 0
+		else
+			self:incPsi(2 + self.thermal_shield/mast)
+			dam = dam - self.thermal_shield
+		end
+		return dam + guaranteed_dam
+	end,
+
+
+	activate = function(self, t)
+		game:playSoundNear(self, "talents/heal")
+		local s_str = getShieldStrength(self, t)
+		return {
+			am = self:addTemporaryValue("thermal_shield", s_str),
+		}
+	end,
+	deactivate = function(self, t, p)
+		local spike_str = getSpikeStrength(self, t)
+		self:removeTemporaryValue("thermal_shield", p.am)
+		self:setEffect(self.EFF_THERMSPIKE_SHIELD, 5, {power=spike_str})
+		return true
+	end,
+
+	--called when damage gets absorbed by thermal shield spike
+	tss_on_damage = function(self, t, damtype, dam)
+		local mast = 20 - (2*self:getTalentLevel(self.T_ABSORPTION_MASTERY) or 0) - 0.4*getGemLevel(self)
+		local total_dam = dam
+		local absorbable_dam = 0.01*(60 + math.min(self:getCun(50), 40))* total_dam
+		local guaranteed_dam = total_dam - absorbable_dam
+		dam = absorbable_dam
+
+		if damtype == DamageType.FIRE or damtype == DamageType.COLD then
+			-- Absorb damage into the shield
+			if dam <= self.thermspike_shield_absorb then
+				self.thermspike_shield_absorb = self.thermspike_shield_absorb - dam
+				self:incPsi(2 + dam/mast)
+				dam = 0
+			else
+				self:incPsi(2 + self.thermspike_shield_absorb/mast)
+				dam = dam - self.thermspike_shield_absorb
+				self.thermspike_shield_absorb = 0
+			end
+
+			if self.thermspike_shield_absorb <= 0 then
+				game.logPlayer(self, "Your spiked thermal shield crumbles under the damage!")
+				self:removeEffect(self.EFF_THERMSPIKE_SHIELD)
+			end
+			return dam + guaranteed_dam
+		else
+			return total_dam
+		end
+	end,
+
+	info = function(self, t)
+		local s_str = getShieldStrength(self, t)
+		local spike_str = getSpikeStrength(self, t)
+		local mast = 20 - (2*self:getTalentLevel(self.T_ABSORPTION_MASTERY) or 0) - 0.4*getGemLevel(self)
+		local absorb = 60 + math.min(self:getCun(50), 40)
+		return ([[Surround yourself with a shield that will absorb at most %d thermal damage per attack. Deactivating the shield spikes it up to a temporary (five turns) %d point shield. The effect will increase with your Willpower stat.
+		Every time your shield absorbs damage, you gain two points of energy plus an additional point for every %d points of damage absorbed.
+		%d%% of any given attack is subject to absorption by this shield. The rest gets through as normal. Improve this by increasing Cunning.]]):
+		format(s_str, spike_str, mast, absorb)
+	end,
+}
+
+newTalent{
+	name = "Charged Shield",
+	type = {"psionic/absorption", 1},
+	require = psi_absorb,
+	mode = "sustained", no_sustain_autoreset = true,
+	points = 5,
+	sustain_psi = 30,
+	cooldown = function(self, t)
+		return 20 - 2*(self:getTalentLevelRaw(self.T_ABSORPTION_MASTERY) or 0)
+	end,
+	range = 10,
+
+	--called when damage gets absorbed by charged shield
+	cs_on_damage = function(self, t, damtype, dam)
+		local mast = 20 - (2*self:getTalentLevel(self.T_ABSORPTION_MASTERY) or 0) - 0.4*getGemLevel(self)
+		local total_dam = dam
+		local absorbable_dam = 0.01*(60 + math.min(self:getCun(50), 40))* total_dam
+		local guaranteed_dam = total_dam - absorbable_dam
+		dam = absorbable_dam
+		if damtype ~= DamageType.LIGHTNING and damtype ~= DamageType.BLIGHT then return total_dam end
+
+		if dam <= self.charged_shield then
+			self:incPsi(2 + dam/mast)
+			dam = 0
+		else
+			self:incPsi(2 + self.charged_shield/mast)
+			dam = dam - self.charged_shield
+		end
+		return dam + guaranteed_dam
+	end,
+
+
+	activate = function(self, t)
+		game:playSoundNear(self, "talents/heal")
+		local s_str = getShieldStrength(self, t)
+		return {
+			am = self:addTemporaryValue("charged_shield", s_str),
+		}
+	end,
+	deactivate = function(self, t, p)
+		local spike_str = getSpikeStrength(self, t)
+		self:removeTemporaryValue("charged_shield", p.am)
+		self:setEffect(self.EFF_CHARGESPIKE_SHIELD, 5, {power=spike_str})
+		return true
+	end,
+
+	--called when damage gets absorbed by charged shield spike
+	css_on_damage = function(self, t, damtype, dam)
+		local mast = 20 - (2*self:getTalentLevel(self.T_ABSORPTION_MASTERY) or 0) - 0.4*getGemLevel(self)
+		local total_dam = dam
+		local absorbable_dam = 0.01*(60 + math.min(self:getCun(50), 40))* total_dam
+		local guaranteed_dam = total_dam - absorbable_dam
+		dam = absorbable_dam
+		if damtype == DamageType.LIGHTNING or damtype == DamageType.BLIGHT then
+			-- Absorb damage into the shield
+			if dam <= self.chargespike_shield_absorb then
+				self.chargespike_shield_absorb = self.chargespike_shield_absorb - dam
+				self:incPsi(2 + dam/mast)
+				dam = 0
+			else
+				self:incPsi(2 + self.chargespike_shield_absorb/mast)
+				dam = dam - self.chargespike_shield_absorb
+				self.chargespike_shield_absorb = 0
+			end
+
+			if self.chargespike_shield_absorb <= 0 then
+				game.logPlayer(self, "Your spiked charged shield crumbles under the damage!")
+				self:removeEffect(self.EFF_CHARGESPIKE_SHIELD)
+			end
+			return dam + guaranteed_dam
+		else
+			return total_dam
+		end
+	end,
+
+	info = function(self, t)
+		local s_str = getShieldStrength(self, t)
+		local spike_str = getSpikeStrength(self, t)
+		local mast = 20 - (2*self:getTalentLevel(self.T_ABSORPTION_MASTERY) or 0) - 0.4*getGemLevel(self)
+		local absorb = 60 + math.min(self:getCun(50), 40)
+		return ([[Surround yourself with a shield that will absorb at most %d lightning or blight damage per attack. Deactivating the shield spikes it up to a temporary (five turns) %d point shield. The effect will increase with your Willpower stat.
+		Every time your shield absorbs damage, you gain two points of energy plus an additional point for every %d points of damage absorbed.
+		%d%% of any given attack is subject to absorption by this shield. The rest gets through as normal. Improve this by increasing Cunning.]]):
+		format(s_str, spike_str, mast, absorb)
+	end,
+}
+
+newTalent{
+	name = "Absorption Mastery",
+	type = {"psionic/absorption", 4},
+	require = psi_wil_req2,
+	points = 5,
+	mode = "passive",
+	info = function(self, t)
+		local cooldown = 2*self:getTalentLevelRaw(t)
+		local mast = 2*self:getTalentLevel(t)
+		return ([[Your expertise in the art of energy absorption grows. Shield cooldowns are all reduced by %d turns, and the amount of damage absorption required to gain a point of energy is reduced by %0.2f.]]):
+		format(cooldown, mast)
+	end,
+}
diff --git a/game/modules/tome/data/talents/psionic/augmented-mobility.lua b/game/modules/tome/data/talents/psionic/augmented-mobility.lua
index 1b07c54d9f..3530d5aab3 100644
--- a/game/modules/tome/data/talents/psionic/augmented-mobility.lua
+++ b/game/modules/tome/data/talents/psionic/augmented-mobility.lua
@@ -1,204 +1,204 @@
--- ToME - Tales of Maj'Eyal
--- Copyright (C) 2009, 2010 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 function getGemLevel(self)
-		local gem_level = 0
-		if not self:getInven("PSIONIC_FOCUS")[1] then return gem_level end
-		local tk_item = self:getInven("PSIONIC_FOCUS")[1]
-		if tk_item.type == "gem" then 
-			gem_level = tk_item.material_level
-		else
-			gem_level = 0
-		end
-		return gem_level
-end
-
-newTalent{
-	name = "Mindhook",
-	type = {"psionic/augmented-mobility", 1},
-	require = psi_wil_high1,
-	cooldown = 40,
-	psi = 20,
-	points = 5,
-	range = function(self, t)
-		local r = 2+self:getTalentLevelRaw(t)
-		local gem_level = getGemLevel(self)
-		local mult = (1 + 0.02*gem_level*(self:getTalentLevel(self.T_REACH)))
-		r = math.floor(r*mult)
-		return math.min(r, 10)
-	end,
-	action = function(self, t)
-		local tg = {type="bolt", range=self:getTalentRange(t)}
-		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
-			local nx, ny = util.findFreeGrid(self.x, self.y, 5, true, {[Map.ACTOR]=true})
-			if not nx then return end
-			target:move(nx, ny, true)
-
-		end)
-		game:playSoundNear(self, "talents/arcane")
-
-		return true
-	end,
-	info = function(self, t)
-		local range = self:getTalentRange(t)
-		return ([[Briefly extend your telekinetic reach to grab an enemy and haul them towards you.
-		Works on enemies up to %d squares away.]]):
-		format(range)
-	end,
-}
-
-
-
-newTalent{
-	name = "Quick as Thought",
-	type = {"psionic/augmented-mobility", 2},
-	points = 5,
-	random_ego = "utility",
-	cooldown = 80,
-	psi = 30,
-	require = psi_wil_high2,
-	action = function(self, t)
-		self:setEffect(self.EFF_QUICKNESS, 10+self:getWil(10), {power=self:getTalentLevel(t) * 0.1})
-		return true
-	end,
-	info = function(self, t)
-		local inc = self:getTalentLevel(t)*0.1
-		local percentinc = ((1/(1-inc))-1)*100
-		return ([[You encase your legs in precise sheathes of force, increasing your movement speed by %d%% for %d turns.]]):
-		format(percentinc, 10+self:getWil(10))
-	end,
-}
-
-
-newTalent{
-	name = "Superhuman Leap",
-	type = {"psionic/augmented-mobility", 3},
-	require = psi_wil_high3,
-	cooldown = 15,
-	psi = 10,
-	points = 5,
-	range = function(self, t)
-		local r = 2 + self:getTalentLevelRaw(t)
-		local gem_level = getGemLevel(self)
-		local mult = (1 + 0.02*gem_level*(self:getTalentLevel(self.T_REACH)))
-		r = math.floor(r*mult)
-		return math.min(r, 10)
-	end,
-	action = function(self, t)
-		local tg = {default_target=self, type="ball", nolock=true, pass_terrain=false, nowarning=true, range=self:getTalentRange(t), radius=0, requires_knowledge=false}
-		x, y = self:getTarget(tg)
-		if not x or not y then return nil end
-		-- Target code does not restrict the target coordinates to the range, it lets the project function do it
-		-- but we cant ...
-		local _ _, x, y = self:canProject(tg, x, y)
-		if self:hasLOS(x, y) and not game.level.map:checkEntity(x, y, Map.TERRAIN, "block_move") and not game.level.map:checkEntity(x, y, Map.ACTOR, "block_move") then
-			--self:teleportRandom(x, y, 0)
-			self:move(x, y, true)
-		else
-			game.logSeen(self, "You can't move there.")
-			return nil
-		end
-		return true
-	end,
-	info = function(self, t)
-		local range = self:getTalentRange(t)
-		return ([[You perform a precision, telekinetically-enhanced leap, landing up to %d squares away.]]):
-		format(range)
-	end,
-}
-
-newTalent{
-	name = "Shattering Charge",
-	type = {"psionic/augmented-mobility", 4},
-	require = psi_wil_high4,
-	points = 5,
-	random_ego = "attack",
-	psi = 60,
-	cooldown = 10,
-	tactical = {
-		ATTACK = 10,
-	},
-	range = function(self, t)
-		local r = 2 + self:getTalentLevel(t)
-		local gem_level = getGemLevel(self)
-		local mult = (1 + 0.02*gem_level*(self:getTalentLevel(self.T_REACH)))
-		r = math.floor(r*mult)
-		return math.min(r, 10)
-	end,
-	--range = function(self, t) return 3+self:getTalentLevel(t)+self:getWil(4) end,
-	direct_hit = true,
-	requires_target = true,
-	action = function(self, t)
-		if not self:hasEffect(self.EFF_KINSPIKE_SHIELD) then game.logSeen(self, "You must have a spiked kinetic shield active. Cancelling charge.") return end
-		if self:getTalentLevelRaw(t) < 5 then
-			local tg = {type="beam", range=self:getTalentRange(t), friendlyfire=false, talent=t}
-			local x, y = self:getTarget(tg)
-			if not x or not y then return nil end
-			if self:hasLOS(x, y) and not game.level.map:checkEntity(x, y, Map.TERRAIN, "block_move") and not game.level.map:checkEntity(x, y, Map.ACTOR, "block_move") then
-				local dam = self:spellCrit(self:combatTalentMindDamage(t, 20, 600))
-				self:project(tg, x, y, DamageType.BATTER, self:spellCrit(rng.avg(2*dam/3, dam, 3)))
-				local _ _, x, y = self:canProject(tg, x, y)
-				game.level.map:particleEmitter(self.x, self.y, tg.radius, "flamebeam", {tx=x-self.x, ty=y-self.y})
-				game:playSoundNear(self, "talents/lightning")
-				self:move(x, y, true)
-			else
-				game.logSeen(self, "You can't move there.")
-				return nil
-			end
-			return true
-		else
-
-			local tg = {type="beam", range=self:getTalentRange(t), nolock=true, talent=t, display={particle="bolt_earth", trail="earthtrail"}}
-			local x, y = self:getTarget(tg)
-			if not x or not y then return nil end
-			local dam = self:spellCrit(self:combatTalentMindDamage(t, 20, 600))
-
-			for i = 1, self:getTalentRange(t) do
-				self:project(tg, x, y, DamageType.DIG, 1)
-			end
-			self:project(tg, x, y, DamageType.BATTER, self:spellCrit(rng.avg(2*dam/3, dam, 3)))
-			local _ _, x, y = self:canProject(tg, x, y)
-			game.level.map:particleEmitter(self.x, self.y, tg.radius, "flamebeam", {tx=x-self.x, ty=y-self.y})
-			game:playSoundNear(self, "talents/lightning")
-			local l = line.new(self.x, self.y, x, y)
-			local lx, ly = l()
-			local tx, ty = self.x, self.y
-			lx, ly = l()
-			while lx and ly do
-				if game.level.map:checkEntity(lx, ly, engine.Map.TERRAIN, "block_move", self) then break end
-				tx, ty = lx, ly
-				lx, ly = l()
-			end
-			self:move(tx, ty, true)
-			return true		
-		end
-	end,
-	info = function(self, t)
-		local range = self:getTalentRange(t)
-		local dam = self:combatTalentMindDamage(t, 20, 600)
-		return ([[You expend massive amounts of energy to launch yourself across %d squares at incredible speed. All enemies in your path will be knocked flying and dealt between %d and %d damage. At high levels, you can batter through solid walls.
-		You must have a spiked kinetic shield erected in order to use this ability.]]):
-		format(range, 2*dam/3, dam)
-	end,
-}
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009, 2010 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 function getGemLevel(self)
+		local gem_level = 0
+		if not self:getInven("PSIONIC_FOCUS")[1] then return gem_level end
+		local tk_item = self:getInven("PSIONIC_FOCUS")[1]
+		if tk_item.type == "gem" then 
+			gem_level = tk_item.material_level
+		else
+			gem_level = 0
+		end
+		return gem_level
+end
+
+newTalent{
+	name = "Mindhook",
+	type = {"psionic/augmented-mobility", 1},
+	require = psi_wil_high1,
+	cooldown = 40,
+	psi = 20,
+	points = 5,
+	range = function(self, t)
+		local r = 2+self:getTalentLevelRaw(t)
+		local gem_level = getGemLevel(self)
+		local mult = (1 + 0.02*gem_level*(self:getTalentLevel(self.T_REACH)))
+		r = math.floor(r*mult)
+		return math.min(r, 10)
+	end,
+	action = function(self, t)
+		local tg = {type="bolt", range=self:getTalentRange(t)}
+		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
+			local nx, ny = util.findFreeGrid(self.x, self.y, 5, true, {[Map.ACTOR]=true})
+			if not nx then return end
+			target:move(nx, ny, true)
+
+		end)
+		game:playSoundNear(self, "talents/arcane")
+
+		return true
+	end,
+	info = function(self, t)
+		local range = self:getTalentRange(t)
+		return ([[Briefly extend your telekinetic reach to grab an enemy and haul them towards you.
+		Works on enemies up to %d squares away.]]):
+		format(range)
+	end,
+}
+
+
+
+newTalent{
+	name = "Quick as Thought",
+	type = {"psionic/augmented-mobility", 2},
+	points = 5,
+	random_ego = "utility",
+	cooldown = 80,
+	psi = 30,
+	require = psi_wil_high2,
+	action = function(self, t)
+		self:setEffect(self.EFF_QUICKNESS, 10+self:getWil(10), {power=self:getTalentLevel(t) * 0.1})
+		return true
+	end,
+	info = function(self, t)
+		local inc = self:getTalentLevel(t)*0.1
+		local percentinc = ((1/(1-inc))-1)*100
+		return ([[You encase your legs in precise sheathes of force, increasing your movement speed by %d%% for %d turns.]]):
+		format(percentinc, 10+self:getWil(10))
+	end,
+}
+
+
+newTalent{
+	name = "Superhuman Leap",
+	type = {"psionic/augmented-mobility", 3},
+	require = psi_wil_high3,
+	cooldown = 15,
+	psi = 10,
+	points = 5,
+	range = function(self, t)
+		local r = 2 + self:getTalentLevelRaw(t)
+		local gem_level = getGemLevel(self)
+		local mult = (1 + 0.02*gem_level*(self:getTalentLevel(self.T_REACH)))
+		r = math.floor(r*mult)
+		return math.min(r, 10)
+	end,
+	action = function(self, t)
+		local tg = {default_target=self, type="ball", nolock=true, pass_terrain=false, nowarning=true, range=self:getTalentRange(t), radius=0, requires_knowledge=false}
+		x, y = self:getTarget(tg)
+		if not x or not y then return nil end
+		-- Target code does not restrict the target coordinates to the range, it lets the project function do it
+		-- but we cant ...
+		local _ _, x, y = self:canProject(tg, x, y)
+		if self:hasLOS(x, y) and not game.level.map:checkEntity(x, y, Map.TERRAIN, "block_move") and not game.level.map:checkEntity(x, y, Map.ACTOR, "block_move") then
+			--self:teleportRandom(x, y, 0)
+			self:move(x, y, true)
+		else
+			game.logSeen(self, "You can't move there.")
+			return nil
+		end
+		return true
+	end,
+	info = function(self, t)
+		local range = self:getTalentRange(t)
+		return ([[You perform a precision, telekinetically-enhanced leap, landing up to %d squares away.]]):
+		format(range)
+	end,
+}
+
+newTalent{
+	name = "Shattering Charge",
+	type = {"psionic/augmented-mobility", 4},
+	require = psi_wil_high4,
+	points = 5,
+	random_ego = "attack",
+	psi = 60,
+	cooldown = 10,
+	tactical = {
+		ATTACK = 10,
+	},
+	range = function(self, t)
+		local r = 2 + self:getTalentLevel(t)
+		local gem_level = getGemLevel(self)
+		local mult = (1 + 0.02*gem_level*(self:getTalentLevel(self.T_REACH)))
+		r = math.floor(r*mult)
+		return math.min(r, 10)
+	end,
+	--range = function(self, t) return 3+self:getTalentLevel(t)+self:getWil(4) end,
+	direct_hit = true,
+	requires_target = true,
+	action = function(self, t)
+		if not self:hasEffect(self.EFF_KINSPIKE_SHIELD) then game.logSeen(self, "You must have a spiked kinetic shield active. Cancelling charge.") return end
+		if self:getTalentLevelRaw(t) < 5 then
+			local tg = {type="beam", range=self:getTalentRange(t), friendlyfire=false, talent=t}
+			local x, y = self:getTarget(tg)
+			if not x or not y then return nil end
+			if self:hasLOS(x, y) and not game.level.map:checkEntity(x, y, Map.TERRAIN, "block_move") and not game.level.map:checkEntity(x, y, Map.ACTOR, "block_move") then
+				local dam = self:spellCrit(self:combatTalentMindDamage(t, 20, 600))
+				self:project(tg, x, y, DamageType.BATTER, self:spellCrit(rng.avg(2*dam/3, dam, 3)))
+				local _ _, x, y = self:canProject(tg, x, y)
+				game.level.map:particleEmitter(self.x, self.y, tg.radius, "flamebeam", {tx=x-self.x, ty=y-self.y})
+				game:playSoundNear(self, "talents/lightning")
+				self:move(x, y, true)
+			else
+				game.logSeen(self, "You can't move there.")
+				return nil
+			end
+			return true
+		else
+
+			local tg = {type="beam", range=self:getTalentRange(t), nolock=true, talent=t, display={particle="bolt_earth", trail="earthtrail"}}
+			local x, y = self:getTarget(tg)
+			if not x or not y then return nil end
+			local dam = self:spellCrit(self:combatTalentMindDamage(t, 20, 600))
+
+			for i = 1, self:getTalentRange(t) do
+				self:project(tg, x, y, DamageType.DIG, 1)
+			end
+			self:project(tg, x, y, DamageType.BATTER, self:spellCrit(rng.avg(2*dam/3, dam, 3)))
+			local _ _, x, y = self:canProject(tg, x, y)
+			game.level.map:particleEmitter(self.x, self.y, tg.radius, "flamebeam", {tx=x-self.x, ty=y-self.y})
+			game:playSoundNear(self, "talents/lightning")
+			local l = line.new(self.x, self.y, x, y)
+			local lx, ly = l()
+			local tx, ty = self.x, self.y
+			lx, ly = l()
+			while lx and ly do
+				if game.level.map:checkEntity(lx, ly, engine.Map.TERRAIN, "block_move", self) then break end
+				tx, ty = lx, ly
+				lx, ly = l()
+			end
+			self:move(tx, ty, true)
+			return true		
+		end
+	end,
+	info = function(self, t)
+		local range = self:getTalentRange(t)
+		local dam = self:combatTalentMindDamage(t, 20, 600)
+		return ([[You expend massive amounts of energy to launch yourself across %d squares at incredible speed. All enemies in your path will be knocked flying and dealt between %d and %d damage. At high levels, you can batter through solid walls.
+		You must have a spiked kinetic shield erected in order to use this ability.]]):
+		format(range, 2*dam/3, dam)
+	end,
+}
diff --git a/game/modules/tome/data/talents/psionic/finer-energy-manipulations.lua b/game/modules/tome/data/talents/psionic/finer-energy-manipulations.lua
index aa62b1c577..a5dde898c3 100644
--- a/game/modules/tome/data/talents/psionic/finer-energy-manipulations.lua
+++ b/game/modules/tome/data/talents/psionic/finer-energy-manipulations.lua
@@ -1,140 +1,140 @@
--- ToME - Tales of Maj'Eyal
--- Copyright (C) 2009, 2010 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
-
-newTalent{
-	name = "Perfect Control",
-	type = {"psionic/finer-energy-manipulations", 1},
-	require = psi_cun_req1,
-	cooldown = 100,
-	psi = 15,
-	points = 5,
-	action = function(self, t)
-		self:setEffect(self.EFF_CONTROL, 5 + self:getTalentLevelRaw(t), {power=15 + math.ceil(self:getTalentLevel(t)*(1 + self:getCun(8)))})
-		return true
-	end,
-	info = function(self, t)
-		local boost = 15 + math.ceil(self:getTalentLevel(t)*(1 + self:getCun(8)))
-		local dur = 5 + self:getTalentLevelRaw(t)
-		return ([[Encase your body in a sheath of thought-quick forces, allowing you to control your body's movements directly without the inefficiency of dealing with crude mechanisms like nerves and muscles.
-		Increases attack by %d and critical strike chance by %0.2f%% for %d turns. The effect scales with Cunning.]]):
-		format(boost, 0.3*boost, dur)
-	end,
-}
-
-newTalent{
-	name = "Reshape Weapon",
-	type = {"psionic/finer-energy-manipulations", 2},
-	require = psi_cun_req2,
-	cooldown = 1,
-	psi = 100,
-	points = 5,
-	action = function(self, t)
-		self:showInventory("Reshape which weapon?", self:getInven("INVEN"), function(o) return o.type == "weapon" and not o.fully_reshaped end, function(o, item)
-			--o.wielder = o.wielder or {}
-			if (o.old_atk or 0) < math.floor(self:getTalentLevel(t)*(1 + self:getWil(4))) then
-				o.combat.atk = (o.combat.atk or 0) - (o.old_atk or 0)
-				o.combat.dam = (o.combat.dam or 0) - (o.old_dam or 0)
-				o.combat.atk = (o.combat.atk or 0) + math.floor(self:getTalentLevel(t)*(1 + self:getWil(4)))
-				o.combat.dam = (o.combat.dam or 0) + math.floor(self:getTalentLevel(t)*(1 + self:getWil(4)))
-				o.old_atk = math.floor(self:getTalentLevel(t)*(1 + self:getWil(4)))
-				o.old_dam = math.floor(self:getTalentLevel(t)*(1 + self:getWil(4)))
-				game.logPlayer(self, "You reshape your %s.", o:getName{do_colour=true, no_count=true})
-				if not o.been_reshaped then
-					o.name = "reshaped" .. " "..o.name..""
-					o.been_reshaped = true
-				end
-			else
-				game.logPlayer(self, "You cannot reshape your %s any further.", o:getName{do_colour=true, no_count=true})
-			end
-		end)
-		return true
-	end,
-	info = function(self, t)
-		return ([[Manipulate forces on the molecular level to realign, rebalance, and hone your weapon. Permanently increases the attack and damage of any weapon by %d.
-		This value scales with Willpower.]]):
-		format(math.floor(self:getTalentLevel(t)*(1 + self:getWil(4))))
-	end,
-}
-
-newTalent{
-	name = "Reshape Armor",
-	type = {"psionic/finer-energy-manipulations", 3},
-	require = psi_cun_req3,
-	cooldown = 1,
-	psi = 100,
-	points = 5,
-	action = function(self, t)
-		self:showInventory("Reshape which piece of armor?", self:getInven("INVEN"), function(o) return o.type == "armor" and not o.fully_reshaped end, function(o, item)
-			if (o.old_fat or 0) < math.ceil(0.5*self:getTalentLevel(t)*(1 + self:getWil(4))) then
-				o.wielder = o.wielder or {}
-				if not o.been_reshaped then
-					o.orig_arm = (o.wielder.combat_armor or 0)
-					o.orig_fat = (o.wielder.fatigue or 0)
-				end
-				o.wielder.combat_armor = o.orig_arm
-				o.wielder.fatigue = o.orig_fat
-				o.wielder.combat_armor = (o.wielder.combat_armor or 0) + math.ceil(0.1*self:getTalentLevel(t)*(1 + self:getWil(4)))
-				o.wielder.fatigue = (o.wielder.fatigue or 0) - math.ceil(0.5*self:getTalentLevel(t)*(1 + self:getWil(4)))
-				if o.wielder.fatigue < 0 then o.wielder.fatigue = 0 end
-				o.old_fat = math.ceil(0.5*self:getTalentLevel(t)*(1 + self:getWil(4)))
-				game.logPlayer(self, "You reshape your %s.", o:getName{do_colour=true, no_count=true})
-				if not o.been_reshaped then
-					o.name = "reshaped" .. " "..o.name..""
-					o.been_reshaped = true
-				end
-			else
-				game.logPlayer(self, "You cannot reshape your %s any further.", o:getName{do_colour=true, no_count=true})
-			end
-		end)
-		return true
-	end,
-	info = function(self, t)
-		local arm = math.ceil(0.1*self:getTalentLevel(t)*(1 + self:getWil(4)))
-		local fat = math.ceil(0.5*self:getTalentLevel(t)*(1 + self:getWil(4)))
-		return ([[Manipulate forces on the molecular level to realign, rebalance, and hone your weapon. Permanently increases the armor rating of any piece of armor by %d. Also permanently reduces the fatigue rating of any piece of armor by %d.
-		These values scale with Willpower.]]):
-		format(arm, fat)
-	end,
-}
-
-newTalent{
-	name = "Matter is Energy",
-	type = {"psionic/finer-energy-manipulations", 4},
-	require = psi_cun_req4,
-	cooldown = 50,
-	psi = 0,
-	points = 5,
-	action = function(self, t)
-		self:showInventory("Use which gem?", self:getInven("INVEN"), function(gem) return gem.type == "gem" and gem.material_level and gem.material_level == 5 end, function(gem, gem_item)
-			self:removeObject(self:getInven("INVEN"), gem_item)
-			--game.logPlayer(self, "You imbue your %s with %s.", o:getName{do_colour=true, no_count=true}, gem:getName{do_colour=true, no_count=true})
-			local quant = 30 + self:getTalentLevel(t)*self:getCun(30)
-			self:incPsi(quant)
-			self.changed = true
-		end)
-		return true
-	end,
-	info = function(self, t)
-		local quant = 30 + self:getTalentLevel(t)*self:getCun(30)
-		return ([[Matter is energy, as any good Mindslayer knows. Unfortunately, the various bonds and particles involved are just too numerous and complex to make the conversion feasible in most cases. Fortunately, the organized, crystalline structure of gems makes it possible to transform a small percentage of its matter into usable energy.
-		Turns a high-quality (material level 5) gem into %d energy. This value scales with Cunning.]]):
-		format(quant)
-	end,
-}
-
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009, 2010 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
+
+newTalent{
+	name = "Perfect Control",
+	type = {"psionic/finer-energy-manipulations", 1},
+	require = psi_cun_req1,
+	cooldown = 100,
+	psi = 15,
+	points = 5,
+	action = function(self, t)
+		self:setEffect(self.EFF_CONTROL, 5 + self:getTalentLevelRaw(t), {power=15 + math.ceil(self:getTalentLevel(t)*(1 + self:getCun(8)))})
+		return true
+	end,
+	info = function(self, t)
+		local boost = 15 + math.ceil(self:getTalentLevel(t)*(1 + self:getCun(8)))
+		local dur = 5 + self:getTalentLevelRaw(t)
+		return ([[Encase your body in a sheath of thought-quick forces, allowing you to control your body's movements directly without the inefficiency of dealing with crude mechanisms like nerves and muscles.
+		Increases attack by %d and critical strike chance by %0.2f%% for %d turns. The effect scales with Cunning.]]):
+		format(boost, 0.3*boost, dur)
+	end,
+}
+
+newTalent{
+	name = "Reshape Weapon",
+	type = {"psionic/finer-energy-manipulations", 2},
+	require = psi_cun_req2,
+	cooldown = 1,
+	psi = 100,
+	points = 5,
+	action = function(self, t)
+		self:showInventory("Reshape which weapon?", self:getInven("INVEN"), function(o) return o.type == "weapon" and not o.fully_reshaped end, function(o, item)
+			--o.wielder = o.wielder or {}
+			if (o.old_atk or 0) < math.floor(self:getTalentLevel(t)*(1 + self:getWil(4))) then
+				o.combat.atk = (o.combat.atk or 0) - (o.old_atk or 0)
+				o.combat.dam = (o.combat.dam or 0) - (o.old_dam or 0)
+				o.combat.atk = (o.combat.atk or 0) + math.floor(self:getTalentLevel(t)*(1 + self:getWil(4)))
+				o.combat.dam = (o.combat.dam or 0) + math.floor(self:getTalentLevel(t)*(1 + self:getWil(4)))
+				o.old_atk = math.floor(self:getTalentLevel(t)*(1 + self:getWil(4)))
+				o.old_dam = math.floor(self:getTalentLevel(t)*(1 + self:getWil(4)))
+				game.logPlayer(self, "You reshape your %s.", o:getName{do_colour=true, no_count=true})
+				if not o.been_reshaped then
+					o.name = "reshaped" .. " "..o.name..""
+					o.been_reshaped = true
+				end
+			else
+				game.logPlayer(self, "You cannot reshape your %s any further.", o:getName{do_colour=true, no_count=true})
+			end
+		end)
+		return true
+	end,
+	info = function(self, t)
+		return ([[Manipulate forces on the molecular level to realign, rebalance, and hone your weapon. Permanently increases the attack and damage of any weapon by %d.
+		This value scales with Willpower.]]):
+		format(math.floor(self:getTalentLevel(t)*(1 + self:getWil(4))))
+	end,
+}
+
+newTalent{
+	name = "Reshape Armor",
+	type = {"psionic/finer-energy-manipulations", 3},
+	require = psi_cun_req3,
+	cooldown = 1,
+	psi = 100,
+	points = 5,
+	action = function(self, t)
+		self:showInventory("Reshape which piece of armor?", self:getInven("INVEN"), function(o) return o.type == "armor" and not o.fully_reshaped end, function(o, item)
+			if (o.old_fat or 0) < math.ceil(0.5*self:getTalentLevel(t)*(1 + self:getWil(4))) then
+				o.wielder = o.wielder or {}
+				if not o.been_reshaped then
+					o.orig_arm = (o.wielder.combat_armor or 0)
+					o.orig_fat = (o.wielder.fatigue or 0)
+				end
+				o.wielder.combat_armor = o.orig_arm
+				o.wielder.fatigue = o.orig_fat
+				o.wielder.combat_armor = (o.wielder.combat_armor or 0) + math.ceil(0.1*self:getTalentLevel(t)*(1 + self:getWil(4)))
+				o.wielder.fatigue = (o.wielder.fatigue or 0) - math.ceil(0.5*self:getTalentLevel(t)*(1 + self:getWil(4)))
+				if o.wielder.fatigue < 0 then o.wielder.fatigue = 0 end
+				o.old_fat = math.ceil(0.5*self:getTalentLevel(t)*(1 + self:getWil(4)))
+				game.logPlayer(self, "You reshape your %s.", o:getName{do_colour=true, no_count=true})
+				if not o.been_reshaped then
+					o.name = "reshaped" .. " "..o.name..""
+					o.been_reshaped = true
+				end
+			else
+				game.logPlayer(self, "You cannot reshape your %s any further.", o:getName{do_colour=true, no_count=true})
+			end
+		end)
+		return true
+	end,
+	info = function(self, t)
+		local arm = math.ceil(0.1*self:getTalentLevel(t)*(1 + self:getWil(4)))
+		local fat = math.ceil(0.5*self:getTalentLevel(t)*(1 + self:getWil(4)))
+		return ([[Manipulate forces on the molecular level to realign, rebalance, and hone your weapon. Permanently increases the armor rating of any piece of armor by %d. Also permanently reduces the fatigue rating of any piece of armor by %d.
+		These values scale with Willpower.]]):
+		format(arm, fat)
+	end,
+}
+
+newTalent{
+	name = "Matter is Energy",
+	type = {"psionic/finer-energy-manipulations", 4},
+	require = psi_cun_req4,
+	cooldown = 50,
+	psi = 0,
+	points = 5,
+	action = function(self, t)
+		self:showInventory("Use which gem?", self:getInven("INVEN"), function(gem) return gem.type == "gem" and gem.material_level and gem.material_level == 5 end, function(gem, gem_item)
+			self:removeObject(self:getInven("INVEN"), gem_item)
+			--game.logPlayer(self, "You imbue your %s with %s.", o:getName{do_colour=true, no_count=true}, gem:getName{do_colour=true, no_count=true})
+			local quant = 30 + self:getTalentLevel(t)*self:getCun(30)
+			self:incPsi(quant)
+			self.changed = true
+		end)
+		return true
+	end,
+	info = function(self, t)
+		local quant = 30 + self:getTalentLevel(t)*self:getCun(30)
+		return ([[Matter is energy, as any good Mindslayer knows. Unfortunately, the various bonds and particles involved are just too numerous and complex to make the conversion feasible in most cases. Fortunately, the organized, crystalline structure of gems makes it possible to transform a small percentage of its matter into usable energy.
+		Turns a high-quality (material level 5) gem into %d energy. This value scales with Cunning.]]):
+		format(quant)
+	end,
+}
+
diff --git a/game/modules/tome/data/talents/psionic/focus.lua b/game/modules/tome/data/talents/psionic/focus.lua
index 0c9fd483c7..3ab4da0545 100644
--- a/game/modules/tome/data/talents/psionic/focus.lua
+++ b/game/modules/tome/data/talents/psionic/focus.lua
@@ -1,165 +1,165 @@
--- ToME - Tales of Maj'Eyal
--- Copyright (C) 2009, 2010 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
-
---Mindlash: ranged physical rad-0 ball
---Pyrokinesis: LOS burn attack
---Reach: gem-based range improvements
---Channeling: gem-based shield and improvement
-
-local function getGemLevel(self)
-		local gem_level = 0
-		if not self:getInven("PSIONIC_FOCUS")[1] then return gem_level end
-		local tk_item = self:getInven("PSIONIC_FOCUS")[1]
-		if tk_item.type == "gem" then 
-			gem_level = tk_item.material_level
-		else
-			gem_level = 0
-		end
-		return gem_level
-end
-
-
-newTalent{
-	name = "Mindlash",
-	type = {"psionic/focus", 1},
-	require = psi_wil_high1,
-	points = 5,
-	random_ego = "attack",
-	cooldown = function(self, t)
-		local c = 15
-		local gem_level = getGemLevel(self)
-		return c - gem_level
-	end,
-	psi = 15,
-	range = function(self, t)
-		local r = 5
-		local gem_level = getGemLevel(self)
-		local mult = (1 + 0.02*gem_level*(self:getTalentLevel(self.T_REACH)))
-		r = math.floor(r*mult)
-		return math.min(r, 10)
-	end,
-	getDamage = function (self, t)
-		local gem_level = getGemLevel(self)
-		return self:combatTalentIntervalDamage(t, "wil", 6, 265)*(1 + 0.3*gem_level)
-	end,
-	action = function(self, t)
-		--local gem_level = getGemLevel(self)
-		--local dam = (5 + self:getTalentLevel(t) * self:getWil(40))*(1 + 0.3*gem_level)
-		local dam = t.getDamage(self, t)
-		local tg = {type="ball", range=self:getTalentRange(t), radius=0, friendlyfire=false, talent=t}
-		local x, y = self:getTarget(tg)
-		if not x or not y then return nil end
-		self:project(tg, x, y, DamageType.PHYSICAL, self:spellCrit(rng.avg(0.8*dam, dam)), {type="flame"})
-		return true
-	end,
-	info = function(self, t)
-		--local gem_level = getGemLevel(self)
-		--local dam = (5 + self:getTalentLevel(t) * self:getWil(40))*(1 + 0.3*gem_level)
-		local dam = t.getDamage(self, t)
-		return ([[Focus energies on a distant target to lash it with physical force, doing %d damage.
-		Mindslayers do not do this sort of ranged attack naturally. The use of a telekinetically-wielded gem as a focus will improve the effects considerably.]]):
-		format(dam)
-	end,
-}
-
-newTalent{
-	name = "Pyrokinesis",
-	type = {"psionic/focus", 2},
-	require = psi_wil_high2,
-	points = 5,
-	random_ego = "attack",
-	cooldown = function(self, t)
-		local c = 20
-		local gem_level = getGemLevel(self)
-		return c - gem_level
-	end,
-	psi = 20,
-	range = function(self, t)
-		local r = 5
-		local gem_level = getGemLevel(self)
-		local mult = (1 + 0.02*gem_level*(self:getTalentLevel(self.T_REACH)))
-		r = math.floor(r*mult)
-		return math.min(r, 10)
-	end,
-	getDamage = function (self, t)
-		local gem_level = getGemLevel(self)
-		return self:combatTalentIntervalDamage(t, "wil", 21, 281)*(1 + 0.3*gem_level)
-	end,
-	action = function(self, t)
-		--local gem_level = getGemLevel(self)
-		--local dam = (20 + self:getTalentLevel(t) * self:getWil(40))*(1 + 0.3*gem_level)
-		local dam = t.getDamage(self, t)
-		local tgts = {}
-		local grids = core.fov.circle_grids(self.x, self.y, self:getTalentRange(t), 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
-
-		-- Burn each target
-		local tg = {type="hit", range=self:getTalentRange(t), talent=t}
-		local targ_num = #tgts
-		for i = 1, targ_num 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.FIREBURN, {dur=10, initial=0, dam=(20 + self:getTalentLevel(t) * self:getWil(40))*(1 + 0.3*gem_level)})
-			game.level.map:particleEmitter(a.x, a.y, tg.radius, "ball_fire", {radius=1})
-		end
-
-		return true
-	end,
-	info = function(self, t)
-		local range = self:getTalentRange(t)
-		--local gem_level = getGemLevel(self)
-		--local dam = (20 + self:getTalentLevel(t) * self:getWil(40))*(1 + 0.3*gem_level)
-		local dam = t.getDamage(self, t)
-		return ([[Focus energies on all targets within %d squares, setting them ablaze. Does %d damage over ten turns.
-		Mindslayers do not do this sort of ranged attack naturally. The use of a telekinetically-wielded gem as a focus will improve the effects considerably.]]):
-		format(range, dam)
-	end,
-}
-
-newTalent{
-	name = "Reach",
-	type = {"psionic/focus", 3},
-	require = psi_wil_high3,
-	mode = "passive",
-	points = 5,
-	info = function(self, t)
-		local inc = 2*self:getTalentLevel(t)
-		return ([[You can extend your mental reach beyond your natural limits using a telekinetically-wielded gemstone as a focus. Increases the range of various abilities by %d%% to %d%%, depending on the quality of the gem used as a focus.]]):
-		format(inc, 5*inc)
-	end,
-}
-
-newTalent{
-	name = "Focused Channeling",
-	type = {"psionic/focus", 4},
-	require = psi_wil_high4,
-	mode = "passive",
-	points = 5,
-	info = function(self, t)
-		local inc = 1 + 0.1*self:getTalentLevel(t)
-		return ([[You can channel more energy with your auras and shields using a telekinetically-wielded gemstone as a focus. Increases the base strength of all auras and shields by %0.2f to %0.2f, depending on the quality of the gem used as a focus.]]):
-		format(inc, 5*inc)
-	end,
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009, 2010 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
+
+--Mindlash: ranged physical rad-0 ball
+--Pyrokinesis: LOS burn attack
+--Reach: gem-based range improvements
+--Channeling: gem-based shield and improvement
+
+local function getGemLevel(self)
+		local gem_level = 0
+		if not self:getInven("PSIONIC_FOCUS")[1] then return gem_level end
+		local tk_item = self:getInven("PSIONIC_FOCUS")[1]
+		if tk_item.type == "gem" then 
+			gem_level = tk_item.material_level
+		else
+			gem_level = 0
+		end
+		return gem_level
+end
+
+
+newTalent{
+	name = "Mindlash",
+	type = {"psionic/focus", 1},
+	require = psi_wil_high1,
+	points = 5,
+	random_ego = "attack",
+	cooldown = function(self, t)
+		local c = 15
+		local gem_level = getGemLevel(self)
+		return c - gem_level
+	end,
+	psi = 15,
+	range = function(self, t)
+		local r = 5
+		local gem_level = getGemLevel(self)
+		local mult = (1 + 0.02*gem_level*(self:getTalentLevel(self.T_REACH)))
+		r = math.floor(r*mult)
+		return math.min(r, 10)
+	end,
+	getDamage = function (self, t)
+		local gem_level = getGemLevel(self)
+		return self:combatTalentIntervalDamage(t, "wil", 6, 265)*(1 + 0.3*gem_level)
+	end,
+	action = function(self, t)
+		--local gem_level = getGemLevel(self)
+		--local dam = (5 + self:getTalentLevel(t) * self:getWil(40))*(1 + 0.3*gem_level)
+		local dam = t.getDamage(self, t)
+		local tg = {type="ball", range=self:getTalentRange(t), radius=0, friendlyfire=false, talent=t}
+		local x, y = self:getTarget(tg)
+		if not x or not y then return nil end
+		self:project(tg, x, y, DamageType.PHYSICAL, self:spellCrit(rng.avg(0.8*dam, dam)), {type="flame"})
+		return true
+	end,
+	info = function(self, t)
+		--local gem_level = getGemLevel(self)
+		--local dam = (5 + self:getTalentLevel(t) * self:getWil(40))*(1 + 0.3*gem_level)
+		local dam = t.getDamage(self, t)
+		return ([[Focus energies on a distant target to lash it with physical force, doing %d damage.
+		Mindslayers do not do this sort of ranged attack naturally. The use of a telekinetically-wielded gem as a focus will improve the effects considerably.]]):
+		format(dam)
+	end,
+}
+
+newTalent{
+	name = "Pyrokinesis",
+	type = {"psionic/focus", 2},
+	require = psi_wil_high2,
+	points = 5,
+	random_ego = "attack",
+	cooldown = function(self, t)
+		local c = 20
+		local gem_level = getGemLevel(self)
+		return c - gem_level
+	end,
+	psi = 20,
+	range = function(self, t)
+		local r = 5
+		local gem_level = getGemLevel(self)
+		local mult = (1 + 0.02*gem_level*(self:getTalentLevel(self.T_REACH)))
+		r = math.floor(r*mult)
+		return math.min(r, 10)
+	end,
+	getDamage = function (self, t)
+		local gem_level = getGemLevel(self)
+		return self:combatTalentIntervalDamage(t, "wil", 21, 281)*(1 + 0.3*gem_level)
+	end,
+	action = function(self, t)
+		--local gem_level = getGemLevel(self)
+		--local dam = (20 + self:getTalentLevel(t) * self:getWil(40))*(1 + 0.3*gem_level)
+		local dam = t.getDamage(self, t)
+		local tgts = {}
+		local grids = core.fov.circle_grids(self.x, self.y, self:getTalentRange(t), 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
+
+		-- Burn each target
+		local tg = {type="hit", range=self:getTalentRange(t), talent=t}
+		local targ_num = #tgts
+		for i = 1, targ_num 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.FIREBURN, {dur=10, initial=0, dam=(20 + self:getTalentLevel(t) * self:getWil(40))*(1 + 0.3*gem_level)})
+			game.level.map:particleEmitter(a.x, a.y, tg.radius, "ball_fire", {radius=1})
+		end
+
+		return true
+	end,
+	info = function(self, t)
+		local range = self:getTalentRange(t)
+		--local gem_level = getGemLevel(self)
+		--local dam = (20 + self:getTalentLevel(t) * self:getWil(40))*(1 + 0.3*gem_level)
+		local dam = t.getDamage(self, t)
+		return ([[Focus energies on all targets within %d squares, setting them ablaze. Does %d damage over ten turns.
+		Mindslayers do not do this sort of ranged attack naturally. The use of a telekinetically-wielded gem as a focus will improve the effects considerably.]]):
+		format(range, dam)
+	end,
+}
+
+newTalent{
+	name = "Reach",
+	type = {"psionic/focus", 3},
+	require = psi_wil_high3,
+	mode = "passive",
+	points = 5,
+	info = function(self, t)
+		local inc = 2*self:getTalentLevel(t)
+		return ([[You can extend your mental reach beyond your natural limits using a telekinetically-wielded gemstone as a focus. Increases the range of various abilities by %d%% to %d%%, depending on the quality of the gem used as a focus.]]):
+		format(inc, 5*inc)
+	end,
+}
+
+newTalent{
+	name = "Focused Channeling",
+	type = {"psionic/focus", 4},
+	require = psi_wil_high4,
+	mode = "passive",
+	points = 5,
+	info = function(self, t)
+		local inc = 1 + 0.1*self:getTalentLevel(t)
+		return ([[You can channel more energy with your auras and shields using a telekinetically-wielded gemstone as a focus. Increases the base strength of all auras and shields by %0.2f to %0.2f, depending on the quality of the gem used as a focus.]]):
+		format(inc, 5*inc)
+	end,
 }
\ No newline at end of file
diff --git a/game/modules/tome/data/talents/psionic/projection.lua b/game/modules/tome/data/talents/psionic/projection.lua
index d05a2009f9..6143749fb4 100644
--- a/game/modules/tome/data/talents/psionic/projection.lua
+++ b/game/modules/tome/data/talents/psionic/projection.lua
@@ -1,425 +1,428 @@
--- ToME - Tales of Maj'Eyal
--- Copyright (C) 2009, 2010 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 function getGemLevel(self)
-		local gem_level = 0
-		if not self:getInven("PSIONIC_FOCUS")[1] then return gem_level end
-		local tk_item = self:getInven("PSIONIC_FOCUS")[1]
-		if tk_item.type == "gem" then 
-			gem_level = tk_item.material_level
-		else
-			gem_level = 0
-		end
-		return gem_level
-end
-
-local function combatTalentDamage(self, t, min, max)
-	return self:combatTalentSpellDamage(t, min, max, self.level + self:getWil())
-end
-
--- damage: initial physical damage and used for fractional knockback damage
--- knockback: distance to knockback
--- knockbackDamage: when knockback strikes something, both parties take damage - percent of damage * remaining knockback
--- power: used to determine the initial radius of particles
-local function forceHit(self, target, sourceX, sourceY, damage, knockback, knockbackDamage, power)
-	-- apply initial damage
-	if not target then return end
-	if damage > 0 then
-		self:project(target, target.x, target.y, DamageType.PHYSICAL, damage)
-		game.level.map:particleEmitter(target.x, target.y, 1, "force_hit", {power=power, dx=target.x - sourceX, dy=target.y - sourceY})
-	end
-	
-	-- knockback?
-	if not target.dead and knockback and knockback > 0 and target:canBe("knockback") and (target.never_move or 0) < 1 then
-		-- give direct hit a direction?
-		if sourceX == target.x and sourceY == target.y then
-			local newDirection = rng.range(1, 8)
-			sourceX = sourceX + dir_to_coord[newDirection][1]
-			sourceY = sourceY + dir_to_coord[newDirection][2]
-		end
-	
-		local lineFunction = line.new(sourceX, sourceY, target.x, target.y, true)
-		local finalX, finalY = target.x, target.y
-		local knockbackCount = 0
-		local blocked = false
-		while knockback > 0 do
-			blocked = true
-			local x, y = lineFunction(true)
-			
-			if not game.level.map:isBound(x, y) or game.level.map:checkAllEntities(x, y, "block_move", target) then
-				-- blocked
-				local nextTarget = game.level.map(x, y, Map.ACTOR)
-				if nextTarget then
-					if knockbackCount > 0 then
-						game.logPlayer(self, "%s was blasted %d spaces into %s!", target.name:capitalize(), knockbackCount, nextTarget.name)
-					else
-						game.logPlayer(self, "%s was blasted into %s!", target.name:capitalize(), nextTarget.name)
-					end
-				elseif knockbackCount > 0 then
-					game.logPlayer(self, "%s was smashed back %d spaces!", target.name:capitalize(), knockbackCount)
-				else
-					game.logPlayer(self, "%s was smashed!", target.name:capitalize())
-				end
-				
-				-- take partial damage
-				local blockDamage = damage * knockback * knockbackDamage / 100
-				self:project(target, target.x, target.y, DamageType.PHYSICAL, blockDamage)
-				
-				if nextTarget then
-					-- start a new force hit with the knockback damage and current knockback
-					
-					forceHit(self, nextTarget, sourceX, sourceY, blockDamage, knockback, knockbackDamage, power / 2)
-				end
-				
-				knockback = 0
-			else
-				-- allow move
-				finalX, finalY = x, y
-				knockback = knockback - 1
-				knockbackCount = knockbackCount + 1
-			end
-		end
-		
-		if not blocked and knockbackCount > 0 then
-			game.logPlayer(self, "%s was blasted back %d spaces!", target.name:capitalize())
-		end
-		
-		if not target.dead and (finalX ~= target.x or finalY ~= target.y) then
-			target:move(finalX, finalY, true)
-		end
-	end
-end
-
-newTalent{
-	name = "Kinetic Aura",
-	type = {"psionic/projection", 1},
-	require = psi_wil_req1, no_sustain_autoreset = true,
-	points = 5,
-	mode = "sustained",
-	sustain_psi = 30,
-	cooldown = function(self, t)
-		return 15 - (self:getTalentLevelRaw(self.T_PROJECTION_MASTERY) or 0)
-	end,
-	tactical = {
-		ATTACKAREA = 10,
-	},
-	range = 1,
-	direct_hit = true,
-	getAuraStrength = function(self, t)
-		local add = 0
-		if self:knowTalent(self.T_FOCUSED_CHANNELING) then
-			add = getGemLevel(self)*(1 + 0.1*(self:getTalentLevel(self.T_FOCUSED_CHANNELING) or 0))
-		end
-		--return 5 + (1+ self:getWil(5))*self:getTalentLevel(t) + add
-		return self:combatTalentIntervalDamage(t, "wil", 6, 40) + add
-	end,
-	getKnockback = function(self, t)
-		return 3 + math.floor(self:getTalentLevel(t))
-	end,
-	do_kineticaura = function(self, t)
-
-		local mast = 3 + (self:getTalentLevel(self.T_PROJECTION_MASTERY) or 0) + getGemLevel(self)
-		local dam = t.getAuraStrength(self, t)
-		local tgts = {}
-		local grids = core.fov.circle_grids(self.x, self.y, 1, 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
-
-		-- Randomly take targets
-		local tg = {type="hit", range=1, talent=t}
-		for i = 1, 10 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.PHYSICAL, dam)
-			--game.level.map:particleEmitter(self.x, self.y, math.max(math.abs(a.x-self.x), math.abs(a.y-self.y)), "lightning", {tx=a.x-self.x, ty=a.y-self.y})
-			self:incPsi(-dam/mast)
-		end
-
-	end,
-	activate = function(self, t)
-		return true
-	end,
-	deactivate = function(self, t, p)
-		local dam = 50 + 0.25 * t.getAuraStrength(self, t)*t.getAuraStrength(self, t)
-		local cost = t.sustain_psi - 2*getGemLevel(self)
-		if self:getPsi() <= cost then 
-			game.logPlayer(self, "The aura dissipates without producing a spike.")
-			return true 
-		end
-		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)) > 1 then return nil end
-		local knockback = t.getKnockback(self, t)
-		forceHit(self, target, self.x, self.y, dam, knockback, 15, 1)		
-		--self:project(tg, x, y, DamageType.BATTER, dam)
-		self:incPsi(-cost)
-		
-		return true
-	end,
-
-	info = function(self, t)
-		local dam = t.getAuraStrength(self, t)
-		local spikedam = 50 + 0.25 * dam * dam
-		local mast = 3 + (self:getTalentLevel(self.T_PROJECTION_MASTERY) or 0) + getGemLevel(self)
-		local spikecost = t.sustain_psi - 2*getGemLevel(self)
-		return ([[Fills the air around you with reactive currents of force that do %d physical damage to all who approach. All damage done by the aura will drain one point of energy per %0.2f points of damage dealt.
-		When deactivated, if you have at least %d energy, a massive spike of kinetic energy is released, smashing a target for %d physical damage and sending it flying. Telekinetically wielding a gem instead of a weapon will result in improved spike efficiency.
-		The damage will increase with the Willpower stat.]]):format(dam, mast, spikecost, spikedam)
-	end,
-}
-
-
-newTalent{
-	name = "Thermal Aura",
-	type = {"psionic/projection", 2},
-	require = psi_wil_req2, no_sustain_autoreset = true,
-	points = 5,
-	mode = "sustained",
-	sustain_psi = 40,
-	cooldown = function(self, t)
-		return 15 - (self:getTalentLevelRaw(self.T_PROJECTION_MASTERY) or 0)
-	end,
-	tactical = {
-		ATTACKAREA = 10,
-	},
-	range = function(self, t)
-		local r = 6
-		local gem_level = getGemLevel(self)
-		local mult = (1 + 0.02*gem_level*(self:getTalentLevel(self.T_REACH)))
-		r = math.floor(r*mult)
-		return math.min(r, 10)
-	end,
-	direct_hit = true,
-	getAuraStrength = function(self, t)
-		local add = 0
-		if self:knowTalent(self.T_FOCUSED_CHANNELING) then
-			add = getGemLevel(self)*(1 + 0.1*(self:getTalentLevel(self.T_FOCUSED_CHANNELING) or 0))
-		end
-		--return 5 + (1+ self:getWil(5))*self:getTalentLevel(t) + add
-		return self:combatTalentIntervalDamage(t, "wil", 6, 40) + add
-	end,
-	do_thermalaura = function(self, t)
-
-		local mast = 3 + (self:getTalentLevel(self.T_PROJECTION_MASTERY) or 0) + getGemLevel(self)
-		local dam = t.getAuraStrength(self, t)
-		local tgts = {}
-		local grids = core.fov.circle_grids(self.x, self.y, 1, 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
-
-		-- Randomly take targets
-		local tg = {type="hit", range=1, talent=t}
-		for i = 1, 10 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.FIRE, dam)
-			--game.level.map:particleEmitter(self.x, self.y, math.max(math.abs(a.x-self.x), math.abs(a.y-self.y)), "lightning", {tx=a.x-self.x, ty=a.y-self.y})
-			self:incPsi(-dam/mast)
-		end
-
-	end,
-	activate = function(self, t)
-		return true
-	end,
-	deactivate = function(self, t, p)
-		local dam = 50 + 0.4 * t.getAuraStrength(self, t)*t.getAuraStrength(self, t)
-		local cost = t.sustain_psi - 2*getGemLevel(self)
-		--if self:isTalentActive(self.T_CONDUIT) then return true end
-		if self:getPsi() <= cost then 
-			game.logPlayer(self, "The aura dissipates without producing a spike.")
-			return true 
-		end
-
-		local tg = {type="beam", range=self:getTalentRange(t), talent=t, display={particle="bolt_fire", trail="firetrail"}}
-		local x, y = self:getTarget(tg)
-		if not x or not y then return nil end
-		self:project(tg, x, y, DamageType.FIREBURN, self:spellCrit(rng.avg(0.8*dam, dam)))
-		local _ _, x, y = self:canProject(tg, x, y)
-		game.level.map:particleEmitter(self.x, self.y, tg.radius, "flamebeam", {tx=x-self.x, ty=y-self.y})
-		
-		game:playSoundNear(self, "talents/fire")
-		self:incPsi(-cost)
-		return true
-	end,
-
-	info = function(self, t)
-		local dam = t.getAuraStrength(self, t)
-		local spikedam = 50 + 0.4 * dam * dam
-		local mast = 3 + (self:getTalentLevel(self.T_PROJECTION_MASTERY) or 0) + getGemLevel(self)
-		local spikecost = t.sustain_psi - 2*getGemLevel(self)
-		return ([[Fills the air around you with reactive currents of furnace-like heat that do %d fire damage to all who approach. All damage done by the aura will drain one point of energy per %0.2f points of damage dealt.
-		When deactivated, if you have at least %d energy, a massive spike of thermal energy is released as a tunnel of superheated air. Anybody caught in it will suffer %d fire damage. Telekinetically wielding a gem instead of a weapon will result in improved spike efficiency.
-		The damage will increase with the Willpower stat.]]):format(dam, mast, spikecost, spikedam)
-	end,
-}
-
-
-newTalent{
-	name = "Charged Aura",
-	type = {"psionic/projection", 3},
-	require = psi_wil_req3, no_sustain_autoreset = true,
-	points = 5,
-	mode = "sustained",
-	sustain_psi = 50,
-	cooldown = function(self, t)
-		return 15 - (self:getTalentLevelRaw(self.T_PROJECTION_MASTERY) or 0)
-	end,
-	tactical = {
-		ATTACKAREA = 10,
-	},
-	range = function(self, t)
-		local r = 6
-		local gem_level = getGemLevel(self)
-		local mult = (1 + 0.02*gem_level*(self:getTalentLevel(self.T_REACH)))
-		r = math.floor(r*mult)
-		return math.min(r, 10)
-	end,
-	direct_hit = true,
-	getAuraStrength = function(self, t)
-		local add = 0
-		if self:knowTalent(self.T_FOCUSED_CHANNELING) then
-			add = getGemLevel(self)*(1 + 0.1*(self:getTalentLevel(self.T_FOCUSED_CHANNELING) or 0))
-		end
-		--return 5 + (1+ self:getWil(5))*self:getTalentLevel(t) + add
-		return self:combatTalentIntervalDamage(t, "wil", 6, 40) + add
-	end,
-	do_chargedaura = function(self, t)
-		local mast = 3 + (self:getTalentLevel(self.T_PROJECTION_MASTERY) or 0) + getGemLevel(self)
-		local dam = t.getAuraStrength(self, t)
-		local tgts = {}
-		local grids = core.fov.circle_grids(self.x, self.y, 1, 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
-
-		-- Randomly take targets
-		local tg = {type="hit", range=1, talent=t}
-		for i = 1, 10 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.LIGHTNING, dam)
-			--game.level.map:particleEmitter(self.x, self.y, math.max(math.abs(a.x-self.x), math.abs(a.y-self.y)), "lightning", {tx=a.x-self.x, ty=a.y-self.y})
-			self:incPsi(-dam/mast)
-		end
-	end,
-	activate = function(self, t)
-		game:playSoundNear(self, "talents/thunderstorm")
-		return true
-	end,
-	deactivate = function(self, t, p)
-		local dam = 50 + 0.4 * t.getAuraStrength(self, t)*t.getAuraStrength(self, t)
-		local cost = t.sustain_psi - 2*getGemLevel(self)
-		--if self:isTalentActive(self.T_CONDUIT) then return true end
-		if self:getPsi() <= cost then 
-			game.logPlayer(self, "The aura dissipates without producing a spike.")
-			return true 
-		end
-		
-		local tg = {type="bolt", range=self:getTalentRange(t), talent=t}
-		local fx, fy = self:getTarget(tg)
-		if not fx or not fy then return nil end
-
-		local nb = 1 + math.floor(0.5*self:getTalentLevel(t)) + getGemLevel(self)
-		local affected = {}
-		local first = nil
-		--Here's the part where deactivating the aura fires off a huge chain lightning
-		self:project(tg, fx, fy, function(dx, dy)
-			print("[Chain lightning] targetting", fx, fy, "from", self.x, self.y)
-			local actor = game.level.map(dx, dy, Map.ACTOR)
-			if actor and not affected[actor] then
-				ignored = false
-				affected[actor] = true
-				first = actor
-
-				print("[Chain lightning] looking for more targets", nb, " at ", dx, dy, "radius ", 10, "from", actor.name)
-				self:project({type="ball", friendlyfire=false, x=dx, y=dy, radius=self:getTalentRange(t), range=0}, dx, dy, function(bx, by)
-					local actor = game.level.map(bx, by, Map.ACTOR)
-					if actor and not affected[actor] and self:reactionToward(actor) < 0 then
-						print("[Chain lightning] found possible actor", actor.name, bx, by, "distance", core.fov.distance(dx, dy, bx, by))
-						affected[actor] = true
-					end
-				end)
-				return true
-			end
-		end)
-
-		if not first then return true end
-		local targets = { first }
-		affected[first] = nil
-		local possible_targets = table.listify(affected)
-		print("[Chain lightning] Found targets:", #possible_targets)
-		for i = 2, nb do
-			if #possible_targets == 0 then break end
-			local act = rng.tableRemove(possible_targets)
-			targets[#targets+1] = act[1]
-		end
-
-		local sx, sy = self.x, self.y
-		for i, actor in ipairs(targets) do
-			local tgr = {type="beam", range=self:getTalentRange(t), friendlyfire=false, talent=t, x=sx, y=sy}
-			print("[Chain lightning] jumping from", sx, sy, "to", actor.x, actor.y)
-			self:project(tgr, actor.x, actor.y, DamageType.LIGHTNING, self:spellCrit(rng.avg(0.8*dam, dam)))
-			game.level.map:particleEmitter(sx, sy, math.max(math.abs(actor.x-sx), math.abs(actor.y-sy)), "lightning", {tx=actor.x-sx, ty=actor.y-sy, nb_particles=150, life=6})
-			sx, sy = actor.x, actor.y
-		end
-		game:playSoundNear(self, "talents/lightning")
-		self:incPsi(-cost)
-		return true
-	end,
-
-	info = function(self, t)
-		local dam = t.getAuraStrength(self, t)
-		local spikedam = 50 + 0.4 * dam * dam
-		local mast = 3 + (self:getTalentLevel(self.T_PROJECTION_MASTERY) or 0) + getGemLevel(self)
-		local spikecost = t.sustain_psi - 2*getGemLevel(self)
-		local nb = 3 + self:getTalentLevelRaw(t)
-		return ([[Fills the air around you with crackling energy, doing %d lightning damage to all who stand nearby. All damage done by the aura will drain one point of energy per %0.2f points of damage dealt.
-		When deactivated, if you have at least %d energy, a massive spike of electrical energy jumps between up to %d nearby targets, doing %d lightning damage to each. Telekinetically wielding a gem instead of a weapon will result in improved spike efficiency.
-		The damage will increase with the Willpower stat.]]):format(dam, mast, spikecost, nb, spikedam)
-	end,
-}
-
-newTalent{
-	name = "Projection Mastery",
-	type = {"psionic/projection", 4},
-	require = psi_wil_req4,
-	points = 5,
-	mode = "passive",
-	info = function(self, t)
-		local cooldown = self:getTalentLevelRaw(t)
-		local mast = (self:getTalentLevel(t) or 0)
-		return ([[Your expertise in the art of energy projection grows.
-		Aura cooldowns are all reduced by %d turns. Aura damage drains energy more slowly (+%0.2f damage required to lose a point of energy).]]):format(cooldown, mast)
-	end,
-}
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009, 2010 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 function getGemLevel(self)
+		local gem_level = 0
+		if not self:getInven("PSIONIC_FOCUS")[1] then return gem_level end
+		local tk_item = self:getInven("PSIONIC_FOCUS")[1]
+		if tk_item.type == "gem" then 
+			gem_level = tk_item.material_level
+		else
+			gem_level = 0
+		end
+		return gem_level
+end
+
+local function combatTalentDamage(self, t, min, max)
+	return self:combatTalentSpellDamage(t, min, max, self.level + self:getWil())
+end
+
+-- damage: initial physical damage and used for fractional knockback damage
+-- knockback: distance to knockback
+-- knockbackDamage: when knockback strikes something, both parties take damage - percent of damage * remaining knockback
+-- power: used to determine the initial radius of particles
+local function forceHit(self, target, sourceX, sourceY, damage, knockback, knockbackDamage, power)
+	-- apply initial damage
+	if not target then return end
+	if damage > 0 then
+		self:project(target, target.x, target.y, DamageType.PHYSICAL, damage)
+		game.level.map:particleEmitter(target.x, target.y, 1, "force_hit", {power=power, dx=target.x - sourceX, dy=target.y - sourceY})
+	end
+	
+	-- knockback?
+	if not target.dead and knockback and knockback > 0 and target:canBe("knockback") and (target.never_move or 0) < 1 then
+		-- give direct hit a direction?
+		if sourceX == target.x and sourceY == target.y then
+			local newDirection = rng.range(1, 8)
+			sourceX = sourceX + dir_to_coord[newDirection][1]
+			sourceY = sourceY + dir_to_coord[newDirection][2]
+		end
+	
+		local lineFunction = line.new(sourceX, sourceY, target.x, target.y, true)
+		local finalX, finalY = target.x, target.y
+		local knockbackCount = 0
+		local blocked = false
+		while knockback > 0 do
+			blocked = true
+			local x, y = lineFunction(true)
+			
+			if not game.level.map:isBound(x, y) or game.level.map:checkAllEntities(x, y, "block_move", target) then
+				-- blocked
+				local nextTarget = game.level.map(x, y, Map.ACTOR)
+				if nextTarget then
+					if knockbackCount > 0 then
+						game.logPlayer(self, "%s was blasted %d spaces into %s!", target.name:capitalize(), knockbackCount, nextTarget.name)
+					else
+						game.logPlayer(self, "%s was blasted into %s!", target.name:capitalize(), nextTarget.name)
+					end
+				elseif knockbackCount > 0 then
+					game.logPlayer(self, "%s was smashed back %d spaces!", target.name:capitalize(), knockbackCount)
+				else
+					game.logPlayer(self, "%s was smashed!", target.name:capitalize())
+				end
+				
+				-- take partial damage
+				local blockDamage = damage * knockback * knockbackDamage / 100
+				self:project(target, target.x, target.y, DamageType.PHYSICAL, blockDamage)
+				
+				if nextTarget then
+					-- start a new force hit with the knockback damage and current knockback
+					
+					forceHit(self, nextTarget, sourceX, sourceY, blockDamage, knockback, knockbackDamage, power / 2)
+				end
+				
+				knockback = 0
+			else
+				-- allow move
+				finalX, finalY = x, y
+				knockback = knockback - 1
+				knockbackCount = knockbackCount + 1
+			end
+		end
+		
+		if not blocked and knockbackCount > 0 then
+			game.logPlayer(self, "%s was blasted back %d spaces!", target.name:capitalize())
+		end
+		
+		if not target.dead and (finalX ~= target.x or finalY ~= target.y) then
+			target:move(finalX, finalY, true)
+		end
+	end
+end
+
+newTalent{
+	name = "Kinetic Aura",
+	type = {"psionic/projection", 1},
+	require = psi_wil_req1, no_sustain_autoreset = true,
+	points = 5,
+	mode = "sustained",
+	sustain_psi = 30,
+	remove_on_zero = true,
+	cooldown = function(self, t)
+		return 15 - (self:getTalentLevelRaw(self.T_PROJECTION_MASTERY) or 0)
+	end,
+	tactical = {
+		ATTACKAREA = 10,
+	},
+	range = 1,
+	direct_hit = true,
+	getAuraStrength = function(self, t)
+		local add = 0
+		if self:knowTalent(self.T_FOCUSED_CHANNELING) then
+			add = getGemLevel(self)*(1 + 0.1*(self:getTalentLevel(self.T_FOCUSED_CHANNELING) or 0))
+		end
+		--return 5 + (1+ self:getWil(5))*self:getTalentLevel(t) + add
+		return self:combatTalentIntervalDamage(t, "wil", 6, 40) + add
+	end,
+	getKnockback = function(self, t)
+		return 3 + math.floor(self:getTalentLevel(t))
+	end,
+	do_kineticaura = function(self, t)
+
+		local mast = 3 + (self:getTalentLevel(self.T_PROJECTION_MASTERY) or 0) + getGemLevel(self)
+		local dam = t.getAuraStrength(self, t)
+		local tgts = {}
+		local grids = core.fov.circle_grids(self.x, self.y, 1, 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
+
+		-- Randomly take targets
+		local tg = {type="hit", range=1, talent=t}
+		for i = 1, 10 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.PHYSICAL, dam)
+			--game.level.map:particleEmitter(self.x, self.y, math.max(math.abs(a.x-self.x), math.abs(a.y-self.y)), "lightning", {tx=a.x-self.x, ty=a.y-self.y})
+			self:incPsi(-dam/mast)
+		end
+
+	end,
+	activate = function(self, t)
+		return true
+	end,
+	deactivate = function(self, t, p)
+		local dam = 50 + 0.25 * t.getAuraStrength(self, t)*t.getAuraStrength(self, t)
+		local cost = t.sustain_psi - 2*getGemLevel(self)
+		if self:getPsi() <= cost then 
+			game.logPlayer(self, "The aura dissipates without producing a spike.")
+			return true 
+		end
+		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)) > 1 then return nil end
+		local knockback = t.getKnockback(self, t)
+		forceHit(self, target, self.x, self.y, dam, knockback, 15, 1)		
+		--self:project(tg, x, y, DamageType.BATTER, dam)
+		self:incPsi(-cost)
+		
+		return true
+	end,
+
+	info = function(self, t)
+		local dam = t.getAuraStrength(self, t)
+		local spikedam = 50 + 0.25 * dam * dam
+		local mast = 3 + (self:getTalentLevel(self.T_PROJECTION_MASTERY) or 0) + getGemLevel(self)
+		local spikecost = t.sustain_psi - 2*getGemLevel(self)
+		return ([[Fills the air around you with reactive currents of force that do %d physical damage to all who approach. All damage done by the aura will drain one point of energy per %0.2f points of damage dealt.
+		When deactivated, if you have at least %d energy, a massive spike of kinetic energy is released, smashing a target for %d physical damage and sending it flying. Telekinetically wielding a gem instead of a weapon will result in improved spike efficiency.
+		The damage will increase with the Willpower stat.]]):format(dam, mast, spikecost, spikedam)
+	end,
+}
+
+
+newTalent{
+	name = "Thermal Aura",
+	type = {"psionic/projection", 2},
+	require = psi_wil_req2, no_sustain_autoreset = true,
+	points = 5,
+	mode = "sustained",
+	sustain_psi = 40,
+	remove_on_zero = true,
+	cooldown = function(self, t)
+		return 15 - (self:getTalentLevelRaw(self.T_PROJECTION_MASTERY) or 0)
+	end,
+	tactical = {
+		ATTACKAREA = 10,
+	},
+	range = function(self, t)
+		local r = 6
+		local gem_level = getGemLevel(self)
+		local mult = (1 + 0.02*gem_level*(self:getTalentLevel(self.T_REACH)))
+		r = math.floor(r*mult)
+		return math.min(r, 10)
+	end,
+	direct_hit = true,
+	getAuraStrength = function(self, t)
+		local add = 0
+		if self:knowTalent(self.T_FOCUSED_CHANNELING) then
+			add = getGemLevel(self)*(1 + 0.1*(self:getTalentLevel(self.T_FOCUSED_CHANNELING) or 0))
+		end
+		--return 5 + (1+ self:getWil(5))*self:getTalentLevel(t) + add
+		return self:combatTalentIntervalDamage(t, "wil", 6, 40) + add
+	end,
+	do_thermalaura = function(self, t)
+
+		local mast = 3 + (self:getTalentLevel(self.T_PROJECTION_MASTERY) or 0) + getGemLevel(self)
+		local dam = t.getAuraStrength(self, t)
+		local tgts = {}
+		local grids = core.fov.circle_grids(self.x, self.y, 1, 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
+
+		-- Randomly take targets
+		local tg = {type="hit", range=1, talent=t}
+		for i = 1, 10 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.FIRE, dam)
+			--game.level.map:particleEmitter(self.x, self.y, math.max(math.abs(a.x-self.x), math.abs(a.y-self.y)), "lightning", {tx=a.x-self.x, ty=a.y-self.y})
+			self:incPsi(-dam/mast)
+		end
+
+	end,
+	activate = function(self, t)
+		return true
+	end,
+	deactivate = function(self, t, p)
+		local dam = 50 + 0.4 * t.getAuraStrength(self, t)*t.getAuraStrength(self, t)
+		local cost = t.sustain_psi - 2*getGemLevel(self)
+		--if self:isTalentActive(self.T_CONDUIT) then return true end
+		if self:getPsi() <= cost then 
+			game.logPlayer(self, "The aura dissipates without producing a spike.")
+			return true 
+		end
+
+		local tg = {type="beam", range=self:getTalentRange(t), talent=t, display={particle="bolt_fire", trail="firetrail"}}
+		local x, y = self:getTarget(tg)
+		if not x or not y then return nil end
+		self:project(tg, x, y, DamageType.FIREBURN, self:spellCrit(rng.avg(0.8*dam, dam)))
+		local _ _, x, y = self:canProject(tg, x, y)
+		game.level.map:particleEmitter(self.x, self.y, tg.radius, "flamebeam", {tx=x-self.x, ty=y-self.y})
+		
+		game:playSoundNear(self, "talents/fire")
+		self:incPsi(-cost)
+		return true
+	end,
+
+	info = function(self, t)
+		local dam = t.getAuraStrength(self, t)
+		local spikedam = 50 + 0.4 * dam * dam
+		local mast = 3 + (self:getTalentLevel(self.T_PROJECTION_MASTERY) or 0) + getGemLevel(self)
+		local spikecost = t.sustain_psi - 2*getGemLevel(self)
+		return ([[Fills the air around you with reactive currents of furnace-like heat that do %d fire damage to all who approach. All damage done by the aura will drain one point of energy per %0.2f points of damage dealt.
+		When deactivated, if you have at least %d energy, a massive spike of thermal energy is released as a tunnel of superheated air. Anybody caught in it will suffer %d fire damage. Telekinetically wielding a gem instead of a weapon will result in improved spike efficiency.
+		The damage will increase with the Willpower stat.]]):format(dam, mast, spikecost, spikedam)
+	end,
+}
+
+
+newTalent{
+	name = "Charged Aura",
+	type = {"psionic/projection", 3},
+	require = psi_wil_req3, no_sustain_autoreset = true,
+	points = 5,
+	mode = "sustained",
+	sustain_psi = 50,
+	remove_on_zero = true,
+	cooldown = function(self, t)
+		return 15 - (self:getTalentLevelRaw(self.T_PROJECTION_MASTERY) or 0)
+	end,
+	tactical = {
+		ATTACKAREA = 10,
+	},
+	range = function(self, t)
+		local r = 6
+		local gem_level = getGemLevel(self)
+		local mult = (1 + 0.02*gem_level*(self:getTalentLevel(self.T_REACH)))
+		r = math.floor(r*mult)
+		return math.min(r, 10)
+	end,
+	direct_hit = true,
+	getAuraStrength = function(self, t)
+		local add = 0
+		if self:knowTalent(self.T_FOCUSED_CHANNELING) then
+			add = getGemLevel(self)*(1 + 0.1*(self:getTalentLevel(self.T_FOCUSED_CHANNELING) or 0))
+		end
+		--return 5 + (1+ self:getWil(5))*self:getTalentLevel(t) + add
+		return self:combatTalentIntervalDamage(t, "wil", 6, 40) + add
+	end,
+	do_chargedaura = function(self, t)
+		local mast = 3 + (self:getTalentLevel(self.T_PROJECTION_MASTERY) or 0) + getGemLevel(self)
+		local dam = t.getAuraStrength(self, t)
+		local tgts = {}
+		local grids = core.fov.circle_grids(self.x, self.y, 1, 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
+
+		-- Randomly take targets
+		local tg = {type="hit", range=1, talent=t}
+		for i = 1, 10 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.LIGHTNING, dam)
+			--game.level.map:particleEmitter(self.x, self.y, math.max(math.abs(a.x-self.x), math.abs(a.y-self.y)), "lightning", {tx=a.x-self.x, ty=a.y-self.y})
+			self:incPsi(-dam/mast)
+		end
+	end,
+	activate = function(self, t)
+		game:playSoundNear(self, "talents/thunderstorm")
+		return true
+	end,
+	deactivate = function(self, t, p)
+		local dam = 50 + 0.4 * t.getAuraStrength(self, t)*t.getAuraStrength(self, t)
+		local cost = t.sustain_psi - 2*getGemLevel(self)
+		--if self:isTalentActive(self.T_CONDUIT) then return true end
+		if self:getPsi() <= cost then 
+			game.logPlayer(self, "The aura dissipates without producing a spike.")
+			return true 
+		end
+		
+		local tg = {type="bolt", range=self:getTalentRange(t), talent=t}
+		local fx, fy = self:getTarget(tg)
+		if not fx or not fy then return nil end
+
+		local nb = 1 + math.floor(0.5*self:getTalentLevel(t)) + getGemLevel(self)
+		local affected = {}
+		local first = nil
+		--Here's the part where deactivating the aura fires off a huge chain lightning
+		self:project(tg, fx, fy, function(dx, dy)
+			print("[Chain lightning] targetting", fx, fy, "from", self.x, self.y)
+			local actor = game.level.map(dx, dy, Map.ACTOR)
+			if actor and not affected[actor] then
+				ignored = false
+				affected[actor] = true
+				first = actor
+
+				print("[Chain lightning] looking for more targets", nb, " at ", dx, dy, "radius ", 10, "from", actor.name)
+				self:project({type="ball", friendlyfire=false, x=dx, y=dy, radius=self:getTalentRange(t), range=0}, dx, dy, function(bx, by)
+					local actor = game.level.map(bx, by, Map.ACTOR)
+					if actor and not affected[actor] and self:reactionToward(actor) < 0 then
+						print("[Chain lightning] found possible actor", actor.name, bx, by, "distance", core.fov.distance(dx, dy, bx, by))
+						affected[actor] = true
+					end
+				end)
+				return true
+			end
+		end)
+
+		if not first then return true end
+		local targets = { first }
+		affected[first] = nil
+		local possible_targets = table.listify(affected)
+		print("[Chain lightning] Found targets:", #possible_targets)
+		for i = 2, nb do
+			if #possible_targets == 0 then break end
+			local act = rng.tableRemove(possible_targets)
+			targets[#targets+1] = act[1]
+		end
+
+		local sx, sy = self.x, self.y
+		for i, actor in ipairs(targets) do
+			local tgr = {type="beam", range=self:getTalentRange(t), friendlyfire=false, talent=t, x=sx, y=sy}
+			print("[Chain lightning] jumping from", sx, sy, "to", actor.x, actor.y)
+			self:project(tgr, actor.x, actor.y, DamageType.LIGHTNING, self:spellCrit(rng.avg(0.8*dam, dam)))
+			game.level.map:particleEmitter(sx, sy, math.max(math.abs(actor.x-sx), math.abs(actor.y-sy)), "lightning", {tx=actor.x-sx, ty=actor.y-sy, nb_particles=150, life=6})
+			sx, sy = actor.x, actor.y
+		end
+		game:playSoundNear(self, "talents/lightning")
+		self:incPsi(-cost)
+		return true
+	end,
+
+	info = function(self, t)
+		local dam = t.getAuraStrength(self, t)
+		local spikedam = 50 + 0.4 * dam * dam
+		local mast = 3 + (self:getTalentLevel(self.T_PROJECTION_MASTERY) or 0) + getGemLevel(self)
+		local spikecost = t.sustain_psi - 2*getGemLevel(self)
+		local nb = 3 + self:getTalentLevelRaw(t)
+		return ([[Fills the air around you with crackling energy, doing %d lightning damage to all who stand nearby. All damage done by the aura will drain one point of energy per %0.2f points of damage dealt.
+		When deactivated, if you have at least %d energy, a massive spike of electrical energy jumps between up to %d nearby targets, doing %d lightning damage to each. Telekinetically wielding a gem instead of a weapon will result in improved spike efficiency.
+		The damage will increase with the Willpower stat.]]):format(dam, mast, spikecost, nb, spikedam)
+	end,
+}
+
+newTalent{
+	name = "Projection Mastery",
+	type = {"psionic/projection", 4},
+	require = psi_wil_req4,
+	points = 5,
+	mode = "passive",
+	info = function(self, t)
+		local cooldown = self:getTalentLevelRaw(t)
+		local mast = (self:getTalentLevel(t) or 0)
+		return ([[Your expertise in the art of energy projection grows.
+		Aura cooldowns are all reduced by %d turns. Aura damage drains energy more slowly (+%0.2f damage required to lose a point of energy).]]):format(cooldown, mast)
+	end,
+}
diff --git a/game/modules/tome/data/talents/psionic/psi-fighting.lua b/game/modules/tome/data/talents/psionic/psi-fighting.lua
index dc0b5fa242..a29fa6d5dd 100644
--- a/game/modules/tome/data/talents/psionic/psi-fighting.lua
+++ b/game/modules/tome/data/talents/psionic/psi-fighting.lua
@@ -1,166 +1,166 @@
--- ToME - Tales of Maj'Eyal
--- Copyright (C) 2009, 2010 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 function cancelAuras(self)
-	local auras = {self.T_CHARGED_AURA, self.T_THERMAL_AURA, self.T_KINETIC_AURA,}
-	for i, t in ipairs(auras) do
-		if self:isTalentActive(t) then
-			self:forceUseTalent(t, {ignore_energy=true})
-		end
-	end
-end
-
-newTalent{
-	name = "Telekinetic Smash",
-	type = {"psionic/psi-fighting", 1},
-	require = psi_wil_req1,
-	points = 5,
-	random_ego = "attack",
-	cooldown = 10,
-	psi = 10,
-	range = 1,
-	action = function(self, t)
-
-		local tkweapon = self:getInven("MAINHAND")[1]
-		if type(tkweapon) == "boolean" then tkweapon = nil end
-		if not tkweapon then
-			game.logPlayer(self, "You cannot do that without a weapon in your hands.")
-			return nil
-		end
-		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
-		self:attackTargetWith(target, tkweapon.combat, nil, self:combatTalentWeaponDamage(t, 1.5, 2.5))
-		return true
-	end,
-	info = function(self, t)
-		return ([[Gather your will and brutally smash the target with your mainhand weapon, doing %d%% weapon damage.]]):
-		format(100 * self:combatTalentWeaponDamage(t, 1.5, 2.5))
-	end,
-}
-
-newTalent{
-	name = "Augmentation",
-	type = {"psionic/psi-fighting", 2},
-	require = psi_wil_req2,
-	points = 5,
-	mode = "sustained",
-	cooldown = 0,
-	sustain_psi = 10,
-	activate = function(self, t)
-		local str_power = math.floor(0.06*self:getTalentLevel(t)*self:getWil())
-		local dex_power = math.floor(0.06*self:getTalentLevel(t)*self:getCun())
-		return {
-			stats = self:addTemporaryValue("inc_stats", {
-				[self.STAT_STR] = str_power,
-				[self.STAT_DEX] = dex_power,
-			}),
-		}
-	end,
-	deactivate = function(self, t, p)
-		self:removeTemporaryValue("inc_stats", p.stats)
-		return true
-	end,
-	info = function(self, t)
-		local inc = 10*self:getTalentLevel(t)
-		local str_power = math.floor(0.06*self:getTalentLevel(t)*self:getWil())
-		local dex_power = math.floor(0.06*self:getTalentLevel(t)*self:getCun())
-		return ([[While active, you give your flesh and blood body a little aid in the form of precisely applied mental forces. Increases Strength and Dexterity by %d%% of your Willpower and Cunning, respectively.
-		Strength increased by %d
-		Dexterity increased by %d]]):
-		format(inc, str_power, dex_power)
-	end,
-}
-
-newTalent{
-	name = "Conduit",
-	type = {"psionic/psi-fighting", 3},
-	require = psi_wil_req3, no_sustain_autoreset = true,
-	cooldown = 1,
-	mode = "sustained",
-	sustain_psi = 0,
-	points = 5,
-
-
-	activate = function(self, t)
-		local ret = {
-		k_aura_on = self:isTalentActive(self.T_KINETIC_AURA),
-		t_aura_on = self:isTalentActive(self.T_THERMAL_AURA),
-		c_aura_on = self:isTalentActive(self.T_CHARGED_AURA),
-		}
-		local cur_psi = self:getPsi()
-		self:incPsi(-5000)
-		--self.sustain_talents[t.id] = {}
-		cancelAuras(self)
-		self:incPsi(cur_psi)
-		return ret
-	end,
-
-	do_combat = function(self, t, target)
-		local mult = 1 + 0.1*(self:getTalentLevel(t))
-		local auras = self:isTalentActive(t.id)
-		if auras.k_aura_on then
-			local k_aura = self:getTalentFromId(self.T_KINETIC_AURA)
-			local k_dam = mult * k_aura.getAuraStrength(self, k_aura)
-			DamageType:get(DamageType.PHYSICAL).projector(self, target.x, target.y, DamageType.PHYSICAL, k_dam)
-		end
-		if auras.t_aura_on then
-			local t_aura = self:getTalentFromId(self.T_THERMAL_AURA)
-			local t_dam = mult * t_aura.getAuraStrength(self, t_aura)
-			DamageType:get(DamageType.FIRE).projector(self, target.x, target.y, DamageType.FIRE, t_dam)
-		end
-		if auras.c_aura_on then
-			local c_aura = self:getTalentFromId(self.T_CHARGED_AURA)
-			local c_dam = mult * c_aura.getAuraStrength(self, c_aura)
-			DamageType:get(DamageType.LIGHTNING).projector(self, target.x, target.y, DamageType.LIGHTNING, c_dam)
-		end
-	end,
-
-	deactivate = function(self, t)
-		return true
-	end,
-	info = function(self, t)
-		local mult = 1 + 0.1*(self:getTalentLevel(t))
-		return ([[When activated, turns off any active auras and uses your weapons as conduits for the energies that were being channeled through those auras.
-		Any auras used by Conduit will not start to cool down until Conduit has been deactivated. The damage from each aura applied by Conduit is multiplied by %0.2f, and does not drain energy.]]):
-		format(mult)
-	end,
-}
-
-newTalent{
-	name = "Frenzied Psifighting",
-	type = {"psionic/psi-fighting", 4},
-	require = psi_wil_req4,
-	cooldown = 20,
-	psi = 30,
-	points = 5,
-	action = function(self, t)
-		local targets = 1 + math.ceil(self:getTalentLevel(t)/5)
-		self:setEffect(self.EFF_PSIFRENZY, 3 * self:getTalentLevelRaw(t), {power=targets})
-		return true
-	end,
-	--getTargNum = function(self, t) return 1 + math.ceil(self:getTalentLevel(t)/5) end,
-	info = function(self, t)
-		local targets = 1 + math.ceil(self:getTalentLevel(t)/5)
-		local dur = 3 * self:getTalentLevelRaw(t)
-		return ([[Your telekinetically wielded weapon enters a frenzy for %d turns, striking up to %d targets every turn.]]):
-		format(dur, targets)
-	end,
-}
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009, 2010 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 function cancelAuras(self)
+	local auras = {self.T_CHARGED_AURA, self.T_THERMAL_AURA, self.T_KINETIC_AURA,}
+	for i, t in ipairs(auras) do
+		if self:isTalentActive(t) then
+			self:forceUseTalent(t, {ignore_energy=true})
+		end
+	end
+end
+
+newTalent{
+	name = "Telekinetic Smash",
+	type = {"psionic/psi-fighting", 1},
+	require = psi_wil_req1,
+	points = 5,
+	random_ego = "attack",
+	cooldown = 10,
+	psi = 10,
+	range = 1,
+	action = function(self, t)
+
+		local tkweapon = self:getInven("MAINHAND")[1]
+		if type(tkweapon) == "boolean" then tkweapon = nil end
+		if not tkweapon then
+			game.logPlayer(self, "You cannot do that without a weapon in your hands.")
+			return nil
+		end
+		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
+		self:attackTargetWith(target, tkweapon.combat, nil, self:combatTalentWeaponDamage(t, 1.5, 2.5))
+		return true
+	end,
+	info = function(self, t)
+		return ([[Gather your will and brutally smash the target with your mainhand weapon, doing %d%% weapon damage.]]):
+		format(100 * self:combatTalentWeaponDamage(t, 1.5, 2.5))
+	end,
+}
+
+newTalent{
+	name = "Augmentation",
+	type = {"psionic/psi-fighting", 2},
+	require = psi_wil_req2,
+	points = 5,
+	mode = "sustained",
+	cooldown = 0,
+	sustain_psi = 10,
+	activate = function(self, t)
+		local str_power = math.floor(0.06*self:getTalentLevel(t)*self:getWil())
+		local dex_power = math.floor(0.06*self:getTalentLevel(t)*self:getCun())
+		return {
+			stats = self:addTemporaryValue("inc_stats", {
+				[self.STAT_STR] = str_power,
+				[self.STAT_DEX] = dex_power,
+			}),
+		}
+	end,
+	deactivate = function(self, t, p)
+		self:removeTemporaryValue("inc_stats", p.stats)
+		return true
+	end,
+	info = function(self, t)
+		local inc = 10*self:getTalentLevel(t)
+		local str_power = math.floor(0.06*self:getTalentLevel(t)*self:getWil())
+		local dex_power = math.floor(0.06*self:getTalentLevel(t)*self:getCun())
+		return ([[While active, you give your flesh and blood body a little aid in the form of precisely applied mental forces. Increases Strength and Dexterity by %d%% of your Willpower and Cunning, respectively.
+		Strength increased by %d
+		Dexterity increased by %d]]):
+		format(inc, str_power, dex_power)
+	end,
+}
+
+newTalent{
+	name = "Conduit",
+	type = {"psionic/psi-fighting", 3},
+	require = psi_wil_req3, no_sustain_autoreset = true,
+	cooldown = 1,
+	mode = "sustained",
+	sustain_psi = 0,
+	points = 5,
+
+
+	activate = function(self, t)
+		local ret = {
+		k_aura_on = self:isTalentActive(self.T_KINETIC_AURA),
+		t_aura_on = self:isTalentActive(self.T_THERMAL_AURA),
+		c_aura_on = self:isTalentActive(self.T_CHARGED_AURA),
+		}
+		local cur_psi = self:getPsi()
+		self:incPsi(-5000)
+		--self.sustain_talents[t.id] = {}
+		cancelAuras(self)
+		self:incPsi(cur_psi)
+		return ret
+	end,
+
+	do_combat = function(self, t, target)
+		local mult = 1 + 0.1*(self:getTalentLevel(t))
+		local auras = self:isTalentActive(t.id)
+		if auras.k_aura_on then
+			local k_aura = self:getTalentFromId(self.T_KINETIC_AURA)
+			local k_dam = mult * k_aura.getAuraStrength(self, k_aura)
+			DamageType:get(DamageType.PHYSICAL).projector(self, target.x, target.y, DamageType.PHYSICAL, k_dam)
+		end
+		if auras.t_aura_on then
+			local t_aura = self:getTalentFromId(self.T_THERMAL_AURA)
+			local t_dam = mult * t_aura.getAuraStrength(self, t_aura)
+			DamageType:get(DamageType.FIRE).projector(self, target.x, target.y, DamageType.FIRE, t_dam)
+		end
+		if auras.c_aura_on then
+			local c_aura = self:getTalentFromId(self.T_CHARGED_AURA)
+			local c_dam = mult * c_aura.getAuraStrength(self, c_aura)
+			DamageType:get(DamageType.LIGHTNING).projector(self, target.x, target.y, DamageType.LIGHTNING, c_dam)
+		end
+	end,
+
+	deactivate = function(self, t)
+		return true
+	end,
+	info = function(self, t)
+		local mult = 1 + 0.1*(self:getTalentLevel(t))
+		return ([[When activated, turns off any active auras and uses your weapons as conduits for the energies that were being channeled through those auras.
+		Any auras used by Conduit will not start to cool down until Conduit has been deactivated. The damage from each aura applied by Conduit is multiplied by %0.2f, and does not drain energy.]]):
+		format(mult)
+	end,
+}
+
+newTalent{
+	name = "Frenzied Psifighting",
+	type = {"psionic/psi-fighting", 4},
+	require = psi_wil_req4,
+	cooldown = 20,
+	psi = 30,
+	points = 5,
+	action = function(self, t)
+		local targets = 1 + math.ceil(self:getTalentLevel(t)/5)
+		self:setEffect(self.EFF_PSIFRENZY, 3 * self:getTalentLevelRaw(t), {power=targets})
+		return true
+	end,
+	--getTargNum = function(self, t) return 1 + math.ceil(self:getTalentLevel(t)/5) end,
+	info = function(self, t)
+		local targets = 1 + math.ceil(self:getTalentLevel(t)/5)
+		local dur = 3 * self:getTalentLevelRaw(t)
+		return ([[Your telekinetically wielded weapon enters a frenzy for %d turns, striking up to %d targets every turn.]]):
+		format(dur, targets)
+	end,
+}
diff --git a/game/modules/tome/data/talents/psionic/voracity.lua b/game/modules/tome/data/talents/psionic/voracity.lua
index 2f7e8dfac8..8f2c0a7dcc 100644
--- a/game/modules/tome/data/talents/psionic/voracity.lua
+++ b/game/modules/tome/data/talents/psionic/voracity.lua
@@ -1,193 +1,193 @@
--- ToME - Tales of Maj'Eyal
--- Copyright (C) 2009, 2010 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 function getGemLevel(self)
-		local gem_level = 0
-		if not self:getInven("PSIONIC_FOCUS")[1] then return gem_level end
-		local tk_item = self:getInven("PSIONIC_FOCUS")[1]
-		if tk_item.type == "gem" then 
-			gem_level = tk_item.material_level
-		else
-			gem_level = 0
-		end
-		return gem_level
-end
-
-newTalent{
-	name = "Kinetic Leech",
-	type = {"psionic/voracity", 1},
-	require = psi_wil_req1,
-	points = 5,
-	psi = 0,
-	cooldown = 40,
-	tactical = {
-		ATTACKAREA = 10,
-		DEFEND = 4,
-	},
-	direct_hit = true,
-	range = function(self, t)
-		local r = 2
-		local gem_level = getGemLevel(self)
-		local mult = (1 + 0.02*gem_level*(self:getTalentLevel(self.T_REACH)))
-		return math.ceil(r*mult)
-	end,
-	action = function(self, t)
-		local tgts = {}
-		local grids = core.fov.circle_grids(self.x, self.y, self:getTalentRange(t), 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 en = ( 3 + self:getTalentLevel(t)) * (100 + self:getWil())/100
-		self:incPsi(en*#tgts)
-		local tg = {type="ball", range=0, radius=self:getTalentRange(t), friendlyfire=false, talent=t}
-		local dam = .1 + 0.03*self:getTalentLevel(t)
-		self:project(tg, self.x, self.y, DamageType.MINDSLOW, dam)
-		local x, y = self.x, self.y
-		return true
-	end,
-	info = function(self, t)
-		local range = self:getTalentRange(t)
-		local slow = 3 * self:getTalentLevel(t) + 10
-		local en = ( 3 + self:getTalentLevel(t)) * (100 + self:getWil())/100
-		return ([[You suck the kinetic energy out of your surroundings, slowing all enemies in a radius of %d by %d%%.
-		For each enemy drained, you gain %d energy.
-		The effect scales with Willpower.]]):format(range, slow, en)
-	end,
-}
-
-newTalent{
-	name = "Thermal Leech",
-	type = {"psionic/voracity", 2},
-	require = psi_wil_req2,
-	points = 5,
-	cooldown = 50,
-	psi = 0,
-	tactical = {
-		ATTACKAREA = 10,
-		DEFEND = 4,
-	},
-	range = function(self, t)
-		local r = 1
-		local gem_level = getGemLevel(self)
-		local mult = (1 + 0.02*gem_level*(self:getTalentLevel(self.T_REACH)))
-		return math.ceil(r*mult)
-	end,
-	action = function(self, t)
-		local tgts = {}
-		local grids = core.fov.circle_grids(self.x, self.y, self:getTalentRange(t), 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 en = ( 4 + self:getTalentLevel(t)) * (100 + self:getWil())/85
-		self:incPsi(en*#tgts)
-		local duration = self:getTalentLevel(t) + 2
-		local radius = self:getTalentRange(t)
-		local dam = 1 + 0.3*self:getTalentLevel(t)
-		local tg = {type="ball", range=0, radius=radius, friendlyfire=false}
-		self:project(tg, self.x, self.y, DamageType.FREEZE, dam)
-		return true
-	end,
-	info = function(self, t)
-		local range = self:getTalentRange(t)
-		local en = ( 4 + self:getTalentLevel(t)) * (100 + self:getWil())/85
-		--local duration = self:getTalentLevel(t) + 2
-		return ([[You leech the heat out of all foes in a radius of %d, gaining %d energy for each enemy frozen. 
-		The effect scales with Willpower.]]):
-		format(range, en)
-	end,
-}
-
-newTalent{
-	name = "Charge Leech",
-	type = {"psionic/voracity", 3},
-	require = psi_wil_req3,
-	points = 5,
-	psi = 0,
-	cooldown = 60,
-	tactical = {
-		ATTACKAREA = 10,
-		DEFEND = 4,
-	},
-	direct_hit = true,
-	range = function(self, t)
-		local r = 2
-		local gem_level = getGemLevel(self)
-		local mult = (1 + 0.02*gem_level*(self:getTalentLevel(self.T_REACH)))
-		return math.ceil(r*mult)
-	end,
-	action = function(self, t)
-		local tgts = {}
-		local grids = core.fov.circle_grids(self.x, self.y, self:getTalentRange(t), 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 en = ( 5 + self:getTalentLevel(t)) * (100 + self:getWil())/75
-		self:incPsi(en*#tgts)
-		local tg = {type="ball", range=0, radius=self:getTalentRange(t), friendlyfire=false, talent=t}
-		local dam = self:spellCrit(self:combatTalentMindDamage(t, 28, 170))
-		self:project(tg, self.x, self.y, DamageType.LIGHTNING_DAZE, rng.avg(dam / 3, dam, 3))
-		local x, y = self.x, self.y
-		-- Lightning ball gets a special treatment to make it look neat
-		local sradius = (tg.radius + 0.5) * (engine.Map.tile_w + engine.Map.tile_h) / 2
-		local nb_forks = 16
-		local angle_diff = 360 / nb_forks
-		for i = 0, nb_forks - 1 do
-			local a = math.rad(rng.range(0+i*angle_diff,angle_diff+i*angle_diff))
-			local tx = x + math.floor(math.cos(a) * tg.radius)
-			local ty = y + math.floor(math.sin(a) * tg.radius)
-			game.level.map:particleEmitter(x, y, tg.radius, "lightning", {radius=tg.radius, grids=grids, tx=tx-x, ty=ty-y, nb_particles=25, life=8})
-		end
-
-		game:playSoundNear(self, "talents/lightning")
-		return true
-	end,
-	info = function(self, t)
-		local range = self:getTalentRange(t)
-		local en = ( 5 + self:getTalentLevel(t)) * (100 + self:getWil())/75
-		local dam = damDesc(self, DamageType.LIGHTNING, self:combatTalentMindDamage(t, 28, 170))
-		return ([[You pull electric potential from the foes around you in a radius of %d, gaining %d energy for each one affected and giving them a nasty shock in the process. Deals between %d and %d damage.
-		The effect scales with Willpower.]]):format(range, en, dam / 3, dam)
-	end,
-}
-newTalent{
-	name = "Insatiable",
-	type = {"psionic/voracity", 4},
-	mode = "passive",
-	points = 5,
-	require = psi_wil_req4,
-	on_learn = function(self, t)
-		self.max_psi = self.max_psi + 10
-	end,
-	on_unlearn = function(self, t)
-		self.max_psi = self.max_psi - 10
-	end,
-	info = function(self, t)
-		return ([[Increases your maximum Energy by %d]]):format(10 * self:getTalentLevelRaw(t))
-	end,
-}
-
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009, 2010 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 function getGemLevel(self)
+		local gem_level = 0
+		if not self:getInven("PSIONIC_FOCUS")[1] then return gem_level end
+		local tk_item = self:getInven("PSIONIC_FOCUS")[1]
+		if tk_item.type == "gem" then 
+			gem_level = tk_item.material_level
+		else
+			gem_level = 0
+		end
+		return gem_level
+end
+
+newTalent{
+	name = "Kinetic Leech",
+	type = {"psionic/voracity", 1},
+	require = psi_wil_req1,
+	points = 5,
+	psi = 0,
+	cooldown = 40,
+	tactical = {
+		ATTACKAREA = 10,
+		DEFEND = 4,
+	},
+	direct_hit = true,
+	range = function(self, t)
+		local r = 2
+		local gem_level = getGemLevel(self)
+		local mult = (1 + 0.02*gem_level*(self:getTalentLevel(self.T_REACH)))
+		return math.ceil(r*mult)
+	end,
+	action = function(self, t)
+		local tgts = {}
+		local grids = core.fov.circle_grids(self.x, self.y, self:getTalentRange(t), 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 en = ( 3 + self:getTalentLevel(t)) * (100 + self:getWil())/100
+		self:incPsi(en*#tgts)
+		local tg = {type="ball", range=0, radius=self:getTalentRange(t), friendlyfire=false, talent=t}
+		local dam = .1 + 0.03*self:getTalentLevel(t)
+		self:project(tg, self.x, self.y, DamageType.MINDSLOW, dam)
+		local x, y = self.x, self.y
+		return true
+	end,
+	info = function(self, t)
+		local range = self:getTalentRange(t)
+		local slow = 3 * self:getTalentLevel(t) + 10
+		local en = ( 3 + self:getTalentLevel(t)) * (100 + self:getWil())/100
+		return ([[You suck the kinetic energy out of your surroundings, slowing all enemies in a radius of %d by %d%%.
+		For each enemy drained, you gain %d energy.
+		The effect scales with Willpower.]]):format(range, slow, en)
+	end,
+}
+
+newTalent{
+	name = "Thermal Leech",
+	type = {"psionic/voracity", 2},
+	require = psi_wil_req2,
+	points = 5,
+	cooldown = 50,
+	psi = 0,
+	tactical = {
+		ATTACKAREA = 10,
+		DEFEND = 4,
+	},
+	range = function(self, t)
+		local r = 1
+		local gem_level = getGemLevel(self)
+		local mult = (1 + 0.02*gem_level*(self:getTalentLevel(self.T_REACH)))
+		return math.ceil(r*mult)
+	end,
+	action = function(self, t)
+		local tgts = {}
+		local grids = core.fov.circle_grids(self.x, self.y, self:getTalentRange(t), 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 en = ( 4 + self:getTalentLevel(t)) * (100 + self:getWil())/85
+		self:incPsi(en*#tgts)
+		local duration = self:getTalentLevel(t) + 2
+		local radius = self:getTalentRange(t)
+		local dam = 1 + 0.3*self:getTalentLevel(t)
+		local tg = {type="ball", range=0, radius=radius, friendlyfire=false}
+		self:project(tg, self.x, self.y, DamageType.FREEZE, dam)
+		return true
+	end,
+	info = function(self, t)
+		local range = self:getTalentRange(t)
+		local en = ( 4 + self:getTalentLevel(t)) * (100 + self:getWil())/85
+		--local duration = self:getTalentLevel(t) + 2
+		return ([[You leech the heat out of all foes in a radius of %d, gaining %d energy for each enemy frozen. 
+		The effect scales with Willpower.]]):
+		format(range, en)
+	end,
+}
+
+newTalent{
+	name = "Charge Leech",
+	type = {"psionic/voracity", 3},
+	require = psi_wil_req3,
+	points = 5,
+	psi = 0,
+	cooldown = 60,
+	tactical = {
+		ATTACKAREA = 10,
+		DEFEND = 4,
+	},
+	direct_hit = true,
+	range = function(self, t)
+		local r = 2
+		local gem_level = getGemLevel(self)
+		local mult = (1 + 0.02*gem_level*(self:getTalentLevel(self.T_REACH)))
+		return math.ceil(r*mult)
+	end,
+	action = function(self, t)
+		local tgts = {}
+		local grids = core.fov.circle_grids(self.x, self.y, self:getTalentRange(t), 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 en = ( 5 + self:getTalentLevel(t)) * (100 + self:getWil())/75
+		self:incPsi(en*#tgts)
+		local tg = {type="ball", range=0, radius=self:getTalentRange(t), friendlyfire=false, talent=t}
+		local dam = self:spellCrit(self:combatTalentMindDamage(t, 28, 170))
+		self:project(tg, self.x, self.y, DamageType.LIGHTNING_DAZE, rng.avg(dam / 3, dam, 3))
+		local x, y = self.x, self.y
+		-- Lightning ball gets a special treatment to make it look neat
+		local sradius = (tg.radius + 0.5) * (engine.Map.tile_w + engine.Map.tile_h) / 2
+		local nb_forks = 16
+		local angle_diff = 360 / nb_forks
+		for i = 0, nb_forks - 1 do
+			local a = math.rad(rng.range(0+i*angle_diff,angle_diff+i*angle_diff))
+			local tx = x + math.floor(math.cos(a) * tg.radius)
+			local ty = y + math.floor(math.sin(a) * tg.radius)
+			game.level.map:particleEmitter(x, y, tg.radius, "lightning", {radius=tg.radius, grids=grids, tx=tx-x, ty=ty-y, nb_particles=25, life=8})
+		end
+
+		game:playSoundNear(self, "talents/lightning")
+		return true
+	end,
+	info = function(self, t)
+		local range = self:getTalentRange(t)
+		local en = ( 5 + self:getTalentLevel(t)) * (100 + self:getWil())/75
+		local dam = damDesc(self, DamageType.LIGHTNING, self:combatTalentMindDamage(t, 28, 170))
+		return ([[You pull electric potential from the foes around you in a radius of %d, gaining %d energy for each one affected and giving them a nasty shock in the process. Deals between %d and %d damage.
+		The effect scales with Willpower.]]):format(range, en, dam / 3, dam)
+	end,
+}
+newTalent{
+	name = "Insatiable",
+	type = {"psionic/voracity", 4},
+	mode = "passive",
+	points = 5,
+	require = psi_wil_req4,
+	on_learn = function(self, t)
+		self.max_psi = self.max_psi + 10
+	end,
+	on_unlearn = function(self, t)
+		self.max_psi = self.max_psi - 10
+	end,
+	info = function(self, t)
+		return ([[Increases your maximum Energy by %d]]):format(10 * self:getTalentLevelRaw(t))
+	end,
+}
+
-- 
GitLab