Skip to content
Snippets Groups Projects
Forked from tome / Tales of MajEyal
738 commits behind the upstream repository.
assassination.lua 8.35 KiB
-- ToME - Tales of Maj'Eyal
-- Copyright (C) 2009 - 2019 Nicolas Casalini
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
--
-- Nicolas Casalini "DarkGod"
-- darkgod@te4.org

local DamageType = require "engine.DamageType"
local Object = require "engine.Object"
local Map = require "engine.Map"

newTalent{
	name = "Coup de Grace",
	type = {"technique/assassination", 1},
	points = 5,
	random_ego = "attack",
	cooldown = 8,
	stamina = 24,
	require = techs_dex_req_high1,
	getDamage = function (self, t) return self:combatTalentWeaponDamage(t, 1.0, 1.5) end,
	getPercent = function(self, t) return self:combatTalentLimit(t, 0.3, 0.1, 0.25) end,
	target = function(self, t) return {type="hit", range=self:getTalentRange(t)} end,
	range = 1,
	requires_target = true,
	tactical = { ATTACK = { weapon = 2 } },
	on_pre_use = function(self, t, silent) if not self:hasDualWeapon() then if not silent then game.logPlayer(self, "You require two weapons to use this talent.") end return false end return true end,
	action = function(self, t)
		local weapon, offweapon = self:hasDualWeapon()
		if not weapon then
			game.logPlayer(self, "You cannot use Coup de Grace without dual wielding!")
			return nil
		end

		local tg = self:getTalentTarget(t)
		local x, y, target = self:getTargetLimited(tg)
		if not x or not y or not target then return nil end

		local mult = t.getDamage(self,t)
		if (target.life / target.max_life <= 0.3) then mult = mult * 1.5 end
		-- Attack with offhand first
		local hits = 0
		hits = hits + (self:attackTargetWith(target, offweapon.combat, nil, self:getOffHandMult(offweapon.combat, mult)) and 1 or 0)
		hits = hits + (self:attackTargetWith(target, weapon.combat, nil, mult) and 1 or 0)
		
		if hits > 0 then
			if target:checkHit(self:combatAttack(), target:combatPhysicalResist(), 0, 95) and target:canBe("instakill") and target.life < target.max_life * 0.2 then
				-- KILL IT !
				self:logCombat(target, "#Source# delivers a Coup de Grace against #Target#!")
				target:die(self)
			elseif target.life > 0 and target.life < target.max_life * 0.2 then
				game.logSeen(target, "%s resists the Coup de Grace!", target:getName():capitalize())
			end
		end

		if target.dead and self:knowTalent(self.T_STEALTH) then
			game:onTickEnd(function()
				if not self:isTalentActive(self.T_STEALTH) then
					self.hide_chance = 1000
					self.talents_cd["T_STEALTH"] = 0
					game.logSeen(self, "#GREY#%s slips into shadow.", self:getName():capitalize())
					self:forceUseTalent(self.T_STEALTH, {ignore_energy=true, silent = true})
					self.hide_chance = nil
				end
			end)
		end
		return true
	end,
	info = function(self, t)
		dam = t.getDamage(self,t)*100
		perc = t.getPercent(self,t)*100
		return ([[Attempt to finish off a wounded enemy, striking them with both weapons for %d%% increased by 50%% if their life is below 30%%.  A target brought below 20%% of its maximum life must make a physical save against your Accuracy or be instantly slain.
		You may take advantage of finishing your foe this way to activate stealth (if known).]]):
		tformat(dam, perc)
	end,
}

newTalent{
	name = "Terrorize",
	type = {"technique/assassination", 2},
	require = techs_dex_req_high2,
	points = 5,
	mode = "passive",
	radius = function(self, t) return math.floor(self:combatTalentLimit(t, 10, 2, 5.5)) end,
	range = 0,
	getDuration = function(self, t) return self:combatTalentScale(t, 3, 5) end,
	target = function(self, t)
		return {type="ball", range=self:getTalentRange(t), radius=self:getTalentRadius(t), friendlyfire=false, talent=t}
	end,
	terrorize = function(self, t)
		local tg = self:getTalentTarget(t)
		self:project(tg, self.x, self.y, DamageType.TERROR, {dur=t.getDuration(self,t)})
	end,
	info = function(self, t)
		local radius = self:getTalentRadius(t)
		local duration = t.getDuration(self,t)
		return ([[When you exit stealth, you reveal yourself dramatically, intimidating foes around you. 
		All foes within radius %d that witness you leaving stealth will be stricken with terror, which randomly inflicts stun, slow (40%% power), or confusion (50%% power) for %d turns.
		The chance to terrorize improves with your combat accuracy.]])
		:tformat(radius, duration)
	end,
}

newTalent{
	name = "Garrote",
	type = {"technique/assassination", 3},
	require = techs_dex_req_high3,
	points = 5,
	random_ego = "attack",
	cooldown = 6,
	mode = "passive",
	requires_target = true,
	getDamage = function (self, t) return self:combatTalentWeaponDamage(t, 0.2, 0.6) end,
	getDuration = function(self, t) return math.floor(self:combatTalentScale(t, 3, 7)) end,
	target = function(self, t) return {type="hit", range=self:getTalentRange(t)} end,
	range = 1,
	passives = function(self, t, p)
		self:talentTemporaryValue(p, "show_gloves_combat", 1)
	end,
	callbackOnMeleeAttack = function(self, t, target, hitted, crit, weapon, damtype, mult, dam)
		local dam = t.getDamage(self,t)
		if weapon.no_garrote then return end
		if target and not target.dead and self:isTalentActive(self.T_STEALTH) and not self:isTalentCoolingDown(t) and core.fov.distance(self.x, self.y, target.x, target.y) <= 1 then
			self:startTalentCooldown(t)
			-- check takes the place of normal melee hit chance
			if not self:checkHit(self:combatAttack(), target:combatPhysicalResist()) or not target:canBe("pin") then
				self:logCombat(target, "#Target# avoids a garrote from #Source#!")
				return
			end
			local silence = target:canBe("silence") and math.ceil(t.getDuration(self, t)/2) or 0
			target:setEffect(target.EFF_GARROTE, t.getDuration(self, t), {power=dam, src=self, silence=silence})
		end
	end,
	info = function(self, t)
		local damage = t.getDamage(self, t)*100
		local dur = t.getDuration(self,t)
		local sdur = math.ceil(t.getDuration(self,t)/2)
		return ([[When attacking from stealth, you slip a garrote over the target’s neck (or other vulnerable part).  This strangles for %d turns and silences for %d turns.  Strangled targets are pinned and suffer an automatic unarmed attack for %d%% damage each turn. 
		Your chance to apply the garrote increases with your Accuracy and you must stay adjacent to your target to maintain it.
		This talent has a cooldown.]])
		:tformat(dur, sdur, damage)
	end,
}

newTalent{
	name = "Marked for Death",
	type = {"technique/assassination", 4},
	require = techs_dex_req_high4,
	points = 5,
	cooldown = 25,
	range = 10,
	stamina = 30,
	requires_target = true,
	no_break_stealth = true,
	no_npc_use = true, -- lol 
	getPower = function(self, t) return self:combatTalentScale(t, 15, 40) end,
	getPercent = function(self, t) return self:combatTalentLimit(t, 50, 20, 40) end,
	getDamage = function(self,t) return self:combatTalentStatDamage(t, "dex", 15, 180) end,
	target = function(self, t)
		return {type="hit", range=self:getTalentRange(t), talent=t}
	end,
	action = function(self, t)
		local tg = self:getTalentTarget(t)
		local x, y, target = self:getTarget(tg)
		if not (x and y) or not self:canProject(tg, x, y) then return nil end
		
		self:project(tg, x, y, function(px, py)
		    target = game.level.map(px, py, engine.Map.ACTOR)
			if not target then return end
				target:setEffect(target.EFF_MARKED_FOR_DEATH, 4, {src=self, power=t.getPower(self,t), perc=t.getPercent(self,t)/100, dam = t.getDamage(self,t), stam = t.stamina, max_dur=4})
		end)

		return true
	end,
	info = function(self, t)
		power = t.getPower(self,t)
		perc = t.getPercent(self,t)
		dam = t.getDamage(self,t)
		return ([[You mark a target for death for 4 turns, causing them to take %d%% increased damage from all sources. When this effect ends they will immediately take physical damage equal to %0.2f plus %d%% of all damage taken while marked.
		If a target dies while marked, the cooldown of this ability is reset and the cost refunded.
		This ability can be used without breaking stealth.
		The base damage dealt will increase with your Dexterity.]]):
		tformat(power, damDesc(self, DamageType.PHYSICAL, dam), perc)
	end,
}