local ____lualib = require("lualib_bundle") local __TS__Class = ____lualib.__TS__Class local __TS__ClassExtends = ____lualib.__TS__ClassExtends local __TS__Decorate = ____lualib.__TS__Decorate local __TS__ObjectKeys = ____lualib.__TS__ObjectKeys local __TS__ObjectValues = ____lualib.__TS__ObjectValues local __TS__Delete = ____lualib.__TS__Delete local ____exports = {} local ____dota_ts_adapter = require("lib.dota_ts_adapter") local BaseAbility = ____dota_ts_adapter.BaseAbility local BaseModifier = ____dota_ts_adapter.BaseModifier local registerAbility = ____dota_ts_adapter.registerAbility local registerModifier = ____dota_ts_adapter.registerModifier local ____incoming_damage_reduction_combine = require("utils.incoming_damage_reduction_combine") local removeStatsMultiplierIncomingDamageReductionSource = ____incoming_damage_reduction_combine.removeStatsMultiplierIncomingDamageReductionSource local setStatsMultiplierIncomingDamageReductionSource = ____incoming_damage_reduction_combine.setStatsMultiplierIncomingDamageReductionSource ____exports.MODIFIER_STATS_MULTIPLIER = "modifier_stats_multiplier" --- Стабильные id источников для снятия в `modifier_arsenal_dynamic_stats.OnDestroy`. ____exports.ARSENAL_STATS_MULTIPLIER_SOURCE_ID = { all_stats_pct = "arsenal_dyn_pct_all_stats", strength_pct = "arsenal_dyn_pct_str", agility_pct = "arsenal_dyn_pct_agi", intellect_pct = "arsenal_dyn_pct_int", outgoing_damage_pct = "arsenal_dyn_pct_outgoing", incoming_damage_reduction_pct = "arsenal_dyn_pct_incoming", attack_speed_pct = "arsenal_dyn_pct_as", move_speed_pct = "arsenal_dyn_pct_ms", base_damage_pct = "arsenal_dyn_pct_basedmg" } ____exports.ability_stats_multiplier = __TS__Class() local ability_stats_multiplier = ____exports.ability_stats_multiplier ability_stats_multiplier.name = "ability_stats_multiplier" ability_stats_multiplier.____file_path = "scripts/vscripts/modifiers/modifier_stats_multiplier.lua" __TS__ClassExtends(ability_stats_multiplier, BaseAbility) function ability_stats_multiplier.prototype.GetIntrinsicModifierName(self) return "modifier_stats_multiplier" end function ability_stats_multiplier.prototype.IsHidden(self) return true end ability_stats_multiplier = __TS__Decorate( ability_stats_multiplier, ability_stats_multiplier, {registerAbility(nil)}, {kind = "class", name = "ability_stats_multiplier"} ) ____exports.ability_stats_multiplier = ability_stats_multiplier local globalModifierFunctionSourceSeq = 1 function ____exports.createModifierFunctionSourceId(self, prefix) local safePrefix = prefix and #prefix > 0 and prefix or "source" local id = (safePrefix .. "_") .. tostring(globalModifierFunctionSourceSeq) globalModifierFunctionSourceSeq = globalModifierFunctionSourceSeq + 1 return id end local function sourceKind(self, s) return s.kind or "attributes" end local function getStorage(self, hero) local holder = hero if not holder.__statsMultiplierStorage then holder.__statsMultiplierStorage = {} end return holder.__statsMultiplierStorage end local function hasSources(self, hero) return #__TS__ObjectKeys(getStorage(nil, hero)) > 0 end --- Только карточные % к атрибутам — для видимости баффа и тултипа «%». local function hasAttributeSources(self, hero) local storage = getStorage(nil, hero) for ____, source in ipairs(__TS__ObjectValues(storage)) do if sourceKind(nil, source) == "attributes" then return true end end return false end --- Сбросить кэш сумм (арсенал/колоды меняются без смены id источника). function ____exports.invalidateStatsMultiplierSumCache(self, hero) if not hero or not IsValidEntity(hero) then return end local c = hero.__statsMultiplierSumCache if c then c.dirty = true end end local function rebuildSumCache(self, hero, cache) local storage = getStorage(nil, hero) local byKind = {} for ____, source in ipairs(__TS__ObjectValues(storage)) do local k = sourceKind(nil, source) byKind[k] = (byKind[k] or 0) + source:resolver() end cache.byKind = byKind cache.dirty = false end local function sumForKind(self, hero, kind) local holder = hero local cache = holder.__statsMultiplierSumCache if not cache then cache = {dirty = true, byKind = {}} holder.__statsMultiplierSumCache = cache end if cache.dirty then rebuildSumCache(nil, hero, cache) end return cache.byKind[kind] or 0 end local function ensureModifier(self, hero) if not hero:FindModifierByName(____exports.MODIFIER_STATS_MULTIPLIER) then local sourceAbility = hero:FindAbilityByName("ability_stats_multiplier") or hero:FindAbilityByName("ability_stacking_crit") hero:AddNewModifier(hero, sourceAbility, ____exports.MODIFIER_STATS_MULTIPLIER, {}) end end local function removeModifierIfUnused(self, hero) if hasSources(nil, hero) then return end local mod = hero:FindModifierByName(____exports.MODIFIER_STATS_MULTIPLIER) if mod then mod:Destroy() end ____exports.invalidateStatsMultiplierSumCache(nil, hero) end --- Регистрирует вклад в `modifier_stats_multiplier`. -- - `kind === "attributes"` (по умолчанию): сумма % умножается на **базовые** Str/Agi/Int и даёт бонус к соответствующему атрибуту (карты). -- - `all_stats_pct`: тот же % к **каждой** базовой характеристике Str/Agi/Int (арсенал). -- - `strength_pct` / `agility_pct` / `intellect_pct`: % только к **базовому** Str/Agi/Int (арсенал). -- - Остальные `kind`: сумма идёт в соответствующий `ModifierFunction` (исходящий/входящий урон, AS%/MS%, урон от базы атаки) — арсенал; -- для `incoming_damage_reduction_pct` сумма **не** добавляется линейно в движок, а сжимается формулой с убыванием (см. `incoming_damage_reduction_combine`). function ____exports.setStatsMultiplierSource(self, hero, sourceId, resolver, kind) if kind == nil then kind = "attributes" end if not hero or not IsValidEntity(hero) then return end getStorage(nil, hero)[sourceId] = {resolver = resolver, kind = kind} if kind == "incoming_damage_reduction_pct" then setStatsMultiplierIncomingDamageReductionSource(nil, hero, sourceId, resolver) end ____exports.invalidateStatsMultiplierSumCache(nil, hero) ensureModifier(nil, hero) end function ____exports.removeStatsMultiplierSource(self, hero, sourceId) if not hero or not IsValidEntity(hero) then return end local storage = getStorage(nil, hero) local removed = storage[sourceId] if removed and sourceKind(nil, removed) == "incoming_damage_reduction_pct" then removeStatsMultiplierIncomingDamageReductionSource(nil, hero, sourceId) end __TS__Delete(storage, sourceId) ____exports.invalidateStatsMultiplierSumCache(nil, hero) removeModifierIfUnused(nil, hero) end ____exports.modifier_stats_multiplier = __TS__Class() local modifier_stats_multiplier = ____exports.modifier_stats_multiplier modifier_stats_multiplier.name = "modifier_stats_multiplier" modifier_stats_multiplier.____file_path = "scripts/vscripts/modifiers/modifier_stats_multiplier.lua" __TS__ClassExtends(modifier_stats_multiplier, BaseModifier) function modifier_stats_multiplier.prototype.____constructor(self, ...) BaseModifier.prototype.____constructor(self, ...) self.lock = false end function modifier_stats_multiplier.prototype.IsHidden(self) local parent = self:GetParent() if not parent or not IsValidEntity(parent) then return true end return not hasAttributeSources(nil, parent) end function modifier_stats_multiplier.prototype.RemoveOnDeath(self) return false end function modifier_stats_multiplier.prototype.IsPurgable(self) return false end function modifier_stats_multiplier.prototype.GetTexture(self) return "nevermore_dark_lord" end function modifier_stats_multiplier.prototype.DeclareFunctions(self) return { MODIFIER_PROPERTY_STATS_STRENGTH_BONUS, MODIFIER_PROPERTY_STATS_AGILITY_BONUS, MODIFIER_PROPERTY_STATS_INTELLECT_BONUS, MODIFIER_PROPERTY_TOOLTIP, MODIFIER_PROPERTY_DAMAGEOUTGOING_PERCENTAGE, MODIFIER_PROPERTY_ATTACKSPEED_PERCENTAGE, MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE, MODIFIER_PROPERTY_BASEDAMAGEOUTGOING_PERCENTAGE } end function modifier_stats_multiplier.prototype.sumResolverPercentAttributes(self) local hero = self:GetParent() if not hero or not IsValidEntity(hero) then return 0 end return sumForKind(nil, hero, "attributes") end function modifier_stats_multiplier.prototype.OnTooltip(self) if not IsServer() then return 0 end return self:sumResolverPercentAttributes() end function modifier_stats_multiplier.prototype.GetModifierBonusStats_Strength(self) if self.lock then return 0 end local hero = self:GetParent() if not hero or not IsValidEntity(hero) then return 0 end self.lock = true local base = hero:GetStrength() or 0 local shared = sumForKind(nil, hero, "all_stats_pct") local pct = self:sumResolverPercentAttributes() + shared + sumForKind(nil, hero, "strength_pct") local v = base * (pct / 100) self.lock = false return v end function modifier_stats_multiplier.prototype.GetModifierBonusStats_Agility(self) if self.lock then return 0 end local hero = self:GetParent() if not hero or not IsValidEntity(hero) then return 0 end self.lock = true local base = hero:GetAgility() or 0 local shared = sumForKind(nil, hero, "all_stats_pct") local pct = self:sumResolverPercentAttributes() + shared + sumForKind(nil, hero, "agility_pct") local v = base * (pct / 100) self.lock = false return v end function modifier_stats_multiplier.prototype.GetModifierBonusStats_Intellect(self) if self.lock then return 0 end local hero = self:GetParent() if not hero or not IsValidEntity(hero) then return 0 end self.lock = true local base = hero:GetIntellect(true) or 0 local shared = sumForKind(nil, hero, "all_stats_pct") local pct = self:sumResolverPercentAttributes() + shared + sumForKind(nil, hero, "intellect_pct") local v = base * (pct / 100) self.lock = false return v end function modifier_stats_multiplier.prototype.GetModifierDamageOutgoing_Percentage(self) local hero = self:GetParent() if not hero or not IsValidEntity(hero) then return 0 end return sumForKind(nil, hero, "outgoing_damage_pct") end function modifier_stats_multiplier.prototype.GetModifierAttackSpeedPercentage(self) local hero = self:GetParent() if not hero or not IsValidEntity(hero) then return 0 end return sumForKind(nil, hero, "attack_speed_pct") end function modifier_stats_multiplier.prototype.GetModifierMoveSpeedBonus_Percentage(self) local hero = self:GetParent() if not hero or not IsValidEntity(hero) then return 0 end return sumForKind(nil, hero, "move_speed_pct") end function modifier_stats_multiplier.prototype.GetModifierBaseDamageOutgoing_Percentage(self) local hero = self:GetParent() if not hero or not IsValidEntity(hero) then return 0 end return sumForKind(nil, hero, "base_damage_pct") end modifier_stats_multiplier = __TS__Decorate( modifier_stats_multiplier, modifier_stats_multiplier, {registerModifier(nil)}, {kind = "class", name = "modifier_stats_multiplier"} ) ____exports.modifier_stats_multiplier = modifier_stats_multiplier return ____exports