Files
2026-05-29 15:11:31 +07:00

430 lines
16 KiB
Lua
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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