local ____lualib = require("lualib_bundle") 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 BaseAbility = ____dota_ts_adapter.BaseAbility local BaseModifier = ____dota_ts_adapter.BaseModifier local registerAbility = ____dota_ts_adapter.registerAbility local registerModifier = ____dota_ts_adapter.registerModifier local ____hero_rage_config = require("abilities.system.hero_rage_config") local shouldHeroUseRageResource = ____hero_rage_config.shouldHeroUseRageResource function ____exports.heroRageGetModifier(self, hero) return hero:FindModifierByName(____exports.modifier_hero_rage.name) end function ____exports.heroRageSet(self, hero, value) if not IsServer() then return end local m = ____exports.heroRageGetModifier(nil, hero) if not m then return end m:SetStackCount(math.max( 0, math.min( math.floor(value), m.maxRage ) )) m:pushRageNetClient() end --- Списание ярости после успешного каста (стоимость из `rage_cost` в KV или своё число). function ____exports.heroRageTrySpend(self, hero, cost) if not IsServer() then return false end if cost <= 0 then return true end local m = ____exports.heroRageGetModifier(nil, hero) if not m then return false end if m:GetStackCount() < cost then return false end m:addRageInternal(-cost) return true end local function defaultSpawnParams() return { max_rage = 100, rage_per_attack = 6, rage_per_damage = 1, attack_speed_per_stack = 0, time_out_of_combat = 4, tick = 0.01 } end ____exports.ability_hero_rage = __TS__Class() local ability_hero_rage = ____exports.ability_hero_rage ability_hero_rage.name = "ability_hero_rage" ability_hero_rage.____file_path = "scripts/vscripts/abilities/system/hero_rage.lua" __TS__ClassExtends(ability_hero_rage, BaseAbility) function ability_hero_rage.prototype.GetIntrinsicModifierName(self) return ____exports.modifier_hero_rage.name end function ability_hero_rage.prototype.OnOwnerDied(self) if not IsServer() then return end local c = self:GetCaster() ____exports.heroRageSet(nil, c, 0) end ability_hero_rage = __TS__Decorate( ability_hero_rage, ability_hero_rage, {registerAbility(nil)}, {kind = "class", name = "ability_hero_rage"} ) ____exports.ability_hero_rage = ability_hero_rage ____exports.modifier_hero_rage = __TS__Class() local modifier_hero_rage = ____exports.modifier_hero_rage modifier_hero_rage.name = "modifier_hero_rage" modifier_hero_rage.____file_path = "scripts/vscripts/abilities/system/hero_rage.lua" __TS__ClassExtends(modifier_hero_rage, BaseModifier) function modifier_hero_rage.prototype.____constructor(self, ...) BaseModifier.prototype.____constructor(self, ...) self.maxRage = 100 self.ragePerAttack = 0 self.ragePerDamage = 0 self.attackSpeedPerStack = 0 self.timeOutOfCombatThreshold = 4 self.tick = 0.01 self.outOfCombatTime = 0 end function modifier_hero_rage.prototype.pushRageNetClient(self) if not IsServer() then return end CustomNetTables:SetTableValue( "custom_stats", "rage_ent_" .. tostring(self.parentHero:entindex()), { cur = self:GetStackCount(), max = self.maxRage } ) end function modifier_hero_rage.prototype.clearRageNetClient(self) if not IsServer() then return end CustomNetTables:SetTableValue( "custom_stats", "rage_ent_" .. tostring(self.parentHero:entindex()), {off = 1} ) end function modifier_hero_rage.prototype.IsHidden(self) return true end function modifier_hero_rage.prototype.IsPurgable(self) return false end function modifier_hero_rage.prototype.RemoveOnDeath(self) return false end function modifier_hero_rage.prototype.DeclareFunctions(self) return { MODIFIER_EVENT_ON_ATTACK_LANDED, MODIFIER_EVENT_ON_TAKEDAMAGE, MODIFIER_EVENT_ON_ABILITY_EXECUTED, MODIFIER_PROPERTY_ATTACKSPEED_BONUS_CONSTANT, MODIFIER_PROPERTY_MANA_REGEN_TOTAL_PERCENTAGE, MODIFIER_PROPERTY_FIXED_MANA_REGEN, MODIFIER_PROPERTY_FORCE_MAX_MANA, MODIFIER_PROPERTY_DAMAGEOUTGOING_PERCENTAGE, MODIFIER_PROPERTY_MANACOST_PERCENTAGE_STACKING } end function modifier_hero_rage.prototype.GetModifierPercentageManacostStacking(self, ...) local args = {...} local event = args[1] if event and event.ability and not event.ability:IsNull() and event.ability:IsItem() then return 100 end return 0 end function modifier_hero_rage.prototype.GetModifierDamageOutgoing_Percentage(self, event) return self:GetStackCount() * 0.25 end function modifier_hero_rage.prototype.GetModifierTotalPercentageManaRegen(self) return 0 end function modifier_hero_rage.prototype.GetModifierFixedManaRegen(self) return 0 end function modifier_hero_rage.prototype.GetModifierForceMaxMana(self) return self.maxRage end function modifier_hero_rage.prototype.syncManaToRage(self) if not IsServer() then return end local hero = self.parentHero local rage = self:GetStackCount() local maxM = hero:GetMaxMana() hero:SetMaxMana(100) hero:SetMana(math.min(rage, maxM)) end function modifier_hero_rage.prototype.reapplyManaPoolAfterStatBump(self) if not IsServer() then return end local hero = self.parentHero local rage = self:GetStackCount() hero:SetMaxMana(self.maxRage) hero:SetMana(math.min( rage, hero:GetMaxMana() )) end function modifier_hero_rage.prototype.OnTakeDamage(self, event) if not IsServer() then return end if event.damage <= 0 then return end local victim = event.unit local attacker = event.attacker if attacker == self.parentHero and victim and victim:GetTeamNumber() ~= self.parentHero:GetTeamNumber() then if self.ragePerAttack ~= 0 then self:addRageInternal(self.ragePerAttack) end self:reapplyManaPoolAfterStatBump() return end if victim == self.parentHero then if self.ragePerDamage ~= 0 then self:addRageInternal(self.ragePerDamage) end self:reapplyManaPoolAfterStatBump() end end function modifier_hero_rage.prototype.getAbilityRageSpendCost(self, ability) local customRageCost = math.max( 0, math.floor(ability:GetSpecialValueFor("rage_cost")) ) if customRageCost > 0 then return customRageCost end return math.max( 0, math.floor(ability:GetManaCost(ability:GetLevel())) ) end function modifier_hero_rage.prototype.OnAbilityExecuted(self, event) if not IsServer() then return end if event.unit ~= self.parentHero then return end local ability = event.ability if not ability or ability:IsNull() then return end if ability:IsItem() then return end if ability == self:GetAbility() then return end local cost = self:getAbilityRageSpendCost(ability) if cost <= 0 then return end ____exports.heroRageTrySpend(nil, self.parentHero, cost) end function modifier_hero_rage.prototype.OnCreated(self, params) local parent = self:GetParent() self.parentHero = parent local rawAbility = self:GetAbility() local ability = rawAbility and rawAbility:GetAbilityName() == "ability_hero_rage" and rawAbility or nil local def = defaultSpawnParams(nil) if ability then self.maxRage = math.max( 1, math.floor(ability:GetSpecialValueFor("max_rage")) ) self.ragePerAttack = ability:GetSpecialValueFor("rage_per_attack") self.ragePerDamage = ability:GetSpecialValueFor("rage_per_damage") self.attackSpeedPerStack = ability:GetSpecialValueFor("attack_speed_per_stack") self.timeOutOfCombatThreshold = math.max( 0.1, ability:GetSpecialValueFor("time_out_of_combat") ) self.tick = math.max( 0.03, ability:GetSpecialValueFor("tick") ) else self.maxRage = math.max( 1, math.floor(params.max_rage or def.max_rage) ) self.ragePerAttack = params.rage_per_attack or def.rage_per_attack self.ragePerDamage = params.rage_per_damage or def.rage_per_damage self.attackSpeedPerStack = params.attack_speed_per_stack or def.attack_speed_per_stack self.timeOutOfCombatThreshold = math.max(0.1, params.time_out_of_combat or def.time_out_of_combat) self.tick = math.max(0.03, params.tick or def.tick) end if not IsServer() then return end parent.__zHeroRage = self self:SetStackCount(0) self.outOfCombatTime = 0 parent:AddNewModifier( parent, self:GetAbility() or nil, ____exports.modifier_hero_rage_max.name, {} ) self:StartIntervalThink(self.tick) self:pushRageNetClient() self:syncManaToRage() self.levelUpListener = ListenToGameEvent( "dota_player_gained_level", function(event) if not IsServer() then return end if not IsValidEntity(self.parentHero) then return end if not self.parentHero:IsRealHero() or self.parentHero:IsIllusion() then return end local pid = self.parentHero:GetPlayerOwnerID() if event.player ~= pid then return end self:reapplyManaPoolAfterStatBump() end, nil ) end function modifier_hero_rage.prototype.OnDestroy(self) if not IsServer() then return end if self.levelUpListener ~= nil then StopListeningToGameEvent(self.levelUpListener) self.levelUpListener = nil end self:clearRageNetClient() local p = self:GetParent() if p.__zHeroRage == self then p.__zHeroRage = nil end p:RemoveModifierByName(____exports.modifier_hero_rage_max.name) end function modifier_hero_rage.prototype.OnRefresh(self) local rawAbility = self:GetAbility() local ability = rawAbility and rawAbility:GetAbilityName() == "ability_hero_rage" and rawAbility or nil if ability then self.maxRage = math.max( 1, math.floor(ability:GetSpecialValueFor("max_rage")) ) self.ragePerAttack = ability:GetSpecialValueFor("rage_per_attack") self.ragePerDamage = ability:GetSpecialValueFor("rage_per_damage") self.attackSpeedPerStack = ability:GetSpecialValueFor("attack_speed_per_stack") self.timeOutOfCombatThreshold = math.max( 0.1, ability:GetSpecialValueFor("time_out_of_combat") ) self.tick = math.max( 0.03, ability:GetSpecialValueFor("tick") ) end if not IsServer() then return end self:clampRageStack() local maxMod = self.parentHero:FindModifierByName(____exports.modifier_hero_rage_max.name) if maxMod then maxMod:SetStackCount(self.maxRage) end self:pushRageNetClient() self:syncManaToRage() end function modifier_hero_rage.prototype.OnStackCountChanged(self, _stack) if not IsServer() then return end self:clampRageStack() self:pushRageNetClient() self:syncManaToRage() end function modifier_hero_rage.prototype.OnIntervalThink(self) if not IsServer() then return end self:syncManaToRage() self.outOfCombatTime = self.outOfCombatTime + self.tick if self.outOfCombatTime >= self.timeOutOfCombatThreshold then self:addRageInternal(-1) end end function modifier_hero_rage.prototype.OnAttackLanded(self, event) if not IsServer() then return end end function modifier_hero_rage.prototype.GetModifierAttackSpeedBonus_Constant(self) return self.attackSpeedPerStack * self:GetStackCount() end function modifier_hero_rage.prototype.resetOutOfCombatTimer(self) self.outOfCombatTime = 0 end function modifier_hero_rage.prototype.addRageInternal(self, delta) self:resetOutOfCombatTimer() local next = self:GetStackCount() + delta if next > self.maxRage then next = self.maxRage end if next < 0 then next = 0 end self:SetStackCount(next) end function modifier_hero_rage.prototype.clampRageStack(self) local v = self:GetStackCount() if v < 0 then v = 0 end if v > self.maxRage then v = self.maxRage end if v ~= self:GetStackCount() then self:SetStackCount(v) end end modifier_hero_rage = __TS__Decorate( modifier_hero_rage, modifier_hero_rage, {registerModifier(nil)}, {kind = "class", name = "modifier_hero_rage"} ) ____exports.modifier_hero_rage = modifier_hero_rage ____exports.modifier_hero_rage_max = __TS__Class() local modifier_hero_rage_max = ____exports.modifier_hero_rage_max modifier_hero_rage_max.name = "modifier_hero_rage_max" modifier_hero_rage_max.____file_path = "scripts/vscripts/abilities/system/hero_rage.lua" __TS__ClassExtends(modifier_hero_rage_max, BaseModifier) function modifier_hero_rage_max.prototype.____constructor(self, ...) BaseModifier.prototype.____constructor(self, ...) self.manaBonusLock = false end function modifier_hero_rage_max.prototype.IsHidden(self) return true end function modifier_hero_rage_max.prototype.IsPurgable(self) return false end function modifier_hero_rage_max.prototype.RemoveOnDeath(self) return false end function modifier_hero_rage_max.prototype.DeclareFunctions(self) return {MODIFIER_PROPERTY_MANA_BONUS} end function modifier_hero_rage_max.prototype.GetModifierManaBonus(self) if self.manaBonusLock then return 0 end self.manaBonusLock = true local maxWithoutThis = self:GetParent():GetMaxMana() self.manaBonusLock = false local targetMax = self:GetStackCount() return targetMax - maxWithoutThis end function modifier_hero_rage_max.prototype.OnCreated(self) if not IsServer() then return end local parent = self:GetParent() local rage = parent:FindModifierByName(____exports.modifier_hero_rage.name) self:SetStackCount(rage and rage.maxRage or 0) end modifier_hero_rage_max = __TS__Decorate( modifier_hero_rage_max, modifier_hero_rage_max, {registerModifier(nil)}, {kind = "class", name = "modifier_hero_rage_max"} ) ____exports.modifier_hero_rage_max = modifier_hero_rage_max function ____exports.heroRageGetCurrent(self, hero) local m = ____exports.heroRageGetModifier(nil, hero) return m and m:GetStackCount() or 0 end function ____exports.heroRageGetMax(self, hero) local m = ____exports.heroRageGetModifier(nil, hero) return m and m.maxRage or 0 end function ____exports.heroRageIncrease(self, hero, amount) if not IsServer() then return end local m = ____exports.heroRageGetModifier(nil, hero) if not m then return end m:addRageInternal(amount) end function ____exports.heroRageDecrement(self, hero, amount) ____exports.heroRageIncrease( nil, hero, -math.abs(amount) ) end --- Вешает систему ярости на героя из конфига `HERO_RAGE_UNIT_NAMES`. -- Если в `game/` уже есть `ability_hero_rage` в KV — берётся она, иначе — модификатор с дефолтами. function ____exports.attachHeroRageSystemForConfiguredHero(self, hero) if not IsServer() then return end if not hero:IsRealHero() or hero:IsIllusion() then return end if not shouldHeroUseRageResource( nil, hero:GetUnitName() ) then return end if hero:FindModifierByName(____exports.modifier_hero_rage.name) then return end hero:AddAbility("ability_hero_rage") local rageAb = hero:FindAbilityByName("ability_hero_rage") if rageAb ~= nil then rageAb:SetLevel(1) return end hero:AddNewModifier( hero, getModifierSourceAbility(nil, hero), ____exports.modifier_hero_rage.name, defaultSpawnParams(nil) ) end return ____exports