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 CRIT_LUCK_ADDITIVE_PERCENT_PER_POINT = 1 ____exports.ability_stacking_crit = __TS__Class() local ability_stacking_crit = ____exports.ability_stacking_crit ability_stacking_crit.name = "ability_stacking_crit" ability_stacking_crit.____file_path = "scripts/vscripts/abilities/modifiers/ability_stacking_crit.lua" __TS__ClassExtends(ability_stacking_crit, BaseAbility) function ability_stacking_crit.prototype.GetIntrinsicModifierName(self) return "modifier_stacking_crit" end ability_stacking_crit = __TS__Decorate( ability_stacking_crit, ability_stacking_crit, {registerAbility(nil)}, {kind = "class", name = "ability_stacking_crit"} ) ____exports.ability_stacking_crit = ability_stacking_crit ____exports.modifier_stacking_crit = __TS__Class() local modifier_stacking_crit = ____exports.modifier_stacking_crit modifier_stacking_crit.name = "modifier_stacking_crit" modifier_stacking_crit.____file_path = "scripts/vscripts/abilities/modifiers/ability_stacking_crit.lua" __TS__ClassExtends(modifier_stacking_crit, BaseModifier) function modifier_stacking_crit.prototype.____constructor(self, ...) BaseModifier.prototype.____constructor(self, ...) self.critModifiers = {} self.guaranteedCritCount = 0 self.guaranteedCritUntilDestroy = false self.wasCrit = false self.pendingGuaranteedCritConsume = false self.forcedTargetCritEntityIndex = nil self.lastCritChance = 0 self.lastCritMult = 1 end function modifier_stacking_crit.GetForUnit(self, unit) local mod = unit:FindModifierByName("modifier_stacking_crit") return mod and mod or nil end function modifier_stacking_crit.prototype.RemoveOnDeath(self) return false end function modifier_stacking_crit.prototype.IsPurgable(self) return false end function modifier_stacking_crit.prototype.IsHidden(self) return true end function modifier_stacking_crit.prototype.DeclareFunctions(self) return {MODIFIER_PROPERTY_PREATTACK_CRITICALSTRIKE, MODIFIER_EVENT_ON_ATTACK_LANDED, MODIFIER_PROPERTY_MAGICAL_RESISTANCE_DIRECT_MODIFICATION} end function modifier_stacking_crit.prototype.GetModifierMagicalResistanceDirectModification(self) return self:GetParent():GetIntellect(true) * -0.1 end function modifier_stacking_crit.prototype.GetModifierPreAttack_CriticalStrike(self, params) if not IsServer() then return 0 end self.wasCrit = false self.lastCritChance = 0 self.lastCritMult = 1 local totalBonus = 0 local parent = self:GetParent() local target = params and params.target if self.forcedTargetCritEntityIndex ~= nil and target and IsValidEntity(target) and target:GetEntityIndex() == self.forcedTargetCritEntityIndex then self.forcedTargetCritEntityIndex = nil if #self.critModifiers > 0 then local baseCritMult = 0 __TS__ArrayForEach( self.critModifiers, function(____, critData) baseCritMult = baseCritMult + critData.mult / 100 end ) local finalMult = getFinalStackingCritMultiplier(nil, parent, baseCritMult) self.wasCrit = true self.lastCritMult = finalMult self.lastCritChance = 100 return finalMult * 100 end end if self.guaranteedCritUntilDestroy and #self.critModifiers > 0 then print((("[STACKING_CRIT] guaranteedCritUntilDestroy=TRUE, unit=" .. parent:GetUnitName()) .. ", crits=") .. tostring(#self.critModifiers)) local baseCritMult = 0 __TS__ArrayForEach( self.critModifiers, function(____, critData) print((((((("[STACKING_CRIT] source=" .. critData.source) .. " chance=") .. tostring(critData.chance)) .. " mult=") .. tostring(critData.mult)) .. " entityIndex=") .. tostring(critData.entityIndex or -1)) baseCritMult = baseCritMult + critData.mult / 100 end ) local finalMult = getFinalStackingCritMultiplier(nil, parent, baseCritMult) print((("[STACKING_CRIT] baseCritMult_sum=" .. tostring(baseCritMult)) .. " finalMult=") .. tostring(finalMult)) self.wasCrit = true self.lastCritMult = finalMult self.lastCritChance = 100 return finalMult * 100 end if self.guaranteedCritCount > 0 and #self.critModifiers > 0 then local baseCritMult = 0 __TS__ArrayForEach( self.critModifiers, function(____, critData) baseCritMult = baseCritMult + critData.mult / 100 end ) local finalMult = getFinalStackingCritMultiplier(nil, parent, baseCritMult) self.pendingGuaranteedCritConsume = true self.wasCrit = true self.lastCritMult = finalMult self.lastCritChance = 100 return finalMult * 100 end local hadAnyCrit = false local bestProcChance = 0 __TS__ArrayForEach( self.critModifiers, function(____, critData) local luck = parent and parent:IsHero() and getLuck(nil, parent) or 0 local effectiveChance = parent and parent:IsHero() and luck > 0 and math.min( 100, math.max(0, critData.chance + luck * CRIT_LUCK_ADDITIVE_PERCENT_PER_POINT) ) or critData.chance if parent and parent:IsHero() and luck > 0 then end if RollPseudoRandomPercentage( effectiveChance, 1, self:GetParent() ) then totalBonus = totalBonus + critData.mult / 100 hadAnyCrit = true if effectiveChance > bestProcChance then bestProcChance = effectiveChance end end end ) if hadAnyCrit then local baseCritMult = totalBonus local finalMult = getFinalStackingCritMultiplier(nil, parent, baseCritMult) self.wasCrit = true self.lastCritChance = bestProcChance self.lastCritMult = finalMult return finalMult * 100 end return 0 end function modifier_stacking_crit.prototype.ForceNextCritOnTarget(self, target) if not IsServer() then return end if not target or not IsValidEntity(target) or target:IsNull() then self.forcedTargetCritEntityIndex = nil return end self.forcedTargetCritEntityIndex = target:GetEntityIndex() end function modifier_stacking_crit.prototype.OnCreated(self) if not IsServer() then return end self:ClearCritModifiers() end function modifier_stacking_crit.prototype.ClearCritModifiers(self) self.critModifiers = {} end function modifier_stacking_crit.prototype.UpdateCrit(self, oldChance, oldMult, newChance, newMult, source) if source == nil then source = "ability" end if not IsServer() then return end if newChance < 0 and newMult <= 0 then self:RemoveCrit(source) return end self:UpdateExistingCrit(newChance, newMult, source) end function modifier_stacking_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 local ____self_critModifiers_4 = self.critModifiers ____self_critModifiers_4[#____self_critModifiers_4 + 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_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_7 = self.critModifiers ____self_critModifiers_7[#____self_critModifiers_7 + 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_crit.prototype.GuaranteeNextCrit(self, count) if count == nil then count = 1 end if not IsServer() then return end self.guaranteedCritCount = math.max(0, self.guaranteedCritCount + count) end function modifier_stacking_crit.prototype.GuaranteeCritUntilDestroy(self, enable) if enable == nil then enable = true end if not IsServer() then return end self.guaranteedCritUntilDestroy = enable end function modifier_stacking_crit.prototype.OnAttackLanded(self, event) if not IsServer() then return end if event.attacker ~= self:GetParent() then return end if self.pendingGuaranteedCritConsume then self.guaranteedCritCount = math.max(0, self.guaranteedCritCount - 1) self.pendingGuaranteedCritConsume = false end local parent = self:GetParent() local target = event.target if not parent or not target then return end if self.wasCrit then local avgTrue = parent:GetAverageTrueAttackDamage(target) local approxDamage = math.floor((avgTrue or 0) * (self.lastCritMult or 1)) if self:GetParent():GetUnitName() == "npc_dota_hero_phantom_assassin" then EmitSoundOn("Hero_PhantomAssassin.CoupDeGrace", target) local effect_cast = ParticleManager:CreateParticle("particles/units/heroes/hero_phantom_assassin/phantom_assassin_crit_impact.vpcf", PATTACH_POINT_FOLLOW, target) ParticleManager:SetParticleControlEnt( effect_cast, 0, target, PATTACH_POINT_FOLLOW, "attach_hitloc", target:GetOrigin(), true ) ParticleManager:SetParticleControl( effect_cast, 1, target:GetAbsOrigin() ) ParticleManager:SetParticleControlForward( effect_cast, 1, (parent:GetOrigin() - target:GetOrigin()):Normalized() ) ParticleManager:ReleaseParticleIndex(effect_cast) end self.wasCrit = false end end function modifier_stacking_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_crit.prototype.OnDestroy(self) if not IsServer() then return end self.critModifiers = {} self.guaranteedCritUntilDestroy = false end modifier_stacking_crit = __TS__Decorate( modifier_stacking_crit, modifier_stacking_crit, {registerModifier(nil)}, {kind = "class", name = "modifier_stacking_crit"} ) ____exports.modifier_stacking_crit = modifier_stacking_crit return ____exports