226 lines
8.1 KiB
Lua
226 lines
8.1 KiB
Lua
local ____lualib = require("lualib_bundle")
|
|
local __TS__Delete = ____lualib.__TS__Delete
|
|
local __TS__Class = ____lualib.__TS__Class
|
|
local __TS__ClassExtends = ____lualib.__TS__ClassExtends
|
|
local __TS__Decorate = ____lualib.__TS__Decorate
|
|
local ____exports = {}
|
|
local ____CardSystem = require("cards.CardSystem")
|
|
local CardBase = ____CardSystem.CardBase
|
|
local RegisterCard = ____CardSystem.RegisterCard
|
|
local ____CardBaseModifier = require("cards.CardBaseModifier")
|
|
local CardBaseModifier = ____CardBaseModifier.CardBaseModifier
|
|
local ____dota_ts_adapter = require("lib.dota_ts_adapter")
|
|
local registerModifier = ____dota_ts_adapter.registerModifier
|
|
local ____modifier_general_fired = require("abilities.modifiers.modifier_general_fired")
|
|
local modifier_general_fired = ____modifier_general_fired.modifier_general_fired
|
|
local ____luck = require("utils.luck")
|
|
local rollLuckChance = ____luck.rollLuckChance
|
|
local CARD_ID = 14
|
|
--- Общая глубина рикошета на герое: при нескольких копиях карты каждый modifier_card_14 — отдельный инстанс, иначе второй снова прокает от рикошетного удара.
|
|
local sharedRicochetDepthByHero = {}
|
|
local function getSharedCard14RicochetDepth(self, hero)
|
|
return sharedRicochetDepthByHero[hero:entindex()] or 0
|
|
end
|
|
local function addSharedCard14RicochetDepth(self, hero)
|
|
local k = hero:entindex()
|
|
sharedRicochetDepthByHero[k] = (sharedRicochetDepthByHero[k] or 0) + 1
|
|
end
|
|
local function removeSharedCard14RicochetDepth(self, hero)
|
|
local k = hero:entindex()
|
|
local next = (sharedRicochetDepthByHero[k] or 1) - 1
|
|
if next <= 0 then
|
|
__TS__Delete(sharedRicochetDepthByHero, k)
|
|
else
|
|
sharedRicochetDepthByHero[k] = next
|
|
end
|
|
end
|
|
____exports.card_14 = __TS__Class()
|
|
local card_14 = ____exports.card_14
|
|
card_14.name = "card_14"
|
|
card_14.____file_path = "scripts/vscripts/cards/examples/card_14.lua"
|
|
__TS__ClassExtends(card_14, CardBase)
|
|
function card_14.prototype.GetModifierName(self)
|
|
return "modifier_card_14"
|
|
end
|
|
card_14 = __TS__Decorate(card_14, card_14, {RegisterCard}, {kind = "class", name = "card_14"})
|
|
____exports.card_14 = card_14
|
|
____exports.modifier_card_14 = __TS__Class()
|
|
local modifier_card_14 = ____exports.modifier_card_14
|
|
modifier_card_14.name = "modifier_card_14"
|
|
modifier_card_14.____file_path = "scripts/vscripts/cards/examples/card_14.lua"
|
|
__TS__ClassExtends(modifier_card_14, CardBaseModifier)
|
|
function modifier_card_14.prototype.____constructor(self, ...)
|
|
CardBaseModifier.prototype.____constructor(self, ...)
|
|
self.ricochetProjectile = self:GetParent():GetRangedProjectileName() or ""
|
|
self.ricochetAttackDepth = 0
|
|
self.forcedRicochetDamagePct = 0
|
|
end
|
|
function modifier_card_14.prototype.DeclareFunctions(self)
|
|
return {MODIFIER_EVENT_ON_ATTACK_LANDED, MODIFIER_PROPERTY_DAMAGEOUTGOING_PERCENTAGE, MODIFIER_PROPERTY_TOOLTIP}
|
|
end
|
|
function modifier_card_14.prototype.getValue(self, key, fallback)
|
|
return self:getCardValue(key, fallback, CARD_ID)
|
|
end
|
|
function modifier_card_14.prototype.addFiredStacks(self, target, attacker, stacks)
|
|
if stacks <= 0 then
|
|
return
|
|
end
|
|
local firedModifier = target:AddNewModifier(
|
|
attacker,
|
|
getModifierSourceAbility(nil, attacker),
|
|
modifier_general_fired.name,
|
|
{}
|
|
)
|
|
if not firedModifier then
|
|
return
|
|
end
|
|
do
|
|
local i = 0
|
|
while i < stacks do
|
|
firedModifier:IncrementStackCount()
|
|
i = i + 1
|
|
end
|
|
end
|
|
end
|
|
function modifier_card_14.prototype.GetModifierDamageOutgoing_Percentage(self)
|
|
if self.ricochetAttackDepth <= 0 then
|
|
return 0
|
|
end
|
|
return self.forcedRicochetDamagePct
|
|
end
|
|
function modifier_card_14.prototype.performRicochetAttack(self, attacker, target, damagePct, burnStacks)
|
|
if not attacker:IsAlive() or not target:IsAlive() then
|
|
return
|
|
end
|
|
addSharedCard14RicochetDepth(nil, attacker)
|
|
self.ricochetAttackDepth = self.ricochetAttackDepth + 1
|
|
self.forcedRicochetDamagePct = damagePct - 100
|
|
attacker:PerformAttack(
|
|
target,
|
|
true,
|
|
true,
|
|
true,
|
|
false,
|
|
false,
|
|
false,
|
|
true
|
|
)
|
|
self.forcedRicochetDamagePct = 0
|
|
self.ricochetAttackDepth = math.max(0, self.ricochetAttackDepth - 1)
|
|
removeSharedCard14RicochetDepth(nil, attacker)
|
|
self:addFiredStacks(target, attacker, burnStacks)
|
|
end
|
|
function modifier_card_14.prototype.OnAttackLanded(self, event)
|
|
if not IsServer() then
|
|
return
|
|
end
|
|
if self.ricochetAttackDepth > 0 then
|
|
return
|
|
end
|
|
local attacker = self:GetParent()
|
|
if not attacker or not IsValidEntity(attacker) or event.attacker ~= attacker then
|
|
return
|
|
end
|
|
if getSharedCard14RicochetDepth(nil, attacker) > 0 then
|
|
return
|
|
end
|
|
local target = event.target
|
|
if not target or not IsValidEntity(target) or not target:IsAlive() then
|
|
return
|
|
end
|
|
if target:GetTeamNumber() == attacker:GetTeamNumber() then
|
|
return
|
|
end
|
|
local copies = self:getCardCopies()
|
|
local chancePct = math.min(
|
|
100,
|
|
self:getValue("proc_chance_pct", 22) * copies
|
|
)
|
|
local ____attacker_IsRealHero_result_0
|
|
if attacker:IsRealHero() then
|
|
____attacker_IsRealHero_result_0 = rollLuckChance(nil, attacker, chancePct / 100)
|
|
else
|
|
____attacker_IsRealHero_result_0 = RollPercentage(chancePct)
|
|
end
|
|
local proc = ____attacker_IsRealHero_result_0
|
|
if not proc then
|
|
return
|
|
end
|
|
local attackDamage = attacker:GetAverageTrueAttackDamage(target)
|
|
if attackDamage <= 0 then
|
|
return
|
|
end
|
|
local burnStacks = self:getValue("fired_stacks", 3) * copies
|
|
local mainBonusPct = self:getValue("main_target_bonus_damage_pct", 35) * copies
|
|
local mainDamage = attackDamage * (mainBonusPct / 100)
|
|
if mainDamage > 0 then
|
|
ApplyDamage({victim = target, attacker = attacker, damage = mainDamage, damage_type = DAMAGE_TYPE_MAGICAL})
|
|
end
|
|
self:addFiredStacks(target, attacker, burnStacks)
|
|
local radius = self:getValue("ricochet_radius", 300)
|
|
local splashPct = self:getValue("ricochet_damage_pct", 60) * copies
|
|
if splashPct <= 0 or radius <= 0 then
|
|
return
|
|
end
|
|
local enemies = FindUnitsInRadius(
|
|
attacker:GetTeamNumber(),
|
|
target:GetAbsOrigin(),
|
|
nil,
|
|
radius,
|
|
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
|
bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC),
|
|
DOTA_UNIT_TARGET_FLAG_NONE,
|
|
FIND_ANY_ORDER,
|
|
false
|
|
)
|
|
local projectileSpeed = math.max(
|
|
700,
|
|
attacker:GetProjectileSpeed()
|
|
)
|
|
for ____, enemy in ipairs(enemies) do
|
|
do
|
|
if enemy == target then
|
|
goto __continue29
|
|
end
|
|
if not enemy:IsAlive() then
|
|
goto __continue29
|
|
end
|
|
local distance = (enemy:GetAbsOrigin() - target:GetAbsOrigin()):Length2D()
|
|
local travelTime = projectileSpeed > 0 and distance / projectileSpeed or 0
|
|
ProjectileManager:CreateTrackingProjectile({
|
|
Source = target,
|
|
Target = enemy,
|
|
Ability = nil,
|
|
EffectName = self.ricochetProjectile,
|
|
iMoveSpeed = projectileSpeed,
|
|
bDodgeable = true,
|
|
bVisibleToEnemies = true,
|
|
bReplaceExisting = false,
|
|
bProvidesVision = false
|
|
})
|
|
Timers:CreateTimer(
|
|
travelTime,
|
|
function()
|
|
if not IsServer() then
|
|
return nil
|
|
end
|
|
if not attacker:IsAlive() or not enemy:IsAlive() then
|
|
return nil
|
|
end
|
|
self:performRicochetAttack(attacker, enemy, splashPct, burnStacks)
|
|
return nil
|
|
end
|
|
)
|
|
end
|
|
::__continue29::
|
|
end
|
|
end
|
|
modifier_card_14 = __TS__Decorate(
|
|
modifier_card_14,
|
|
modifier_card_14,
|
|
{registerModifier(nil)},
|
|
{kind = "class", name = "modifier_card_14"}
|
|
)
|
|
____exports.modifier_card_14 = modifier_card_14
|
|
return ____exports
|