From b7969db15c0a68695f226a34f77594f184ce9a3f Mon Sep 17 00:00:00 2001
From: dg <dg@51575b47-30f0-44d4-a5cc-537603b46e54>
Date: Sun, 30 Jan 2011 23:21:45 +0000
Subject: [PATCH] New Fighters high tree: Battle Tactics

git-svn-id: http://svn.net-core.org/repos/t-engine4@2575 51575b47-30f0-44d4-a5cc-537603b46e54
---
 game/modules/tome/class/Actor.lua             |  21 ++-
 game/modules/tome/class/Player.lua            |   2 +
 game/modules/tome/class/interface/Combat.lua  |  11 ++
 .../tome/data/birth/classes/chronomancer.lua  |   2 +
 .../tome/data/birth/classes/warrior.lua       |   1 +
 game/modules/tome/data/damage_types.lua       |   2 +-
 .../talents/chronomancy/age-manipulation.lua  |   2 +-
 .../tome/data/talents/chronomancy/energy.lua  | 129 +++++++++---------
 .../talents/chronomancy/spacetime-weaving.lua |  39 ++++++
 .../talents/chronomancy/speed-control.lua     |   2 +-
 .../chronomancy/timeline-threading.lua        |   8 +-
 .../data/talents/chronomancy/timetravel.lua   |   6 +-
 .../talents/techniques/battle-tactics.lua     | 123 +++++++++++++++++
 .../data/talents/techniques/techniques.lua    |   2 +
 game/modules/tome/data/timed_effects.lua      |  96 ++++++++++++-
 15 files changed, 371 insertions(+), 75 deletions(-)
 create mode 100644 game/modules/tome/data/talents/techniques/battle-tactics.lua

diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua
index e246aa4c94..9424a81fd6 100644
--- a/game/modules/tome/class/Actor.lua
+++ b/game/modules/tome/class/Actor.lua
@@ -263,6 +263,10 @@ function _M:act()
 		local t = self:getTalentFromId(self.T_BLOOD_FRENZY)
 		t.do_turn(self, t)
 	end
+	if self:isTalentActive(self.T_TRUE_GRIT) then
+		local t = self:getTalentFromId(self.T_TRUE_GRIT)
+		t.do_turn(self, t)
+	end
 	-- this handles cursed gloom turn based effects
 	if self:isTalentActive(self.T_GLOOM) then
 	    local t = self:getTalentFromId(self.T_GLOOM)
@@ -401,7 +405,7 @@ function _M:move(x, y, force)
 
 	if moved and not force and ox and oy and (ox ~= self.x or oy ~= self.y) and config.settings.tome.smooth_move > 0 then
 		local blur = 0
-		if self:attr("lightning_speed") then blur = 3 end
+		if self:attr("lightning_speed") or self:attr("step_up") then blur = 3 end
 		self:setMoveAnim(ox, oy, config.settings.tome.smooth_move, blur)
 	end
 
@@ -705,6 +709,9 @@ function _M:onTakeHit(value, src)
 	if self:hasEffect(self.EFF_MEDITATION) then
 		self:removeEffect(self.EFF_MEDITATION)
 	end
+	if self:hasEffect(self.EFF_SPACETIME_TUNING) then
+		self:removeEffect(self.EFF_SPACETIME_TUNING)
+	end
 	-- remove stalking if there is an interaction
 	if self.stalker and src and self.stalker == src then
 		self.stalker:removeEffect(self.EFF_STALKER)
@@ -1110,6 +1117,10 @@ function _M:die(src)
 		p.kills = p.kills + 1
 	end
 
+	if src and src.knowTalent and src:knowTalent(src.T_STEP_UP) and rng.percent(src:getTalentLevelRaw(src.T_STEP_UP) * 20) then
+		src:setEffect(self.EFF_STEP_UP, 6, {})
+	end
+
 	if self:hasEffect(self.EFF_CORROSIVE_WORM) then
 		local p = self:hasEffect(self.EFF_CORROSIVE_WORM)
 		p.src:project({type="ball", radius=4, x=self.x, y=self.y}, self.x, self.y, DamageType.ACID, p.explosion, {type="acid"})
@@ -1765,6 +1776,7 @@ function _M:postUseTalent(ab, ret)
 	if ab.id ~= self.T_STEALTH and ab.id ~= self.T_HIDE_IN_PLAIN_SIGHT and not ab.no_break_stealth then self:breakStealth() end
 	if ab.id ~= self.T_LIGHTNING_SPEED then self:breakLightningSpeed() end
 	if ab.id ~= self.T_GATHER_THE_THREADS then self:breakGatherTheThreads() end
+	self:breakStepUp()
 	return true
 end
 
@@ -1800,6 +1812,13 @@ function _M:breakStealth()
 	end
 end
 
+--- Breaks step up if active
+function _M:breakStepUp()
+	if self:hasEffect(self.EFF_STEP_UP) then
+		self:removeEffect(self.EFF_STEP_UP)
+	end
+end
+
 --- Breaks lightning speed if active
 function _M:breakLightningSpeed()
 	if self:hasEffect(self.EFF_LIGHTNING_SPEED) then
diff --git a/game/modules/tome/class/Player.lua b/game/modules/tome/class/Player.lua
index 1e0f5af3e8..0fed3d3a3e 100644
--- a/game/modules/tome/class/Player.lua
+++ b/game/modules/tome/class/Player.lua
@@ -728,12 +728,14 @@ function _M:playerUseItem(object, item, inven)
 					end
 					self:sortInven(self:getInven(inven))
 				end
+				self:breakStepUp()
 				self:breakStealth()
 				self:breakLightningSpeed()
 				self:breakGatherTheThreads()
 				return true
 			end
 
+			self:breakStepUp()
 			self:breakStealth()
 			self:breakLightningSpeed()
 			self:breakGatherTheThreads()
diff --git a/game/modules/tome/class/interface/Combat.lua b/game/modules/tome/class/interface/Combat.lua
index b9c8f5314c..99d51ca2f2 100644
--- a/game/modules/tome/class/interface/Combat.lua
+++ b/game/modules/tome/class/interface/Combat.lua
@@ -70,6 +70,9 @@ function _M:attackTarget(target, damtype, mult, noenergy)
 	local speed, hit = nil, false
 	local sound, sound_miss = nil, nil
 
+	-- Break before we do the blow, because it might start step up, we dont want to insta-cancel it
+	self:breakStepUp()
+
 	if self:attr("feared") then
 		if not noenergy then
 			self:useEnergy(game.energy_to_act * speed)
@@ -366,6 +369,14 @@ function _M:attackTargetWith(target, weapon, damtype, mult)
 		target:attackTarget(self, nil, nil, true)
 	end
 
+	-- Greater Weapon Focus
+	local gwf = self:hasEffect(self.EFF_GREATER_WEAPON_FOCUS)
+	if hitted and not target.dead and gwf and not gwf.inside and rng.percent(gwf.chance) then
+		gwf.inside = true
+		self:attackTargetWith(target, weapon, damtype, mult)
+		gwf.inside = nil
+	end
+
 	-- Visual feedback
 	if hitted then game.level.map:particleEmitter(target.x, target.y, 1, "melee_attack", {color=target.blood_color}) end
 
diff --git a/game/modules/tome/data/birth/classes/chronomancer.lua b/game/modules/tome/data/birth/classes/chronomancer.lua
index 96172bfdfa..9a05fce300 100644
--- a/game/modules/tome/data/birth/classes/chronomancer.lua
+++ b/game/modules/tome/data/birth/classes/chronomancer.lua
@@ -64,6 +64,7 @@ newBirthDescriptor{
 
 	},
 	talents = {
+		[ActorTalents.T_SPACETIME_TUNING] = 1,
 		[ActorTalents.T_STATIC_HISTORY] = 1,
 		[ActorTalents.T_TURN_BACK_THE_CLOCK] = 1,
 		[ActorTalents.T_DUST_TO_DUST] = 1,
@@ -111,6 +112,7 @@ newBirthDescriptor{
 	},
 	talents = {
 		[ActorTalents.T_SHOOT] = 1,
+		[ActorTalents.T_SPACETIME_TUNING] = 1,
 		[ActorTalents.T_WEAPON_COMBAT] = 1,
 		[ActorTalents.T_DUAL_STRIKE] = 1,
 		[ActorTalents.T_UNERRING_SHOT] = 1,
diff --git a/game/modules/tome/data/birth/classes/warrior.lua b/game/modules/tome/data/birth/classes/warrior.lua
index c5c1b4e49f..d11c378e89 100644
--- a/game/modules/tome/data/birth/classes/warrior.lua
+++ b/game/modules/tome/data/birth/classes/warrior.lua
@@ -60,6 +60,7 @@ newBirthDescriptor{
 		["technique/combat-training"]={true, 0.3},
 		["technique/superiority"]={false, 0.3},
 		["technique/warcries"]={false, 0.3},
+		["technique/battle-tactics"]={false, 0.3},
 		["technique/field-control"]={false, 0},
 		["cunning/survival"]={true, 0},
 		["cunning/dirty"]={false, 0},
diff --git a/game/modules/tome/data/damage_types.lua b/game/modules/tome/data/damage_types.lua
index 73672a01f2..f084c7371f 100644
--- a/game/modules/tome/data/damage_types.lua
+++ b/game/modules/tome/data/damage_types.lua
@@ -1223,7 +1223,7 @@ newDamageType{
 	projector = function(src, x, y, type, dam)
 		local target = game.level.map(x, y, Map.ACTOR)
 		if target then
-			local dam = 5 + math.ceil(dam / 20)
+			local dam = 2 + math.ceil(dam / 15)
 			if target:checkHit(src:combatSpellpower(), target:combatSpellResist(), 0, 95, 15) then
 				target:setEffect(target.EFF_TURN_BACK_THE_CLOCK, 3, {power=dam})
 			else
diff --git a/game/modules/tome/data/talents/chronomancy/age-manipulation.lua b/game/modules/tome/data/talents/chronomancy/age-manipulation.lua
index 63c22820e1..57d54e0542 100644
--- a/game/modules/tome/data/talents/chronomancy/age-manipulation.lua
+++ b/game/modules/tome/data/talents/chronomancy/age-manipulation.lua
@@ -31,7 +31,7 @@ newTalent{
 	proj_speed = 3,
 	direct_hit = true,
 	getDamage = function(self, t) return self:combatTalentSpellDamage(t, 20, 200)*getParadoxModifier(self, pm) end,
-	getDamageStat = function(self, t) return 5 + math.ceil(t.getDamage(self, t) / 10) end,
+	getDamageStat = function(self, t) return 2 + math.ceil(t.getDamage(self, t) / 15) end,
 	action = function(self, t)
 		local tg = {type="bolt", range=self:getTalentRange(t), talent=t, display={particle="temporal_bolt"}}
 		local x, y = self:getTarget(tg)
diff --git a/game/modules/tome/data/talents/chronomancy/energy.lua b/game/modules/tome/data/talents/chronomancy/energy.lua
index 9ef85bd42f..b31578a8e4 100644
--- a/game/modules/tome/data/talents/chronomancy/energy.lua
+++ b/game/modules/tome/data/talents/chronomancy/energy.lua
@@ -67,11 +67,72 @@ newTalent{
 }
 
 newTalent{
-	name = "Energy Wall",
-	type = {"chronomancy/energy", 2},
+	name = "Energy Decomposition",
+	type = {"chronomancy/energy",2},
 	require = chrono_req2,
 	points = 5,
-	paradox = 5,
+	paradox = 15,
+	cooldown = 24,
+	tactical = { DISABLE = 2 },
+	direct_hit = true,
+	requires_target = true,
+	range = 6,
+	getRemoveCount = function(self, t) return math.floor(self:getTalentLevel(t)*getParadoxModifier(self, pm)) end,
+	action = function(self, t)
+		local tg = {type="hit", range=self:getTalentRange(t)}
+		local tx, ty = self:getTarget(tg)
+		if not tx or not ty then return nil end
+		tx, ty = checkBackfire(self, tx, ty)
+		if game.level.map(tx, ty, Map.ACTOR) then
+			target = game.level.map(tx, ty, Map.ACTOR)
+		end
+		
+		local effs = {}
+
+		-- Go through all spell effects
+		for eff_id, p in pairs(target.tmp) do
+		local e = target.tempeffect_def[eff_id]
+			if e.type == "magical" then
+				effs[#effs+1] = {"effect", eff_id}
+			end
+		end
+
+		-- Go through all sustained spells
+		for tid, act in pairs(target.sustain_talents) do
+		local talent = target:getTalentFromId(tid)
+			  if talent.is_spell then
+				effs[#effs+1] = {"talent", tid}
+			end
+		end
+
+		for i = 1, t.getRemoveCount(self, t) do
+			if #effs == 0 then break end
+			local eff = rng.tableRemove(effs)
+			
+			if eff[1] == "effect" then
+				target:removeEffect(eff[2])
+			else
+				target:forceUseTalent(eff[2], {ignore_energy=true})
+			end
+		end
+		game.level.map:particleEmitter(tx, ty, 1, "temporal_thrust")
+		game:playSoundNear(self, "talents/spell_generic")
+		return true
+	end,
+	info = function(self, t)
+		local count = t.getRemoveCount(self, t)
+		return ([[Removes up to %d magical effects or sustained spells(both good and bad) from the target.
+		The number of effects removed will scale with your Paradox.]]):
+		format(count)
+	end,
+}
+
+newTalent{
+	name = "Energy Wall",
+	type = {"chronomancy/energy", 3},
+	require = chrono_req3,
+	points = 5,
+	paradox = 20,
 	cooldown = 20,
 	range = 6,
 	tactical = { PROTECT = 2 },
@@ -123,7 +184,7 @@ newTalent{
 
 			inc_damage = table.clone(self.inc_damage, true),
 
-			combat = { dam=8, atk=15, apr=100, damtype=DamageType.LIGHTNING, dammod={mag=1} },
+			combat = { dam=8, atk=15, apr=100, damtype=DamageType.LIGHTNING, dammod={mag=0.5} },
 			on_melee_hit = { [DamageType.LIGHTNING] = 20, 10},
 				
 			summoner = self, summoner_gain_exp=true,
@@ -144,66 +205,6 @@ newTalent{
 	end,
 }
 
-newTalent{
-	name = "Energy Decomposition",
-	type = {"chronomancy/energy",3},
-	require = chrono_req3,
-	points = 5,
-	paradox = 20,
-	cooldown = 24,
-	tactical = { DISABLE = 2 },
-	direct_hit = true,
-	requires_target = true,
-	range = 6,
-	getRemoveCount = function(self, t) return math.floor(self:getTalentLevel(t)*getParadoxModifier(self, pm)) end,
-	action = function(self, t)
-		local tg = {type="hit", range=self:getTalentRange(t)}
-		local tx, ty = self:getTarget(tg)
-		if not tx or not ty then return nil end
-		tx, ty = checkBackfire(self, tx, ty)
-		if game.level.map(tx, ty, Map.ACTOR) then
-			target = game.level.map(tx, ty, Map.ACTOR)
-		end
-		
-		local effs = {}
-
-		-- Go through all spell effects
-		for eff_id, p in pairs(target.tmp) do
-		local e = target.tempeffect_def[eff_id]
-			if e.type == "magical" then
-				effs[#effs+1] = {"effect", eff_id}
-			end
-		end
-
-		-- Go through all sustained spells
-		for tid, act in pairs(target.sustain_talents) do
-			if act then
-				effs[#effs+1] = {"talent", tid}
-			end
-		end
-
-		for i = 1, t.getRemoveCount(self, t) do
-			if #effs == 0 then break end
-			local eff = rng.tableRemove(effs)
-			
-			if eff[1] == "effect" then
-				target:removeEffect(eff[2])
-			else
-				target:forceUseTalent(eff[2], {ignore_energy=true})
-			end
-		end
-		game.level.map:particleEmitter(tx, ty, 1, "temporal_thrust")
-		game:playSoundNear(self, "talents/spell_generic")
-		return true
-	end,
-	info = function(self, t)
-		local count = t.getRemoveCount(self, t)
-		return ([[Removes up to %d magical effects (both good and bad) from the target.
-		The number of effects removed will scale with your Paradox.]]):
-		format(count)
-	end,
-}
-
 newTalent{
 	name = "Redux",
 	type = {"chronomancy/energy",4},
diff --git a/game/modules/tome/data/talents/chronomancy/spacetime-weaving.lua b/game/modules/tome/data/talents/chronomancy/spacetime-weaving.lua
index 8cd8dd6c54..963f5128bd 100644
--- a/game/modules/tome/data/talents/chronomancy/spacetime-weaving.lua
+++ b/game/modules/tome/data/talents/chronomancy/spacetime-weaving.lua
@@ -17,6 +17,44 @@
 -- Nicolas Casalini "DarkGod"
 -- darkgod@te4.org
 
+newTalent{
+	name = "Spacetime Tuning",
+	type = {"chronomancy/spacetime-weaving", 1},
+	hide = true,
+	points = 1,
+	message = "@Source@ retunes the fabric of spacetime.",
+	cooldown = 100,
+	tactical = { PARADOX = 2 },
+	no_npc_use = true,
+	no_energy = true,
+	getAnomaly = function(self, t) return 6 - (self:getTalentLevelRaw(self.T_STATIC_HISTORY) or 0) end,
+	action = function(self, t)
+		-- open dialog to get desired paradox
+		local q = engine.dialogs.GetQuantity.new("Retuning the fabric of spacetime...", "What's your desired paradox level?", math.floor(self.paradox), nil, function(qty)
+			
+			-- get reduction amount and find duration
+			amount = qty - self.paradox
+			local dur = math.floor(math.abs(qty-self.paradox)/self:getWil())
+			
+			-- set tuning effect
+			if amount >= 0 then
+				self:setEffect(self.EFF_SPACETIME_TUNING, dur, {power = self:getWil()})
+			elseif amount < 0 then
+				self:setEffect(self.EFF_SPACETIME_TUNING, dur, {power = -self:getWil()})
+			end
+			
+		end)
+		game:registerDialog(q)
+		return true
+	end,
+	info = function(self, t)
+		local chance = t.getAnomaly(self, t)
+		return ([[Retunes your Paradox to the desired level.  You will be dazed while tuning and each turn your Paradox will increase or decrease by an amount equal to your Willpower stat.
+		Each turn you have a %d%% chance of triggering a temporal anomaly which will end the tuning process.]]):
+		format(chance)
+	end,
+}
+
 newTalent{
 	name = "Static History",
 	type = {"chronomancy/spacetime-weaving", 1},
@@ -121,6 +159,7 @@ newTalent{
 	getRadius = function(self, t) return math.floor(7 - self:getTalentLevel(t)) end,
 	getRange = function (self, t) return 10 + math.floor (self:getTalentLevel(t)/2) end,
 	getDuration = function (self, t) return 5 + math.floor(self:getTalentLevel(t)*getParadoxModifier(self, pm)) end,
+	no_npc_use = true,
 	action = function(self, t)
 		local tg = {type="bolt", nowarning=true, range=self:getTalentRange(t), nolock=true, talent=t}
 		local entrance_x, entrance_y = self:getTarget(tg)
diff --git a/game/modules/tome/data/talents/chronomancy/speed-control.lua b/game/modules/tome/data/talents/chronomancy/speed-control.lua
index fdbfb697fb..3df5e18efa 100644
--- a/game/modules/tome/data/talents/chronomancy/speed-control.lua
+++ b/game/modules/tome/data/talents/chronomancy/speed-control.lua
@@ -33,7 +33,7 @@ newTalent{
 	info = function(self, t)
 		local power = t.getPower(self, t)
 		return ([[You encase yourself in a shield that slows incoming projectiles by %d%% and grants you %d%% physical resistance for 10 turns.  Additionally any attacker that strikes you in melee will suffer %0.2f temporal damage.
-		The effect will scale with your Paradox and Magic stat.]]):format(power, power / 2, damDesc(self, DamageType.TEMPORAL, power))
+		The effect will scale with your Paradox and Magic stat.]]):format(power, power / 2, damDesc(self, DamageType.TEMPORAL, power / 2))
 	end,
 }
 
diff --git a/game/modules/tome/data/talents/chronomancy/timeline-threading.lua b/game/modules/tome/data/talents/chronomancy/timeline-threading.lua
index 1d155e07d1..b292ca8ae1 100644
--- a/game/modules/tome/data/talents/chronomancy/timeline-threading.lua
+++ b/game/modules/tome/data/talents/chronomancy/timeline-threading.lua
@@ -166,15 +166,17 @@ newTalent{
 	paradox = 100,
 	cooldown = 100,
 	no_npc_use = true,
+	getDuration = function(self, t) return 4 + math.floor(self:getTalentLevel(t) * getParadoxModifier(self, pm)) end,
 	action = function(self, t)
 		if checkTimeline(self) == true then
 			return
 		end
-		self:setEffect(self.EFF_SEE_THREADS, 10, {})
+		self:setEffect(self.EFF_SEE_THREADS, t.getDuration(self, t), {})
 		return true
 	end,
 	info = function(self, t)
-		return ([[]])
-		:format()
+		local duration = t.getDuration(self, t)
+		return ([[You peer %d turns into three different possible futures and then follow one of them into the present.  Note that seeing visions of your own death is fatal.]])
+		:format(duration)
 	end,
 }
diff --git a/game/modules/tome/data/talents/chronomancy/timetravel.lua b/game/modules/tome/data/talents/chronomancy/timetravel.lua
index 76133b62ac..e269782712 100644
--- a/game/modules/tome/data/talents/chronomancy/timetravel.lua
+++ b/game/modules/tome/data/talents/chronomancy/timetravel.lua
@@ -49,6 +49,8 @@ newTalent{
 	end,
 }
 
+-- Time Skip and other Overlay talents like jumpgate are causing issues on remove.  They'll eat other overlays.
+
 newTalent{
 	name = "Time Skip",
 	type = {"chronomancy/timetravel",2},
@@ -206,8 +208,8 @@ newTalent{
 	end,
 	info = function(self, t)
 		local percent = t.getPercent(self, t)
-		return ([[Casting this spell sends you back to the moment you entered the current dungeon level.
-		Traveling through time carries with it inherent penalties and doing so will permanently reduce your hit points by %d%%.]])
+		return ([[Casting this spell sends you back to the moment you entered the current dungeon level.  Traveling through time carries with it inherent penalties and doing so will permanently reduce your hit points by %d%%.
+		Additional talent points will lower the hit point cost.]])
 		:format(percent)
 	end,
 }
diff --git a/game/modules/tome/data/talents/techniques/battle-tactics.lua b/game/modules/tome/data/talents/techniques/battle-tactics.lua
new file mode 100644
index 0000000000..d41d90cdeb
--- /dev/null
+++ b/game/modules/tome/data/talents/techniques/battle-tactics.lua
@@ -0,0 +1,123 @@
+-- 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 = "Greater Weapon Focus",
+	type = {"technique/battle-tactics", 1},
+	require = techs_req_high1,
+	points = 5,
+	cooldown = 20,
+	stamina = 25,
+	no_energy = true,
+	tactical = { ATTACK = 3 },
+	action = function(self, t)
+		self:setEffect(self.EFF_GREATER_WEAPON_FOCUS, math.floor(4 + self:getTalentLevel(t) * 1.3), {chance=self:combatTalentStatDamage(t, "dex", 10, 90)})
+		return true
+	end,
+	info = function(self, t)
+		return ([[Concentrate on your blows, each strike has %d%% chance to deal an other similar blow for %d turns.
+		This works for all blows, even ones form other talents and shield bashes.
+		The chance increaseswith your Dexterity.]]):format(self:combatTalentStatDamage(t, "dex", 10, 90), math.floor(4 + self:getTalentLevel(t) * 1.3))
+	end,
+}
+
+newTalent{
+	name = "Step Up",
+	type = {"technique/battle-tactics", 2},
+	require = techs_req_high2,
+	mode = "passive",
+	points = 5,
+	info = function(self, t)
+		return ([[After killing a foe you have %d%% chances to gain a 1000%% movement speed bonus for 6 turns.
+		The bonus disappears as soon as any action other than moving is done.]]):format(self:getTalentLevelRaw(t) * 20)
+	end,
+}
+
+newTalent{
+	name = "Bleeding Edge",
+	type = {"technique/battle-tactics", 3},
+	require = techs_req_high3,
+	points = 5,
+	cooldown = 12,
+	stamina = 24,
+	tactical = { ATTACK = 1, DISABLE = 2 },
+	action = function(self, t)
+		local tg = {type="hit", range=self:getTalentRange(t)}
+		local x, y, target = self:getTarget(tg)
+		if not x or not y or not target then return nil end
+		if math.floor(core.fov.distance(self.x, self.y, x, y)) > 1 then return nil end
+
+		local hit = self:attackTarget(target, nil, self:combatTalentWeaponDamage(t, 1, 1.7), true)
+		if hit then
+			if target:checkHit(self:combatAttackDex(), target:combatPhysicalResist(), 0, 95, 5) and target:canBe("cut") then
+				local sw = self:getInven("MAINHAND")
+				if sw then
+					sw = sw[1] and sw[1].combat
+				end
+				sw = sw or self.combat
+				local dam = self:combatDamage(sw)
+				local damrange = self:combatDamageRange(sw)
+				dam = rng.range(dam, dam * damrange)
+				dam = dam * self:combatTalentWeaponDamage(t, 2, 3.2)
+
+				target:setEffect(target.EFF_DEEP_WOUND, 7, {heal_factor=self:getTalentLevel(t) * 10, power=dam / 7})
+			end
+		end
+		return true
+	end,
+	info = function(self, t)
+		return ([[Lashes at the target, doing %d%% weapon damage.
+		If the attack hits the target will bleed for %d%% weapon damage over 7 turns and all healing will be reduced by %d%%.]]):
+		format(100 * self:combatTalentWeaponDamage(t, 1, 1.7), 100 * self:combatTalentWeaponDamage(t, 2, 3.2), self:getTalentLevel(t) * 10)
+	end,
+}
+
+newTalent{
+	name = "True Grit",
+	type = {"technique/battle-tactics", 4},
+	require = techs_req_high4,
+	points = 5,
+	mode = "sustained",
+	cooldown = 30,
+	sustain_stamina = 70,
+	tactical = { BUFF = 2 },
+	do_turn = function(self, t)
+		local p = self:isTalentActive(t.id)
+		if p.resid then self:removeTemporaryValue("resists", p.resid) end
+		local perc = (1 - (self.life / self.max_life)) * 10 * (1 + math.floor(self:getTalentLevel(t) / 1.4))
+		p.resid = self:addTemporaryValue("resists", {all=perc})
+	end,
+	activate = function(self, t)
+		return {
+			stamina = self:addTemporaryValue("stamina_regen", -15),
+		}
+	end,
+	deactivate = function(self, t, p)
+		if p.resid then self:removeTemporaryValue("resists", p.resid) end
+		self:removeTemporaryValue("stamina_regen", p.stamina)
+		return true
+	end,
+	info = function(self, t)
+		return ([[Take an defensive stance to resist the onslaught of your foes.
+		For each 10%% of your total life you lack, you gain %d%% global damage resistance.
+		This consumes stamina rapidly(-15 stamina/turn).]]):
+		format(1 + math.floor(self:getTalentLevel(t) / 1.4))
+	end,
+}
diff --git a/game/modules/tome/data/talents/techniques/techniques.lua b/game/modules/tome/data/talents/techniques/techniques.lua
index 3166b75217..c0445c799e 100644
--- a/game/modules/tome/data/talents/techniques/techniques.lua
+++ b/game/modules/tome/data/talents/techniques/techniques.lua
@@ -30,6 +30,7 @@ newTalentType{ allow_random=true, type="technique/archery-sling", name = "archer
 newTalentType{ allow_random=true, type="technique/archery-training", name = "archery - common", description = "Generic archery techniques." }
 newTalentType{ allow_random=true, type="technique/archery-utility", name = "archery - utility", description = "Specialized archery techniques to maim your targets." }
 newTalentType{ allow_random=true, type="technique/superiority", name = "superiority", description = "Advanced combat techniques." }
+newTalentType{ allow_random=true, type="technique/battle-tactics", name = "battle tactics", description = "Advanced combat tactics." }
 newTalentType{ allow_random=true, type="technique/warcries", name = "warcries", no_silence = true, description = "Master the warcries to improve yourself and weaken others." }
 newTalentType{ allow_random=true, type="technique/bloodthirst", name = "bloodthirst", description = "Delight in the act of battle and the spilling of blood." }
 newTalentType{ allow_random=true, type="technique/field-control", name = "field control", generic = true, description = "Control the battlefield using various techniques." }
@@ -139,6 +140,7 @@ load("/data/talents/techniques/weaponshield.lua")
 load("/data/talents/techniques/superiority.lua")
 load("/data/talents/techniques/warcries.lua")
 load("/data/talents/techniques/bloodthirst.lua")
+load("/data/talents/techniques/battle-tactics.lua")
 load("/data/talents/techniques/field-control.lua")
 load("/data/talents/techniques/combat-techniques.lua")
 load("/data/talents/techniques/combat-training.lua")
diff --git a/game/modules/tome/data/timed_effects.lua b/game/modules/tome/data/timed_effects.lua
index d3a3547f98..90ab5e6eda 100644
--- a/game/modules/tome/data/timed_effects.lua
+++ b/game/modules/tome/data/timed_effects.lua
@@ -73,6 +73,26 @@ newEffect{
 	end,
 }
 
+newEffect{
+	name = "DEEP_WOUND",
+	desc = "Deep Wound",
+	long_desc = function(self, eff) return ("Huge cut that bleeds blood, doing %0.2f physical damage per turn and decreasing all heals received by %d%%."):format(eff.power, eff.heal_factor) end,
+	type = "physical",
+	status = "detrimental",
+	parameters = {power=10, heal_factor=30},
+	on_gain = function(self, err) return "#Target# starts to bleed.", "+Deep Wounds" end,
+	on_lose = function(self, err) return "#Target# stops bleeding.", "-Deep Wounds" end,
+	activate = function(self, eff)
+		eff.healid = self:addTemporaryValue("healing_factor", -eff.heal_factor / 100)
+	end,
+	on_timeout = function(self, eff)
+		DamageType:get(DamageType.PHYSICAL).projector(eff.src or self, self.x, self.y, DamageType.PHYSICAL, eff.power)
+	end,
+	deactivate = function(self, eff)
+		self:removeTemporaryValue("healing_factor", eff.healid)
+	end,
+}
+
 newEffect{
 	name = "MANAFLOW",
 	desc = "Manaflow",
@@ -2213,6 +2233,27 @@ newEffect{
 	end,
 }
 
+newEffect{
+	name = "STEP_UP",
+	desc = "Step Up",
+	long_desc = function(self, eff) return ("Movement is %d%% faster."):format(eff.power) end,
+	type = "physical",
+	status = "beneficial",
+	parameters = {power=1000},
+	on_gain = function(self, err) return "#Target# prepares for the next kill!.", "+Step Up" end,
+	on_lose = function(self, err) return "#Target# slows down.", "-Step Up" end,
+	activate = function(self, eff)
+		eff.tmpid = self:addTemporaryValue("step_up", 1)
+		eff.moveid = self:addTemporaryValue("energy", {mod=self.energy.mod*eff.power/100})
+		if self.ai_state then eff.aiid = self:addTemporaryValue("ai_state", {no_talents=1}) end -- Make AI not use talents while using it
+	end,
+	deactivate = function(self, eff)
+		self:removeTemporaryValue("step_up", eff.tmpid)
+		if eff.aiid then self:removeTemporaryValue("ai_state", eff.aiid) end
+		self:removeTemporaryValue("energy", eff.moveid)
+	end,
+}
+
 newEffect{
 	name = "LIGHTNING_SPEED",
 	desc = "Lightning Speed",
@@ -2324,7 +2365,7 @@ newEffect{
 	on_lose = function(self, err) return "The entropic shield around #Target# dissipates.", "-Entropic Shield" end,
 	activate = function(self, eff)
 		eff.particle = self:addParticles(Particles.new("time_shield", 1))
-		eff.tmpid = self:addTemporaryValue("on_melee_hit", {[DamageType.TEMPORAL]= eff.power})
+		eff.tmpid = self:addTemporaryValue("on_melee_hit", {[DamageType.TEMPORAL]= eff.power/2})
 		eff.phys = self:addTemporaryValue("resists", {[DamageType.PHYSICAL]=eff.power/2})
 		eff.proj = self:addTemporaryValue("slow_projectiles", eff.power)
 	end,
@@ -3334,4 +3375,55 @@ newEffect{
 	deactivate = function(self, eff)
 		self:removeTemporaryValue("resists", eff.tmpid)
 	end,
-}
\ No newline at end of file
+}
+
+newEffect{
+	name = "GREATER_WEAPON_FOCUS",
+	desc = "Greater Weapon Focus",
+	long_desc = function(self, eff) return ("%d%% chances to score a secondary blow."):format(eff.chance) end,
+	type = "physical",
+	status = "beneficial",
+	parameters = { chance=50 },
+	activate = function(self, eff)
+	end,
+	deactivate = function(self, eff)
+	end,
+}
+
+newEffect{
+	name = "SPACETIME_TUNING",
+	desc = "Spacetime Tuning",
+	long_desc = function(self, eff) return "The target is retuning the fabric of spacetime, any damage will stop it." end,
+	type = "magical",
+	status = "detrimental",
+	parameters = {},
+	on_timeout = function(self, eff)
+		local t = self:getTalentFromId(self.T_SPACETIME_TUNING)
+		local anomaly = t.getAnomaly(self, t)
+
+		-- first check for anomaly
+		if rng.percent(anomaly) then
+			-- Random anomaly
+			local ts = {}
+			for id, t in pairs(self.talents_def) do
+				if t.type[1] == "chronomancy/anomalies" then ts[#ts+1] = id end
+			end
+			if not silent then game.logPlayer(self, "You lose control and unleash an anomaly!") end
+			self:forceUseTalent(rng.table(ts), {ignore_energy=true})
+			-- cancel tuning
+			self:removeEffect(self.EFF_SPACETIME_TUNING)
+		else
+			self:incParadox(eff.power)
+		end
+
+	end,
+	activate = function(self, eff)
+		-- energy gets used here instead of in the talent since the talent will use it when the dialog box pops up and mess with our daze
+		self:useEnergy()
+		-- set daze
+		eff.tmpid = self:addTemporaryValue("dazed", 1)
+	end,
+	deactivate = function(self, eff)
+		self:removeTemporaryValue("dazed", eff.tmpid)
+	end,
+}
-- 
GitLab