Newer
Older
max = math.max(max, self:getTalentLevel(tid))
end
return max
else
return self:getTalentLevel(_M.weapon_talents[weapon.talented])
end
--- Fake denotes a check not actually being made, used by character sheets etc.
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
if not self:attr("encased_in_ice") then
if self:hasDualWeapon() and self:knowTalent(self.T_DUAL_WEAPON_DEFENSE) then
add = add + self:callTalent(self.T_DUAL_WEAPON_DEFENSE,"getDefense")
end
if not fake then
add = add + (self:checkOnDefenseCall("defense") or 0)
end
if self:knowTalent(self.T_TACTICAL_EXPERT) then
local t = self:getTalentFromId(self.T_TACTICAL_EXPERT)
add = add + t.do_tact_update(self, t)
end
if self:knowTalent(self.T_CORRUPTED_SHELL) then
add = add + self:getCon() / 3
end
if self:knowTalent(self.T_STEADY_MIND) then
local t = self:getTalentFromId(self.T_STEADY_MIND)
add = add + t.getDefense(self, t)
end
if self:isTalentActive(Talents.T_SURGE) then
local t = self:getTalentFromId(self.T_SURGE)
add = add + t.getDefenseChange(self, t)
end
local d = math.max(0, self.combat_def + (self:getDex() - 10) * 0.35 + (self:getLck() - 50) * 0.4)
if self:hasLightArmor() and self:knowTalent(self.T_MOBILE_DEFENCE) then
mult = mult + self:callTalent(self.T_MOBILE_DEFENCE,"getDef")
mult = mult + self:callTalent(self.T_MISDIRECTION,"getDefense")/100
return math.max(0, d * mult + add) -- Add bonuses last to avoid compounding defense multipliers from talents
function _M:combatDefense(fake, add)
local base_defense = self:combatDefenseBase(true)
if not fake then base_defense = self:combatDefenseBase() end
local d = math.max(0, base_defense + (add or 0))
return self:rescaleCombatStats(d)
end
--- Gets the defense ranged
function _M:combatDefenseRanged(fake, add)
local base_defense = self:combatDefenseBase(true)
if not fake then base_defense = self:combatDefenseBase() end
local d = math.max(0, base_defense + (self.combat_def_ranged or 0) + (add or 0))
if self:hasHeavyArmor() and self:knowTalent(self.T_ARMOUR_TRAINING) then
local at = Talents:getTalentFromId(Talents.T_ARMOUR_TRAINING)
add = add + at.getArmor(self, at)
if self:knowTalent(self.T_GOLEM_ARMOUR) then
local ga = Talents:getTalentFromId(Talents.T_GOLEM_ARMOUR)
add = add + ga.getArmor(self, ga)
end
if self:knowTalent(self.T_CARBON_SPIKES) and self:isTalentActive(self.T_CARBON_SPIKES) then
add = add + self.carbon_armor
end
if self:knowTalent(self.T_ARMOUR_OF_SHADOWS) and not game.level.map.lites(self.x, self.y) then
add = add + self:callTalent(self.T_ARMOUR_OF_SHADOWS,"ArmourBonus")
--- Gets armor hardiness
-- This is how much % of a blow we can reduce with armor
function _M:combatArmorHardiness()
local add = 0
if self:hasHeavyArmor() and self:knowTalent(self.T_ARMOUR_TRAINING) then
local at = Talents:getTalentFromId(Talents.T_ARMOUR_TRAINING)
add = add + at.getArmorHardiness(self, at)
if self:knowTalent(self.T_GOLEM_ARMOUR) then
local ga = Talents:getTalentFromId(Talents.T_GOLEM_ARMOUR)
add = add + ga.getArmorHardiness(self, ga)
end
if self:hasLightArmor() and self:knowTalent(self.T_MOBILE_DEFENCE) then
add = add + self:callTalent(self.T_MOBILE_DEFENCE, "getHardiness")
if self:knowTalent(self.T_ARMOUR_OF_SHADOWS) and not game.level.map.lites(self.x, self.y) then
add = add + 50
end
function _M:combatAttackBase(weapon, ammo)
return 4 + self.combat_atk + self:getTalentLevel(Talents.T_WEAPON_COMBAT) * 10 + (weapon.atk or 0) + (ammo and ammo.atk or 0) + (self:getLck() - 50) * 0.4
end
function _M:combatAttack(weapon, ammo)
if self.use_psi_combat then stats = self:getCun(100, true) - 10
elseif weapon and weapon.wil_attack then stats = self:getWil(100, true) - 10
local d = self:combatAttackBase(weapon, ammo) + stats
if self:attr("dazed") then d = d / 2 end
return self:rescaleCombatStats(d)
function _M:combatAttackRanged(weapon, ammo)
local stats
if self.use_psi_combat then stats = self:getCun(100, true) - 10
elseif weapon and weapon.wil_attack then stats = self:getWil(100, true) - 10
else stats = self:getDex(100, true) - 10
end
local d = self:combatAttackBase(weapon, ammo) + stats + (self.combat_atk_ranged or 0)
function _M:combatAttackStr(weapon, ammo)
local d = self:combatAttackBase(weapon, ammo) + (self:getStr(100, true) - 10)
if self:attr("dazed") then d = d / 2 end
return self:rescaleCombatStats(d)
function _M:combatAttackDex(weapon, ammo)
local d = self:combatAttackBase(weapon, ammo) + (self:getDex(100, true) - 10)
if self:attr("dazed") then d = d / 2 end
return self:rescaleCombatStats(d)
function _M:combatAttackMag(weapon, ammo)
local d = self:combatAttackBase(weapon, ammo) + (self:getMag(100, true) - 10)
if self:attr("dazed") then d = d / 2 end
return self:rescaleCombatStats(d)
--- Gets the armor penetration
function _M:combatAPR(weapon)
local addapr = 0
return self.combat_apr + (weapon.apr or 0) + addapr
end
--- Gets the weapon speed
function _M:combatSpeed(weapon)
return (weapon.physspeed or 1) / math.max(self.combat_physspeed, 0.1)
end
--- Gets the crit rate
function _M:combatCrit(weapon)
if weapon.talented and self:knowTalent(Talents.T_LETHALITY) then
addcrit = 1 + self:callTalent(Talents.T_LETHALITY, "getCriticalChance")
dg
committed
local crit = self.combat_physcrit + (self:getCun() - 10) * 0.3 + (self:getLck() - 50) * 0.30 + (weapon.physcrit or 1) + addcrit
return math.max(crit, 0) -- note: crit > 100% may be offset by crit reduction elsewhere
-- This currently beefs up high-end damage values to make up for the combat stat rescale nerf.
function _M:rescaleDamage(dam)
if dam <= 0 then return dam end
-- return dam * (1 - math.log10(dam * 2) / 7) --this is the old version, pre-combat-stat-rescale
return dam ^ 1.04
end
--Diminishing-returns method of scaling combat stats, observing this rule: the first twenty ranks cost 1 point each, the second twenty cost two each, and so on. This is much, much better for players than some logarithmic mess, since they always know exactly what's going on, and there are nice breakpoints to strive for.
function _M:rescaleCombatStats(raw_combat_stat_value)
local x = raw_combat_stat_value
local tiers = 50 -- Just increase this if you want to add high-level content that allows for combat stat scores over 100.
--return math.floor(math.min(x, 20) + math.min(math.max((x-20), 0)/2, 20) + math.min(math.max((x-60), 0)/3, 20) + math.min(math.max((x-120), 0)/4, 20) + math.min(math.max((x-200), 0)/5, 20)) --Five terms of the summation below.
local total = 0
for i = 1, tiers do
local sub = 20*(i*(i-1)/2)
total = total + math.min(math.max(x-sub, 0)/i, 20)
end
return total
-- Scale a value up or down by a power
-- x = a numeric value
-- y_low = value to match at x_low
-- y_high = value to match at x_high
-- power = scaling factor (default 0.5)
-- add = amount to add the result (default 0)
-- shift = amount to add to the input value before computation (default 0)
function _M:combatScale(x, y_low, x_low, y_high, x_high, power, add, shift)
power, add, shift = power or 0.5, add or 0, shift or 0
local x_low_adj, x_high_adj = (x_low+shift)^power, (x_high+shift)^power
local m = (y_high - y_low)/(x_high_adj - x_low_adj)
local b = y_low - m*x_low_adj
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
end
-- Scale a value up or down subject to a limit
-- x = a numeric value
-- limit = value approached as x increases
-- y_high = value to match at when x = x_high
-- y_low (optional) = value to match when x = x_low
-- returns (limit - add)*x/(x + halfpoint) + add (= add when x = 0 and limit when x = infinity), halfpoint, add
-- halfpoint and add are internally computed to match the desired high/low values
-- note that the progression low->high->limit must be monotone, consistently increasing or decreasing
function _M:combatLimit(x, limit, y_low, x_low, y_high, x_high)
-- local x_low, x_high = 1,5 -- Implied talent levels for low and high values respectively
-- local tl = type(t) == "table" and (raw and self:getTalentLevelRaw(t) or self:getTalentLevel(t)) or t
if y_low and x_low then
local p = limit*(x_high-x_low)
local m = x_high*y_high - x_low*y_low
local halfpoint = (p-m)/(y_high - y_low)
local add = (limit*(x_high*y_low-x_low*y_high) + y_high*y_low*(x_low-x_high))/(p-m)
return (limit-add)*x/(x + halfpoint) + add
-- return (limit-add)*x/(x + halfpoint) + add, halfpoint, add
else
local add = 0
local halfpoint = limit*x_high/(y_high-add)-x_high
return (limit-add)*x/(x + halfpoint) + add
-- return (limit-add)*x/(x + halfpoint) + add, halfpoint, add
end
end
-- Compute a diminishing returns value based on talent level that scales with a power
-- t = talent def table or a numeric value
-- low = value to match at talent level 1
-- high = value to match at talent level 5
-- power = scaling factor (default 0.5) or "log" for log10
-- add = amount to add the result (default 0)
-- shift = amount to add to the talent level before computation (default 0)
-- raw if true specifies use of raw talent level
function _M:combatTalentScale(t, low, high, power, add, shift, raw)
local tl = type(t) == "table" and (raw and self:getTalentLevelRaw(t) or self:getTalentLevel(t)) or t
power, add, shift = power or 0.5, add or 0, shift or 0
local x_low, x_high = 1, 5 -- Implied talent levels to fit
local x_low_adj, x_high_adj
if power == "log" then
x_low_adj, x_high_adj = math.log10(x_low+shift), math.log10(x_high+shift)
tl = math.max(1, tl)
else
x_low_adj, x_high_adj = (x_low+shift)^power, (x_high+shift)^power
end
local m = (high - low)/(x_high_adj - x_low_adj)
local b = low - m*x_low_adj
if power == "log" then -- always >= 0
return math.max(0, m * math.log10(tl + shift) + b + add)
-- return math.max(0, m * math.log10(tl + shift) + b + add), m, b
else
return math.max(0, m * (tl + shift)^power + b + add)
-- return math.max(0, m * (tl + shift)^power + b + add), m, b
end
end
-- Compute a diminishing returns value based on a stat value that scales with a power
-- stat == "str", "con",.... or a numeric value
-- low = value to match when stat = 10
-- high = value to match when stat = 100
-- power = scaling factor (default 0.5) or "log" for log10
-- add = amount to add the result (default 0)
-- shift = amount to add to the stat value before computation (default 0)
function _M:combatStatScale(stat, low, high, power, add, shift)
stat = type(stat) == "string" and self:getStat(stat,nil,true) or stat
power, add, shift = power or 0.5, add or 0, shift or 0
local x_low, x_high = 10, 100 -- Implied stat values to match
local x_low_adj, x_high_adj
if power == "log" then
x_low_adj, x_high_adj = math.log10(x_low+shift), math.log10(x_high+shift)
stat = math.max(1, stat)
else
x_low_adj, x_high_adj = (x_low+shift)^power, (x_high+shift)^power
end
local m = (high - low)/(x_high_adj - x_low_adj)
local b = low -m*x_low_adj
if power == "log" then -- always >= 0
return math.max(0, m * math.log10(stat + shift) + b + add)
-- return math.max(0, m * math.log10(stat + shift) + b + add), m, b
else
return math.max(0, m * (stat + shift)^power + b + add)
-- return math.max(0, m * (stat + shift)^power + b + add), m, b
end
end
-- Compute a diminishing returns value based on talent level that cannot go beyond a limit
-- t = talent def table or a numeric value
-- limit = value approached as talent levels increase
-- high = value at talent level 5
-- low = value at talent level 1 (optional)
-- raw if true specifies use of raw talent level
-- returns (limit - add)*TL/(TL + halfpoint) + add == add when TL = 0 and limit when TL = infinity
-- TL = talent level, halfpoint and add are internally computed to match the desired high/low values
-- note that the progression low->high->limit must be monotone, consistently increasing or decreasing
function _M:combatTalentLimit(t, limit, low, high, raw)
local x_low, x_high = 1,5 -- Implied talent levels for low and high values respectively
local tl = type(t) == "table" and (raw and self:getTalentLevelRaw(t) or self:getTalentLevel(t)) or t
if low then
local p = limit*(x_high-x_low)
local m = x_high*high - x_low*low
local halfpoint = (p-m)/(high - low)
local add = (limit*(x_high*low-x_low*high) + high*low*(x_low-x_high))/(p-m)
return (limit-add)*tl/(tl + halfpoint) + add
-- return (limit-add)*tl/(tl + halfpoint) + add, halfpoint, add
else
local add = 0
local halfpoint = limit*x_high/(high-add)-x_high
return (limit-add)*tl/(tl + halfpoint) + add
-- return (limit-add)*tl/(tl + halfpoint) + add, halfpoint, add
end
end
-- Compute a diminishing returns value based on a stat value that cannot go beyond a limit
-- stat == "str", "con",.... or a numeric value
-- limit = value approached as talent levels increase
-- high = value to match when stat = 100
-- low = value to match when stat = 10 (optional)
-- returns (limit - add)*stat/(stat + halfpoint) + add == add when STAT = 0 and limit when stat = infinity
-- halfpoint and add are internally computed to match the desired high/low values
-- note that the progression low->high->limit must be monotone, consistently increasing or decreasing
function _M:combatStatLimit(stat, limit, low, high)
local x_low, x_high = 10,100 -- Implied talent levels for low and high values respectively
stat = type(stat) == "string" and self:getStat(stat,nil,true) or stat
if low then
local p = limit*(x_high-x_low)
local m = x_high*high - x_low*low
local halfpoint = (p-m)/(high - low)
local add = (limit*(x_high*low-x_low*high) + high*low*(x_low-x_high))/(p-m)
return (limit-add)*stat/(stat + halfpoint) + add
-- return (limit-add)*stat/(stat + halfpoint) + add, halfpoint, add
else
local add = 0
local halfpoint = limit*x_high/(high-add)-x_high
return (limit-add)*stat/(stat + halfpoint) + add
-- return (limit-add)*stat/(stat + halfpoint) + add, halfpoint, add
end
end
--- Gets the damage
function _M:combatDamage(weapon)
local sub_cun_to_str = false
if weapon.talented and weapon.talented == "knife" and self:knowTalent(Talents.T_LETHALITY) then sub_cun_to_str = true end
if self.use_psi_combat and stat == "str" then stat = "wil" end
if self.use_psi_combat and stat == "dex" then stat = "cun" end
totstat = totstat + self:getStat(stat) * mod
if self:knowTalent(self.T_GREATER_TELEKINETIC_GRASP) then
local g = self:getTalentFromId(self.T_GREATER_TELEKINETIC_GRASP)
totstat = totstat * g.stat_sub(self, g)
else
if self:knowTalent(self.T_SUPERPOWER) then
totstat = totstat + self:getStat("wil") * 0.3
end
if self:knowTalent(self.T_ARCANE_MIGHT) then
totstat = totstat + self:getStat("mag") * 0.5
end
local talented_mod = math.sqrt(self:combatCheckTraining(weapon) / 5) / 2 + 1
local power = math.max((weapon.dam or 1), 1)
power = (math.sqrt(power / 10) - 1) * 0.5 + 1
--print(("[COMBAT DAMAGE] power(%f) totstat(%f) talent_mod(%f)"):format(power, totstat, talented_mod))
return self:rescaleDamage(0.3*(self:combatPhysicalpower(nil, weapon) + totstat) * power * talented_mod)
function _M:combatPhysicalpower(mod, weapon, add)
add = add or 0
if self:knowTalent(Talents.T_ARCANE_DESTRUCTION) then
add = add + self:combatSpellpower() * self:callTalent(Talents.T_ARCANE_DESTRUCTION, "getSPMult")
if self:isTalentActive(Talents.T_BLOOD_FRENZY) then
add = add + self.blood_frenzy
end
if self:knowTalent(self.T_EMPTY_HAND) and self:isUnarmed() then
local t = self:getTalentFromId(self.T_EMPTY_HAND)
add = add + t.getDamage(self, t)
end
if self:attr("psychometry_power") then
add = add + self:attr("psychometry_power")
end
if not weapon then
local inven = self:getInven(self.INVEN_MAINHAND)
if inven and inven[1] then weapon = self:getObjectCombat(inven[1], "mainhand") else weapon = self.combat end
add = add + 10 * self:combatCheckTraining(weapon)
local d = math.max(0, self.combat_dam + add) + self:getStr() -- allows strong debuffs to offset strength
--- Gets damage based on talent
function _M:combatTalentPhysicalDamage(t, base, max)
-- Compute at "max"
local mod = max / ((base + 100) * ((math.sqrt(5) - 1) * 0.8 + 1))
-- Compute real
return self:rescaleDamage((base + (self:combatPhysicalpower())) * ((math.sqrt(self:getTalentLevel(t)) - 1) * 0.8 + 1) * mod)
end
function _M:combatSpellpower(mod, add)
add = add or 0
if self:knowTalent(self.T_ARCANE_CUNNING) then
add = add + self:callTalent(self.T_ARCANE_CUNNING,"getSpellpower") * self:getCun() / 100
if self:knowTalent(self.T_SHADOW_CUNNING) then
add = add + self:callTalent(self.T_SHADOW_CUNNING,"getSpellpower") * self:getCun() / 100
add = add + self:hasEffect(self.EFF_BLOODLUST).power
local am = 1
if self:attr("spellpower_reduction") then am = 1 / (1 + self:attr("spellpower_reduction")) end
local d = (self.combat_spellpower > 0 and self.combat_spellpower or 0) + add + self:getMag()
return self:rescaleCombatStats(d) * mod * am
end
--- Gets damage based on talent
function _M:combatTalentSpellDamage(t, base, max, spellpower_override)
-- Compute at "max"
local mod = max / ((base + 100) * ((math.sqrt(5) - 1) * 0.8 + 1))
-- Compute real
return self:rescaleDamage((base + (spellpower_override or self:combatSpellpower())) * ((math.sqrt(self:getTalentLevel(t)) - 1) * 0.8 + 1) * mod)
--- Gets weapon damage mult based on talent
function _M:combatTalentWeaponDamage(t, base, max, t2)
if t2 then t2 = t2 / 2 else t2 = 0 end
local diff = max - base
local mult = base + diff * math.sqrt((self:getTalentLevel(t) + t2) / 5)
-- print("[TALENT WEAPON MULT]", self:getTalentLevel(t), base, max, t2, mult)
local offmult = 1/2
-- Take the bigger multiplier from Dual weapon training and Corrupted Strength
if self:knowTalent(Talents.T_DUAL_WEAPON_TRAINING) then
offmult = math.max(offmult,self:callTalent(Talents.T_DUAL_WEAPON_TRAINING,"getoffmult"))
offmult = math.max(offmult,self:callTalent(Talents.T_CORRUPTED_STRENGTH,"getoffmult"))
if self:hasEffect(self.EFF_CURSE_OF_MADNESS) then
local eff = self:hasEffect(self.EFF_CURSE_OF_MADNESS)
if eff.level >= 1 and eff.unlockLevel >= 1 then
local def = self.tempeffect_def[self.EFF_CURSE_OF_MADNESS]
offmult = offmult + ((mult or 1) * def.getOffHandMultChange(eff.level) / 100)
end
end
if combat and combat.no_offhand_penalty then
return math.max(1, offmult)
else
return offmult
end
--- Gets fatigue
function _M:combatFatigue()
if self.fatigue < 0 then return 0 end
dg
committed
local crit = self.combat_spellcrit + (self:getCun() - 10) * 0.3 + (self:getLck() - 50) * 0.30 + 1
dg
committed
--- Gets mindcrit
dg
committed
function _M:combatMindCrit(add)
local add = add or 0
dg
committed
if self:knowTalent(self.T_GESTURE_OF_POWER) then
local t = self:getTalentFromId(self.T_GESTURE_OF_POWER)
add = t.getMindCritChange(self, t)
end
dg
committed
local crit = self.combat_mindcrit + (self:getCun() - 10) * 0.3 + (self:getLck() - 50) * 0.30 + 1 + add
dg
committed
end
return 1 / math.max(self.combat_spellspeed, 0.1)
function _M:combatMindSpeed()
return 1 / math.max(self.combat_mindspeed, 0.1)
--- Gets summon speed
function _M:combatSummonSpeed()
return math.max(1 - ((self:attr("fast_summons") or 0) / 100), 0.1)
end
--- Computes physical crit chance reduction
function _M:combatCritReduction()
local crit_reduction = 0
if self:hasHeavyArmor() and self:knowTalent(self.T_ARMOUR_TRAINING) then
local at = Talents:getTalentFromId(Talents.T_ARMOUR_TRAINING)
crit_reduction = crit_reduction + at.getCriticalChanceReduction(self, at)
if self:knowTalent(self.T_GOLEM_ARMOUR) then
local ga = Talents:getTalentFromId(Talents.T_GOLEM_ARMOUR)
crit_reduction = crit_reduction + ga.getCriticalChanceReduction(self, ga)
end
end
if self:attr("combat_crit_reduction") then
crit_reduction = crit_reduction + self:attr("combat_crit_reduction")
end
return crit_reduction
end
function _M:physicalCrit(dam, weapon, target, atk, def, add_chance, crit_power_add)
local chance = self:combatCrit(weapon) + (add_chance or 0)
crit_power_add = crit_power_add or 0
if target:hasEffect(target.EFF_DISMAYED) then
chance = 100
end
if self:knowTalent(self.T_BACKSTAB) and target:attr("stunned") then chance = chance + self:callTalent(self.T_BACKSTAB,"getCriticalChance") end
if target:attr("combat_crit_vulnerable") then
chance = chance + target:attr("combat_crit_vulnerable")
if target:hasEffect(target.EFF_SET_UP) then
local p = target:hasEffect(target.EFF_SET_UP)
if p and p.src == self then
chance = chance - target:combatCritReduction()
-- Scoundrel's Strategies
if self:attr("cut") and target:knowTalent(self.T_SCOUNDREL) then
chance = chance - target:callTalent(target.T_SCOUNDREL,"getCritPenalty")
if self:attr("stealth") and self:knowTalent(self.T_SHADOWSTRIKE) and not target:canSee(self) then -- bug fix
chance = 100
crit_power_add = crit_power_add + self:callTalent(self.T_SHADOWSTRIKE,"getMultiplier")
if self:isAccuracyEffect(weapon, "axe") then
local bonus = self:getAccuracyEffect(weapon, atk, def, 0.2, 10)
print("[PHYS CRIT %] axe accuracy bonus", atk, def, "=", bonus)
chance = chance + bonus
end
if target:hasEffect(target.EFF_OFFGUARD) then
crit_power_add = crit_power_add + 0.1
if self:isAccuracyEffect(weapon, "sword") then
local bonus = self:getAccuracyEffect(weapon, atk, def, 0.004, 0.25)
print("[PHYS CRIT %] sword accuracy bonus", atk, def, "=", bonus)
crit_power_add = crit_power_add + bonus
end
self.turn_procs.is_crit = "physical"
self.turn_procs.crit_power = (1.5 + crit_power_add + (self.combat_critical_power or 0) / 100)
dam = dam * (1.5 + crit_power_add + (self.combat_critical_power or 0) / 100)
if self:knowTalent(self.T_EYE_OF_THE_TIGER) then self:triggerTalent(self.T_EYE_OF_THE_TIGER, nil, "physical") end
local chance = self:combatSpellCrit() + (add_chance or 0)
-- if self:attr("stealth") and self:knowTalent(self.T_SHADOWSTRIKE) and not target:canSee(self) then -- bug fix
if self:attr("stealth") and self:knowTalent(self.T_SHADOWSTRIKE) then -- bug fix
chance = 100
crit_power_add = crit_power_add + self:callTalent(self.T_SHADOWSTRIKE,"getMultiplier")
self.turn_procs.crit_power = (1.5 + crit_power_add + (self.combat_critical_power or 0) / 100)
dam = dam * (1.5 + crit_power_add + (self.combat_critical_power or 0) / 100)
dg
committed
game.logSeen(self, "#{bold}#%s's spell attains critical power!#{normal}#", self.name:capitalize())
if self:attr("mana_on_crit") then self:incMana(self:attr("mana_on_crit")) end
if self:attr("vim_on_crit") then self:incVim(self:attr("vim_on_crit")) end
if self:attr("paradox_on_crit") then self:incParadox(self:attr("paradox_on_crit")) end
if self:attr("positive_on_crit") then self:incPositive(self:attr("positive_on_crit")) end
if self:attr("negative_on_crit") then self:incNegative(self:attr("negative_on_crit")) end
if self:attr("spellsurge_on_crit") then
local power = self:attr("spellsurge_on_crit")
self:setEffect(self.EFF_SPELLSURGE, 10, {power=power, max=power*3})
end
if self:isTalentActive(self.T_BLOOD_FURY) then
local t = self:getTalentFromId(self.T_BLOOD_FURY)
t.on_crit(self, t)
end
if self:isTalentActive(self.T_CORONA) then
local t = self:getTalentFromId(self.T_CORONA)
if self:knowTalent(self.T_EYE_OF_THE_TIGER) then self:triggerTalent(self.T_EYE_OF_THE_TIGER, nil, "spell") end
local chance = self:combatMindCrit() + (add_chance or 0)
local crit = false
-- if self:attr("stealth") and self:knowTalent(self.T_SHADOWSTRIKE) and not target:canSee(self) then -- bug fix
if self:attr("stealth") and self:knowTalent(self.T_SHADOWSTRIKE) then -- bug fix
chance = 100
crit_power_add = crit_power_add + self:callTalent(self.T_SHADOWSTRIKE,"getMultiplier")
print("[MIND CRIT %]", chance)
if rng.percent(chance) then
self.turn_procs.crit_power = (1.5 + crit_power_add + (self.combat_critical_power or 0) / 100)
dam = dam * (1.5 + crit_power_add + (self.combat_critical_power or 0) / 100)
dg
committed
game.logSeen(self, "#{bold}#%s's mind surges with critical power!#{normal}#", self.name:capitalize())
if self:attr("hate_on_crit") then self:incHate(self:attr("hate_on_crit")) end
if self:attr("psi_on_crit") then self:incPsi(self:attr("psi_on_crit")) end
if self:attr("equilibrium_on_crit") then self:incEquilibrium(self:attr("equilibrium_on_crit")) end
if self:knowTalent(self.T_EYE_OF_THE_TIGER) then self:triggerTalent(self.T_EYE_OF_THE_TIGER, nil, "mind") end
if self:knowTalent(self.T_LIVING_MUCUS) then self:callTalent(self.T_LIVING_MUCUS, "on_crit") end
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
dg
committed
chance = 100 - chance
print("[SPELL] friendly fire chance", chance)
dg
committed
end
dg
committed
function _M:combatMindpower(mod, add)
dg
committed
add = add or 0
dg
committed
if self:knowTalent(self.T_SUPERPOWER) then
add = add + 50 * self:getStr() / 100
end
if self:knowTalent(self.T_GESTURE_OF_POWER) then
local t = self:getTalentFromId(self.T_GESTURE_OF_POWER)
dg
committed
add = add + t.getMindpowerChange(self, t)
dg
committed
end
if self:attr("psychometry_power") then
add = add + self:attr("psychometry_power")
end
dg
committed
local d = (self.combat_mindpower > 0 and self.combat_mindpower or 0) + add + self:getWil() * 0.7 + self:getCun() * 0.4
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
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
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)
function _M:combatTalentIntervalDamage(t, stat, min, max, stat_weight)
local stat_weight = stat_weight or 0.5
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))
--- Fake denotes a check not actually being made, used by character sheets etc.
function _M:combatPhysicalResist(fake)
add = add + (self:checkOnDefenseCall("physical") or 0)
end
if self:knowTalent(self.T_CORRUPTED_SHELL) then
add = add + self:getCon() / 3
end
add = add + self:callTalent(self.T_POWER_IS_MONEY, "getSaves")
local d = self.combat_physresist + (self:getCon() + self:getStr() + (self:getLck() - 50) * 0.5) * 0.35 + add
if self:attr("dazed") then d = d / 2 end
local total = self:rescaleCombatStats(d)
-- Psionic Balance
if self:knowTalent(self.T_BALANCE) then
local t = self:getTalentFromId(self.T_BALANCE)
local ratio = t.getBalanceRatio(self, t)
total = (1 - ratio)*total + self:combatMentalResist(fake)*ratio
end
return total
--- Fake denotes a check not actually being made, used by character sheets etc.
function _M:combatSpellResist(fake)
add = add + (self:checkOnDefenseCall("spell") or 0)
end
if self:knowTalent(self.T_CORRUPTED_SHELL) then
add = add + self:getCon() / 3
end
add = add + self:callTalent(self.T_POWER_IS_MONEY, "getSaves")
local d = self.combat_spellresist + (self:getMag() + self:getWil() + (self:getLck() - 50) * 0.5) * 0.35 + add
if self:attr("dazed") then d = d / 2 end
local total = self:rescaleCombatStats(d)
-- Psionic Balance
if self:knowTalent(self.T_BALANCE) then
local t = self:getTalentFromId(self.T_BALANCE)
local ratio = t.getBalanceRatio(self, t)
total = (1 - ratio)*total + self:combatMentalResist(fake)*ratio
end
return total
--- Fake denotes a check not actually being made, used by character sheets etc.
function _M:combatMentalResist(fake)
add = add + (self:checkOnDefenseCall("mental") or 0)
end
if self:knowTalent(self.T_CORRUPTED_SHELL) then
add = add + self:getCon() / 3
end
if self:knowTalent(self.T_STEADY_MIND) then
local t = self:getTalentFromId(self.T_STEADY_MIND)
add = add + t.getMental(self, t)
end
add = add + self:callTalent(self.T_POWER_IS_MONEY, "getSaves")
local d = self.combat_mentalresist + (self:getCun() + self:getWil() + (self:getLck() - 50) * 0.5) * 0.35 + add
if self:attr("dazed") then d = d / 2 end
local nm = self:hasEffect(self.EFF_CURSE_OF_NIGHTMARES)
if nm and rng.percent(20) then
d = d * (1-self.tempeffect_def.EFF_CURSE_OF_NIGHTMARES.getVisionsReduction(nm, nm.level)/100)
end
-- 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 power = 100
if self.force_use_resist and self.force_use_resist ~= type then
type = self.force_use_resist
power = self.force_use_resist_percent or 100
end
local a = math.min((self.resists.all or 0) / 100,1) -- Prevent large numbers from inverting the resist formulas
local b = math.min((self.resists[type] or 0) / 100,1)
local r = math.min(100 * (1 - (1 - a) * (1 - b)), (self.resists_cap.all or 0) + (self.resists_cap[type] or 0))
return r * power / 100
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
--- Returns the damage increase
function _M:combatHasDamageIncrease(type)
if self.inc_damage[type] and self.inc_damage[type] ~= 0 then return true else return false end
end
--- Returns the damage increase
function _M:combatGetDamageIncrease(type, straight)
local a = self.inc_damage.all or 0
local b = self.inc_damage[type] or 0
local inc = a + b
if straight then return inc end
if self.auto_highest_inc_damage and self.auto_highest_inc_damage[type] then
local highest = self.inc_damage.all or 0
for kind, v in pairs(self.inc_damage) do
if kind ~= "all" then
local inc = self:combatGetDamageIncrease(kind, true)
highest = math.max(highest, inc)
end
end
return highest + self.auto_highest_inc_damage[type]
end
return inc
end
function _M:combatMovementSpeed(x, y)
if game.level and game.level.data.zero_gravity then
local movement_speed = self.movement_speed
if x and y and game.level.map:checkAllEntities(x, y, "creepingDark") and self:knowTalent(self.T_DARK_VISION) then
local t = self:getTalentFromId(self.T_DARK_VISION)
movement_speed = movement_speed + t.getMovementSpeedChange(self, t)
end
movement_speed = math.max(movement_speed, 0.1)
return mult * (self.base_movement_speed or 1) / movement_speed
--- Computes see stealth
function _M:combatSeeStealth()
local bonus = 0
if self:knowTalent(self.T_PIERCING_SIGHT) then bonus = bonus + self:callTalent(self.T_PIERCING_SIGHT,"seePower") end
if self:knowTalent(self.T_PRETERNATURAL_SENSES) then bonus = bonus + self:callTalent(self.T_PRETERNATURAL_SENSES, "sensePower") end
return self:combatScale(self.level/2 + self:getCun()/4 + (self:attr("see_stealth") or 0), 0, 0, 50, 50) + bonus -- Note bonus scaled separately from talents
end
--- Computes see invisible
function _M:combatSeeInvisible()
local bonus = 0
if self:knowTalent(self.T_PIERCING_SIGHT) then bonus = bonus + self:callTalent(self.T_PIERCING_SIGHT,"seePower") end
if self:knowTalent(self.T_PRETERNATURAL_SENSES) then bonus = bonus + self:callTalent(self.T_PRETERNATURAL_SENSES, "sensePower") end
return (self:attr("see_invisible") or 0) + bonus
end
--- 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
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 ~= "staff" then
return nil
end
return weapon
end
--- 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 weapon
function _M:hasWeaponType(type)
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 then return nil end
if type and weapon.combat.talented ~= type then return nil end
return weapon
end
--- Check if the actor has a cursed weapon
function _M:hasCursedWeapon()
if self:attr("disarmed") then