Skip to content
Snippets Groups Projects
Combat.lua 48.7 KiB
Newer Older
dg's avatar
dg committed
	end
dg's avatar
dg committed
	return dam, crit
dg's avatar
dg committed
end
dg's avatar
dg committed

--- Do we get hit by our own AOE ?
function _M:spellFriendlyFire()
	local chance = (self:getLck() - 50) * 0.2
	if self:isTalentActive(self.T_SPELLCRAFT) then chance = chance + self:getTalentLevelRaw(self.T_SPELLCRAFT) * 20 end
	chance = 100 - chance
	print("[SPELL] friendly fire chance", chance)
	return chance
--- Computes mind crit for a damage
function _M:mindCrit(dam, add_chance)
	if self:isTalentActive(self.T_STEALTH) and self:knowTalent(self.T_SHADOWSTRIKE) then
		return dam * (1.5 + self:getTalentLevel(self.T_SHADOWSTRIKE) / 7), true
	end

	local chance = self:combatMindCrit() + (add_chance or 0)
	local crit = false

	print("[MIND CRIT %]", chance)
	if rng.percent(chance) then
		dam = dam * (1.5 + (self.combat_critical_power or 0) / 100)
		crit = true
		game.logSeen(self, "#{bold}#%s's power attains critical effect!#{normal}#", self.name:capitalize())
	end
	return dam, crit
end

--- Gets mindpower
function _M:combatMindpower(mod)
	mod = mod or 1
	local add = 0
dg's avatar
dg committed
	return self:rescaleCombatStats((self.combat_mindpower > 0 and self.combat_mindpower or 0) + add + self:getWil() * 0.7 + self:getCun() * 0.4) * mod
end

--- Gets damage based on talent
function _M:combatTalentMindDamage(t, base, max)
	-- Compute at "max"
	local mod = max / ((base + 100) * ((math.sqrt(5) - 1) * 0.8 + 1))
	-- Compute real
dg's avatar
dg committed
	return self:rescaleDamage((base + (self:combatMindpower())) * ((math.sqrt(self:getTalentLevel(t)) - 1) * 0.8 + 1) * mod)
--- Gets damage based on talent
function _M:combatTalentStatDamage(t, stat, base, max)
	-- Compute at "max"
	local mod = max / ((base + 100) * ((math.sqrt(5) - 1) * 0.8 + 1))
	-- Compute real
dg's avatar
dg committed
	local dam = (base + (self:getStat(stat))) * ((math.sqrt(self:getTalentLevel(t)) - 1) * 0.8 + 1) * mod
	dam =  dam * (1 - math.log10(dam * 2) / 7)
	dam = dam ^ (1 / 1.04)
	return self:rescaleDamage(dam)
dg's avatar
dg committed
--- Gets damage based on talent, basic stat, and interval
function _M:combatTalentIntervalDamage(t, stat, min, max, stat_weight)
	local stat_weight = stat_weight or 0.5
dg's avatar
dg committed
	local dam = min + (max - min)*((stat_weight * self:getStat(stat)/100) + (1 - stat_weight) * self:getTalentLevel(t)/6.5)
	dam =  dam * (1 - math.log10(dam * 2) / 7)
	dam = dam ^ (1 / 1.04)
	return self:rescaleDamage(dam)
end

--- Gets damage based on talent, stat, and interval
function _M:combatStatTalentIntervalDamage(t, stat, min, max, stat_weight)
	local stat_weight = stat_weight or 0.5
	scaled_stat = self[stat](self)
	return self:rescaleDamage(min + (max - min)*((stat_weight * self[stat](self)/100) + (1 - stat_weight) * self:getTalentLevel(t)/6.5))
dg's avatar
dg committed
end

dg's avatar
dg committed
--- Computes physical resistance
--- Fake denotes a check not actually being made, used by character sheets etc.
function _M:combatPhysicalResist(fake)
dg's avatar
dg committed
	local add = 0
dg's avatar
dg committed
	if not fake then
		add = add + (self:checkOnDefenseCall("physical") or 0)
	end
dg's avatar
dg committed
	if self:knowTalent(self.T_POWER_IS_MONEY) then
		add = add + util.bound(self.money / (80 - self:getTalentLevelRaw(self.T_POWER_IS_MONEY) * 5), 0, self:getTalentLevelRaw(self.T_POWER_IS_MONEY) * 10)
	end
dg's avatar
dg committed
	return self:rescaleCombatStats(self.combat_physresist + (self:getCon() + self:getStr() + (self:getLck() - 50) * 0.5) * 0.35 + add)
dg's avatar
dg committed
end

--- Computes spell resistance
--- Fake denotes a check not actually being made, used by character sheets etc.
function _M:combatSpellResist(fake)
dg's avatar
dg committed
	local add = 0
dg's avatar
dg committed
	if not fake then
		add = add + (self:checkOnDefenseCall("spell") or 0)
	end
dg's avatar
dg committed
	if self:knowTalent(self.T_POWER_IS_MONEY) then
		add = add + util.bound(self.money / (80 - self:getTalentLevelRaw(self.T_POWER_IS_MONEY) * 5), 0, self:getTalentLevelRaw(self.T_POWER_IS_MONEY) * 10)
dg's avatar
dg committed
	end
dg's avatar
dg committed
	return self:rescaleCombatStats(self.combat_spellresist + (self:getMag() + self:getWil() + (self:getLck() - 50) * 0.5) * 0.35 + add)
dg's avatar
dg committed
end
dg's avatar
dg committed

--- Computes mental resistance
--- Fake denotes a check not actually being made, used by character sheets etc.
function _M:combatMentalResist(fake)
dg's avatar
dg committed
	local add = 0
dg's avatar
dg committed
	if not fake then
		add = add + (self:checkOnDefenseCall("mental") or 0)
	end
dg's avatar
dg committed
	if self:knowTalent(self.T_STEADY_MIND) then
		local t = self:getTalentFromId(self.T_STEADY_MIND)
		add = add + t.getMental(self, t)
	end
dg's avatar
dg committed
	if self:knowTalent(self.T_POWER_IS_MONEY) then
		add = add + util.bound(self.money / (80 - self:getTalentLevelRaw(self.T_POWER_IS_MONEY) * 5), 0, self:getTalentLevelRaw(self.T_POWER_IS_MONEY) * 10)
dg's avatar
dg committed
	end
dg's avatar
dg committed
	return self:rescaleCombatStats(self.combat_mentalresist + (self:getCun() + self:getWil() + (self:getLck() - 50) * 0.5) * 0.35 + add)
-- Called when a Save or Defense is checked
function _M:checkOnDefenseCall(type)
	local add = 0
	if self:knowTalent(self.T_SPIN_FATE) then
		print("Spin Fate", type)
		local t = self:getTalentFromId(self.T_SPIN_FATE)
		t.do_spin_fate(self, t, type)
	end
	return add
end

--- Returns the resistance
function _M:combatGetResist(type)
	local a = (self.resists.all or 0) / 100
	local b = (self.resists[type] or 0) / 100
	local r = math.min(100 * (1 - (1 - a) * (1 - b)), (self.resists_cap.all or 0) + (self.resists_cap[type] or 0))
	return r
end

dg's avatar
dg committed
--- Computes movement speed
function _M:combatMovementSpeed()
dg's avatar
dg committed
	local mult = 1
	if game.level and game.level.data.zero_gravity then
dg's avatar
dg committed
		mult = 3
	end
	return mult * (self.base_movement_speed or 1) / self.movement_speed
dg's avatar
dg committed
end

dg's avatar
dg committed
--- Check if the actor has a gem bomb in quiver
function _M:hasAlchemistWeapon()
	if not self:getInven("QUIVER") then return nil, "no ammo" end
	local ammo = self:getInven("QUIVER")[1]
	if not ammo or not ammo.alchemist_power then
		return nil, "bad or no ammo"
	end
	return ammo
end

dg's avatar
dg committed
--- Check if the actor has a staff weapon
function _M:hasStaffWeapon()
dg's avatar
dg committed
	if self:attr("disarmed") then
		return nil, "disarmed"
	end

dg's avatar
dg committed
	if not self:getInven("MAINHAND") then return end
	local weapon = self:getInven("MAINHAND")[1]
	if not weapon or weapon.subtype ~= "staff" then
		return nil
	end
	return weapon
end

dg's avatar
dg committed
--- Check if the actor has an axe weapon
function _M:hasAxeWeapon()
	if self:attr("disarmed") then
		return nil, "disarmed"
	end

	if not self:getInven("MAINHAND") then return end
	local weapon = self:getInven("MAINHAND")[1]
	if not weapon or (weapon.subtype ~= "battleaxe" and weapon.subtype ~= "waraxe") then
		return nil
	end
	return weapon
end

--- Check if the actor has a cursed weapon
function _M:hasCursedWeapon()
	if self:attr("disarmed") then
		return nil, "disarmed"
	end

	if not self:getInven("MAINHAND") then return end
	local weapon = self:getInven("MAINHAND")[1]
	if not weapon or not weapon.curse then
	local t = self:getTalentFromId(self.T_DEFILING_TOUCH)
	if not t.canCurseItem(self, t, weapon) then return nil end
--- Check if the actor has a cursed weapon
function _M:hasCursedOffhandWeapon()
	if self:attr("disarmed") then
		return nil, "disarmed"
	end

	if not self:getInven("OFFHAND") then return end
	local weapon = self:getInven("OFFHAND")[1]
	if not weapon or not weapon.combat or not weapon.curse then
		return nil
	end
	local t = self:getTalentFromId(self.T_DEFILING_TOUCH)
	if not t.canCurseItem(self, t, weapon) then return nil end
	return weapon
end

--- Check if the actor has a two handed weapon
dg's avatar
dg committed
function _M:hasTwoHandedWeapon()
dg's avatar
dg committed
	if self:attr("disarmed") then
		return nil, "disarmed"
	end

dg's avatar
dg committed
	if not self:getInven("MAINHAND") then return end
dg's avatar
dg committed
	local weapon = self:getInven("MAINHAND")[1]
	if not weapon or not weapon.twohanded then
		return nil
	end
	return weapon
end

--- Check if the actor has a shield
function _M:hasShield()
dg's avatar
dg committed
	if self:attr("disarmed") then
		return nil, "disarmed"
	end

dg's avatar
dg committed
	if not self:getInven("MAINHAND") or not self:getInven("OFFHAND") then return end
	local shield = self:getInven("OFFHAND")[1]
	if not shield or not shield.special_combat then
		return nil
	end
	return shield
dg's avatar
dg committed
end

dg's avatar
dg committed
-- Check if actor is unarmed
function _M:isUnarmed()
	local unarmed = true
	if not self:getInven("MAINHAND") or not self:getInven("OFFHAND") then return end
	local weapon = self:getInven("MAINHAND")[1]
	local offweapon = self:getInven("OFFHAND")[1]
	if weapon or offweapon then
		unarmed = false
	end
	return unarmed
end

dg's avatar
dg committed
--- Check if the actor dual wields
function _M:hasDualWeapon()
dg's avatar
dg committed
	if self:attr("disarmed") then
		return nil, "disarmed"
	end

dg's avatar
dg committed
	if not self:getInven("MAINHAND") or not self:getInven("OFFHAND") then return end
	local weapon = self:getInven("MAINHAND")[1]
	local offweapon = self:getInven("OFFHAND")[1]
	if not weapon or not offweapon or not weapon.combat or not offweapon.combat then
		return nil
	end
	return weapon, offweapon
end
dg's avatar
dg committed

dg's avatar
dg committed
--- Check if the actor has a light armor
function _M:hasLightArmor()
	if not self:getInven("BODY") then return end
	local armor = self:getInven("BODY")[1]
	if not armor or (armor.subtype ~= "cloth" and armor.subtype ~= "light") then
		return nil
	end
	return armor
end

dg's avatar
dg committed
--- Check if the actor has a heavy armor
function _M:hasHeavyArmor()
	if not self:getInven("BODY") then return end
	local armor = self:getInven("BODY")[1]
	if not armor or (armor.subtype ~= "heavy" and armor.subtype ~= "massive") then
dg's avatar
dg committed
		return nil
	end
dg's avatar
dg committed
	return armor
dg's avatar
dg committed
end

--- Check if the actor has a massive armor
function _M:hasMassiveArmor()
	if not self:getInven("BODY") then return end
	local armor = self:getInven("BODY")[1]
	if not armor or armor.subtype ~= "massive" then
		return nil
	end
dg's avatar
dg committed
	return armor
dg's avatar
dg committed
end
dg's avatar
dg committed

dg's avatar
dg committed
-- Unarmed Combat; this handles grapple checks and building combo points
-- Builds Comob; reduces the cooldown on all unarmed abilities on cooldown by one
function _M:buildCombo()
	local duration = 3
	local power = 1
	-- Combo String bonuses
	if self:knowTalent(self.T_COMBO_STRING) then
		local t = self:getTalentFromId(self.T_COMBO_STRING)
dg's avatar
dg committed
		if rng.percent(t.getChance(self, t)) then
			power = 2
		end
dg's avatar
dg committed
		duration = 3 + t.getDuration(self, t)
	end
dg's avatar
dg committed

dg's avatar
dg committed
	if self:knowTalent(self.T_RELENTLESS_STRIKES) then
		local t = self:getTalentFromId(self.T_RELENTLESS_STRIKES)
dg's avatar
dg committed
		self:incStamina(t.getStamina(self, t))
dg's avatar
dg committed
	end
dg's avatar
dg committed
	self:setEffect(self.EFF_COMBO, duration, {power=power})
end

function _M:getCombo(combo)
	local combo = 0
	local p = self:hasEffect(self.EFF_COMBO)
dg's avatar
dg committed
		combo = p.cur_power
	end
		return combo
end

function _M:clearCombo()
	if self:hasEffect(self.EFF_COMBO) then
		self:removeEffect(self.EFF_COMBO)
	end
end

-- Check to see if the target is already being grappled; many talents have extra effects on grappled targets
function _M:isGrappled(source)
	local p = self:hasEffect(self.EFF_GRAPPLED)
	if p and p.src == source then
dg's avatar
dg committed
		return true
	else
		return false
	end
end

-- Breaks active grapples; called by a few talents that involve a lot of movement
function _M:breakGrapples()
	if self:hasEffect(self.EFF_GRAPPLING) then
		local p = self:hasEffect(self.EFF_GRAPPLING)
		if p.trgt then
			p.trgt:removeEffect(p.trgt.EFF_GRAPPLED)
		end
dg's avatar
dg committed
		self:removeEffect(self.EFF_GRAPPLING)
	end
end

-- grapple size check; compares attackers size and targets size
function _M:grappleSizeCheck(target)
	size = target.size_category - self.size_category
	if size > 1 then
		game.logSeen(target, "%s fails because %s is too big!", self.name:capitalize(), target.name:capitalize())
		return true
	else
		return false
	end
end

-- Starts the grapple
function _M:startGrapple(target)
	-- pulls boosted grapple effect from the clinch talent if known
	if self:knowTalent(self.T_CLINCH) then
		local t = self:getTalentFromId(self.T_CLINCH)
		power = t.getPower(self, t)
		duration = t.getDuration(self, t)
		hitbonus = self:getTalentLevel(t)/2
	else
		power = 5
		duration = 4
		hitbonus = 0
	end
	-- Breaks the grapple before reapplying
	if self:hasEffect(self.EFF_GRAPPLING) then
		self:removeEffect(self.EFF_GRAPPLING, true)
		target:setEffect(target.EFF_GRAPPLED, duration, {src=self, power=power}, true)
		self:setEffect(self.EFF_GRAPPLING, duration, {trgt=target}, true)
dg's avatar
dg committed
		return true
dg's avatar
dg committed
		target:setEffect(target.EFF_GRAPPLED, duration, {src=self, power=power, apply_power=self:combatPhysicalpower()})
		target:crossTierEffect(target.EFF_GRAPPLED, self:combatPhysicalpower())
		self:setEffect(self.EFF_GRAPPLING, duration, {trgt=target})
dg's avatar
dg committed
		return true
	else
		game.logSeen(target, "%s resists the grapple!", target.name:capitalize())
		return false
	end