430 lines
16 KiB
Lua
430 lines
16 KiB
Lua
local ____lualib = require("lualib_bundle")
|
||
local __TS__ObjectValues = ____lualib.__TS__ObjectValues
|
||
local __TS__ObjectAssign = ____lualib.__TS__ObjectAssign
|
||
local __TS__Class = ____lualib.__TS__Class
|
||
local __TS__ClassExtends = ____lualib.__TS__ClassExtends
|
||
local __TS__Decorate = ____lualib.__TS__Decorate
|
||
local ____exports = {}
|
||
local ____dota_ts_adapter = require("lib.dota_ts_adapter")
|
||
local BaseModifier = ____dota_ts_adapter.BaseModifier
|
||
local registerModifier = ____dota_ts_adapter.registerModifier
|
||
local ____ArsenalStatRuntime = require("arsenal.ArsenalStatRuntime")
|
||
local getArsenalTotals = ____ArsenalStatRuntime.getArsenalTotals
|
||
local ____modifier_stats_multiplier = require("modifiers.modifier_stats_multiplier")
|
||
local ARSENAL_STATS_MULTIPLIER_SOURCE_ID = ____modifier_stats_multiplier.ARSENAL_STATS_MULTIPLIER_SOURCE_ID
|
||
local invalidateStatsMultiplierSumCache = ____modifier_stats_multiplier.invalidateStatsMultiplierSumCache
|
||
local removeStatsMultiplierSource = ____modifier_stats_multiplier.removeStatsMultiplierSource
|
||
local setStatsMultiplierSource = ____modifier_stats_multiplier.setStatsMultiplierSource
|
||
local ____incoming_damage_reduction_combine = require("utils.incoming_damage_reduction_combine")
|
||
local removeIncomingDamageReductionSource = ____incoming_damage_reduction_combine.removeIncomingDamageReductionSource
|
||
local setIncomingDamageReductionSource = ____incoming_damage_reduction_combine.setIncomingDamageReductionSource
|
||
local ____luck = require("utils.luck")
|
||
local addLuck = ____luck.addLuck
|
||
local reduceLuck = ____luck.reduceLuck
|
||
local ____crit_mult = require("utils.crit_mult")
|
||
local addCritMult = ____crit_mult.addCritMult
|
||
local reduceCritMult = ____crit_mult.reduceCritMult
|
||
local ZERO_TOTALS = {
|
||
battle_level = 0,
|
||
bonus_damage = 0,
|
||
attack_speed = 0,
|
||
bonus_armor = 0,
|
||
max_health = 0,
|
||
max_mana = 0,
|
||
health_regen = 0,
|
||
mana_regen = 0,
|
||
move_speed = 0,
|
||
magic_resist = 0,
|
||
spell_amp = 0,
|
||
all_stats = 0,
|
||
all_stats_pct = 0,
|
||
bonus_strength = 0,
|
||
bonus_agility = 0,
|
||
bonus_intellect = 0,
|
||
strength_pct = 0,
|
||
agility_pct = 0,
|
||
intellect_pct = 0,
|
||
luck = 0,
|
||
outgoing_damage_pct = 0,
|
||
incoming_damage_reduction_pct = 0,
|
||
attack_speed_pct = 0,
|
||
move_speed_pct = 0,
|
||
base_damage_pct = 0,
|
||
crit_mult = 0
|
||
}
|
||
--- % атрибутов — через stats multiplier (база × %). Боевые % — прямо в DeclareFunctions ниже.
|
||
local ARSENAL_ATTRIBUTE_PCT_KINDS = {"all_stats_pct", "strength_pct", "agility_pct", "intellect_pct"}
|
||
local ALL_ARSENAL_MULTIPLIER_SOURCE_IDS = __TS__ObjectValues(ARSENAL_STATS_MULTIPLIER_SOURCE_ID)
|
||
local ARSENAL_INCOMING_REDUCTION_SOURCE_ID = "arsenal_dynamic_stats_incoming"
|
||
--- Раньше вешались на stats_multiplier — снимаем, чтобы не дублировать с прямым DeclareFunctions.
|
||
local ARSENAL_LEGACY_COMBAT_PCT_KINDS = {
|
||
"outgoing_damage_pct",
|
||
"incoming_damage_reduction_pct",
|
||
"attack_speed_pct",
|
||
"move_speed_pct",
|
||
"base_damage_pct"
|
||
}
|
||
local function coerceTotals(self, next)
|
||
if not next then
|
||
return __TS__ObjectAssign({}, ZERO_TOTALS)
|
||
end
|
||
return {
|
||
battle_level = next.battle_level or 0,
|
||
bonus_damage = next.bonus_damage or 0,
|
||
attack_speed = next.attack_speed or 0,
|
||
bonus_armor = next.bonus_armor or 0,
|
||
max_health = next.max_health or 0,
|
||
max_mana = next.max_mana or 0,
|
||
health_regen = next.health_regen or 0,
|
||
mana_regen = next.mana_regen or 0,
|
||
move_speed = next.move_speed or 0,
|
||
magic_resist = next.magic_resist or 0,
|
||
spell_amp = next.spell_amp or 0,
|
||
all_stats = next.all_stats or 0,
|
||
all_stats_pct = next.all_stats_pct or 0,
|
||
bonus_strength = next.bonus_strength or 0,
|
||
bonus_agility = next.bonus_agility or 0,
|
||
bonus_intellect = next.bonus_intellect or 0,
|
||
strength_pct = next.strength_pct or 0,
|
||
agility_pct = next.agility_pct or 0,
|
||
intellect_pct = next.intellect_pct or 0,
|
||
luck = next.luck or 0,
|
||
outgoing_damage_pct = next.outgoing_damage_pct or 0,
|
||
incoming_damage_reduction_pct = next.incoming_damage_reduction_pct or 0,
|
||
attack_speed_pct = next.attack_speed_pct or 0,
|
||
move_speed_pct = next.move_speed_pct or 0,
|
||
base_damage_pct = next.base_damage_pct or 0,
|
||
crit_mult = next.crit_mult or 0
|
||
}
|
||
end
|
||
____exports.modifier_arsenal_dynamic_stats = __TS__Class()
|
||
local modifier_arsenal_dynamic_stats = ____exports.modifier_arsenal_dynamic_stats
|
||
modifier_arsenal_dynamic_stats.name = "modifier_arsenal_dynamic_stats"
|
||
modifier_arsenal_dynamic_stats.____file_path = "scripts/vscripts/arsenal/items/dynamic_stats.lua"
|
||
__TS__ClassExtends(modifier_arsenal_dynamic_stats, BaseModifier)
|
||
function modifier_arsenal_dynamic_stats.prototype.____constructor(self, ...)
|
||
BaseModifier.prototype.____constructor(self, ...)
|
||
self.totals = __TS__ObjectAssign({}, ZERO_TOTALS)
|
||
self.appliedLuckFromArsenal = 0
|
||
self.appliedCritMultFromArsenal = 0
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.OnCreated(self)
|
||
if not IsServer() then
|
||
return
|
||
end
|
||
self:SetHasCustomTransmitterData(true)
|
||
self:refreshTotalsFromServer()
|
||
self:applyBattleLevelFromTotals()
|
||
self:syncLuckFromTotals()
|
||
self:syncCritMultFromTotals()
|
||
self:wireArsenalAttributePctSources()
|
||
self:wireArsenalIncomingReductionSource()
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.OnRefresh(self)
|
||
if not IsServer() then
|
||
return
|
||
end
|
||
self:refreshTotalsFromServer()
|
||
self:applyBattleLevelFromTotals()
|
||
self:syncLuckFromTotals()
|
||
self:syncCritMultFromTotals()
|
||
self:wireArsenalAttributePctSources()
|
||
self:wireArsenalIncomingReductionSource()
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.OnDestroy(self)
|
||
if not IsServer() then
|
||
return
|
||
end
|
||
self:clearLuckFromTotals()
|
||
self:clearCritMultFromTotals()
|
||
self:unwireAllArsenalMultiplierSources()
|
||
self:unwireArsenalIncomingReductionSource()
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.wireArsenalIncomingReductionSource(self)
|
||
local hero = self:GetParent()
|
||
if not hero or not IsValidEntity(hero) or not hero:IsRealHero() then
|
||
return
|
||
end
|
||
setIncomingDamageReductionSource(
|
||
nil,
|
||
hero,
|
||
ARSENAL_INCOMING_REDUCTION_SOURCE_ID,
|
||
function()
|
||
if not hero or not IsValidEntity(hero) then
|
||
return 0
|
||
end
|
||
local totals = coerceTotals(
|
||
nil,
|
||
getArsenalTotals(nil, hero)
|
||
)
|
||
return math.max(0, totals.incoming_damage_reduction_pct or 0)
|
||
end
|
||
)
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.unwireArsenalIncomingReductionSource(self)
|
||
local hero = self:GetParent()
|
||
if not hero or not IsValidEntity(hero) then
|
||
return
|
||
end
|
||
removeIncomingDamageReductionSource(nil, hero, ARSENAL_INCOMING_REDUCTION_SOURCE_ID)
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.unwireLegacyArsenalCombatPctSources(self, hero)
|
||
for ____, k in ipairs(ARSENAL_LEGACY_COMBAT_PCT_KINDS) do
|
||
removeStatsMultiplierSource(nil, hero, ARSENAL_STATS_MULTIPLIER_SOURCE_ID[k])
|
||
end
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.wireArsenalAttributePctSources(self)
|
||
local hero = self:GetParent()
|
||
if not hero or not IsValidEntity(hero) or not hero:IsRealHero() then
|
||
return
|
||
end
|
||
self:unwireLegacyArsenalCombatPctSources(hero)
|
||
for ____, k in ipairs(ARSENAL_ATTRIBUTE_PCT_KINDS) do
|
||
local sourceId = ARSENAL_STATS_MULTIPLIER_SOURCE_ID[k]
|
||
setStatsMultiplierSource(
|
||
nil,
|
||
hero,
|
||
sourceId,
|
||
function()
|
||
if not hero or not IsValidEntity(hero) then
|
||
return 0
|
||
end
|
||
local t = coerceTotals(
|
||
nil,
|
||
getArsenalTotals(nil, hero)
|
||
)
|
||
if k == "strength_pct" then
|
||
return t.strength_pct
|
||
end
|
||
if k == "agility_pct" then
|
||
return t.agility_pct
|
||
end
|
||
if k == "intellect_pct" then
|
||
return t.intellect_pct
|
||
end
|
||
if k == "all_stats_pct" then
|
||
return t.all_stats_pct
|
||
end
|
||
return 0
|
||
end,
|
||
k
|
||
)
|
||
end
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.unwireAllArsenalMultiplierSources(self)
|
||
local hero = self:GetParent()
|
||
if not hero or not IsValidEntity(hero) then
|
||
return
|
||
end
|
||
for ____, sourceId in ipairs(ALL_ARSENAL_MULTIPLIER_SOURCE_IDS) do
|
||
removeStatsMultiplierSource(nil, hero, sourceId)
|
||
end
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.AddCustomTransmitterData(self)
|
||
return self.totals
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.HandleCustomTransmitterData(self, data)
|
||
self.totals = coerceTotals(nil, data)
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.refreshTotalsFromServer(self)
|
||
local parent = self:GetParent()
|
||
local hero = parent
|
||
local next = getArsenalTotals(nil, hero)
|
||
self.totals = coerceTotals(nil, next)
|
||
self:SendBuffRefreshToClients()
|
||
if hero and IsValidEntity(hero) and hero:IsRealHero() then
|
||
invalidateStatsMultiplierSumCache(nil, hero)
|
||
end
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.syncLuckFromTotals(self)
|
||
local hero = self:GetParent()
|
||
if not hero or not IsValidEntity(hero) or not hero:IsRealHero() then
|
||
return
|
||
end
|
||
local targetLuck = self:getTotals().luck or 0
|
||
local delta = targetLuck - self.appliedLuckFromArsenal
|
||
if math.abs(delta) <= 0.0001 then
|
||
return
|
||
end
|
||
if delta > 0 then
|
||
addLuck(nil, hero, delta)
|
||
else
|
||
reduceLuck(nil, hero, -delta)
|
||
end
|
||
self.appliedLuckFromArsenal = targetLuck
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.clearLuckFromTotals(self)
|
||
local hero = self:GetParent()
|
||
if not hero or not IsValidEntity(hero) or not hero:IsRealHero() then
|
||
return
|
||
end
|
||
if math.abs(self.appliedLuckFromArsenal) <= 0.0001 then
|
||
return
|
||
end
|
||
if self.appliedLuckFromArsenal > 0 then
|
||
reduceLuck(nil, hero, self.appliedLuckFromArsenal)
|
||
else
|
||
addLuck(nil, hero, -self.appliedLuckFromArsenal)
|
||
end
|
||
self.appliedLuckFromArsenal = 0
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.syncCritMultFromTotals(self)
|
||
local hero = self:GetParent()
|
||
if not hero or not IsValidEntity(hero) or not hero:IsRealHero() then
|
||
return
|
||
end
|
||
local targetCrit = self:getTotals().crit_mult or 0
|
||
local delta = targetCrit - self.appliedCritMultFromArsenal
|
||
if math.abs(delta) <= 0.0001 then
|
||
return
|
||
end
|
||
if delta > 0 then
|
||
addCritMult(nil, hero, delta)
|
||
else
|
||
reduceCritMult(nil, hero, -delta)
|
||
end
|
||
self.appliedCritMultFromArsenal = targetCrit
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.clearCritMultFromTotals(self)
|
||
local hero = self:GetParent()
|
||
if not hero or not IsValidEntity(hero) or not hero:IsRealHero() then
|
||
return
|
||
end
|
||
if math.abs(self.appliedCritMultFromArsenal) <= 0.0001 then
|
||
return
|
||
end
|
||
if self.appliedCritMultFromArsenal > 0 then
|
||
reduceCritMult(nil, hero, self.appliedCritMultFromArsenal)
|
||
else
|
||
addCritMult(nil, hero, -self.appliedCritMultFromArsenal)
|
||
end
|
||
self.appliedCritMultFromArsenal = 0
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.applyBattleLevelFromTotals(self)
|
||
local hero = self:GetParent()
|
||
if not hero or not IsValidEntity(hero) or not hero:IsRealHero() then
|
||
return
|
||
end
|
||
local bonus = math.max(
|
||
0,
|
||
math.floor(self:getTotals().battle_level or 0)
|
||
)
|
||
if bonus <= 0 then
|
||
return
|
||
end
|
||
local targetLevel = 1 + bonus
|
||
if hero:GetLevel() >= targetLevel then
|
||
return
|
||
end
|
||
local prevAbilityPoints = hero:GetAbilityPoints()
|
||
while hero:GetLevel() < targetLevel do
|
||
hero:HeroLevelUp(false)
|
||
hero:SetAbilityPoints(prevAbilityPoints)
|
||
end
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.IsHidden(self)
|
||
return true
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.IsPurgable(self)
|
||
return false
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.RemoveOnDeath(self)
|
||
return false
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.GetTexture(self)
|
||
return "item_mantle"
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.DeclareFunctions(self)
|
||
return {
|
||
MODIFIER_PROPERTY_PREATTACK_BONUS_DAMAGE,
|
||
MODIFIER_PROPERTY_ATTACKSPEED_BONUS_CONSTANT,
|
||
MODIFIER_PROPERTY_PHYSICAL_ARMOR_BONUS,
|
||
MODIFIER_PROPERTY_HEALTH_BONUS,
|
||
MODIFIER_PROPERTY_MANA_BONUS,
|
||
MODIFIER_PROPERTY_HEALTH_REGEN_CONSTANT,
|
||
MODIFIER_PROPERTY_MANA_REGEN_CONSTANT,
|
||
MODIFIER_PROPERTY_MOVESPEED_BONUS_CONSTANT,
|
||
MODIFIER_PROPERTY_MAGICAL_RESISTANCE_BONUS,
|
||
MODIFIER_PROPERTY_SPELL_AMPLIFY_PERCENTAGE,
|
||
MODIFIER_PROPERTY_STATS_STRENGTH_BONUS,
|
||
MODIFIER_PROPERTY_STATS_AGILITY_BONUS,
|
||
MODIFIER_PROPERTY_STATS_INTELLECT_BONUS,
|
||
MODIFIER_PROPERTY_DAMAGEOUTGOING_PERCENTAGE,
|
||
MODIFIER_PROPERTY_ATTACKSPEED_PERCENTAGE,
|
||
MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE,
|
||
MODIFIER_PROPERTY_BASEDAMAGEOUTGOING_PERCENTAGE
|
||
}
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.getTotals(self)
|
||
if IsServer() then
|
||
local parent = self:GetParent()
|
||
return coerceTotals(
|
||
nil,
|
||
getArsenalTotals(nil, parent)
|
||
)
|
||
end
|
||
return self.totals
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.GetModifierPreAttack_BonusDamage(self)
|
||
return self:getTotals().bonus_damage
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.GetModifierAttackSpeedBonus_Constant(self)
|
||
return self:getTotals().attack_speed
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.GetModifierPhysicalArmorBonus(self)
|
||
return self:getTotals().bonus_armor
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.GetModifierHealthBonus(self)
|
||
return self:getTotals().max_health
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.GetModifierManaBonus(self)
|
||
return self:getTotals().max_mana
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.GetModifierConstantHealthRegen(self)
|
||
return self:getTotals().health_regen
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.GetModifierConstantManaRegen(self)
|
||
return self:getTotals().mana_regen
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.GetModifierMoveSpeedBonus_Constant(self)
|
||
return self:getTotals().move_speed
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.GetModifierMagicalResistanceBonus(self)
|
||
return self:getTotals().magic_resist
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.GetModifierSpellAmplify_Percentage(self)
|
||
return self:getTotals().spell_amp
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.GetModifierBonusStats_Strength(self)
|
||
local t = self:getTotals()
|
||
return t.all_stats + t.bonus_strength
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.GetModifierBonusStats_Agility(self)
|
||
local t = self:getTotals()
|
||
return t.all_stats + t.bonus_agility
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.GetModifierBonusStats_Intellect(self)
|
||
local t = self:getTotals()
|
||
return t.all_stats + t.bonus_intellect
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.GetModifierDamageOutgoing_Percentage(self)
|
||
return self:getTotals().outgoing_damage_pct
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.GetModifierAttackSpeedPercentage(self)
|
||
return self:getTotals().attack_speed_pct
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.GetModifierMoveSpeedBonus_Percentage(self)
|
||
return self:getTotals().move_speed_pct
|
||
end
|
||
function modifier_arsenal_dynamic_stats.prototype.GetModifierBaseDamageOutgoing_Percentage(self)
|
||
return self:getTotals().base_damage_pct
|
||
end
|
||
modifier_arsenal_dynamic_stats = __TS__Decorate(
|
||
modifier_arsenal_dynamic_stats,
|
||
modifier_arsenal_dynamic_stats,
|
||
{registerModifier(nil)},
|
||
{kind = "class", name = "modifier_arsenal_dynamic_stats"}
|
||
)
|
||
____exports.modifier_arsenal_dynamic_stats = modifier_arsenal_dynamic_stats
|
||
return ____exports
|