293 lines
12 KiB
Lua
293 lines
12 KiB
Lua
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
|