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__ArrayForEach = ____lualib.__TS__ArrayForEach local __TS__ArrayFindIndex = ____lualib.__TS__ArrayFindIndex local __TS__ArraySort = ____lualib.__TS__ArraySort local __TS__ArrayFilter = ____lualib.__TS__ArrayFilter 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 ____crit_mult = require("utils.crit_mult") local getFinalStackingCritMultiplier = ____crit_mult.getFinalStackingCritMultiplier local SPELL_CRIT_LUCK_ADDITIVE_PERCENT_PER_POINT = 1 local isApplyingSpellCritBonus = false ____exports.ability_stacking_spell_crit = __TS__Class() local ability_stacking_spell_crit = ____exports.ability_stacking_spell_crit ability_stacking_spell_crit.name = "ability_stacking_spell_crit" ability_stacking_spell_crit.____file_path = "scripts/vscripts/abilities/modifiers/ability_stacking_spell_crit.lua" __TS__ClassExtends(ability_stacking_spell_crit, BaseAbility) function ability_stacking_spell_crit.prototype.GetIntrinsicModifierName(self) return "modifier_stacking_spell_crit" end ability_stacking_spell_crit = __TS__Decorate( ability_stacking_spell_crit, ability_stacking_spell_crit, {registerAbility(nil)}, {kind = "class", name = "ability_stacking_spell_crit"} ) ____exports.ability_stacking_spell_crit = ability_stacking_spell_crit ____exports.modifier_stacking_spell_crit = __TS__Class() local modifier_stacking_spell_crit = ____exports.modifier_stacking_spell_crit modifier_stacking_spell_crit.name = "modifier_stacking_spell_crit" modifier_stacking_spell_crit.____file_path = "scripts/vscripts/abilities/modifiers/ability_stacking_spell_crit.lua" __TS__ClassExtends(modifier_stacking_spell_crit, BaseModifier) function modifier_stacking_spell_crit.prototype.____constructor(self, ...) BaseModifier.prototype.____constructor(self, ...) self.critModifiers = {} end function modifier_stacking_spell_crit.GetForUnit(self, unit) local mod = unit:FindModifierByName("modifier_stacking_spell_crit") return mod and mod or nil end function modifier_stacking_spell_crit.prototype.RemoveOnDeath(self) return false end function modifier_stacking_spell_crit.prototype.IsPurgable(self) return false end function modifier_stacking_spell_crit.prototype.IsHidden(self) return true end function modifier_stacking_spell_crit.prototype.DeclareFunctions(self) return {MODIFIER_EVENT_ON_TAKEDAMAGE} end function modifier_stacking_spell_crit.prototype.OnCreated(self) if not IsServer() then return end self.critModifiers = {} end function modifier_stacking_spell_crit.prototype.OnTakeDamage(self, event) if not IsServer() or isApplyingSpellCritBonus then return end if event.attacker ~= self:GetParent() then return end if #self.critModifiers == 0 then return end local damage = event.damage if damage <= 0 then return end if event.damage_type ~= DAMAGE_TYPE_MAGICAL then return end if not event.inflictor then return end local damageFlags = event.damage_flags or 0 if bit.band(damageFlags, DOTA_DAMAGE_FLAG_HPLOSS) == DOTA_DAMAGE_FLAG_HPLOSS then return end if bit.band(damageFlags, DOTA_DAMAGE_FLAG_REFLECTION) == DOTA_DAMAGE_FLAG_REFLECTION then return end local parent = self:GetParent() local victim = event.unit local inflictor = event.inflictor if not victim or not inflictor or victim:IsNull() or inflictor:IsNull() then return end if inflictor == self:GetAbility() then return end local totalBonusMult = 0 local bestProcChance = 0 local luck = parent:IsHero() and getLuck(nil, parent) or 0 __TS__ArrayForEach( self.critModifiers, function(____, critData) local effectiveChance = parent:IsHero() and luck > 0 and math.min( 100, math.max(0, critData.chance + luck * SPELL_CRIT_LUCK_ADDITIVE_PERCENT_PER_POINT) ) or critData.chance if RollPseudoRandomPercentage(effectiveChance, 1, parent) then totalBonusMult = totalBonusMult + critData.mult / 100 if effectiveChance > bestProcChance then bestProcChance = effectiveChance end end end ) if totalBonusMult <= 0 then return end local finalMult = getFinalStackingCritMultiplier(nil, parent, totalBonusMult) if finalMult <= 0 then return end local bonusDamage = damage * math.max(0, finalMult - 1) if bonusDamage <= 0 then return end local spellAmpFrac = parent:IsHero() and math.max( 0, parent:GetSpellAmplification(false) ) or 0 local damageToApply = bonusDamage / math.max(0.000001, 1 + spellAmpFrac) isApplyingSpellCritBonus = true ApplyDamage({ victim = victim, attacker = parent, damage = damageToApply, damage_type = DAMAGE_TYPE_MAGICAL, damage_flags = DOTA_DAMAGE_FLAG_NO_SPELL_AMPLIFICATION, ability = inflictor }) isApplyingSpellCritBonus = false SendOverheadEventMessage( nil, OVERHEAD_ALERT_DEADLY_BLOW, victim, math.floor(bonusDamage), nil ) end function modifier_stacking_spell_crit.prototype.AddCustomCrit(self, chance, mult, source, entity) if not IsServer() then return end if chance < 0 and mult <= 0 then return end local entityIndex = entity and entity:GetEntityIndex() local existingIndex = __TS__ArrayFindIndex( self.critModifiers, function(____, crit) return crit.source == source and crit.entityIndex == entityIndex end ) if existingIndex ~= -1 then self.critModifiers[existingIndex + 1] = {chance = chance, mult = mult, source = source, entityIndex = entityIndex} else local ____self_critModifiers_2 = self.critModifiers ____self_critModifiers_2[#____self_critModifiers_2 + 1] = {chance = chance, mult = mult, source = source, entityIndex = entityIndex} end __TS__ArraySort( self.critModifiers, function(____, a, b) return b.chance - a.chance end ) end function modifier_stacking_spell_crit.prototype.UpdateExistingCrit(self, chance, mult, source, entity) if not IsServer() then return end if chance < 0 and mult <= 0 then self:RemoveCrit(source, entity) return end local entityIndex = entity and entity:GetEntityIndex() local existingIndex = __TS__ArrayFindIndex( self.critModifiers, function(____, crit) if crit.source ~= source then return false end if entityIndex ~= nil then return crit.entityIndex == entityIndex end return true end ) if existingIndex ~= -1 then self.critModifiers[existingIndex + 1].chance = chance self.critModifiers[existingIndex + 1].mult = mult if entityIndex ~= nil then self.critModifiers[existingIndex + 1].entityIndex = entityIndex end else self:AddCustomCrit(chance, mult, source, entity) end __TS__ArraySort( self.critModifiers, function(____, a, b) return b.chance - a.chance end ) end function modifier_stacking_spell_crit.prototype.RemoveCrit(self, source, entity) if not IsServer() then return end local entityIndex = entity and entity:GetEntityIndex() self.critModifiers = __TS__ArrayFilter( self.critModifiers, function(____, crit) return not (crit.source == source and crit.entityIndex == entityIndex) end ) end function modifier_stacking_spell_crit.prototype.OnDestroy(self) if not IsServer() then return end self.critModifiers = {} end modifier_stacking_spell_crit = __TS__Decorate( modifier_stacking_spell_crit, modifier_stacking_spell_crit, {registerModifier(nil)}, {kind = "class", name = "modifier_stacking_spell_crit"} ) ____exports.modifier_stacking_spell_crit = modifier_stacking_spell_crit return ____exports