initial commit
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
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
|
||||
--- Пассивка для npc_capture_point: при приближении героя спавнит npc_teleport один раз.
|
||||
____exports.ability_capture_point = __TS__Class()
|
||||
local ability_capture_point = ____exports.ability_capture_point
|
||||
ability_capture_point.name = "ability_capture_point"
|
||||
ability_capture_point.____file_path = "scripts/vscripts/abilities/ability_capture_point.lua"
|
||||
__TS__ClassExtends(ability_capture_point, BaseAbility)
|
||||
function ability_capture_point.prototype.GetIntrinsicModifierName(self)
|
||||
return ____exports.modifier_ability_capture_point.name
|
||||
end
|
||||
ability_capture_point = __TS__Decorate(
|
||||
ability_capture_point,
|
||||
ability_capture_point,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_capture_point"}
|
||||
)
|
||||
____exports.ability_capture_point = ability_capture_point
|
||||
____exports.modifier_ability_capture_point = __TS__Class()
|
||||
local modifier_ability_capture_point = ____exports.modifier_ability_capture_point
|
||||
modifier_ability_capture_point.name = "modifier_ability_capture_point"
|
||||
modifier_ability_capture_point.____file_path = "scripts/vscripts/abilities/ability_capture_point.lua"
|
||||
__TS__ClassExtends(modifier_ability_capture_point, BaseModifier)
|
||||
function modifier_ability_capture_point.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.activated = false
|
||||
self.intervalThinkCount = 0
|
||||
end
|
||||
function modifier_ability_capture_point.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_ability_capture_point.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_ability_capture_point.prototype.OnCreated(self)
|
||||
if IsServer() then
|
||||
local p = self:GetParent()
|
||||
local o = p and p:GetAbsOrigin()
|
||||
local ____opt_2 = p and p.GetEntityIndex
|
||||
local idx = ____opt_2 and ____opt_2(p) or -1
|
||||
self:StartIntervalThink(0.25)
|
||||
end
|
||||
end
|
||||
function modifier_ability_capture_point.prototype.OnIntervalThink(self)
|
||||
if not IsServer() or self.activated then
|
||||
return
|
||||
end
|
||||
self:GetParent():StartGesture(ACT_DOTA_RUN)
|
||||
local parent = self:GetParent()
|
||||
if not parent or not IsValidEntity(parent) or not parent:IsAlive() then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
local radius = ability:GetSpecialValueFor("activation_radius")
|
||||
local spawnDistance = ability:GetSpecialValueFor("spawn_distance")
|
||||
local heroes = FindUnitsInRadius(
|
||||
parent:GetTeamNumber(),
|
||||
parent:GetAbsOrigin(),
|
||||
nil,
|
||||
radius,
|
||||
DOTA_UNIT_TARGET_TEAM_FRIENDLY,
|
||||
DOTA_UNIT_TARGET_HERO,
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
self.intervalThinkCount = self.intervalThinkCount + 1
|
||||
local realHeroCount = 0
|
||||
for ____, u in ipairs(heroes) do
|
||||
if u and u:IsAlive() and u:IsRealHero() then
|
||||
realHeroCount = realHeroCount + 1
|
||||
end
|
||||
end
|
||||
for ____, h in ipairs(heroes) do
|
||||
if h and h:IsAlive() and h:IsRealHero() then
|
||||
parent:RemoveGesture(ACT_DOTA_RUN)
|
||||
self:spawnTeleport(parent, spawnDistance)
|
||||
self.activated = true
|
||||
self:StartIntervalThink(-1)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
function modifier_ability_capture_point.prototype.spawnTeleport(self, capture, distance)
|
||||
local origin = capture:GetAbsOrigin()
|
||||
local forward = capture:GetForwardVector()
|
||||
local rawPos = Vector(origin.x + forward.x * distance, origin.y + forward.y * distance, -origin.z)
|
||||
local pos = GetGroundPosition(rawPos, nil)
|
||||
capture:StartGesture(ACT_DOTA_CAPTURE)
|
||||
local tp = CreateUnitByName(
|
||||
"npc_teleport",
|
||||
pos,
|
||||
false,
|
||||
nil,
|
||||
nil,
|
||||
capture:GetTeamNumber()
|
||||
)
|
||||
if tp and IsValidEntity(tp) then
|
||||
local pfx = ParticleManager:CreateParticle(
|
||||
"particles/econ/events/fall_2021/fountain_regen_fall_2021_lvl3.vpcf",
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
self:GetParent()
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(pfx)
|
||||
tp:SetEntityName("npc_teleport")
|
||||
tp:SetAbsOrigin(pos)
|
||||
tp:SetForwardVector(forward)
|
||||
end
|
||||
end
|
||||
modifier_ability_capture_point = __TS__Decorate(
|
||||
modifier_ability_capture_point,
|
||||
modifier_ability_capture_point,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_ability_capture_point"}
|
||||
)
|
||||
____exports.modifier_ability_capture_point = modifier_ability_capture_point
|
||||
return ____exports
|
||||
@@ -0,0 +1,136 @@
|
||||
local ____lualib = require("lualib_bundle")
|
||||
local __TS__Class = ____lualib.__TS__Class
|
||||
local __TS__ClassExtends = ____lualib.__TS__ClassExtends
|
||||
local __TS__ArrayForEach = ____lualib.__TS__ArrayForEach
|
||||
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
|
||||
____exports.agro_leader = __TS__Class()
|
||||
local agro_leader = ____exports.agro_leader
|
||||
agro_leader.name = "agro_leader"
|
||||
agro_leader.____file_path = "scripts/vscripts/abilities/creep/agro_leader.lua"
|
||||
__TS__ClassExtends(agro_leader, BaseAbility)
|
||||
function agro_leader.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_axe/axe_beserkers_call_owner.vpcf", context)
|
||||
PrecacheResource("particle", "particles/status_fx/status_effect_beserkers_call.vpcf", context)
|
||||
PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_axe.vsndevts", context)
|
||||
end
|
||||
function agro_leader.prototype.OnAbilityPhaseStart(self)
|
||||
if IsServer() then
|
||||
self:GetCaster():StartGestureWithPlaybackRate(ACT_DOTA_CAST_ABILITY_4, 0.7)
|
||||
self.preParticle = ParticleManager:CreateParticle(
|
||||
"particles/darkmoon_creep_warning.vpcf",
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
self:GetCaster()
|
||||
)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
self.preParticle,
|
||||
0,
|
||||
self:GetCaster(),
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
"",
|
||||
self:GetCaster():GetOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
self.preParticle,
|
||||
1,
|
||||
Vector(100, 100, 100)
|
||||
)
|
||||
end
|
||||
return true
|
||||
end
|
||||
function agro_leader.prototype.OnAbilityPhaseInterrupted(self)
|
||||
if IsClient() or not self.preParticle then
|
||||
return
|
||||
end
|
||||
self:GetCaster():FadeGesture(ACT_DOTA_CAST_ABILITY_4)
|
||||
ParticleManager:DestroyParticle(self.preParticle, false)
|
||||
end
|
||||
function agro_leader.prototype.OnSpellStart(self)
|
||||
local caster = self:GetCaster()
|
||||
if self.preParticle then
|
||||
ParticleManager:DestroyParticle(self.preParticle, false)
|
||||
end
|
||||
caster:FadeGesture(ACT_DOTA_CAST_ABILITY_4)
|
||||
local radius = self:GetSpecialValueFor("radius")
|
||||
local particle = ParticleManager:CreateParticle("particles/units/heroes/hero_axe/axe_beserkers_call_owner.vpcf", PATTACH_ABSORIGIN_FOLLOW, caster)
|
||||
ParticleManager:SetParticleControl(
|
||||
particle,
|
||||
2,
|
||||
Vector(radius, radius, radius)
|
||||
)
|
||||
caster:EmitSound("Hero_Axe.Berserkers_Call")
|
||||
local enemies = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
caster:GetAbsOrigin(),
|
||||
nil,
|
||||
radius,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC,
|
||||
DOTA_UNIT_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
__TS__ArrayForEach(
|
||||
enemies,
|
||||
function(____, enemy)
|
||||
enemy:MoveToTargetToAttack(caster)
|
||||
____exports.modifier_agro_leader_debuff:apply(
|
||||
enemy,
|
||||
caster,
|
||||
self,
|
||||
{duration = self:GetSpecialValueFor("duration")}
|
||||
)
|
||||
end
|
||||
)
|
||||
end
|
||||
agro_leader = __TS__Decorate(
|
||||
agro_leader,
|
||||
agro_leader,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "agro_leader"}
|
||||
)
|
||||
____exports.agro_leader = agro_leader
|
||||
____exports.modifier_agro_leader_debuff = __TS__Class()
|
||||
local modifier_agro_leader_debuff = ____exports.modifier_agro_leader_debuff
|
||||
modifier_agro_leader_debuff.name = "modifier_agro_leader_debuff"
|
||||
modifier_agro_leader_debuff.____file_path = "scripts/vscripts/abilities/creep/agro_leader.lua"
|
||||
__TS__ClassExtends(modifier_agro_leader_debuff, BaseModifier)
|
||||
function modifier_agro_leader_debuff.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_agro_leader_debuff.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_agro_leader_debuff.prototype.IsDebuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_agro_leader_debuff.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_COMMAND_RESTRICTED] = true}
|
||||
end
|
||||
function modifier_agro_leader_debuff.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_EVENT_ON_DEATH}
|
||||
end
|
||||
function modifier_agro_leader_debuff.prototype.OnDeath(self, event)
|
||||
if event.unit == self:GetCaster() then
|
||||
self:Destroy()
|
||||
end
|
||||
end
|
||||
function modifier_agro_leader_debuff.prototype.OnDestroy(self)
|
||||
end
|
||||
function modifier_agro_leader_debuff.prototype.GetStatusEffectName(self)
|
||||
return "particles/status_fx/status_effect_beserkers_call.vpcf"
|
||||
end
|
||||
modifier_agro_leader_debuff = __TS__Decorate(
|
||||
modifier_agro_leader_debuff,
|
||||
modifier_agro_leader_debuff,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_agro_leader_debuff"}
|
||||
)
|
||||
____exports.modifier_agro_leader_debuff = modifier_agro_leader_debuff
|
||||
return ____exports
|
||||
@@ -0,0 +1,150 @@
|
||||
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 ____item_shameful_pipe = require("items.quest_items.item_shameful_pipe")
|
||||
local modifier_item_shameful_pipe = ____item_shameful_pipe.modifier_item_shameful_pipe
|
||||
local ____luck = require("utils.luck")
|
||||
local rollLuckChance = ____luck.rollLuckChance
|
||||
____exports.bone_armor = __TS__Class()
|
||||
local bone_armor = ____exports.bone_armor
|
||||
bone_armor.name = "bone_armor"
|
||||
bone_armor.____file_path = "scripts/vscripts/abilities/creep/bone_armor.lua"
|
||||
__TS__ClassExtends(bone_armor, BaseAbility)
|
||||
function bone_armor.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/econ/items/centaur/centaur_crownfall_belt/centaur_crownfall_belt_retaliate.vpcf", context)
|
||||
end
|
||||
function bone_armor.prototype.GetIntrinsicModifierName(self)
|
||||
return "modifier_bone_armor_passive"
|
||||
end
|
||||
bone_armor = __TS__Decorate(
|
||||
bone_armor,
|
||||
bone_armor,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "bone_armor"}
|
||||
)
|
||||
____exports.bone_armor = bone_armor
|
||||
____exports.modifier_bone_armor_passive = __TS__Class()
|
||||
local modifier_bone_armor_passive = ____exports.modifier_bone_armor_passive
|
||||
modifier_bone_armor_passive.name = "modifier_bone_armor_passive"
|
||||
modifier_bone_armor_passive.____file_path = "scripts/vscripts/abilities/creep/bone_armor.lua"
|
||||
__TS__ClassExtends(modifier_bone_armor_passive, BaseModifier)
|
||||
function modifier_bone_armor_passive.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_bone_armor_passive.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bone_armor_passive.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bone_armor_passive.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_PHYSICAL_ARMOR_BONUS, MODIFIER_EVENT_ON_ATTACK_LANDED}
|
||||
end
|
||||
function modifier_bone_armor_passive.prototype.GetModifierPhysicalArmorBonus(self)
|
||||
local ____opt_0 = self:GetAbility()
|
||||
return ____opt_0 and ____opt_0:GetSpecialValueFor("armor_bonus") or 0
|
||||
end
|
||||
function modifier_bone_armor_passive.prototype.playRetaliateParticle(self)
|
||||
ParticleManager:CreateParticle(
|
||||
"particles/econ/items/centaur/centaur_crownfall_belt/centaur_crownfall_belt_retaliate.vpcf",
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
self:GetParent()
|
||||
)
|
||||
end
|
||||
function modifier_bone_armor_passive.prototype.hasHeroNearCreep(self, creep, radius)
|
||||
local heroes = FindUnitsInRadius(
|
||||
creep:GetTeamNumber(),
|
||||
creep:GetAbsOrigin(),
|
||||
nil,
|
||||
radius,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
DOTA_UNIT_TARGET_HERO,
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
for ____, hero in ipairs(heroes) do
|
||||
if hero and IsValidEntity(hero) and hero:IsAlive() and hero:IsRealHero() then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
function modifier_bone_armor_passive.prototype.applyReflectedDamage(self, attacker, parent, ability, eventDamage)
|
||||
local reflectChance = ability:GetSpecialValueFor("damage_reflect_chance")
|
||||
if not rollLuckChance(nil, attacker, reflectChance / 100) then
|
||||
return
|
||||
end
|
||||
local reflectPct = ability:GetSpecialValueFor("damage_reflect_pct")
|
||||
local reflectDamage = eventDamage * (reflectPct / 100)
|
||||
if reflectDamage <= 0 then
|
||||
return
|
||||
end
|
||||
ApplyDamage({
|
||||
victim = attacker,
|
||||
attacker = parent,
|
||||
damage = reflectDamage,
|
||||
damage_type = DAMAGE_TYPE_PHYSICAL,
|
||||
ability = ability
|
||||
})
|
||||
self:playRetaliateParticle()
|
||||
end
|
||||
function modifier_bone_armor_passive.prototype.OnAttackLanded(self, event)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if event.target ~= self:GetParent() then
|
||||
return
|
||||
end
|
||||
local attacker = event.attacker
|
||||
if not attacker or not IsValidEntity(attacker) or not attacker:IsAlive() then
|
||||
return
|
||||
end
|
||||
if not attacker:IsHero() or not attacker:IsRealHero() then
|
||||
return
|
||||
end
|
||||
if not attacker:IsRangedAttacker() then
|
||||
return
|
||||
end
|
||||
if attacker:HasModifier(modifier_item_shameful_pipe.name) then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local heroProximityRadius = ability:GetSpecialValueFor("hero_proximity_radius")
|
||||
local heroNearCreep = self:hasHeroNearCreep(parent, heroProximityRadius)
|
||||
if not heroNearCreep then
|
||||
local isolatedMultiplier = ability:GetSpecialValueFor("isolated_damage_multiplier")
|
||||
local damage = event.damage * isolatedMultiplier
|
||||
if damage > 0 then
|
||||
ApplyDamage({
|
||||
victim = attacker,
|
||||
attacker = parent,
|
||||
damage = damage,
|
||||
damage_type = DAMAGE_TYPE_PHYSICAL,
|
||||
ability = ability
|
||||
})
|
||||
self:playRetaliateParticle()
|
||||
end
|
||||
return
|
||||
end
|
||||
self:applyReflectedDamage(attacker, parent, ability, event.damage)
|
||||
end
|
||||
modifier_bone_armor_passive = __TS__Decorate(
|
||||
modifier_bone_armor_passive,
|
||||
modifier_bone_armor_passive,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_bone_armor_passive"}
|
||||
)
|
||||
____exports.modifier_bone_armor_passive = modifier_bone_armor_passive
|
||||
return ____exports
|
||||
@@ -0,0 +1,362 @@
|
||||
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 modifier_boss_nevermore_coil_beam_lock
|
||||
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 ____modifier_boss_nevermore_coil_debuff = require("abilities.creep.modifier_boss_nevermore_coil_debuff")
|
||||
local modifier_boss_nevermore_coil_debuff = ____modifier_boss_nevermore_coil_debuff.modifier_boss_nevermore_coil_debuff
|
||||
--- Как 1-я способность (две волны по «шахматке»), но слоты в линию от босса к точке каста — «к врагу».
|
||||
____exports.boss_nevermore_coil_beam = __TS__Class()
|
||||
local boss_nevermore_coil_beam = ____exports.boss_nevermore_coil_beam
|
||||
boss_nevermore_coil_beam.name = "boss_nevermore_coil_beam"
|
||||
boss_nevermore_coil_beam.____file_path = "scripts/vscripts/abilities/creep/boss_nevermore_coil_beam.lua"
|
||||
__TS__ClassExtends(boss_nevermore_coil_beam, BaseAbility)
|
||||
function boss_nevermore_coil_beam.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_nevermore/nevermore_shadowraze.vpcf", context)
|
||||
PrecacheResource("particle", "particles/darkmoon_creep_warning.vpcf", context)
|
||||
PrecacheResource("particle", "particles/econ/events/darkmoon_2017/darkmoon_generic_aoe.vpcf", context)
|
||||
PrecacheResource("soundfile", "sounds/units/heroes/nevermore/shadowraze.vsnd", context)
|
||||
end
|
||||
function boss_nevermore_coil_beam.prototype.GetAOERadius(self)
|
||||
local r = self:GetSpecialValueFor("radius")
|
||||
local start = self:getSpecialOrDefault("beam_start_dist", ____exports.boss_nevermore_coil_beam.DEFAULT_BEAM_START)
|
||||
local step = self:getBeamStepDistance()
|
||||
local slots = self:getSlotCountForPhase(self:getBossPhase())
|
||||
return start + step * math.max(0, slots - 1) + r * 2
|
||||
end
|
||||
function boss_nevermore_coil_beam.prototype.getSpecialOrDefault(self, name, fallback)
|
||||
local value = self:GetSpecialValueFor(name)
|
||||
if not value or value <= 0 then
|
||||
return fallback
|
||||
end
|
||||
return value
|
||||
end
|
||||
function boss_nevermore_coil_beam.prototype.getBossPhase(self)
|
||||
local caster = self:GetCaster()
|
||||
if not caster or caster:IsNull() then
|
||||
return 1
|
||||
end
|
||||
local hp = caster:GetHealthPercent()
|
||||
if hp <= 25 then
|
||||
return 4
|
||||
end
|
||||
if hp <= 50 then
|
||||
return 3
|
||||
end
|
||||
if hp <= 75 then
|
||||
return 2
|
||||
end
|
||||
return 1
|
||||
end
|
||||
function boss_nevermore_coil_beam.prototype.getSlotCountForPhase(self, phase)
|
||||
local baseSlots = math.floor(self:getSpecialOrDefault("lane_slot_count", ____exports.boss_nevermore_coil_beam.DEFAULT_SLOT_COUNT))
|
||||
local perPhase = math.floor(self:getSpecialOrDefault("lane_slot_phase_bonus", ____exports.boss_nevermore_coil_beam.DEFAULT_SLOT_PHASE_BONUS))
|
||||
return baseSlots + (phase - 1) * perPhase
|
||||
end
|
||||
function boss_nevermore_coil_beam.prototype.getBeamStepDistance(self)
|
||||
local radius = self:GetSpecialValueFor("radius")
|
||||
local stepKv = self:GetSpecialValueFor("beam_step")
|
||||
if stepKv > 0 then
|
||||
return stepKv
|
||||
end
|
||||
return math.max(radius * 1.22, ____exports.boss_nevermore_coil_beam.DEFAULT_BEAM_STEP)
|
||||
end
|
||||
function boss_nevermore_coil_beam.prototype.buildBeamSlots(self, origin, direction, phase)
|
||||
local dir2d = direction:Normalized()
|
||||
dir2d.z = 0
|
||||
local slotCount = self:getSlotCountForPhase(phase)
|
||||
local startDist = self:getSpecialOrDefault("beam_start_dist", ____exports.boss_nevermore_coil_beam.DEFAULT_BEAM_START)
|
||||
local step = self:getBeamStepDistance()
|
||||
local firstWaveHitsEvenIndex = RandomInt(0, 1) == 0
|
||||
local slots = {}
|
||||
do
|
||||
local i = 0
|
||||
while i < slotCount do
|
||||
local along = startDist + i * step
|
||||
local pos = GetGroundPosition(origin + dir2d * along, nil)
|
||||
local isEven = i % 2 == 0
|
||||
local ____firstWaveHitsEvenIndex_0
|
||||
if firstWaveHitsEvenIndex then
|
||||
____firstWaveHitsEvenIndex_0 = isEven
|
||||
else
|
||||
____firstWaveHitsEvenIndex_0 = not isEven
|
||||
end
|
||||
local hitsOnFirstWave = ____firstWaveHitsEvenIndex_0
|
||||
slots[#slots + 1] = {position = pos, hitsOnFirstWave = hitsOnFirstWave}
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
return slots
|
||||
end
|
||||
function boss_nevermore_coil_beam.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local origin = caster:GetAbsOrigin()
|
||||
local point = self:GetCursorPosition()
|
||||
local toPoint = point - origin
|
||||
local direction = toPoint:Length2D() < 1 and caster:GetForwardVector() or toPoint:Normalized()
|
||||
local phase = self:getBossPhase()
|
||||
self:spawnBeamPattern(origin, direction, phase)
|
||||
end
|
||||
function boss_nevermore_coil_beam.prototype.spawnBeamPattern(self, origin, direction, phase)
|
||||
local caster = self:GetCaster()
|
||||
local slots = self:buildBeamSlots(origin, direction, phase)
|
||||
if #slots == 0 then
|
||||
return
|
||||
end
|
||||
local radius = self:GetSpecialValueFor("radius")
|
||||
local startDelay = self:getSpecialOrDefault("start_delay", ____exports.boss_nevermore_coil_beam.DEFAULT_START_DELAY)
|
||||
local warningTime = self:getSpecialOrDefault("precast_warning_time", ____exports.boss_nevermore_coil_beam.DEFAULT_WARNING_TIME)
|
||||
local secondDelayRaw = self:GetSpecialValueFor("second_wave_delay")
|
||||
local secondWaveDelay = secondDelayRaw > 0 and secondDelayRaw or ____exports.boss_nevermore_coil_beam.DEFAULT_SECOND_WAVE_DELAY
|
||||
local firstHitTime = startDelay
|
||||
local secondHitTime = firstHitTime + secondWaveDelay
|
||||
local firstWarningStart = math.max(0, firstHitTime - warningTime)
|
||||
local secondWarningStart = math.max(firstHitTime + 0.1, secondHitTime - warningTime)
|
||||
caster:AddNewModifier(caster, self, modifier_boss_nevermore_coil_beam_lock.name, {duration = secondHitTime + 0.35})
|
||||
local warnings = {}
|
||||
local function destroyWarningPair(____, pair)
|
||||
ParticleManager:DestroyParticle(pair[1], true)
|
||||
ParticleManager:ReleaseParticleIndex(pair[1])
|
||||
ParticleManager:DestroyParticle(pair[2], true)
|
||||
ParticleManager:ReleaseParticleIndex(pair[2])
|
||||
end
|
||||
local function clearWarning(____, idx)
|
||||
local pair = warnings[idx + 1]
|
||||
if not pair then
|
||||
return
|
||||
end
|
||||
destroyWarningPair(nil, pair)
|
||||
warnings[idx + 1] = nil
|
||||
end
|
||||
Timers:CreateTimer(
|
||||
firstWarningStart,
|
||||
function()
|
||||
if not caster or caster:IsNull() or not caster:IsAlive() then
|
||||
return nil
|
||||
end
|
||||
do
|
||||
local i = 0
|
||||
while i < #slots do
|
||||
do
|
||||
if not slots[i + 1].hitsOnFirstWave then
|
||||
goto __continue25
|
||||
end
|
||||
warnings[i + 1] = self:createPulseWarningColored(slots[i + 1].position, radius, true)
|
||||
end
|
||||
::__continue25::
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
)
|
||||
Timers:CreateTimer(
|
||||
firstHitTime,
|
||||
function()
|
||||
if not caster or caster:IsNull() or not caster:IsAlive() then
|
||||
return nil
|
||||
end
|
||||
local impactPhase = self:getBossPhase()
|
||||
local baseDamage = caster:GetAttackDamage()
|
||||
local bonusDamagePerStack = self:GetSpecialValueFor("coil_stack_bonus_damage")
|
||||
local damageMultiplier = 1 + (impactPhase - 1) * 0.3
|
||||
do
|
||||
local i = 0
|
||||
while i < #slots do
|
||||
do
|
||||
if not slots[i + 1].hitsOnFirstWave then
|
||||
goto __continue29
|
||||
end
|
||||
clearWarning(nil, i)
|
||||
self:applyPulseDamage(
|
||||
slots[i + 1].position,
|
||||
radius,
|
||||
baseDamage,
|
||||
bonusDamagePerStack,
|
||||
damageMultiplier
|
||||
)
|
||||
end
|
||||
::__continue29::
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
)
|
||||
Timers:CreateTimer(
|
||||
secondWarningStart,
|
||||
function()
|
||||
if not caster or caster:IsNull() or not caster:IsAlive() then
|
||||
return nil
|
||||
end
|
||||
do
|
||||
local i = 0
|
||||
while i < #slots do
|
||||
do
|
||||
if slots[i + 1].hitsOnFirstWave then
|
||||
goto __continue33
|
||||
end
|
||||
if warnings[i + 1] then
|
||||
goto __continue33
|
||||
end
|
||||
warnings[i + 1] = self:createPulseWarningColored(slots[i + 1].position, radius, false)
|
||||
end
|
||||
::__continue33::
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
)
|
||||
Timers:CreateTimer(
|
||||
secondHitTime,
|
||||
function()
|
||||
if not caster or caster:IsNull() or not caster:IsAlive() then
|
||||
return nil
|
||||
end
|
||||
local impactPhase = self:getBossPhase()
|
||||
local baseDamage = caster:GetAttackDamage()
|
||||
local bonusDamagePerStack = self:GetSpecialValueFor("coil_stack_bonus_damage")
|
||||
local damageMultiplier = 1 + (impactPhase - 1) * 0.3
|
||||
do
|
||||
local i = 0
|
||||
while i < #slots do
|
||||
do
|
||||
if slots[i + 1].hitsOnFirstWave then
|
||||
goto __continue38
|
||||
end
|
||||
clearWarning(nil, i)
|
||||
self:applyPulseDamage(
|
||||
slots[i + 1].position,
|
||||
radius,
|
||||
baseDamage,
|
||||
bonusDamagePerStack,
|
||||
damageMultiplier
|
||||
)
|
||||
end
|
||||
::__continue38::
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
)
|
||||
end
|
||||
function boss_nevermore_coil_beam.prototype.createPulseWarningColored(self, pulsePoint, radius, firstWaveStrike)
|
||||
local caster = self:GetCaster()
|
||||
local warningParticle = ParticleManager:CreateParticle("particles/darkmoon_creep_warning.vpcf", PATTACH_CUSTOMORIGIN, caster)
|
||||
local aoeParticle = ParticleManager:CreateParticle("particles/econ/events/darkmoon_2017/darkmoon_generic_aoe.vpcf", PATTACH_CUSTOMORIGIN, caster)
|
||||
ParticleManager:SetParticleControl(warningParticle, 0, pulsePoint)
|
||||
ParticleManager:SetParticleControl(
|
||||
warningParticle,
|
||||
1,
|
||||
Vector(radius, 100, 100)
|
||||
)
|
||||
ParticleManager:SetParticleControl(aoeParticle, 0, pulsePoint)
|
||||
ParticleManager:SetParticleControl(
|
||||
aoeParticle,
|
||||
1,
|
||||
Vector(radius, 0, 0)
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
aoeParticle,
|
||||
2,
|
||||
Vector(6, 0, 1)
|
||||
)
|
||||
local rgb = firstWaveStrike and Vector(240, 40, 40) or Vector(70, 170, 255)
|
||||
ParticleManager:SetParticleControl(aoeParticle, 3, rgb)
|
||||
ParticleManager:SetParticleControl(aoeParticle, 4, pulsePoint)
|
||||
return {warningParticle, aoeParticle}
|
||||
end
|
||||
function boss_nevermore_coil_beam.prototype.applyPulseDamage(self, pulsePoint, radius, baseDamage, bonusDamagePerStack, damageMultiplier)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
if not caster or caster:IsNull() or not caster:IsAlive() then
|
||||
return
|
||||
end
|
||||
local enemies = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
pulsePoint,
|
||||
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
|
||||
)
|
||||
for ____, enemy in ipairs(enemies) do
|
||||
local coilDebuff = enemy:FindModifierByName(modifier_boss_nevermore_coil_debuff.name)
|
||||
local stacks = coilDebuff and coilDebuff:GetStackCount() or 0
|
||||
local damage = (baseDamage + stacks * bonusDamagePerStack) * damageMultiplier
|
||||
ApplyDamage({
|
||||
victim = enemy,
|
||||
attacker = caster,
|
||||
damage = damage,
|
||||
damage_type = DAMAGE_TYPE_MAGICAL,
|
||||
ability = self
|
||||
})
|
||||
local updatedDebuff = enemy:AddNewModifier(caster, self, modifier_boss_nevermore_coil_debuff.name, {})
|
||||
if updatedDebuff ~= nil then
|
||||
if stacks > 0 then
|
||||
updatedDebuff:SetStackCount(stacks + 1)
|
||||
else
|
||||
updatedDebuff:SetStackCount(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
EmitSoundOnLocationWithCaster(pulsePoint, "Hero_Nevermore.Shadowraze", caster)
|
||||
local hitParticle = ParticleManager:CreateParticle("particles/units/heroes/hero_nevermore/nevermore_shadowraze.vpcf", PATTACH_WORLDORIGIN, nil)
|
||||
ParticleManager:SetParticleControl(hitParticle, 0, pulsePoint)
|
||||
ParticleManager:SetParticleControl(
|
||||
hitParticle,
|
||||
1,
|
||||
Vector(radius, 1, 1)
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(hitParticle)
|
||||
end
|
||||
boss_nevermore_coil_beam.DEFAULT_START_DELAY = 0.9
|
||||
boss_nevermore_coil_beam.DEFAULT_SECOND_WAVE_DELAY = 1
|
||||
boss_nevermore_coil_beam.DEFAULT_WARNING_TIME = 0.85
|
||||
boss_nevermore_coil_beam.DEFAULT_SLOT_COUNT = 12
|
||||
boss_nevermore_coil_beam.DEFAULT_SLOT_PHASE_BONUS = 2
|
||||
boss_nevermore_coil_beam.DEFAULT_BEAM_START = 140
|
||||
boss_nevermore_coil_beam.DEFAULT_BEAM_STEP = 195
|
||||
boss_nevermore_coil_beam = __TS__Decorate(
|
||||
boss_nevermore_coil_beam,
|
||||
boss_nevermore_coil_beam,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "boss_nevermore_coil_beam"}
|
||||
)
|
||||
____exports.boss_nevermore_coil_beam = boss_nevermore_coil_beam
|
||||
modifier_boss_nevermore_coil_beam_lock = __TS__Class()
|
||||
modifier_boss_nevermore_coil_beam_lock.name = "modifier_boss_nevermore_coil_beam_lock"
|
||||
modifier_boss_nevermore_coil_beam_lock.____file_path = "scripts/vscripts/abilities/creep/boss_nevermore_coil_beam.lua"
|
||||
__TS__ClassExtends(modifier_boss_nevermore_coil_beam_lock, BaseModifier)
|
||||
function modifier_boss_nevermore_coil_beam_lock.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_boss_nevermore_coil_beam_lock.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_boss_nevermore_coil_beam_lock.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_DISARMED] = true}
|
||||
end
|
||||
modifier_boss_nevermore_coil_beam_lock = __TS__Decorate(
|
||||
modifier_boss_nevermore_coil_beam_lock,
|
||||
modifier_boss_nevermore_coil_beam_lock,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_boss_nevermore_coil_beam_lock"}
|
||||
)
|
||||
return ____exports
|
||||
@@ -0,0 +1,353 @@
|
||||
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 modifier_boss_nevermore_coil_wave_lock
|
||||
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 ____modifier_boss_nevermore_coil_debuff = require("abilities.creep.modifier_boss_nevermore_coil_debuff")
|
||||
local modifier_boss_nevermore_coil_debuff = ____modifier_boss_nevermore_coil_debuff.modifier_boss_nevermore_coil_debuff
|
||||
____exports.boss_nevermore_coil_wave = __TS__Class()
|
||||
local boss_nevermore_coil_wave = ____exports.boss_nevermore_coil_wave
|
||||
boss_nevermore_coil_wave.name = "boss_nevermore_coil_wave"
|
||||
boss_nevermore_coil_wave.____file_path = "scripts/vscripts/abilities/creep/boss_nevermore_coil_wave.lua"
|
||||
__TS__ClassExtends(boss_nevermore_coil_wave, BaseAbility)
|
||||
function boss_nevermore_coil_wave.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_nevermore/nevermore_shadowraze.vpcf", context)
|
||||
PrecacheResource("particle", "particles/darkmoon_creep_warning.vpcf", context)
|
||||
PrecacheResource("particle", "particles/econ/events/darkmoon_2017/darkmoon_generic_aoe.vpcf", context)
|
||||
PrecacheResource("soundfile", "sounds/units/heroes/nevermore/shadowraze.vsnd", context)
|
||||
end
|
||||
function boss_nevermore_coil_wave.prototype.GetAOERadius(self)
|
||||
local r = self:GetSpecialValueFor("radius")
|
||||
local forward = self:getSpecialOrDefault("lane_forward_dist", ____exports.boss_nevermore_coil_wave.DEFAULT_FORWARD_DIST)
|
||||
return forward + r * 2
|
||||
end
|
||||
function boss_nevermore_coil_wave.prototype.getSpecialOrDefault(self, name, fallback)
|
||||
local value = self:GetSpecialValueFor(name)
|
||||
if not value or value <= 0 then
|
||||
return fallback
|
||||
end
|
||||
return value
|
||||
end
|
||||
function boss_nevermore_coil_wave.prototype.getBossPhase(self)
|
||||
local caster = self:GetCaster()
|
||||
if not caster or caster:IsNull() then
|
||||
return 1
|
||||
end
|
||||
local hp = caster:GetHealthPercent()
|
||||
if hp <= 25 then
|
||||
return 4
|
||||
end
|
||||
if hp <= 50 then
|
||||
return 3
|
||||
end
|
||||
if hp <= 75 then
|
||||
return 2
|
||||
end
|
||||
return 1
|
||||
end
|
||||
function boss_nevermore_coil_wave.prototype.buildGapLaneSlots(self, origin, direction, phase)
|
||||
local radius = self:GetSpecialValueFor("radius")
|
||||
local baseSlots = math.floor(self:getSpecialOrDefault("lane_slot_count", ____exports.boss_nevermore_coil_wave.DEFAULT_SLOT_COUNT))
|
||||
local slotCount = baseSlots + (phase - 1)
|
||||
local forwardDist = self:getSpecialOrDefault("lane_forward_dist", ____exports.boss_nevermore_coil_wave.DEFAULT_FORWARD_DIST) + (phase - 1) * 35
|
||||
local spacing = math.max(
|
||||
radius * self:getSpecialOrDefault("lane_slot_spacing", ____exports.boss_nevermore_coil_wave.DEFAULT_SLOT_SPACING_FACTOR),
|
||||
185
|
||||
)
|
||||
local dir2d = direction:Normalized()
|
||||
dir2d.z = 0
|
||||
local right = Vector(-dir2d.y, dir2d.x, 0):Normalized()
|
||||
local centerRow = GetGroundPosition(origin + dir2d * forwardDist, nil)
|
||||
local firstWaveHitsEvenIndex = RandomInt(0, 1) == 0
|
||||
local slots = {}
|
||||
local mid = (slotCount - 1) / 2
|
||||
do
|
||||
local i = 0
|
||||
while i < slotCount do
|
||||
local lateral = (i - mid) * spacing
|
||||
local pos = GetGroundPosition(centerRow + right * lateral, nil)
|
||||
local isEven = i % 2 == 0
|
||||
local ____firstWaveHitsEvenIndex_0
|
||||
if firstWaveHitsEvenIndex then
|
||||
____firstWaveHitsEvenIndex_0 = isEven
|
||||
else
|
||||
____firstWaveHitsEvenIndex_0 = not isEven
|
||||
end
|
||||
local hitsOnFirstWave = ____firstWaveHitsEvenIndex_0
|
||||
slots[#slots + 1] = {position = pos, hitsOnFirstWave = hitsOnFirstWave}
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
return slots
|
||||
end
|
||||
function boss_nevermore_coil_wave.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local origin = caster:GetAbsOrigin()
|
||||
local point = self:GetCursorPosition()
|
||||
local toPoint = point - origin
|
||||
local direction = toPoint:Length2D() < 1 and caster:GetForwardVector() or toPoint:Normalized()
|
||||
local phase = self:getBossPhase()
|
||||
self:spawnGapLanePattern(origin, direction, phase)
|
||||
end
|
||||
function boss_nevermore_coil_wave.prototype.spawnGapLanePattern(self, origin, direction, phase)
|
||||
local caster = self:GetCaster()
|
||||
local slots = self:buildGapLaneSlots(origin, direction, phase)
|
||||
if #slots == 0 then
|
||||
return
|
||||
end
|
||||
local radius = self:GetSpecialValueFor("radius")
|
||||
local startDelay = self:getSpecialOrDefault("start_delay", ____exports.boss_nevermore_coil_wave.DEFAULT_START_DELAY)
|
||||
local warningTime = self:getSpecialOrDefault("precast_warning_time", ____exports.boss_nevermore_coil_wave.DEFAULT_WARNING_TIME)
|
||||
local secondDelayRaw = self:GetSpecialValueFor("second_wave_delay")
|
||||
local secondWaveDelay = secondDelayRaw > 0 and secondDelayRaw or ____exports.boss_nevermore_coil_wave.DEFAULT_SECOND_WAVE_DELAY
|
||||
local firstHitTime = startDelay
|
||||
local secondHitTime = firstHitTime + secondWaveDelay
|
||||
local firstWarningStart = math.max(0, firstHitTime - warningTime)
|
||||
local secondWarningStart = math.max(firstHitTime + 0.1, secondHitTime - warningTime)
|
||||
caster:AddNewModifier(caster, self, modifier_boss_nevermore_coil_wave_lock.name, {duration = secondHitTime + 0.35})
|
||||
local warnings = {}
|
||||
local function destroyWarningPair(____, pair)
|
||||
ParticleManager:DestroyParticle(pair[1], true)
|
||||
ParticleManager:ReleaseParticleIndex(pair[1])
|
||||
ParticleManager:DestroyParticle(pair[2], true)
|
||||
ParticleManager:ReleaseParticleIndex(pair[2])
|
||||
end
|
||||
local function clearWarning(____, idx)
|
||||
local pair = warnings[idx + 1]
|
||||
if not pair then
|
||||
return
|
||||
end
|
||||
destroyWarningPair(nil, pair)
|
||||
warnings[idx + 1] = nil
|
||||
end
|
||||
Timers:CreateTimer(
|
||||
firstWarningStart,
|
||||
function()
|
||||
if not caster or caster:IsNull() or not caster:IsAlive() then
|
||||
return nil
|
||||
end
|
||||
do
|
||||
local i = 0
|
||||
while i < #slots do
|
||||
do
|
||||
if not slots[i + 1].hitsOnFirstWave then
|
||||
goto __continue22
|
||||
end
|
||||
warnings[i + 1] = self:createPulseWarningColored(slots[i + 1].position, radius, true)
|
||||
end
|
||||
::__continue22::
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
)
|
||||
Timers:CreateTimer(
|
||||
firstHitTime,
|
||||
function()
|
||||
if not caster or caster:IsNull() or not caster:IsAlive() then
|
||||
return nil
|
||||
end
|
||||
local impactPhase = self:getBossPhase()
|
||||
local baseDamage = caster:GetAttackDamage()
|
||||
local bonusDamagePerStack = self:GetSpecialValueFor("coil_stack_bonus_damage")
|
||||
local damageMultiplier = 1 + (impactPhase - 1) * 0.3
|
||||
do
|
||||
local i = 0
|
||||
while i < #slots do
|
||||
do
|
||||
if not slots[i + 1].hitsOnFirstWave then
|
||||
goto __continue26
|
||||
end
|
||||
clearWarning(nil, i)
|
||||
self:applyPulseDamage(
|
||||
slots[i + 1].position,
|
||||
radius,
|
||||
baseDamage,
|
||||
bonusDamagePerStack,
|
||||
damageMultiplier
|
||||
)
|
||||
end
|
||||
::__continue26::
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
)
|
||||
Timers:CreateTimer(
|
||||
secondWarningStart,
|
||||
function()
|
||||
if not caster or caster:IsNull() or not caster:IsAlive() then
|
||||
return nil
|
||||
end
|
||||
do
|
||||
local i = 0
|
||||
while i < #slots do
|
||||
do
|
||||
if slots[i + 1].hitsOnFirstWave then
|
||||
goto __continue30
|
||||
end
|
||||
if warnings[i + 1] then
|
||||
goto __continue30
|
||||
end
|
||||
warnings[i + 1] = self:createPulseWarningColored(slots[i + 1].position, radius, false)
|
||||
end
|
||||
::__continue30::
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
)
|
||||
Timers:CreateTimer(
|
||||
secondHitTime,
|
||||
function()
|
||||
if not caster or caster:IsNull() or not caster:IsAlive() then
|
||||
return nil
|
||||
end
|
||||
local impactPhase = self:getBossPhase()
|
||||
local baseDamage = caster:GetAttackDamage()
|
||||
local bonusDamagePerStack = self:GetSpecialValueFor("coil_stack_bonus_damage")
|
||||
local damageMultiplier = 1 + (impactPhase - 1) * 0.3
|
||||
do
|
||||
local i = 0
|
||||
while i < #slots do
|
||||
do
|
||||
if slots[i + 1].hitsOnFirstWave then
|
||||
goto __continue35
|
||||
end
|
||||
clearWarning(nil, i)
|
||||
self:applyPulseDamage(
|
||||
slots[i + 1].position,
|
||||
radius,
|
||||
baseDamage,
|
||||
bonusDamagePerStack,
|
||||
damageMultiplier
|
||||
)
|
||||
end
|
||||
::__continue35::
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
)
|
||||
end
|
||||
function boss_nevermore_coil_wave.prototype.createPulseWarningColored(self, pulsePoint, radius, firstWaveStrike)
|
||||
local caster = self:GetCaster()
|
||||
local warningParticle = ParticleManager:CreateParticle("particles/darkmoon_creep_warning.vpcf", PATTACH_CUSTOMORIGIN, caster)
|
||||
local aoeParticle = ParticleManager:CreateParticle("particles/econ/events/darkmoon_2017/darkmoon_generic_aoe.vpcf", PATTACH_CUSTOMORIGIN, caster)
|
||||
ParticleManager:SetParticleControl(warningParticle, 0, pulsePoint)
|
||||
ParticleManager:SetParticleControl(
|
||||
warningParticle,
|
||||
1,
|
||||
Vector(radius, 100, 100)
|
||||
)
|
||||
ParticleManager:SetParticleControl(aoeParticle, 0, pulsePoint)
|
||||
ParticleManager:SetParticleControl(
|
||||
aoeParticle,
|
||||
1,
|
||||
Vector(radius, 0, 0)
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
aoeParticle,
|
||||
2,
|
||||
Vector(6, 0, 1)
|
||||
)
|
||||
local rgb = firstWaveStrike and Vector(240, 40, 40) or Vector(70, 170, 255)
|
||||
ParticleManager:SetParticleControl(aoeParticle, 3, rgb)
|
||||
ParticleManager:SetParticleControl(aoeParticle, 4, pulsePoint)
|
||||
return {warningParticle, aoeParticle}
|
||||
end
|
||||
function boss_nevermore_coil_wave.prototype.applyPulseDamage(self, pulsePoint, radius, baseDamage, bonusDamagePerStack, damageMultiplier)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
if not caster or caster:IsNull() or not caster:IsAlive() then
|
||||
return
|
||||
end
|
||||
local enemies = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
pulsePoint,
|
||||
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
|
||||
)
|
||||
for ____, enemy in ipairs(enemies) do
|
||||
local coilDebuff = enemy:FindModifierByName(modifier_boss_nevermore_coil_debuff.name)
|
||||
local stacks = coilDebuff and coilDebuff:GetStackCount() or 0
|
||||
local damage = (baseDamage + stacks * bonusDamagePerStack) * damageMultiplier
|
||||
ApplyDamage({
|
||||
victim = enemy,
|
||||
attacker = caster,
|
||||
damage = damage,
|
||||
damage_type = DAMAGE_TYPE_MAGICAL,
|
||||
ability = self
|
||||
})
|
||||
local updatedDebuff = enemy:AddNewModifier(caster, self, modifier_boss_nevermore_coil_debuff.name, {})
|
||||
if updatedDebuff ~= nil then
|
||||
if stacks > 0 then
|
||||
updatedDebuff:SetStackCount(stacks + 1)
|
||||
else
|
||||
updatedDebuff:SetStackCount(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
EmitSoundOnLocationWithCaster(pulsePoint, "Hero_Nevermore.Shadowraze", caster)
|
||||
local hitParticle = ParticleManager:CreateParticle("particles/units/heroes/hero_nevermore/nevermore_shadowraze.vpcf", PATTACH_WORLDORIGIN, nil)
|
||||
ParticleManager:SetParticleControl(hitParticle, 0, pulsePoint)
|
||||
ParticleManager:SetParticleControl(
|
||||
hitParticle,
|
||||
1,
|
||||
Vector(radius, 1, 1)
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(hitParticle)
|
||||
end
|
||||
boss_nevermore_coil_wave.DEFAULT_START_DELAY = 0.9
|
||||
boss_nevermore_coil_wave.DEFAULT_SECOND_WAVE_DELAY = 1
|
||||
boss_nevermore_coil_wave.DEFAULT_WARNING_TIME = 0.85
|
||||
boss_nevermore_coil_wave.DEFAULT_SLOT_COUNT = 5
|
||||
boss_nevermore_coil_wave.DEFAULT_FORWARD_DIST = 360
|
||||
boss_nevermore_coil_wave.DEFAULT_SLOT_SPACING_FACTOR = 1.32
|
||||
boss_nevermore_coil_wave = __TS__Decorate(
|
||||
boss_nevermore_coil_wave,
|
||||
boss_nevermore_coil_wave,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "boss_nevermore_coil_wave"}
|
||||
)
|
||||
____exports.boss_nevermore_coil_wave = boss_nevermore_coil_wave
|
||||
modifier_boss_nevermore_coil_wave_lock = __TS__Class()
|
||||
modifier_boss_nevermore_coil_wave_lock.name = "modifier_boss_nevermore_coil_wave_lock"
|
||||
modifier_boss_nevermore_coil_wave_lock.____file_path = "scripts/vscripts/abilities/creep/boss_nevermore_coil_wave.lua"
|
||||
__TS__ClassExtends(modifier_boss_nevermore_coil_wave_lock, BaseModifier)
|
||||
function modifier_boss_nevermore_coil_wave_lock.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_boss_nevermore_coil_wave_lock.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_boss_nevermore_coil_wave_lock.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_DISARMED] = true}
|
||||
end
|
||||
modifier_boss_nevermore_coil_wave_lock = __TS__Decorate(
|
||||
modifier_boss_nevermore_coil_wave_lock,
|
||||
modifier_boss_nevermore_coil_wave_lock,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_boss_nevermore_coil_wave_lock"}
|
||||
)
|
||||
return ____exports
|
||||
@@ -0,0 +1,284 @@
|
||||
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 modifier_boss_nevermore_hub_crossburst_lock
|
||||
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 ____modifier_boss_nevermore_coil_debuff = require("abilities.creep.modifier_boss_nevermore_coil_debuff")
|
||||
local modifier_boss_nevermore_coil_debuff = ____modifier_boss_nevermore_coil_debuff.modifier_boss_nevermore_coil_debuff
|
||||
--- Случайная точка-хаб: от неё волны взрывов по 4 лучам (плюс или крест).
|
||||
____exports.boss_nevermore_hub_crossburst = __TS__Class()
|
||||
local boss_nevermore_hub_crossburst = ____exports.boss_nevermore_hub_crossburst
|
||||
boss_nevermore_hub_crossburst.name = "boss_nevermore_hub_crossburst"
|
||||
boss_nevermore_hub_crossburst.____file_path = "scripts/vscripts/abilities/creep/boss_nevermore_hub_crossburst.lua"
|
||||
__TS__ClassExtends(boss_nevermore_hub_crossburst, BaseAbility)
|
||||
function boss_nevermore_hub_crossburst.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_nevermore/nevermore_shadowraze.vpcf", context)
|
||||
PrecacheResource("particle", "particles/darkmoon_creep_warning.vpcf", context)
|
||||
PrecacheResource("particle", "particles/econ/events/darkmoon_2017/darkmoon_generic_aoe.vpcf", context)
|
||||
PrecacheResource("soundfile", "sounds/units/heroes/nevermore/shadowraze.vsnd", context)
|
||||
end
|
||||
function boss_nevermore_hub_crossburst.prototype.GetAOERadius(self)
|
||||
local pick = self:getSpecialOrDefault("spawn_pick_radius", ____exports.boss_nevermore_hub_crossburst.DEFAULT_PICK_RADIUS)
|
||||
local start = self:getSpecialOrDefault("ring_start_dist", ____exports.boss_nevermore_hub_crossburst.DEFAULT_RING_START)
|
||||
local step = self:getSpecialOrDefault("ring_step", ____exports.boss_nevermore_hub_crossburst.DEFAULT_RING_STEP)
|
||||
local n = self:getRingCountForPhase(self:getBossPhase())
|
||||
local reach = start + (n - 1) * step + self:GetSpecialValueFor("radius")
|
||||
return pick + reach
|
||||
end
|
||||
function boss_nevermore_hub_crossburst.prototype.getSpecialOrDefault(self, name, fallback)
|
||||
local v = self:GetSpecialValueFor(name)
|
||||
if not v or v <= 0 then
|
||||
return fallback
|
||||
end
|
||||
return v
|
||||
end
|
||||
function boss_nevermore_hub_crossburst.prototype.getBossPhase(self)
|
||||
local c = self:GetCaster()
|
||||
if not c or c:IsNull() then
|
||||
return 1
|
||||
end
|
||||
local hp = c:GetHealthPercent()
|
||||
if hp <= 25 then
|
||||
return 4
|
||||
end
|
||||
if hp <= 50 then
|
||||
return 3
|
||||
end
|
||||
if hp <= 75 then
|
||||
return 2
|
||||
end
|
||||
return 1
|
||||
end
|
||||
function boss_nevermore_hub_crossburst.prototype.getRingCountForPhase(self, phase)
|
||||
local base = math.floor(self:getSpecialOrDefault("ring_count", ____exports.boss_nevermore_hub_crossburst.DEFAULT_RING_COUNT))
|
||||
local perPhase = math.floor(self:getSpecialOrDefault("ring_count_phase_bonus", ____exports.boss_nevermore_hub_crossburst.DEFAULT_RING_COUNT_PHASE_BONUS))
|
||||
return math.max(1, base + (phase - 1) * perPhase)
|
||||
end
|
||||
function boss_nevermore_hub_crossburst.prototype.pickRandomHub(self, groundOrigin)
|
||||
local maxR = self:getSpecialOrDefault("spawn_pick_radius", ____exports.boss_nevermore_hub_crossburst.DEFAULT_PICK_RADIUS)
|
||||
local ang = RandomFloat(0, 360)
|
||||
local dist = maxR * math.sqrt(RandomFloat(0.001, 1))
|
||||
local rad = ang * math.pi / 180
|
||||
local dx = math.cos(rad) * dist
|
||||
local dy = math.sin(rad) * dist
|
||||
return GetGroundPosition(
|
||||
groundOrigin + Vector(dx, dy, 0),
|
||||
nil
|
||||
)
|
||||
end
|
||||
function boss_nevermore_hub_crossburst.prototype.getDirections(self, pattern)
|
||||
if pattern == "plus" then
|
||||
return {
|
||||
Vector(1, 0, 0),
|
||||
Vector(-1, 0, 0),
|
||||
Vector(0, 1, 0),
|
||||
Vector(0, -1, 0)
|
||||
}
|
||||
end
|
||||
local s = 1 / math.sqrt(2)
|
||||
return {
|
||||
Vector(s, s, 0),
|
||||
Vector(s, -s, 0),
|
||||
Vector(-s, s, 0),
|
||||
Vector(-s, -s, 0)
|
||||
}
|
||||
end
|
||||
function boss_nevermore_hub_crossburst.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
if not caster or caster:IsNull() or not caster:IsAlive() then
|
||||
return
|
||||
end
|
||||
local bossGround = GetGroundPosition(
|
||||
caster:GetAbsOrigin(),
|
||||
nil
|
||||
)
|
||||
local hub = self:pickRandomHub(bossGround)
|
||||
local pattern = RandomInt(0, 1) == 0 and "plus" or "cross"
|
||||
local radius = self:GetSpecialValueFor("radius")
|
||||
local rEff = radius > 0 and radius or ____exports.boss_nevermore_hub_crossburst.DEFAULT_RADIUS
|
||||
local phaseNow = self:getBossPhase()
|
||||
local ringCount = self:getRingCountForPhase(phaseNow)
|
||||
local ringStep = self:getSpecialOrDefault("ring_step", ____exports.boss_nevermore_hub_crossburst.DEFAULT_RING_STEP)
|
||||
local ringStart = self:getSpecialOrDefault("ring_start_dist", ____exports.boss_nevermore_hub_crossburst.DEFAULT_RING_START)
|
||||
local baseInterval = self:getSpecialOrDefault("ring_interval", ____exports.boss_nevermore_hub_crossburst.DEFAULT_RING_INTERVAL)
|
||||
--- Ниже фаза — чуть быстрее волны (до ~−12% на фазе 4).
|
||||
local ringInterval = baseInterval * math.max(0.72, 1 - (phaseNow - 1) * 0.06)
|
||||
local firstDelay = self:getSpecialOrDefault("first_ring_delay", ____exports.boss_nevermore_hub_crossburst.DEFAULT_FIRST_DELAY)
|
||||
local warningTime = self:getSpecialOrDefault("precast_warning_time", ____exports.boss_nevermore_hub_crossburst.DEFAULT_WARNING_TIME)
|
||||
local dirs = self:getDirections(pattern)
|
||||
local function ringPositionsForIndex(____, ringIdx)
|
||||
local along = ringStart + ringIdx * ringStep
|
||||
local out = {}
|
||||
for ____, d in ipairs(dirs) do
|
||||
local scaled = Vector(d.x * along, d.y * along, 0)
|
||||
out[#out + 1] = GetGroundPosition(hub + scaled, nil)
|
||||
end
|
||||
return out
|
||||
end
|
||||
local lastHitTime = firstDelay + (ringCount - 1) * ringInterval
|
||||
caster:AddNewModifier(caster, self, modifier_boss_nevermore_hub_crossburst_lock.name, {duration = lastHitTime + 0.5})
|
||||
do
|
||||
local r = 0
|
||||
while r < ringCount do
|
||||
local hitTime = firstDelay + r * ringInterval
|
||||
local warnT = math.max(0, hitTime - warningTime)
|
||||
local positions = ringPositionsForIndex(nil, r)
|
||||
local isRedWave = r % 2 == 0
|
||||
Timers:CreateTimer(
|
||||
warnT,
|
||||
function()
|
||||
if not caster or caster:IsNull() or not caster:IsAlive() then
|
||||
return nil
|
||||
end
|
||||
local ____pairs = {}
|
||||
for ____, p in ipairs(positions) do
|
||||
____pairs[#____pairs + 1] = self:createWarningPair(p, rEff, isRedWave)
|
||||
end
|
||||
Timers:CreateTimer(
|
||||
hitTime - warnT,
|
||||
function()
|
||||
if not caster or caster:IsNull() or not caster:IsAlive() then
|
||||
return nil
|
||||
end
|
||||
for ____, pair in ipairs(____pairs) do
|
||||
do
|
||||
if not pair then
|
||||
goto __continue28
|
||||
end
|
||||
ParticleManager:DestroyParticle(pair[1], true)
|
||||
ParticleManager:ReleaseParticleIndex(pair[1])
|
||||
ParticleManager:DestroyParticle(pair[2], true)
|
||||
ParticleManager:ReleaseParticleIndex(pair[2])
|
||||
end
|
||||
::__continue28::
|
||||
end
|
||||
for ____, p in ipairs(positions) do
|
||||
self:applyRingDamage(p, rEff)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
)
|
||||
return nil
|
||||
end
|
||||
)
|
||||
r = r + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
function boss_nevermore_hub_crossburst.prototype.createWarningPair(self, pos, rad, firstStyle)
|
||||
local caster = self:GetCaster()
|
||||
local warningParticle = ParticleManager:CreateParticle("particles/darkmoon_creep_warning.vpcf", PATTACH_CUSTOMORIGIN, caster)
|
||||
local aoeParticle = ParticleManager:CreateParticle("particles/econ/events/darkmoon_2017/darkmoon_generic_aoe.vpcf", PATTACH_CUSTOMORIGIN, caster)
|
||||
ParticleManager:SetParticleControl(warningParticle, 0, pos)
|
||||
ParticleManager:SetParticleControl(
|
||||
warningParticle,
|
||||
1,
|
||||
Vector(rad, 100, 100)
|
||||
)
|
||||
ParticleManager:SetParticleControl(aoeParticle, 0, pos)
|
||||
ParticleManager:SetParticleControl(
|
||||
aoeParticle,
|
||||
1,
|
||||
Vector(rad, 0, 0)
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
aoeParticle,
|
||||
2,
|
||||
Vector(6, 0, 1)
|
||||
)
|
||||
local rgb = firstStyle and Vector(230, 80, 40) or Vector(180, 80, 230)
|
||||
ParticleManager:SetParticleControl(aoeParticle, 3, rgb)
|
||||
ParticleManager:SetParticleControl(aoeParticle, 4, pos)
|
||||
return {warningParticle, aoeParticle}
|
||||
end
|
||||
function boss_nevermore_hub_crossburst.prototype.applyRingDamage(self, pulsePoint, rad)
|
||||
local caster = self:GetCaster()
|
||||
if not caster or caster:IsNull() or not caster:IsAlive() then
|
||||
return
|
||||
end
|
||||
local phase = self:getBossPhase()
|
||||
local baseDamage = caster:GetAttackDamage()
|
||||
local bonusDamagePerStack = self:GetSpecialValueFor("coil_stack_bonus_damage")
|
||||
local damageMultiplier = 1 + (phase - 1) * 0.3
|
||||
local enemies = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
pulsePoint,
|
||||
nil,
|
||||
rad,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC),
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
for ____, enemy in ipairs(enemies) do
|
||||
local coilDebuff = enemy:FindModifierByName(modifier_boss_nevermore_coil_debuff.name)
|
||||
local stacks = coilDebuff and coilDebuff:GetStackCount() or 0
|
||||
local damage = (baseDamage + stacks * bonusDamagePerStack) * damageMultiplier * 0.85
|
||||
ApplyDamage({
|
||||
victim = enemy,
|
||||
attacker = caster,
|
||||
damage = damage,
|
||||
damage_type = DAMAGE_TYPE_MAGICAL,
|
||||
ability = self
|
||||
})
|
||||
local updatedDebuff = enemy:AddNewModifier(caster, self, modifier_boss_nevermore_coil_debuff.name, {})
|
||||
if updatedDebuff ~= nil then
|
||||
updatedDebuff:SetStackCount(stacks > 0 and stacks + 1 or 1)
|
||||
end
|
||||
end
|
||||
EmitSoundOnLocationWithCaster(pulsePoint, "Hero_Nevermore.Shadowraze", caster)
|
||||
local hitParticle = ParticleManager:CreateParticle("particles/units/heroes/hero_nevermore/nevermore_shadowraze.vpcf", PATTACH_WORLDORIGIN, nil)
|
||||
ParticleManager:SetParticleControl(hitParticle, 0, pulsePoint)
|
||||
ParticleManager:SetParticleControl(
|
||||
hitParticle,
|
||||
1,
|
||||
Vector(rad, 1, 1)
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(hitParticle)
|
||||
end
|
||||
boss_nevermore_hub_crossburst.DEFAULT_PICK_RADIUS = 1500
|
||||
boss_nevermore_hub_crossburst.DEFAULT_RADIUS = 165
|
||||
boss_nevermore_hub_crossburst.DEFAULT_RING_COUNT = 7
|
||||
boss_nevermore_hub_crossburst.DEFAULT_RING_COUNT_PHASE_BONUS = 1
|
||||
boss_nevermore_hub_crossburst.DEFAULT_RING_STEP = 190
|
||||
boss_nevermore_hub_crossburst.DEFAULT_RING_START = 90
|
||||
boss_nevermore_hub_crossburst.DEFAULT_RING_INTERVAL = 0.52
|
||||
boss_nevermore_hub_crossburst.DEFAULT_FIRST_DELAY = 0.55
|
||||
boss_nevermore_hub_crossburst.DEFAULT_WARNING_TIME = 0.82
|
||||
boss_nevermore_hub_crossburst = __TS__Decorate(
|
||||
boss_nevermore_hub_crossburst,
|
||||
boss_nevermore_hub_crossburst,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "boss_nevermore_hub_crossburst"}
|
||||
)
|
||||
____exports.boss_nevermore_hub_crossburst = boss_nevermore_hub_crossburst
|
||||
modifier_boss_nevermore_hub_crossburst_lock = __TS__Class()
|
||||
modifier_boss_nevermore_hub_crossburst_lock.name = "modifier_boss_nevermore_hub_crossburst_lock"
|
||||
modifier_boss_nevermore_hub_crossburst_lock.____file_path = "scripts/vscripts/abilities/creep/boss_nevermore_hub_crossburst.lua"
|
||||
__TS__ClassExtends(modifier_boss_nevermore_hub_crossburst_lock, BaseModifier)
|
||||
function modifier_boss_nevermore_hub_crossburst_lock.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_boss_nevermore_hub_crossburst_lock.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_boss_nevermore_hub_crossburst_lock.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_DISARMED] = true}
|
||||
end
|
||||
modifier_boss_nevermore_hub_crossburst_lock = __TS__Decorate(
|
||||
modifier_boss_nevermore_hub_crossburst_lock,
|
||||
modifier_boss_nevermore_hub_crossburst_lock,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_boss_nevermore_hub_crossburst_lock"}
|
||||
)
|
||||
return ____exports
|
||||
@@ -0,0 +1,494 @@
|
||||
local ____lualib = require("lualib_bundle")
|
||||
local __TS__Class = ____lualib.__TS__Class
|
||||
local __TS__ClassExtends = ____lualib.__TS__ClassExtends
|
||||
local __TS__ArrayMap = ____lualib.__TS__ArrayMap
|
||||
local __TS__ArrayPush = ____lualib.__TS__ArrayPush
|
||||
local __TS__SparseArrayNew = ____lualib.__TS__SparseArrayNew
|
||||
local __TS__SparseArrayPush = ____lualib.__TS__SparseArrayPush
|
||||
local __TS__SparseArraySpread = ____lualib.__TS__SparseArraySpread
|
||||
local __TS__Decorate = ____lualib.__TS__Decorate
|
||||
local ____exports = {}
|
||||
local modifier_boss_nevermore_requiem_barrage_casting
|
||||
local ____nevermore_boss_requiem_bridge = require("ai.nevermore_boss_requiem_bridge")
|
||||
local nevermoreIncrementRequiemCastCount = ____nevermore_boss_requiem_bridge.nevermoreIncrementRequiemCastCount
|
||||
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 ____modifier_boss_nevermore_requiem_magic_resist_debuff = require("abilities.creep.modifier_boss_nevermore_requiem_magic_resist_debuff")
|
||||
local modifier_boss_nevermore_requiem_magic_resist_debuff = ____modifier_boss_nevermore_requiem_magic_resist_debuff.modifier_boss_nevermore_requiem_magic_resist_debuff
|
||||
--- Глобальный множитель урона залпа (подогнан под KV-бафф ~+60%).
|
||||
local REQUIEM_BARRAGE_DAMAGE_MULT = 1.6
|
||||
--- Доп. дальность волны за стадию HP босса (согласовано с KV wave_distance ×1.6).
|
||||
local REQUIEM_PHASE_DISTANCE_BONUS = 192
|
||||
____exports.boss_nevermore_requiem_barrage = __TS__Class()
|
||||
local boss_nevermore_requiem_barrage = ____exports.boss_nevermore_requiem_barrage
|
||||
boss_nevermore_requiem_barrage.name = "boss_nevermore_requiem_barrage"
|
||||
boss_nevermore_requiem_barrage.____file_path = "scripts/vscripts/abilities/creep/boss_nevermore_requiem_barrage.lua"
|
||||
__TS__ClassExtends(boss_nevermore_requiem_barrage, BaseAbility)
|
||||
function boss_nevermore_requiem_barrage.prototype.____constructor(self, ...)
|
||||
BaseAbility.prototype.____constructor(self, ...)
|
||||
self.castSerial = 0
|
||||
self.phasePreviewParticles = {}
|
||||
self.phasePreviewRotationOffset = 0
|
||||
self.activePreviewParticles = {}
|
||||
self.gestureLoopSerial = 0
|
||||
end
|
||||
function boss_nevermore_requiem_barrage.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_nevermore/nevermore_requiemofsouls_line.vpcf", context)
|
||||
PrecacheResource("particle", "particles/boss_tinker_laser_preview_vector.vpcf", context)
|
||||
PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_nevermore.vsndevts", context)
|
||||
end
|
||||
function boss_nevermore_requiem_barrage.prototype.GetChannelTime(self)
|
||||
local channel = self:GetSpecialValueFor("channel_time")
|
||||
return channel > 0 and channel or 3
|
||||
end
|
||||
function boss_nevermore_requiem_barrage.prototype.getBossPhase(self)
|
||||
local caster = self:GetCaster()
|
||||
if not caster or caster:IsNull() then
|
||||
return 1
|
||||
end
|
||||
local hp = caster:GetHealthPercent()
|
||||
if hp <= 25 then
|
||||
return 4
|
||||
end
|
||||
if hp <= 50 then
|
||||
return 3
|
||||
end
|
||||
if hp <= 75 then
|
||||
return 2
|
||||
end
|
||||
return 1
|
||||
end
|
||||
function boss_nevermore_requiem_barrage.prototype.OnAbilityPhaseStart(self)
|
||||
if not IsServer() then
|
||||
return true
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local point = self:GetCursorPosition()
|
||||
local toPoint = point - caster:GetAbsOrigin()
|
||||
local baseDirection = toPoint:Length2D() < 1 and caster:GetForwardVector() or toPoint:Normalized()
|
||||
self:clearPhasePreview()
|
||||
self.phasePreviewRotationOffset = self:getWaveRotationOffset(1)
|
||||
local previewDirections = self:buildBurstDirections(
|
||||
baseDirection,
|
||||
self.phasePreviewRotationOffset,
|
||||
self:getBossPhase()
|
||||
)
|
||||
local previewDuration = self:GetCastPoint() > 0 and self:GetCastPoint() or 0.3
|
||||
self.phasePreviewParticles = self:createWavePreview(previewDirections, previewDuration)
|
||||
return true
|
||||
end
|
||||
function boss_nevermore_requiem_barrage.prototype.OnAbilityPhaseInterrupted(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self:clearPhasePreview()
|
||||
end
|
||||
function boss_nevermore_requiem_barrage.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
nevermoreIncrementRequiemCastCount(nil, caster)
|
||||
local point = self:GetCursorPosition()
|
||||
local toPoint = point - caster:GetAbsOrigin()
|
||||
local baseDirection = toPoint:Length2D() < 1 and caster:GetForwardVector() or toPoint:Normalized()
|
||||
self.castSerial = self.castSerial + 1
|
||||
local currentCast = self.castSerial
|
||||
local channelTime = self:GetChannelTime()
|
||||
local phase = self:getBossPhase()
|
||||
local baseWaveInterval = self:GetSpecialValueFor("wave_interval") > 0 and self:GetSpecialValueFor("wave_interval") or 1
|
||||
local waveInterval = math.max(0.35, baseWaveInterval - (phase - 1) * 0.12)
|
||||
local wavesCount = math.max(
|
||||
1,
|
||||
math.floor(channelTime / waveInterval)
|
||||
)
|
||||
local previewTime = self:GetSpecialValueFor("wave_preview_time") > 0 and self:GetSpecialValueFor("wave_preview_time") or 0.35
|
||||
local waveDistanceKv = self:GetSpecialValueFor("wave_distance") > 0 and self:GetSpecialValueFor("wave_distance") or 850
|
||||
local travelDist = waveDistanceKv + (phase - 1) * REQUIEM_PHASE_DISTANCE_BONUS
|
||||
local waveTravelTimeMax = self:getMaxWaveTravelTime(travelDist)
|
||||
self:clearPhasePreview()
|
||||
caster:AddNewModifier(caster, self, modifier_boss_nevermore_requiem_barrage_casting.name, {duration = channelTime + 0.1})
|
||||
EmitSoundOn("Hero_Nevermore.RequiemOfSoulsCast", caster)
|
||||
self:startGestureLoop(caster)
|
||||
local firstRotationOffset = self.phasePreviewRotationOffset
|
||||
self:fireSoulWaveBurst(baseDirection, firstRotationOffset)
|
||||
Timers:CreateTimer(
|
||||
waveTravelTimeMax,
|
||||
function()
|
||||
self:destroyPreviewParticles(self.phasePreviewParticles)
|
||||
self.phasePreviewParticles = {}
|
||||
return nil
|
||||
end
|
||||
)
|
||||
do
|
||||
local i = 1
|
||||
while i <= wavesCount do
|
||||
local fireTime = i * waveInterval
|
||||
local waveRotationOffset = self:getWaveRotationOffset(i + 1)
|
||||
local wavePreviewParticles = {}
|
||||
local wasPreviewShown = false
|
||||
local speedsForWave
|
||||
Timers:CreateTimer(
|
||||
math.max(0, fireTime - previewTime),
|
||||
function()
|
||||
if self.castSerial ~= currentCast then
|
||||
return nil
|
||||
end
|
||||
if not caster or caster:IsNull() or not caster:IsAlive() then
|
||||
return nil
|
||||
end
|
||||
local previewDirections = self:buildBurstDirections(
|
||||
baseDirection,
|
||||
waveRotationOffset,
|
||||
self:getBossPhase()
|
||||
)
|
||||
speedsForWave = __TS__ArrayMap(
|
||||
previewDirections,
|
||||
function() return self:sampleWaveSpeed() end
|
||||
)
|
||||
wavePreviewParticles = self:createWavePreview(previewDirections, previewTime, speedsForWave)
|
||||
wasPreviewShown = true
|
||||
return nil
|
||||
end
|
||||
)
|
||||
Timers:CreateTimer(
|
||||
fireTime,
|
||||
function()
|
||||
if not wasPreviewShown then
|
||||
return nil
|
||||
end
|
||||
if not caster or caster:IsNull() or not caster:IsAlive() then
|
||||
return nil
|
||||
end
|
||||
self:fireSoulWaveBurst(baseDirection, waveRotationOffset, speedsForWave)
|
||||
Timers:CreateTimer(
|
||||
waveTravelTimeMax,
|
||||
function()
|
||||
self:destroyPreviewParticles(wavePreviewParticles)
|
||||
return nil
|
||||
end
|
||||
)
|
||||
return nil
|
||||
end
|
||||
)
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
function boss_nevermore_requiem_barrage.prototype.OnChannelFinish(self, _bInterrupted)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self.castSerial = self.castSerial + 1
|
||||
local caster = self:GetCaster()
|
||||
self:clearPhasePreview()
|
||||
self:clearAllActivePreviews()
|
||||
self:stopGestureLoop(caster)
|
||||
caster:RemoveModifierByName(modifier_boss_nevermore_requiem_barrage_casting.name)
|
||||
StopSoundOn("Hero_Nevermore.RequiemOfSoulsCast", caster)
|
||||
end
|
||||
function boss_nevermore_requiem_barrage.prototype.startGestureLoop(self, caster)
|
||||
self.gestureLoopSerial = self.gestureLoopSerial + 1
|
||||
local serial = self.gestureLoopSerial
|
||||
local interval = 0.45
|
||||
local tick
|
||||
tick = function()
|
||||
if self.gestureLoopSerial ~= serial then
|
||||
return
|
||||
end
|
||||
if not caster or caster:IsNull() or not caster:IsAlive() then
|
||||
return
|
||||
end
|
||||
if not caster:HasModifier(modifier_boss_nevermore_requiem_barrage_casting.name) then
|
||||
return
|
||||
end
|
||||
caster:StartGestureWithPlaybackRate(ACT_DOTA_RAZE_1, 1)
|
||||
Timers:CreateTimer(
|
||||
interval,
|
||||
function()
|
||||
tick(nil)
|
||||
return nil
|
||||
end
|
||||
)
|
||||
end
|
||||
tick(nil)
|
||||
end
|
||||
function boss_nevermore_requiem_barrage.prototype.stopGestureLoop(self, caster)
|
||||
self.gestureLoopSerial = self.gestureLoopSerial + 1
|
||||
if not caster or caster:IsNull() then
|
||||
return
|
||||
end
|
||||
caster:FadeGesture(ACT_DOTA_RAZE_1)
|
||||
end
|
||||
function boss_nevermore_requiem_barrage.prototype.getWaveSpeedBounds(self)
|
||||
local base = self:GetSpecialValueFor("wave_speed") > 0 and self:GetSpecialValueFor("wave_speed") or 800
|
||||
local minKv = self:GetSpecialValueFor("wave_speed_min")
|
||||
local maxKv = self:GetSpecialValueFor("wave_speed_max")
|
||||
if minKv > 0 and maxKv > 0 and maxKv >= minKv then
|
||||
return {minKv, maxKv}
|
||||
end
|
||||
local lo = math.max(120, base * 0.72)
|
||||
local hi = base * 1.32
|
||||
return {lo, hi}
|
||||
end
|
||||
function boss_nevermore_requiem_barrage.prototype.sampleWaveSpeed(self)
|
||||
local mn, mx = unpack(self:getWaveSpeedBounds())
|
||||
return RandomFloat(mn, mx)
|
||||
end
|
||||
function boss_nevermore_requiem_barrage.prototype.getMaxWaveTravelTime(self, distance)
|
||||
local mn, _ = unpack(self:getWaveSpeedBounds())
|
||||
return distance / mn
|
||||
end
|
||||
function boss_nevermore_requiem_barrage.prototype.fireSoulWaveBurst(self, baseDirection, rotationOffset, precomputedSpeeds)
|
||||
local phase = self:getBossPhase()
|
||||
local directions = self:buildBurstDirections(baseDirection, rotationOffset, phase)
|
||||
local waveDistanceBase = self:GetSpecialValueFor("wave_distance") > 0 and self:GetSpecialValueFor("wave_distance") or 850
|
||||
local startRadius = self:GetSpecialValueFor("wave_width_start") > 0 and self:GetSpecialValueFor("wave_width_start") or 45
|
||||
local endRadius = self:GetSpecialValueFor("wave_width_end") > 0 and self:GetSpecialValueFor("wave_width_end") or 45
|
||||
local distance = waveDistanceBase + (phase - 1) * REQUIEM_PHASE_DISTANCE_BONUS
|
||||
do
|
||||
local i = 0
|
||||
while i < #directions do
|
||||
local direction = directions[i + 1]
|
||||
local spd = precomputedSpeeds ~= nil and precomputedSpeeds[i + 1] ~= nil and precomputedSpeeds[i + 1] or self:sampleWaveSpeed()
|
||||
self:fireSoulWave(
|
||||
direction,
|
||||
distance,
|
||||
spd,
|
||||
startRadius,
|
||||
endRadius
|
||||
)
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
EmitSoundOn(
|
||||
"Hero_Nevermore.RequiemOfSouls",
|
||||
self:GetCaster()
|
||||
)
|
||||
end
|
||||
function boss_nevermore_requiem_barrage.prototype.getWaveRotationOffset(self, waveIndex)
|
||||
local randomOffset = RandomInt(0, 359)
|
||||
local waveOffset = waveIndex * 17
|
||||
return (randomOffset + waveOffset) % 360
|
||||
end
|
||||
function boss_nevermore_requiem_barrage.prototype.buildBurstDirections(self, baseDirection, rotationOffset, phase)
|
||||
local dirs = {}
|
||||
local baseAngles = {
|
||||
0,
|
||||
90,
|
||||
180,
|
||||
270,
|
||||
45,
|
||||
135,
|
||||
225,
|
||||
315
|
||||
}
|
||||
local phaseAngles = {}
|
||||
if phase >= 1 then
|
||||
__TS__ArrayPush(
|
||||
phaseAngles,
|
||||
22.5,
|
||||
112.5,
|
||||
202.5,
|
||||
292.5
|
||||
)
|
||||
end
|
||||
if phase >= 2 then
|
||||
__TS__ArrayPush(
|
||||
phaseAngles,
|
||||
30,
|
||||
120,
|
||||
210,
|
||||
300
|
||||
)
|
||||
end
|
||||
if phase >= 3 then
|
||||
__TS__ArrayPush(
|
||||
phaseAngles,
|
||||
67.5,
|
||||
157.5,
|
||||
247.5,
|
||||
337.5
|
||||
)
|
||||
end
|
||||
if phase >= 4 then
|
||||
__TS__ArrayPush(
|
||||
phaseAngles,
|
||||
15,
|
||||
105,
|
||||
195,
|
||||
285
|
||||
)
|
||||
end
|
||||
local ____array_0 = __TS__SparseArrayNew(unpack(baseAngles))
|
||||
__TS__SparseArrayPush(
|
||||
____array_0,
|
||||
unpack(phaseAngles)
|
||||
)
|
||||
local angles = {__TS__SparseArraySpread(____array_0)}
|
||||
for ____, angle in ipairs(angles) do
|
||||
local q = QAngle(0, angle + rotationOffset, 0)
|
||||
dirs[#dirs + 1] = RotatePosition(
|
||||
Vector(0, 0, 0),
|
||||
q,
|
||||
baseDirection
|
||||
)
|
||||
end
|
||||
return dirs
|
||||
end
|
||||
function boss_nevermore_requiem_barrage.prototype.createWavePreview(self, directions, previewDuration, speeds)
|
||||
local caster = self:GetCaster()
|
||||
local phase = self:getBossPhase()
|
||||
local distance = (self:GetSpecialValueFor("wave_distance") > 0 and self:GetSpecialValueFor("wave_distance") or 850) + (phase - 1) * REQUIEM_PHASE_DISTANCE_BONUS
|
||||
local defaultSpeed = self:GetSpecialValueFor("wave_speed") > 0 and self:GetSpecialValueFor("wave_speed") or 800
|
||||
local casterPos = caster:GetAbsOrigin()
|
||||
local casterZ = casterPos.z
|
||||
local particles = {}
|
||||
do
|
||||
local i = 0
|
||||
while i < #directions do
|
||||
local direction = directions[i + 1]
|
||||
local spd = speeds ~= nil and speeds[i + 1] ~= nil and speeds[i + 1] or defaultSpeed
|
||||
local travelTime = distance / spd
|
||||
local startPos = casterPos + direction * 110
|
||||
startPos.z = casterZ
|
||||
local endPos = startPos + direction * distance
|
||||
endPos.z = casterZ
|
||||
local previewFx = ParticleManager:CreateParticle("particles/boss_tinker_laser_preview_vector.vpcf", PATTACH_WORLDORIGIN, nil)
|
||||
ParticleManager:SetParticleControl(previewFx, 0, startPos)
|
||||
ParticleManager:SetParticleControl(previewFx, 1, endPos)
|
||||
ParticleManager:SetParticleControl(
|
||||
previewFx,
|
||||
2,
|
||||
Vector(
|
||||
math.max(previewDuration, travelTime),
|
||||
0,
|
||||
0
|
||||
)
|
||||
)
|
||||
particles[#particles + 1] = previewFx
|
||||
local ____self_activePreviewParticles_1 = self.activePreviewParticles
|
||||
____self_activePreviewParticles_1[#____self_activePreviewParticles_1 + 1] = previewFx
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
return particles
|
||||
end
|
||||
function boss_nevermore_requiem_barrage.prototype.destroyPreviewParticles(self, particles)
|
||||
if #particles == 0 then
|
||||
return
|
||||
end
|
||||
local toRemove = {}
|
||||
for ____, p in ipairs(particles) do
|
||||
toRemove[p] = true
|
||||
end
|
||||
for ____, particle in ipairs(particles) do
|
||||
ParticleManager:DestroyParticle(particle, false)
|
||||
ParticleManager:ReleaseParticleIndex(particle)
|
||||
end
|
||||
local nextActive = {}
|
||||
for ____, p in ipairs(self.activePreviewParticles) do
|
||||
if not (toRemove[p] ~= nil) then
|
||||
nextActive[#nextActive + 1] = p
|
||||
end
|
||||
end
|
||||
self.activePreviewParticles = nextActive
|
||||
end
|
||||
function boss_nevermore_requiem_barrage.prototype.clearPhasePreview(self)
|
||||
self:destroyPreviewParticles(self.phasePreviewParticles)
|
||||
self.phasePreviewParticles = {}
|
||||
end
|
||||
function boss_nevermore_requiem_barrage.prototype.clearAllActivePreviews(self)
|
||||
self:destroyPreviewParticles(self.activePreviewParticles)
|
||||
self.activePreviewParticles = {}
|
||||
end
|
||||
function boss_nevermore_requiem_barrage.prototype.fireSoulWave(self, direction, distance, speed, startRadius, endRadius)
|
||||
local caster = self:GetCaster()
|
||||
local casterPos = caster:GetAbsOrigin()
|
||||
local startPos = casterPos + direction * 110
|
||||
startPos.z = casterPos.z
|
||||
local velocity = direction * speed
|
||||
velocity.z = 0
|
||||
ProjectileManager:CreateLinearProjectile({
|
||||
Ability = self,
|
||||
EffectName = "",
|
||||
vSpawnOrigin = startPos,
|
||||
fDistance = distance,
|
||||
fStartRadius = startRadius,
|
||||
fEndRadius = endRadius,
|
||||
Source = caster,
|
||||
bHasFrontalCone = false,
|
||||
iUnitTargetTeam = DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
iUnitTargetFlags = DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
iUnitTargetType = bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC),
|
||||
vVelocity = velocity,
|
||||
bProvidesVision = false
|
||||
})
|
||||
local travelTime = distance / speed
|
||||
local lineParticle = ParticleManager:CreateParticle("particles/units/heroes/hero_nevermore/nevermore_requiemofsouls_line.vpcf", PATTACH_WORLDORIGIN, nil)
|
||||
ParticleManager:SetParticleControl(lineParticle, 0, startPos)
|
||||
ParticleManager:SetParticleControl(lineParticle, 1, velocity)
|
||||
ParticleManager:SetParticleControl(
|
||||
lineParticle,
|
||||
2,
|
||||
Vector(0, travelTime, 0)
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(lineParticle)
|
||||
end
|
||||
function boss_nevermore_requiem_barrage.prototype.OnProjectileHit(self, target, _location)
|
||||
if not target then
|
||||
return false
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local phase = self:getBossPhase()
|
||||
local baseDamage = caster:GetAverageTrueAttackDamage(target)
|
||||
local damage = baseDamage * (1 + (phase - 1) * 0.28) * REQUIEM_BARRAGE_DAMAGE_MULT
|
||||
ApplyDamage({
|
||||
victim = target,
|
||||
attacker = caster,
|
||||
damage = damage,
|
||||
damage_type = DAMAGE_TYPE_MAGICAL,
|
||||
ability = self
|
||||
})
|
||||
local debuff = target:AddNewModifier(caster, self, modifier_boss_nevermore_requiem_magic_resist_debuff.name, {duration = -1})
|
||||
if debuff ~= nil then
|
||||
debuff:SetStackCount(debuff:GetStackCount() + 1)
|
||||
end
|
||||
return false
|
||||
end
|
||||
boss_nevermore_requiem_barrage = __TS__Decorate(
|
||||
boss_nevermore_requiem_barrage,
|
||||
boss_nevermore_requiem_barrage,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "boss_nevermore_requiem_barrage"}
|
||||
)
|
||||
____exports.boss_nevermore_requiem_barrage = boss_nevermore_requiem_barrage
|
||||
modifier_boss_nevermore_requiem_barrage_casting = __TS__Class()
|
||||
modifier_boss_nevermore_requiem_barrage_casting.name = "modifier_boss_nevermore_requiem_barrage_casting"
|
||||
modifier_boss_nevermore_requiem_barrage_casting.____file_path = "scripts/vscripts/abilities/creep/boss_nevermore_requiem_barrage.lua"
|
||||
__TS__ClassExtends(modifier_boss_nevermore_requiem_barrage_casting, BaseModifier)
|
||||
function modifier_boss_nevermore_requiem_barrage_casting.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_boss_nevermore_requiem_barrage_casting.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_boss_nevermore_requiem_barrage_casting.prototype.CheckState(self)
|
||||
return {
|
||||
[MODIFIER_STATE_STUNNED] = false,
|
||||
[MODIFIER_STATE_DISARMED] = true,
|
||||
[MODIFIER_STATE_SILENCED] = false,
|
||||
[MODIFIER_STATE_ROOTED] = true,
|
||||
[MODIFIER_STATE_COMMAND_RESTRICTED] = true
|
||||
}
|
||||
end
|
||||
modifier_boss_nevermore_requiem_barrage_casting = __TS__Decorate(
|
||||
modifier_boss_nevermore_requiem_barrage_casting,
|
||||
modifier_boss_nevermore_requiem_barrage_casting,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_boss_nevermore_requiem_barrage_casting"}
|
||||
)
|
||||
return ____exports
|
||||
@@ -0,0 +1,431 @@
|
||||
local ____lualib = require("lualib_bundle")
|
||||
local __TS__Class = ____lualib.__TS__Class
|
||||
local __TS__ClassExtends = ____lualib.__TS__ClassExtends
|
||||
local __TS__NumberToFixed = ____lualib.__TS__NumberToFixed
|
||||
local __TS__Decorate = ____lualib.__TS__Decorate
|
||||
local ____exports = {}
|
||||
local modifier_boss_nevermore_time_walk
|
||||
local ____dota_ts_adapter = require("lib.dota_ts_adapter")
|
||||
local BaseAbility = ____dota_ts_adapter.BaseAbility
|
||||
local BaseModifierMotionHorizontal = ____dota_ts_adapter.BaseModifierMotionHorizontal
|
||||
local registerAbility = ____dota_ts_adapter.registerAbility
|
||||
local registerModifier = ____dota_ts_adapter.registerModifier
|
||||
local DEBUG_TIME_WALK = false
|
||||
local twDebugNextAt = {}
|
||||
local function timeWalkDebug(self, tag, message, throttle)
|
||||
if throttle == nil then
|
||||
throttle = 0.35
|
||||
end
|
||||
if not DEBUG_TIME_WALK then
|
||||
return
|
||||
end
|
||||
local now = GameRules:GetGameTime()
|
||||
local nextAt = twDebugNextAt[tag] or 0
|
||||
if now < nextAt then
|
||||
return
|
||||
end
|
||||
twDebugNextAt[tag] = now + throttle
|
||||
print((("[NevermoreTimeWalk][" .. tag) .. "] ") .. message)
|
||||
end
|
||||
____exports.boss_nevermore_time_walk = __TS__Class()
|
||||
local boss_nevermore_time_walk = ____exports.boss_nevermore_time_walk
|
||||
boss_nevermore_time_walk.name = "boss_nevermore_time_walk"
|
||||
boss_nevermore_time_walk.____file_path = "scripts/vscripts/abilities/creep/boss_nevermore_time_walk.lua"
|
||||
__TS__ClassExtends(boss_nevermore_time_walk, BaseAbility)
|
||||
function boss_nevermore_time_walk.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_nevermore/nevermore_shadowraze.vpcf", context)
|
||||
PrecacheResource("particle", "particles/econ/events/darkmoon_2017/darkmoon_generic_aoe.vpcf", context)
|
||||
PrecacheResource("soundfile", "sounds/units/heroes/nevermore/shadowraze.vsnd", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_faceless_void/faceless_void_time_walk_slow.vpcf", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_faceless_void/faceless_void_time_walk_preimage.vpcf", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_faceless_void/faceless_void_time_walk.vpcf", context)
|
||||
end
|
||||
function boss_nevermore_time_walk.prototype.GetCastRange(self, _location, _target)
|
||||
return self:GetSpecialValueFor("range")
|
||||
end
|
||||
function boss_nevermore_time_walk.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
if caster:HasModifier(modifier_boss_nevermore_time_walk.name) then
|
||||
timeWalkDebug(nil, "cast_skip", "already moving, skip recast", 0.2)
|
||||
return
|
||||
end
|
||||
local range = self:GetSpecialValueFor("range")
|
||||
local speed = math.max(
|
||||
1,
|
||||
self:GetSpecialValueFor("speed")
|
||||
)
|
||||
local radius = self:GetSpecialValueFor("radius")
|
||||
local point = self:GetCursorPosition()
|
||||
local origin = caster:GetAbsOrigin()
|
||||
local direction = point - origin
|
||||
local distance = direction:Length2D()
|
||||
if distance < 1 then
|
||||
direction = caster:GetForwardVector()
|
||||
distance = range
|
||||
else
|
||||
direction = direction:Normalized()
|
||||
end
|
||||
local clampedDistance = math.min(distance, range)
|
||||
local targetPosition = GetGroundPosition(origin + direction * clampedDistance, nil)
|
||||
local duration = clampedDistance / speed
|
||||
timeWalkDebug(
|
||||
nil,
|
||||
"cast",
|
||||
(((("distance=" .. __TS__NumberToFixed(clampedDistance, 0)) .. " duration=") .. __TS__NumberToFixed(duration, 2)) .. " speed=") .. tostring(speed),
|
||||
0.1
|
||||
)
|
||||
EmitSoundOn("Hero_FacelessVoid.TimeWalk", caster)
|
||||
local startFx = ParticleManager:CreateParticle("particles/units/heroes/hero_faceless_void/faceless_void_time_walk_slow.vpcf", PATTACH_WORLDORIGIN, caster)
|
||||
ParticleManager:SetParticleControl(startFx, 0, origin)
|
||||
ParticleManager:SetParticleControl(
|
||||
startFx,
|
||||
1,
|
||||
Vector(radius, 0, 0)
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(startFx)
|
||||
local preimageFx = ParticleManager:CreateParticle("particles/units/heroes/hero_faceless_void/faceless_void_time_walk_preimage.vpcf", PATTACH_WORLDORIGIN, caster)
|
||||
ParticleManager:SetParticleControl(preimageFx, 0, origin)
|
||||
ParticleManager:SetParticleControl(preimageFx, 1, targetPosition)
|
||||
ParticleManager:SetParticleControl(preimageFx, 2, targetPosition)
|
||||
ParticleManager:ReleaseParticleIndex(preimageFx)
|
||||
caster:AddNewModifier(
|
||||
caster,
|
||||
self,
|
||||
modifier_boss_nevermore_time_walk.name,
|
||||
{
|
||||
duration = duration,
|
||||
target_x = targetPosition.x,
|
||||
target_y = targetPosition.y,
|
||||
target_z = targetPosition.z,
|
||||
speed = speed,
|
||||
radius = radius,
|
||||
damage = self:GetSpecialValueFor("damage")
|
||||
}
|
||||
)
|
||||
ProjectileManager:ProjectileDodge(caster)
|
||||
end
|
||||
function boss_nevermore_time_walk.prototype.OnProjectileHit(self, target, _location)
|
||||
if not IsServer() or not target then
|
||||
return false
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
if not caster or caster:IsNull() or not caster:IsAlive() then
|
||||
return false
|
||||
end
|
||||
local base = self:GetSpecialValueFor("damage")
|
||||
local damage = math.max(1, base * 0.35)
|
||||
ApplyDamage({
|
||||
victim = target,
|
||||
attacker = caster,
|
||||
damage = damage,
|
||||
damage_type = DAMAGE_TYPE_MAGICAL,
|
||||
ability = self
|
||||
})
|
||||
return false
|
||||
end
|
||||
boss_nevermore_time_walk = __TS__Decorate(
|
||||
boss_nevermore_time_walk,
|
||||
boss_nevermore_time_walk,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "boss_nevermore_time_walk"}
|
||||
)
|
||||
____exports.boss_nevermore_time_walk = boss_nevermore_time_walk
|
||||
modifier_boss_nevermore_time_walk = __TS__Class()
|
||||
modifier_boss_nevermore_time_walk.name = "modifier_boss_nevermore_time_walk"
|
||||
modifier_boss_nevermore_time_walk.____file_path = "scripts/vscripts/abilities/creep/boss_nevermore_time_walk.lua"
|
||||
__TS__ClassExtends(modifier_boss_nevermore_time_walk, BaseModifierMotionHorizontal)
|
||||
function modifier_boss_nevermore_time_walk.prototype.____constructor(self, ...)
|
||||
BaseModifierMotionHorizontal.prototype.____constructor(self, ...)
|
||||
self.direction = Vector(0, 0, 0)
|
||||
self.remainingDistance = 0
|
||||
self.speed = 0
|
||||
self.damage = 0
|
||||
self.radius = 0
|
||||
self.coilInterval = 0.25
|
||||
self.sideWaveInterval = 0.7
|
||||
self.nextSideWaveAt = 0
|
||||
self.sideWaveToggle = false
|
||||
self.maxTargetsPerTick = 8
|
||||
self.nextCoilSoundAllowedAt = 0
|
||||
self.coilPulseIndex = 0
|
||||
end
|
||||
function modifier_boss_nevermore_time_walk.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_boss_nevermore_time_walk.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_boss_nevermore_time_walk.prototype.OnCreated(self, params)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local target = Vector(
|
||||
params.target_x or parent:GetAbsOrigin().x,
|
||||
params.target_y or parent:GetAbsOrigin().y,
|
||||
params.target_z or parent:GetAbsOrigin().z
|
||||
)
|
||||
local toTarget = target - parent:GetAbsOrigin()
|
||||
self.remainingDistance = toTarget:Length2D()
|
||||
self.direction = self.remainingDistance < 1 and parent:GetForwardVector() or toTarget:Normalized()
|
||||
local ____math_max_3 = math.max
|
||||
local ____params_speed_2 = params.speed
|
||||
if ____params_speed_2 == nil then
|
||||
local ____opt_0 = self:GetAbility()
|
||||
____params_speed_2 = ____opt_0 and ____opt_0:GetSpecialValueFor("speed")
|
||||
end
|
||||
self.speed = ____math_max_3(1, ____params_speed_2 or 1)
|
||||
local ____math_max_7 = math.max
|
||||
local ____params_radius_6 = params.radius
|
||||
if ____params_radius_6 == nil then
|
||||
local ____opt_4 = self:GetAbility()
|
||||
____params_radius_6 = ____opt_4 and ____opt_4:GetSpecialValueFor("radius")
|
||||
end
|
||||
self.radius = ____math_max_7(1, ____params_radius_6 or 180)
|
||||
local ____params_damage_10 = params.damage
|
||||
if ____params_damage_10 == nil then
|
||||
local ____opt_8 = self:GetAbility()
|
||||
____params_damage_10 = ____opt_8 and ____opt_8:GetSpecialValueFor("damage")
|
||||
end
|
||||
self.damage = ____params_damage_10 or 0
|
||||
local ____math_max_13 = math.max
|
||||
local ____opt_11 = self:GetAbility()
|
||||
self.coilInterval = ____math_max_13(
|
||||
0.22,
|
||||
____opt_11 and ____opt_11:GetSpecialValueFor("coil_interval") or 0.25
|
||||
)
|
||||
local ____math_max_16 = math.max
|
||||
local ____opt_14 = self:GetAbility()
|
||||
self.sideWaveInterval = ____math_max_16(
|
||||
0.45,
|
||||
____opt_14 and ____opt_14:GetSpecialValueFor("side_wave_interval") or 0.7
|
||||
)
|
||||
local ____math_max_19 = math.max
|
||||
local ____opt_17 = self:GetAbility()
|
||||
self.maxTargetsPerTick = ____math_max_19(
|
||||
1,
|
||||
____opt_17 and ____opt_17:GetSpecialValueFor("max_targets_per_tick") or 8
|
||||
)
|
||||
local now = GameRules:GetGameTime()
|
||||
self.nextSideWaveAt = now + self.sideWaveInterval
|
||||
self.nextCoilSoundAllowedAt = now
|
||||
timeWalkDebug(
|
||||
nil,
|
||||
"start",
|
||||
(((((("dist=" .. __TS__NumberToFixed(self.remainingDistance, 0)) .. " speed=") .. tostring(self.speed)) .. " coilInt=") .. __TS__NumberToFixed(self.coilInterval, 2)) .. " sideInt=") .. __TS__NumberToFixed(self.sideWaveInterval, 2),
|
||||
0.1
|
||||
)
|
||||
if not self:ApplyHorizontalMotionController() then
|
||||
self:Destroy()
|
||||
return
|
||||
end
|
||||
self.cachedRequiemAbility = parent:FindAbilityByName("boss_nevermore_requiem_barrage") or nil
|
||||
self:StartIntervalThink(self.coilInterval)
|
||||
self:runCoilPulse(now)
|
||||
end
|
||||
function modifier_boss_nevermore_time_walk.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
if not parent or parent:IsNull() or not ability then
|
||||
return
|
||||
end
|
||||
local now = GameRules:GetGameTime()
|
||||
if now >= self.nextSideWaveAt then
|
||||
self.nextSideWaveAt = now + self.sideWaveInterval
|
||||
self.sideWaveToggle = not self.sideWaveToggle
|
||||
self:fireSideWaves(
|
||||
parent,
|
||||
ability,
|
||||
GetGroundPosition(
|
||||
parent:GetAbsOrigin(),
|
||||
nil
|
||||
),
|
||||
self.sideWaveToggle
|
||||
)
|
||||
timeWalkDebug(nil, "side_waves", "spawn side=" .. (self.sideWaveToggle and "L" or "R"), 0.25)
|
||||
end
|
||||
self:runCoilPulse(now)
|
||||
end
|
||||
function modifier_boss_nevermore_time_walk.prototype.runCoilPulse(self, now)
|
||||
local parent = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
if not parent or parent:IsNull() or not ability then
|
||||
return
|
||||
end
|
||||
local pulsePoint = GetGroundPosition(
|
||||
parent:GetAbsOrigin(),
|
||||
nil
|
||||
)
|
||||
local enemies = FindUnitsInRadius(
|
||||
parent:GetTeamNumber(),
|
||||
pulsePoint,
|
||||
nil,
|
||||
self.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 targetCount = math.min(#enemies, self.maxTargetsPerTick)
|
||||
do
|
||||
local i = 0
|
||||
while i < targetCount do
|
||||
local enemy = enemies[i + 1]
|
||||
ApplyDamage({
|
||||
victim = enemy,
|
||||
attacker = parent,
|
||||
damage = self.damage,
|
||||
damage_type = DAMAGE_TYPE_MAGICAL,
|
||||
ability = ability
|
||||
})
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
self.coilPulseIndex = self.coilPulseIndex + 1
|
||||
timeWalkDebug(
|
||||
nil,
|
||||
"coil_tick",
|
||||
(("enemies=" .. tostring(#enemies)) .. " applied=") .. tostring(targetCount),
|
||||
0.25
|
||||
)
|
||||
if now >= self.nextCoilSoundAllowedAt then
|
||||
self.nextCoilSoundAllowedAt = now + 0.45
|
||||
EmitSoundOnLocationWithCaster(pulsePoint, "Hero_Nevermore.Shadowraze", parent)
|
||||
end
|
||||
if self.coilPulseIndex % 2 == 0 then
|
||||
local hitParticle = ParticleManager:CreateParticle("particles/units/heroes/hero_nevermore/nevermore_shadowraze.vpcf", PATTACH_WORLDORIGIN, nil)
|
||||
ParticleManager:SetParticleControl(hitParticle, 0, pulsePoint)
|
||||
ParticleManager:SetParticleControl(
|
||||
hitParticle,
|
||||
1,
|
||||
Vector(self.radius, 1, 1)
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(hitParticle)
|
||||
else
|
||||
local aoeFx = ParticleManager:CreateParticle("particles/econ/events/darkmoon_2017/darkmoon_generic_aoe.vpcf", PATTACH_CUSTOMORIGIN, parent)
|
||||
ParticleManager:SetParticleControl(aoeFx, 0, pulsePoint)
|
||||
ParticleManager:SetParticleControl(
|
||||
aoeFx,
|
||||
1,
|
||||
Vector(self.radius, 0, 0)
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
aoeFx,
|
||||
2,
|
||||
Vector(5, 0, 1)
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
aoeFx,
|
||||
3,
|
||||
Vector(200, 50, 0)
|
||||
)
|
||||
ParticleManager:SetParticleControl(aoeFx, 4, pulsePoint)
|
||||
ParticleManager:ReleaseParticleIndex(aoeFx)
|
||||
end
|
||||
end
|
||||
function modifier_boss_nevermore_time_walk.prototype.fireSideWaves(self, caster, sourceAbility, origin, fireLeft)
|
||||
local requiem = self.cachedRequiemAbility
|
||||
if not requiem or requiem:IsNull() then
|
||||
requiem = caster:FindAbilityByName("boss_nevermore_requiem_barrage") or nil
|
||||
self.cachedRequiemAbility = requiem
|
||||
end
|
||||
local distance = requiem and not requiem:IsNull() and math.max(
|
||||
200,
|
||||
requiem:GetSpecialValueFor("wave_distance") or 900
|
||||
) or 900
|
||||
local waveSpeed = requiem and not requiem:IsNull() and math.max(
|
||||
200,
|
||||
requiem:GetSpecialValueFor("wave_speed") or 700
|
||||
) or 700
|
||||
local startRadius = requiem and not requiem:IsNull() and math.max(
|
||||
10,
|
||||
requiem:GetSpecialValueFor("wave_width_start") or 45
|
||||
) or 45
|
||||
local endRadius = requiem and not requiem:IsNull() and math.max(
|
||||
10,
|
||||
requiem:GetSpecialValueFor("wave_width_end") or 45
|
||||
) or 45
|
||||
local dir = fireLeft and Vector(-self.direction.y, self.direction.x, 0):Normalized() or Vector(self.direction.y, -self.direction.x, 0):Normalized()
|
||||
local startPos = origin + dir * 80
|
||||
startPos.z = origin.z
|
||||
local velocity = dir * waveSpeed
|
||||
velocity.z = 0
|
||||
ProjectileManager:CreateLinearProjectile({
|
||||
Ability = sourceAbility,
|
||||
EffectName = "",
|
||||
vSpawnOrigin = startPos,
|
||||
fDistance = distance,
|
||||
fStartRadius = startRadius,
|
||||
fEndRadius = endRadius,
|
||||
Source = caster,
|
||||
bHasFrontalCone = false,
|
||||
iUnitTargetTeam = DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
iUnitTargetFlags = DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
iUnitTargetType = bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC),
|
||||
vVelocity = velocity,
|
||||
bProvidesVision = false
|
||||
})
|
||||
end
|
||||
function modifier_boss_nevermore_time_walk.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
parent:RemoveHorizontalMotionController(self)
|
||||
FindClearSpaceForUnit(
|
||||
parent,
|
||||
parent:GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
parent:StartGesture(ACT_DOTA_CAST_ABILITY_1_END)
|
||||
timeWalkDebug(nil, "end", "modifier destroyed", 0.1)
|
||||
end
|
||||
function modifier_boss_nevermore_time_walk.prototype.UpdateHorizontalMotion(self, me, dt)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local step = self.speed * dt
|
||||
if self.remainingDistance > 0 then
|
||||
local oldPos = me:GetAbsOrigin()
|
||||
local nextPos = GetGroundPosition(
|
||||
oldPos + self.direction * math.min(step, self.remainingDistance),
|
||||
nil
|
||||
)
|
||||
me:SetAbsOrigin(nextPos)
|
||||
self.remainingDistance = self.remainingDistance - step
|
||||
return
|
||||
end
|
||||
self:Destroy()
|
||||
end
|
||||
function modifier_boss_nevermore_time_walk.prototype.OnHorizontalMotionInterrupted(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self:Destroy()
|
||||
end
|
||||
function modifier_boss_nevermore_time_walk.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_COMMAND_RESTRICTED] = true, [MODIFIER_STATE_NO_UNIT_COLLISION] = true, [MODIFIER_STATE_FLYING_FOR_PATHING_PURPOSES_ONLY] = true}
|
||||
end
|
||||
function modifier_boss_nevermore_time_walk.prototype.GetEffectName(self)
|
||||
return "particles/units/heroes/hero_faceless_void/faceless_void_time_walk.vpcf"
|
||||
end
|
||||
function modifier_boss_nevermore_time_walk.prototype.GetStatusEffectName(self)
|
||||
return "particles/status_fx/status_effect_abaddon_borrowed_time.vpcf"
|
||||
end
|
||||
function modifier_boss_nevermore_time_walk.prototype.StatusEffectPriority(self)
|
||||
return 10
|
||||
end
|
||||
modifier_boss_nevermore_time_walk = __TS__Decorate(
|
||||
modifier_boss_nevermore_time_walk,
|
||||
modifier_boss_nevermore_time_walk,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_boss_nevermore_time_walk"}
|
||||
)
|
||||
return ____exports
|
||||
@@ -0,0 +1,382 @@
|
||||
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 modifier_boss_nevermore_triple_coil_aoe_lock
|
||||
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 ____modifier_boss_nevermore_coil_debuff = require("abilities.creep.modifier_boss_nevermore_coil_debuff")
|
||||
local modifier_boss_nevermore_coil_debuff = ____modifier_boss_nevermore_coil_debuff.modifier_boss_nevermore_coil_debuff
|
||||
____exports.boss_nevermore_triple_coil_aoe = __TS__Class()
|
||||
local boss_nevermore_triple_coil_aoe = ____exports.boss_nevermore_triple_coil_aoe
|
||||
boss_nevermore_triple_coil_aoe.name = "boss_nevermore_triple_coil_aoe"
|
||||
boss_nevermore_triple_coil_aoe.____file_path = "scripts/vscripts/abilities/creep/boss_nevermore_triple_coil_aoe.lua"
|
||||
__TS__ClassExtends(boss_nevermore_triple_coil_aoe, BaseAbility)
|
||||
function boss_nevermore_triple_coil_aoe.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_nevermore/nevermore_shadowraze.vpcf", context)
|
||||
PrecacheResource("particle", "particles/darkmoon_creep_warning.vpcf", context)
|
||||
PrecacheResource("particle", "particles/econ/events/darkmoon_2017/darkmoon_generic_aoe.vpcf", context)
|
||||
PrecacheResource("soundfile", "sounds/units/heroes/nevermore/shadowraze.vsnd", context)
|
||||
end
|
||||
function boss_nevermore_triple_coil_aoe.prototype.GetAOERadius(self)
|
||||
return self:GetSpecialValueFor("radius")
|
||||
end
|
||||
function boss_nevermore_triple_coil_aoe.prototype.getSpecialOrDefault(self, name, fallback)
|
||||
local value = self:GetSpecialValueFor(name)
|
||||
if not value or value <= 0 then
|
||||
return fallback
|
||||
end
|
||||
return value
|
||||
end
|
||||
function boss_nevermore_triple_coil_aoe.prototype.getBossPhase(self)
|
||||
local caster = self:GetCaster()
|
||||
if not caster or caster:IsNull() then
|
||||
return 1
|
||||
end
|
||||
local hp = caster:GetHealthPercent()
|
||||
if hp <= 25 then
|
||||
return 4
|
||||
end
|
||||
if hp <= 50 then
|
||||
return 3
|
||||
end
|
||||
if hp <= 75 then
|
||||
return 2
|
||||
end
|
||||
return 1
|
||||
end
|
||||
function boss_nevermore_triple_coil_aoe.prototype.getConfiguredDelay(self)
|
||||
local delay = self:GetSpecialValueFor("delay")
|
||||
local startDelay = self:GetSpecialValueFor("start_delay")
|
||||
local best = math.max(delay or 0, startDelay or 0)
|
||||
if best > 0 then
|
||||
return best
|
||||
end
|
||||
return ____exports.boss_nevermore_triple_coil_aoe.DEFAULT_DELAY
|
||||
end
|
||||
function boss_nevermore_triple_coil_aoe.prototype.buildCoilPoints(self, centerPoint, forward, sideDir)
|
||||
local sideOffset = self:getSpecialOrDefault("side_offset", ____exports.boss_nevermore_triple_coil_aoe.DEFAULT_SIDE_OFFSET)
|
||||
local forwardOffset = self:getSpecialOrDefault("forward_offset", ____exports.boss_nevermore_triple_coil_aoe.DEFAULT_FORWARD_OFFSET)
|
||||
local leftPoint = GetGroundPosition(centerPoint + sideDir * sideOffset, nil)
|
||||
local rightPoint = GetGroundPosition(centerPoint - sideDir * sideOffset, nil)
|
||||
local frontPoint = GetGroundPosition(centerPoint + forward * forwardOffset, nil)
|
||||
local backPoint = GetGroundPosition(centerPoint - forward * forwardOffset, nil)
|
||||
return {
|
||||
centerPoint,
|
||||
leftPoint,
|
||||
rightPoint,
|
||||
frontPoint,
|
||||
backPoint
|
||||
}
|
||||
end
|
||||
function boss_nevermore_triple_coil_aoe.prototype.getCoilRadiusForPhase(self, phase)
|
||||
return self:getSpecialOrDefault("coil_radius", ____exports.boss_nevermore_triple_coil_aoe.DEFAULT_COIL_RADIUS) + (phase - 1) * 15
|
||||
end
|
||||
function boss_nevermore_triple_coil_aoe.prototype.randomPointNearHero(self, heroOrigin, minDist, maxDist)
|
||||
local angleDeg = RandomFloat(0, 360)
|
||||
local dist = RandomFloat(minDist, maxDist)
|
||||
local rad = angleDeg * math.pi / 180
|
||||
local ox = math.cos(rad) * dist
|
||||
local oy = math.sin(rad) * dist
|
||||
return GetGroundPosition(
|
||||
heroOrigin + Vector(ox, oy, 0),
|
||||
nil
|
||||
)
|
||||
end
|
||||
function boss_nevermore_triple_coil_aoe.prototype.spawnAoeAtPoint(self, centerPoint, phase, lockDuration, delayOverride)
|
||||
local caster = self:GetCaster()
|
||||
local castOrigin = caster:GetAbsOrigin()
|
||||
local toCenter = centerPoint - castOrigin
|
||||
local forward = toCenter:Length2D() < 1 and caster:GetForwardVector() or toCenter:Normalized()
|
||||
local sideDir = Vector(-forward.y, forward.x, 0):Normalized()
|
||||
local delay = delayOverride ~= nil and delayOverride or self:getConfiguredDelay()
|
||||
local warningTime = self:getSpecialOrDefault("precast_warning_time", delay)
|
||||
local coilRadius = self:getCoilRadiusForPhase(phase)
|
||||
local coilPoints = self:buildCoilPoints(centerPoint, forward, sideDir)
|
||||
caster:AddNewModifier(caster, self, modifier_boss_nevermore_triple_coil_aoe_lock.name, {duration = lockDuration})
|
||||
local warningParticles = {}
|
||||
for ____, coilPoint in ipairs(coilPoints) do
|
||||
warningParticles[#warningParticles + 1] = self:createWarning(coilPoint, coilRadius, warningTime)
|
||||
end
|
||||
Timers:CreateTimer(
|
||||
delay,
|
||||
function()
|
||||
for ____, ____value in ipairs(warningParticles) do
|
||||
local warningParticle = ____value[1]
|
||||
local aoeParticle = ____value[2]
|
||||
ParticleManager:DestroyParticle(warningParticle, true)
|
||||
ParticleManager:ReleaseParticleIndex(warningParticle)
|
||||
ParticleManager:DestroyParticle(aoeParticle, true)
|
||||
ParticleManager:ReleaseParticleIndex(aoeParticle)
|
||||
end
|
||||
local impactPhase = self:getBossPhase()
|
||||
local baseDamage = caster:GetAttackDamage()
|
||||
local bonusDamagePerStack = self:GetSpecialValueFor("coil_stack_bonus_damage")
|
||||
local damageMultiplier = 1 + (impactPhase - 1) * 0.3
|
||||
EmitSoundOn("Hero_Nevermore.Shadowraze", caster)
|
||||
for ____, coilPoint in ipairs(coilPoints) do
|
||||
self:applyMiniCoil(
|
||||
coilPoint,
|
||||
coilRadius,
|
||||
baseDamage,
|
||||
bonusDamagePerStack,
|
||||
damageMultiplier
|
||||
)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
)
|
||||
end
|
||||
function boss_nevermore_triple_coil_aoe.prototype.scheduleHeroProximityCoils(self, phase, mainHitDelay, lockDuration)
|
||||
if not IsServer() or phase < 1 then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
if not caster or caster:IsNull() then
|
||||
return
|
||||
end
|
||||
local minD = self:getSpecialOrDefault("hero_coil_radius_min", ____exports.boss_nevermore_triple_coil_aoe.DEFAULT_HERO_RING_MIN)
|
||||
local maxD = self:getSpecialOrDefault("hero_coil_radius_max", ____exports.boss_nevermore_triple_coil_aoe.DEFAULT_HERO_RING_MAX)
|
||||
local stagger = self:getSpecialOrDefault("hero_coil_stagger", ____exports.boss_nevermore_triple_coil_aoe.DEFAULT_HERO_COIL_STAGGER)
|
||||
local afterMain = self:getSpecialOrDefault("hero_coil_start_after_main", ____exports.boss_nevermore_triple_coil_aoe.DEFAULT_HERO_COIL_AFTER_MAIN)
|
||||
local warningTime = self:getSpecialOrDefault("precast_warning_time", mainHitDelay)
|
||||
local coilRadius = self:getCoilRadiusForPhase(phase)
|
||||
local heroes = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
caster:GetAbsOrigin(),
|
||||
nil,
|
||||
____exports.boss_nevermore_triple_coil_aoe.HERO_SEARCH_RADIUS,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
DOTA_UNIT_TARGET_HERO,
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
local slot = 0
|
||||
for ____, hero in ipairs(heroes) do
|
||||
do
|
||||
if not hero or hero:IsNull() or not hero:IsAlive() then
|
||||
goto __continue27
|
||||
end
|
||||
local heroGround = GetGroundPosition(
|
||||
hero:GetAbsOrigin(),
|
||||
nil
|
||||
)
|
||||
do
|
||||
local j = 0
|
||||
while j < phase do
|
||||
local coilPos = self:randomPointNearHero(heroGround, minD, maxD)
|
||||
local hitTime = mainHitDelay + afterMain + slot * stagger
|
||||
slot = slot + 1
|
||||
self:spawnDelayedSingleCoil(
|
||||
coilPos,
|
||||
hitTime,
|
||||
warningTime,
|
||||
coilRadius,
|
||||
lockDuration
|
||||
)
|
||||
j = j + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
::__continue27::
|
||||
end
|
||||
end
|
||||
function boss_nevermore_triple_coil_aoe.prototype.spawnDelayedSingleCoil(self, coilPoint, hitTime, warningTime, coilRadius, _lockDuration)
|
||||
local caster = self:GetCaster()
|
||||
local warnAt = math.max(0, hitTime - warningTime)
|
||||
local state = {pair = nil}
|
||||
Timers:CreateTimer(
|
||||
warnAt,
|
||||
function()
|
||||
if not caster or caster:IsNull() or not caster:IsAlive() then
|
||||
return nil
|
||||
end
|
||||
state.pair = self:createWarning(coilPoint, coilRadius, warningTime)
|
||||
return nil
|
||||
end
|
||||
)
|
||||
Timers:CreateTimer(
|
||||
hitTime,
|
||||
function()
|
||||
if state.pair then
|
||||
ParticleManager:DestroyParticle(state.pair[1], true)
|
||||
ParticleManager:ReleaseParticleIndex(state.pair[1])
|
||||
ParticleManager:DestroyParticle(state.pair[2], true)
|
||||
ParticleManager:ReleaseParticleIndex(state.pair[2])
|
||||
state.pair = nil
|
||||
end
|
||||
if not caster or caster:IsNull() or not caster:IsAlive() then
|
||||
return nil
|
||||
end
|
||||
local impactPhase = self:getBossPhase()
|
||||
local baseDamage = caster:GetAttackDamage()
|
||||
local bonusDamagePerStack = self:GetSpecialValueFor("coil_stack_bonus_damage")
|
||||
local damageMultiplier = 1 + (impactPhase - 1) * 0.3
|
||||
self:applyMiniCoil(
|
||||
coilPoint,
|
||||
coilRadius,
|
||||
baseDamage,
|
||||
bonusDamagePerStack,
|
||||
damageMultiplier
|
||||
)
|
||||
return nil
|
||||
end
|
||||
)
|
||||
end
|
||||
function boss_nevermore_triple_coil_aoe.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local phase = self:getBossPhase()
|
||||
local centerPoint = GetGroundPosition(
|
||||
self:GetCursorPosition(),
|
||||
nil
|
||||
)
|
||||
local mainDelay = self:getConfiguredDelay()
|
||||
local stagger = self:getSpecialOrDefault("hero_coil_stagger", ____exports.boss_nevermore_triple_coil_aoe.DEFAULT_HERO_COIL_STAGGER)
|
||||
local afterMain = self:getSpecialOrDefault("hero_coil_start_after_main", ____exports.boss_nevermore_triple_coil_aoe.DEFAULT_HERO_COIL_AFTER_MAIN)
|
||||
local caster = self:GetCaster()
|
||||
local heroes = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
caster:GetAbsOrigin(),
|
||||
nil,
|
||||
____exports.boss_nevermore_triple_coil_aoe.HERO_SEARCH_RADIUS,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
DOTA_UNIT_TARGET_HERO,
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
local heroCount = 0
|
||||
for ____, h in ipairs(heroes) do
|
||||
if h and not h:IsNull() and h:IsAlive() then
|
||||
heroCount = heroCount + 1
|
||||
end
|
||||
end
|
||||
local extraCoils = heroCount * phase
|
||||
local lastHeroCoilTime = extraCoils > 0 and mainDelay + afterMain + (extraCoils - 1) * stagger or mainDelay
|
||||
local lockDuration = lastHeroCoilTime + 0.55
|
||||
self:spawnAoeAtPoint(centerPoint, phase, lockDuration)
|
||||
self:scheduleHeroProximityCoils(phase, mainDelay, lockDuration)
|
||||
end
|
||||
function boss_nevermore_triple_coil_aoe.prototype.createWarning(self, point, radius, _duration)
|
||||
if not IsServer() then
|
||||
return {-1, -1}
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local warningParticle = ParticleManager:CreateParticle("particles/darkmoon_creep_warning.vpcf", PATTACH_CUSTOMORIGIN, caster)
|
||||
local aoeParticle = ParticleManager:CreateParticle("particles/econ/events/darkmoon_2017/darkmoon_generic_aoe.vpcf", PATTACH_CUSTOMORIGIN, caster)
|
||||
ParticleManager:SetParticleControl(warningParticle, 0, point)
|
||||
ParticleManager:SetParticleControl(
|
||||
warningParticle,
|
||||
1,
|
||||
Vector(radius, 100, 100)
|
||||
)
|
||||
ParticleManager:SetParticleControl(aoeParticle, 0, point)
|
||||
ParticleManager:SetParticleControl(
|
||||
aoeParticle,
|
||||
1,
|
||||
Vector(radius, 0, 0)
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
aoeParticle,
|
||||
2,
|
||||
Vector(6, 0, 1)
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
aoeParticle,
|
||||
3,
|
||||
Vector(220, 0, 0)
|
||||
)
|
||||
ParticleManager:SetParticleControl(aoeParticle, 4, point)
|
||||
return {warningParticle, aoeParticle}
|
||||
end
|
||||
function boss_nevermore_triple_coil_aoe.prototype.applyMiniCoil(self, point, radius, baseDamage, bonusDamagePerStack, damageMultiplier)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
if not caster or caster:IsNull() or not caster:IsAlive() then
|
||||
return
|
||||
end
|
||||
local enemies = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
point,
|
||||
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
|
||||
)
|
||||
for ____, enemy in ipairs(enemies) do
|
||||
local coilDebuff = enemy:FindModifierByName(modifier_boss_nevermore_coil_debuff.name)
|
||||
local stacks = coilDebuff and coilDebuff:GetStackCount() or 0
|
||||
local damage = (baseDamage + stacks * bonusDamagePerStack) * damageMultiplier
|
||||
ApplyDamage({
|
||||
victim = enemy,
|
||||
attacker = caster,
|
||||
damage = damage,
|
||||
damage_type = DAMAGE_TYPE_MAGICAL,
|
||||
ability = self
|
||||
})
|
||||
local updatedDebuff = enemy:AddNewModifier(caster, self, modifier_boss_nevermore_coil_debuff.name, {})
|
||||
if updatedDebuff ~= nil then
|
||||
if stacks > 0 then
|
||||
updatedDebuff:SetStackCount(stacks + 1)
|
||||
else
|
||||
updatedDebuff:SetStackCount(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
local hitParticle = ParticleManager:CreateParticle("particles/units/heroes/hero_nevermore/nevermore_shadowraze.vpcf", PATTACH_WORLDORIGIN, nil)
|
||||
ParticleManager:SetParticleControl(hitParticle, 0, point)
|
||||
ParticleManager:SetParticleControl(
|
||||
hitParticle,
|
||||
1,
|
||||
Vector(radius, 1, 1)
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(hitParticle)
|
||||
end
|
||||
boss_nevermore_triple_coil_aoe.DEFAULT_DELAY = 2
|
||||
boss_nevermore_triple_coil_aoe.DEFAULT_SIDE_OFFSET = 170
|
||||
boss_nevermore_triple_coil_aoe.DEFAULT_FORWARD_OFFSET = 170
|
||||
boss_nevermore_triple_coil_aoe.DEFAULT_COIL_RADIUS = 120
|
||||
boss_nevermore_triple_coil_aoe.DEFAULT_HERO_RING_MIN = 100
|
||||
boss_nevermore_triple_coil_aoe.DEFAULT_HERO_RING_MAX = 200
|
||||
boss_nevermore_triple_coil_aoe.DEFAULT_HERO_COIL_STAGGER = 0.32
|
||||
boss_nevermore_triple_coil_aoe.DEFAULT_HERO_COIL_AFTER_MAIN = 0.45
|
||||
boss_nevermore_triple_coil_aoe.HERO_SEARCH_RADIUS = 3200
|
||||
boss_nevermore_triple_coil_aoe = __TS__Decorate(
|
||||
boss_nevermore_triple_coil_aoe,
|
||||
boss_nevermore_triple_coil_aoe,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "boss_nevermore_triple_coil_aoe"}
|
||||
)
|
||||
____exports.boss_nevermore_triple_coil_aoe = boss_nevermore_triple_coil_aoe
|
||||
modifier_boss_nevermore_triple_coil_aoe_lock = __TS__Class()
|
||||
modifier_boss_nevermore_triple_coil_aoe_lock.name = "modifier_boss_nevermore_triple_coil_aoe_lock"
|
||||
modifier_boss_nevermore_triple_coil_aoe_lock.____file_path = "scripts/vscripts/abilities/creep/boss_nevermore_triple_coil_aoe.lua"
|
||||
__TS__ClassExtends(modifier_boss_nevermore_triple_coil_aoe_lock, BaseModifier)
|
||||
function modifier_boss_nevermore_triple_coil_aoe_lock.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_boss_nevermore_triple_coil_aoe_lock.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_boss_nevermore_triple_coil_aoe_lock.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_DISARMED] = true}
|
||||
end
|
||||
modifier_boss_nevermore_triple_coil_aoe_lock = __TS__Decorate(
|
||||
modifier_boss_nevermore_triple_coil_aoe_lock,
|
||||
modifier_boss_nevermore_triple_coil_aoe_lock,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_boss_nevermore_triple_coil_aoe_lock"}
|
||||
)
|
||||
return ____exports
|
||||
@@ -0,0 +1,468 @@
|
||||
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 ____difficulty_manager = require("difficulty_manager")
|
||||
local Difficulty = ____difficulty_manager.Difficulty
|
||||
local ____dota_ts_adapter = require("lib.dota_ts_adapter")
|
||||
local BaseAbility = ____dota_ts_adapter.BaseAbility
|
||||
local BaseModifier = ____dota_ts_adapter.BaseModifier
|
||||
local BaseModifierMotionBoth = ____dota_ts_adapter.BaseModifierMotionBoth
|
||||
local registerAbility = ____dota_ts_adapter.registerAbility
|
||||
local registerModifier = ____dota_ts_adapter.registerModifier
|
||||
--- Та же физика дуги, что у `frogmen_acid_jump` / `modifier_acid_blob_jump`.
|
||||
local CONTRACT_HEAD_LEAP_MIN_HEIGHT_ABOVE_LOWEST = 400
|
||||
local CONTRACT_HEAD_LEAP_MIN_HEIGHT_ABOVE_HIGHEST = 200
|
||||
local CONTRACT_HEAD_LEAP_ACCELERATION_Z = 1500
|
||||
local CONTRACT_HEAD_LEAP_MAX_HORIZONTAL_ACCELERATION = 1500
|
||||
--- Пассивка контракта: прыжок дугой к точке, где стоял герой; урон только если к моменту приземления он не ушёл из зоны.
|
||||
____exports.contract_head_leap = __TS__Class()
|
||||
local contract_head_leap = ____exports.contract_head_leap
|
||||
contract_head_leap.name = "contract_head_leap"
|
||||
contract_head_leap.____file_path = "scripts/vscripts/abilities/creep/contract_head_leap.lua"
|
||||
__TS__ClassExtends(contract_head_leap, BaseAbility)
|
||||
function contract_head_leap.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/generic_gameplay/generic_hit_blood.vpcf", context)
|
||||
PrecacheResource("particle", "particles/econ/events/darkmoon_2017/darkmoon_generic_aoe.vpcf", context)
|
||||
PrecacheResource("particle", "particles/econ/events/darkmoon_2017/darkmoon_calldown_marker_arrows.vpcf", context)
|
||||
end
|
||||
function contract_head_leap.prototype.GetIntrinsicModifierName(self)
|
||||
return "modifier_contract_head_leap_passive"
|
||||
end
|
||||
function contract_head_leap.prototype.showLeapPrecastVisuals(self, caster, strikeGround, damageRadius, warningDuration)
|
||||
if not IsServer() or warningDuration <= 0 then
|
||||
return
|
||||
end
|
||||
local groundPos = GetGroundPosition(strikeGround, nil)
|
||||
local r = math.max(1, damageRadius)
|
||||
local particleAoe = ParticleManager:CreateParticle("particles/econ/events/darkmoon_2017/darkmoon_generic_aoe.vpcf", PATTACH_CUSTOMORIGIN, nil)
|
||||
if particleAoe ~= nil then
|
||||
ParticleManager:SetParticleControl(particleAoe, 0, groundPos)
|
||||
ParticleManager:SetParticleControl(
|
||||
particleAoe,
|
||||
1,
|
||||
Vector(r, 0, 0)
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
particleAoe,
|
||||
2,
|
||||
Vector(6, 0, 1)
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
particleAoe,
|
||||
3,
|
||||
Vector(200, 0, 0)
|
||||
)
|
||||
ParticleManager:SetParticleControl(particleAoe, 4, groundPos)
|
||||
Timers:CreateTimer(
|
||||
warningDuration,
|
||||
function()
|
||||
ParticleManager:DestroyParticle(particleAoe, false)
|
||||
ParticleManager:ReleaseParticleIndex(particleAoe)
|
||||
end
|
||||
)
|
||||
end
|
||||
local casterPos = GetGroundPosition(
|
||||
caster:GetAbsOrigin(),
|
||||
nil
|
||||
)
|
||||
local particleArrow = ParticleManager:CreateParticle("particles/econ/events/darkmoon_2017/darkmoon_calldown_marker_arrows.vpcf", PATTACH_CUSTOMORIGIN, nil)
|
||||
if particleArrow ~= nil then
|
||||
ParticleManager:SetParticleControl(particleArrow, 0, casterPos)
|
||||
ParticleManager:SetParticleControl(particleArrow, 1, groundPos)
|
||||
ParticleManager:SetParticleControl(
|
||||
particleArrow,
|
||||
2,
|
||||
Vector(warningDuration, 0, 0)
|
||||
)
|
||||
Timers:CreateTimer(
|
||||
warningDuration,
|
||||
function()
|
||||
ParticleManager:DestroyParticle(particleArrow, false)
|
||||
ParticleManager:ReleaseParticleIndex(particleArrow)
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
function contract_head_leap.prototype.performLeapLanding(self, heroEntIndex, strikeGround)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local creep = self:GetCaster()
|
||||
if not creep or not IsValidEntity(creep) or not creep:IsAlive() then
|
||||
return
|
||||
end
|
||||
local hero = EntIndexToHScript(heroEntIndex)
|
||||
local hitRadius = math.max(
|
||||
0,
|
||||
self:GetSpecialValueFor("damage_radius")
|
||||
)
|
||||
local hpos = hero and IsValidEntity(hero) and hero:IsAlive() and hero:GetAbsOrigin() or nil
|
||||
local inZone = false
|
||||
if hpos ~= nil then
|
||||
local dx = hpos.x - strikeGround.x
|
||||
local dy = hpos.y - strikeGround.y
|
||||
inZone = math.sqrt(dx * dx + dy * dy) <= hitRadius
|
||||
end
|
||||
if hero and IsValidEntity(hero) and hero:IsAlive() and not hero:IsInvulnerable() and inZone then
|
||||
local scale = Difficulty:getNpcStatScale()
|
||||
local flatDamage = math.max(
|
||||
1,
|
||||
math.floor(self:GetSpecialValueFor("leap_damage") * scale + 0.5)
|
||||
)
|
||||
local pctFromAttack = math.max(
|
||||
0,
|
||||
self:GetSpecialValueFor("damage_from_attack_pct")
|
||||
)
|
||||
local avgAtk = creep:GetAverageTrueAttackDamage(hero)
|
||||
local fromAttack = math.floor(avgAtk * (pctFromAttack / 100) * scale + 0.5)
|
||||
local totalDamage = flatDamage + fromAttack
|
||||
ApplyDamage({
|
||||
victim = hero,
|
||||
attacker = creep,
|
||||
damage = totalDamage,
|
||||
damage_type = DAMAGE_TYPE_PHYSICAL,
|
||||
ability = self
|
||||
})
|
||||
local hitFx = ParticleManager:CreateParticle("particles/generic_gameplay/generic_hit_blood.vpcf", PATTACH_CUSTOMORIGIN, nil)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
hitFx,
|
||||
0,
|
||||
hero,
|
||||
PATTACH_POINT_FOLLOW,
|
||||
"attach_hitloc",
|
||||
hero:GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(hitFx)
|
||||
EmitSoundOn("Hero_MonkeyKing.Spring.Channel", creep)
|
||||
end
|
||||
end
|
||||
contract_head_leap = __TS__Decorate(
|
||||
contract_head_leap,
|
||||
contract_head_leap,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "contract_head_leap"}
|
||||
)
|
||||
____exports.contract_head_leap = contract_head_leap
|
||||
____exports.modifier_contract_head_leap_passive = __TS__Class()
|
||||
local modifier_contract_head_leap_passive = ____exports.modifier_contract_head_leap_passive
|
||||
modifier_contract_head_leap_passive.name = "modifier_contract_head_leap_passive"
|
||||
modifier_contract_head_leap_passive.____file_path = "scripts/vscripts/abilities/creep/contract_head_leap.lua"
|
||||
__TS__ClassExtends(modifier_contract_head_leap_passive, BaseModifier)
|
||||
function modifier_contract_head_leap_passive.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.precastLockUntil = -1
|
||||
end
|
||||
function modifier_contract_head_leap_passive.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_contract_head_leap_passive.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_contract_head_leap_passive.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_contract_head_leap_passive.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
local interval = ability and math.max(
|
||||
0.35,
|
||||
ability:GetSpecialValueFor("leap_interval")
|
||||
) or 2.4
|
||||
self:StartIntervalThink(interval)
|
||||
end
|
||||
function modifier_contract_head_leap_passive.prototype.OnRefresh(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
local interval = ability and math.max(
|
||||
0.35,
|
||||
ability:GetSpecialValueFor("leap_interval")
|
||||
) or 2.4
|
||||
self:StartIntervalThink(interval)
|
||||
end
|
||||
function modifier_contract_head_leap_passive.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
if not ability or not IsValidEntity(parent) or not parent:IsAlive() or parent:IsStunned() or parent:IsHexed() then
|
||||
return
|
||||
end
|
||||
local now = GameRules:GetDOTATime(false, false)
|
||||
if now < self.precastLockUntil then
|
||||
return
|
||||
end
|
||||
local primary = parent:FindAbilityByName("contract_head_leap")
|
||||
if not primary or primary ~= ability then
|
||||
return
|
||||
end
|
||||
if parent:HasModifier("modifier_contract_head_leap_arc") then
|
||||
return
|
||||
end
|
||||
local radius = math.max(
|
||||
50,
|
||||
ability:GetSpecialValueFor("search_radius")
|
||||
)
|
||||
local heroes = FindUnitsInRadius(
|
||||
parent:GetTeamNumber(),
|
||||
parent:GetAbsOrigin(),
|
||||
nil,
|
||||
radius,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
DOTA_UNIT_TARGET_HERO,
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_CLOSEST,
|
||||
false
|
||||
)
|
||||
local hero
|
||||
for ____, u in ipairs(heroes) do
|
||||
do
|
||||
if not u or not IsValidEntity(u) or not u:IsAlive() or not u:IsHero() then
|
||||
goto __continue28
|
||||
end
|
||||
if not u:IsRealHero() then
|
||||
goto __continue28
|
||||
end
|
||||
if u:IsInvulnerable() then
|
||||
goto __continue28
|
||||
end
|
||||
if u:GetUnitName() == "npc_homer" then
|
||||
goto __continue28
|
||||
end
|
||||
hero = u
|
||||
break
|
||||
end
|
||||
::__continue28::
|
||||
end
|
||||
if not hero then
|
||||
return
|
||||
end
|
||||
local height = math.max(
|
||||
40,
|
||||
ability:GetSpecialValueFor("height_above_hero")
|
||||
)
|
||||
local strikeGround = GetGroundPosition(
|
||||
hero:GetAbsOrigin(),
|
||||
hero
|
||||
)
|
||||
local headPos = Vector(strikeGround.x, strikeGround.y, strikeGround.z + height)
|
||||
local warningTime = math.max(
|
||||
0,
|
||||
ability:GetSpecialValueFor("precast_warning_time")
|
||||
)
|
||||
local dmgRadius = math.max(
|
||||
1,
|
||||
ability:GetSpecialValueFor("damage_radius")
|
||||
)
|
||||
local arcPayload = {
|
||||
vLocX = headPos.x,
|
||||
vLocY = headPos.y,
|
||||
vLocZ = headPos.z,
|
||||
strikeX = strikeGround.x,
|
||||
strikeY = strikeGround.y,
|
||||
strikeZ = strikeGround.z,
|
||||
hero_ei = hero:entindex()
|
||||
}
|
||||
if warningTime > 0 then
|
||||
self.precastLockUntil = now + warningTime
|
||||
ability:showLeapPrecastVisuals(parent, strikeGround, dmgRadius, warningTime)
|
||||
local ____self = self
|
||||
Timers:CreateTimer(
|
||||
warningTime,
|
||||
function()
|
||||
____self.precastLockUntil = -1
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if not IsValidEntity(parent) or not parent:IsAlive() then
|
||||
return
|
||||
end
|
||||
if parent:HasModifier("modifier_contract_head_leap_arc") then
|
||||
return
|
||||
end
|
||||
local ab = parent:FindAbilityByName("contract_head_leap")
|
||||
if not ab then
|
||||
return
|
||||
end
|
||||
parent:AddNewModifier(parent, ab, "modifier_contract_head_leap_arc", arcPayload)
|
||||
end
|
||||
)
|
||||
else
|
||||
parent:AddNewModifier(parent, ability, "modifier_contract_head_leap_arc", arcPayload)
|
||||
end
|
||||
end
|
||||
modifier_contract_head_leap_passive = __TS__Decorate(
|
||||
modifier_contract_head_leap_passive,
|
||||
modifier_contract_head_leap_passive,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_contract_head_leap_passive"}
|
||||
)
|
||||
____exports.modifier_contract_head_leap_passive = modifier_contract_head_leap_passive
|
||||
____exports.modifier_contract_head_leap_arc = __TS__Class()
|
||||
local modifier_contract_head_leap_arc = ____exports.modifier_contract_head_leap_arc
|
||||
modifier_contract_head_leap_arc.name = "modifier_contract_head_leap_arc"
|
||||
modifier_contract_head_leap_arc.____file_path = "scripts/vscripts/abilities/creep/contract_head_leap.lua"
|
||||
__TS__ClassExtends(modifier_contract_head_leap_arc, BaseModifierMotionBoth)
|
||||
function modifier_contract_head_leap_arc.prototype.____constructor(self, ...)
|
||||
BaseModifierMotionBoth.prototype.____constructor(self, ...)
|
||||
self.bHorizontalMotionInterrupted = false
|
||||
self.flCurrentTimeHoriz = 0
|
||||
self.flCurrentTimeVert = 0
|
||||
self.flInitialVelocityZ = 0
|
||||
self.flPredictedTotalTime = 0
|
||||
end
|
||||
function modifier_contract_head_leap_arc.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_contract_head_leap_arc.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_contract_head_leap_arc.prototype.RemoveOnDeath(self)
|
||||
return true
|
||||
end
|
||||
function modifier_contract_head_leap_arc.prototype.OnCreated(self, params)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
if not parent or not ability then
|
||||
self:Destroy()
|
||||
return
|
||||
end
|
||||
local hx = params.hero_ei
|
||||
if hx == nil or hx == nil then
|
||||
self:Destroy()
|
||||
return
|
||||
end
|
||||
self.heroEntIndex = hx
|
||||
local sx = params.strikeX or parent:GetOrigin().x
|
||||
local sy = params.strikeY or parent:GetOrigin().y
|
||||
local sz = params.strikeZ or parent:GetOrigin().z
|
||||
self.strikeGround = Vector(sx, sy, sz)
|
||||
self.bHorizontalMotionInterrupted = false
|
||||
if not self:ApplyHorizontalMotionController() or not self:ApplyVerticalMotionController() then
|
||||
self:Destroy()
|
||||
return
|
||||
end
|
||||
self.vStartPosition = GetGroundPosition(
|
||||
parent:GetOrigin(),
|
||||
parent
|
||||
)
|
||||
self.flCurrentTimeHoriz = 0
|
||||
self.flCurrentTimeVert = 0
|
||||
local x = params.vLocX or parent:GetOrigin().x
|
||||
local y = params.vLocY or parent:GetOrigin().y
|
||||
local z = params.vLocZ or parent:GetOrigin().z
|
||||
self.vLoc = Vector(x, y, z)
|
||||
self.vLastKnownTargetPos = self.vLoc
|
||||
local duration = math.max(
|
||||
0.08,
|
||||
ability:GetSpecialValueFor("jump_duration")
|
||||
)
|
||||
local flDesiredHeight = CONTRACT_HEAD_LEAP_MIN_HEIGHT_ABOVE_LOWEST * duration * duration
|
||||
local flLowZ = math.min(self.vLastKnownTargetPos.z, self.vStartPosition.z)
|
||||
local flHighZ = math.max(self.vLastKnownTargetPos.z, self.vStartPosition.z)
|
||||
local flArcTopZ = math.max(flLowZ + flDesiredHeight, flHighZ + CONTRACT_HEAD_LEAP_MIN_HEIGHT_ABOVE_HIGHEST)
|
||||
local flArcDeltaZ = flArcTopZ - self.vStartPosition.z
|
||||
self.flInitialVelocityZ = math.sqrt(2 * flArcDeltaZ * CONTRACT_HEAD_LEAP_ACCELERATION_Z)
|
||||
local flDeltaZ = self.vLastKnownTargetPos.z - self.vStartPosition.z
|
||||
local flSqrtDet = math.sqrt(math.max(0, self.flInitialVelocityZ * self.flInitialVelocityZ - 2 * CONTRACT_HEAD_LEAP_ACCELERATION_Z * flDeltaZ))
|
||||
self.flPredictedTotalTime = math.max((self.flInitialVelocityZ + flSqrtDet) / CONTRACT_HEAD_LEAP_ACCELERATION_Z, (self.flInitialVelocityZ - flSqrtDet) / CONTRACT_HEAD_LEAP_ACCELERATION_Z)
|
||||
self.vHorizontalVelocity = (self.vLastKnownTargetPos - self.vStartPosition) / self.flPredictedTotalTime
|
||||
self.vHorizontalVelocity.z = 0
|
||||
end
|
||||
function modifier_contract_head_leap_arc.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
if parent ~= nil then
|
||||
parent:RemoveHorizontalMotionController(self)
|
||||
parent:RemoveVerticalMotionController(self)
|
||||
end
|
||||
end
|
||||
function modifier_contract_head_leap_arc.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_STUNNED] = true, [MODIFIER_STATE_UNSELECTABLE] = true}
|
||||
end
|
||||
function modifier_contract_head_leap_arc.prototype.UpdateHorizontalMotion(self, me, dt)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self.flCurrentTimeHoriz = math.min(self.flCurrentTimeHoriz + dt, self.flPredictedTotalTime)
|
||||
local t = self.flCurrentTimeHoriz / self.flPredictedTotalTime
|
||||
local vStartToTarget = self.vLastKnownTargetPos - self.vStartPosition
|
||||
local vDesiredPos = self.vStartPosition + vStartToTarget * t
|
||||
local vOldPos = me:GetOrigin()
|
||||
local vToDesired = vDesiredPos - vOldPos
|
||||
vToDesired.z = 0
|
||||
local vDesiredVel = vToDesired / dt
|
||||
local vVelDif = vDesiredVel - self.vHorizontalVelocity
|
||||
local flVelDif = vVelDif:Length2D()
|
||||
vVelDif = vVelDif:Normalized()
|
||||
local flVelDelta = math.min(flVelDif, CONTRACT_HEAD_LEAP_MAX_HORIZONTAL_ACCELERATION)
|
||||
self.vHorizontalVelocity = self.vHorizontalVelocity + vVelDif * flVelDelta * dt
|
||||
local vNewPos = vOldPos + self.vHorizontalVelocity * dt
|
||||
me:SetOrigin(vNewPos)
|
||||
end
|
||||
function modifier_contract_head_leap_arc.prototype.UpdateVerticalMotion(self, me, dt)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self.flCurrentTimeVert = self.flCurrentTimeVert + dt
|
||||
local bGoingDown = -CONTRACT_HEAD_LEAP_ACCELERATION_Z * self.flCurrentTimeVert + self.flInitialVelocityZ < 0
|
||||
local vNewPos = me:GetOrigin()
|
||||
vNewPos.z = self.vStartPosition.z + (-0.5 * CONTRACT_HEAD_LEAP_ACCELERATION_Z * self.flCurrentTimeVert * self.flCurrentTimeVert + self.flInitialVelocityZ * self.flCurrentTimeVert)
|
||||
local flGroundHeight = GetGroundHeight(
|
||||
vNewPos,
|
||||
self:GetParent()
|
||||
)
|
||||
local bLanded = false
|
||||
if vNewPos.z < flGroundHeight and bGoingDown then
|
||||
vNewPos.z = flGroundHeight
|
||||
bLanded = true
|
||||
end
|
||||
me:SetOrigin(vNewPos)
|
||||
if bLanded then
|
||||
if not self.bHorizontalMotionInterrupted then
|
||||
local ability = self:GetAbility()
|
||||
if ability then
|
||||
ability:performLeapLanding(self.heroEntIndex, self.strikeGround)
|
||||
end
|
||||
end
|
||||
self:Destroy()
|
||||
end
|
||||
end
|
||||
function modifier_contract_head_leap_arc.prototype.OnHorizontalMotionInterrupted(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self.bHorizontalMotionInterrupted = true
|
||||
end
|
||||
function modifier_contract_head_leap_arc.prototype.OnVerticalMotionInterrupted(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self:Destroy()
|
||||
end
|
||||
function modifier_contract_head_leap_arc.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_OVERRIDE_ANIMATION}
|
||||
end
|
||||
function modifier_contract_head_leap_arc.prototype.GetOverrideAnimation(self)
|
||||
return ACT_DOTA_CAST_ABILITY_1
|
||||
end
|
||||
modifier_contract_head_leap_arc = __TS__Decorate(
|
||||
modifier_contract_head_leap_arc,
|
||||
modifier_contract_head_leap_arc,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_contract_head_leap_arc"}
|
||||
)
|
||||
____exports.modifier_contract_head_leap_arc = modifier_contract_head_leap_arc
|
||||
return ____exports
|
||||
@@ -0,0 +1,50 @@
|
||||
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
|
||||
____exports.harpy_passive = __TS__Class()
|
||||
local harpy_passive = ____exports.harpy_passive
|
||||
harpy_passive.name = "harpy_passive"
|
||||
harpy_passive.____file_path = "scripts/vscripts/abilities/creep/dead_harpy.lua"
|
||||
__TS__ClassExtends(harpy_passive, BaseAbility)
|
||||
function harpy_passive.prototype.GetIntrinsicModifierName(self)
|
||||
return "modifier_harpy_passive"
|
||||
end
|
||||
harpy_passive = __TS__Decorate(
|
||||
harpy_passive,
|
||||
harpy_passive,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "harpy_passive"}
|
||||
)
|
||||
____exports.harpy_passive = harpy_passive
|
||||
____exports.modifier_harpy_passive = __TS__Class()
|
||||
local modifier_harpy_passive = ____exports.modifier_harpy_passive
|
||||
modifier_harpy_passive.name = "modifier_harpy_passive"
|
||||
modifier_harpy_passive.____file_path = "scripts/vscripts/abilities/creep/dead_harpy.lua"
|
||||
__TS__ClassExtends(modifier_harpy_passive, BaseModifier)
|
||||
function modifier_harpy_passive.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_harpy_passive.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_VISUAL_Z_DELTA}
|
||||
end
|
||||
function modifier_harpy_passive.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_FLYING] = true, [MODIFIER_STATE_NO_UNIT_COLLISION] = true}
|
||||
end
|
||||
function modifier_harpy_passive.prototype.GetVisualZDelta(self)
|
||||
return 100
|
||||
end
|
||||
modifier_harpy_passive = __TS__Decorate(
|
||||
modifier_harpy_passive,
|
||||
modifier_harpy_passive,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_harpy_passive"}
|
||||
)
|
||||
____exports.modifier_harpy_passive = modifier_harpy_passive
|
||||
return ____exports
|
||||
@@ -0,0 +1,117 @@
|
||||
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 ____incoming_damage_reduction_combine = require("utils.incoming_damage_reduction_combine")
|
||||
local removeIncomingDamageReductionSource = ____incoming_damage_reduction_combine.removeIncomingDamageReductionSource
|
||||
local setIncomingDamageReductionSource = ____incoming_damage_reduction_combine.setIncomingDamageReductionSource
|
||||
local ANIMATION_STUN_INCOMING_SOURCE = "modifier_animation_stun"
|
||||
____exports.ability_animation = __TS__Class()
|
||||
local ability_animation = ____exports.ability_animation
|
||||
ability_animation.name = "ability_animation"
|
||||
ability_animation.____file_path = "scripts/vscripts/abilities/creep/demon_dragon_satyr/animation.lua"
|
||||
__TS__ClassExtends(ability_animation, BaseAbility)
|
||||
function ability_animation.prototype.GetIntrinsicModifierName(self)
|
||||
return "modifier_animation_checker"
|
||||
end
|
||||
ability_animation = __TS__Decorate(
|
||||
ability_animation,
|
||||
ability_animation,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_animation"}
|
||||
)
|
||||
____exports.ability_animation = ability_animation
|
||||
____exports.modifier_animation_checker = __TS__Class()
|
||||
local modifier_animation_checker = ____exports.modifier_animation_checker
|
||||
modifier_animation_checker.name = "modifier_animation_checker"
|
||||
modifier_animation_checker.____file_path = "scripts/vscripts/abilities/creep/demon_dragon_satyr/animation.lua"
|
||||
__TS__ClassExtends(modifier_animation_checker, BaseModifier)
|
||||
function modifier_animation_checker.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_animation_checker.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_EVENT_ON_TAKEDAMAGE}
|
||||
end
|
||||
function modifier_animation_checker.prototype.OnTakeDamage(self, event)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local unit = event.unit
|
||||
if unit ~= self:GetParent() then
|
||||
return
|
||||
end
|
||||
local maxHealth = unit:GetMaxHealth()
|
||||
local currentHealth = unit:GetHealth()
|
||||
local ability = self:GetAbility()
|
||||
if currentHealth <= maxHealth * 0.75 and self:GetStackCount() < 1 then
|
||||
self:IncrementStackCount()
|
||||
unit:AddNewModifier(unit, ability, "modifier_animation_stun", {duration = 3.7})
|
||||
end
|
||||
if currentHealth <= maxHealth * 0.5 and self:GetStackCount() < 2 then
|
||||
self:IncrementStackCount()
|
||||
unit:AddNewModifier(unit, ability, "modifier_animation_stun", {duration = 3.7})
|
||||
end
|
||||
if currentHealth <= maxHealth * 0.25 and self:GetStackCount() < 3 then
|
||||
self:IncrementStackCount()
|
||||
unit:AddNewModifier(unit, ability, "modifier_animation_stun", {duration = 3.7})
|
||||
end
|
||||
end
|
||||
modifier_animation_checker = __TS__Decorate(
|
||||
modifier_animation_checker,
|
||||
modifier_animation_checker,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_animation_checker"}
|
||||
)
|
||||
____exports.modifier_animation_checker = modifier_animation_checker
|
||||
____exports.modifier_animation_stun = __TS__Class()
|
||||
local modifier_animation_stun = ____exports.modifier_animation_stun
|
||||
modifier_animation_stun.name = "modifier_animation_stun"
|
||||
modifier_animation_stun.____file_path = "scripts/vscripts/abilities/creep/demon_dragon_satyr/animation.lua"
|
||||
__TS__ClassExtends(modifier_animation_stun, BaseModifier)
|
||||
function modifier_animation_stun.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_animation_stun.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
parent:StartGestureWithFade(ACT_DOTA_NIAN_PIN_TO_STUN, 0.5, 0)
|
||||
setIncomingDamageReductionSource(
|
||||
nil,
|
||||
parent,
|
||||
ANIMATION_STUN_INCOMING_SOURCE,
|
||||
function() return 50 end
|
||||
)
|
||||
end
|
||||
function modifier_animation_stun.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
removeIncomingDamageReductionSource(nil, parent, ANIMATION_STUN_INCOMING_SOURCE)
|
||||
parent:RemoveGesture(ACT_DOTA_NIAN_PIN_TO_STUN)
|
||||
end
|
||||
function modifier_animation_stun.prototype.CheckState(self)
|
||||
return {
|
||||
[MODIFIER_STATE_COMMAND_RESTRICTED] = false,
|
||||
[MODIFIER_STATE_DISARMED] = true,
|
||||
[MODIFIER_STATE_IGNORING_MOVE_AND_ATTACK_ORDERS] = true,
|
||||
[MODIFIER_STATE_IGNORING_MOVE_ORDERS] = true,
|
||||
[MODIFIER_STATE_SILENCED] = true
|
||||
}
|
||||
end
|
||||
modifier_animation_stun = __TS__Decorate(
|
||||
modifier_animation_stun,
|
||||
modifier_animation_stun,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_animation_stun"}
|
||||
)
|
||||
____exports.modifier_animation_stun = modifier_animation_stun
|
||||
return ____exports
|
||||
@@ -0,0 +1,92 @@
|
||||
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
|
||||
____exports.back_stun = __TS__Class()
|
||||
local back_stun = ____exports.back_stun
|
||||
back_stun.name = "back_stun"
|
||||
back_stun.____file_path = "scripts/vscripts/abilities/creep/demon_dragon_satyr/back_stun.lua"
|
||||
__TS__ClassExtends(back_stun, BaseAbility)
|
||||
function back_stun.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/econ/items/earthshaker/deep_magma/deep_magma_10th/deep_magma_10th_echoslam_start.vpcf", context)
|
||||
PrecacheResource("particle", "particles/generic_gameplay/generic_stunned.vpcf", context)
|
||||
end
|
||||
function back_stun.prototype.GetCastRange(self)
|
||||
return self:GetSpecialValueFor("cast_range")
|
||||
end
|
||||
function back_stun.prototype.OnSpellStart(self)
|
||||
local caster = self:GetCaster()
|
||||
local duration = self:GetSpecialValueFor("duration")
|
||||
local radius = self:GetSpecialValueFor("cast_range")
|
||||
if not caster then
|
||||
return
|
||||
end
|
||||
local direction = caster:GetForwardVector()
|
||||
local backPosition = caster:GetOrigin():__sub(direction:__mul(radius))
|
||||
self.particle = ParticleManager:CreateParticle("particles/econ/items/earthshaker/deep_magma/deep_magma_10th/deep_magma_10th_echoslam_start.vpcf", PATTACH_WORLDORIGIN, nil)
|
||||
ParticleManager:SetParticleControl(self.particle, 0, backPosition)
|
||||
local units = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
backPosition,
|
||||
nil,
|
||||
radius,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC,
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
for ____, unit in ipairs(units) do
|
||||
unit:AddNewModifier(caster, self, "modifier_back_stunned", {duration = duration})
|
||||
local damage = self:GetSpecialValueFor("damage") + self:GetCaster():GetAverageTrueAttackDamage(unit) * 3
|
||||
ApplyDamage({victim = unit, attacker = caster, damage = damage, damage_type = DAMAGE_TYPE_PURE})
|
||||
EmitSoundOn("Hero_Centaur.HoofStomp", unit)
|
||||
end
|
||||
end
|
||||
back_stun = __TS__Decorate(
|
||||
back_stun,
|
||||
back_stun,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "back_stun"}
|
||||
)
|
||||
____exports.back_stun = back_stun
|
||||
____exports.modifier_back_stunned = __TS__Class()
|
||||
local modifier_back_stunned = ____exports.modifier_back_stunned
|
||||
modifier_back_stunned.name = "modifier_back_stunned"
|
||||
modifier_back_stunned.____file_path = "scripts/vscripts/abilities/creep/demon_dragon_satyr/back_stun.lua"
|
||||
__TS__ClassExtends(modifier_back_stunned, BaseModifier)
|
||||
function modifier_back_stunned.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_back_stunned.prototype.IsDebuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_back_stunned.prototype.IsStunDebuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_back_stunned.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
function modifier_back_stunned.prototype.GetEffectName(self)
|
||||
return "particles/generic_gameplay/generic_stunned.vpcf"
|
||||
end
|
||||
function modifier_back_stunned.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_OVERHEAD_FOLLOW
|
||||
end
|
||||
function modifier_back_stunned.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_STUNNED] = true}
|
||||
end
|
||||
modifier_back_stunned = __TS__Decorate(
|
||||
modifier_back_stunned,
|
||||
modifier_back_stunned,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_back_stunned"}
|
||||
)
|
||||
____exports.modifier_back_stunned = modifier_back_stunned
|
||||
return ____exports
|
||||
@@ -0,0 +1,92 @@
|
||||
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
|
||||
____exports.face_stun = __TS__Class()
|
||||
local face_stun = ____exports.face_stun
|
||||
face_stun.name = "face_stun"
|
||||
face_stun.____file_path = "scripts/vscripts/abilities/creep/demon_dragon_satyr/face_stun.lua"
|
||||
__TS__ClassExtends(face_stun, BaseAbility)
|
||||
function face_stun.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/econ/items/earthshaker/deep_magma/deep_magma_10th/deep_magma_10th_echoslam_start.vpcf", context)
|
||||
PrecacheResource("particle", "particles/generic_gameplay/generic_stunned.vpcf", context)
|
||||
end
|
||||
function face_stun.prototype.GetCastRange(self)
|
||||
return self:GetSpecialValueFor("cast_range")
|
||||
end
|
||||
function face_stun.prototype.OnSpellStart(self)
|
||||
local caster = self:GetCaster()
|
||||
local duration = self:GetSpecialValueFor("duration")
|
||||
local radius = self:GetSpecialValueFor("cast_range")
|
||||
if not caster then
|
||||
return
|
||||
end
|
||||
local direction = caster:GetForwardVector()
|
||||
local frontPosition = caster:GetOrigin():__add(direction:__mul(radius))
|
||||
self.particle = ParticleManager:CreateParticle("particles/econ/items/earthshaker/deep_magma/deep_magma_10th/deep_magma_10th_echoslam_start.vpcf", PATTACH_WORLDORIGIN, nil)
|
||||
ParticleManager:SetParticleControl(self.particle, 0, frontPosition)
|
||||
local units = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
frontPosition,
|
||||
nil,
|
||||
radius,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC,
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
for ____, unit in ipairs(units) do
|
||||
unit:AddNewModifier(caster, self, "modifier_face_stunned", {duration = duration})
|
||||
local damage = self:GetSpecialValueFor("damage") + self:GetCaster():GetAverageTrueAttackDamage(unit) * 3
|
||||
ApplyDamage({victim = unit, attacker = caster, damage = damage, damage_type = DAMAGE_TYPE_PURE})
|
||||
EmitSoundOn("Hero_Centaur.HoofStomp", unit)
|
||||
end
|
||||
end
|
||||
face_stun = __TS__Decorate(
|
||||
face_stun,
|
||||
face_stun,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "face_stun"}
|
||||
)
|
||||
____exports.face_stun = face_stun
|
||||
____exports.modifier_face_stunned = __TS__Class()
|
||||
local modifier_face_stunned = ____exports.modifier_face_stunned
|
||||
modifier_face_stunned.name = "modifier_face_stunned"
|
||||
modifier_face_stunned.____file_path = "scripts/vscripts/abilities/creep/demon_dragon_satyr/face_stun.lua"
|
||||
__TS__ClassExtends(modifier_face_stunned, BaseModifier)
|
||||
function modifier_face_stunned.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_face_stunned.prototype.IsDebuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_face_stunned.prototype.IsStunDebuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_face_stunned.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
function modifier_face_stunned.prototype.GetEffectName(self)
|
||||
return "particles/generic_gameplay/generic_stunned.vpcf"
|
||||
end
|
||||
function modifier_face_stunned.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_OVERHEAD_FOLLOW
|
||||
end
|
||||
function modifier_face_stunned.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_STUNNED] = true}
|
||||
end
|
||||
modifier_face_stunned = __TS__Decorate(
|
||||
modifier_face_stunned,
|
||||
modifier_face_stunned,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_face_stunned"}
|
||||
)
|
||||
____exports.modifier_face_stunned = modifier_face_stunned
|
||||
return ____exports
|
||||
@@ -0,0 +1,132 @@
|
||||
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
|
||||
____exports.fear = __TS__Class()
|
||||
local fear = ____exports.fear
|
||||
fear.name = "fear"
|
||||
fear.____file_path = "scripts/vscripts/abilities/creep/demon_dragon_satyr/fear.lua"
|
||||
__TS__ClassExtends(fear, BaseAbility)
|
||||
function fear.prototype.GetCastRange(self)
|
||||
return self:GetSpecialValueFor("cast_range")
|
||||
end
|
||||
function fear.prototype.OnSpellStart(self)
|
||||
local caster = self:GetCaster()
|
||||
local duration = self:GetSpecialValueFor("duration")
|
||||
if not caster then
|
||||
return
|
||||
end
|
||||
local units = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
caster:GetAbsOrigin(),
|
||||
nil,
|
||||
1200,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC,
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
local particle = ParticleManager:CreateParticle("particles/econ/items/terrorblade/terrorblade_back_ti8/terrorblade_sunder_ti8_swirl_rope.vpcf", PATTACH_WORLDORIGIN, nil)
|
||||
ParticleManager:SetParticleControl(
|
||||
particle,
|
||||
0,
|
||||
caster:GetAbsOrigin()
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(particle)
|
||||
for ____, unit in ipairs(units) do
|
||||
unit:AddNewModifier(caster, self, "modifier_fear_debuff", {duration = duration})
|
||||
EmitSoundOn("Hero_NightStalker.Void", unit)
|
||||
end
|
||||
end
|
||||
fear = __TS__Decorate(
|
||||
fear,
|
||||
fear,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "fear"}
|
||||
)
|
||||
____exports.fear = fear
|
||||
____exports.modifier_fear_debuff = __TS__Class()
|
||||
local modifier_fear_debuff = ____exports.modifier_fear_debuff
|
||||
modifier_fear_debuff.name = "modifier_fear_debuff"
|
||||
modifier_fear_debuff.____file_path = "scripts/vscripts/abilities/creep/demon_dragon_satyr/fear.lua"
|
||||
__TS__ClassExtends(modifier_fear_debuff, BaseModifier)
|
||||
function modifier_fear_debuff.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.interval = 0.03
|
||||
end
|
||||
function modifier_fear_debuff.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_fear_debuff.prototype.IsDebuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_fear_debuff.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
function modifier_fear_debuff.prototype.GetEffectName(self)
|
||||
return "particles/econ/items/nightstalker/nightstalker_black_nihility/nightstalker_black_nihility_void.vpcf"
|
||||
end
|
||||
function modifier_fear_debuff.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_CENTER_FOLLOW
|
||||
end
|
||||
function modifier_fear_debuff.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self.particle = ParticleManager:CreateParticle(
|
||||
"particles/generic_gameplay/generic_feared.vpcf",
|
||||
PATTACH_OVERHEAD_FOLLOW,
|
||||
self:GetParent()
|
||||
)
|
||||
self:StartIntervalThink(self.interval)
|
||||
local ____opt_0 = self:GetCaster()
|
||||
self.casterPos = ____opt_0 and ____opt_0:GetAbsOrigin()
|
||||
end
|
||||
function modifier_fear_debuff.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if self.particle then
|
||||
ParticleManager:DestroyParticle(self.particle, false)
|
||||
ParticleManager:ReleaseParticleIndex(self.particle)
|
||||
end
|
||||
end
|
||||
function modifier_fear_debuff.prototype.OnIntervalThink(self)
|
||||
if not IsServer() or not self.casterPos then
|
||||
return
|
||||
end
|
||||
local unit = self:GetParent()
|
||||
local unitPos = unit:GetAbsOrigin()
|
||||
local direction = unitPos:__sub(self.casterPos):Normalized()
|
||||
unit:MoveToPosition(unitPos:__add(direction:__mul(150)))
|
||||
end
|
||||
function modifier_fear_debuff.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_PROVIDES_FOW_POSITION, MODIFIER_PROPERTY_BONUS_VISION_PERCENTAGE, MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE}
|
||||
end
|
||||
function modifier_fear_debuff.prototype.BonusVisionPercentage(self)
|
||||
return -100
|
||||
end
|
||||
function modifier_fear_debuff.prototype.MovespeedBonusPercentage(self)
|
||||
return -100
|
||||
end
|
||||
function modifier_fear_debuff.prototype.GetModifierProvidesFOWVision(self)
|
||||
return 0
|
||||
end
|
||||
function modifier_fear_debuff.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_FEARED] = true, [MODIFIER_STATE_COMMAND_RESTRICTED] = true}
|
||||
end
|
||||
modifier_fear_debuff = __TS__Decorate(
|
||||
modifier_fear_debuff,
|
||||
modifier_fear_debuff,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_fear_debuff"}
|
||||
)
|
||||
____exports.modifier_fear_debuff = modifier_fear_debuff
|
||||
return ____exports
|
||||
@@ -0,0 +1,171 @@
|
||||
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
|
||||
____exports.ability_grab = __TS__Class()
|
||||
local ability_grab = ____exports.ability_grab
|
||||
ability_grab.name = "ability_grab"
|
||||
ability_grab.____file_path = "scripts/vscripts/abilities/creep/demon_dragon_satyr/grab.lua"
|
||||
__TS__ClassExtends(ability_grab, BaseAbility)
|
||||
function ability_grab.prototype.CancelGrabCast(self)
|
||||
self:EndChannel(true)
|
||||
self:EndCooldown()
|
||||
self:RefundManaCost()
|
||||
end
|
||||
function ability_grab.prototype.GetCastRange(self)
|
||||
return 150
|
||||
end
|
||||
function ability_grab.prototype.GetChannelTime(self)
|
||||
return 4.37
|
||||
end
|
||||
function ability_grab.prototype.GetBehavior(self)
|
||||
return bit.bor(DOTA_ABILITY_BEHAVIOR_POINT, DOTA_ABILITY_BEHAVIOR_CHANNELLED)
|
||||
end
|
||||
function ability_grab.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local point = self:GetCursorPosition()
|
||||
local direction = (point - caster:GetAbsOrigin()):Normalized()
|
||||
local units = FindUnitsInLine(
|
||||
caster:GetTeamNumber(),
|
||||
caster:GetAbsOrigin(),
|
||||
caster:GetAbsOrigin() + direction * self:GetCastRange(),
|
||||
nil,
|
||||
100,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC,
|
||||
DOTA_UNIT_TARGET_FLAG_NONE
|
||||
)
|
||||
if #units == 0 then
|
||||
self:CancelGrabCast()
|
||||
return
|
||||
end
|
||||
local target = units[1]
|
||||
if not target then
|
||||
self:CancelGrabCast()
|
||||
return
|
||||
end
|
||||
target:AddNewModifier(
|
||||
caster,
|
||||
self,
|
||||
"modifier_grab_stunned",
|
||||
{duration = self:GetChannelTime()}
|
||||
)
|
||||
caster:AddNewModifier(
|
||||
caster,
|
||||
self,
|
||||
"modifier_grab_caster",
|
||||
{duration = self:GetChannelTime()}
|
||||
)
|
||||
caster:StartGesture(ACT_DOTA_NIAN_PIN_LOOP)
|
||||
target:SetParent(caster, "attach_pindown")
|
||||
target:SetOrigin(caster:GetAbsOrigin())
|
||||
self.target = target
|
||||
self.damageTimer = Timers:CreateTimer(
|
||||
0.33,
|
||||
function()
|
||||
if self.target and not self.target:IsNull() and self.target:IsAlive() then
|
||||
ParticleManager:CreateParticle("particles/econ/items/lifestealer/ls_ti9_immortal/ls_ti9_open_wounds_blood_bulk.vpcf", PATTACH_ABSORIGIN_FOLLOW, self.target)
|
||||
EmitSoundOn("Hero_Pudge.Dismember", self.target)
|
||||
ApplyDamage({
|
||||
victim = self.target,
|
||||
attacker = caster,
|
||||
damage = self:GetSpecialValueFor("damage") + self:GetCaster():GetAverageTrueAttackDamage(target) * 3,
|
||||
damage_type = DAMAGE_TYPE_PURE
|
||||
})
|
||||
return 0.33
|
||||
end
|
||||
return nil
|
||||
end
|
||||
)
|
||||
end
|
||||
function ability_grab.prototype.OnChannelFinish(self, interrupted)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self:GetCaster():RemoveGesture(ACT_DOTA_NIAN_PIN_LOOP)
|
||||
if self.damageTimer then
|
||||
Timers:RemoveTimer(self.damageTimer)
|
||||
self.damageTimer = nil
|
||||
end
|
||||
if self.target then
|
||||
self.target:RemoveModifierByName("modifier_grab_stunned")
|
||||
self.target:SetParent(nil, "")
|
||||
self.target:RemoveHorizontalMotionController(self.target)
|
||||
self.target:RemoveVerticalMotionController(self.target)
|
||||
self.target:InterruptMotionControllers(true)
|
||||
local casterPos = self:GetCaster():GetAbsOrigin()
|
||||
local casterForward = self:GetCaster():GetForwardVector()
|
||||
local dropPoint = casterPos + casterForward * 100
|
||||
FindClearSpaceForUnit(self.target, dropPoint, true)
|
||||
ApplyDamage({
|
||||
victim = self.target,
|
||||
attacker = self:GetCaster(),
|
||||
damage = self:GetSpecialValueFor("damage"),
|
||||
damage_type = DAMAGE_TYPE_PHYSICAL
|
||||
})
|
||||
end
|
||||
self:GetCaster():RemoveModifierByName("modifier_grab_caster")
|
||||
self.target = nil
|
||||
end
|
||||
ability_grab = __TS__Decorate(
|
||||
ability_grab,
|
||||
ability_grab,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_grab"}
|
||||
)
|
||||
____exports.ability_grab = ability_grab
|
||||
____exports.modifier_grab_stunned = __TS__Class()
|
||||
local modifier_grab_stunned = ____exports.modifier_grab_stunned
|
||||
modifier_grab_stunned.name = "modifier_grab_stunned"
|
||||
modifier_grab_stunned.____file_path = "scripts/vscripts/abilities/creep/demon_dragon_satyr/grab.lua"
|
||||
__TS__ClassExtends(modifier_grab_stunned, BaseModifier)
|
||||
function modifier_grab_stunned.prototype.IsDebuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_grab_stunned.prototype.IsStunDebuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_grab_stunned.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_STUNNED] = true, [MODIFIER_STATE_NO_UNIT_COLLISION] = true}
|
||||
end
|
||||
function modifier_grab_stunned.prototype.GetEffectName(self)
|
||||
return "particles/generic_gameplay/generic_stunned.vpcf"
|
||||
end
|
||||
function modifier_grab_stunned.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_OVERHEAD_FOLLOW
|
||||
end
|
||||
modifier_grab_stunned = __TS__Decorate(
|
||||
modifier_grab_stunned,
|
||||
modifier_grab_stunned,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_grab_stunned"}
|
||||
)
|
||||
____exports.modifier_grab_stunned = modifier_grab_stunned
|
||||
____exports.modifier_grab_caster = __TS__Class()
|
||||
local modifier_grab_caster = ____exports.modifier_grab_caster
|
||||
modifier_grab_caster.name = "modifier_grab_caster"
|
||||
modifier_grab_caster.____file_path = "scripts/vscripts/abilities/creep/demon_dragon_satyr/grab.lua"
|
||||
__TS__ClassExtends(modifier_grab_caster, BaseModifier)
|
||||
function modifier_grab_caster.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_grab_caster.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_DISARMED] = true, [MODIFIER_STATE_ROOTED] = true}
|
||||
end
|
||||
modifier_grab_caster = __TS__Decorate(
|
||||
modifier_grab_caster,
|
||||
modifier_grab_caster,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_grab_caster"}
|
||||
)
|
||||
____exports.modifier_grab_caster = modifier_grab_caster
|
||||
return ____exports
|
||||
@@ -0,0 +1,290 @@
|
||||
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 BaseModifierMotionBoth = ____dota_ts_adapter.BaseModifierMotionBoth
|
||||
local registerAbility = ____dota_ts_adapter.registerAbility
|
||||
local registerModifier = ____dota_ts_adapter.registerModifier
|
||||
____exports.frogmen_acid_jump = __TS__Class()
|
||||
local frogmen_acid_jump = ____exports.frogmen_acid_jump
|
||||
frogmen_acid_jump.name = "frogmen_acid_jump"
|
||||
frogmen_acid_jump.____file_path = "scripts/vscripts/abilities/creep/frogmen_acid_jump.lua"
|
||||
__TS__ClassExtends(frogmen_acid_jump, BaseAbility)
|
||||
function frogmen_acid_jump.prototype.____constructor(self, ...)
|
||||
BaseAbility.prototype.____constructor(self, ...)
|
||||
self.radius = 0
|
||||
self.stun_duration = 0
|
||||
self.land_damage = 0
|
||||
end
|
||||
function frogmen_acid_jump.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/neutral_fx/ogre_bruiser_smash.vpcf", context)
|
||||
end
|
||||
function frogmen_acid_jump.prototype.OnAbilityPhaseStart(self)
|
||||
if not IsServer() then
|
||||
return true
|
||||
end
|
||||
return true
|
||||
end
|
||||
function frogmen_acid_jump.prototype.OnAbilityPhaseInterrupted(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
end
|
||||
function frogmen_acid_jump.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
if not caster then
|
||||
return
|
||||
end
|
||||
local cursorPos = self:GetCursorPosition()
|
||||
local kv = {vLocX = cursorPos.x, vLocY = cursorPos.y, vLocZ = cursorPos.z}
|
||||
caster:AddNewModifier(caster, self, "modifier_acid_blob_jump", kv)
|
||||
self.radius = self:GetSpecialValueFor("radius")
|
||||
self.stun_duration = self:GetSpecialValueFor("stun_duration")
|
||||
self.land_damage = self:GetSpecialValueFor("land_damage")
|
||||
end
|
||||
function frogmen_acid_jump.prototype.Smash(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
if not caster then
|
||||
return
|
||||
end
|
||||
local origin = caster:GetOrigin()
|
||||
EmitSoundOnLocationWithCaster(origin, "OgreTank.GroundSmash", caster)
|
||||
local smashFX = ParticleManager:CreateParticle("particles/neutral_fx/ogre_bruiser_smash.vpcf", PATTACH_WORLDORIGIN, caster)
|
||||
ParticleManager:SetParticleControl(smashFX, 0, origin)
|
||||
ParticleManager:SetParticleControl(
|
||||
smashFX,
|
||||
1,
|
||||
Vector(self.radius, self.radius, self.radius)
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(smashFX)
|
||||
caster:Interrupt()
|
||||
local enemies = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
origin,
|
||||
caster,
|
||||
self.radius,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC,
|
||||
DOTA_UNIT_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
for ____, enemy in ipairs(enemies) do
|
||||
do
|
||||
if not enemy then
|
||||
goto __continue13
|
||||
end
|
||||
if enemy:IsInvulnerable() then
|
||||
goto __continue13
|
||||
end
|
||||
ApplyDamage({
|
||||
victim = enemy,
|
||||
attacker = caster,
|
||||
damage = self.land_damage + self:GetCaster():GetAverageTrueAttackDamage(enemy) * 2,
|
||||
damage_type = DAMAGE_TYPE_PHYSICAL,
|
||||
ability = self
|
||||
})
|
||||
if not enemy:IsAlive() then
|
||||
local critFX = ParticleManager:CreateParticle("particles/units/heroes/hero_phantom_assassin/phantom_assassin_crit_impact.vpcf", PATTACH_CUSTOMORIGIN, nil)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
critFX,
|
||||
0,
|
||||
enemy,
|
||||
PATTACH_POINT_FOLLOW,
|
||||
"attach_hitloc",
|
||||
enemy:GetOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
critFX,
|
||||
1,
|
||||
enemy:GetOrigin()
|
||||
)
|
||||
local forward = caster:GetForwardVector()
|
||||
ParticleManager:SetParticleControlForward(critFX, 1, forward * -1)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
critFX,
|
||||
10,
|
||||
enemy,
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
nil,
|
||||
enemy:GetOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(critFX)
|
||||
EmitSoundOn("Dungeon.BloodSplatterImpact", enemy)
|
||||
else
|
||||
enemy:AddNewModifier(caster, self, "modifier_stunned", {duration = self.stun_duration})
|
||||
end
|
||||
end
|
||||
::__continue13::
|
||||
end
|
||||
end
|
||||
frogmen_acid_jump = __TS__Decorate(
|
||||
frogmen_acid_jump,
|
||||
frogmen_acid_jump,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "frogmen_acid_jump"}
|
||||
)
|
||||
____exports.frogmen_acid_jump = frogmen_acid_jump
|
||||
local AMOEBA_MINIMUM_HEIGHT_ABOVE_LOWEST = 400
|
||||
local AMOEBA_MINIMUM_HEIGHT_ABOVE_HIGHEST = 200
|
||||
local AMOEBA_ACCELERATION_Z = 1500
|
||||
local AMOEBA_MAX_HORIZONTAL_ACCELERATION = 1500
|
||||
____exports.modifier_acid_blob_jump = __TS__Class()
|
||||
local modifier_acid_blob_jump = ____exports.modifier_acid_blob_jump
|
||||
modifier_acid_blob_jump.name = "modifier_acid_blob_jump"
|
||||
modifier_acid_blob_jump.____file_path = "scripts/vscripts/abilities/creep/frogmen_acid_jump.lua"
|
||||
__TS__ClassExtends(modifier_acid_blob_jump, BaseModifierMotionBoth)
|
||||
function modifier_acid_blob_jump.prototype.____constructor(self, ...)
|
||||
BaseModifierMotionBoth.prototype.____constructor(self, ...)
|
||||
self.bHorizontalMotionInterrupted = false
|
||||
self.flCurrentTimeHoriz = 0
|
||||
self.flCurrentTimeVert = 0
|
||||
self.flInitialVelocityZ = 0
|
||||
self.flPredictedTotalTime = 0
|
||||
end
|
||||
function modifier_acid_blob_jump.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_acid_blob_jump.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_acid_blob_jump.prototype.RemoveOnDeath(self)
|
||||
return false
|
||||
end
|
||||
function modifier_acid_blob_jump.prototype.OnCreated(self, params)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
if not parent or not ability then
|
||||
self:Destroy()
|
||||
return
|
||||
end
|
||||
self.bHorizontalMotionInterrupted = false
|
||||
if not self:ApplyHorizontalMotionController() or not self:ApplyVerticalMotionController() then
|
||||
self:Destroy()
|
||||
return
|
||||
end
|
||||
self.vStartPosition = GetGroundPosition(
|
||||
parent:GetOrigin(),
|
||||
parent
|
||||
)
|
||||
self.flCurrentTimeHoriz = 0
|
||||
self.flCurrentTimeVert = 0
|
||||
local x = params.vLocX or parent:GetOrigin().x
|
||||
local y = params.vLocY or parent:GetOrigin().y
|
||||
local z = params.vLocZ or parent:GetOrigin().z
|
||||
self.vLoc = Vector(x, y, z)
|
||||
self.vLastKnownTargetPos = self.vLoc
|
||||
local duration = ability:GetSpecialValueFor("duration")
|
||||
local flDesiredHeight = AMOEBA_MINIMUM_HEIGHT_ABOVE_LOWEST * duration * duration
|
||||
local flLowZ = math.min(self.vLastKnownTargetPos.z, self.vStartPosition.z)
|
||||
local flHighZ = math.max(self.vLastKnownTargetPos.z, self.vStartPosition.z)
|
||||
local flArcTopZ = math.max(flLowZ + flDesiredHeight, flHighZ + AMOEBA_MINIMUM_HEIGHT_ABOVE_HIGHEST)
|
||||
local flArcDeltaZ = flArcTopZ - self.vStartPosition.z
|
||||
self.flInitialVelocityZ = math.sqrt(2 * flArcDeltaZ * AMOEBA_ACCELERATION_Z)
|
||||
local flDeltaZ = self.vLastKnownTargetPos.z - self.vStartPosition.z
|
||||
local flSqrtDet = math.sqrt(math.max(0, self.flInitialVelocityZ * self.flInitialVelocityZ - 2 * AMOEBA_ACCELERATION_Z * flDeltaZ))
|
||||
self.flPredictedTotalTime = math.max((self.flInitialVelocityZ + flSqrtDet) / AMOEBA_ACCELERATION_Z, (self.flInitialVelocityZ - flSqrtDet) / AMOEBA_ACCELERATION_Z)
|
||||
self.vHorizontalVelocity = (self.vLastKnownTargetPos - self.vStartPosition) / self.flPredictedTotalTime
|
||||
self.vHorizontalVelocity.z = 0
|
||||
end
|
||||
function modifier_acid_blob_jump.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
if parent ~= nil then
|
||||
parent:RemoveHorizontalMotionController(self)
|
||||
parent:RemoveVerticalMotionController(self)
|
||||
end
|
||||
end
|
||||
function modifier_acid_blob_jump.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_STUNNED] = true, [MODIFIER_STATE_UNSELECTABLE] = true}
|
||||
end
|
||||
function modifier_acid_blob_jump.prototype.UpdateHorizontalMotion(self, me, dt)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self.flCurrentTimeHoriz = math.min(self.flCurrentTimeHoriz + dt, self.flPredictedTotalTime)
|
||||
local t = self.flCurrentTimeHoriz / self.flPredictedTotalTime
|
||||
local vStartToTarget = self.vLastKnownTargetPos - self.vStartPosition
|
||||
local vDesiredPos = self.vStartPosition + vStartToTarget * t
|
||||
local vOldPos = me:GetOrigin()
|
||||
local vToDesired = vDesiredPos - vOldPos
|
||||
vToDesired.z = 0
|
||||
local vDesiredVel = vToDesired / dt
|
||||
local vVelDif = vDesiredVel - self.vHorizontalVelocity
|
||||
local flVelDif = vVelDif:Length2D()
|
||||
vVelDif = vVelDif:Normalized()
|
||||
local flVelDelta = math.min(flVelDif, AMOEBA_MAX_HORIZONTAL_ACCELERATION)
|
||||
self.vHorizontalVelocity = self.vHorizontalVelocity + vVelDif * flVelDelta * dt
|
||||
local vNewPos = vOldPos + self.vHorizontalVelocity * dt
|
||||
me:SetOrigin(vNewPos)
|
||||
end
|
||||
function modifier_acid_blob_jump.prototype.UpdateVerticalMotion(self, me, dt)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self.flCurrentTimeVert = self.flCurrentTimeVert + dt
|
||||
local bGoingDown = -AMOEBA_ACCELERATION_Z * self.flCurrentTimeVert + self.flInitialVelocityZ < 0
|
||||
local vNewPos = me:GetOrigin()
|
||||
vNewPos.z = self.vStartPosition.z + (-0.5 * AMOEBA_ACCELERATION_Z * self.flCurrentTimeVert * self.flCurrentTimeVert + self.flInitialVelocityZ * self.flCurrentTimeVert)
|
||||
local flGroundHeight = GetGroundHeight(
|
||||
vNewPos,
|
||||
self:GetParent()
|
||||
)
|
||||
local bLanded = false
|
||||
if vNewPos.z < flGroundHeight and bGoingDown then
|
||||
vNewPos.z = flGroundHeight
|
||||
bLanded = true
|
||||
end
|
||||
me:SetOrigin(vNewPos)
|
||||
if bLanded then
|
||||
if not self.bHorizontalMotionInterrupted then
|
||||
local ability = self:GetAbility()
|
||||
if ability then
|
||||
ability:Smash()
|
||||
end
|
||||
end
|
||||
self:Destroy()
|
||||
end
|
||||
end
|
||||
function modifier_acid_blob_jump.prototype.OnHorizontalMotionInterrupted(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self.bHorizontalMotionInterrupted = true
|
||||
end
|
||||
function modifier_acid_blob_jump.prototype.OnVerticalMotionInterrupted(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self:Destroy()
|
||||
end
|
||||
function modifier_acid_blob_jump.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_OVERRIDE_ANIMATION}
|
||||
end
|
||||
function modifier_acid_blob_jump.prototype.GetOverrideAnimation(self)
|
||||
return ACT_DOTA_CAST_ABILITY_1
|
||||
end
|
||||
modifier_acid_blob_jump = __TS__Decorate(
|
||||
modifier_acid_blob_jump,
|
||||
modifier_acid_blob_jump,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_acid_blob_jump"}
|
||||
)
|
||||
____exports.modifier_acid_blob_jump = modifier_acid_blob_jump
|
||||
return ____exports
|
||||
@@ -0,0 +1,60 @@
|
||||
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
|
||||
____exports.ghost_evasive = __TS__Class()
|
||||
local ghost_evasive = ____exports.ghost_evasive
|
||||
ghost_evasive.name = "ghost_evasive"
|
||||
ghost_evasive.____file_path = "scripts/vscripts/abilities/creep/ghost_evasive.lua"
|
||||
__TS__ClassExtends(ghost_evasive, BaseAbility)
|
||||
function ghost_evasive.prototype.GetIntrinsicModifierName(self)
|
||||
return "modifier_ghost_evasive_passive"
|
||||
end
|
||||
ghost_evasive = __TS__Decorate(
|
||||
ghost_evasive,
|
||||
ghost_evasive,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ghost_evasive"}
|
||||
)
|
||||
____exports.ghost_evasive = ghost_evasive
|
||||
____exports.modifier_ghost_evasive_passive = __TS__Class()
|
||||
local modifier_ghost_evasive_passive = ____exports.modifier_ghost_evasive_passive
|
||||
modifier_ghost_evasive_passive.name = "modifier_ghost_evasive_passive"
|
||||
modifier_ghost_evasive_passive.____file_path = "scripts/vscripts/abilities/creep/ghost_evasive.lua"
|
||||
__TS__ClassExtends(modifier_ghost_evasive_passive, BaseModifier)
|
||||
function modifier_ghost_evasive_passive.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_ghost_evasive_passive.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_ghost_evasive_passive.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_ghost_evasive_passive.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_EVASION_CONSTANT}
|
||||
end
|
||||
function modifier_ghost_evasive_passive.prototype.GetModifierEvasion_Constant(self, event)
|
||||
local ab = self:GetAbility()
|
||||
if not ab then
|
||||
return 0
|
||||
end
|
||||
return ab:GetSpecialValueFor("evasive")
|
||||
end
|
||||
function modifier_ghost_evasive_passive.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_FLYING] = true, [MODIFIER_STATE_NO_UNIT_COLLISION] = true}
|
||||
end
|
||||
modifier_ghost_evasive_passive = __TS__Decorate(
|
||||
modifier_ghost_evasive_passive,
|
||||
modifier_ghost_evasive_passive,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_ghost_evasive_passive"}
|
||||
)
|
||||
____exports.modifier_ghost_evasive_passive = modifier_ghost_evasive_passive
|
||||
return ____exports
|
||||
@@ -0,0 +1,158 @@
|
||||
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
|
||||
____exports.kaban_rofl_ability = __TS__Class()
|
||||
local kaban_rofl_ability = ____exports.kaban_rofl_ability
|
||||
kaban_rofl_ability.name = "kaban_rofl_ability"
|
||||
kaban_rofl_ability.____file_path = "scripts/vscripts/abilities/creep/kaban_rofl_ability.lua"
|
||||
__TS__ClassExtends(kaban_rofl_ability, BaseAbility)
|
||||
function kaban_rofl_ability.prototype.GetIntrinsicModifierName(self)
|
||||
return "modifier_kaban_rofl_ability"
|
||||
end
|
||||
kaban_rofl_ability = __TS__Decorate(
|
||||
kaban_rofl_ability,
|
||||
kaban_rofl_ability,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "kaban_rofl_ability"}
|
||||
)
|
||||
____exports.kaban_rofl_ability = kaban_rofl_ability
|
||||
____exports.modifier_kaban_rofl_ability = __TS__Class()
|
||||
local modifier_kaban_rofl_ability = ____exports.modifier_kaban_rofl_ability
|
||||
modifier_kaban_rofl_ability.name = "modifier_kaban_rofl_ability"
|
||||
modifier_kaban_rofl_ability.____file_path = "scripts/vscripts/abilities/creep/kaban_rofl_ability.lua"
|
||||
__TS__ClassExtends(modifier_kaban_rofl_ability, BaseModifier)
|
||||
function modifier_kaban_rofl_ability.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_kaban_rofl_ability.prototype.OnCreated(self, params)
|
||||
Timers:CreateTimer(
|
||||
30,
|
||||
function()
|
||||
UTIL_Remove(self:GetParent())
|
||||
end
|
||||
)
|
||||
end
|
||||
function modifier_kaban_rofl_ability.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_EVENT_ON_TAKEDAMAGE, MODIFIER_PROPERTY_MIN_HEALTH}
|
||||
end
|
||||
function modifier_kaban_rofl_ability.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_NO_HEALTH_BAR] = true}
|
||||
end
|
||||
function modifier_kaban_rofl_ability.prototype.GetMinHealth(self)
|
||||
return 1
|
||||
end
|
||||
function modifier_kaban_rofl_ability.prototype.OnTakeDamage(self, event)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if event.attacker ~= self:GetParent():GetOwner() then
|
||||
return
|
||||
end
|
||||
if event.unit ~= self:GetParent() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local parentPos = parent:GetAbsOrigin()
|
||||
local attacker = event.attacker
|
||||
local hasEasterEgg = self:GetParent():HasModifier("modifier_easter_egg_dropped")
|
||||
if not hasEasterEgg and RandomFloat(0, 100) <= 2 then
|
||||
local easterEgg = CreateItem("item_easter_egg", nil, nil)
|
||||
if easterEgg then
|
||||
local physicalItem = CreateItemOnPositionForLaunch(parentPos, easterEgg)
|
||||
if physicalItem ~= nil then
|
||||
local randomAngle = RandomFloat(0, 2 * math.pi)
|
||||
local randomDistance = RandomFloat(100, 500)
|
||||
local randomX = parentPos.x + randomDistance * math.cos(randomAngle)
|
||||
local randomY = parentPos.y + randomDistance * math.sin(randomAngle)
|
||||
local randomPos = Vector(randomX, randomY, parentPos.z)
|
||||
local dropRadius = RandomFloat(50, 100)
|
||||
easterEgg:LaunchLootInitialHeight(
|
||||
false,
|
||||
0,
|
||||
150,
|
||||
0.5,
|
||||
randomPos + RandomVector(dropRadius)
|
||||
)
|
||||
self:GetParent():AddNewModifier(
|
||||
self:GetParent(),
|
||||
getModifierSourceAbility(
|
||||
nil,
|
||||
self:GetParent()
|
||||
),
|
||||
"modifier_easter_egg_dropped",
|
||||
{}
|
||||
)
|
||||
end
|
||||
end
|
||||
elseif RandomFloat(0, 100) <= 20 then
|
||||
local item = CreateItem("item_candy", nil, nil)
|
||||
if item then
|
||||
local physicalItem = CreateItemOnPositionForLaunch(parentPos, item)
|
||||
if physicalItem ~= nil then
|
||||
local randomAngle = RandomFloat(0, 2 * math.pi)
|
||||
local randomDistance = RandomFloat(100, 500)
|
||||
local randomX = parentPos.x + randomDistance * math.cos(randomAngle)
|
||||
local randomY = parentPos.y + randomDistance * math.sin(randomAngle)
|
||||
local randomPos = Vector(randomX, randomY, parentPos.z)
|
||||
local dropRadius = RandomFloat(50, 100)
|
||||
item:LaunchLootInitialHeight(
|
||||
false,
|
||||
0,
|
||||
150,
|
||||
0.5,
|
||||
randomPos + RandomVector(dropRadius)
|
||||
)
|
||||
Timers:CreateTimer(
|
||||
30,
|
||||
function()
|
||||
if IsValidEntity(physicalItem) then
|
||||
UTIL_Remove(physicalItem)
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
if attacker and attacker:IsAlive() then
|
||||
self:MakeUnitFlee(parent, attacker)
|
||||
end
|
||||
end
|
||||
function modifier_kaban_rofl_ability.prototype.MakeUnitFlee(self, fleeingUnit, fromUnit)
|
||||
local fleeingPos = fleeingUnit:GetAbsOrigin()
|
||||
local fromPos = fromUnit:GetAbsOrigin()
|
||||
local directionX = fleeingPos.x - fromPos.x
|
||||
local directionY = fleeingPos.y - fromPos.y
|
||||
local distance = math.sqrt(directionX * directionX + directionY * directionY)
|
||||
if distance > 0 then
|
||||
local normalizedX = directionX / distance
|
||||
local normalizedY = directionY / distance
|
||||
local fleeDistance = math.min(800, distance * 2)
|
||||
local fleeX = fleeingPos.x + normalizedX * fleeDistance
|
||||
local fleeY = fleeingPos.y + normalizedY * fleeDistance
|
||||
local fleePos = Vector(fleeX, fleeY, fleeingPos.z)
|
||||
local groundHeight = GetGroundHeight(fleePos, nil)
|
||||
local validPos = Vector(fleeX, fleeY, groundHeight)
|
||||
fleeingUnit:MoveToPosition(toVectorWS(nil, validPos))
|
||||
fleeingUnit:AddNewModifier(
|
||||
fleeingUnit,
|
||||
getModifierSourceAbility(nil, fleeingUnit),
|
||||
"modifier_phased",
|
||||
{duration = 2}
|
||||
)
|
||||
end
|
||||
end
|
||||
modifier_kaban_rofl_ability = __TS__Decorate(
|
||||
modifier_kaban_rofl_ability,
|
||||
modifier_kaban_rofl_ability,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_kaban_rofl_ability"}
|
||||
)
|
||||
____exports.modifier_kaban_rofl_ability = modifier_kaban_rofl_ability
|
||||
return ____exports
|
||||
@@ -0,0 +1,54 @@
|
||||
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 BaseModifier = ____dota_ts_adapter.BaseModifier
|
||||
local registerModifier = ____dota_ts_adapter.registerModifier
|
||||
____exports.modifier_boss_nevermore_coil_debuff = __TS__Class()
|
||||
local modifier_boss_nevermore_coil_debuff = ____exports.modifier_boss_nevermore_coil_debuff
|
||||
modifier_boss_nevermore_coil_debuff.name = "modifier_boss_nevermore_coil_debuff"
|
||||
modifier_boss_nevermore_coil_debuff.____file_path = "scripts/vscripts/abilities/creep/modifier_boss_nevermore_coil_debuff.lua"
|
||||
__TS__ClassExtends(modifier_boss_nevermore_coil_debuff, BaseModifier)
|
||||
function modifier_boss_nevermore_coil_debuff.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_boss_nevermore_coil_debuff.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
function modifier_boss_nevermore_coil_debuff.prototype.IsDebuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_boss_nevermore_coil_debuff.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE, MODIFIER_PROPERTY_TOOLTIP}
|
||||
end
|
||||
function modifier_boss_nevermore_coil_debuff.prototype.GetModifierMoveSpeedBonus_Percentage(self)
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return 0
|
||||
end
|
||||
local slowPerStack = ability:GetSpecialValueFor("coil_slow_per_stack")
|
||||
return -slowPerStack * self:GetStackCount()
|
||||
end
|
||||
function modifier_boss_nevermore_coil_debuff.prototype.OnTooltip(self)
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return 0
|
||||
end
|
||||
return ability:GetSpecialValueFor("coil_stack_bonus_damage") * self:GetStackCount()
|
||||
end
|
||||
function modifier_boss_nevermore_coil_debuff.prototype.GetEffectName(self)
|
||||
return "particles/units/heroes/hero_nevermore/nevermore_requiemofsouls_debuff.vpcf"
|
||||
end
|
||||
function modifier_boss_nevermore_coil_debuff.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_ABSORIGIN_FOLLOW
|
||||
end
|
||||
modifier_boss_nevermore_coil_debuff = __TS__Decorate(
|
||||
modifier_boss_nevermore_coil_debuff,
|
||||
modifier_boss_nevermore_coil_debuff,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_boss_nevermore_coil_debuff"}
|
||||
)
|
||||
____exports.modifier_boss_nevermore_coil_debuff = modifier_boss_nevermore_coil_debuff
|
||||
return ____exports
|
||||
@@ -0,0 +1,47 @@
|
||||
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 BaseModifier = ____dota_ts_adapter.BaseModifier
|
||||
local registerModifier = ____dota_ts_adapter.registerModifier
|
||||
--- Только для npc_boss_nevermore: без DEBUFF_IMMUNE на общем no_healthbar (Луна и др.),
|
||||
-- чтобы там не ломать диспелы. Боссу сайленс/орчид и прочие дебаффы с героев не цепляются.
|
||||
____exports.modifier_boss_nevermore_debuff_immune = __TS__Class()
|
||||
local modifier_boss_nevermore_debuff_immune = ____exports.modifier_boss_nevermore_debuff_immune
|
||||
modifier_boss_nevermore_debuff_immune.name = "modifier_boss_nevermore_debuff_immune"
|
||||
modifier_boss_nevermore_debuff_immune.____file_path = "scripts/vscripts/abilities/creep/modifier_boss_nevermore_debuff_immune.lua"
|
||||
__TS__ClassExtends(modifier_boss_nevermore_debuff_immune, BaseModifier)
|
||||
function modifier_boss_nevermore_debuff_immune.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_boss_nevermore_debuff_immune.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_boss_nevermore_debuff_immune.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_boss_nevermore_debuff_immune.prototype.GetPriority(self)
|
||||
return MODIFIER_PRIORITY_SUPER_ULTRA
|
||||
end
|
||||
function modifier_boss_nevermore_debuff_immune.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_SILENCED] = false, [MODIFIER_STATE_ROOTED] = false}
|
||||
end
|
||||
function modifier_boss_nevermore_debuff_immune.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_PROVIDES_FOW_POSITION}
|
||||
end
|
||||
function modifier_boss_nevermore_debuff_immune.prototype.providesFOWPosition(self)
|
||||
return true
|
||||
end
|
||||
function modifier_boss_nevermore_debuff_immune.prototype.GetModifierProvidesFOWVision(self)
|
||||
return 1
|
||||
end
|
||||
modifier_boss_nevermore_debuff_immune = __TS__Decorate(
|
||||
modifier_boss_nevermore_debuff_immune,
|
||||
modifier_boss_nevermore_debuff_immune,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_boss_nevermore_debuff_immune"}
|
||||
)
|
||||
____exports.modifier_boss_nevermore_debuff_immune = modifier_boss_nevermore_debuff_immune
|
||||
return ____exports
|
||||
@@ -0,0 +1,152 @@
|
||||
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 BaseModifier = ____dota_ts_adapter.BaseModifier
|
||||
local registerModifier = ____dota_ts_adapter.registerModifier
|
||||
local ____nevermore_boss_requiem_bridge = require("ai.nevermore_boss_requiem_bridge")
|
||||
local tryForceNevermoreRequiemCast = ____nevermore_boss_requiem_bridge.tryForceNevermoreRequiemCast
|
||||
local TERROR_WAVE_ABILITY = "terrorblade_terror_wave"
|
||||
--- Задержка между несколькими волнами, если за один удар пересечено несколько порогов HP.
|
||||
local MULTI_WAVE_STAGGER = 0.22
|
||||
--- После последней Terror Wave — пауза перед телепортом в центр и реквиемом.
|
||||
local AFTER_TERROR_WAVE_TO_REQUIEM = 0.72
|
||||
--- Фаза босса Nevermore по HP (как у coil_wave / ИИ).
|
||||
local function getNevermoreBossHpPhase(self, unit)
|
||||
local hp = unit:GetHealthPercent()
|
||||
if hp <= 25 then
|
||||
return 4
|
||||
end
|
||||
if hp <= 50 then
|
||||
return 3
|
||||
end
|
||||
if hp <= 75 then
|
||||
return 2
|
||||
end
|
||||
return 1
|
||||
end
|
||||
local function tryCastTerrorWave(self, boss)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if not boss or boss:IsNull() or not boss:IsAlive() then
|
||||
return
|
||||
end
|
||||
local ab = boss:FindAbilityByName(TERROR_WAVE_ABILITY)
|
||||
if not ab or ab:IsNull() then
|
||||
return
|
||||
end
|
||||
if ab:GetLevel() < 1 then
|
||||
ab:SetLevel(1)
|
||||
end
|
||||
ab:EndCooldown()
|
||||
ExecuteOrderFromTable({
|
||||
UnitIndex = boss:entindex(),
|
||||
OrderType = DOTA_UNIT_ORDER_CAST_NO_TARGET,
|
||||
AbilityIndex = ab:entindex()
|
||||
})
|
||||
end
|
||||
--- Вешается на npc_boss_nevermore: при каждом переходе на новую фазу по HP — Terror Wave.
|
||||
function ____exports.applyNevermorePhaseTerrorWave(self, boss)
|
||||
if not IsServer() or not boss or boss:IsNull() or not boss:IsAlive() then
|
||||
return
|
||||
end
|
||||
if boss:HasModifier(____exports.modifier_boss_nevermore_phase_terror_wave.name) then
|
||||
return
|
||||
end
|
||||
boss:AddNewModifier(
|
||||
boss,
|
||||
getModifierSourceAbility(nil, boss),
|
||||
____exports.modifier_boss_nevermore_phase_terror_wave.name,
|
||||
{}
|
||||
)
|
||||
end
|
||||
____exports.modifier_boss_nevermore_phase_terror_wave = __TS__Class()
|
||||
local modifier_boss_nevermore_phase_terror_wave = ____exports.modifier_boss_nevermore_phase_terror_wave
|
||||
modifier_boss_nevermore_phase_terror_wave.name = "modifier_boss_nevermore_phase_terror_wave"
|
||||
modifier_boss_nevermore_phase_terror_wave.____file_path = "scripts/vscripts/abilities/creep/modifier_boss_nevermore_phase_terror_wave.lua"
|
||||
__TS__ClassExtends(modifier_boss_nevermore_phase_terror_wave, BaseModifier)
|
||||
function modifier_boss_nevermore_phase_terror_wave.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.storedPhase = 1
|
||||
end
|
||||
function modifier_boss_nevermore_phase_terror_wave.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_boss_nevermore_phase_terror_wave.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_boss_nevermore_phase_terror_wave.prototype.RemoveOnDeath(self)
|
||||
return true
|
||||
end
|
||||
function modifier_boss_nevermore_phase_terror_wave.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self.parent = self:GetParent()
|
||||
self.storedPhase = getNevermoreBossHpPhase(nil, self.parent)
|
||||
self:StartIntervalThink(0.2)
|
||||
end
|
||||
function modifier_boss_nevermore_phase_terror_wave.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_EVENT_ON_TAKEDAMAGE}
|
||||
end
|
||||
function modifier_boss_nevermore_phase_terror_wave.prototype.OnTakeDamage(self)
|
||||
self:checkPhaseTransitions()
|
||||
end
|
||||
function modifier_boss_nevermore_phase_terror_wave.prototype.OnIntervalThink(self)
|
||||
if not IsValidEntity(self.parent) or not self.parent:IsAlive() then
|
||||
self:StartIntervalThink(-1)
|
||||
return
|
||||
end
|
||||
self:checkPhaseTransitions()
|
||||
end
|
||||
function modifier_boss_nevermore_phase_terror_wave.prototype.checkPhaseTransitions(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if not IsValidEntity(self.parent) or not self.parent:IsAlive() then
|
||||
return
|
||||
end
|
||||
local newPhase = getNevermoreBossHpPhase(nil, self.parent)
|
||||
if newPhase <= self.storedPhase then
|
||||
return
|
||||
end
|
||||
local wavesToFire = newPhase - self.storedPhase
|
||||
self.storedPhase = newPhase
|
||||
local entIndex = self.parent:entindex()
|
||||
do
|
||||
local i = 0
|
||||
while i < wavesToFire do
|
||||
local delay = i * MULTI_WAVE_STAGGER
|
||||
Timers:CreateTimer(
|
||||
delay,
|
||||
function()
|
||||
local u = EntIndexToHScript(entIndex)
|
||||
tryCastTerrorWave(nil, u)
|
||||
end
|
||||
)
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
local lastWaveDelay = wavesToFire > 0 and (wavesToFire - 1) * MULTI_WAVE_STAGGER or 0
|
||||
local requiemDelay = lastWaveDelay + AFTER_TERROR_WAVE_TO_REQUIEM
|
||||
Timers:CreateTimer(
|
||||
requiemDelay,
|
||||
function()
|
||||
local u = EntIndexToHScript(entIndex)
|
||||
if u and not u:IsNull() and u:IsAlive() then
|
||||
tryForceNevermoreRequiemCast(nil, u)
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
modifier_boss_nevermore_phase_terror_wave = __TS__Decorate(
|
||||
modifier_boss_nevermore_phase_terror_wave,
|
||||
modifier_boss_nevermore_phase_terror_wave,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_boss_nevermore_phase_terror_wave"}
|
||||
)
|
||||
____exports.modifier_boss_nevermore_phase_terror_wave = modifier_boss_nevermore_phase_terror_wave
|
||||
return ____exports
|
||||
@@ -0,0 +1,116 @@
|
||||
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 BaseModifier = ____dota_ts_adapter.BaseModifier
|
||||
local registerModifier = ____dota_ts_adapter.registerModifier
|
||||
local ____nevermore_boss_requiem_bridge = require("ai.nevermore_boss_requiem_bridge")
|
||||
local NEVERMORE_REQUIEM_DAMAGE_REDUCTION_PCT = ____nevermore_boss_requiem_bridge.NEVERMORE_REQUIEM_DAMAGE_REDUCTION_PCT
|
||||
local NEVERMORE_REQUIEM_HP_THRESHOLD = ____nevermore_boss_requiem_bridge.NEVERMORE_REQUIEM_HP_THRESHOLD
|
||||
local NEVERMORE_REQUIEM_REQUIRED_CASTS = ____nevermore_boss_requiem_bridge.NEVERMORE_REQUIEM_REQUIRED_CASTS
|
||||
local nevermoreGetRequiemCastCount = ____nevermore_boss_requiem_bridge.nevermoreGetRequiemCastCount
|
||||
local nevermoreNeedsMandatoryRequiem = ____nevermore_boss_requiem_bridge.nevermoreNeedsMandatoryRequiem
|
||||
local tryForceNevermoreRequiemCast = ____nevermore_boss_requiem_bridge.tryForceNevermoreRequiemCast
|
||||
--- Снижение входящего урона, пока HP < 50% и реквием не скастован 3 раза; дожим каста ульты.
|
||||
____exports.modifier_boss_nevermore_requiem_gate = __TS__Class()
|
||||
local modifier_boss_nevermore_requiem_gate = ____exports.modifier_boss_nevermore_requiem_gate
|
||||
modifier_boss_nevermore_requiem_gate.name = "modifier_boss_nevermore_requiem_gate"
|
||||
modifier_boss_nevermore_requiem_gate.____file_path = "scripts/vscripts/abilities/creep/modifier_boss_nevermore_requiem_gate.lua"
|
||||
__TS__ClassExtends(modifier_boss_nevermore_requiem_gate, BaseModifier)
|
||||
function modifier_boss_nevermore_requiem_gate.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.nextForceCastAt = 0
|
||||
end
|
||||
function modifier_boss_nevermore_requiem_gate.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_boss_nevermore_requiem_gate.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_boss_nevermore_requiem_gate.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_boss_nevermore_requiem_gate.prototype.RemoveOnDeath(self)
|
||||
return true
|
||||
end
|
||||
function modifier_boss_nevermore_requiem_gate.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self.parent = self:GetParent()
|
||||
self:syncStacks()
|
||||
self:StartIntervalThink(0.35)
|
||||
end
|
||||
function modifier_boss_nevermore_requiem_gate.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if not IsValidEntity(self.parent) or not self.parent:IsAlive() then
|
||||
self:StartIntervalThink(-1)
|
||||
return
|
||||
end
|
||||
self:syncStacks()
|
||||
if not nevermoreNeedsMandatoryRequiem(nil, self.parent) then
|
||||
return
|
||||
end
|
||||
local now = GameRules:GetGameTime()
|
||||
if now < self.nextForceCastAt then
|
||||
return
|
||||
end
|
||||
if self.parent:IsChanneling() then
|
||||
return
|
||||
end
|
||||
if tryForceNevermoreRequiemCast(nil, self.parent) then
|
||||
self.nextForceCastAt = now + 5
|
||||
else
|
||||
self.nextForceCastAt = now + 1.25
|
||||
end
|
||||
end
|
||||
function modifier_boss_nevermore_requiem_gate.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_INCOMING_DAMAGE_PERCENTAGE, MODIFIER_EVENT_ON_TAKEDAMAGE}
|
||||
end
|
||||
function modifier_boss_nevermore_requiem_gate.prototype.OnTakeDamage(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self:syncStacks()
|
||||
end
|
||||
function modifier_boss_nevermore_requiem_gate.prototype.GetModifierIncomingDamage_Percentage(self, event)
|
||||
if event.target ~= self.parent then
|
||||
return 0
|
||||
end
|
||||
if nevermoreGetRequiemCastCount(nil, self.parent) >= NEVERMORE_REQUIEM_REQUIRED_CASTS then
|
||||
return 0
|
||||
end
|
||||
if self.parent:GetHealthPercent() >= NEVERMORE_REQUIEM_HP_THRESHOLD then
|
||||
return 0
|
||||
end
|
||||
return -NEVERMORE_REQUIEM_DAMAGE_REDUCTION_PCT
|
||||
end
|
||||
function modifier_boss_nevermore_requiem_gate.prototype.syncStacks(self)
|
||||
self:SetStackCount(nevermoreGetRequiemCastCount(nil, self.parent))
|
||||
end
|
||||
modifier_boss_nevermore_requiem_gate = __TS__Decorate(
|
||||
modifier_boss_nevermore_requiem_gate,
|
||||
modifier_boss_nevermore_requiem_gate,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_boss_nevermore_requiem_gate"}
|
||||
)
|
||||
____exports.modifier_boss_nevermore_requiem_gate = modifier_boss_nevermore_requiem_gate
|
||||
function ____exports.applyNevermoreRequiemGate(self, boss)
|
||||
if not IsServer() or not boss or boss:IsNull() or not boss:IsAlive() then
|
||||
return
|
||||
end
|
||||
if boss:HasModifier(____exports.modifier_boss_nevermore_requiem_gate.name) then
|
||||
return
|
||||
end
|
||||
boss:AddNewModifier(
|
||||
boss,
|
||||
getModifierSourceAbility(nil, boss),
|
||||
____exports.modifier_boss_nevermore_requiem_gate.name,
|
||||
{}
|
||||
)
|
||||
end
|
||||
return ____exports
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
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 BaseModifier = ____dota_ts_adapter.BaseModifier
|
||||
local registerModifier = ____dota_ts_adapter.registerModifier
|
||||
____exports.modifier_boss_nevermore_requiem_magic_resist_debuff = __TS__Class()
|
||||
local modifier_boss_nevermore_requiem_magic_resist_debuff = ____exports.modifier_boss_nevermore_requiem_magic_resist_debuff
|
||||
modifier_boss_nevermore_requiem_magic_resist_debuff.name = "modifier_boss_nevermore_requiem_magic_resist_debuff"
|
||||
modifier_boss_nevermore_requiem_magic_resist_debuff.____file_path = "scripts/vscripts/abilities/creep/modifier_boss_nevermore_requiem_magic_resist_debuff.lua"
|
||||
__TS__ClassExtends(modifier_boss_nevermore_requiem_magic_resist_debuff, BaseModifier)
|
||||
function modifier_boss_nevermore_requiem_magic_resist_debuff.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_boss_nevermore_requiem_magic_resist_debuff.prototype.IsDebuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_boss_nevermore_requiem_magic_resist_debuff.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_boss_nevermore_requiem_magic_resist_debuff.prototype.RemoveOnDeath(self)
|
||||
return false
|
||||
end
|
||||
function modifier_boss_nevermore_requiem_magic_resist_debuff.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_MAGICAL_RESISTANCE_DECREPIFY_UNIQUE, MODIFIER_PROPERTY_TOOLTIP}
|
||||
end
|
||||
function modifier_boss_nevermore_requiem_magic_resist_debuff.prototype.GetModifierMagicalResistanceDecrepifyUnique(self)
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return 0
|
||||
end
|
||||
local reducePerStack = ability:GetSpecialValueFor("permanent_magic_resist_reduce_per_hit")
|
||||
return -reducePerStack * self:GetStackCount()
|
||||
end
|
||||
function modifier_boss_nevermore_requiem_magic_resist_debuff.prototype.OnTooltip(self)
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return 0
|
||||
end
|
||||
return -ability:GetSpecialValueFor("permanent_magic_resist_reduce_per_hit") * self:GetStackCount()
|
||||
end
|
||||
function modifier_boss_nevermore_requiem_magic_resist_debuff.prototype.GetEffectName(self)
|
||||
return "particles/items4_fx/nullifier_mute_debuff.vpcf"
|
||||
end
|
||||
function modifier_boss_nevermore_requiem_magic_resist_debuff.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_ABSORIGIN_FOLLOW
|
||||
end
|
||||
modifier_boss_nevermore_requiem_magic_resist_debuff = __TS__Decorate(
|
||||
modifier_boss_nevermore_requiem_magic_resist_debuff,
|
||||
modifier_boss_nevermore_requiem_magic_resist_debuff,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_boss_nevermore_requiem_magic_resist_debuff"}
|
||||
)
|
||||
____exports.modifier_boss_nevermore_requiem_magic_resist_debuff = modifier_boss_nevermore_requiem_magic_resist_debuff
|
||||
return ____exports
|
||||
@@ -0,0 +1,242 @@
|
||||
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 registerModifier = ____dota_ts_adapter.registerModifier
|
||||
local ____dota_ts_adapter = require("lib.dota_ts_adapter")
|
||||
local registerAbility = ____dota_ts_adapter.registerAbility
|
||||
____exports.sheep_coil = __TS__Class()
|
||||
local sheep_coil = ____exports.sheep_coil
|
||||
sheep_coil.name = "sheep_coil"
|
||||
sheep_coil.____file_path = "scripts/vscripts/abilities/creep/sheep_coil.lua"
|
||||
__TS__ClassExtends(sheep_coil, BaseAbility)
|
||||
function sheep_coil.prototype.GetAOERadius(self)
|
||||
return self:GetSpecialValueFor("radius")
|
||||
end
|
||||
function sheep_coil.prototype.OnSpellStart(self)
|
||||
local caster = self:GetCaster()
|
||||
local cursorPt = self:GetCursorPosition()
|
||||
local targetPoint = GetGroundPosition(cursorPt, nil)
|
||||
local spawnOrigin
|
||||
do
|
||||
local function ____catch()
|
||||
spawnOrigin = caster:GetAbsOrigin()
|
||||
end
|
||||
local ____try = pcall(function()
|
||||
local attachId = caster:ScriptLookupAttachment("attach_attack1")
|
||||
if attachId ~= nil and attachId >= 0 then
|
||||
spawnOrigin = caster:GetAttachmentOrigin(attachId)
|
||||
else
|
||||
spawnOrigin = caster:GetAbsOrigin()
|
||||
end
|
||||
end)
|
||||
if not ____try then
|
||||
____catch()
|
||||
end
|
||||
end
|
||||
local projectileSpeed = self:GetSpecialValueFor("projectile_speed")
|
||||
local direction = targetPoint - spawnOrigin
|
||||
direction.z = 0
|
||||
local directionLength = math.sqrt(direction.x * direction.x + direction.y * direction.y)
|
||||
if directionLength < 0.01 then
|
||||
return
|
||||
end
|
||||
direction = direction:Normalized()
|
||||
local distance = directionLength
|
||||
local particle = ParticleManager:CreateParticle("particles/units/heroes/hero_abaddon/abaddon_death_coil.vpcf", PATTACH_CUSTOMORIGIN, nil)
|
||||
ParticleManager:SetParticleControl(particle, 0, spawnOrigin)
|
||||
ParticleManager:SetParticleControl(particle, 1, targetPoint)
|
||||
ParticleManager:SetParticleControl(
|
||||
particle,
|
||||
2,
|
||||
Vector(projectileSpeed, 0, 0)
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
particle,
|
||||
3,
|
||||
Vector(distance, 0, 0)
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
particle,
|
||||
4,
|
||||
Vector(0, 0, 0)
|
||||
)
|
||||
ParticleManager:SetParticleControl(particle, 9, spawnOrigin)
|
||||
ParticleManager:SetParticleControl(particle, 13, direction)
|
||||
self.projectile_particle = particle
|
||||
self.projectile_start_pos = spawnOrigin
|
||||
self.projectile_target_pos = targetPoint
|
||||
self.projectile_direction = direction
|
||||
self.projectile_speed = projectileSpeed
|
||||
self.projectile_distance = distance
|
||||
local info = {
|
||||
Ability = self,
|
||||
EffectName = "",
|
||||
vSpawnOrigin = spawnOrigin,
|
||||
fDistance = distance,
|
||||
fStartRadius = 0,
|
||||
fEndRadius = 0,
|
||||
Source = caster,
|
||||
bHasFrontalCone = false,
|
||||
bReplaceExisting = false,
|
||||
iUnitTargetTeam = DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
iUnitTargetFlags = DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
iUnitTargetType = bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC),
|
||||
fExpireTime = GameRules:GetGameTime() + 10,
|
||||
bDeleteOnHit = true,
|
||||
vVelocity = direction * projectileSpeed,
|
||||
iMoveSpeed = projectileSpeed,
|
||||
bProvidesVision = true,
|
||||
iVisionRadius = 200,
|
||||
iVisionTeamNumber = caster:GetTeamNumber()
|
||||
}
|
||||
ProjectileManager:CreateLinearProjectile(info)
|
||||
Timers:CreateTimer(
|
||||
distance / projectileSpeed + 0.1,
|
||||
function()
|
||||
if self.projectile_particle ~= nil then
|
||||
ParticleManager:DestroyParticle(self.projectile_particle, false)
|
||||
ParticleManager:ReleaseParticleIndex(self.projectile_particle)
|
||||
self.projectile_particle = nil
|
||||
end
|
||||
return nil
|
||||
end
|
||||
)
|
||||
self.target_point = targetPoint
|
||||
local travelTime = distance / projectileSpeed
|
||||
Timers:CreateTimer(
|
||||
travelTime,
|
||||
function()
|
||||
if not self.projectile_hit then
|
||||
self:OnProjectileHit(nil, targetPoint)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
)
|
||||
local effectCast = ParticleManager:CreateParticle("particles/units/heroes/hero_abaddon/abaddon_death_coil_abaddon.vpcf", PATTACH_ABSORIGIN_FOLLOW, caster)
|
||||
ParticleManager:ReleaseParticleIndex(effectCast)
|
||||
EmitSoundOn("Hero_Abaddon.DeathCoil.Cast", caster)
|
||||
end
|
||||
function sheep_coil.prototype.OnProjectileThink(self, location)
|
||||
if self.projectile_particle ~= nil then
|
||||
local currentPos = GetGroundPosition(location, nil)
|
||||
ParticleManager:SetParticleControl(self.projectile_particle, 0, currentPos)
|
||||
ParticleManager:SetParticleControl(self.projectile_particle, 9, currentPos)
|
||||
local startPos = self.projectile_start_pos
|
||||
local targetPos = self.projectile_target_pos
|
||||
local totalDist = self.projectile_distance
|
||||
local currentDist = math.sqrt((currentPos.x - startPos.x) * (currentPos.x - startPos.x) + (currentPos.y - startPos.y) * (currentPos.y - startPos.y))
|
||||
local progress = math.min(currentDist / totalDist, 1)
|
||||
ParticleManager:SetParticleControl(
|
||||
self.projectile_particle,
|
||||
3,
|
||||
Vector(totalDist * (1 - progress), 0, 0)
|
||||
)
|
||||
end
|
||||
end
|
||||
function sheep_coil.prototype.OnProjectileHit(self, target, location)
|
||||
self.projectile_hit = true
|
||||
if self.projectile_particle ~= nil then
|
||||
ParticleManager:DestroyParticle(self.projectile_particle, false)
|
||||
ParticleManager:ReleaseParticleIndex(self.projectile_particle)
|
||||
self.projectile_particle = nil
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local slow_duration = self:GetSpecialValueFor("slow_duration")
|
||||
local radius = self:GetSpecialValueFor("radius")
|
||||
local hitPoint = self.target_point or location
|
||||
local units = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
hitPoint,
|
||||
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
|
||||
)
|
||||
for ____, unit in ipairs(units) do
|
||||
ApplyDamage({
|
||||
victim = unit,
|
||||
attacker = caster,
|
||||
damage = caster:GetAttackDamage(),
|
||||
damage_type = DAMAGE_TYPE_MAGICAL,
|
||||
ability = self
|
||||
})
|
||||
local existingModifier = unit:FindModifierByName("modifier_sheep_coil_slow")
|
||||
if existingModifier then
|
||||
existingModifier:IncrementStackCount()
|
||||
existingModifier:SetDuration(slow_duration, true)
|
||||
else
|
||||
local modifier = unit:AddNewModifier(caster, self, "modifier_sheep_coil_slow", {duration = slow_duration})
|
||||
if modifier ~= nil then
|
||||
modifier:SetStackCount(1)
|
||||
end
|
||||
end
|
||||
EmitSoundOn("Hero_Abaddon.DeathCoil.Target", unit)
|
||||
end
|
||||
local particle = ParticleManager:CreateParticle("particles/units/heroes/hero_abaddon/abaddon_death_coil_explosion.vpcf", PATTACH_CUSTOMORIGIN, nil)
|
||||
ParticleManager:SetParticleControl(particle, 0, hitPoint)
|
||||
ParticleManager:SetParticleControl(
|
||||
particle,
|
||||
1,
|
||||
Vector(radius, 0, 0)
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(particle)
|
||||
return true
|
||||
end
|
||||
function sheep_coil.prototype.GetAbilityTextureName(self)
|
||||
return "abaddon_death_coil"
|
||||
end
|
||||
sheep_coil = __TS__Decorate(
|
||||
sheep_coil,
|
||||
sheep_coil,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "sheep_coil"}
|
||||
)
|
||||
____exports.sheep_coil = sheep_coil
|
||||
____exports.modifier_sheep_coil_slow = __TS__Class()
|
||||
local modifier_sheep_coil_slow = ____exports.modifier_sheep_coil_slow
|
||||
modifier_sheep_coil_slow.name = "modifier_sheep_coil_slow"
|
||||
modifier_sheep_coil_slow.____file_path = "scripts/vscripts/abilities/creep/sheep_coil.lua"
|
||||
__TS__ClassExtends(modifier_sheep_coil_slow, BaseModifier)
|
||||
function modifier_sheep_coil_slow.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE}
|
||||
end
|
||||
function modifier_sheep_coil_slow.prototype.GetModifierMoveSpeedBonus_Percentage(self)
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return 0
|
||||
end
|
||||
return ability:GetSpecialValueFor("movement_slow") * self:GetStackCount()
|
||||
end
|
||||
function modifier_sheep_coil_slow.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
end
|
||||
function modifier_sheep_coil_slow.prototype.GetEffectName(self)
|
||||
return "particles/items4_fx/nullifier_slow.vpcf"
|
||||
end
|
||||
function modifier_sheep_coil_slow.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_ABSORIGIN_FOLLOW
|
||||
end
|
||||
function modifier_sheep_coil_slow.prototype.RemoveOnDeath(self)
|
||||
return true
|
||||
end
|
||||
function modifier_sheep_coil_slow.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
modifier_sheep_coil_slow = __TS__Decorate(
|
||||
modifier_sheep_coil_slow,
|
||||
modifier_sheep_coil_slow,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_sheep_coil_slow"}
|
||||
)
|
||||
____exports.modifier_sheep_coil_slow = modifier_sheep_coil_slow
|
||||
return ____exports
|
||||
@@ -0,0 +1,91 @@
|
||||
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 registerModifier = ____dota_ts_adapter.registerModifier
|
||||
local ____dota_ts_adapter = require("lib.dota_ts_adapter")
|
||||
local registerAbility = ____dota_ts_adapter.registerAbility
|
||||
____exports.skeleton_archer_fire_arrow = __TS__Class()
|
||||
local skeleton_archer_fire_arrow = ____exports.skeleton_archer_fire_arrow
|
||||
skeleton_archer_fire_arrow.name = "skeleton_archer_fire_arrow"
|
||||
skeleton_archer_fire_arrow.____file_path = "scripts/vscripts/abilities/creep/skeleton_archer.lua"
|
||||
__TS__ClassExtends(skeleton_archer_fire_arrow, BaseAbility)
|
||||
function skeleton_archer_fire_arrow.prototype.GetIntrinsicModifierName(self)
|
||||
return "modifier_skeleton_archer_fire_arrow"
|
||||
end
|
||||
skeleton_archer_fire_arrow = __TS__Decorate(
|
||||
skeleton_archer_fire_arrow,
|
||||
skeleton_archer_fire_arrow,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "skeleton_archer_fire_arrow"}
|
||||
)
|
||||
____exports.skeleton_archer_fire_arrow = skeleton_archer_fire_arrow
|
||||
____exports.modifier_skeleton_archer_fire_arrow = __TS__Class()
|
||||
local modifier_skeleton_archer_fire_arrow = ____exports.modifier_skeleton_archer_fire_arrow
|
||||
modifier_skeleton_archer_fire_arrow.name = "modifier_skeleton_archer_fire_arrow"
|
||||
modifier_skeleton_archer_fire_arrow.____file_path = "scripts/vscripts/abilities/creep/skeleton_archer.lua"
|
||||
__TS__ClassExtends(modifier_skeleton_archer_fire_arrow, BaseModifier)
|
||||
function modifier_skeleton_archer_fire_arrow.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.bonusDamage = 0
|
||||
end
|
||||
function modifier_skeleton_archer_fire_arrow.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_skeleton_archer_fire_arrow.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_skeleton_archer_fire_arrow.prototype.OnCreated(self, params)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if not self:GetAbility() then
|
||||
return
|
||||
end
|
||||
self.bonusDamage = self:GetAbility():GetSpecialValueFor("bonus_damage")
|
||||
end
|
||||
function modifier_skeleton_archer_fire_arrow.prototype.OnRefresh(self, params)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if not self:GetAbility() then
|
||||
return
|
||||
end
|
||||
self.bonusDamage = self:GetAbility():GetSpecialValueFor("bonus_damage")
|
||||
end
|
||||
function modifier_skeleton_archer_fire_arrow.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_EVENT_ON_ATTACK_LANDED}
|
||||
end
|
||||
function modifier_skeleton_archer_fire_arrow.prototype.OnAttackLanded(self, event)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if event.attacker ~= self:GetParent() then
|
||||
return
|
||||
end
|
||||
if not event.target then
|
||||
return
|
||||
end
|
||||
if not event.target:IsAlive() then
|
||||
return
|
||||
end
|
||||
ApplyDamage({
|
||||
victim = event.target,
|
||||
attacker = event.attacker,
|
||||
damage = self.bonusDamage,
|
||||
damage_type = DAMAGE_TYPE_MAGICAL,
|
||||
ability = self:GetAbility()
|
||||
})
|
||||
end
|
||||
modifier_skeleton_archer_fire_arrow = __TS__Decorate(
|
||||
modifier_skeleton_archer_fire_arrow,
|
||||
modifier_skeleton_archer_fire_arrow,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_skeleton_archer_fire_arrow"}
|
||||
)
|
||||
____exports.modifier_skeleton_archer_fire_arrow = modifier_skeleton_archer_fire_arrow
|
||||
return ____exports
|
||||
@@ -0,0 +1,123 @@
|
||||
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 registerAbility = ____dota_ts_adapter.registerAbility
|
||||
____exports.thief_arrow = __TS__Class()
|
||||
local thief_arrow = ____exports.thief_arrow
|
||||
thief_arrow.name = "thief_arrow"
|
||||
thief_arrow.____file_path = "scripts/vscripts/abilities/creep/thief_arrow.lua"
|
||||
__TS__ClassExtends(thief_arrow, BaseAbility)
|
||||
function thief_arrow.prototype.Precache(self, context)
|
||||
PrecacheResource("soundfile", "sounds/units/heroes/mirana/arrow.vsnd", context)
|
||||
PrecacheResource("soundfile", "sounds/units/heroes/mirana/arrow_target.vsnd", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_phantom_assassin/phantom_assassin_crit_impact.vpcf", context)
|
||||
end
|
||||
function thief_arrow.prototype.OnAbilityPhaseStart(self)
|
||||
if IsServer() then
|
||||
self:GetCaster():StartGestureWithPlaybackRate(ACT_DOTA_ATTACK, 0.5)
|
||||
self.preParticle = ParticleManager:CreateParticle(
|
||||
"particles/darkmoon_creep_warning.vpcf",
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
self:GetCaster()
|
||||
)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
self.preParticle,
|
||||
0,
|
||||
self:GetCaster(),
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
"",
|
||||
self:GetCaster():GetOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
self.preParticle,
|
||||
1,
|
||||
Vector(100, 100, 100)
|
||||
)
|
||||
end
|
||||
return true
|
||||
end
|
||||
function thief_arrow.prototype.OnAbilityPhaseInterrupted(self)
|
||||
if IsClient() or not self.preParticle then
|
||||
return
|
||||
end
|
||||
self:GetCaster():FadeGesture(ACT_DOTA_ATTACK)
|
||||
ParticleManager:DestroyParticle(self.preParticle, false)
|
||||
end
|
||||
function thief_arrow.prototype.OnSpellStart(self)
|
||||
if self.preParticle then
|
||||
ParticleManager:DestroyParticle(self.preParticle, false)
|
||||
end
|
||||
self:GetCaster():FadeGesture(ACT_DOTA_ATTACK)
|
||||
local caster = self:GetCaster()
|
||||
local origin = caster:GetOrigin()
|
||||
local point = self:GetCursorPosition()
|
||||
local projectile_speed = self:GetSpecialValueFor("arrow_speed")
|
||||
local projectile_distance = self:GetSpecialValueFor("arrow_range")
|
||||
local projectile_start_radius = self:GetSpecialValueFor("arrow_width")
|
||||
local projectile_end_radius = self:GetSpecialValueFor("arrow_width")
|
||||
local projectile_direction = Vector(point.x - origin.x, point.y - origin.y, 0):Normalized()
|
||||
ProjectileManager:CreateLinearProjectile({
|
||||
Source = caster,
|
||||
Ability = self,
|
||||
vSpawnOrigin = caster:GetOrigin(),
|
||||
iUnitTargetTeam = DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
iUnitTargetFlags = DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
iUnitTargetType = bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC),
|
||||
EffectName = "particles/units/heroes/hero_mirana/mirana_spell_arrow.vpcf",
|
||||
fDistance = projectile_distance,
|
||||
fStartRadius = projectile_start_radius,
|
||||
fEndRadius = projectile_end_radius,
|
||||
vVelocity = projectile_direction * projectile_speed,
|
||||
bHasFrontalCone = false,
|
||||
fExpireTime = GameRules:GetGameTime() + 10
|
||||
})
|
||||
self:EmitSound("Hero_Mirana.ArrowCast")
|
||||
end
|
||||
function thief_arrow.prototype.OnProjectileHit_ExtraData(self, target, location)
|
||||
if not target then
|
||||
return
|
||||
end
|
||||
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,
|
||||
(self:GetCaster():GetOrigin() - target:GetOrigin()):Normalized()
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(effect_cast)
|
||||
ApplyDamage({
|
||||
victim = target,
|
||||
attacker = self:GetCaster(),
|
||||
damage = self:GetCaster():GetAttackDamage(),
|
||||
damage_type = DAMAGE_TYPE_PHYSICAL,
|
||||
ability = self
|
||||
})
|
||||
EmitSoundOn("Hero_Mirana.ArrowImpact", target)
|
||||
return true
|
||||
end
|
||||
thief_arrow = __TS__Decorate(
|
||||
thief_arrow,
|
||||
thief_arrow,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "thief_arrow"}
|
||||
)
|
||||
____exports.thief_arrow = thief_arrow
|
||||
return ____exports
|
||||
@@ -0,0 +1,844 @@
|
||||
local ____lualib = require("lualib_bundle")
|
||||
local __TS__NumberIsFinite = ____lualib.__TS__NumberIsFinite
|
||||
local __TS__ArraySort = ____lualib.__TS__ArraySort
|
||||
local Map = ____lualib.Map
|
||||
local __TS__New = ____lualib.__TS__New
|
||||
local __TS__ArrayFilter = ____lualib.__TS__ArrayFilter
|
||||
local __TS__Class = ____lualib.__TS__Class
|
||||
local __TS__ClassExtends = ____lualib.__TS__ClassExtends
|
||||
local __TS__Decorate = ____lualib.__TS__Decorate
|
||||
local ____exports = {}
|
||||
local clampToxinPoolRadius, toxinIsValidUnit, isToxinPoolThinker, toxinIsValidAbility, toxinPoolsOverlap, getToxinPoolRadiusForThinker, getToxinPoolMergeStackForThinker, getToxinDamagePerTickForThinker, getToxinPoolRemainingDuration, toxinDistSqHoriz, buildToxinOverlapCluster, computeMergedPoolStats, toxinSortDedupeEntIndices, toxinEnqueuePoolMergeForThinker, toxinDrainToxinMergeQueueFrame, toxinTryResolvePoolMergeForThinkerIndex, TOXIN_POOL_MODIFIER_NAME, TOXIN_MERGE_SCAN_RADIUS, TOXIN_MERGE_OVERLAP_EPSILON, TOXIN_THINKER_CLASS, TOXIN_POOL_MAX_RADIUS, TOXIN_MAX_MERGE_SCAN_THINKERS, toxinMergeQueue, toxinMergeDrainScheduled, toxinIsDrainingMerge
|
||||
local ____difficulty_manager = require("difficulty_manager")
|
||||
local Difficulty = ____difficulty_manager.Difficulty
|
||||
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 ____creep_render_color = require("utils.creep_render_color")
|
||||
local trySetIntrinsicCreepRenderColor = ____creep_render_color.trySetIntrinsicCreepRenderColor
|
||||
local ____entity_radius = require("utils.entity_radius")
|
||||
local findAllByClassnameInRadius = ____entity_radius.findAllByClassnameInRadius
|
||||
function clampToxinPoolRadius(self, r)
|
||||
return math.max(
|
||||
1,
|
||||
math.min(r, TOXIN_POOL_MAX_RADIUS)
|
||||
)
|
||||
end
|
||||
function toxinIsValidUnit(self, unit)
|
||||
return unit ~= nil and unit ~= nil and not unit:IsNull() and IsValidEntity(unit)
|
||||
end
|
||||
function isToxinPoolThinker(self, unit)
|
||||
return toxinIsValidUnit(nil, unit) and unit:GetClassname() == TOXIN_THINKER_CLASS
|
||||
end
|
||||
function toxinIsValidAbility(self, ab)
|
||||
return ab ~= nil and ab ~= nil and not ab:IsNull() and IsValidEntity(ab)
|
||||
end
|
||||
function toxinPoolsOverlap(self, centerA, radiusA, centerB, radiusB)
|
||||
local dx = centerA.x - centerB.x
|
||||
local dy = centerA.y - centerB.y
|
||||
local dist = math.sqrt(dx * dx + dy * dy)
|
||||
return dist <= radiusA + radiusB + TOXIN_MERGE_OVERLAP_EPSILON
|
||||
end
|
||||
function getToxinPoolRadiusForThinker(self, thinker, poolMod, ability)
|
||||
if not toxinIsValidAbility(nil, ability) then
|
||||
return 0
|
||||
end
|
||||
local raw
|
||||
if poolMod and poolMod.effectiveRadius > 0 then
|
||||
raw = poolMod.effectiveRadius
|
||||
else
|
||||
raw = ability:GetSpecialValueFor("radius")
|
||||
end
|
||||
return clampToxinPoolRadius(nil, raw)
|
||||
end
|
||||
function getToxinPoolMergeStackForThinker(self, poolMod, _ability)
|
||||
if poolMod ~= nil and poolMod.poolMergeStackCount > 0 then
|
||||
return math.floor(poolMod.poolMergeStackCount)
|
||||
end
|
||||
return 1
|
||||
end
|
||||
function getToxinDamagePerTickForThinker(self, poolMod, ability)
|
||||
if not toxinIsValidAbility(nil, ability) then
|
||||
return 0
|
||||
end
|
||||
if poolMod and poolMod.damagePerTick > 0 then
|
||||
return poolMod.damagePerTick
|
||||
end
|
||||
return ability:GetSpecialValueFor("damage") * 0.33
|
||||
end
|
||||
function getToxinPoolRemainingDuration(self, poolMod, fallbackDuration)
|
||||
local getter = poolMod.GetRemainingTime
|
||||
if getter ~= nil then
|
||||
local v = getter(poolMod)
|
||||
if type(v) == "number" and __TS__NumberIsFinite(v) then
|
||||
return math.max(0, v)
|
||||
end
|
||||
end
|
||||
return math.max(0, fallbackDuration)
|
||||
end
|
||||
function toxinDistSqHoriz(self, ax, bx)
|
||||
local dx = ax.x - bx.x
|
||||
local dy = ax.y - bx.y
|
||||
return dx * dx + dy * dy
|
||||
end
|
||||
function buildToxinOverlapCluster(self, seed, ability)
|
||||
if not toxinIsValidUnit(nil, seed) or not toxinIsValidAbility(nil, ability) then
|
||||
return {}
|
||||
end
|
||||
local seedTeam = seed:GetTeamNumber()
|
||||
local origin = seed:GetAbsOrigin()
|
||||
local raw = findAllByClassnameInRadius("npc_dota_thinker", origin, TOXIN_MERGE_SCAN_RADIUS)
|
||||
local toxinThinkers = {}
|
||||
for ____, ent in ipairs(raw) do
|
||||
do
|
||||
local npc = ent
|
||||
if not toxinIsValidUnit(nil, npc) then
|
||||
goto __continue22
|
||||
end
|
||||
if npc:GetTeamNumber() ~= seedTeam then
|
||||
goto __continue22
|
||||
end
|
||||
if not npc:FindModifierByName(TOXIN_POOL_MODIFIER_NAME) then
|
||||
goto __continue22
|
||||
end
|
||||
toxinThinkers[#toxinThinkers + 1] = npc
|
||||
end
|
||||
::__continue22::
|
||||
end
|
||||
if #toxinThinkers > TOXIN_MAX_MERGE_SCAN_THINKERS then
|
||||
__TS__ArraySort(
|
||||
toxinThinkers,
|
||||
function(____, a, b) return toxinDistSqHoriz(
|
||||
nil,
|
||||
a:GetAbsOrigin(),
|
||||
origin
|
||||
) - toxinDistSqHoriz(
|
||||
nil,
|
||||
b:GetAbsOrigin(),
|
||||
origin
|
||||
) end
|
||||
)
|
||||
while #toxinThinkers > TOXIN_MAX_MERGE_SCAN_THINKERS do
|
||||
table.remove(toxinThinkers)
|
||||
end
|
||||
end
|
||||
local cluster = {}
|
||||
local visited = __TS__New(Map)
|
||||
local queue = {seed}
|
||||
while #queue > 0 do
|
||||
do
|
||||
local cur = table.remove(queue)
|
||||
if not toxinIsValidUnit(nil, cur) then
|
||||
goto __continue30
|
||||
end
|
||||
local idx = cur:GetEntityIndex()
|
||||
if visited:get(idx) then
|
||||
goto __continue30
|
||||
end
|
||||
visited:set(idx, true)
|
||||
cluster[#cluster + 1] = cur
|
||||
local curMod = cur:FindModifierByName(TOXIN_POOL_MODIFIER_NAME)
|
||||
local rCur = getToxinPoolRadiusForThinker(nil, cur, curMod, ability)
|
||||
local pCur = cur:GetAbsOrigin()
|
||||
for ____, other in ipairs(toxinThinkers) do
|
||||
do
|
||||
if not toxinIsValidUnit(nil, other) then
|
||||
goto __continue33
|
||||
end
|
||||
local oIdx = other:GetEntityIndex()
|
||||
if visited:get(oIdx) then
|
||||
goto __continue33
|
||||
end
|
||||
local oMod = other:FindModifierByName(TOXIN_POOL_MODIFIER_NAME)
|
||||
local rO = getToxinPoolRadiusForThinker(nil, other, oMod, ability)
|
||||
if toxinPoolsOverlap(
|
||||
nil,
|
||||
pCur,
|
||||
rCur,
|
||||
other:GetAbsOrigin(),
|
||||
rO
|
||||
) then
|
||||
queue[#queue + 1] = other
|
||||
end
|
||||
end
|
||||
::__continue33::
|
||||
end
|
||||
end
|
||||
::__continue30::
|
||||
end
|
||||
return cluster
|
||||
end
|
||||
function computeMergedPoolStats(self, cluster, ability)
|
||||
if not toxinIsValidAbility(nil, ability) then
|
||||
return {
|
||||
centroid = Vector(0, 0, 0),
|
||||
damagePerTick = 0,
|
||||
radius = 1,
|
||||
duration = 0.05,
|
||||
mergeStackCount = 1
|
||||
}
|
||||
end
|
||||
local valid = __TS__ArrayFilter(
|
||||
cluster,
|
||||
function(____, t) return toxinIsValidUnit(nil, t) end
|
||||
)
|
||||
local n = #valid
|
||||
if n <= 0 then
|
||||
local baseRadius = ability:GetSpecialValueFor("radius")
|
||||
local baseDur = ability:GetSpecialValueFor("duration")
|
||||
return {
|
||||
centroid = Vector(0, 0, 0),
|
||||
damagePerTick = ability:GetSpecialValueFor("damage") * 0.33,
|
||||
radius = clampToxinPoolRadius(nil, baseRadius),
|
||||
duration = math.max(0.05, baseDur),
|
||||
mergeStackCount = 1
|
||||
}
|
||||
end
|
||||
local baseDuration = ability:GetSpecialValueFor("duration")
|
||||
local radiusBonusPerMerge = math.max(
|
||||
0,
|
||||
ability:GetSpecialValueFor("merge_radius_bonus")
|
||||
)
|
||||
local durationBonusPerMerge = math.max(
|
||||
0,
|
||||
ability:GetSpecialValueFor("merge_duration_bonus")
|
||||
)
|
||||
local sumR2 = 0
|
||||
local sumDmg = 0
|
||||
local sumStacks = 0
|
||||
local maxRem = 0
|
||||
local sx = 0
|
||||
local sy = 0
|
||||
local sz = 0
|
||||
for ____, t in ipairs(valid) do
|
||||
local mod = t:FindModifierByName(TOXIN_POOL_MODIFIER_NAME)
|
||||
local r = getToxinPoolRadiusForThinker(nil, t, mod, ability)
|
||||
sumR2 = sumR2 + r * r
|
||||
sumDmg = sumDmg + getToxinDamagePerTickForThinker(nil, mod, ability)
|
||||
sumStacks = sumStacks + getToxinPoolMergeStackForThinker(nil, mod, ability)
|
||||
local p = t:GetAbsOrigin()
|
||||
sx = sx + p.x
|
||||
sy = sy + p.y
|
||||
sz = sz + p.z
|
||||
if mod then
|
||||
maxRem = math.max(
|
||||
maxRem,
|
||||
getToxinPoolRemainingDuration(nil, mod, baseDuration)
|
||||
)
|
||||
else
|
||||
maxRem = math.max(maxRem, baseDuration)
|
||||
end
|
||||
end
|
||||
local mergedRadiusUncapped = math.max(
|
||||
1,
|
||||
math.sqrt(sumR2) + math.max(0, n - 1) * radiusBonusPerMerge
|
||||
)
|
||||
local mergedRadius = clampToxinPoolRadius(nil, mergedRadiusUncapped)
|
||||
local mergedDuration = math.max(
|
||||
0.05,
|
||||
maxRem + math.max(0, n - 1) * durationBonusPerMerge
|
||||
)
|
||||
return {
|
||||
centroid = Vector(sx / n, sy / n, sz / n),
|
||||
damagePerTick = sumDmg,
|
||||
radius = mergedRadius,
|
||||
duration = mergedDuration,
|
||||
mergeStackCount = math.max(1, sumStacks)
|
||||
}
|
||||
end
|
||||
function toxinSortDedupeEntIndices(self, arr)
|
||||
if #arr <= 1 then
|
||||
return arr
|
||||
end
|
||||
local sorted = {unpack(arr)}
|
||||
__TS__ArraySort(
|
||||
sorted,
|
||||
function(____, a, b) return a - b end
|
||||
)
|
||||
local out = {}
|
||||
local prev = -2147483648
|
||||
for ____, x in ipairs(sorted) do
|
||||
local n = x
|
||||
if n ~= prev then
|
||||
out[#out + 1] = x
|
||||
prev = n
|
||||
end
|
||||
end
|
||||
return out
|
||||
end
|
||||
function toxinEnqueuePoolMergeForThinker(self, entIndex)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
toxinMergeQueue[#toxinMergeQueue + 1] = entIndex
|
||||
if toxinMergeDrainScheduled or toxinIsDrainingMerge then
|
||||
return
|
||||
end
|
||||
toxinMergeDrainScheduled = true
|
||||
Timers:CreateTimer(0, toxinDrainToxinMergeQueueFrame)
|
||||
end
|
||||
function toxinDrainToxinMergeQueueFrame(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
toxinIsDrainingMerge = true
|
||||
toxinMergeDrainScheduled = false
|
||||
local batch = toxinMergeQueue
|
||||
toxinMergeQueue = {}
|
||||
local uniq = toxinSortDedupeEntIndices(nil, batch)
|
||||
for ____, idx in ipairs(uniq) do
|
||||
toxinTryResolvePoolMergeForThinkerIndex(nil, idx)
|
||||
end
|
||||
toxinIsDrainingMerge = false
|
||||
if #toxinMergeQueue > 0 then
|
||||
toxinMergeDrainScheduled = true
|
||||
Timers:CreateTimer(0, toxinDrainToxinMergeQueueFrame)
|
||||
end
|
||||
end
|
||||
function toxinTryResolvePoolMergeForThinkerIndex(self, entIndex)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local npc = EntIndexToHScript(entIndex)
|
||||
if not isToxinPoolThinker(nil, npc) then
|
||||
return
|
||||
end
|
||||
local buff = npc:FindModifierByName(TOXIN_POOL_MODIFIER_NAME)
|
||||
if not buff then
|
||||
return
|
||||
end
|
||||
local poolMod = buff
|
||||
local abilityRaw = buff:GetAbility()
|
||||
if not toxinIsValidAbility(nil, abilityRaw) then
|
||||
return
|
||||
end
|
||||
local ability = abilityRaw
|
||||
local caster = buff:GetCaster()
|
||||
if not toxinIsValidUnit(nil, caster) then
|
||||
return
|
||||
end
|
||||
local cluster = buildToxinOverlapCluster(nil, npc, ability)
|
||||
if #cluster < 2 then
|
||||
poolMod:initializeSinglePoolFromAbility()
|
||||
return
|
||||
end
|
||||
local mergeLeader
|
||||
local minIdx = 2147483647
|
||||
for ____, t in ipairs(cluster) do
|
||||
do
|
||||
if not toxinIsValidUnit(nil, t) then
|
||||
goto __continue141
|
||||
end
|
||||
local ei = t:GetEntityIndex()
|
||||
if ei < minIdx then
|
||||
minIdx = ei
|
||||
mergeLeader = t
|
||||
end
|
||||
end
|
||||
::__continue141::
|
||||
end
|
||||
if not mergeLeader or not toxinIsValidUnit(nil, mergeLeader) then
|
||||
return
|
||||
end
|
||||
if npc:GetEntityIndex() ~= mergeLeader:GetEntityIndex() then
|
||||
toxinEnqueuePoolMergeForThinker(
|
||||
nil,
|
||||
mergeLeader:GetEntityIndex()
|
||||
)
|
||||
return
|
||||
end
|
||||
local merged = computeMergedPoolStats(nil, cluster, ability)
|
||||
local spawnParams = {duration = merged.duration, merged_damage_per_tick = merged.damagePerTick, merged_radius = merged.radius, merged_stack_count = merged.mergeStackCount}
|
||||
for ____, t in ipairs(cluster) do
|
||||
if toxinIsValidUnit(nil, t) then
|
||||
UTIL_Remove(t)
|
||||
end
|
||||
end
|
||||
if not toxinIsValidAbility(nil, ability) or not toxinIsValidUnit(nil, caster) then
|
||||
return
|
||||
end
|
||||
CreateModifierThinker(
|
||||
caster,
|
||||
ability,
|
||||
TOXIN_POOL_MODIFIER_NAME,
|
||||
spawnParams,
|
||||
merged.centroid,
|
||||
caster:GetTeamNumber(),
|
||||
false
|
||||
)
|
||||
end
|
||||
TOXIN_POOL_MODIFIER_NAME = "modifier_spider_nethertoxin_lua"
|
||||
TOXIN_MERGE_SCAN_RADIUS = 2400
|
||||
TOXIN_MERGE_OVERLAP_EPSILON = 12
|
||||
TOXIN_THINKER_CLASS = "npc_dota_thinker"
|
||||
TOXIN_POOL_MAX_RADIUS = 900
|
||||
TOXIN_MAX_MERGE_SCAN_THINKERS = 220
|
||||
toxinMergeQueue = {}
|
||||
toxinMergeDrainScheduled = false
|
||||
toxinIsDrainingMerge = false
|
||||
____exports.toxin = __TS__Class()
|
||||
local toxin = ____exports.toxin
|
||||
toxin.name = "toxin"
|
||||
toxin.____file_path = "scripts/vscripts/abilities/creep/toxin.lua"
|
||||
__TS__ClassExtends(toxin, BaseAbility)
|
||||
function toxin.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_alchemist/alchemist_acid_spray.vpcf", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_viper/viper_nethertoxin.vpcf", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_viper/viper_nethertoxin_debuff.vpcf", context)
|
||||
PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_broodmother.vsndevts", context)
|
||||
PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_viper.vsndevts", context)
|
||||
end
|
||||
function toxin.prototype.GetIntrinsicModifierName(self)
|
||||
return "modifier_spider_toxin_death_listener"
|
||||
end
|
||||
toxin = __TS__Decorate(
|
||||
toxin,
|
||||
toxin,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "toxin"}
|
||||
)
|
||||
____exports.toxin = toxin
|
||||
____exports.modifier_spider_toxin_death_listener = __TS__Class()
|
||||
local modifier_spider_toxin_death_listener = ____exports.modifier_spider_toxin_death_listener
|
||||
modifier_spider_toxin_death_listener.name = "modifier_spider_toxin_death_listener"
|
||||
modifier_spider_toxin_death_listener.____file_path = "scripts/vscripts/abilities/creep/toxin.lua"
|
||||
__TS__ClassExtends(modifier_spider_toxin_death_listener, BaseModifier)
|
||||
function modifier_spider_toxin_death_listener.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_spider_toxin_death_listener.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_spider_toxin_death_listener.prototype.OnCreated(self, _params)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
if not toxinIsValidUnit(nil, parent) then
|
||||
return
|
||||
end
|
||||
trySetIntrinsicCreepRenderColor(
|
||||
nil,
|
||||
parent,
|
||||
51,
|
||||
102,
|
||||
0
|
||||
)
|
||||
end
|
||||
function modifier_spider_toxin_death_listener.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_EVENT_ON_DEATH}
|
||||
end
|
||||
function modifier_spider_toxin_death_listener.prototype.OnDeath(self, event)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if event.unit ~= self:GetParent() then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
if not toxinIsValidAbility(nil, ability) then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
if not toxinIsValidUnit(nil, caster) then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local spawnOrigin = parent:GetAbsOrigin()
|
||||
local duration = ability:GetSpecialValueFor("duration")
|
||||
local radius = clampToxinPoolRadius(
|
||||
nil,
|
||||
ability:GetSpecialValueFor("radius")
|
||||
)
|
||||
local splashPfx = ParticleManager:CreateParticle("particles/units/heroes/hero_alchemist/alchemist_acid_spray.vpcf", PATTACH_WORLDORIGIN, nil)
|
||||
ParticleManager:SetParticleControl(splashPfx, 0, spawnOrigin)
|
||||
ParticleManager:SetParticleControl(
|
||||
splashPfx,
|
||||
1,
|
||||
Vector(radius, 1, 1)
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
splashPfx,
|
||||
15,
|
||||
Vector(255, 153, 102)
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
splashPfx,
|
||||
16,
|
||||
Vector(1, 0, 0)
|
||||
)
|
||||
Timers:CreateTimer(
|
||||
4,
|
||||
function()
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
ParticleManager:DestroyParticle(splashPfx, false)
|
||||
ParticleManager:ReleaseParticleIndex(splashPfx)
|
||||
end
|
||||
)
|
||||
EmitSoundOn("Hero_Broodmother.SpawnSpiderlings", parent)
|
||||
CreateModifierThinker(
|
||||
caster,
|
||||
ability,
|
||||
TOXIN_POOL_MODIFIER_NAME,
|
||||
{duration = duration},
|
||||
spawnOrigin,
|
||||
caster:GetTeamNumber(),
|
||||
false
|
||||
)
|
||||
end
|
||||
modifier_spider_toxin_death_listener = __TS__Decorate(
|
||||
modifier_spider_toxin_death_listener,
|
||||
modifier_spider_toxin_death_listener,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_spider_toxin_death_listener"}
|
||||
)
|
||||
____exports.modifier_spider_toxin_death_listener = modifier_spider_toxin_death_listener
|
||||
____exports.modifier_spider_nethertoxin_lua = __TS__Class()
|
||||
local modifier_spider_nethertoxin_lua = ____exports.modifier_spider_nethertoxin_lua
|
||||
modifier_spider_nethertoxin_lua.name = "modifier_spider_nethertoxin_lua"
|
||||
modifier_spider_nethertoxin_lua.____file_path = "scripts/vscripts/abilities/creep/toxin.lua"
|
||||
__TS__ClassExtends(modifier_spider_nethertoxin_lua, BaseModifier)
|
||||
function modifier_spider_nethertoxin_lua.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.effectiveRadius = 0
|
||||
self.damagePerTick = 0
|
||||
self.poolMergeStackCount = 0
|
||||
self.auraPenaltyStack = 1
|
||||
self.damageTable = {}
|
||||
end
|
||||
function modifier_spider_nethertoxin_lua.prototype.GetTexture(self)
|
||||
return "viper_nethertoxin"
|
||||
end
|
||||
function modifier_spider_nethertoxin_lua.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_spider_nethertoxin_lua.prototype.IsDebuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_spider_nethertoxin_lua.prototype.IsStunDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_spider_nethertoxin_lua.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_spider_nethertoxin_lua.prototype.initializeAuraVictimDebuff(self)
|
||||
local ability = self:GetAbility()
|
||||
if toxinIsValidAbility(nil, ability) then
|
||||
self:SetDuration(
|
||||
math.max(
|
||||
0.05,
|
||||
self:GetAuraDuration()
|
||||
),
|
||||
true
|
||||
)
|
||||
end
|
||||
self.effectiveRadius = 0
|
||||
self.damagePerTick = 0
|
||||
self.poolMergeStackCount = 0
|
||||
if IsServer() then
|
||||
self:syncAuraPenaltyFromCasterThinker()
|
||||
self:SetStackCount(math.max(
|
||||
1,
|
||||
math.floor(self.auraPenaltyStack)
|
||||
))
|
||||
end
|
||||
end
|
||||
function modifier_spider_nethertoxin_lua.prototype.syncAuraPenaltyFromCasterThinker(self)
|
||||
local parent = self:GetParent()
|
||||
if isToxinPoolThinker(nil, parent) then
|
||||
return
|
||||
end
|
||||
local source = self:GetCaster()
|
||||
if not toxinIsValidUnit(nil, source) then
|
||||
return
|
||||
end
|
||||
local srcMod = source:FindModifierByName(TOXIN_POOL_MODIFIER_NAME)
|
||||
if srcMod ~= nil and srcMod.poolMergeStackCount > 0 then
|
||||
self.auraPenaltyStack = math.floor(srcMod.poolMergeStackCount)
|
||||
else
|
||||
self.auraPenaltyStack = math.max(1, self.auraPenaltyStack)
|
||||
end
|
||||
end
|
||||
function modifier_spider_nethertoxin_lua.prototype.initializeSinglePoolFromAbility(self)
|
||||
local ability = self:GetAbility()
|
||||
local caster = self:GetCaster()
|
||||
local parent = self:GetParent()
|
||||
if not toxinIsValidAbility(nil, ability) or not toxinIsValidUnit(nil, caster) or not toxinIsValidUnit(nil, parent) then
|
||||
return
|
||||
end
|
||||
local baseDamage = ability:GetSpecialValueFor("damage")
|
||||
local baseRadius = ability:GetSpecialValueFor("radius")
|
||||
local baseDuration = ability:GetSpecialValueFor("duration")
|
||||
self.damagePerTick = (baseDamage + self:GetCaster():GetAttackDamage() * 0.15) * 0.33
|
||||
local totaldamage = self:GetParent():IsRangedAttacker() and self.damagePerTick or self.damagePerTick * 0.25
|
||||
self.effectiveRadius = clampToxinPoolRadius(nil, baseRadius)
|
||||
self.poolMergeStackCount = 1
|
||||
self:SetDuration(
|
||||
math.max(0.05, baseDuration),
|
||||
true
|
||||
)
|
||||
self.damageTable = {
|
||||
victim = caster,
|
||||
attacker = caster,
|
||||
damage = totaldamage,
|
||||
damage_type = ability:GetAbilityDamageType(),
|
||||
ability = ability
|
||||
}
|
||||
self:StartIntervalThink(0.33)
|
||||
self:PlayEffects()
|
||||
end
|
||||
function modifier_spider_nethertoxin_lua.prototype.initializeMergedPoolFromParams(self, params)
|
||||
local ability = self:GetAbility()
|
||||
local caster = self:GetCaster()
|
||||
local parent = self:GetParent()
|
||||
if not toxinIsValidAbility(nil, ability) or not toxinIsValidUnit(nil, caster) or not toxinIsValidUnit(nil, parent) then
|
||||
return
|
||||
end
|
||||
self.damagePerTick = params.merged_damage_per_tick
|
||||
self.effectiveRadius = clampToxinPoolRadius(nil, params.merged_radius)
|
||||
self.poolMergeStackCount = math.max(
|
||||
1,
|
||||
math.floor(params.merged_stack_count or 1)
|
||||
)
|
||||
self:SetDuration(
|
||||
math.max(0.05, params.duration),
|
||||
true
|
||||
)
|
||||
self.damageTable = {
|
||||
victim = caster,
|
||||
attacker = caster,
|
||||
damage = self.damagePerTick,
|
||||
damage_type = ability:GetAbilityDamageType(),
|
||||
ability = ability
|
||||
}
|
||||
self:StartIntervalThink(0.33)
|
||||
self:PlayEffects()
|
||||
end
|
||||
function modifier_spider_nethertoxin_lua.prototype.OnCreated(self, params)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
if not isToxinPoolThinker(nil, parent) then
|
||||
self:initializeAuraVictimDebuff()
|
||||
return
|
||||
end
|
||||
local p = params
|
||||
if p.merged_damage_per_tick ~= nil and p.merged_radius ~= nil and p.duration ~= nil then
|
||||
self:initializeMergedPoolFromParams(p)
|
||||
return
|
||||
end
|
||||
toxinEnqueuePoolMergeForThinker(
|
||||
nil,
|
||||
parent:GetEntityIndex()
|
||||
)
|
||||
end
|
||||
function modifier_spider_nethertoxin_lua.prototype.OnRefresh(self, _params)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
if not toxinIsValidAbility(nil, ability) then
|
||||
return
|
||||
end
|
||||
if not isToxinPoolThinker(
|
||||
nil,
|
||||
self:GetParent()
|
||||
) then
|
||||
self:syncAuraPenaltyFromCasterThinker()
|
||||
self:SetStackCount(math.max(
|
||||
1,
|
||||
math.floor(self.auraPenaltyStack)
|
||||
))
|
||||
return
|
||||
end
|
||||
self.damageTable.damage = self.damagePerTick
|
||||
self.damageTable.damage_type = ability:GetAbilityDamageType()
|
||||
end
|
||||
function modifier_spider_nethertoxin_lua.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_PHYSICAL_ARMOR_BONUS, MODIFIER_PROPERTY_MAGICAL_RESISTANCE_BONUS}
|
||||
end
|
||||
function modifier_spider_nethertoxin_lua.prototype.GetModifierPhysicalArmorBonus(self)
|
||||
local parent = self:GetParent()
|
||||
if isToxinPoolThinker(nil, parent) then
|
||||
return 0
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
if not toxinIsValidAbility(nil, ability) then
|
||||
return 0
|
||||
end
|
||||
local per = math.max(
|
||||
0,
|
||||
ability:GetSpecialValueFor("armor_magic_reduce_per_stack")
|
||||
)
|
||||
local scale = Difficulty:getNpcStatScale()
|
||||
return -per * math.max(
|
||||
1,
|
||||
math.floor(self.auraPenaltyStack)
|
||||
) * scale
|
||||
end
|
||||
function modifier_spider_nethertoxin_lua.prototype.GetModifierMagicalResistanceBonus(self)
|
||||
local parent = self:GetParent()
|
||||
if isToxinPoolThinker(nil, parent) then
|
||||
return 0
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
if not toxinIsValidAbility(nil, ability) then
|
||||
return 0
|
||||
end
|
||||
local per = math.max(
|
||||
0,
|
||||
ability:GetSpecialValueFor("armor_magic_reduce_per_stack")
|
||||
)
|
||||
local scale = Difficulty:getNpcStatScale()
|
||||
return -per * math.max(
|
||||
1,
|
||||
math.floor(self.auraPenaltyStack)
|
||||
) * scale
|
||||
end
|
||||
function modifier_spider_nethertoxin_lua.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
if not isToxinPoolThinker(nil, parent) then
|
||||
return
|
||||
end
|
||||
local entIndex = parent:GetEntityIndex()
|
||||
Timers:CreateTimer(
|
||||
0,
|
||||
function()
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local npc = EntIndexToHScript(entIndex)
|
||||
if not toxinIsValidUnit(nil, npc) then
|
||||
return
|
||||
end
|
||||
if npc:GetClassname() ~= TOXIN_THINKER_CLASS then
|
||||
return
|
||||
end
|
||||
UTIL_Remove(npc)
|
||||
end
|
||||
)
|
||||
end
|
||||
function modifier_spider_nethertoxin_lua.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_PASSIVES_DISABLED] = true}
|
||||
end
|
||||
function modifier_spider_nethertoxin_lua.prototype.OnIntervalThink(self)
|
||||
local parent = self:GetParent()
|
||||
if not isToxinPoolThinker(nil, parent) then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local ability = self:GetAbility()
|
||||
if not toxinIsValidUnit(nil, caster) or not toxinIsValidAbility(nil, ability) then
|
||||
return
|
||||
end
|
||||
local units = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
parent:GetAbsOrigin(),
|
||||
nil,
|
||||
self.effectiveRadius,
|
||||
self:GetAuraSearchTeam(),
|
||||
self:GetAuraSearchType(),
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
self.damageTable.attacker = caster
|
||||
self.damageTable.ability = ability
|
||||
self.damageTable.damage_type = ability:GetAbilityDamageType()
|
||||
local damagedAny = false
|
||||
for ____, unit in ipairs(units) do
|
||||
do
|
||||
if not toxinIsValidUnit(nil, unit) or not unit:IsAlive() then
|
||||
goto __continue104
|
||||
end
|
||||
self.damageTable.victim = unit
|
||||
ApplyDamage(self.damageTable)
|
||||
damagedAny = true
|
||||
end
|
||||
::__continue104::
|
||||
end
|
||||
if damagedAny then
|
||||
EmitSoundOn("Hero_Viper.NetherToxin.Damage", parent)
|
||||
end
|
||||
end
|
||||
function modifier_spider_nethertoxin_lua.prototype.IsAura(self)
|
||||
return isToxinPoolThinker(
|
||||
nil,
|
||||
self:GetParent()
|
||||
)
|
||||
end
|
||||
function modifier_spider_nethertoxin_lua.prototype.GetModifierAura(self)
|
||||
return TOXIN_POOL_MODIFIER_NAME
|
||||
end
|
||||
function modifier_spider_nethertoxin_lua.prototype.GetAuraRadius(self)
|
||||
local ability = self:GetAbility()
|
||||
if self.effectiveRadius > 0 then
|
||||
return self.effectiveRadius
|
||||
end
|
||||
if toxinIsValidAbility(nil, ability) then
|
||||
return clampToxinPoolRadius(
|
||||
nil,
|
||||
ability:GetSpecialValueFor("radius")
|
||||
)
|
||||
end
|
||||
return 0
|
||||
end
|
||||
function modifier_spider_nethertoxin_lua.prototype.GetAuraDuration(self)
|
||||
return 0.25
|
||||
end
|
||||
function modifier_spider_nethertoxin_lua.prototype.GetAuraSearchTeam(self)
|
||||
return DOTA_UNIT_TARGET_TEAM_ENEMY
|
||||
end
|
||||
function modifier_spider_nethertoxin_lua.prototype.GetAuraSearchType(self)
|
||||
return DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC
|
||||
end
|
||||
function modifier_spider_nethertoxin_lua.prototype.GetEffectName(self)
|
||||
return "particles/units/heroes/hero_viper/viper_nethertoxin_debuff.vpcf"
|
||||
end
|
||||
function modifier_spider_nethertoxin_lua.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_ABSORIGIN_FOLLOW
|
||||
end
|
||||
function modifier_spider_nethertoxin_lua.prototype.PlayEffects(self)
|
||||
local parent = self:GetParent()
|
||||
if not toxinIsValidUnit(nil, parent) then
|
||||
return
|
||||
end
|
||||
local particle_cast = "particles/units/heroes/hero_viper/viper_nethertoxin.vpcf"
|
||||
local sound_cast = "Hero_Viper.NetherToxin"
|
||||
local effect_cast = ParticleManager:CreateParticle(particle_cast, PATTACH_WORLDORIGIN, nil)
|
||||
ParticleManager:SetParticleControl(
|
||||
effect_cast,
|
||||
0,
|
||||
parent:GetOrigin()
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
effect_cast,
|
||||
1,
|
||||
Vector(self.effectiveRadius, 1, 1)
|
||||
)
|
||||
self:AddParticle(
|
||||
effect_cast,
|
||||
false,
|
||||
false,
|
||||
-1,
|
||||
false,
|
||||
false
|
||||
)
|
||||
EmitSoundOn(sound_cast, parent)
|
||||
end
|
||||
modifier_spider_nethertoxin_lua = __TS__Decorate(
|
||||
modifier_spider_nethertoxin_lua,
|
||||
modifier_spider_nethertoxin_lua,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_spider_nethertoxin_lua"}
|
||||
)
|
||||
____exports.modifier_spider_nethertoxin_lua = modifier_spider_nethertoxin_lua
|
||||
return ____exports
|
||||
@@ -0,0 +1,94 @@
|
||||
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 ____difficulty_manager = require("difficulty_manager")
|
||||
local Difficulty = ____difficulty_manager.Difficulty
|
||||
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
|
||||
--- Пассивка для волновых крипов: при здоровье ниже порога атаки лечат на долю нанесённого урона.
|
||||
____exports.wave_desperate_vampirism = __TS__Class()
|
||||
local wave_desperate_vampirism = ____exports.wave_desperate_vampirism
|
||||
wave_desperate_vampirism.name = "wave_desperate_vampirism"
|
||||
wave_desperate_vampirism.____file_path = "scripts/vscripts/abilities/creep/wave_desperate_vampirism.lua"
|
||||
__TS__ClassExtends(wave_desperate_vampirism, BaseAbility)
|
||||
function wave_desperate_vampirism.prototype.GetIntrinsicModifierName(self)
|
||||
return "modifier_wave_desperate_vampirism"
|
||||
end
|
||||
function wave_desperate_vampirism.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_bloodseeker/bloodseeker_bloodbath.vpcf", context)
|
||||
end
|
||||
wave_desperate_vampirism = __TS__Decorate(
|
||||
wave_desperate_vampirism,
|
||||
wave_desperate_vampirism,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "wave_desperate_vampirism"}
|
||||
)
|
||||
____exports.wave_desperate_vampirism = wave_desperate_vampirism
|
||||
____exports.modifier_wave_desperate_vampirism = __TS__Class()
|
||||
local modifier_wave_desperate_vampirism = ____exports.modifier_wave_desperate_vampirism
|
||||
modifier_wave_desperate_vampirism.name = "modifier_wave_desperate_vampirism"
|
||||
modifier_wave_desperate_vampirism.____file_path = "scripts/vscripts/abilities/creep/wave_desperate_vampirism.lua"
|
||||
__TS__ClassExtends(modifier_wave_desperate_vampirism, BaseModifier)
|
||||
function modifier_wave_desperate_vampirism.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_wave_desperate_vampirism.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_wave_desperate_vampirism.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_wave_desperate_vampirism.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_EVENT_ON_ATTACK_LANDED}
|
||||
end
|
||||
function modifier_wave_desperate_vampirism.prototype.OnAttackLanded(self, event)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
if not ability or not parent or event.attacker ~= parent then
|
||||
return
|
||||
end
|
||||
local target = event.target
|
||||
if not target or not target:IsAlive() or target:IsBuilding() or target:IsOther() then
|
||||
return
|
||||
end
|
||||
local thresholdPct = ability:GetSpecialValueFor("hp_threshold_pct")
|
||||
if parent:GetHealthPercent() >= thresholdPct then
|
||||
return
|
||||
end
|
||||
local vampPct = ability:GetSpecialValueFor("vamp_pct")
|
||||
local damage = event.damage or 0
|
||||
if damage <= 0 then
|
||||
damage = parent:GetAverageTrueAttackDamage(target)
|
||||
end
|
||||
if damage <= 0 then
|
||||
return
|
||||
end
|
||||
local heal = damage * vampPct / 100 * Difficulty:getNpcStatScale()
|
||||
if heal <= 0 then
|
||||
return
|
||||
end
|
||||
parent:Heal(heal, ability)
|
||||
local p = ParticleManager:CreateParticle("particles/units/heroes/hero_bloodseeker/bloodseeker_bloodbath.vpcf", PATTACH_ABSORIGIN_FOLLOW, parent)
|
||||
ParticleManager:SetParticleControl(
|
||||
p,
|
||||
0,
|
||||
parent:GetAbsOrigin()
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(p)
|
||||
end
|
||||
modifier_wave_desperate_vampirism = __TS__Decorate(
|
||||
modifier_wave_desperate_vampirism,
|
||||
modifier_wave_desperate_vampirism,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_wave_desperate_vampirism"}
|
||||
)
|
||||
____exports.modifier_wave_desperate_vampirism = modifier_wave_desperate_vampirism
|
||||
return ____exports
|
||||
@@ -0,0 +1,139 @@
|
||||
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 ____creep_render_color = require("utils.creep_render_color")
|
||||
local trySetIntrinsicCreepRenderColor = ____creep_render_color.trySetIntrinsicCreepRenderColor
|
||||
--- Пассив: усиливает юнита на bonus_pct процентов (по умолчанию 100 = удвоение по смыслу «+100%»).
|
||||
-- HP/мана — плоский бонус от базового макс.; урон/IAS/мувспид/спелламп/реген HP% — через модификаторы.
|
||||
____exports.wave_full_brutality = __TS__Class()
|
||||
local wave_full_brutality = ____exports.wave_full_brutality
|
||||
wave_full_brutality.name = "wave_full_brutality"
|
||||
wave_full_brutality.____file_path = "scripts/vscripts/abilities/creep/wave_full_brutality.lua"
|
||||
__TS__ClassExtends(wave_full_brutality, BaseAbility)
|
||||
function wave_full_brutality.prototype.GetIntrinsicModifierName(self)
|
||||
return "modifier_wave_full_brutality_passive"
|
||||
end
|
||||
wave_full_brutality = __TS__Decorate(
|
||||
wave_full_brutality,
|
||||
wave_full_brutality,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "wave_full_brutality"}
|
||||
)
|
||||
____exports.wave_full_brutality = wave_full_brutality
|
||||
____exports.modifier_wave_full_brutality_passive = __TS__Class()
|
||||
local modifier_wave_full_brutality_passive = ____exports.modifier_wave_full_brutality_passive
|
||||
modifier_wave_full_brutality_passive.name = "modifier_wave_full_brutality_passive"
|
||||
modifier_wave_full_brutality_passive.____file_path = "scripts/vscripts/abilities/creep/wave_full_brutality.lua"
|
||||
__TS__ClassExtends(modifier_wave_full_brutality_passive, BaseModifier)
|
||||
function modifier_wave_full_brutality_passive.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.bonusPct = 100
|
||||
self.effectiveBonusPct = 100
|
||||
self.healthBonusFlat = 0
|
||||
self.manaBonusFlat = 0
|
||||
end
|
||||
function modifier_wave_full_brutality_passive.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_wave_full_brutality_passive.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_wave_full_brutality_passive.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_wave_full_brutality_passive.prototype.OnCreated(self)
|
||||
local ability = self:GetAbility()
|
||||
local parent = self:GetParent()
|
||||
if not parent then
|
||||
return
|
||||
end
|
||||
if IsServer() then
|
||||
trySetIntrinsicCreepRenderColor(
|
||||
nil,
|
||||
self:GetParent(),
|
||||
52,
|
||||
0,
|
||||
0
|
||||
)
|
||||
end
|
||||
self.bonusPct = ability and ability:GetSpecialValueFor("bonus_pct") or 100
|
||||
if self.bonusPct < 0 then
|
||||
self.bonusPct = 0
|
||||
end
|
||||
self.effectiveBonusPct = self.bonusPct
|
||||
local baseHp = math.max(
|
||||
1,
|
||||
parent:GetMaxHealth()
|
||||
)
|
||||
local baseMana = math.max(
|
||||
0,
|
||||
parent:GetMaxMana()
|
||||
)
|
||||
self.healthBonusFlat = math.floor(baseHp * (self.effectiveBonusPct / 100))
|
||||
self.manaBonusFlat = math.floor(baseMana * (self.effectiveBonusPct / 100))
|
||||
if IsServer() then
|
||||
Timers:CreateTimer(
|
||||
0,
|
||||
function()
|
||||
local p = self:GetParent()
|
||||
if not p or not IsValidEntity(p) or not p:IsAlive() then
|
||||
return nil
|
||||
end
|
||||
p:SetHealth(p:GetMaxHealth() * 2)
|
||||
if p:GetMaxMana() > 0 then
|
||||
p:SetMana(p:GetMaxMana() * 2)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
function modifier_wave_full_brutality_passive.prototype.DeclareFunctions(self)
|
||||
return {
|
||||
MODIFIER_PROPERTY_HEALTH_BONUS,
|
||||
MODIFIER_PROPERTY_MANA_BONUS,
|
||||
MODIFIER_PROPERTY_DAMAGEOUTGOING_PERCENTAGE,
|
||||
MODIFIER_PROPERTY_ATTACKSPEED_PERCENTAGE,
|
||||
MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE,
|
||||
MODIFIER_PROPERTY_SPELL_AMPLIFY_PERCENTAGE
|
||||
}
|
||||
end
|
||||
function modifier_wave_full_brutality_passive.prototype.GetModifierHealthBonus(self)
|
||||
return self.healthBonusFlat
|
||||
end
|
||||
function modifier_wave_full_brutality_passive.prototype.GetModifierManaBonus(self)
|
||||
return self.manaBonusFlat
|
||||
end
|
||||
function modifier_wave_full_brutality_passive.prototype.GetModifierDamageOutgoing_Percentage(self, _event)
|
||||
return self.effectiveBonusPct
|
||||
end
|
||||
function modifier_wave_full_brutality_passive.prototype.GetModifierAttackSpeedPercentage(self)
|
||||
return self.effectiveBonusPct
|
||||
end
|
||||
function modifier_wave_full_brutality_passive.prototype.GetModifierMoveSpeedBonus_Percentage(self)
|
||||
return self.effectiveBonusPct
|
||||
end
|
||||
function modifier_wave_full_brutality_passive.prototype.GetModifierSpellAmplify_Percentage(self, _event)
|
||||
return self.effectiveBonusPct
|
||||
end
|
||||
function modifier_wave_full_brutality_passive.prototype.GetEffectName(self)
|
||||
return "particles/items2_fx/mask_of_madness.vpcf"
|
||||
end
|
||||
function modifier_wave_full_brutality_passive.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_ABSORIGIN_FOLLOW
|
||||
end
|
||||
modifier_wave_full_brutality_passive = __TS__Decorate(
|
||||
modifier_wave_full_brutality_passive,
|
||||
modifier_wave_full_brutality_passive,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_wave_full_brutality_passive"}
|
||||
)
|
||||
____exports.modifier_wave_full_brutality_passive = modifier_wave_full_brutality_passive
|
||||
return ____exports
|
||||
@@ -0,0 +1,63 @@
|
||||
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 ____difficulty_manager = require("difficulty_manager")
|
||||
local Difficulty = ____difficulty_manager.Difficulty
|
||||
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
|
||||
--- Пассив волны: проход сквозь юнитов и бонус к скорости передвижения.
|
||||
____exports.wave_phasing_march = __TS__Class()
|
||||
local wave_phasing_march = ____exports.wave_phasing_march
|
||||
wave_phasing_march.name = "wave_phasing_march"
|
||||
wave_phasing_march.____file_path = "scripts/vscripts/abilities/creep/wave_phasing_march.lua"
|
||||
__TS__ClassExtends(wave_phasing_march, BaseAbility)
|
||||
function wave_phasing_march.prototype.GetIntrinsicModifierName(self)
|
||||
return "modifier_wave_phasing_march_passive"
|
||||
end
|
||||
wave_phasing_march = __TS__Decorate(
|
||||
wave_phasing_march,
|
||||
wave_phasing_march,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "wave_phasing_march"}
|
||||
)
|
||||
____exports.wave_phasing_march = wave_phasing_march
|
||||
____exports.modifier_wave_phasing_march_passive = __TS__Class()
|
||||
local modifier_wave_phasing_march_passive = ____exports.modifier_wave_phasing_march_passive
|
||||
modifier_wave_phasing_march_passive.name = "modifier_wave_phasing_march_passive"
|
||||
modifier_wave_phasing_march_passive.____file_path = "scripts/vscripts/abilities/creep/wave_phasing_march.lua"
|
||||
__TS__ClassExtends(modifier_wave_phasing_march_passive, BaseModifier)
|
||||
function modifier_wave_phasing_march_passive.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_wave_phasing_march_passive.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_wave_phasing_march_passive.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_wave_phasing_march_passive.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_MOVESPEED_BONUS_CONSTANT}
|
||||
end
|
||||
function modifier_wave_phasing_march_passive.prototype.GetModifierMoveSpeedBonus_Constant(self)
|
||||
local ab = self:GetAbility()
|
||||
if not ab then
|
||||
return 0
|
||||
end
|
||||
return ab:GetSpecialValueFor("bonus_movement_speed") * Difficulty:getNpcStatScale()
|
||||
end
|
||||
function modifier_wave_phasing_march_passive.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_NO_UNIT_COLLISION] = true}
|
||||
end
|
||||
modifier_wave_phasing_march_passive = __TS__Decorate(
|
||||
modifier_wave_phasing_march_passive,
|
||||
modifier_wave_phasing_march_passive,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_wave_phasing_march_passive"}
|
||||
)
|
||||
____exports.modifier_wave_phasing_march_passive = modifier_wave_phasing_march_passive
|
||||
return ____exports
|
||||
@@ -0,0 +1,137 @@
|
||||
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
|
||||
____exports.weaking_impetus = __TS__Class()
|
||||
local weaking_impetus = ____exports.weaking_impetus
|
||||
weaking_impetus.name = "weaking_impetus"
|
||||
weaking_impetus.____file_path = "scripts/vscripts/abilities/creep/weaking_impetus.lua"
|
||||
__TS__ClassExtends(weaking_impetus, BaseAbility)
|
||||
function weaking_impetus.prototype.GetIntrinsicModifierName(self)
|
||||
return "modifier_weaking_impetus_passive"
|
||||
end
|
||||
weaking_impetus = __TS__Decorate(
|
||||
weaking_impetus,
|
||||
weaking_impetus,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "weaking_impetus"}
|
||||
)
|
||||
____exports.weaking_impetus = weaking_impetus
|
||||
____exports.modifier_weaking_impetus_passive = __TS__Class()
|
||||
local modifier_weaking_impetus_passive = ____exports.modifier_weaking_impetus_passive
|
||||
modifier_weaking_impetus_passive.name = "modifier_weaking_impetus_passive"
|
||||
modifier_weaking_impetus_passive.____file_path = "scripts/vscripts/abilities/creep/weaking_impetus.lua"
|
||||
__TS__ClassExtends(modifier_weaking_impetus_passive, BaseModifier)
|
||||
function modifier_weaking_impetus_passive.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_weaking_impetus_passive.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_weaking_impetus_passive.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_weaking_impetus_passive.prototype.GetStatusEffectName(self)
|
||||
return "particles/status_fx/status_effect_abaddon_borrowed_time.vpcf"
|
||||
end
|
||||
function modifier_weaking_impetus_passive.prototype.GetEffectName(self)
|
||||
return "particles/units/heroes/hero_abaddon/abaddon_borrowed_time.vpcf"
|
||||
end
|
||||
function modifier_weaking_impetus_passive.prototype.StatusEffectPriority(self)
|
||||
return 10
|
||||
end
|
||||
function modifier_weaking_impetus_passive.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_EVENT_ON_ATTACK_LANDED}
|
||||
end
|
||||
function modifier_weaking_impetus_passive.prototype.OnAttackLanded(self, event)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if event.attacker ~= self:GetParent() then
|
||||
return
|
||||
end
|
||||
local target = event.target
|
||||
if not target then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
if parent:GetMana() < self:GetAbility():GetSpecialValueFor("mana_hit") then
|
||||
return
|
||||
end
|
||||
parent:SpendMana(
|
||||
self:GetAbility():GetSpecialValueFor("mana_hit"),
|
||||
ability
|
||||
)
|
||||
local duration = ability:GetSpecialValueFor("debuff_duration")
|
||||
local modifier = target:FindModifierByName("modifier_weaking_impetus_debuff")
|
||||
if modifier then
|
||||
modifier:SetDuration(duration, true)
|
||||
if modifier:GetStackCount() < self:GetAbility():GetSpecialValueFor("max_stacks") then
|
||||
modifier:IncrementStackCount()
|
||||
end
|
||||
else
|
||||
modifier = target:AddNewModifier(
|
||||
self:GetParent(),
|
||||
ability,
|
||||
"modifier_weaking_impetus_debuff",
|
||||
{duration = duration}
|
||||
)
|
||||
modifier:SetStackCount(1)
|
||||
end
|
||||
end
|
||||
modifier_weaking_impetus_passive = __TS__Decorate(
|
||||
modifier_weaking_impetus_passive,
|
||||
modifier_weaking_impetus_passive,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_weaking_impetus_passive"}
|
||||
)
|
||||
____exports.modifier_weaking_impetus_passive = modifier_weaking_impetus_passive
|
||||
____exports.modifier_weaking_impetus_debuff = __TS__Class()
|
||||
local modifier_weaking_impetus_debuff = ____exports.modifier_weaking_impetus_debuff
|
||||
modifier_weaking_impetus_debuff.name = "modifier_weaking_impetus_debuff"
|
||||
modifier_weaking_impetus_debuff.____file_path = "scripts/vscripts/abilities/creep/weaking_impetus.lua"
|
||||
__TS__ClassExtends(modifier_weaking_impetus_debuff, BaseModifier)
|
||||
function modifier_weaking_impetus_debuff.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.damage_reduction = 0
|
||||
end
|
||||
function modifier_weaking_impetus_debuff.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_weaking_impetus_debuff.prototype.IsDebuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_weaking_impetus_debuff.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
function modifier_weaking_impetus_debuff.prototype.OnCreated(self)
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
self.damage_reduction = ability:GetSpecialValueFor("damage_reduction")
|
||||
end
|
||||
function modifier_weaking_impetus_debuff.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_DAMAGEOUTGOING_PERCENTAGE, MODIFIER_PROPERTY_SPELL_AMPLIFY_PERCENTAGE}
|
||||
end
|
||||
function modifier_weaking_impetus_debuff.prototype.GetModifierDamageOutgoing_Percentage(self, event)
|
||||
return -self.damage_reduction * self:GetStackCount()
|
||||
end
|
||||
function modifier_weaking_impetus_debuff.prototype.GetModifierSpellAmplify_Percentage(self, event)
|
||||
return -self.damage_reduction * self:GetStackCount()
|
||||
end
|
||||
modifier_weaking_impetus_debuff = __TS__Decorate(
|
||||
modifier_weaking_impetus_debuff,
|
||||
modifier_weaking_impetus_debuff,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_weaking_impetus_debuff"}
|
||||
)
|
||||
____exports.modifier_weaking_impetus_debuff = modifier_weaking_impetus_debuff
|
||||
return ____exports
|
||||
@@ -0,0 +1,72 @@
|
||||
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 ____difficulty_manager = require("difficulty_manager")
|
||||
local Difficulty = ____difficulty_manager.Difficulty
|
||||
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
|
||||
____exports.witch_base = __TS__Class()
|
||||
local witch_base = ____exports.witch_base
|
||||
witch_base.name = "witch_base"
|
||||
witch_base.____file_path = "scripts/vscripts/abilities/creep/witch_base.lua"
|
||||
__TS__ClassExtends(witch_base, BaseAbility)
|
||||
function witch_base.prototype.GetIntrinsicModifierName(self)
|
||||
return "modifier_witch_base"
|
||||
end
|
||||
witch_base = __TS__Decorate(
|
||||
witch_base,
|
||||
witch_base,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "witch_base"}
|
||||
)
|
||||
____exports.witch_base = witch_base
|
||||
____exports.modifier_witch_base = __TS__Class()
|
||||
local modifier_witch_base = ____exports.modifier_witch_base
|
||||
modifier_witch_base.name = "modifier_witch_base"
|
||||
modifier_witch_base.____file_path = "scripts/vscripts/abilities/creep/witch_base.lua"
|
||||
__TS__ClassExtends(modifier_witch_base, BaseModifier)
|
||||
function modifier_witch_base.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_witch_base.prototype.OnCreated(self, params)
|
||||
local difficulty = Difficulty:getNpcStatScale()
|
||||
local gameTime = GameRules:GetGameTime() / 60
|
||||
self:SetStackCount(math.floor(gameTime))
|
||||
end
|
||||
function modifier_witch_base.prototype.OnRefresh(self, params)
|
||||
self:OnCreated(params)
|
||||
end
|
||||
function modifier_witch_base.prototype.DeclareFunctions(self)
|
||||
return {
|
||||
MODIFIER_EVENT_ON_DEATH,
|
||||
MODIFIER_PROPERTY_SPELL_AMPLIFY_PERCENTAGE,
|
||||
MODIFIER_PROPERTY_PREATTACK_BONUS_DAMAGE,
|
||||
MODIFIER_PROPERTY_EXTRA_HEALTH_BONUS,
|
||||
MODIFIER_PROPERTY_PHYSICAL_ARMOR_BONUS
|
||||
}
|
||||
end
|
||||
function modifier_witch_base.prototype.GetModifierSpellAmplify_Percentage(self, event)
|
||||
return self:GetStackCount() * self:GetAbility():GetSpecialValueFor("amp_death_bonus") * Difficulty:getNpcStatScale()
|
||||
end
|
||||
function modifier_witch_base.prototype.GetModifierPreAttack_BonusDamage(self)
|
||||
return self:GetStackCount() * self:GetAbility():GetSpecialValueFor("attack_death_bonus") * Difficulty:getNpcStatScale()
|
||||
end
|
||||
function modifier_witch_base.prototype.GetModifierExtraHealthBonus(self)
|
||||
return self:GetStackCount() * self:GetAbility():GetSpecialValueFor("health_death_bonus") * Difficulty:getNpcStatScale()
|
||||
end
|
||||
function modifier_witch_base.prototype.GetModifierPhysicalArmorBonus(self, event)
|
||||
return self:GetStackCount() * self:GetAbility():GetSpecialValueFor("armor_death_bonus") * Difficulty:getNpcStatScale()
|
||||
end
|
||||
modifier_witch_base = __TS__Decorate(
|
||||
modifier_witch_base,
|
||||
modifier_witch_base,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_witch_base"}
|
||||
)
|
||||
____exports.modifier_witch_base = modifier_witch_base
|
||||
return ____exports
|
||||
@@ -0,0 +1,115 @@
|
||||
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 ____difficulty_manager = require("difficulty_manager")
|
||||
local Difficulty = ____difficulty_manager.Difficulty
|
||||
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
|
||||
____exports.zombie_armor_decress = __TS__Class()
|
||||
local zombie_armor_decress = ____exports.zombie_armor_decress
|
||||
zombie_armor_decress.name = "zombie_armor_decress"
|
||||
zombie_armor_decress.____file_path = "scripts/vscripts/abilities/creep/zombie_armor_decress.lua"
|
||||
__TS__ClassExtends(zombie_armor_decress, BaseAbility)
|
||||
function zombie_armor_decress.prototype.GetIntrinsicModifierName(self)
|
||||
return "modifier_zombie_armor_decress"
|
||||
end
|
||||
zombie_armor_decress = __TS__Decorate(
|
||||
zombie_armor_decress,
|
||||
zombie_armor_decress,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "zombie_armor_decress"}
|
||||
)
|
||||
____exports.zombie_armor_decress = zombie_armor_decress
|
||||
____exports.modifier_zombie_armor_decress = __TS__Class()
|
||||
local modifier_zombie_armor_decress = ____exports.modifier_zombie_armor_decress
|
||||
modifier_zombie_armor_decress.name = "modifier_zombie_armor_decress"
|
||||
modifier_zombie_armor_decress.____file_path = "scripts/vscripts/abilities/creep/zombie_armor_decress.lua"
|
||||
__TS__ClassExtends(modifier_zombie_armor_decress, BaseModifier)
|
||||
function modifier_zombie_armor_decress.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_zombie_armor_decress.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_zombie_armor_decress.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_EVENT_ON_ATTACK_LANDED}
|
||||
end
|
||||
function modifier_zombie_armor_decress.prototype.OnAttackLanded(self, event)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if event.attacker ~= self:GetParent() then
|
||||
return
|
||||
end
|
||||
if not event.target or not event.target:IsAlive() then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
local duration = ability:GetSpecialValueFor("corruption_duration")
|
||||
event.target:AddNewModifier(
|
||||
self:GetParent(),
|
||||
ability,
|
||||
____exports.modifier_zombie_armor_decress_debuff.name,
|
||||
{duration = duration}
|
||||
)
|
||||
end
|
||||
modifier_zombie_armor_decress = __TS__Decorate(
|
||||
modifier_zombie_armor_decress,
|
||||
modifier_zombie_armor_decress,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_zombie_armor_decress"}
|
||||
)
|
||||
____exports.modifier_zombie_armor_decress = modifier_zombie_armor_decress
|
||||
____exports.modifier_zombie_armor_decress_debuff = __TS__Class()
|
||||
local modifier_zombie_armor_decress_debuff = ____exports.modifier_zombie_armor_decress_debuff
|
||||
modifier_zombie_armor_decress_debuff.name = "modifier_zombie_armor_decress_debuff"
|
||||
modifier_zombie_armor_decress_debuff.____file_path = "scripts/vscripts/abilities/creep/zombie_armor_decress.lua"
|
||||
__TS__ClassExtends(modifier_zombie_armor_decress_debuff, BaseModifier)
|
||||
function modifier_zombie_armor_decress_debuff.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.armorDebuffBase = 0
|
||||
end
|
||||
function modifier_zombie_armor_decress_debuff.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_zombie_armor_decress_debuff.prototype.GetAttributes(self)
|
||||
return MODIFIER_ATTRIBUTE_MULTIPLE
|
||||
end
|
||||
function modifier_zombie_armor_decress_debuff.prototype.IsDebuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_zombie_armor_decress_debuff.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
function modifier_zombie_armor_decress_debuff.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_PHYSICAL_ARMOR_BONUS}
|
||||
end
|
||||
function modifier_zombie_armor_decress_debuff.prototype.OnCreated(self)
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
self.armorDebuffBase = ability:GetSpecialValueFor("armor_debuff")
|
||||
end
|
||||
function modifier_zombie_armor_decress_debuff.prototype.OnRefresh(self)
|
||||
self:OnCreated()
|
||||
end
|
||||
function modifier_zombie_armor_decress_debuff.prototype.GetModifierPhysicalArmorBonus(self)
|
||||
return self.armorDebuffBase * Difficulty:getNpcStatScale()
|
||||
end
|
||||
modifier_zombie_armor_decress_debuff = __TS__Decorate(
|
||||
modifier_zombie_armor_decress_debuff,
|
||||
modifier_zombie_armor_decress_debuff,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_zombie_armor_decress_debuff"}
|
||||
)
|
||||
____exports.modifier_zombie_armor_decress_debuff = modifier_zombie_armor_decress_debuff
|
||||
return ____exports
|
||||
@@ -0,0 +1,123 @@
|
||||
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 ____creep_render_color = require("utils.creep_render_color")
|
||||
local trySetIntrinsicCreepRenderColor = ____creep_render_color.trySetIntrinsicCreepRenderColor
|
||||
--- Пассив: при смерти взрыв по радиусу — урон и союзникам, и врагам (герои и крипы).
|
||||
____exports.zombie_death_explosion = __TS__Class()
|
||||
local zombie_death_explosion = ____exports.zombie_death_explosion
|
||||
zombie_death_explosion.name = "zombie_death_explosion"
|
||||
zombie_death_explosion.____file_path = "scripts/vscripts/abilities/creep/zombie_death_explosion.lua"
|
||||
__TS__ClassExtends(zombie_death_explosion, BaseAbility)
|
||||
function zombie_death_explosion.prototype.GetIntrinsicModifierName(self)
|
||||
return "modifier_zombie_death_explosion_listener"
|
||||
end
|
||||
function zombie_death_explosion.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_techies/techies_remote_mines_detonate.vpcf", context)
|
||||
end
|
||||
zombie_death_explosion = __TS__Decorate(
|
||||
zombie_death_explosion,
|
||||
zombie_death_explosion,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "zombie_death_explosion"}
|
||||
)
|
||||
____exports.zombie_death_explosion = zombie_death_explosion
|
||||
____exports.modifier_zombie_death_explosion_listener = __TS__Class()
|
||||
local modifier_zombie_death_explosion_listener = ____exports.modifier_zombie_death_explosion_listener
|
||||
modifier_zombie_death_explosion_listener.name = "modifier_zombie_death_explosion_listener"
|
||||
modifier_zombie_death_explosion_listener.____file_path = "scripts/vscripts/abilities/creep/zombie_death_explosion.lua"
|
||||
__TS__ClassExtends(modifier_zombie_death_explosion_listener, BaseModifier)
|
||||
function modifier_zombie_death_explosion_listener.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_zombie_death_explosion_listener.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_zombie_death_explosion_listener.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_EVENT_ON_DEATH}
|
||||
end
|
||||
function modifier_zombie_death_explosion_listener.prototype.OnCreated(self, params)
|
||||
if IsServer() then
|
||||
trySetIntrinsicCreepRenderColor(
|
||||
nil,
|
||||
self:GetParent(),
|
||||
255,
|
||||
0,
|
||||
0
|
||||
)
|
||||
end
|
||||
end
|
||||
function modifier_zombie_death_explosion_listener.prototype.OnDeath(self, event)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
if event.unit ~= parent then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
local radius = ability:GetSpecialValueFor("radius")
|
||||
local baseDamage = ability:GetSpecialValueFor("explosion_damage") + self:GetCaster():GetAttackDamage() * 0.35
|
||||
local origin = parent:GetAbsOrigin()
|
||||
local particle = ParticleManager:CreateParticle("particles/units/heroes/hero_techies/techies_remote_mines_detonate.vpcf", PATTACH_WORLDORIGIN, nil)
|
||||
ParticleManager:SetParticleControl(particle, 0, origin)
|
||||
ParticleManager:SetParticleControl(
|
||||
particle,
|
||||
1,
|
||||
Vector(radius, radius, radius)
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(particle)
|
||||
EmitSoundOn("Hero_Techies.RemoteMine.Detonate", parent)
|
||||
local units = FindUnitsInRadius(
|
||||
parent:GetTeamNumber(),
|
||||
origin,
|
||||
nil,
|
||||
radius,
|
||||
DOTA_UNIT_TARGET_TEAM_BOTH,
|
||||
bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC),
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
local deadIndex = parent:entindex()
|
||||
for ____, target in ipairs(units) do
|
||||
do
|
||||
if not target or not IsValidEntity(target) or not target:IsAlive() then
|
||||
goto __continue13
|
||||
end
|
||||
if target:entindex() == deadIndex then
|
||||
goto __continue13
|
||||
end
|
||||
local damage = target:IsRangedAttacker() and baseDamage or baseDamage * 0.25
|
||||
if target:GetTeamNumber() == parent:GetTeamNumber() then
|
||||
damage = damage * 0.5
|
||||
end
|
||||
ApplyDamage({
|
||||
victim = target,
|
||||
attacker = parent,
|
||||
damage = damage,
|
||||
damage_type = DAMAGE_TYPE_PHYSICAL,
|
||||
ability = ability
|
||||
})
|
||||
end
|
||||
::__continue13::
|
||||
end
|
||||
end
|
||||
modifier_zombie_death_explosion_listener = __TS__Decorate(
|
||||
modifier_zombie_death_explosion_listener,
|
||||
modifier_zombie_death_explosion_listener,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_zombie_death_explosion_listener"}
|
||||
)
|
||||
____exports.modifier_zombie_death_explosion_listener = modifier_zombie_death_explosion_listener
|
||||
return ____exports
|
||||
@@ -0,0 +1,227 @@
|
||||
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 ____difficulty_manager = require("difficulty_manager")
|
||||
local Difficulty = ____difficulty_manager.Difficulty
|
||||
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 ZOMBIE_VIRUS_DEBUFF_NAME = "modifier_zombie_virus_debuff"
|
||||
local ZOMBIE_VIRUS_MAX_INSTANCES = 9
|
||||
local function countZombieVirusDebuffInstances(self, unit)
|
||||
local n = 0
|
||||
do
|
||||
local i = 0
|
||||
while i < unit:GetModifierCount() do
|
||||
if unit:GetModifierNameByIndex(i) == ZOMBIE_VIRUS_DEBUFF_NAME then
|
||||
n = n + 1
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
return n
|
||||
end
|
||||
____exports.zombie_virus = __TS__Class()
|
||||
local zombie_virus = ____exports.zombie_virus
|
||||
zombie_virus.name = "zombie_virus"
|
||||
zombie_virus.____file_path = "scripts/vscripts/abilities/creep/zombie_virus.lua"
|
||||
__TS__ClassExtends(zombie_virus, BaseAbility)
|
||||
function zombie_virus.prototype.GetIntrinsicModifierName(self)
|
||||
return "modifier_zombie_virus_intrinsic"
|
||||
end
|
||||
zombie_virus = __TS__Decorate(
|
||||
zombie_virus,
|
||||
zombie_virus,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "zombie_virus"}
|
||||
)
|
||||
____exports.zombie_virus = zombie_virus
|
||||
____exports.modifier_zombie_virus_intrinsic = __TS__Class()
|
||||
local modifier_zombie_virus_intrinsic = ____exports.modifier_zombie_virus_intrinsic
|
||||
modifier_zombie_virus_intrinsic.name = "modifier_zombie_virus_intrinsic"
|
||||
modifier_zombie_virus_intrinsic.____file_path = "scripts/vscripts/abilities/creep/zombie_virus.lua"
|
||||
__TS__ClassExtends(modifier_zombie_virus_intrinsic, BaseModifier)
|
||||
function modifier_zombie_virus_intrinsic.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_zombie_virus_intrinsic.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_zombie_virus_intrinsic.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_zombie_virus_intrinsic.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
end
|
||||
function modifier_zombie_virus_intrinsic.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_EVENT_ON_ATTACK_LANDED}
|
||||
end
|
||||
function modifier_zombie_virus_intrinsic.prototype.OnAttackLanded(self, event)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if event.attacker ~= self:GetParent() then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
local primaryZombieVirus = event.attacker:FindAbilityByName("zombie_virus")
|
||||
if not primaryZombieVirus or ability ~= primaryZombieVirus then
|
||||
return
|
||||
end
|
||||
local target = event.target
|
||||
if not target or target:GetUnitName() == "npc_homer" then
|
||||
return
|
||||
end
|
||||
local duration = ability:GetSpecialValueFor("duration")
|
||||
if RandomInt(1, 100) > ability:GetSpecialValueFor("chance") then
|
||||
return
|
||||
end
|
||||
if countZombieVirusDebuffInstances(nil, target) >= ZOMBIE_VIRUS_MAX_INSTANCES then
|
||||
return
|
||||
end
|
||||
target:AddNewModifier(
|
||||
self:GetParent(),
|
||||
ability,
|
||||
ZOMBIE_VIRUS_DEBUFF_NAME,
|
||||
{duration = duration}
|
||||
)
|
||||
end
|
||||
modifier_zombie_virus_intrinsic = __TS__Decorate(
|
||||
modifier_zombie_virus_intrinsic,
|
||||
modifier_zombie_virus_intrinsic,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_zombie_virus_intrinsic"}
|
||||
)
|
||||
____exports.modifier_zombie_virus_intrinsic = modifier_zombie_virus_intrinsic
|
||||
____exports.modifier_zombie_virus_debuff = __TS__Class()
|
||||
local modifier_zombie_virus_debuff = ____exports.modifier_zombie_virus_debuff
|
||||
modifier_zombie_virus_debuff.name = "modifier_zombie_virus_debuff"
|
||||
modifier_zombie_virus_debuff.____file_path = "scripts/vscripts/abilities/creep/zombie_virus.lua"
|
||||
__TS__ClassExtends(modifier_zombie_virus_debuff, BaseModifier)
|
||||
function modifier_zombie_virus_debuff.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_zombie_virus_debuff.prototype.IsDebuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_zombie_virus_debuff.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
function modifier_zombie_virus_debuff.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE, MODIFIER_PROPERTY_PHYSICAL_ARMOR_BONUS}
|
||||
end
|
||||
function modifier_zombie_virus_debuff.prototype.GetModifierMoveSpeedBonus_Percentage(self)
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return 0
|
||||
end
|
||||
return -ability:GetSpecialValueFor("slow_movespeed") * Difficulty:getNpcStatScale()
|
||||
end
|
||||
function modifier_zombie_virus_debuff.prototype.GetModifierPhysicalArmorBonus(self)
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return 0
|
||||
end
|
||||
return -ability:GetSpecialValueFor("armor") * Difficulty:getNpcStatScale()
|
||||
end
|
||||
function modifier_zombie_virus_debuff.prototype.OnCreated(self)
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
self.damage = ability:GetSpecialValueFor("damage")
|
||||
self.tickInterval = ability:GetSpecialValueFor("tick_interval")
|
||||
if IsServer() then
|
||||
local caster = self:GetCaster()
|
||||
if not caster or caster:IsNull() or not IsValidEntity(caster) or not caster:IsAlive() then
|
||||
self:Destroy()
|
||||
return
|
||||
end
|
||||
self:StartIntervalThink(self.tickInterval)
|
||||
self.particleId = ParticleManager:CreateParticle(
|
||||
"particles/econ/items/viper/viper_ti7_immortal/viper_poison_debuff_ti7.vpcf",
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
self:GetParent()
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
self.particleId,
|
||||
0,
|
||||
self:GetParent():GetAbsOrigin()
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
self.particleId,
|
||||
1,
|
||||
self:GetParent():GetAbsOrigin()
|
||||
)
|
||||
end
|
||||
end
|
||||
function modifier_zombie_virus_debuff.prototype.OnRefresh(self, params)
|
||||
if self.particleId then
|
||||
ParticleManager:DestroyParticle(self.particleId, false)
|
||||
ParticleManager:ReleaseParticleIndex(self.particleId)
|
||||
end
|
||||
end
|
||||
function modifier_zombie_virus_debuff.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
local caster = self:GetCaster()
|
||||
local target = self:GetParent()
|
||||
if not ability or ability:IsNull() or not caster or caster:IsNull() or not IsValidEntity(caster) or not caster:IsAlive() then
|
||||
self:Destroy()
|
||||
return
|
||||
end
|
||||
local tickDamage = self.damage * self.tickInterval * Difficulty:getNpcStatScale()
|
||||
if target and target:IsAlive() then
|
||||
ApplyDamage({
|
||||
victim = target,
|
||||
attacker = caster,
|
||||
damage = tickDamage,
|
||||
damage_type = DAMAGE_TYPE_PHYSICAL,
|
||||
ability = ability
|
||||
})
|
||||
SendOverheadEventMessage(
|
||||
nil,
|
||||
OVERHEAD_ALERT_DAMAGE,
|
||||
target,
|
||||
tickDamage,
|
||||
nil
|
||||
)
|
||||
end
|
||||
end
|
||||
function modifier_zombie_virus_debuff.prototype.OnDestroy(self)
|
||||
if IsClient() then
|
||||
return
|
||||
end
|
||||
if self.particleId then
|
||||
ParticleManager:DestroyParticle(self.particleId, false)
|
||||
ParticleManager:ReleaseParticleIndex(self.particleId)
|
||||
end
|
||||
end
|
||||
function modifier_zombie_virus_debuff.prototype.GetEffectName(self)
|
||||
return "particles/econ/items/void_spirit/void_spirit_immortal_2021/void_spirit_immortal_2021_astral_step_debuff.vpcf"
|
||||
end
|
||||
function modifier_zombie_virus_debuff.prototype.GetTexture(self)
|
||||
return "life_stealer_open_wounds"
|
||||
end
|
||||
function modifier_zombie_virus_debuff.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_ABSORIGIN_FOLLOW
|
||||
end
|
||||
modifier_zombie_virus_debuff = __TS__Decorate(
|
||||
modifier_zombie_virus_debuff,
|
||||
modifier_zombie_virus_debuff,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_zombie_virus_debuff"}
|
||||
)
|
||||
____exports.modifier_zombie_virus_debuff = modifier_zombie_virus_debuff
|
||||
return ____exports
|
||||
@@ -0,0 +1,56 @@
|
||||
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
|
||||
____exports.fish_basic = __TS__Class()
|
||||
local fish_basic = ____exports.fish_basic
|
||||
fish_basic.name = "fish_basic"
|
||||
fish_basic.____file_path = "scripts/vscripts/abilities/fish_basic.lua"
|
||||
__TS__ClassExtends(fish_basic, BaseAbility)
|
||||
function fish_basic.prototype.GetIntrinsicModifierName(self)
|
||||
return "modifier_fish_basic"
|
||||
end
|
||||
fish_basic = __TS__Decorate(
|
||||
fish_basic,
|
||||
fish_basic,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "fish_basic"}
|
||||
)
|
||||
____exports.fish_basic = fish_basic
|
||||
____exports.modifier_fish_basic = __TS__Class()
|
||||
local modifier_fish_basic = ____exports.modifier_fish_basic
|
||||
modifier_fish_basic.name = "modifier_fish_basic"
|
||||
modifier_fish_basic.____file_path = "scripts/vscripts/abilities/fish_basic.lua"
|
||||
__TS__ClassExtends(modifier_fish_basic, BaseModifier)
|
||||
function modifier_fish_basic.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_fish_basic.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_fish_basic.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_fish_basic.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_NO_HEALTH_BAR] = true, [MODIFIER_STATE_UNSELECTABLE] = true, [MODIFIER_STATE_UNTARGETABLE] = true}
|
||||
end
|
||||
function modifier_fish_basic.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_MIN_HEALTH}
|
||||
end
|
||||
function modifier_fish_basic.prototype.GetMinHealth(self)
|
||||
return self:GetParent():GetHealth()
|
||||
end
|
||||
modifier_fish_basic = __TS__Decorate(
|
||||
modifier_fish_basic,
|
||||
modifier_fish_basic,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_fish_basic"}
|
||||
)
|
||||
____exports.modifier_fish_basic = modifier_fish_basic
|
||||
return ____exports
|
||||
@@ -0,0 +1,39 @@
|
||||
--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
|
||||
local ____exports = {}
|
||||
local ____hero_rage_whitelist = require("abilities.hero_rage.hero_rage_whitelist")
|
||||
local isUnitNameAllowedForHeroRage = ____hero_rage_whitelist.isUnitNameAllowedForHeroRage
|
||||
--- Параметры в духе infinity_levels / hero_rage.
|
||||
local DEFAULT_HERO_RAGE = {
|
||||
max_rage = 100,
|
||||
rage_per_attack = 3,
|
||||
rage_per_damage = 1,
|
||||
time_decrase_rage = 4,
|
||||
tick_decrase_rage = 0.5
|
||||
}
|
||||
local MOD_NAME = "modifier_hero_rage"
|
||||
--- Вешает систему «ярости» (мана = ярость) на героя, если ещё не висит.
|
||||
-- Нужна любая способность-носитель для AddNewModifier (у всех наших героев есть ability_stacking_crit).
|
||||
-- **Только** герои из `hero_rage_whitelist.ts`.
|
||||
function ____exports.tryApplyDefaultHeroRage(self, hero)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if not hero:IsRealHero() or hero:IsIllusion() then
|
||||
return
|
||||
end
|
||||
if not isUnitNameAllowedForHeroRage(
|
||||
nil,
|
||||
hero:GetUnitName()
|
||||
) then
|
||||
return
|
||||
end
|
||||
if hero:HasModifier(MOD_NAME) then
|
||||
return
|
||||
end
|
||||
local host = hero:FindAbilityByName("ability_stacking_crit")
|
||||
if not host then
|
||||
return
|
||||
end
|
||||
hero:AddNewModifier(hero, host, MOD_NAME, DEFAULT_HERO_RAGE)
|
||||
end
|
||||
return ____exports
|
||||
@@ -0,0 +1,222 @@
|
||||
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 BaseModifier = ____dota_ts_adapter.BaseModifier
|
||||
local registerModifier = ____dota_ts_adapter.registerModifier
|
||||
local ____hero_rage_nettable = require("abilities.hero_rage.hero_rage_nettable")
|
||||
local clearHeroRageNetTable = ____hero_rage_nettable.clearHeroRageNetTable
|
||||
local syncHeroRageNetTable = ____hero_rage_nettable.syncHeroRageNetTable
|
||||
local DEFAULT_MAX = 100
|
||||
local DEFAULT_RAGE_PER_ATTACK = 3
|
||||
local DEFAULT_RAGE_WHEN_HIT = 1
|
||||
local DEFAULT_OUT_OF_COMBAT = 4
|
||||
--- Как у infinity: шаг, с которым списывается 1 ед. ярости после тайм-аута «без прироста».
|
||||
local DEFAULT_DECAY_COOLDOWN = 0.5
|
||||
--- Реже тик — меньше нагрузка (раньше 0.05 сильно лагало).
|
||||
local MANA_SYNC = 0.12
|
||||
local function readParam(self, params, key, def)
|
||||
if params == nil then
|
||||
return def
|
||||
end
|
||||
local t = params
|
||||
local v = t[key]
|
||||
if v == nil or v == nil then
|
||||
return def
|
||||
end
|
||||
local n = tonumber(v)
|
||||
return n ~= nil and n ~= nil and n or def
|
||||
end
|
||||
--- Максимальная и текущая «мана» героя = шкала ярости; списание маны при кастах = ярость.
|
||||
-- Накопление: удары и получение автоатак, затухание после простоя.
|
||||
____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/hero_rage/hero_rage_modifiers.lua"
|
||||
__TS__ClassExtends(modifier_hero_rage, BaseModifier)
|
||||
function modifier_hero_rage.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.maxRage = DEFAULT_MAX
|
||||
self.ragePerAttack = DEFAULT_RAGE_PER_ATTACK
|
||||
self.rageWhenHit = DEFAULT_RAGE_WHEN_HIT
|
||||
self.timeOutForDecay = DEFAULT_OUT_OF_COMBAT
|
||||
self.decayStep = DEFAULT_DECAY_COOLDOWN
|
||||
self.manaDelta = 0
|
||||
self.timeSinceRageGain = 0
|
||||
self.decayAcc = 0
|
||||
self.lastNetCur = -1
|
||||
self.lastNetMax = -1
|
||||
self.intellectReadLock = false
|
||||
end
|
||||
function modifier_hero_rage.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_hero_rage.prototype.IsDebuff(self)
|
||||
return false
|
||||
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_PROPERTY_MANA_BONUS, MODIFIER_PROPERTY_STATS_INTELLECT_BONUS, MODIFIER_EVENT_ON_ATTACK_LANDED, MODIFIER_EVENT_ON_DEATH}
|
||||
end
|
||||
function modifier_hero_rage.prototype.OnCreated(self, params)
|
||||
self.parentHero = self:GetParent()
|
||||
self.maxRage = readParam(nil, params, "max_rage", DEFAULT_MAX)
|
||||
self.ragePerAttack = readParam(nil, params, "rage_per_attack", DEFAULT_RAGE_PER_ATTACK)
|
||||
self.rageWhenHit = readParam(nil, params, "rage_per_damage", DEFAULT_RAGE_WHEN_HIT)
|
||||
self.timeOutForDecay = readParam(nil, params, "time_decrase_rage", DEFAULT_OUT_OF_COMBAT)
|
||||
self.decayStep = readParam(nil, params, "tick_decrase_rage", DEFAULT_DECAY_COOLDOWN)
|
||||
self.timeSinceRageGain = 0
|
||||
self.decayAcc = 0
|
||||
self.manaDelta = 0
|
||||
if IsServer() then
|
||||
self:StartIntervalThink(MANA_SYNC)
|
||||
self.parentHero:SetMana(0)
|
||||
self.lastNetCur = -1
|
||||
self.lastNetMax = -1
|
||||
self.parentHero:CalculateStatBonus(true)
|
||||
syncHeroRageNetTable(
|
||||
nil,
|
||||
self.parentHero,
|
||||
0,
|
||||
self.maxRage,
|
||||
true
|
||||
)
|
||||
end
|
||||
end
|
||||
function modifier_hero_rage.prototype.OnRefresh(self, params)
|
||||
self.maxRage = readParam(nil, params, "max_rage", self.maxRage)
|
||||
end
|
||||
function modifier_hero_rage.prototype.GetModifierManaBonus(self)
|
||||
return self.manaDelta
|
||||
end
|
||||
function modifier_hero_rage.prototype.GetModifierBonusStats_Intellect(self)
|
||||
if self.intellectReadLock then
|
||||
return 0
|
||||
end
|
||||
local p = self:GetParent()
|
||||
if not p or p:IsNull() then
|
||||
return 0
|
||||
end
|
||||
self.intellectReadLock = true
|
||||
local int = p:GetIntellect(true)
|
||||
self.intellectReadLock = false
|
||||
return int * -1
|
||||
end
|
||||
function modifier_hero_rage.prototype.OnAttackLanded(self, event)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local p = self.parentHero
|
||||
if event.attacker == p then
|
||||
self:bumpRage(self.ragePerAttack)
|
||||
elseif event.target == p then
|
||||
self:bumpRage(self.rageWhenHit)
|
||||
end
|
||||
end
|
||||
function modifier_hero_rage.prototype.OnDeath(self, event)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if event.unit ~= self:GetParent() then
|
||||
return
|
||||
end
|
||||
self.parentHero:SetMana(0)
|
||||
self:pushNet(0, self.maxRage, true)
|
||||
end
|
||||
function modifier_hero_rage.prototype.bumpRage(self, amt)
|
||||
if amt <= 0 then
|
||||
return
|
||||
end
|
||||
self.timeSinceRageGain = 0
|
||||
self.decayAcc = 0
|
||||
local p = self.parentHero
|
||||
local m = p:GetMaxMana()
|
||||
p:SetMana(math.min(
|
||||
m,
|
||||
p:GetMana() + amt
|
||||
))
|
||||
end
|
||||
function modifier_hero_rage.prototype.stripPassiveManaRegen(self, p)
|
||||
if not p or p:IsNull() then
|
||||
return
|
||||
end
|
||||
local regenPerSec = p:GetManaRegen()
|
||||
if regenPerSec > 0 then
|
||||
p:SetMana(math.max(
|
||||
0,
|
||||
p:GetMana() - regenPerSec * MANA_SYNC
|
||||
))
|
||||
end
|
||||
end
|
||||
function modifier_hero_rage.prototype.pushNet(self, cur, max, force)
|
||||
if force == nil then
|
||||
force = false
|
||||
end
|
||||
if not force and self.lastNetCur == cur and self.lastNetMax == max then
|
||||
return
|
||||
end
|
||||
self.lastNetCur = cur
|
||||
self.lastNetMax = max
|
||||
syncHeroRageNetTable(
|
||||
nil,
|
||||
self.parentHero,
|
||||
cur,
|
||||
max,
|
||||
true
|
||||
)
|
||||
end
|
||||
function modifier_hero_rage.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local p = self.parentHero
|
||||
if not p or p:IsNull() or not p:IsAlive() then
|
||||
return
|
||||
end
|
||||
self.manaDelta = self.maxRage - p:GetMaxMana() + (self.manaDelta or 0)
|
||||
p:CalculateStatBonus(true)
|
||||
p:SetMana(math.min(
|
||||
p:GetMana(),
|
||||
p:GetMaxMana()
|
||||
))
|
||||
self:stripPassiveManaRegen(p)
|
||||
self.timeSinceRageGain = self.timeSinceRageGain + MANA_SYNC
|
||||
if self.timeSinceRageGain >= self.timeOutForDecay then
|
||||
self.decayAcc = self.decayAcc + MANA_SYNC
|
||||
if self.decayAcc >= self.decayStep then
|
||||
p:SetMana(math.max(
|
||||
0,
|
||||
p:GetMana() - 1
|
||||
))
|
||||
self.decayAcc = self.decayAcc - self.decayStep
|
||||
end
|
||||
end
|
||||
self:pushNet(
|
||||
p:GetMana(),
|
||||
math.min(
|
||||
p:GetMaxMana(),
|
||||
self.maxRage
|
||||
),
|
||||
false
|
||||
)
|
||||
end
|
||||
function modifier_hero_rage.prototype.OnDestroy(self)
|
||||
if IsServer() then
|
||||
clearHeroRageNetTable(nil, self.parentHero)
|
||||
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
|
||||
return ____exports
|
||||
@@ -0,0 +1,41 @@
|
||||
--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
|
||||
local ____exports = {}
|
||||
--- Синхронизация «ярости» (игровая мана) с клиентом для кастомного HUD.
|
||||
local TABLE = "hero_rage"
|
||||
function ____exports.syncHeroRageNetTable(self, hero, current, max, active)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local pid = hero:GetPlayerOwnerID()
|
||||
if pid < 0 then
|
||||
return
|
||||
end
|
||||
local row = {
|
||||
current = math.floor(current + 0.5),
|
||||
max = math.max(
|
||||
0,
|
||||
math.floor(max + 0.5)
|
||||
),
|
||||
active = active and 1 or 0
|
||||
}
|
||||
CustomNetTables:SetTableValue(
|
||||
TABLE,
|
||||
tostring(pid),
|
||||
row
|
||||
)
|
||||
end
|
||||
function ____exports.clearHeroRageNetTable(self, hero)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local pid = hero:GetPlayerOwnerID()
|
||||
if pid < 0 then
|
||||
return
|
||||
end
|
||||
CustomNetTables:SetTableValue(
|
||||
TABLE,
|
||||
tostring(pid),
|
||||
{current = 0, max = 0, active = 0}
|
||||
)
|
||||
end
|
||||
return ____exports
|
||||
@@ -0,0 +1,13 @@
|
||||
--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
|
||||
local ____exports = {}
|
||||
--- Ярость выдаётся **только** героям, чьё `GetUnitName()` есть в этой таблице.
|
||||
-- Имена — как в KV/npc: `npc_dota_hero_*`, кастомы — `npc_из_твоей_таверны_hero_...` если так заведено.
|
||||
local HERO_RAGE_UNIT_NAMES = {npc_dota_hero_axe = true, npc_dota_hero_sven = true, npc_dota_hero_juggernaut = true}
|
||||
--- Проверка, входит ли юнит в белый список ярости.
|
||||
function ____exports.isUnitNameAllowedForHeroRage(self, unitName)
|
||||
if unitName == nil or unitName == nil or unitName == "" then
|
||||
return false
|
||||
end
|
||||
return HERO_RAGE_UNIT_NAMES[unitName] == true
|
||||
end
|
||||
return ____exports
|
||||
@@ -0,0 +1,352 @@
|
||||
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 ____modifier_general_hunger = require("abilities.modifiers.modifier_general_hunger")
|
||||
local modifier_general_hunger = ____modifier_general_hunger.modifier_general_hunger
|
||||
local ____heal_tracker = require("utils.heal_tracker")
|
||||
local HealWithBattlePass = ____heal_tracker.HealWithBattlePass
|
||||
____exports.AXE_BATTLE_HUNGER_CUSTOM = "axe_battle_hunger_custom"
|
||||
--- Вызывается из Berserker's Call (шард): вешает голод даже без клика, если способность прокачана.
|
||||
function ____exports.axeApplyBattleHungerFromCall(self, caster, target, duration)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local hunger = caster:FindAbilityByName(____exports.AXE_BATTLE_HUNGER_CUSTOM)
|
||||
if not hunger or hunger:GetLevel() <= 0 then
|
||||
return
|
||||
end
|
||||
if target:GetTeamNumber() == caster:GetTeamNumber() then
|
||||
target:AddNewModifier(caster, hunger, ____exports.modifier_axe_battle_hunger_ally_buff.name, {duration = duration})
|
||||
else
|
||||
target:AddNewModifier(caster, hunger, ____exports.modifier_axe_battle_hunger_debuff.name, {duration = duration})
|
||||
end
|
||||
end
|
||||
____exports.axe_battle_hunger_custom = __TS__Class()
|
||||
local axe_battle_hunger_custom = ____exports.axe_battle_hunger_custom
|
||||
axe_battle_hunger_custom.name = "axe_battle_hunger_custom"
|
||||
axe_battle_hunger_custom.____file_path = "scripts/vscripts/abilities/heroes/axe/axe_battle_hunger_custom.lua"
|
||||
__TS__ClassExtends(axe_battle_hunger_custom, BaseAbility)
|
||||
function axe_battle_hunger_custom.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_axe/axe_battle_hunger.vpcf", context)
|
||||
PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_axe.vsndevts", context)
|
||||
end
|
||||
function axe_battle_hunger_custom.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local target = self:GetCursorTarget()
|
||||
if not target then
|
||||
return
|
||||
end
|
||||
local duration = self:GetSpecialValueFor("duration")
|
||||
if target:GetTeamNumber() == caster:GetTeamNumber() then
|
||||
target:AddNewModifier(caster, self, ____exports.modifier_axe_battle_hunger_ally_buff.name, {duration = duration})
|
||||
else
|
||||
target:AddNewModifier(caster, self, ____exports.modifier_axe_battle_hunger_debuff.name, {duration = duration})
|
||||
end
|
||||
EmitSoundOn("Hero_Axe.Battle_Hunger", target)
|
||||
end
|
||||
function axe_battle_hunger_custom.prototype.GetCastRange(self)
|
||||
return self:GetSpecialValueFor("AbilityCastRange")
|
||||
end
|
||||
function axe_battle_hunger_custom.prototype.CastFilterResultTarget(self, target)
|
||||
if target:IsInvulnerable() then
|
||||
return UF_FAIL_CUSTOM
|
||||
end
|
||||
return UF_SUCCESS
|
||||
end
|
||||
function axe_battle_hunger_custom.prototype.GetAbilityTargetTeam(self)
|
||||
return DOTA_UNIT_TARGET_TEAM_BOTH
|
||||
end
|
||||
axe_battle_hunger_custom.ALLY_HUNGER_STACKS_PER_SECOND = 5
|
||||
axe_battle_hunger_custom.ALLY_HEAL_PCT_PER_SECOND = 1.5
|
||||
axe_battle_hunger_custom = __TS__Decorate(
|
||||
axe_battle_hunger_custom,
|
||||
axe_battle_hunger_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "axe_battle_hunger_custom"}
|
||||
)
|
||||
____exports.axe_battle_hunger_custom = axe_battle_hunger_custom
|
||||
--- На Аксе: +% скорости за каждого врага с Battle Hunger.
|
||||
____exports.modifier_axe_battle_hunger_owner_counter = __TS__Class()
|
||||
local modifier_axe_battle_hunger_owner_counter = ____exports.modifier_axe_battle_hunger_owner_counter
|
||||
modifier_axe_battle_hunger_owner_counter.name = "modifier_axe_battle_hunger_owner_counter"
|
||||
modifier_axe_battle_hunger_owner_counter.____file_path = "scripts/vscripts/abilities/heroes/axe/axe_battle_hunger_custom.lua"
|
||||
__TS__ClassExtends(modifier_axe_battle_hunger_owner_counter, BaseModifier)
|
||||
function modifier_axe_battle_hunger_owner_counter.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_axe_battle_hunger_owner_counter.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_axe_battle_hunger_owner_counter.changeStacksOnCaster(self, caster, delta)
|
||||
local ab = caster:FindAbilityByName(____exports.AXE_BATTLE_HUNGER_CUSTOM)
|
||||
if ab then
|
||||
local m = caster:FindModifierByName(____exports.modifier_axe_battle_hunger_owner_counter.name)
|
||||
if not m and delta > 0 then
|
||||
m = caster:AddNewModifier(caster, ab, ____exports.modifier_axe_battle_hunger_owner_counter.name, {})
|
||||
end
|
||||
if m and not m:IsNull() then
|
||||
local next = math.max(
|
||||
0,
|
||||
m:GetStackCount() + delta
|
||||
)
|
||||
m:SetStackCount(next)
|
||||
if next <= 0 then
|
||||
m:Destroy()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
function modifier_axe_battle_hunger_owner_counter.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE}
|
||||
end
|
||||
function modifier_axe_battle_hunger_owner_counter.prototype.GetModifierMoveSpeedBonus_Percentage(self)
|
||||
local ab = self:GetAbility()
|
||||
if not ab then
|
||||
return 0
|
||||
end
|
||||
local pct = ab:GetSpecialValueFor("speed_per_enemy_pct")
|
||||
return self:GetStackCount() * pct
|
||||
end
|
||||
modifier_axe_battle_hunger_owner_counter = __TS__Decorate(
|
||||
modifier_axe_battle_hunger_owner_counter,
|
||||
modifier_axe_battle_hunger_owner_counter,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_axe_battle_hunger_owner_counter"}
|
||||
)
|
||||
____exports.modifier_axe_battle_hunger_owner_counter = modifier_axe_battle_hunger_owner_counter
|
||||
____exports.modifier_axe_battle_hunger_debuff = __TS__Class()
|
||||
local modifier_axe_battle_hunger_debuff = ____exports.modifier_axe_battle_hunger_debuff
|
||||
modifier_axe_battle_hunger_debuff.name = "modifier_axe_battle_hunger_debuff"
|
||||
modifier_axe_battle_hunger_debuff.____file_path = "scripts/vscripts/abilities/heroes/axe/axe_battle_hunger_custom.lua"
|
||||
__TS__ClassExtends(modifier_axe_battle_hunger_debuff, BaseModifier)
|
||||
function modifier_axe_battle_hunger_debuff.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_axe_battle_hunger_debuff.prototype.IsDebuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_axe_battle_hunger_debuff.prototype.GetAttributes(self)
|
||||
return MODIFIER_ATTRIBUTE_MULTIPLE
|
||||
end
|
||||
function modifier_axe_battle_hunger_debuff.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
function modifier_axe_battle_hunger_debuff.prototype.GetTexture(self)
|
||||
return "axe_battle_hunger"
|
||||
end
|
||||
function modifier_axe_battle_hunger_debuff.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self:StartIntervalThink(1)
|
||||
____exports.modifier_axe_battle_hunger_owner_counter:changeStacksOnCaster(
|
||||
self:GetCaster(),
|
||||
1
|
||||
)
|
||||
local parent = self:GetParent()
|
||||
self.killListener = ListenToGameEvent(
|
||||
"entity_killed",
|
||||
function(event)
|
||||
if not parent or not parent:IsAlive() then
|
||||
return
|
||||
end
|
||||
local attacker = EntIndexToHScript(event.entindex_attacker)
|
||||
if attacker == parent then
|
||||
self:Destroy()
|
||||
end
|
||||
end,
|
||||
nil
|
||||
)
|
||||
end
|
||||
function modifier_axe_battle_hunger_debuff.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local caster = self:GetCaster()
|
||||
local ability = self:GetAbility()
|
||||
if not parent or not caster or not ability then
|
||||
return
|
||||
end
|
||||
local basePerSec = ability:GetSpecialValueFor("damage_per_second")
|
||||
local armorMult = ability:GetSpecialValueFor("armor_mult") * 0.01
|
||||
local armorBonus = caster:GetPhysicalArmorBaseValue() * armorMult
|
||||
local dmg = math.max(1, basePerSec + armorBonus)
|
||||
local dealtDamage = ApplyDamage({
|
||||
victim = parent,
|
||||
attacker = caster,
|
||||
damage = dmg,
|
||||
damage_type = ability:GetAbilityDamageType(),
|
||||
ability = ability
|
||||
})
|
||||
SendOverheadEventMessage(
|
||||
nil,
|
||||
OVERHEAD_ALERT_BONUS_SPELL_DAMAGE,
|
||||
parent,
|
||||
dealtDamage,
|
||||
caster:GetPlayerOwner()
|
||||
)
|
||||
end
|
||||
function modifier_axe_battle_hunger_debuff.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE}
|
||||
end
|
||||
function modifier_axe_battle_hunger_debuff.prototype.GetModifierMoveSpeedBonus_Percentage(self)
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return 0
|
||||
end
|
||||
local slow = ability:GetSpecialValueFor("slow")
|
||||
local parent = self:GetParent()
|
||||
local caster = self:GetCaster()
|
||||
if not parent or not caster then
|
||||
return 0
|
||||
end
|
||||
local toAxe = caster:GetAbsOrigin():__sub(parent:GetAbsOrigin()):Normalized()
|
||||
local fwd = parent:GetForwardVector()
|
||||
local facingAxe = fwd:Dot(toAxe)
|
||||
if facingAxe < 0.25 then
|
||||
return -slow
|
||||
end
|
||||
return 0
|
||||
end
|
||||
function modifier_axe_battle_hunger_debuff.prototype.GetEffectName(self)
|
||||
return "particles/units/heroes/hero_axe/axe_battle_hunger.vpcf"
|
||||
end
|
||||
function modifier_axe_battle_hunger_debuff.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_OVERHEAD_FOLLOW
|
||||
end
|
||||
function modifier_axe_battle_hunger_debuff.prototype.OnRefresh(self)
|
||||
if IsServer() then
|
||||
self:SetDuration(
|
||||
self:GetAbility():GetSpecialValueFor("duration"),
|
||||
true
|
||||
)
|
||||
end
|
||||
end
|
||||
function modifier_axe_battle_hunger_debuff.prototype.OnDestroy(self)
|
||||
if IsServer() then
|
||||
local caster = self:GetCaster()
|
||||
if caster then
|
||||
____exports.modifier_axe_battle_hunger_owner_counter:changeStacksOnCaster(caster, -1)
|
||||
end
|
||||
if self.killListener ~= nil then
|
||||
StopListeningToGameEvent(self.killListener)
|
||||
self.killListener = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
modifier_axe_battle_hunger_debuff = __TS__Decorate(
|
||||
modifier_axe_battle_hunger_debuff,
|
||||
modifier_axe_battle_hunger_debuff,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_axe_battle_hunger_debuff"}
|
||||
)
|
||||
____exports.modifier_axe_battle_hunger_debuff = modifier_axe_battle_hunger_debuff
|
||||
____exports.modifier_axe_battle_hunger_ally_buff = __TS__Class()
|
||||
local modifier_axe_battle_hunger_ally_buff = ____exports.modifier_axe_battle_hunger_ally_buff
|
||||
modifier_axe_battle_hunger_ally_buff.name = "modifier_axe_battle_hunger_ally_buff"
|
||||
modifier_axe_battle_hunger_ally_buff.____file_path = "scripts/vscripts/abilities/heroes/axe/axe_battle_hunger_custom.lua"
|
||||
__TS__ClassExtends(modifier_axe_battle_hunger_ally_buff, BaseModifier)
|
||||
function modifier_axe_battle_hunger_ally_buff.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_axe_battle_hunger_ally_buff.prototype.GetAttributes(self)
|
||||
return MODIFIER_ATTRIBUTE_MULTIPLE
|
||||
end
|
||||
function modifier_axe_battle_hunger_ally_buff.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_axe_battle_hunger_ally_buff.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
function modifier_axe_battle_hunger_ally_buff.prototype.GetTexture(self)
|
||||
return "axe_battle_hunger"
|
||||
end
|
||||
function modifier_axe_battle_hunger_ally_buff.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self:StartIntervalThink(1)
|
||||
end
|
||||
function modifier_axe_battle_hunger_ally_buff.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local caster = self:GetCaster()
|
||||
local ability = self:GetAbility()
|
||||
if not parent or not caster or not ability then
|
||||
return
|
||||
end
|
||||
local hunger = parent:FindModifierByName(modifier_general_hunger.name)
|
||||
if not hunger then
|
||||
hunger = parent:AddNewModifier(caster, ability, modifier_general_hunger.name, {})
|
||||
end
|
||||
if hunger and not hunger:IsNull() then
|
||||
do
|
||||
local i = 0
|
||||
while i < ____exports.axe_battle_hunger_custom.ALLY_HUNGER_STACKS_PER_SECOND do
|
||||
hunger:IncrementStackCount()
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
local heal = parent:GetMaxHealth() * (____exports.axe_battle_hunger_custom.ALLY_HEAL_PCT_PER_SECOND * 0.01)
|
||||
if heal > 0 then
|
||||
HealWithBattlePass(
|
||||
nil,
|
||||
parent,
|
||||
heal,
|
||||
ability,
|
||||
caster
|
||||
)
|
||||
SendOverheadEventMessage(
|
||||
nil,
|
||||
OVERHEAD_ALERT_HEAL,
|
||||
parent,
|
||||
heal,
|
||||
parent:GetPlayerOwner()
|
||||
)
|
||||
end
|
||||
end
|
||||
function modifier_axe_battle_hunger_ally_buff.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_BASEDAMAGEOUTGOING_PERCENTAGE, MODIFIER_PROPERTY_TOOLTIP}
|
||||
end
|
||||
function modifier_axe_battle_hunger_ally_buff.prototype.GetModifierBaseDamageOutgoing_Percentage(self)
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return 0
|
||||
end
|
||||
return ability:GetSpecialValueFor("speed_bonus")
|
||||
end
|
||||
function modifier_axe_battle_hunger_ally_buff.prototype.GetModifierTooltip(self)
|
||||
local parent = self:GetParent()
|
||||
if not parent then
|
||||
return 0
|
||||
end
|
||||
return math.floor(parent:GetMaxHealth() * (____exports.axe_battle_hunger_custom.ALLY_HEAL_PCT_PER_SECOND * 0.01) + 0.5)
|
||||
end
|
||||
function modifier_axe_battle_hunger_ally_buff.prototype.GetEffectName(self)
|
||||
return "particles/units/heroes/hero_axe/axe_battle_hunger.vpcf"
|
||||
end
|
||||
function modifier_axe_battle_hunger_ally_buff.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_OVERHEAD_FOLLOW
|
||||
end
|
||||
modifier_axe_battle_hunger_ally_buff = __TS__Decorate(
|
||||
modifier_axe_battle_hunger_ally_buff,
|
||||
modifier_axe_battle_hunger_ally_buff,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_axe_battle_hunger_ally_buff"}
|
||||
)
|
||||
____exports.modifier_axe_battle_hunger_ally_buff = modifier_axe_battle_hunger_ally_buff
|
||||
return ____exports
|
||||
@@ -0,0 +1,257 @@
|
||||
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
|
||||
require("utils.utils")
|
||||
local ____axe_battle_hunger_custom = require("abilities.heroes.axe.axe_battle_hunger_custom")
|
||||
local axeApplyBattleHungerFromCall = ____axe_battle_hunger_custom.axeApplyBattleHungerFromCall
|
||||
local NEVERMORE_RAID_BOSS = "npc_boss_nevermore"
|
||||
local function isNevermoreRaidBoss(self, unit)
|
||||
return unit:GetUnitName() == NEVERMORE_RAID_BOSS
|
||||
end
|
||||
--- Berserker's Call — как в доте: тантует врагов в радиусе, броня на Акса; шард вешает Battle Hunger.
|
||||
____exports.axe_berserkers_call_custom = __TS__Class()
|
||||
local axe_berserkers_call_custom = ____exports.axe_berserkers_call_custom
|
||||
axe_berserkers_call_custom.name = "axe_berserkers_call_custom"
|
||||
axe_berserkers_call_custom.____file_path = "scripts/vscripts/abilities/heroes/axe/axe_berserkers_call_custom.lua"
|
||||
__TS__ClassExtends(axe_berserkers_call_custom, BaseAbility)
|
||||
function axe_berserkers_call_custom.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_axe/axe_beserkers_call_owner.vpcf", context)
|
||||
PrecacheResource("particle", "particles/status_fx/status_effect_beserkers_call.vpcf", context)
|
||||
PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_axe.vsndevts", context)
|
||||
end
|
||||
function axe_berserkers_call_custom.prototype.OnAbilityPhaseStart(self)
|
||||
if IsServer() then
|
||||
EmitSoundOn(
|
||||
"Hero_Axe.BerserkersCall.Start",
|
||||
self:GetCaster()
|
||||
)
|
||||
end
|
||||
return true
|
||||
end
|
||||
function axe_berserkers_call_custom.prototype.OnAbilityPhaseInterrupted(self)
|
||||
if IsServer() then
|
||||
StopSoundOn(
|
||||
"Hero_Axe.BerserkersCall.Start",
|
||||
self:GetCaster()
|
||||
)
|
||||
end
|
||||
end
|
||||
function axe_berserkers_call_custom.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local radius = self:GetSpecialValueFor("radius")
|
||||
local duration = self:GetSpecialValueFor("duration")
|
||||
caster:AddNewModifier(caster, self, ____exports.modifier_axe_berserkers_call_armor.name, {duration = duration})
|
||||
local enemies = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
caster:GetAbsOrigin(),
|
||||
nil,
|
||||
radius,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC,
|
||||
DOTA_UNIT_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
local shard = HasShard(nil, caster)
|
||||
local hungerDur = self:GetSpecialValueFor("shard_hunger_duration")
|
||||
for ____, enemy in ipairs(enemies) do
|
||||
do
|
||||
if not enemy or not enemy:IsAlive() then
|
||||
goto __continue10
|
||||
end
|
||||
if isNevermoreRaidBoss(nil, enemy) then
|
||||
goto __continue10
|
||||
end
|
||||
self:issueAttackOrder(enemy, caster)
|
||||
enemy:AddNewModifier(caster, self, ____exports.modifier_axe_berserkers_call_taunt.name, {duration = duration})
|
||||
if shard then
|
||||
axeApplyBattleHungerFromCall(nil, caster, enemy, hungerDur)
|
||||
end
|
||||
end
|
||||
::__continue10::
|
||||
end
|
||||
if shard then
|
||||
local allies = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
caster:GetAbsOrigin(),
|
||||
nil,
|
||||
radius,
|
||||
DOTA_UNIT_TARGET_TEAM_FRIENDLY,
|
||||
DOTA_UNIT_TARGET_HERO,
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
for ____, ally in ipairs(allies) do
|
||||
do
|
||||
if not ally or not ally:IsAlive() then
|
||||
goto __continue16
|
||||
end
|
||||
axeApplyBattleHungerFromCall(nil, caster, ally, hungerDur)
|
||||
end
|
||||
::__continue16::
|
||||
end
|
||||
end
|
||||
if #enemies > 0 then
|
||||
EmitSoundOn("Hero_Axe.Berserkers_Call", caster)
|
||||
end
|
||||
local particle_cast = "particles/units/heroes/hero_axe/axe_beserkers_call_owner.vpcf"
|
||||
local effect_cast = ParticleManager:CreateParticle(particle_cast, PATTACH_ABSORIGIN_FOLLOW, caster)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
effect_cast,
|
||||
1,
|
||||
caster,
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
"attach_mouth",
|
||||
Vector(0, 0, 0),
|
||||
true
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
effect_cast,
|
||||
2,
|
||||
Vector(radius, radius, radius)
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(effect_cast)
|
||||
end
|
||||
function axe_berserkers_call_custom.prototype.issueAttackOrder(self, attacker, target)
|
||||
local canAttack = attacker:GetAttackCapability() ~= DOTA_UNIT_CAP_NO_ATTACK and not attacker:IsDisarmed()
|
||||
if not canAttack then
|
||||
ExecuteOrderFromTable({
|
||||
UnitIndex = attacker:entindex(),
|
||||
OrderType = DOTA_UNIT_ORDER_MOVE_TO_POSITION,
|
||||
Position = target:GetAbsOrigin(),
|
||||
Queue = false
|
||||
})
|
||||
return
|
||||
end
|
||||
ExecuteOrderFromTable({
|
||||
UnitIndex = attacker:entindex(),
|
||||
OrderType = DOTA_UNIT_ORDER_ATTACK_TARGET,
|
||||
TargetIndex = target:entindex(),
|
||||
Queue = false
|
||||
})
|
||||
end
|
||||
axe_berserkers_call_custom = __TS__Decorate(
|
||||
axe_berserkers_call_custom,
|
||||
axe_berserkers_call_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "axe_berserkers_call_custom"}
|
||||
)
|
||||
____exports.axe_berserkers_call_custom = axe_berserkers_call_custom
|
||||
____exports.modifier_axe_berserkers_call_armor = __TS__Class()
|
||||
local modifier_axe_berserkers_call_armor = ____exports.modifier_axe_berserkers_call_armor
|
||||
modifier_axe_berserkers_call_armor.name = "modifier_axe_berserkers_call_armor"
|
||||
modifier_axe_berserkers_call_armor.____file_path = "scripts/vscripts/abilities/heroes/axe/axe_berserkers_call_custom.lua"
|
||||
__TS__ClassExtends(modifier_axe_berserkers_call_armor, BaseModifier)
|
||||
function modifier_axe_berserkers_call_armor.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_axe_berserkers_call_armor.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_axe_berserkers_call_armor.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_PHYSICAL_ARMOR_BONUS}
|
||||
end
|
||||
function modifier_axe_berserkers_call_armor.prototype.GetModifierPhysicalArmorBonus(self)
|
||||
return self:GetAbility():GetSpecialValueFor("bonus_armor")
|
||||
end
|
||||
function modifier_axe_berserkers_call_armor.prototype.GetStatusEffectName(self)
|
||||
return "particles/status_fx/status_effect_beserkers_call.vpcf"
|
||||
end
|
||||
modifier_axe_berserkers_call_armor = __TS__Decorate(
|
||||
modifier_axe_berserkers_call_armor,
|
||||
modifier_axe_berserkers_call_armor,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_axe_berserkers_call_armor"}
|
||||
)
|
||||
____exports.modifier_axe_berserkers_call_armor = modifier_axe_berserkers_call_armor
|
||||
____exports.modifier_axe_berserkers_call_taunt = __TS__Class()
|
||||
local modifier_axe_berserkers_call_taunt = ____exports.modifier_axe_berserkers_call_taunt
|
||||
modifier_axe_berserkers_call_taunt.name = "modifier_axe_berserkers_call_taunt"
|
||||
modifier_axe_berserkers_call_taunt.____file_path = "scripts/vscripts/abilities/heroes/axe/axe_berserkers_call_custom.lua"
|
||||
__TS__ClassExtends(modifier_axe_berserkers_call_taunt, BaseModifier)
|
||||
function modifier_axe_berserkers_call_taunt.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_axe_berserkers_call_taunt.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_axe_berserkers_call_taunt.prototype.IsDebuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_axe_berserkers_call_taunt.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_COMMAND_RESTRICTED] = true}
|
||||
end
|
||||
function modifier_axe_berserkers_call_taunt.prototype.GetStatusEffectName(self)
|
||||
return "particles/status_fx/status_effect_beserkers_call.vpcf"
|
||||
end
|
||||
function modifier_axe_berserkers_call_taunt.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local caster = self:GetCaster()
|
||||
if not caster or not parent then
|
||||
return
|
||||
end
|
||||
self.reissueTimer = Timers:CreateTimer(
|
||||
0.25,
|
||||
function()
|
||||
if not IsValidEntity(parent) or not IsValidEntity(caster) then
|
||||
return nil
|
||||
end
|
||||
if not parent:IsAlive() or not caster:IsAlive() then
|
||||
return nil
|
||||
end
|
||||
local canAttack = parent:GetAttackCapability() ~= DOTA_UNIT_CAP_NO_ATTACK and not parent:IsDisarmed()
|
||||
if not canAttack then
|
||||
ExecuteOrderFromTable({
|
||||
UnitIndex = parent:entindex(),
|
||||
OrderType = DOTA_UNIT_ORDER_MOVE_TO_POSITION,
|
||||
Position = caster:GetAbsOrigin(),
|
||||
Queue = false
|
||||
})
|
||||
return 0.25
|
||||
end
|
||||
ExecuteOrderFromTable({
|
||||
UnitIndex = parent:entindex(),
|
||||
OrderType = DOTA_UNIT_ORDER_ATTACK_TARGET,
|
||||
TargetIndex = caster:entindex(),
|
||||
Queue = false
|
||||
})
|
||||
return 0.25
|
||||
end
|
||||
)
|
||||
end
|
||||
function modifier_axe_berserkers_call_taunt.prototype.OnDestroy(self)
|
||||
if self.reissueTimer ~= nil then
|
||||
Timers:RemoveTimer(self.reissueTimer)
|
||||
self.reissueTimer = nil
|
||||
end
|
||||
end
|
||||
function modifier_axe_berserkers_call_taunt.prototype.OnDeath(self, event)
|
||||
if IsServer() and event.unit == self:GetCaster() then
|
||||
self:Destroy()
|
||||
end
|
||||
end
|
||||
function modifier_axe_berserkers_call_taunt.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_EVENT_ON_DEATH}
|
||||
end
|
||||
modifier_axe_berserkers_call_taunt = __TS__Decorate(
|
||||
modifier_axe_berserkers_call_taunt,
|
||||
modifier_axe_berserkers_call_taunt,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_axe_berserkers_call_taunt"}
|
||||
)
|
||||
____exports.modifier_axe_berserkers_call_taunt = modifier_axe_berserkers_call_taunt
|
||||
return ____exports
|
||||
@@ -0,0 +1,158 @@
|
||||
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
|
||||
____exports.axe_counter_helix_custom = __TS__Class()
|
||||
local axe_counter_helix_custom = ____exports.axe_counter_helix_custom
|
||||
axe_counter_helix_custom.name = "axe_counter_helix_custom"
|
||||
axe_counter_helix_custom.____file_path = "scripts/vscripts/abilities/heroes/axe/axe_counter_helix_custom.lua"
|
||||
__TS__ClassExtends(axe_counter_helix_custom, BaseAbility)
|
||||
function axe_counter_helix_custom.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_axe/axe_counterhelix.vpcf", context)
|
||||
PrecacheResource("particle", "particles/econ/items/axe/axe_weapon_bloodchaser/axe_attack_blur_counterhelix_bloodchaser.vpcf", context)
|
||||
PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_axe.vsndevts", context)
|
||||
end
|
||||
function axe_counter_helix_custom.prototype.GetIntrinsicModifierName(self)
|
||||
return ____exports.modifier_axe_counter_helix_passive.name
|
||||
end
|
||||
axe_counter_helix_custom = __TS__Decorate(
|
||||
axe_counter_helix_custom,
|
||||
axe_counter_helix_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "axe_counter_helix_custom"}
|
||||
)
|
||||
____exports.axe_counter_helix_custom = axe_counter_helix_custom
|
||||
____exports.modifier_axe_counter_helix_passive = __TS__Class()
|
||||
local modifier_axe_counter_helix_passive = ____exports.modifier_axe_counter_helix_passive
|
||||
modifier_axe_counter_helix_passive.name = "modifier_axe_counter_helix_passive"
|
||||
modifier_axe_counter_helix_passive.____file_path = "scripts/vscripts/abilities/heroes/axe/axe_counter_helix_custom.lua"
|
||||
__TS__ClassExtends(modifier_axe_counter_helix_passive, BaseModifier)
|
||||
function modifier_axe_counter_helix_passive.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.lastHelixGameTime = 0
|
||||
self.scepterLandCounter = 0
|
||||
end
|
||||
function modifier_axe_counter_helix_passive.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_axe_counter_helix_passive.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_axe_counter_helix_passive.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_EVENT_ON_TAKEDAMAGE}
|
||||
end
|
||||
function modifier_axe_counter_helix_passive.prototype.OnTakeDamage(self, event)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
if parent:PassivesDisabled() then
|
||||
return
|
||||
end
|
||||
if not ability or event.unit ~= parent then
|
||||
return
|
||||
end
|
||||
local attacker = event.attacker
|
||||
if not attacker or attacker == parent or not attacker:IsAlive() then
|
||||
return
|
||||
end
|
||||
if attacker:GetTeamNumber() == parent:GetTeamNumber() then
|
||||
return
|
||||
end
|
||||
local cd = ability:GetSpecialValueFor("duration")
|
||||
local now = GameRules:GetGameTime()
|
||||
if now < self.lastHelixGameTime + cd then
|
||||
return
|
||||
end
|
||||
local chance = ability:GetSpecialValueFor("trigger_chance")
|
||||
local needScepter = parent:HasScepter()
|
||||
local attacksForProc = needScepter and ability:GetSpecialValueFor("scepter_attacks") or 0
|
||||
local helixed = false
|
||||
if attacksForProc > 0 then
|
||||
self.scepterLandCounter = self.scepterLandCounter + 1
|
||||
if self.scepterLandCounter >= attacksForProc then
|
||||
self.scepterLandCounter = 0
|
||||
self:triggerHelix(ability)
|
||||
helixed = true
|
||||
end
|
||||
end
|
||||
if not helixed and RollPercentage(chance) then
|
||||
self:triggerHelix(ability)
|
||||
helixed = true
|
||||
end
|
||||
if helixed then
|
||||
self.lastHelixGameTime = now
|
||||
end
|
||||
end
|
||||
function modifier_axe_counter_helix_passive.prototype.triggerHelix(self, ability)
|
||||
if self:GetParent():PassivesDisabled() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local radius = ability:GetSpecialValueFor("radius")
|
||||
local damage = ability:GetSpecialValueFor("damage")
|
||||
parent:StartGestureWithPlaybackRate(ACT_DOTA_CAST_ABILITY_3, 1)
|
||||
local helix_pfx = ParticleManager:CreateParticle("particles/econ/items/axe/axe_weapon_bloodchaser/axe_attack_blur_counterhelix_bloodchaser.vpcf", PATTACH_ABSORIGIN_FOLLOW, parent)
|
||||
EmitSoundOn("Hero_Axe.CounterHelix", parent)
|
||||
ParticleManager:SetParticleControl(
|
||||
helix_pfx,
|
||||
0,
|
||||
parent:GetAbsOrigin()
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(helix_pfx)
|
||||
local enemies = FindUnitsInRadius(
|
||||
parent:GetTeamNumber(),
|
||||
parent:GetAbsOrigin(),
|
||||
nil,
|
||||
radius,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC,
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
for ____, enemy in ipairs(enemies) do
|
||||
do
|
||||
if not enemy or not enemy:IsAlive() then
|
||||
goto __continue20
|
||||
end
|
||||
ApplyDamage({
|
||||
victim = enemy,
|
||||
attacker = parent,
|
||||
damage = damage,
|
||||
damage_type = ability:GetAbilityDamageType(),
|
||||
ability = ability
|
||||
})
|
||||
parent:PerformAttack(
|
||||
enemy,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
)
|
||||
end
|
||||
::__continue20::
|
||||
end
|
||||
local icd = ability:GetSpecialValueFor("duration")
|
||||
if icd > 0 then
|
||||
ability:StartCooldown(icd)
|
||||
end
|
||||
end
|
||||
modifier_axe_counter_helix_passive = __TS__Decorate(
|
||||
modifier_axe_counter_helix_passive,
|
||||
modifier_axe_counter_helix_passive,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_axe_counter_helix_passive"}
|
||||
)
|
||||
____exports.modifier_axe_counter_helix_passive = modifier_axe_counter_helix_passive
|
||||
return ____exports
|
||||
@@ -0,0 +1,334 @@
|
||||
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
|
||||
--- Навсегда броня: стаки × armor_per_creep_kill (крип +0.1, герой — через armor_per_stack).
|
||||
local function axeCullingAddPermanentArmor(self, caster, ability, flatArmor)
|
||||
if flatArmor <= 0 then
|
||||
return
|
||||
end
|
||||
local step = ability:GetSpecialValueFor("armor_per_creep_kill")
|
||||
if step <= 0 then
|
||||
return
|
||||
end
|
||||
local addStacks = math.floor(flatArmor / step + 0.5)
|
||||
if addStacks < 1 then
|
||||
addStacks = 1
|
||||
end
|
||||
local m = caster:FindModifierByName(____exports.modifier_axe_culling_blade_armor_stack.name)
|
||||
if not m then
|
||||
m = caster:AddNewModifier(caster, ability, ____exports.modifier_axe_culling_blade_armor_stack.name, {})
|
||||
end
|
||||
if m ~= nil then
|
||||
m:SetStackCount(m:GetStackCount() + addStacks)
|
||||
end
|
||||
end
|
||||
local function axeReduceAllAbilityCooldowns(self, caster, seconds)
|
||||
if seconds <= 0 then
|
||||
return
|
||||
end
|
||||
do
|
||||
local i = 0
|
||||
while i < 24 do
|
||||
do
|
||||
local ab = caster:GetAbilityByIndex(i)
|
||||
if not ab or ab:IsNull() then
|
||||
goto __continue10
|
||||
end
|
||||
local remaining = ab:GetCooldownTimeRemaining()
|
||||
if remaining <= 0 then
|
||||
goto __continue10
|
||||
end
|
||||
ab:EndCooldown()
|
||||
local next = math.max(0, remaining - seconds)
|
||||
if next > 0 then
|
||||
ab:StartCooldown(next)
|
||||
end
|
||||
end
|
||||
::__continue10::
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
____exports.axe_culling_blade_custom = __TS__Class()
|
||||
local axe_culling_blade_custom = ____exports.axe_culling_blade_custom
|
||||
axe_culling_blade_custom.name = "axe_culling_blade_custom"
|
||||
axe_culling_blade_custom.____file_path = "scripts/vscripts/abilities/heroes/axe/axe_culling_blade_custom.lua"
|
||||
__TS__ClassExtends(axe_culling_blade_custom, BaseAbility)
|
||||
function axe_culling_blade_custom.prototype.GetIntrinsicModifierName(self)
|
||||
return ____exports.modifier_axe_culling_blade_shard_cdr.name
|
||||
end
|
||||
function axe_culling_blade_custom.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_axe/axe_culling_blade.vpcf", context)
|
||||
PrecacheResource("particle", "particles/econ/items/axe/ti9_jungle_axe/ti9_jungle_axe_culling_blade_sprint_fire.vpcf", context)
|
||||
PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_axe.vsndevts", context)
|
||||
end
|
||||
function axe_culling_blade_custom.prototype.GetCastRange(self, location, target)
|
||||
return self:GetSpecialValueFor("AbilityCastRange")
|
||||
end
|
||||
function axe_culling_blade_custom.prototype.GetAOERadius(self)
|
||||
return self:GetSpecialValueFor("cull_radius")
|
||||
end
|
||||
function axe_culling_blade_custom.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local point = self:GetCursorPosition()
|
||||
local cullRadius = self:GetSpecialValueFor("cull_radius")
|
||||
local buffRadius = self:GetSpecialValueFor("speed_aoe")
|
||||
local creepArmor = self:GetSpecialValueFor("armor_per_creep_kill")
|
||||
local heroArmor = self:GetSpecialValueFor("armor_per_stack")
|
||||
EmitSoundOn("Hero_Axe.Culling_Blade", caster)
|
||||
local enemies = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
point,
|
||||
nil,
|
||||
cullRadius,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC,
|
||||
DOTA_UNIT_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
local anyExecuted = false
|
||||
for ____, target in ipairs(enemies) do
|
||||
do
|
||||
if not target or not target:IsAlive() or target:IsInvulnerable() then
|
||||
goto __continue20
|
||||
end
|
||||
self:playCullingBladeParticle(caster, target)
|
||||
EmitSoundOn("Hero_Axe.Culling_Blade", target)
|
||||
local wasHero = target:IsHero()
|
||||
local bonusAttackDamage = caster:GetAverageTrueAttackDamage(target)
|
||||
ApplyDamage({
|
||||
victim = target,
|
||||
attacker = caster,
|
||||
damage = bonusAttackDamage,
|
||||
damage_type = self:GetAbilityDamageType(),
|
||||
ability = self
|
||||
})
|
||||
if target:IsAlive() then
|
||||
caster:PerformAttack(
|
||||
target,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
)
|
||||
end
|
||||
if not target:IsAlive() then
|
||||
anyExecuted = true
|
||||
EmitSoundOn("Hero_Axe.Culling_Blade_Success", target)
|
||||
if not wasHero and creepArmor > 0 then
|
||||
axeCullingAddPermanentArmor(nil, caster, self, creepArmor)
|
||||
elseif wasHero and heroArmor > 0 then
|
||||
axeCullingAddPermanentArmor(nil, caster, self, heroArmor)
|
||||
end
|
||||
end
|
||||
end
|
||||
::__continue20::
|
||||
end
|
||||
if anyExecuted then
|
||||
local durBase = self:GetSpecialValueFor("speed_duration")
|
||||
local dur = caster:HasScepter() and self:GetSpecialValueFor("scepter_speed_duration") or durBase
|
||||
local tm = caster:GetTeamNumber()
|
||||
local unitList = FindUnitsInRadius(
|
||||
tm,
|
||||
caster:GetAbsOrigin(),
|
||||
nil,
|
||||
buffRadius,
|
||||
DOTA_UNIT_TARGET_TEAM_FRIENDLY,
|
||||
DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC,
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
for ____, unit in ipairs(unitList) do
|
||||
do
|
||||
if not unit or not unit:IsAlive() then
|
||||
goto __continue28
|
||||
end
|
||||
unit:AddNewModifier(caster, self, ____exports.modifier_axe_culling_blade_movespeed.name, {duration = dur})
|
||||
end
|
||||
::__continue28::
|
||||
end
|
||||
end
|
||||
end
|
||||
function axe_culling_blade_custom.prototype.playCullingBladeParticle(self, caster, target)
|
||||
local particle = ParticleManager:CreateParticle("particles/units/heroes/hero_axe/axe_culling_blade.vpcf", PATTACH_ABSORIGIN_FOLLOW, target)
|
||||
ParticleManager:SetParticleControl(
|
||||
particle,
|
||||
0,
|
||||
caster:GetAbsOrigin()
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
particle,
|
||||
1,
|
||||
target:GetAbsOrigin()
|
||||
)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
particle,
|
||||
2,
|
||||
target,
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
"attach_hitloc",
|
||||
target:GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(particle)
|
||||
end
|
||||
axe_culling_blade_custom = __TS__Decorate(
|
||||
axe_culling_blade_custom,
|
||||
axe_culling_blade_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "axe_culling_blade_custom"}
|
||||
)
|
||||
____exports.axe_culling_blade_custom = axe_culling_blade_custom
|
||||
____exports.modifier_axe_culling_blade_shard_cdr = __TS__Class()
|
||||
local modifier_axe_culling_blade_shard_cdr = ____exports.modifier_axe_culling_blade_shard_cdr
|
||||
modifier_axe_culling_blade_shard_cdr.name = "modifier_axe_culling_blade_shard_cdr"
|
||||
modifier_axe_culling_blade_shard_cdr.____file_path = "scripts/vscripts/abilities/heroes/axe/axe_culling_blade_custom.lua"
|
||||
__TS__ClassExtends(modifier_axe_culling_blade_shard_cdr, BaseModifier)
|
||||
function modifier_axe_culling_blade_shard_cdr.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_axe_culling_blade_shard_cdr.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_axe_culling_blade_shard_cdr.prototype.RemoveOnDeath(self)
|
||||
return false
|
||||
end
|
||||
function modifier_axe_culling_blade_shard_cdr.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
self.killListener = ListenToGameEvent(
|
||||
"entity_killed",
|
||||
function(event)
|
||||
local attacker = EntIndexToHScript(event.entindex_attacker)
|
||||
local killed = EntIndexToHScript(event.entindex_killed)
|
||||
local ____temp_0
|
||||
if event.entindex_inflictor ~= nil and event.entindex_inflictor ~= 0 then
|
||||
____temp_0 = EntIndexToHScript(event.entindex_inflictor)
|
||||
else
|
||||
____temp_0 = nil
|
||||
end
|
||||
local inflictor = ____temp_0
|
||||
if not attacker or not killed then
|
||||
return
|
||||
end
|
||||
if attacker ~= parent then
|
||||
return
|
||||
end
|
||||
if inflictor and not inflictor:IsNull() then
|
||||
return
|
||||
end
|
||||
if not parent:IsRealHero() or parent:IsIllusion() then
|
||||
return
|
||||
end
|
||||
if parent:PassivesDisabled() then
|
||||
return
|
||||
end
|
||||
if not parent:HasScepter() then
|
||||
return
|
||||
end
|
||||
if not killed:IsCreep() then
|
||||
return
|
||||
end
|
||||
axeReduceAllAbilityCooldowns(nil, parent, 1)
|
||||
end,
|
||||
nil
|
||||
)
|
||||
end
|
||||
function modifier_axe_culling_blade_shard_cdr.prototype.OnDestroy(self)
|
||||
if self.killListener ~= nil then
|
||||
StopListeningToGameEvent(self.killListener)
|
||||
self.killListener = nil
|
||||
end
|
||||
end
|
||||
modifier_axe_culling_blade_shard_cdr = __TS__Decorate(
|
||||
modifier_axe_culling_blade_shard_cdr,
|
||||
modifier_axe_culling_blade_shard_cdr,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_axe_culling_blade_shard_cdr"}
|
||||
)
|
||||
____exports.modifier_axe_culling_blade_shard_cdr = modifier_axe_culling_blade_shard_cdr
|
||||
____exports.modifier_axe_culling_blade_armor_stack = __TS__Class()
|
||||
local modifier_axe_culling_blade_armor_stack = ____exports.modifier_axe_culling_blade_armor_stack
|
||||
modifier_axe_culling_blade_armor_stack.name = "modifier_axe_culling_blade_armor_stack"
|
||||
modifier_axe_culling_blade_armor_stack.____file_path = "scripts/vscripts/abilities/heroes/axe/axe_culling_blade_custom.lua"
|
||||
__TS__ClassExtends(modifier_axe_culling_blade_armor_stack, BaseModifier)
|
||||
function modifier_axe_culling_blade_armor_stack.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_axe_culling_blade_armor_stack.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_axe_culling_blade_armor_stack.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_axe_culling_blade_armor_stack.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_PHYSICAL_ARMOR_BONUS}
|
||||
end
|
||||
function modifier_axe_culling_blade_armor_stack.prototype.GetModifierPhysicalArmorBonus(self)
|
||||
local ab = self:GetAbility()
|
||||
if not ab then
|
||||
return 0
|
||||
end
|
||||
return self:GetStackCount() * ab:GetSpecialValueFor("armor_per_creep_kill")
|
||||
end
|
||||
modifier_axe_culling_blade_armor_stack = __TS__Decorate(
|
||||
modifier_axe_culling_blade_armor_stack,
|
||||
modifier_axe_culling_blade_armor_stack,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_axe_culling_blade_armor_stack"}
|
||||
)
|
||||
____exports.modifier_axe_culling_blade_armor_stack = modifier_axe_culling_blade_armor_stack
|
||||
____exports.modifier_axe_culling_blade_movespeed = __TS__Class()
|
||||
local modifier_axe_culling_blade_movespeed = ____exports.modifier_axe_culling_blade_movespeed
|
||||
modifier_axe_culling_blade_movespeed.name = "modifier_axe_culling_blade_movespeed"
|
||||
modifier_axe_culling_blade_movespeed.____file_path = "scripts/vscripts/abilities/heroes/axe/axe_culling_blade_custom.lua"
|
||||
__TS__ClassExtends(modifier_axe_culling_blade_movespeed, BaseModifier)
|
||||
function modifier_axe_culling_blade_movespeed.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_axe_culling_blade_movespeed.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
function modifier_axe_culling_blade_movespeed.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE, MODIFIER_PROPERTY_ATTACKSPEED_BONUS_CONSTANT}
|
||||
end
|
||||
function modifier_axe_culling_blade_movespeed.prototype.GetModifierMoveSpeedBonus_Percentage(self)
|
||||
return self:GetAbility():GetSpecialValueFor("speed_bonus")
|
||||
end
|
||||
function modifier_axe_culling_blade_movespeed.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_DEBUFF_IMMUNE] = true}
|
||||
end
|
||||
function modifier_axe_culling_blade_movespeed.prototype.GetModifierAttackSpeedBonus_Constant(self)
|
||||
return self:GetAbility():GetSpecialValueFor("attack_speed_bonus")
|
||||
end
|
||||
function modifier_axe_culling_blade_movespeed.prototype.GetEffectName(self)
|
||||
return "particles/econ/items/axe/ti9_jungle_axe/ti9_jungle_axe_culling_blade_sprint_fire.vpcf"
|
||||
end
|
||||
function modifier_axe_culling_blade_movespeed.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_ABSORIGIN_FOLLOW
|
||||
end
|
||||
modifier_axe_culling_blade_movespeed = __TS__Decorate(
|
||||
modifier_axe_culling_blade_movespeed,
|
||||
modifier_axe_culling_blade_movespeed,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_axe_culling_blade_movespeed"}
|
||||
)
|
||||
____exports.modifier_axe_culling_blade_movespeed = modifier_axe_culling_blade_movespeed
|
||||
return ____exports
|
||||
@@ -0,0 +1,83 @@
|
||||
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
|
||||
____exports.axe_one_man_army_custom = __TS__Class()
|
||||
local axe_one_man_army_custom = ____exports.axe_one_man_army_custom
|
||||
axe_one_man_army_custom.name = "axe_one_man_army_custom"
|
||||
axe_one_man_army_custom.____file_path = "scripts/vscripts/abilities/heroes/axe/axe_one_man_army_custom.lua"
|
||||
__TS__ClassExtends(axe_one_man_army_custom, BaseAbility)
|
||||
function axe_one_man_army_custom.prototype.GetIntrinsicModifierName(self)
|
||||
return ____exports.modifier_axe_one_man_army_custom.name
|
||||
end
|
||||
axe_one_man_army_custom = __TS__Decorate(
|
||||
axe_one_man_army_custom,
|
||||
axe_one_man_army_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "axe_one_man_army_custom"}
|
||||
)
|
||||
____exports.axe_one_man_army_custom = axe_one_man_army_custom
|
||||
____exports.modifier_axe_one_man_army_custom = __TS__Class()
|
||||
local modifier_axe_one_man_army_custom = ____exports.modifier_axe_one_man_army_custom
|
||||
modifier_axe_one_man_army_custom.name = "modifier_axe_one_man_army_custom"
|
||||
modifier_axe_one_man_army_custom.____file_path = "scripts/vscripts/abilities/heroes/axe/axe_one_man_army_custom.lua"
|
||||
__TS__ClassExtends(modifier_axe_one_man_army_custom, BaseModifier)
|
||||
function modifier_axe_one_man_army_custom.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_axe_one_man_army_custom.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_axe_one_man_army_custom.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_STATS_STRENGTH_BONUS}
|
||||
end
|
||||
function modifier_axe_one_man_army_custom.prototype.GetModifierBonusStats_Strength(self)
|
||||
local parent = self:GetParent()
|
||||
if parent:PassivesDisabled() then
|
||||
return 0
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
if not ability or parent:IsIllusion() then
|
||||
return 0
|
||||
end
|
||||
local factor = ability:GetSpecialValueFor("armor_to_str")
|
||||
local allies = FindUnitsInRadius(
|
||||
parent:GetTeamNumber(),
|
||||
parent:GetAbsOrigin(),
|
||||
nil,
|
||||
ability:GetSpecialValueFor("radius"),
|
||||
DOTA_UNIT_TARGET_TEAM_FRIENDLY,
|
||||
DOTA_UNIT_TARGET_HERO,
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
for ____, u in ipairs(allies) do
|
||||
do
|
||||
if u == parent then
|
||||
goto __continue9
|
||||
end
|
||||
if not u:IsRealHero() or u:IsIllusion() then
|
||||
goto __continue9
|
||||
end
|
||||
return 0
|
||||
end
|
||||
::__continue9::
|
||||
end
|
||||
local armor = parent:GetPhysicalArmorValue(false)
|
||||
return math.floor(armor * factor)
|
||||
end
|
||||
modifier_axe_one_man_army_custom = __TS__Decorate(
|
||||
modifier_axe_one_man_army_custom,
|
||||
modifier_axe_one_man_army_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_axe_one_man_army_custom"}
|
||||
)
|
||||
____exports.modifier_axe_one_man_army_custom = modifier_axe_one_man_army_custom
|
||||
return ____exports
|
||||
@@ -0,0 +1,221 @@
|
||||
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 ____vampirism = require("utils.vampirism")
|
||||
local addPhysicalVampirism = ____vampirism.addPhysicalVampirism
|
||||
local reducePhysicalVampirism = ____vampirism.reducePhysicalVampirism
|
||||
____exports.ability_bloodrage = __TS__Class()
|
||||
local ability_bloodrage = ____exports.ability_bloodrage
|
||||
ability_bloodrage.name = "ability_bloodrage"
|
||||
ability_bloodrage.____file_path = "scripts/vscripts/abilities/heroes/bloodhunter/ability_bloodrage.lua"
|
||||
__TS__ClassExtends(ability_bloodrage, BaseAbility)
|
||||
function ability_bloodrage.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
caster:AddNewModifier(
|
||||
caster,
|
||||
self,
|
||||
____exports.modifier_bloodrage_buff.name,
|
||||
{duration = self:GetSpecialValueFor("duration")}
|
||||
)
|
||||
EmitSoundOn("hero_bloodseeker.bloodRage", caster)
|
||||
end
|
||||
ability_bloodrage = __TS__Decorate(
|
||||
ability_bloodrage,
|
||||
ability_bloodrage,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_bloodrage"}
|
||||
)
|
||||
____exports.ability_bloodrage = ability_bloodrage
|
||||
____exports.modifier_bloodrage_buff = __TS__Class()
|
||||
local modifier_bloodrage_buff = ____exports.modifier_bloodrage_buff
|
||||
modifier_bloodrage_buff.name = "modifier_bloodrage_buff"
|
||||
modifier_bloodrage_buff.____file_path = "scripts/vscripts/abilities/heroes/bloodhunter/ability_bloodrage.lua"
|
||||
__TS__ClassExtends(modifier_bloodrage_buff, BaseModifier)
|
||||
function modifier_bloodrage_buff.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.bonusDamage = 0
|
||||
self.bonusAttackSpeed = 0
|
||||
self.stackDamage = 0
|
||||
self.stackAttackspeed = 0
|
||||
self.physicalVampirism = 0
|
||||
self.stackPhysicalVampirism = 0
|
||||
end
|
||||
function modifier_bloodrage_buff.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bloodrage_buff.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bloodrage_buff.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bloodrage_buff.prototype.IsBuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_bloodrage_buff.prototype.RemoveOnDeath(self)
|
||||
return true
|
||||
end
|
||||
function modifier_bloodrage_buff.prototype.GetEffectName(self)
|
||||
return "particles/econ/items/bloodseeker/bloodseeker_eztzhok_weapon/bloodseeker_bloodrage_eztzhok.vpcf"
|
||||
end
|
||||
function modifier_bloodrage_buff.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_ABSORIGIN_FOLLOW
|
||||
end
|
||||
function modifier_bloodrage_buff.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_PREATTACK_BONUS_DAMAGE, MODIFIER_PROPERTY_ATTACKSPEED_BONUS_CONSTANT, MODIFIER_PROPERTY_PROCATTACK_FEEDBACK}
|
||||
end
|
||||
function modifier_bloodrage_buff.prototype.OnCreated(self)
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
self.bonusDamage = ability:GetSpecialValueFor("bonus_damage")
|
||||
self.bonusAttackSpeed = ability:GetSpecialValueFor("bonus_attack_speed")
|
||||
self.stackDamage = ability:GetSpecialValueFor("stack_damage")
|
||||
self.stackAttackspeed = ability:GetSpecialValueFor("stack_attackspeed")
|
||||
self.physicalVampirism = ability:GetSpecialValueFor("physical_vampirism")
|
||||
self.stackPhysicalVampirism = 0
|
||||
local parent = self:GetParent()
|
||||
if parent:IsRealHero() then
|
||||
addPhysicalVampirism(nil, parent, self.physicalVampirism)
|
||||
end
|
||||
self:StartIntervalThink(0.25)
|
||||
self:OnIntervalThink()
|
||||
end
|
||||
function modifier_bloodrage_buff.prototype.OnRefresh(self)
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
self.bonusDamage = ability:GetSpecialValueFor("bonus_damage")
|
||||
self.bonusAttackSpeed = ability:GetSpecialValueFor("bonus_attack_speed")
|
||||
self.stackDamage = ability:GetSpecialValueFor("stack_damage")
|
||||
self.stackAttackspeed = ability:GetSpecialValueFor("stack_attackspeed")
|
||||
self.physicalVampirism = ability:GetSpecialValueFor("physical_vampirism")
|
||||
self.stackPhysicalVampirism = 0
|
||||
self:StartIntervalThink(0.25)
|
||||
self:OnIntervalThink()
|
||||
end
|
||||
function modifier_bloodrage_buff.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local currentHealth = parent:GetHealth()
|
||||
local damage = 100 * 0.1
|
||||
parent:SetHealth(math.max(currentHealth - damage, 1))
|
||||
end
|
||||
function modifier_bloodrage_buff.prototype.GetModifierProcAttack_Feedback(self, event)
|
||||
if not IsServer() then
|
||||
return 0
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
if parent:HasScepter() then
|
||||
self:AddStack(40, 5)
|
||||
else
|
||||
self:AddStack(20, 2)
|
||||
end
|
||||
parent:CalculateStatBonus(true)
|
||||
return 0
|
||||
end
|
||||
function modifier_bloodrage_buff.prototype.GetModifierPreAttack_BonusDamage(self)
|
||||
return self.bonusDamage + self:GetStackCount() * self.stackDamage
|
||||
end
|
||||
function modifier_bloodrage_buff.prototype.GetModifierAttackSpeedBonus_Constant(self)
|
||||
return self.bonusAttackSpeed + self:GetStackCount() * self.stackAttackspeed
|
||||
end
|
||||
function modifier_bloodrage_buff.prototype.AddStack(self, duration, count)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
local mod = parent:AddNewModifier(parent, ability, ____exports.modifier_bloodrage_count.name, {duration = duration})
|
||||
if mod then
|
||||
mod.modifier = self
|
||||
mod.bonus = count
|
||||
end
|
||||
self:SetStackCount(self:GetStackCount() + count)
|
||||
if parent:IsRealHero() then
|
||||
addPhysicalVampirism(nil, parent, self.stackPhysicalVampirism)
|
||||
end
|
||||
end
|
||||
function modifier_bloodrage_buff.prototype.RemoveStack(self, value)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self:SetStackCount(self:GetStackCount() - value)
|
||||
local parent = self:GetParent()
|
||||
if parent:IsRealHero() then
|
||||
reducePhysicalVampirism(nil, parent, self.stackPhysicalVampirism)
|
||||
end
|
||||
end
|
||||
function modifier_bloodrage_buff.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
if parent:IsRealHero() then
|
||||
local totalPhysical = self.physicalVampirism + self:GetStackCount() * self.stackPhysicalVampirism
|
||||
reducePhysicalVampirism(nil, parent, totalPhysical)
|
||||
end
|
||||
end
|
||||
modifier_bloodrage_buff = __TS__Decorate(
|
||||
modifier_bloodrage_buff,
|
||||
modifier_bloodrage_buff,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_bloodrage_buff"}
|
||||
)
|
||||
____exports.modifier_bloodrage_buff = modifier_bloodrage_buff
|
||||
____exports.modifier_bloodrage_count = __TS__Class()
|
||||
local modifier_bloodrage_count = ____exports.modifier_bloodrage_count
|
||||
modifier_bloodrage_count.name = "modifier_bloodrage_count"
|
||||
modifier_bloodrage_count.____file_path = "scripts/vscripts/abilities/heroes/bloodhunter/ability_bloodrage.lua"
|
||||
__TS__ClassExtends(modifier_bloodrage_count, BaseModifier)
|
||||
function modifier_bloodrage_count.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_bloodrage_count.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bloodrage_count.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bloodrage_count.prototype.IsBuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_bloodrage_count.prototype.RemoveOnDeath(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bloodrage_count.prototype.GetAttributes(self)
|
||||
return MODIFIER_ATTRIBUTE_MULTIPLE
|
||||
end
|
||||
function modifier_bloodrage_count.prototype.OnRemoved(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if self.modifier and self.bonus ~= nil then
|
||||
self.modifier:RemoveStack(self.bonus)
|
||||
end
|
||||
end
|
||||
modifier_bloodrage_count = __TS__Decorate(
|
||||
modifier_bloodrage_count,
|
||||
modifier_bloodrage_count,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_bloodrage_count"}
|
||||
)
|
||||
____exports.modifier_bloodrage_count = modifier_bloodrage_count
|
||||
return ____exports
|
||||
@@ -0,0 +1,106 @@
|
||||
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 ____ability_bloodrage = require("abilities.heroes.bloodhunter.ability_bloodrage")
|
||||
local modifier_bloodrage_buff = ____ability_bloodrage.modifier_bloodrage_buff
|
||||
____exports.ability_bloodstained_memory = __TS__Class()
|
||||
local ability_bloodstained_memory = ____exports.ability_bloodstained_memory
|
||||
ability_bloodstained_memory.name = "ability_bloodstained_memory"
|
||||
ability_bloodstained_memory.____file_path = "scripts/vscripts/abilities/heroes/bloodhunter/ability_bloodstained_memory.lua"
|
||||
__TS__ClassExtends(ability_bloodstained_memory, BaseAbility)
|
||||
function ability_bloodstained_memory.prototype.GetIntrinsicModifierName(self)
|
||||
return ____exports.modifier_bloodstained_memory.name
|
||||
end
|
||||
ability_bloodstained_memory = __TS__Decorate(
|
||||
ability_bloodstained_memory,
|
||||
ability_bloodstained_memory,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_bloodstained_memory"}
|
||||
)
|
||||
____exports.ability_bloodstained_memory = ability_bloodstained_memory
|
||||
____exports.modifier_bloodstained_memory = __TS__Class()
|
||||
local modifier_bloodstained_memory = ____exports.modifier_bloodstained_memory
|
||||
modifier_bloodstained_memory.name = "modifier_bloodstained_memory"
|
||||
modifier_bloodstained_memory.____file_path = "scripts/vscripts/abilities/heroes/bloodhunter/ability_bloodstained_memory.lua"
|
||||
__TS__ClassExtends(modifier_bloodstained_memory, BaseModifier)
|
||||
function modifier_bloodstained_memory.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.healthbonus = 0
|
||||
self.movespeed = 0
|
||||
end
|
||||
function modifier_bloodstained_memory.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bloodstained_memory.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bloodstained_memory.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bloodstained_memory.prototype.RemoveOnDeath(self)
|
||||
return true
|
||||
end
|
||||
function modifier_bloodstained_memory.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_HEALTH_BONUS, MODIFIER_PROPERTY_MOVESPEED_BONUS_CONSTANT, MODIFIER_PROPERTY_IGNORE_MOVESPEED_LIMIT}
|
||||
end
|
||||
function modifier_bloodstained_memory.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
self.healthbonus = ability:GetSpecialValueFor("healthbonus")
|
||||
self.movespeed = ability:GetSpecialValueFor("movespeed")
|
||||
self:StartIntervalThink(0.1)
|
||||
end
|
||||
function modifier_bloodstained_memory.prototype.OnIntervalThink(self)
|
||||
self:OnCreated()
|
||||
end
|
||||
function modifier_bloodstained_memory.prototype.OnRefresh(self)
|
||||
self:OnCreated()
|
||||
end
|
||||
function modifier_bloodstained_memory.prototype.GetModifierIgnoreMovespeedLimit(self)
|
||||
if self:GetParent():PassivesDisabled() then
|
||||
return 0
|
||||
end
|
||||
return 1
|
||||
end
|
||||
function modifier_bloodstained_memory.prototype.GetModifierHealthBonus(self)
|
||||
local parent = self:GetParent()
|
||||
if parent:PassivesDisabled() then
|
||||
return 0
|
||||
end
|
||||
local bloodrageMod = parent:FindModifierByName(modifier_bloodrage_buff.name)
|
||||
if not bloodrageMod then
|
||||
return 0
|
||||
end
|
||||
return self.healthbonus * bloodrageMod:GetStackCount()
|
||||
end
|
||||
function modifier_bloodstained_memory.prototype.GetModifierMoveSpeedBonus_Constant(self)
|
||||
local parent = self:GetParent()
|
||||
if parent:PassivesDisabled() then
|
||||
return 0
|
||||
end
|
||||
local bloodrageMod = parent:FindModifierByName(modifier_bloodrage_buff.name)
|
||||
if not bloodrageMod then
|
||||
return 0
|
||||
end
|
||||
return self.movespeed * bloodrageMod:GetStackCount()
|
||||
end
|
||||
modifier_bloodstained_memory = __TS__Decorate(
|
||||
modifier_bloodstained_memory,
|
||||
modifier_bloodstained_memory,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_bloodstained_memory"}
|
||||
)
|
||||
____exports.modifier_bloodstained_memory = modifier_bloodstained_memory
|
||||
return ____exports
|
||||
@@ -0,0 +1,291 @@
|
||||
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 ____ability_bloodrage = require("abilities.heroes.bloodhunter.ability_bloodrage")
|
||||
local modifier_bloodrage_buff = ____ability_bloodrage.modifier_bloodrage_buff
|
||||
local modifier_bloodrage_count = ____ability_bloodrage.modifier_bloodrage_count
|
||||
____exports.ability_illusion_of_blood = __TS__Class()
|
||||
local ability_illusion_of_blood = ____exports.ability_illusion_of_blood
|
||||
ability_illusion_of_blood.name = "ability_illusion_of_blood"
|
||||
ability_illusion_of_blood.____file_path = "scripts/vscripts/abilities/heroes/bloodhunter/ability_illusion_of_blood.lua"
|
||||
__TS__ClassExtends(ability_illusion_of_blood, BaseAbility)
|
||||
function ability_illusion_of_blood.prototype.____constructor(self, ...)
|
||||
BaseAbility.prototype.____constructor(self, ...)
|
||||
self.illusions = {}
|
||||
end
|
||||
function ability_illusion_of_blood.prototype.GetHealthCost(self, level)
|
||||
return 0
|
||||
end
|
||||
function ability_illusion_of_blood.prototype.CastFilterResult(self)
|
||||
local caster = self:GetCaster()
|
||||
if not caster:HasModifier(modifier_bloodrage_buff.name) then
|
||||
return UF_FAIL_CUSTOM
|
||||
end
|
||||
local modif = caster:FindModifierByName(modifier_bloodrage_buff.name)
|
||||
if not modif then
|
||||
return UF_FAIL_CUSTOM
|
||||
end
|
||||
local healthCost = self:GetHealthCost(self:GetLevel())
|
||||
if modif:GetStackCount() < healthCost then
|
||||
return UF_FAIL_CUSTOM
|
||||
end
|
||||
return UF_SUCCESS
|
||||
end
|
||||
function ability_illusion_of_blood.prototype.GetCustomCastError(self)
|
||||
local caster = self:GetCaster()
|
||||
if not caster:HasModifier(modifier_bloodrage_buff.name) then
|
||||
return "#dota_hud_error_havent_charges"
|
||||
end
|
||||
local modif = caster:FindModifierByName(modifier_bloodrage_buff.name)
|
||||
if not modif then
|
||||
return "#dota_hud_error_havent_charges"
|
||||
end
|
||||
local healthCost = self:GetHealthCost(self:GetLevel())
|
||||
if modif:GetStackCount() < healthCost then
|
||||
return "#dota_hud_error_havent_charges"
|
||||
end
|
||||
return ""
|
||||
end
|
||||
function ability_illusion_of_blood.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local healthCost = self:GetHealthCost(self:GetLevel())
|
||||
local modif = caster:FindModifierByName(modifier_bloodrage_buff.name)
|
||||
if modif then
|
||||
modif:SetStackCount(modif:GetStackCount() - healthCost)
|
||||
end
|
||||
local invDuration = self:GetSpecialValueFor("invuln_duration")
|
||||
caster:AddNewModifier(caster, self, ____exports.modifier_illusion_of_blood.name, {duration = 0.5})
|
||||
end
|
||||
ability_illusion_of_blood = __TS__Decorate(
|
||||
ability_illusion_of_blood,
|
||||
ability_illusion_of_blood,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_illusion_of_blood"}
|
||||
)
|
||||
____exports.ability_illusion_of_blood = ability_illusion_of_blood
|
||||
____exports.modifier_illusion_of_blood = __TS__Class()
|
||||
local modifier_illusion_of_blood = ____exports.modifier_illusion_of_blood
|
||||
modifier_illusion_of_blood.name = "modifier_illusion_of_blood"
|
||||
modifier_illusion_of_blood.____file_path = "scripts/vscripts/abilities/heroes/bloodhunter/ability_illusion_of_blood.lua"
|
||||
__TS__ClassExtends(modifier_illusion_of_blood, BaseModifier)
|
||||
function modifier_illusion_of_blood.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.imageCount = 0
|
||||
self.imageDuration = 0
|
||||
self.incomingDamage = 0
|
||||
self.outgoingDamage = 0
|
||||
self.magicResistance = 0
|
||||
self.illusions = {}
|
||||
self.spawnLoc = {}
|
||||
self.spawnedUnits = 0
|
||||
end
|
||||
function modifier_illusion_of_blood.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_illusion_of_blood.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
function modifier_illusion_of_blood.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_illusion_of_blood.prototype.IsBuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_illusion_of_blood.prototype.RemoveOnDeath(self)
|
||||
return true
|
||||
end
|
||||
function modifier_illusion_of_blood.prototype.AllowIllusionDuplicate(self)
|
||||
return false
|
||||
end
|
||||
function modifier_illusion_of_blood.prototype.CheckState(self)
|
||||
return {
|
||||
[MODIFIER_STATE_INVULNERABLE] = true,
|
||||
[MODIFIER_STATE_NO_HEALTH_BAR] = true,
|
||||
[MODIFIER_STATE_UNSELECTABLE] = true,
|
||||
[MODIFIER_STATE_OUT_OF_GAME] = true,
|
||||
[MODIFIER_STATE_ROOTED] = true,
|
||||
[MODIFIER_STATE_UNTARGETABLE] = true
|
||||
}
|
||||
end
|
||||
function modifier_illusion_of_blood.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
self.imageCount = ability:GetSpecialValueFor("images_count")
|
||||
self.imageDuration = ability:GetSpecialValueFor("illusion_duration")
|
||||
self.incomingDamage = ability:GetSpecialValueFor("incoming_damage")
|
||||
self.outgoingDamage = ability:GetSpecialValueFor("outgoing_damage")
|
||||
self.magicResistance = ability:GetSpecialValueFor("magic_resistance")
|
||||
local caster = self:GetParent()
|
||||
self.illusions = {}
|
||||
caster:Purge(
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
)
|
||||
caster:Stop()
|
||||
local abilityInstance = ability
|
||||
if abilityInstance ~= nil and abilityInstance ~= nil and abilityInstance.illusions ~= nil and abilityInstance.illusions ~= nil then
|
||||
for ____, illusion in ipairs(abilityInstance.illusions) do
|
||||
if IsValidEntity(illusion) and illusion:IsAlive() then
|
||||
illusion:ForceKill(false)
|
||||
end
|
||||
end
|
||||
abilityInstance.illusions = {}
|
||||
end
|
||||
local distance = 135
|
||||
local spawn = {}
|
||||
spawn[1] = caster:GetAbsOrigin()
|
||||
local rightVector = caster:GetRightVector():Normalized()
|
||||
local forwardVector = caster:GetForwardVector():Normalized()
|
||||
spawn[2] = spawn[1] + rightVector * distance
|
||||
spawn[3] = spawn[1] + rightVector * -distance
|
||||
spawn[4] = spawn[1] + forwardVector * -distance
|
||||
spawn[5] = spawn[1] + forwardVector * distance
|
||||
local realPos = RandomInt(0, #spawn - 1)
|
||||
self.spawnSelf = spawn[realPos + 1]
|
||||
self.spawnLoc = {}
|
||||
do
|
||||
local i = 0
|
||||
while i < self.imageCount do
|
||||
local sp = i
|
||||
if sp == realPos then
|
||||
sp = self.imageCount + 1
|
||||
end
|
||||
self.spawnLoc[i + 1] = spawn[sp + 1]
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
self.spawnedUnits = 0
|
||||
self.effectCast = ParticleManager:CreateParticle("particles/units/heroes/hero_chaos_knight/chaos_knight_phantasm.vpcf", PATTACH_ABSORIGIN, caster)
|
||||
ParticleManager:SetParticleControl(self.effectCast, 0, spawn[1])
|
||||
self:AddParticle(
|
||||
self.effectCast,
|
||||
false,
|
||||
false,
|
||||
-1,
|
||||
false,
|
||||
false
|
||||
)
|
||||
EmitSoundOn("Hero_ChaosKnight.Phantasm", caster)
|
||||
end
|
||||
function modifier_illusion_of_blood.prototype.CreateIllusionAndAdd(self, caster, location)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
local function modifyIllusion(____, illusion)
|
||||
if not illusion:IsRealHero() then
|
||||
return
|
||||
end
|
||||
local heroIllusion = illusion
|
||||
local heroCaster = caster
|
||||
heroIllusion:SetForwardVector(heroCaster:GetForwardVector())
|
||||
while heroIllusion:GetLevel() < heroCaster:GetLevel() do
|
||||
heroIllusion:HeroLevelUp(false)
|
||||
end
|
||||
heroIllusion:SetAbilityPoints(0)
|
||||
do
|
||||
local i = 0
|
||||
while i < 24 do
|
||||
local casterAbility = heroCaster:GetAbilityByIndex(i)
|
||||
local illusionAbility = heroIllusion:GetAbilityByIndex(i)
|
||||
if casterAbility and illusionAbility then
|
||||
illusionAbility:SetLevel(casterAbility:GetLevel())
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
do
|
||||
local slot = 0
|
||||
while slot <= 5 do
|
||||
local item = heroCaster:GetItemInSlot(slot)
|
||||
if item then
|
||||
heroIllusion:AddItemByName(item:GetName())
|
||||
end
|
||||
slot = slot + 1
|
||||
end
|
||||
end
|
||||
heroIllusion:MakeIllusion()
|
||||
heroIllusion:SetControllableByPlayer(
|
||||
heroCaster:GetPlayerID(),
|
||||
false
|
||||
)
|
||||
heroIllusion:SetOwner(heroCaster)
|
||||
illusion:AddNewModifier(caster, ability, "modifier_illusion", {duration = self.imageDuration, outgoing_damage = self.outgoingDamage, incoming_damage = self.incomingDamage})
|
||||
illusion:AddNewModifier(caster, ability, modifier_bloodrage_buff.name, {duration = 100})
|
||||
illusion:AddNewModifier(caster, ability, modifier_bloodrage_count.name, {duration = 100})
|
||||
illusion:SetBaseMagicalResistanceValue(self.magicResistance)
|
||||
local abilityInstance = ability
|
||||
if abilityInstance ~= nil and abilityInstance ~= nil then
|
||||
if abilityInstance.illusions == nil or abilityInstance.illusions == nil then
|
||||
abilityInstance.illusions = {}
|
||||
end
|
||||
local ____abilityInstance_illusions_0 = abilityInstance.illusions
|
||||
____abilityInstance_illusions_0[#____abilityInstance_illusions_0 + 1] = illusion
|
||||
end
|
||||
end
|
||||
CreateUnitByNameAsync(
|
||||
caster:GetUnitName(),
|
||||
location,
|
||||
false,
|
||||
caster,
|
||||
caster,
|
||||
caster:GetTeamNumber(),
|
||||
function(illusion) return modifyIllusion(nil, illusion) end
|
||||
)
|
||||
end
|
||||
function modifier_illusion_of_blood.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if self.spawnSelf then
|
||||
FindClearSpaceForUnit(
|
||||
self:GetParent(),
|
||||
self.spawnSelf,
|
||||
true
|
||||
)
|
||||
end
|
||||
local loop = true
|
||||
while loop do
|
||||
if self.spawnedUnits < #self.spawnLoc then
|
||||
self:CreateIllusionAndAdd(
|
||||
self:GetParent(),
|
||||
self.spawnLoc[self.spawnedUnits + 1]
|
||||
)
|
||||
self.spawnedUnits = self.spawnedUnits + 1
|
||||
if self.spawnedUnits >= self.imageCount then
|
||||
loop = false
|
||||
end
|
||||
else
|
||||
loop = false
|
||||
end
|
||||
end
|
||||
end
|
||||
modifier_illusion_of_blood = __TS__Decorate(
|
||||
modifier_illusion_of_blood,
|
||||
modifier_illusion_of_blood,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_illusion_of_blood"}
|
||||
)
|
||||
____exports.modifier_illusion_of_blood = modifier_illusion_of_blood
|
||||
return ____exports
|
||||
@@ -0,0 +1,227 @@
|
||||
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
|
||||
____exports.ability_ring_of_corrosive = __TS__Class()
|
||||
local ability_ring_of_corrosive = ____exports.ability_ring_of_corrosive
|
||||
ability_ring_of_corrosive.name = "ability_ring_of_corrosive"
|
||||
ability_ring_of_corrosive.____file_path = "scripts/vscripts/abilities/heroes/bloodhunter/ability_ring_of_corrosive.lua"
|
||||
__TS__ClassExtends(ability_ring_of_corrosive, BaseAbility)
|
||||
function ability_ring_of_corrosive.prototype.GetAOERadius(self)
|
||||
return self:GetSpecialValueFor("radius")
|
||||
end
|
||||
function ability_ring_of_corrosive.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local point = self:GetCursorPosition()
|
||||
local duration = self:GetSpecialValueFor("duration")
|
||||
CreateModifierThinker(
|
||||
caster,
|
||||
self,
|
||||
____exports.modifier_ring_of_corrosive.name,
|
||||
{duration = duration},
|
||||
point,
|
||||
caster:GetTeamNumber(),
|
||||
false
|
||||
)
|
||||
end
|
||||
ability_ring_of_corrosive = __TS__Decorate(
|
||||
ability_ring_of_corrosive,
|
||||
ability_ring_of_corrosive,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_ring_of_corrosive"}
|
||||
)
|
||||
____exports.ability_ring_of_corrosive = ability_ring_of_corrosive
|
||||
____exports.modifier_ring_of_corrosive = __TS__Class()
|
||||
local modifier_ring_of_corrosive = ____exports.modifier_ring_of_corrosive
|
||||
modifier_ring_of_corrosive.name = "modifier_ring_of_corrosive"
|
||||
modifier_ring_of_corrosive.____file_path = "scripts/vscripts/abilities/heroes/bloodhunter/ability_ring_of_corrosive.lua"
|
||||
__TS__ClassExtends(modifier_ring_of_corrosive, BaseModifier)
|
||||
function modifier_ring_of_corrosive.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.interval = 0
|
||||
self.damage = 0
|
||||
self.radius = 0
|
||||
self.thinker = false
|
||||
end
|
||||
function modifier_ring_of_corrosive.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_ring_of_corrosive.prototype.IsDebuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_ring_of_corrosive.prototype.IsStunDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_ring_of_corrosive.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_ring_of_corrosive.prototype.OnCreated(self, params)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
self.interval = ability:GetSpecialValueFor("tick_rate")
|
||||
self.damage = ability:GetSpecialValueFor("damage")
|
||||
self.radius = ability:GetSpecialValueFor("radius")
|
||||
self.thinker = params.isProvidedByAura ~= 1
|
||||
if not self.thinker then
|
||||
return
|
||||
end
|
||||
self.soundCast = "Hero_Warlock.Upheaval"
|
||||
EmitSoundOn(
|
||||
self.soundCast,
|
||||
self:GetParent()
|
||||
)
|
||||
self:StartIntervalThink(self.interval)
|
||||
self:OnIntervalThink()
|
||||
self:PlayEffects()
|
||||
end
|
||||
function modifier_ring_of_corrosive.prototype.OnRefresh(self)
|
||||
end
|
||||
function modifier_ring_of_corrosive.prototype.OnRemoved(self)
|
||||
end
|
||||
function modifier_ring_of_corrosive.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if not self.thinker then
|
||||
return
|
||||
end
|
||||
if self.soundCast then
|
||||
StopSoundOn(
|
||||
self.soundCast,
|
||||
self:GetParent()
|
||||
)
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
if IsValidEntity(parent) then
|
||||
UTIL_Remove(parent)
|
||||
end
|
||||
end
|
||||
function modifier_ring_of_corrosive.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local ability = self:GetAbility()
|
||||
if not caster or not ability then
|
||||
return
|
||||
end
|
||||
local enemies = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
self:GetParent():GetAbsOrigin(),
|
||||
nil,
|
||||
self.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
|
||||
)
|
||||
for ____, enemy in ipairs(enemies) do
|
||||
do
|
||||
if not enemy or not enemy:IsAlive() then
|
||||
goto __continue23
|
||||
end
|
||||
local baseDamage = self:GetParent():GetBaseDamageMax() * 3
|
||||
local damageTick = self.damage * baseDamage * self.interval
|
||||
ApplyDamage({
|
||||
victim = enemy,
|
||||
attacker = caster,
|
||||
damage = damageTick,
|
||||
damage_type = ability:GetAbilityDamageType(),
|
||||
ability = ability
|
||||
})
|
||||
end
|
||||
::__continue23::
|
||||
end
|
||||
end
|
||||
function modifier_ring_of_corrosive.prototype.DeclareFunctions(self)
|
||||
return {}
|
||||
end
|
||||
function modifier_ring_of_corrosive.prototype.IsAura(self)
|
||||
return self.thinker
|
||||
end
|
||||
function modifier_ring_of_corrosive.prototype.GetModifierAura(self)
|
||||
return ____exports.modifier_ring_of_corrosive.name
|
||||
end
|
||||
function modifier_ring_of_corrosive.prototype.GetAuraRadius(self)
|
||||
return self.radius
|
||||
end
|
||||
function modifier_ring_of_corrosive.prototype.GetAuraDuration(self)
|
||||
return 0.5
|
||||
end
|
||||
function modifier_ring_of_corrosive.prototype.GetAuraSearchTeam(self)
|
||||
return DOTA_UNIT_TARGET_TEAM_ENEMY
|
||||
end
|
||||
function modifier_ring_of_corrosive.prototype.GetAuraSearchType(self)
|
||||
return bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC)
|
||||
end
|
||||
function modifier_ring_of_corrosive.prototype.GetAuraSearchFlags(self)
|
||||
return DOTA_UNIT_TARGET_FLAG_NONE
|
||||
end
|
||||
function modifier_ring_of_corrosive.prototype.PlayEffects(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local particleCast = "particles/units/heroes/hero_bloodseeker/bloodseeker_bloodritual_ring_lv.vpcf"
|
||||
local effectCast = ParticleManager:CreateParticle(
|
||||
particleCast,
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
self:GetParent()
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
effectCast,
|
||||
0,
|
||||
self:GetParent():GetAbsOrigin()
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
effectCast,
|
||||
1,
|
||||
Vector(self.radius, 1, 1)
|
||||
)
|
||||
local particleCast2 = "particles/units/heroes/hero_bloodseeker/bloodseeker_spell_bloodbath_bubbles_.vpcf"
|
||||
local effectCast2 = ParticleManager:CreateParticle(
|
||||
particleCast2,
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
self:GetParent()
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
effectCast2,
|
||||
0,
|
||||
self:GetParent():GetAbsOrigin()
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
effectCast2,
|
||||
1,
|
||||
Vector(self.radius, 1, 1)
|
||||
)
|
||||
self:AddParticle(
|
||||
effectCast2,
|
||||
false,
|
||||
false,
|
||||
-1,
|
||||
false,
|
||||
false
|
||||
)
|
||||
end
|
||||
modifier_ring_of_corrosive = __TS__Decorate(
|
||||
modifier_ring_of_corrosive,
|
||||
modifier_ring_of_corrosive,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_ring_of_corrosive"}
|
||||
)
|
||||
____exports.modifier_ring_of_corrosive = modifier_ring_of_corrosive
|
||||
return ____exports
|
||||
@@ -0,0 +1,205 @@
|
||||
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 ____ability_bloodrage = require("abilities.heroes.bloodhunter.ability_bloodrage")
|
||||
local modifier_bloodrage_buff = ____ability_bloodrage.modifier_bloodrage_buff
|
||||
____exports.ability_sacrifice_revenge = __TS__Class()
|
||||
local ability_sacrifice_revenge = ____exports.ability_sacrifice_revenge
|
||||
ability_sacrifice_revenge.name = "ability_sacrifice_revenge"
|
||||
ability_sacrifice_revenge.____file_path = "scripts/vscripts/abilities/heroes/bloodhunter/ability_sacrifice_revenge.lua"
|
||||
__TS__ClassExtends(ability_sacrifice_revenge, BaseAbility)
|
||||
function ability_sacrifice_revenge.prototype.GetHealthCost(self, level)
|
||||
return 0
|
||||
end
|
||||
function ability_sacrifice_revenge.prototype.CastFilterResult(self)
|
||||
local caster = self:GetCaster()
|
||||
if not caster:HasModifier(modifier_bloodrage_buff.name) then
|
||||
return UF_FAIL_CUSTOM
|
||||
end
|
||||
local modif = caster:FindModifierByName(modifier_bloodrage_buff.name)
|
||||
if not modif then
|
||||
return UF_FAIL_CUSTOM
|
||||
end
|
||||
local healthCost = self:GetHealthCost(self:GetLevel())
|
||||
if modif:GetStackCount() < healthCost then
|
||||
return UF_FAIL_CUSTOM
|
||||
end
|
||||
return UF_SUCCESS
|
||||
end
|
||||
function ability_sacrifice_revenge.prototype.GetCustomCastError(self)
|
||||
local caster = self:GetCaster()
|
||||
if not caster:HasModifier(modifier_bloodrage_buff.name) then
|
||||
return "#dota_hud_error_havent_charges"
|
||||
end
|
||||
local modif = caster:FindModifierByName(modifier_bloodrage_buff.name)
|
||||
if not modif then
|
||||
return "#dota_hud_error_havent_charges"
|
||||
end
|
||||
local healthCost = self:GetHealthCost(self:GetLevel())
|
||||
if modif:GetStackCount() < healthCost then
|
||||
return "#dota_hud_error_havent_charges"
|
||||
end
|
||||
return ""
|
||||
end
|
||||
function ability_sacrifice_revenge.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local healthCost = self:GetHealthCost(self:GetLevel())
|
||||
local modif = caster:FindModifierByName(modifier_bloodrage_buff.name)
|
||||
if modif then
|
||||
modif:SetStackCount(modif:GetStackCount() - healthCost)
|
||||
end
|
||||
caster:Purge(
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
)
|
||||
caster:AddNewModifier(
|
||||
caster,
|
||||
self,
|
||||
____exports.modifier_sacrifice_revenge.name,
|
||||
{duration = self:GetSpecialValueFor("transformation_time")}
|
||||
)
|
||||
EmitSoundOn("hero_bloodseeker.rupture", caster)
|
||||
end
|
||||
ability_sacrifice_revenge = __TS__Decorate(
|
||||
ability_sacrifice_revenge,
|
||||
ability_sacrifice_revenge,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_sacrifice_revenge"}
|
||||
)
|
||||
____exports.ability_sacrifice_revenge = ability_sacrifice_revenge
|
||||
____exports.modifier_sacrifice_revenge = __TS__Class()
|
||||
local modifier_sacrifice_revenge = ____exports.modifier_sacrifice_revenge
|
||||
modifier_sacrifice_revenge.name = "modifier_sacrifice_revenge"
|
||||
modifier_sacrifice_revenge.____file_path = "scripts/vscripts/abilities/heroes/bloodhunter/ability_sacrifice_revenge.lua"
|
||||
__TS__ClassExtends(modifier_sacrifice_revenge, BaseModifier)
|
||||
function modifier_sacrifice_revenge.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_sacrifice_revenge.prototype.GetEffectName(self)
|
||||
return "particles/econ/items/lifestealer/ls_ti10_immortal/ls_ti10_immortal_infest_gold.vpcf"
|
||||
end
|
||||
function modifier_sacrifice_revenge.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_ABSORIGIN
|
||||
end
|
||||
function modifier_sacrifice_revenge.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
if parent:GetUnitName() == "npc_dota_hero_bloodseeker" then
|
||||
parent:StartGesture(ACT_DOTA_ALCHEMIST_CHEMICAL_RAGE_START)
|
||||
end
|
||||
end
|
||||
function modifier_sacrifice_revenge.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local ability = self:GetAbility()
|
||||
if not caster or not ability then
|
||||
return
|
||||
end
|
||||
caster:AddNewModifier(
|
||||
caster,
|
||||
ability,
|
||||
____exports.modifier_sacrifice_revenge_buff.name,
|
||||
{duration = ability:GetSpecialValueFor("duration")}
|
||||
)
|
||||
end
|
||||
modifier_sacrifice_revenge = __TS__Decorate(
|
||||
modifier_sacrifice_revenge,
|
||||
modifier_sacrifice_revenge,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_sacrifice_revenge"}
|
||||
)
|
||||
____exports.modifier_sacrifice_revenge = modifier_sacrifice_revenge
|
||||
____exports.modifier_sacrifice_revenge_buff = __TS__Class()
|
||||
local modifier_sacrifice_revenge_buff = ____exports.modifier_sacrifice_revenge_buff
|
||||
modifier_sacrifice_revenge_buff.name = "modifier_sacrifice_revenge_buff"
|
||||
modifier_sacrifice_revenge_buff.____file_path = "scripts/vscripts/abilities/heroes/bloodhunter/ability_sacrifice_revenge.lua"
|
||||
__TS__ClassExtends(modifier_sacrifice_revenge_buff, BaseModifier)
|
||||
function modifier_sacrifice_revenge_buff.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.bonusArmor = 0
|
||||
self.baseAttackTime = 0
|
||||
self.pureDamage = 0
|
||||
end
|
||||
function modifier_sacrifice_revenge_buff.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_sacrifice_revenge_buff.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_sacrifice_revenge_buff.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_sacrifice_revenge_buff.prototype.IsBuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_sacrifice_revenge_buff.prototype.AllowIllusionDuplicate(self)
|
||||
return true
|
||||
end
|
||||
function modifier_sacrifice_revenge_buff.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_PHYSICAL_ARMOR_BONUS, MODIFIER_PROPERTY_BASE_ATTACK_TIME_CONSTANT, MODIFIER_PROPERTY_PROCATTACK_BONUS_DAMAGE_PURE}
|
||||
end
|
||||
function modifier_sacrifice_revenge_buff.prototype.GetEffectName(self)
|
||||
return "particles/units/heroes/hero_ursa/ursa_enrage_buff_2.vpcf"
|
||||
end
|
||||
function modifier_sacrifice_revenge_buff.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_ABSORIGIN_FOLLOW
|
||||
end
|
||||
function modifier_sacrifice_revenge_buff.prototype.StatusEffectPriority(self)
|
||||
return 10
|
||||
end
|
||||
function modifier_sacrifice_revenge_buff.prototype.HeroEffectPriority(self)
|
||||
return 10
|
||||
end
|
||||
function modifier_sacrifice_revenge_buff.prototype.OnCreated(self)
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
self.bonusArmor = ability:GetSpecialValueFor("bonus_armor")
|
||||
self.baseAttackTime = ability:GetSpecialValueFor("base_attack_time")
|
||||
self.pureDamage = ability:GetSpecialValueFor("pure_damage")
|
||||
end
|
||||
function modifier_sacrifice_revenge_buff.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
if parent:GetUnitName() == "npc_dota_hero_bloodseeker" then
|
||||
parent:StartGesture(ACT_DOTA_ALCHEMIST_CHEMICAL_RAGE_END)
|
||||
end
|
||||
end
|
||||
function modifier_sacrifice_revenge_buff.prototype.GetModifierPhysicalArmorBonus(self)
|
||||
return self:GetParent():GetPhysicalArmorBaseValue() * 2
|
||||
end
|
||||
function modifier_sacrifice_revenge_buff.prototype.GetModifierProcAttack_BonusDamage_Pure(self, event)
|
||||
local damage = self:GetParent():GetAverageTrueAttackDamage(event.target) * (self.pureDamage / 100)
|
||||
return damage
|
||||
end
|
||||
function modifier_sacrifice_revenge_buff.prototype.GetModifierBaseAttackTimeConstant(self)
|
||||
return self.baseAttackTime
|
||||
end
|
||||
modifier_sacrifice_revenge_buff = __TS__Decorate(
|
||||
modifier_sacrifice_revenge_buff,
|
||||
modifier_sacrifice_revenge_buff,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_sacrifice_revenge_buff"}
|
||||
)
|
||||
____exports.modifier_sacrifice_revenge_buff = modifier_sacrifice_revenge_buff
|
||||
return ____exports
|
||||
@@ -0,0 +1,303 @@
|
||||
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 ____incoming_damage_reduction_combine = require("utils.incoming_damage_reduction_combine")
|
||||
local removeIncomingDamageReductionSource = ____incoming_damage_reduction_combine.removeIncomingDamageReductionSource
|
||||
local setIncomingDamageReductionEventSource = ____incoming_damage_reduction_combine.setIncomingDamageReductionEventSource
|
||||
local BRISTLEBACK_INCOMING_SOURCE = "modifier_bristleback_bristleback_custom"
|
||||
____exports.bristleback_bristleback_custom = __TS__Class()
|
||||
local bristleback_bristleback_custom = ____exports.bristleback_bristleback_custom
|
||||
bristleback_bristleback_custom.name = "bristleback_bristleback_custom"
|
||||
bristleback_bristleback_custom.____file_path = "scripts/vscripts/abilities/heroes/bristleback/bristleback_bristleback_custom.lua"
|
||||
__TS__ClassExtends(bristleback_bristleback_custom, BaseAbility)
|
||||
function bristleback_bristleback_custom.prototype.____constructor(self, ...)
|
||||
BaseAbility.prototype.____constructor(self, ...)
|
||||
self.caster = self:GetCaster()
|
||||
end
|
||||
function bristleback_bristleback_custom.prototype.GetIntrinsicModifierName(self)
|
||||
return ____exports.modifier_bristleback_bristleback_custom.name
|
||||
end
|
||||
function bristleback_bristleback_custom.prototype.IsFacingBack(self, attacker)
|
||||
local convert_attacker = EntIndexToHScript(attacker)
|
||||
local forwardVector = self.caster:GetForwardVector()
|
||||
local forwardAngle = math.deg(math.atan2(forwardVector.x, forwardVector.y))
|
||||
local reverseEnemyVector = (self.caster:GetAbsOrigin() - convert_attacker:GetAbsOrigin()):Normalized()
|
||||
local reverseEnemyAngle = math.deg(math.atan2(reverseEnemyVector.x, reverseEnemyVector.y))
|
||||
local back_angle = self:GetSpecialValueFor("back_angle")
|
||||
local difference = math.abs(forwardAngle - reverseEnemyAngle)
|
||||
if difference <= back_angle / 1 or difference >= 360 - back_angle / 1 then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
function bristleback_bristleback_custom.prototype.IncStacks(self, add_stack)
|
||||
local stack = add_stack
|
||||
local mod = self.caster:FindModifierByName(____exports.modifier_bristleback_bristleback_custom.name)
|
||||
local quill_release_threshold = self:GetSpecialValueFor("quill_release_threshold")
|
||||
if mod == nil then
|
||||
return
|
||||
end
|
||||
local all_stack = mod:GetStackCount() + stack
|
||||
if self.caster:HasScepter() then
|
||||
local scepter_thresholds = {
|
||||
self:GetSpecialValueFor("quill_release_threshold"),
|
||||
self:GetSpecialValueFor("quill_release_threshold_scepter1"),
|
||||
self:GetSpecialValueFor("quill_release_threshold_scepter2"),
|
||||
self:GetSpecialValueFor("quill_release_threshold_scepter3"),
|
||||
self:GetSpecialValueFor("quill_release_threshold_scepter4")
|
||||
}
|
||||
local spray_count = 0
|
||||
if all_stack >= scepter_thresholds[5] then
|
||||
spray_count = 5
|
||||
elseif all_stack >= scepter_thresholds[4] and all_stack < scepter_thresholds[5] then
|
||||
spray_count = 4
|
||||
elseif all_stack >= scepter_thresholds[3] and all_stack < scepter_thresholds[4] then
|
||||
spray_count = 3
|
||||
elseif all_stack >= scepter_thresholds[2] and all_stack < scepter_thresholds[3] then
|
||||
spray_count = 2
|
||||
elseif all_stack >= scepter_thresholds[1] and all_stack < scepter_thresholds[2] then
|
||||
spray_count = 1
|
||||
end
|
||||
if spray_count >= 1 then
|
||||
do
|
||||
local i = 0
|
||||
while i < spray_count do
|
||||
____exports.modifier_bristleback_bristleback_custom_make_spray:apply(self.caster, self.caster, self, {})
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
mod:SetStackCount(all_stack - spray_count * quill_release_threshold)
|
||||
return
|
||||
end
|
||||
end
|
||||
if all_stack >= quill_release_threshold then
|
||||
local count = math.floor(all_stack / quill_release_threshold)
|
||||
do
|
||||
local i = 0
|
||||
while i < count do
|
||||
____exports.modifier_bristleback_bristleback_custom_make_spray:apply(self.caster, self.caster, self, {})
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
mod:SetStackCount(all_stack - count * quill_release_threshold)
|
||||
else
|
||||
mod:SetStackCount(all_stack)
|
||||
end
|
||||
end
|
||||
bristleback_bristleback_custom = __TS__Decorate(
|
||||
bristleback_bristleback_custom,
|
||||
bristleback_bristleback_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "bristleback_bristleback_custom"}
|
||||
)
|
||||
____exports.bristleback_bristleback_custom = bristleback_bristleback_custom
|
||||
____exports.modifier_bristleback_bristleback_custom = __TS__Class()
|
||||
local modifier_bristleback_bristleback_custom = ____exports.modifier_bristleback_bristleback_custom
|
||||
modifier_bristleback_bristleback_custom.name = "modifier_bristleback_bristleback_custom"
|
||||
modifier_bristleback_bristleback_custom.____file_path = "scripts/vscripts/abilities/heroes/bristleback/bristleback_bristleback_custom.lua"
|
||||
__TS__ClassExtends(modifier_bristleback_bristleback_custom, BaseModifier)
|
||||
function modifier_bristleback_bristleback_custom.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.ability = self:GetAbility()
|
||||
self.parent = self:GetParent()
|
||||
self.side_angle = 0
|
||||
self.back_angle = 0
|
||||
end
|
||||
function modifier_bristleback_bristleback_custom.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bristleback_bristleback_custom.prototype.IsPurgeException(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bristleback_bristleback_custom.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_bristleback_bristleback_custom.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_EVENT_ON_TAKEDAMAGE}
|
||||
end
|
||||
function modifier_bristleback_bristleback_custom.prototype.OnCreated(self)
|
||||
self.side_angle = self.ability:GetSpecialValueFor("side_angle")
|
||||
self.back_angle = self.ability:GetSpecialValueFor("back_angle")
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
setIncomingDamageReductionEventSource(
|
||||
nil,
|
||||
self.parent,
|
||||
BRISTLEBACK_INCOMING_SOURCE,
|
||||
function(____, event)
|
||||
if self.parent:PassivesDisabled() then
|
||||
return 0
|
||||
end
|
||||
if bit.band(event.damage_flags, DOTA_DAMAGE_FLAG_REFLECTION) == DOTA_DAMAGE_FLAG_REFLECTION then
|
||||
return 0
|
||||
end
|
||||
if bit.band(event.damage_flags, DOTA_DAMAGE_FLAG_HPLOSS) == DOTA_DAMAGE_FLAG_HPLOSS then
|
||||
return 0
|
||||
end
|
||||
local forwardVector = self.parent:GetForwardVector()
|
||||
local forwardAngle = math.deg(math.atan2(forwardVector.x, forwardVector.y))
|
||||
local reverseEnemyVector = (self.parent:GetAbsOrigin() - event.attacker:GetAbsOrigin()):Normalized()
|
||||
local reverseEnemyAngle = math.deg(math.atan2(reverseEnemyVector.x, reverseEnemyVector.y))
|
||||
local difference = math.abs(forwardAngle - reverseEnemyAngle)
|
||||
local sideDamageReduction = self.ability:GetSpecialValueFor("side_damage_reduction")
|
||||
local backDamageReduction = self.ability:GetSpecialValueFor("back_damage_reduction")
|
||||
if difference <= self.back_angle / 1 or difference >= 360 - self.back_angle / 1 then
|
||||
local particle = ParticleManager:CreateParticle("particles/units/heroes/hero_bristleback/bristleback_back_dmg.vpcf", PATTACH_ABSORIGIN_FOLLOW, self.parent)
|
||||
ParticleManager:SetParticleControl(
|
||||
particle,
|
||||
1,
|
||||
self.parent:GetAbsOrigin()
|
||||
)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
particle,
|
||||
1,
|
||||
self.parent,
|
||||
PATTACH_POINT_FOLLOW,
|
||||
"attach_hitloc",
|
||||
self.parent:GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(particle)
|
||||
local particle2 = ParticleManager:CreateParticle("particles/units/heroes/hero_bristleback/bristleback_back_lrg_dmg.vpcf", PATTACH_ABSORIGIN_FOLLOW, self.parent)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
particle2,
|
||||
1,
|
||||
self.parent,
|
||||
PATTACH_POINT_FOLLOW,
|
||||
"attach_hitloc",
|
||||
self.parent:GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(particle2)
|
||||
EmitSoundOn("Hero_Bristleback.Bristleback", self.parent)
|
||||
return math.max(0, backDamageReduction)
|
||||
end
|
||||
if difference <= self.side_angle or difference >= 360 - self.side_angle then
|
||||
local particle = ParticleManager:CreateParticle("particles/units/heroes/hero_bristleback/bristleback_back_dmg.vpcf", PATTACH_ABSORIGIN_FOLLOW, self.parent)
|
||||
ParticleManager:SetParticleControl(
|
||||
particle,
|
||||
1,
|
||||
self.parent:GetAbsOrigin()
|
||||
)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
particle,
|
||||
1,
|
||||
self.parent,
|
||||
PATTACH_POINT_FOLLOW,
|
||||
"attach_hitloc",
|
||||
self.parent:GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(particle)
|
||||
return math.max(0, sideDamageReduction)
|
||||
end
|
||||
return 0
|
||||
end
|
||||
)
|
||||
end
|
||||
function modifier_bristleback_bristleback_custom.prototype.OnRefresh(self)
|
||||
self:OnCreated()
|
||||
end
|
||||
function modifier_bristleback_bristleback_custom.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
removeIncomingDamageReductionSource(nil, self.parent, BRISTLEBACK_INCOMING_SOURCE)
|
||||
end
|
||||
function modifier_bristleback_bristleback_custom.prototype.OnTakeDamage(self, event)
|
||||
if event.attacker == nil then
|
||||
return
|
||||
end
|
||||
if event.unit ~= self.parent then
|
||||
return
|
||||
end
|
||||
if self.parent:PassivesDisabled() then
|
||||
return
|
||||
end
|
||||
if bit.band(event.damage_flags, DOTA_DAMAGE_FLAG_REFLECTION) == DOTA_DAMAGE_FLAG_REFLECTION then
|
||||
return
|
||||
end
|
||||
if bit.band(event.damage_flags, DOTA_DAMAGE_FLAG_HPLOSS) == DOTA_DAMAGE_FLAG_HPLOSS then
|
||||
return
|
||||
end
|
||||
if not self.parent:HasAbility("bristleback_quill_spray_custom") then
|
||||
return
|
||||
end
|
||||
if not self.parent:FindAbilityByName("bristleback_quill_spray_custom"):IsTrained() then
|
||||
return
|
||||
end
|
||||
if self.ability:IsFacingBack(event.attacker:GetEntityIndex()) then
|
||||
self.ability:IncStacks(event.damage)
|
||||
end
|
||||
end
|
||||
modifier_bristleback_bristleback_custom = __TS__Decorate(
|
||||
modifier_bristleback_bristleback_custom,
|
||||
modifier_bristleback_bristleback_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_bristleback_bristleback_custom"}
|
||||
)
|
||||
____exports.modifier_bristleback_bristleback_custom = modifier_bristleback_bristleback_custom
|
||||
____exports.modifier_bristleback_bristleback_custom_make_spray = __TS__Class()
|
||||
local modifier_bristleback_bristleback_custom_make_spray = ____exports.modifier_bristleback_bristleback_custom_make_spray
|
||||
modifier_bristleback_bristleback_custom_make_spray.name = "modifier_bristleback_bristleback_custom_make_spray"
|
||||
modifier_bristleback_bristleback_custom_make_spray.____file_path = "scripts/vscripts/abilities/heroes/bristleback/bristleback_bristleback_custom.lua"
|
||||
__TS__ClassExtends(modifier_bristleback_bristleback_custom_make_spray, BaseModifier)
|
||||
function modifier_bristleback_bristleback_custom_make_spray.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_bristleback_bristleback_custom_make_spray.prototype.OnCreated(self, params)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self:SetStackCount(1)
|
||||
self:Active()
|
||||
self:StartIntervalThink(self:GetAbility():GetSpecialValueFor("quill_release_interval"))
|
||||
end
|
||||
function modifier_bristleback_bristleback_custom_make_spray.prototype.OnRefresh(self, params)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self:IncrementStackCount()
|
||||
end
|
||||
function modifier_bristleback_bristleback_custom_make_spray.prototype.Active(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if self:GetStackCount() <= 0 then
|
||||
return
|
||||
end
|
||||
self:DecrementStackCount()
|
||||
local ability_quill = self:GetParent():FindAbilityByName("bristleback_quill_spray_custom")
|
||||
if not ability_quill then
|
||||
return
|
||||
end
|
||||
if ability_quill:GetLevel() <= 0 then
|
||||
return
|
||||
end
|
||||
ability_quill:MakeSpray(nil, true)
|
||||
end
|
||||
function modifier_bristleback_bristleback_custom_make_spray.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self:Active()
|
||||
if self:GetStackCount() <= 0 then
|
||||
self:Destroy()
|
||||
end
|
||||
end
|
||||
modifier_bristleback_bristleback_custom_make_spray = __TS__Decorate(
|
||||
modifier_bristleback_bristleback_custom_make_spray,
|
||||
modifier_bristleback_bristleback_custom_make_spray,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_bristleback_bristleback_custom_make_spray"}
|
||||
)
|
||||
____exports.modifier_bristleback_bristleback_custom_make_spray = modifier_bristleback_bristleback_custom_make_spray
|
||||
return ____exports
|
||||
@@ -0,0 +1,87 @@
|
||||
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 registerAbility = ____dota_ts_adapter.registerAbility
|
||||
____exports.bristleback_hairball_custom = __TS__Class()
|
||||
local bristleback_hairball_custom = ____exports.bristleback_hairball_custom
|
||||
bristleback_hairball_custom.name = "bristleback_hairball_custom"
|
||||
bristleback_hairball_custom.____file_path = "scripts/vscripts/abilities/heroes/bristleback/bristleback_hairball_custom.lua"
|
||||
__TS__ClassExtends(bristleback_hairball_custom, BaseAbility)
|
||||
function bristleback_hairball_custom.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_bristleback/bristleback_hairball.vpcf", context)
|
||||
PrecacheResource("model", "models/heroes/lanaya/lanaya_trap_crystal_invis.vmdl", context)
|
||||
end
|
||||
function bristleback_hairball_custom.prototype.OnSpellStart(self)
|
||||
local caster = self:GetCaster()
|
||||
EmitSoundOn("Hero_Bristleback.Hairball.Cast", caster)
|
||||
local projectile = {
|
||||
Ability = self,
|
||||
EffectName = "particles/units/heroes/hero_bristleback/bristleback_hairball.vpcf",
|
||||
vSpawnOrigin = caster:GetAttachmentOrigin(caster:ScriptLookupAttachment("attach_hitloc")),
|
||||
fDistance = (self:GetCursorPosition() - caster:GetAbsOrigin()):Length2D(),
|
||||
fStartRadius = 0,
|
||||
fEndRadius = 0,
|
||||
Source = caster,
|
||||
bHasFrontalCone = false,
|
||||
bReplaceExisting = false,
|
||||
iUnitTargetTeam = DOTA_UNIT_TARGET_TEAM_NONE,
|
||||
iUnitTargetFlags = DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
iUnitTargetType = DOTA_UNIT_TARGET_NONE,
|
||||
fExpireTime = GameRules:GetGameTime() + 5,
|
||||
bDeleteOnHit = false,
|
||||
vVelocity = (self:GetCursorPosition() - self:GetCaster():GetAbsOrigin()):Normalized() * self:GetSpecialValueFor("projectile_speed") * Vector(1, 1, 0),
|
||||
bProvidesVision = false
|
||||
}
|
||||
ProjectileManager:CreateLinearProjectile(projectile)
|
||||
end
|
||||
function bristleback_hairball_custom.prototype.OnProjectileHit(self, target, vLocation)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local spray = caster:FindAbilityByName("bristleback_quill_spray_custom")
|
||||
AddFOWViewer(
|
||||
caster:GetTeamNumber(),
|
||||
vLocation,
|
||||
self:GetSpecialValueFor("radius"),
|
||||
2,
|
||||
false
|
||||
)
|
||||
self:AddGoo(vLocation, caster)
|
||||
Timers:CreateTimer(
|
||||
0.15,
|
||||
function()
|
||||
self:AddGoo(vLocation, caster)
|
||||
end
|
||||
)
|
||||
if spray and spray:GetLevel() > 0 then
|
||||
do
|
||||
local i = 0
|
||||
while i < self:GetSpecialValueFor("quill_stacks") do
|
||||
spray:MakeSpray(
|
||||
GetGroundPosition(vLocation, nil),
|
||||
nil
|
||||
)
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
function bristleback_hairball_custom.prototype.AddGoo(self, vLocation, caster)
|
||||
local goo = caster:FindAbilityByName("bristleback_viscous_nasal_goo_custom")
|
||||
if goo and goo:GetLevel() > 0 then
|
||||
goo:OnCustomSpellStart(nil, vLocation)
|
||||
end
|
||||
end
|
||||
bristleback_hairball_custom = __TS__Decorate(
|
||||
bristleback_hairball_custom,
|
||||
bristleback_hairball_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "bristleback_hairball_custom"}
|
||||
)
|
||||
____exports.bristleback_hairball_custom = bristleback_hairball_custom
|
||||
return ____exports
|
||||
@@ -0,0 +1,330 @@
|
||||
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 ____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
|
||||
____exports.bristleback_quill_spray_custom = __TS__Class()
|
||||
local bristleback_quill_spray_custom = ____exports.bristleback_quill_spray_custom
|
||||
bristleback_quill_spray_custom.name = "bristleback_quill_spray_custom"
|
||||
bristleback_quill_spray_custom.____file_path = "scripts/vscripts/abilities/heroes/bristleback/bristleback_quill_spray_custom.lua"
|
||||
__TS__ClassExtends(bristleback_quill_spray_custom, BaseAbility)
|
||||
function bristleback_quill_spray_custom.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_bristleback/bristleback_quill_spray.vpcf", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_bristleback/bristleback_quill_spray_impact.vpcf", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_bristleback/bristleback_quill_spray_hit.vpcf", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_bristleback/bristleback_quill_spray_hit_creep.vpcf", context)
|
||||
end
|
||||
function bristleback_quill_spray_custom.prototype.GetCastRange(self, location, target)
|
||||
return self:GetSpecialValueFor("radius")
|
||||
end
|
||||
function bristleback_quill_spray_custom.prototype.OnSpellStart(self)
|
||||
self:MakeSpray(nil, nil)
|
||||
end
|
||||
function bristleback_quill_spray_custom.prototype.GetIntrinsicModifierName(self)
|
||||
return ____exports.modifier_bristleback_quill_spray_autocast_custom.name
|
||||
end
|
||||
function bristleback_quill_spray_custom.prototype.MakeSpray(self, location, isPassive)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local projectile_speed = self:GetSpecialValueFor("projectile_speed")
|
||||
local radius = self:GetSpecialValueFor("radius")
|
||||
local duration = radius / projectile_speed
|
||||
if location == nil then
|
||||
caster:FadeGesture(ACT_DOTA_CAST_ABILITY_2)
|
||||
caster:StartGesture(ACT_DOTA_CAST_ABILITY_2)
|
||||
end
|
||||
local x = nil
|
||||
local y = nil
|
||||
if location ~= nil then
|
||||
x = location.x
|
||||
y = location.y
|
||||
end
|
||||
____exports.modifier_bristleback_quillspray_custom_thinker:apply(caster, caster, self, {x = x, y = y, duration = duration, passive = isPassive})
|
||||
EmitSoundOn("Hero_Bristleback.QuillSpray.Cast", caster)
|
||||
end
|
||||
bristleback_quill_spray_custom = __TS__Decorate(
|
||||
bristleback_quill_spray_custom,
|
||||
bristleback_quill_spray_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "bristleback_quill_spray_custom"}
|
||||
)
|
||||
____exports.bristleback_quill_spray_custom = bristleback_quill_spray_custom
|
||||
____exports.modifier_bristleback_quill_spray_autocast_custom = __TS__Class()
|
||||
local modifier_bristleback_quill_spray_autocast_custom = ____exports.modifier_bristleback_quill_spray_autocast_custom
|
||||
modifier_bristleback_quill_spray_autocast_custom.name = "modifier_bristleback_quill_spray_autocast_custom"
|
||||
modifier_bristleback_quill_spray_autocast_custom.____file_path = "scripts/vscripts/abilities/heroes/bristleback/bristleback_quill_spray_custom.lua"
|
||||
__TS__ClassExtends(modifier_bristleback_quill_spray_autocast_custom, BaseModifier)
|
||||
function modifier_bristleback_quill_spray_autocast_custom.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.ability = self:GetAbility()
|
||||
end
|
||||
function modifier_bristleback_quill_spray_autocast_custom.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bristleback_quill_spray_autocast_custom.prototype.IsPurgeException(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bristleback_quill_spray_autocast_custom.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_bristleback_quill_spray_autocast_custom.prototype.OnCreated(self, params)
|
||||
self:StartIntervalThink(0.1)
|
||||
end
|
||||
function modifier_bristleback_quill_spray_autocast_custom.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
if self.ability:GetAutoCastState() == true and self.ability:IsFullyCastable() and not parent:IsChanneling() and not self.ability:IsHidden() then
|
||||
parent:CastAbilityNoTarget(
|
||||
self.ability,
|
||||
parent:GetPlayerID()
|
||||
)
|
||||
end
|
||||
end
|
||||
modifier_bristleback_quill_spray_autocast_custom = __TS__Decorate(
|
||||
modifier_bristleback_quill_spray_autocast_custom,
|
||||
modifier_bristleback_quill_spray_autocast_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_bristleback_quill_spray_autocast_custom"}
|
||||
)
|
||||
____exports.modifier_bristleback_quill_spray_autocast_custom = modifier_bristleback_quill_spray_autocast_custom
|
||||
____exports.modifier_bristleback_quillspray_custom_thinker = __TS__Class()
|
||||
local modifier_bristleback_quillspray_custom_thinker = ____exports.modifier_bristleback_quillspray_custom_thinker
|
||||
modifier_bristleback_quillspray_custom_thinker.name = "modifier_bristleback_quillspray_custom_thinker"
|
||||
modifier_bristleback_quillspray_custom_thinker.____file_path = "scripts/vscripts/abilities/heroes/bristleback/bristleback_quill_spray_custom.lua"
|
||||
__TS__ClassExtends(modifier_bristleback_quillspray_custom_thinker, BaseModifier)
|
||||
function modifier_bristleback_quillspray_custom_thinker.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.caster = self:GetCaster()
|
||||
self.parent = self:GetParent()
|
||||
self.ability = self:GetAbility()
|
||||
self.radius = 0
|
||||
self.quill_base_damage = 0
|
||||
self.quill_stack_damage = 0
|
||||
self.quill_stack_duration = 0
|
||||
self.max_damage = 0
|
||||
self.attack_damage_bonus_pct = 0
|
||||
self.hit_enemies = {}
|
||||
self.cast_point = Vector(0, 0, 0)
|
||||
self.passive = false
|
||||
end
|
||||
function modifier_bristleback_quillspray_custom_thinker.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_bristleback_quillspray_custom_thinker.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bristleback_quillspray_custom_thinker.prototype.RemoveOnDeath(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bristleback_quillspray_custom_thinker.prototype.GetAttributes(self)
|
||||
return MODIFIER_ATTRIBUTE_MULTIPLE
|
||||
end
|
||||
function modifier_bristleback_quillspray_custom_thinker.prototype.OnCreated(self, params)
|
||||
self.radius = self.ability:GetSpecialValueFor("radius")
|
||||
self.quill_base_damage = self.ability:GetSpecialValueFor("quill_base_damage")
|
||||
self.quill_stack_damage = self.ability:GetSpecialValueFor("quill_stack_damage")
|
||||
self.quill_stack_duration = self.ability:GetSpecialValueFor("quill_stack_duration")
|
||||
self.max_damage = self.ability:GetSpecialValueFor("max_damage")
|
||||
self.attack_damage_bonus_pct = self.ability:GetSpecialValueFor("attack_damage_bonus_pct")
|
||||
self.passive = params.passive
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self.cast_point = self.parent:GetAbsOrigin()
|
||||
if params.x ~= nil and params.y ~= nil then
|
||||
self.cast_point = GetGroundPosition(
|
||||
Vector(params.x, params.y, 0),
|
||||
nil
|
||||
)
|
||||
end
|
||||
local particle = ParticleManager:CreateParticle("particles/units/heroes/hero_bristleback/bristleback_quill_spray.vpcf", PATTACH_WORLDORIGIN, nil)
|
||||
ParticleManager:SetParticleControl(particle, 0, self.cast_point)
|
||||
self:AddParticle(
|
||||
particle,
|
||||
false,
|
||||
false,
|
||||
-1,
|
||||
false,
|
||||
false
|
||||
)
|
||||
self.hit_enemies = {}
|
||||
self:StartIntervalThink(FrameTime())
|
||||
end
|
||||
function modifier_bristleback_quillspray_custom_thinker.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local radius_pct = math.min(
|
||||
(self:GetDuration() - self:GetRemainingTime()) / self:GetDuration(),
|
||||
1
|
||||
)
|
||||
local origin = self.cast_point
|
||||
local enemies = FindUnitsInRadius(
|
||||
self.parent:GetTeamNumber(),
|
||||
origin,
|
||||
nil,
|
||||
self.radius * radius_pct,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_CREEP,
|
||||
DOTA_UNIT_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
__TS__ArrayForEach(
|
||||
enemies,
|
||||
function(____, enemy)
|
||||
if not (self.hit_enemies[enemy:GetEntityIndex()] ~= nil) then
|
||||
self.hit_enemies[enemy:GetEntityIndex()] = true
|
||||
local quill_spray_stacks = 0
|
||||
local quill_spray_modifier = enemy:FindModifierByName("modifier_bristleback_quill_spray_custom")
|
||||
if quill_spray_modifier ~= nil then
|
||||
quill_spray_stacks = quill_spray_modifier:GetStackCount()
|
||||
end
|
||||
local attackBonus = self.caster:GetAverageTrueAttackDamage(enemy) * self.attack_damage_bonus_pct / 100
|
||||
local damage_flags = DOTA_DAMAGE_FLAG_BYPASSES_BLOCK
|
||||
ApplyDamage({
|
||||
victim = enemy,
|
||||
damage = math.min(self.quill_base_damage + self.quill_stack_damage * quill_spray_stacks + attackBonus, self.max_damage),
|
||||
damage_type = DAMAGE_TYPE_PHYSICAL,
|
||||
damage_flags = damage_flags,
|
||||
attacker = self.caster,
|
||||
ability = self.ability
|
||||
})
|
||||
local particle = ParticleManager:CreateParticle("particles/units/heroes/hero_bristleback/bristleback_quill_spray_impact.vpcf", PATTACH_ABSORIGIN_FOLLOW, enemy)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
particle,
|
||||
1,
|
||||
enemy,
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
"attach_hitloc",
|
||||
enemy:GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(particle)
|
||||
EmitSoundOn("Hero_Bristleback.QuillSpray.Target", enemy)
|
||||
____exports.modifier_bristleback_quill_spray_custom:apply(
|
||||
enemy,
|
||||
self.caster,
|
||||
self.ability,
|
||||
{duration = self.quill_stack_duration * (1 - enemy:GetStatusResistance())}
|
||||
)
|
||||
____exports.modifier_bristleback_quill_spray_custom_count:apply(
|
||||
enemy,
|
||||
self.caster,
|
||||
self.ability,
|
||||
{duration = self.quill_stack_duration * (1 - enemy:GetStatusResistance())}
|
||||
)
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
modifier_bristleback_quillspray_custom_thinker = __TS__Decorate(
|
||||
modifier_bristleback_quillspray_custom_thinker,
|
||||
modifier_bristleback_quillspray_custom_thinker,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_bristleback_quillspray_custom_thinker"}
|
||||
)
|
||||
____exports.modifier_bristleback_quillspray_custom_thinker = modifier_bristleback_quillspray_custom_thinker
|
||||
____exports.modifier_bristleback_quill_spray_custom = __TS__Class()
|
||||
local modifier_bristleback_quill_spray_custom = ____exports.modifier_bristleback_quill_spray_custom
|
||||
modifier_bristleback_quill_spray_custom.name = "modifier_bristleback_quill_spray_custom"
|
||||
modifier_bristleback_quill_spray_custom.____file_path = "scripts/vscripts/abilities/heroes/bristleback/bristleback_quill_spray_custom.lua"
|
||||
__TS__ClassExtends(modifier_bristleback_quill_spray_custom, BaseModifier)
|
||||
function modifier_bristleback_quill_spray_custom.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.parent = self:GetParent()
|
||||
end
|
||||
function modifier_bristleback_quill_spray_custom.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bristleback_quill_spray_custom.prototype.OnCreated(self, params)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self:IncrementStackCount()
|
||||
local particle_name = "particles/units/heroes/hero_bristleback/bristleback_quill_spray_hit.vpcf"
|
||||
if self.parent:IsCreep() then
|
||||
particle_name = "particles/units/heroes/hero_bristleback/bristleback_quill_spray_hit_creep.vpcf"
|
||||
end
|
||||
local particle = ParticleManager:CreateParticle(particle_name, PATTACH_ABSORIGIN_FOLLOW, self.parent)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
particle,
|
||||
0,
|
||||
self.parent,
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
"attach_hitloc",
|
||||
self.parent:GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
particle,
|
||||
1,
|
||||
self.parent,
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
"attach_hitloc",
|
||||
self.parent:GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
self:AddParticle(
|
||||
particle,
|
||||
false,
|
||||
false,
|
||||
-1,
|
||||
false,
|
||||
false
|
||||
)
|
||||
end
|
||||
function modifier_bristleback_quill_spray_custom.prototype.OnRefresh(self, params)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self:IncrementStackCount()
|
||||
end
|
||||
modifier_bristleback_quill_spray_custom = __TS__Decorate(
|
||||
modifier_bristleback_quill_spray_custom,
|
||||
modifier_bristleback_quill_spray_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_bristleback_quill_spray_custom"}
|
||||
)
|
||||
____exports.modifier_bristleback_quill_spray_custom = modifier_bristleback_quill_spray_custom
|
||||
____exports.modifier_bristleback_quill_spray_custom_count = __TS__Class()
|
||||
local modifier_bristleback_quill_spray_custom_count = ____exports.modifier_bristleback_quill_spray_custom_count
|
||||
modifier_bristleback_quill_spray_custom_count.name = "modifier_bristleback_quill_spray_custom_count"
|
||||
modifier_bristleback_quill_spray_custom_count.____file_path = "scripts/vscripts/abilities/heroes/bristleback/bristleback_quill_spray_custom.lua"
|
||||
__TS__ClassExtends(modifier_bristleback_quill_spray_custom_count, BaseModifier)
|
||||
function modifier_bristleback_quill_spray_custom_count.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_bristleback_quill_spray_custom_count.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bristleback_quill_spray_custom_count.prototype.GetAttributes(self)
|
||||
return MODIFIER_ATTRIBUTE_MULTIPLE
|
||||
end
|
||||
function modifier_bristleback_quill_spray_custom_count.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local modifier = self:GetParent():FindModifierByName("modifier_bristleback_quill_spray_custom")
|
||||
if modifier ~= nil then
|
||||
modifier:DecrementStackCount()
|
||||
end
|
||||
end
|
||||
modifier_bristleback_quill_spray_custom_count = __TS__Decorate(
|
||||
modifier_bristleback_quill_spray_custom_count,
|
||||
modifier_bristleback_quill_spray_custom_count,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_bristleback_quill_spray_custom_count"}
|
||||
)
|
||||
____exports.modifier_bristleback_quill_spray_custom_count = modifier_bristleback_quill_spray_custom_count
|
||||
return ____exports
|
||||
@@ -0,0 +1,127 @@
|
||||
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 = require("abilities.system.hero_rage")
|
||||
local heroRageGetCurrent = ____hero_rage.heroRageGetCurrent
|
||||
local WARPATH_ACTIVE_PARTICLE = "particles/units/heroes/hero_bristleback/bristleback_warpath_active.vpcf"
|
||||
____exports.bristleback_rage_fortitude_custom = __TS__Class()
|
||||
local bristleback_rage_fortitude_custom = ____exports.bristleback_rage_fortitude_custom
|
||||
bristleback_rage_fortitude_custom.name = "bristleback_rage_fortitude_custom"
|
||||
bristleback_rage_fortitude_custom.____file_path = "scripts/vscripts/abilities/heroes/bristleback/bristleback_rage_fortitude_custom.lua"
|
||||
__TS__ClassExtends(bristleback_rage_fortitude_custom, BaseAbility)
|
||||
function bristleback_rage_fortitude_custom.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", WARPATH_ACTIVE_PARTICLE, context)
|
||||
end
|
||||
function bristleback_rage_fortitude_custom.prototype.GetIntrinsicModifierName(self)
|
||||
return ____exports.modifier_bristleback_rage_fortitude_custom.name
|
||||
end
|
||||
bristleback_rage_fortitude_custom = __TS__Decorate(
|
||||
bristleback_rage_fortitude_custom,
|
||||
bristleback_rage_fortitude_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "bristleback_rage_fortitude_custom"}
|
||||
)
|
||||
____exports.bristleback_rage_fortitude_custom = bristleback_rage_fortitude_custom
|
||||
--- Следит за яростью и включает защитный бафф выше порога.
|
||||
____exports.modifier_bristleback_rage_fortitude_custom = __TS__Class()
|
||||
local modifier_bristleback_rage_fortitude_custom = ____exports.modifier_bristleback_rage_fortitude_custom
|
||||
modifier_bristleback_rage_fortitude_custom.name = "modifier_bristleback_rage_fortitude_custom"
|
||||
modifier_bristleback_rage_fortitude_custom.____file_path = "scripts/vscripts/abilities/heroes/bristleback/bristleback_rage_fortitude_custom.lua"
|
||||
__TS__ClassExtends(modifier_bristleback_rage_fortitude_custom, BaseModifier)
|
||||
function modifier_bristleback_rage_fortitude_custom.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_bristleback_rage_fortitude_custom.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bristleback_rage_fortitude_custom.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self:StartIntervalThink(0.1)
|
||||
self:OnIntervalThink()
|
||||
end
|
||||
function modifier_bristleback_rage_fortitude_custom.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
if not ability or parent:PassivesDisabled() then
|
||||
parent:RemoveModifierByName(____exports.modifier_bristleback_rage_fortitude_buff.name)
|
||||
return
|
||||
end
|
||||
local threshold = ability:GetSpecialValueFor("rage_threshold")
|
||||
local rage = heroRageGetCurrent(nil, parent)
|
||||
local buffName = ____exports.modifier_bristleback_rage_fortitude_buff.name
|
||||
if rage > threshold then
|
||||
if not parent:HasModifier(buffName) then
|
||||
parent:AddNewModifier(parent, ability, buffName, {})
|
||||
end
|
||||
else
|
||||
parent:RemoveModifierByName(buffName)
|
||||
end
|
||||
end
|
||||
function modifier_bristleback_rage_fortitude_custom.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self:GetParent():RemoveModifierByName(____exports.modifier_bristleback_rage_fortitude_buff.name)
|
||||
end
|
||||
modifier_bristleback_rage_fortitude_custom = __TS__Decorate(
|
||||
modifier_bristleback_rage_fortitude_custom,
|
||||
modifier_bristleback_rage_fortitude_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_bristleback_rage_fortitude_custom"}
|
||||
)
|
||||
____exports.modifier_bristleback_rage_fortitude_custom = modifier_bristleback_rage_fortitude_custom
|
||||
____exports.modifier_bristleback_rage_fortitude_buff = __TS__Class()
|
||||
local modifier_bristleback_rage_fortitude_buff = ____exports.modifier_bristleback_rage_fortitude_buff
|
||||
modifier_bristleback_rage_fortitude_buff.name = "modifier_bristleback_rage_fortitude_buff"
|
||||
modifier_bristleback_rage_fortitude_buff.____file_path = "scripts/vscripts/abilities/heroes/bristleback/bristleback_rage_fortitude_custom.lua"
|
||||
__TS__ClassExtends(modifier_bristleback_rage_fortitude_buff, BaseModifier)
|
||||
function modifier_bristleback_rage_fortitude_buff.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bristleback_rage_fortitude_buff.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bristleback_rage_fortitude_buff.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bristleback_rage_fortitude_buff.prototype.GetTexture(self)
|
||||
return "bristleback_warpath"
|
||||
end
|
||||
function modifier_bristleback_rage_fortitude_buff.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_DEBUFF_IMMUNE] = true}
|
||||
end
|
||||
function modifier_bristleback_rage_fortitude_buff.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_MAGICAL_RESISTANCE_BONUS, MODIFIER_PROPERTY_PHYSICAL_ARMOR_BONUS}
|
||||
end
|
||||
function modifier_bristleback_rage_fortitude_buff.prototype.GetModifierMagicalResistanceBonus(self)
|
||||
return self:GetAbility():GetSpecialValueFor("bonus_magic_resist")
|
||||
end
|
||||
function modifier_bristleback_rage_fortitude_buff.prototype.GetModifierPhysicalArmorBonus(self)
|
||||
return self:GetAbility():GetSpecialValueFor("bonus_armor")
|
||||
end
|
||||
function modifier_bristleback_rage_fortitude_buff.prototype.GetEffectName(self)
|
||||
return WARPATH_ACTIVE_PARTICLE
|
||||
end
|
||||
function modifier_bristleback_rage_fortitude_buff.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_ABSORIGIN_FOLLOW
|
||||
end
|
||||
modifier_bristleback_rage_fortitude_buff = __TS__Decorate(
|
||||
modifier_bristleback_rage_fortitude_buff,
|
||||
modifier_bristleback_rage_fortitude_buff,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_bristleback_rage_fortitude_buff"}
|
||||
)
|
||||
____exports.modifier_bristleback_rage_fortitude_buff = modifier_bristleback_rage_fortitude_buff
|
||||
return ____exports
|
||||
+289
@@ -0,0 +1,289 @@
|
||||
local ____lualib = require("lualib_bundle")
|
||||
local __TS__Class = ____lualib.__TS__Class
|
||||
local __TS__ClassExtends = ____lualib.__TS__ClassExtends
|
||||
local __TS__ObjectAssign = ____lualib.__TS__ObjectAssign
|
||||
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
|
||||
____exports.bristleback_viscous_nasal_goo_custom = __TS__Class()
|
||||
local bristleback_viscous_nasal_goo_custom = ____exports.bristleback_viscous_nasal_goo_custom
|
||||
bristleback_viscous_nasal_goo_custom.name = "bristleback_viscous_nasal_goo_custom"
|
||||
bristleback_viscous_nasal_goo_custom.____file_path = "scripts/vscripts/abilities/heroes/bristleback/bristleback_viscous_nasal_goo_custom.lua"
|
||||
__TS__ClassExtends(bristleback_viscous_nasal_goo_custom, BaseAbility)
|
||||
function bristleback_viscous_nasal_goo_custom.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_bristleback/bristleback_viscous_nasal_goo.vpcf", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_bristleback/bristleback_viscous_nasal_goo_debuff.vpcf", context)
|
||||
PrecacheResource("particle", "particles/status_fx/status_effect_goo.vpcf", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_bristleback/bristleback_viscous_nasal_stack.vpcf", context)
|
||||
end
|
||||
function bristleback_viscous_nasal_goo_custom.prototype.GetCastRange(self, location, target)
|
||||
return self:GetSpecialValueFor("AbilityCastRange")
|
||||
end
|
||||
function bristleback_viscous_nasal_goo_custom.prototype.GetAOERadius(self)
|
||||
return self:GetSpecialValueFor("radius")
|
||||
end
|
||||
function bristleback_viscous_nasal_goo_custom.prototype.OnSpellStart(self)
|
||||
local target = self:GetCursorTarget()
|
||||
if not target then
|
||||
return
|
||||
end
|
||||
self:OnCustomSpellStart(
|
||||
target:entindex(),
|
||||
nil
|
||||
)
|
||||
end
|
||||
function bristleback_viscous_nasal_goo_custom.prototype.OnCustomSpellStart(self, target, location)
|
||||
local caster = self:GetCaster()
|
||||
local sourceCaster = caster
|
||||
local sourceLocation = caster:GetAbsOrigin()
|
||||
local shardAbility = caster:FindAbilityByName("bristleback_hairball_custom")
|
||||
local shardRadius = shardAbility and shardAbility:GetSpecialValueFor("radius") or 0
|
||||
local isShard = 0
|
||||
if location ~= nil then
|
||||
local shardUnit = CreateUnitByName(
|
||||
"npc_dota_templar_assassin_psionic_trap",
|
||||
location,
|
||||
false,
|
||||
caster,
|
||||
caster,
|
||||
caster:GetTeamNumber()
|
||||
)
|
||||
____exports.modifier_bristleback_viscous_nasal_goo_shard_unit:apply(shardUnit, nil, self, {duration = 1})
|
||||
sourceLocation = shardUnit:GetAbsOrigin()
|
||||
sourceCaster = shardUnit
|
||||
isShard = 1
|
||||
EmitSoundOn("Hero_Bristleback.ViscousGoo.Cast", shardUnit)
|
||||
self:LaunchGooAtRadius(
|
||||
caster,
|
||||
sourceCaster,
|
||||
sourceLocation,
|
||||
shardRadius,
|
||||
isShard
|
||||
)
|
||||
return
|
||||
end
|
||||
if target == nil then
|
||||
return
|
||||
end
|
||||
local cursorTarget = EntIndexToHScript(target)
|
||||
if not cursorTarget or cursorTarget:IsNull() then
|
||||
return
|
||||
end
|
||||
EmitSoundOn("Hero_Bristleback.ViscousGoo.Cast", caster)
|
||||
local radius = self:GetSpecialValueFor("radius")
|
||||
self:LaunchGooAtRadius(
|
||||
caster,
|
||||
caster,
|
||||
caster:GetAbsOrigin(),
|
||||
radius,
|
||||
isShard,
|
||||
cursorTarget:GetAbsOrigin()
|
||||
)
|
||||
end
|
||||
function bristleback_viscous_nasal_goo_custom.prototype.LaunchGooAtRadius(self, caster, sourceCaster, sourceLocation, radius, isShard, impactLocation)
|
||||
local origin = impactLocation or sourceLocation
|
||||
local enemies = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
origin,
|
||||
nil,
|
||||
radius,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC,
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
if #enemies == 0 then
|
||||
return
|
||||
end
|
||||
local projectile = {
|
||||
Source = sourceCaster,
|
||||
Ability = self,
|
||||
iSourceAttachment = DOTA_PROJECTILE_ATTACHMENT_HITLOCATION,
|
||||
EffectName = "particles/units/heroes/hero_bristleback/bristleback_viscous_nasal_goo.vpcf",
|
||||
iMoveSpeed = self:GetSpecialValueFor("goo_speed"),
|
||||
vSourceLoc = sourceLocation,
|
||||
bDrawsOnMinimap = false,
|
||||
bDodgeable = true,
|
||||
bIsAttack = false,
|
||||
bVisibleToEnemies = true,
|
||||
bReplaceExisting = false,
|
||||
flExpireTime = GameRules:GetGameTime() + 10,
|
||||
bProvidesVision = false,
|
||||
ExtraData = {is_shard = isShard}
|
||||
}
|
||||
for ____, enemy in ipairs(enemies) do
|
||||
ProjectileManager:CreateTrackingProjectile(__TS__ObjectAssign({}, projectile, {Target = enemy}))
|
||||
end
|
||||
if caster:GetUnitName() == "npc_dota_hero_bristleback" then
|
||||
EmitSoundOn(
|
||||
"bristleback_bristle_nasal_goo_0" .. tostring(math.random(1, 7)),
|
||||
caster
|
||||
)
|
||||
end
|
||||
end
|
||||
function bristleback_viscous_nasal_goo_custom.prototype.OnProjectileHit_ExtraData(self, target, location, ExtraData)
|
||||
if target == nil or not target:IsAlive() or target:IsMagicImmune() then
|
||||
return
|
||||
end
|
||||
if ExtraData.is_shard and ExtraData.is_shard == 0 then
|
||||
if target:TriggerSpellAbsorb(self) then
|
||||
return
|
||||
end
|
||||
end
|
||||
local duration = self:GetSpecialValueFor("goo_duration")
|
||||
local modifier = target:FindModifierByName(____exports.modifier_bristleback_viscous_nasal_goo_custom.name)
|
||||
____exports.modifier_bristleback_viscous_nasal_goo_custom:apply(
|
||||
target,
|
||||
self:GetCaster(),
|
||||
self,
|
||||
{duration = duration * (1 - target:GetStatusResistance())}
|
||||
):SetStackCount((modifier and modifier:GetStackCount() or 0) + 1)
|
||||
EmitSoundOn("Hero_Bristleback.ViscousGoo.Target", target)
|
||||
end
|
||||
bristleback_viscous_nasal_goo_custom = __TS__Decorate(
|
||||
bristleback_viscous_nasal_goo_custom,
|
||||
bristleback_viscous_nasal_goo_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "bristleback_viscous_nasal_goo_custom"}
|
||||
)
|
||||
____exports.bristleback_viscous_nasal_goo_custom = bristleback_viscous_nasal_goo_custom
|
||||
____exports.modifier_bristleback_viscous_nasal_goo_custom = __TS__Class()
|
||||
local modifier_bristleback_viscous_nasal_goo_custom = ____exports.modifier_bristleback_viscous_nasal_goo_custom
|
||||
modifier_bristleback_viscous_nasal_goo_custom.name = "modifier_bristleback_viscous_nasal_goo_custom"
|
||||
modifier_bristleback_viscous_nasal_goo_custom.____file_path = "scripts/vscripts/abilities/heroes/bristleback/bristleback_viscous_nasal_goo_custom.lua"
|
||||
__TS__ClassExtends(modifier_bristleback_viscous_nasal_goo_custom, BaseModifier)
|
||||
function modifier_bristleback_viscous_nasal_goo_custom.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.parent = self:GetParent()
|
||||
self.movespeed_slow = 0
|
||||
self.armor_reduce_pct = 0
|
||||
self.armor_reduce_pct_per_stack = 0
|
||||
self.movespeed_slow_per_stack = 0
|
||||
self.ability = self:GetAbility()
|
||||
end
|
||||
function modifier_bristleback_viscous_nasal_goo_custom.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bristleback_viscous_nasal_goo_custom.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
function modifier_bristleback_viscous_nasal_goo_custom.prototype.GetEffectName(self)
|
||||
return "particles/units/heroes/hero_bristleback/bristleback_viscous_nasal_goo_debuff.vpcf"
|
||||
end
|
||||
function modifier_bristleback_viscous_nasal_goo_custom.prototype.GetStatusEffectName(self)
|
||||
return "particles/status_fx/status_effect_goo.vpcf"
|
||||
end
|
||||
function modifier_bristleback_viscous_nasal_goo_custom.prototype.StatusEffectPriority(self)
|
||||
return MODIFIER_PRIORITY_NORMAL
|
||||
end
|
||||
function modifier_bristleback_viscous_nasal_goo_custom.prototype.OnCreated(self, params)
|
||||
self.movespeed_slow = self.ability:GetSpecialValueFor("base_move_slow")
|
||||
self.armor_reduce_pct = self.ability:GetSpecialValueFor("base_armor_pct")
|
||||
self.armor_reduce_pct_per_stack = self.ability:GetSpecialValueFor("armor_per_stack_pct")
|
||||
self.movespeed_slow_per_stack = self.ability:GetSpecialValueFor("move_slow_per_stack")
|
||||
self.particle = ParticleManager:CreateParticle("particles/units/heroes/hero_bristleback/bristleback_viscous_nasal_stack.vpcf", PATTACH_OVERHEAD_FOLLOW, self.parent)
|
||||
ParticleManager:SetParticleControl(
|
||||
self.particle,
|
||||
1,
|
||||
Vector(
|
||||
0,
|
||||
self:GetStackCount(),
|
||||
0
|
||||
)
|
||||
)
|
||||
self:AddParticle(
|
||||
self.particle,
|
||||
false,
|
||||
false,
|
||||
-1,
|
||||
false,
|
||||
false
|
||||
)
|
||||
self.isBoss = self.parent:IsBossCreature()
|
||||
self:SetHasCustomTransmitterData(true)
|
||||
end
|
||||
function modifier_bristleback_viscous_nasal_goo_custom.prototype.AddCustomTransmitterData(self)
|
||||
return {isBoss = self.isBoss, movespeed_total = self.movespeed_total}
|
||||
end
|
||||
function modifier_bristleback_viscous_nasal_goo_custom.prototype.HandleCustomTransmitterData(self, data)
|
||||
self.isBoss = data.isBoss
|
||||
self.movespeed_total = data.movespeed_total
|
||||
end
|
||||
function modifier_bristleback_viscous_nasal_goo_custom.prototype.OnStackCountChanged(self, stackCount)
|
||||
if self.isBoss then
|
||||
self.movespeed_total = 0
|
||||
else
|
||||
self.movespeed_total = (self.movespeed_slow + self.movespeed_slow_per_stack * self:GetStackCount()) * -1
|
||||
end
|
||||
if self.particle ~= nil then
|
||||
ParticleManager:SetParticleControl(
|
||||
self.particle,
|
||||
1,
|
||||
Vector(
|
||||
0,
|
||||
self:GetStackCount(),
|
||||
0
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
function modifier_bristleback_viscous_nasal_goo_custom.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE, MODIFIER_PROPERTY_PHYSICAL_ARMOR_BONUS}
|
||||
end
|
||||
function modifier_bristleback_viscous_nasal_goo_custom.prototype.GetModifierMoveSpeedBonus_Percentage(self)
|
||||
return self.movespeed_total
|
||||
end
|
||||
function modifier_bristleback_viscous_nasal_goo_custom.prototype.GetModifierPhysicalArmorBonus(self, event)
|
||||
local baseArmor = self.parent:GetPhysicalArmorBaseValue()
|
||||
if baseArmor <= 0 then
|
||||
return 0
|
||||
end
|
||||
local totalPct = (self.armor_reduce_pct + self.armor_reduce_pct_per_stack * self:GetStackCount()) / 100
|
||||
return -baseArmor * totalPct
|
||||
end
|
||||
modifier_bristleback_viscous_nasal_goo_custom = __TS__Decorate(
|
||||
modifier_bristleback_viscous_nasal_goo_custom,
|
||||
modifier_bristleback_viscous_nasal_goo_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_bristleback_viscous_nasal_goo_custom"}
|
||||
)
|
||||
____exports.modifier_bristleback_viscous_nasal_goo_custom = modifier_bristleback_viscous_nasal_goo_custom
|
||||
____exports.modifier_bristleback_viscous_nasal_goo_shard_unit = __TS__Class()
|
||||
local modifier_bristleback_viscous_nasal_goo_shard_unit = ____exports.modifier_bristleback_viscous_nasal_goo_shard_unit
|
||||
modifier_bristleback_viscous_nasal_goo_shard_unit.name = "modifier_bristleback_viscous_nasal_goo_shard_unit"
|
||||
modifier_bristleback_viscous_nasal_goo_shard_unit.____file_path = "scripts/vscripts/abilities/heroes/bristleback/bristleback_viscous_nasal_goo_custom.lua"
|
||||
__TS__ClassExtends(modifier_bristleback_viscous_nasal_goo_shard_unit, BaseModifier)
|
||||
function modifier_bristleback_viscous_nasal_goo_shard_unit.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_bristleback_viscous_nasal_goo_shard_unit.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bristleback_viscous_nasal_goo_shard_unit.prototype.CheckState(self)
|
||||
return {
|
||||
[MODIFIER_STATE_INVULNERABLE] = true,
|
||||
[MODIFIER_STATE_OUT_OF_GAME] = true,
|
||||
[MODIFIER_STATE_STUNNED] = true,
|
||||
[MODIFIER_STATE_NO_HEALTH_BAR] = true,
|
||||
[MODIFIER_STATE_NO_UNIT_COLLISION] = true
|
||||
}
|
||||
end
|
||||
function modifier_bristleback_viscous_nasal_goo_shard_unit.prototype.OnCreated(self, params)
|
||||
self:GetParent():AddNoDraw()
|
||||
end
|
||||
function modifier_bristleback_viscous_nasal_goo_shard_unit.prototype.OnDestroy(self)
|
||||
UTIL_Remove(self:GetParent())
|
||||
end
|
||||
modifier_bristleback_viscous_nasal_goo_shard_unit = __TS__Decorate(
|
||||
modifier_bristleback_viscous_nasal_goo_shard_unit,
|
||||
modifier_bristleback_viscous_nasal_goo_shard_unit,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_bristleback_viscous_nasal_goo_shard_unit"}
|
||||
)
|
||||
____exports.modifier_bristleback_viscous_nasal_goo_shard_unit = modifier_bristleback_viscous_nasal_goo_shard_unit
|
||||
return ____exports
|
||||
@@ -0,0 +1,181 @@
|
||||
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
|
||||
--- Значения Warpath от уровня героя (не от уровня способности).
|
||||
local function getWarpathLevel(self, hero)
|
||||
return math.max(
|
||||
1,
|
||||
hero:GetLevel()
|
||||
)
|
||||
end
|
||||
local function getWarpathDamagePerStack(self, ability, hero)
|
||||
local level = getWarpathLevel(nil, hero)
|
||||
return ability:GetSpecialValueFor("damage_per_stack_base") + level * ability:GetSpecialValueFor("damage_per_stack_per_hero_level") + ability:GetSpecialValueFor("damage_per_stack")
|
||||
end
|
||||
local function getWarpathMoveSpeedPerStack(self, ability, hero)
|
||||
local level = getWarpathLevel(nil, hero)
|
||||
return ability:GetSpecialValueFor("move_speed_per_stack_base") + level * ability:GetSpecialValueFor("move_speed_per_stack_per_hero_level")
|
||||
end
|
||||
local function getWarpathMaxStacks(self, ability, hero)
|
||||
local level = getWarpathLevel(nil, hero)
|
||||
return math.floor(ability:GetSpecialValueFor("max_stacks_base") + level * ability:GetSpecialValueFor("max_stacks_per_hero_level"))
|
||||
end
|
||||
local function getWarpathStackDuration(self, ability, hero)
|
||||
local level = getWarpathLevel(nil, hero)
|
||||
return ability:GetSpecialValueFor("stack_duration_base") + level * ability:GetSpecialValueFor("stack_duration_per_hero_level")
|
||||
end
|
||||
____exports.bristleback_warpath = __TS__Class()
|
||||
local bristleback_warpath = ____exports.bristleback_warpath
|
||||
bristleback_warpath.name = "bristleback_warpath"
|
||||
bristleback_warpath.____file_path = "scripts/vscripts/abilities/heroes/bristleback/bristleback_warpath_custom.lua"
|
||||
__TS__ClassExtends(bristleback_warpath, BaseAbility)
|
||||
function bristleback_warpath.prototype.GetIntrinsicModifierName(self)
|
||||
return ____exports.modifier_bristleback_warpath_custom.name
|
||||
end
|
||||
bristleback_warpath = __TS__Decorate(
|
||||
bristleback_warpath,
|
||||
bristleback_warpath,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "bristleback_warpath"}
|
||||
)
|
||||
____exports.bristleback_warpath = bristleback_warpath
|
||||
____exports.modifier_bristleback_warpath_custom = __TS__Class()
|
||||
local modifier_bristleback_warpath_custom = ____exports.modifier_bristleback_warpath_custom
|
||||
modifier_bristleback_warpath_custom.name = "modifier_bristleback_warpath_custom"
|
||||
modifier_bristleback_warpath_custom.____file_path = "scripts/vscripts/abilities/heroes/bristleback/bristleback_warpath_custom.lua"
|
||||
__TS__ClassExtends(modifier_bristleback_warpath_custom, BaseModifier)
|
||||
function modifier_bristleback_warpath_custom.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_bristleback_warpath_custom.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bristleback_warpath_custom.prototype.RemoveOnDeath(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bristleback_warpath_custom.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_EVENT_ON_TAKEDAMAGE}
|
||||
end
|
||||
function modifier_bristleback_warpath_custom.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self.parentHero = self:GetParent()
|
||||
end
|
||||
function modifier_bristleback_warpath_custom.prototype.OnTakeDamage(self, event)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if event.unit ~= self.parentHero then
|
||||
return
|
||||
end
|
||||
if event.damage <= 0 then
|
||||
return
|
||||
end
|
||||
if self.parentHero:PassivesDisabled() then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
if not ability or ability:IsNull() then
|
||||
return
|
||||
end
|
||||
local stacksMod = self.parentHero:FindModifierByName(____exports.modifier_bristleback_warpath_stacks.name)
|
||||
local maxStacks = getWarpathMaxStacks(nil, ability, self.parentHero)
|
||||
local duration = getWarpathStackDuration(nil, ability, self.parentHero)
|
||||
if not stacksMod then
|
||||
self.parentHero:AddNewModifier(self.parentHero, ability, ____exports.modifier_bristleback_warpath_stacks.name, {duration = duration})
|
||||
return
|
||||
end
|
||||
local nextStacks = math.min(
|
||||
maxStacks,
|
||||
stacksMod:GetStackCount() + 1
|
||||
)
|
||||
stacksMod:SetStackCount(nextStacks)
|
||||
stacksMod:SetDuration(duration, true)
|
||||
stacksMod:ForceRefresh()
|
||||
end
|
||||
modifier_bristleback_warpath_custom = __TS__Decorate(
|
||||
modifier_bristleback_warpath_custom,
|
||||
modifier_bristleback_warpath_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_bristleback_warpath_custom"}
|
||||
)
|
||||
____exports.modifier_bristleback_warpath_custom = modifier_bristleback_warpath_custom
|
||||
____exports.modifier_bristleback_warpath_stacks = __TS__Class()
|
||||
local modifier_bristleback_warpath_stacks = ____exports.modifier_bristleback_warpath_stacks
|
||||
modifier_bristleback_warpath_stacks.name = "modifier_bristleback_warpath_stacks"
|
||||
modifier_bristleback_warpath_stacks.____file_path = "scripts/vscripts/abilities/heroes/bristleback/bristleback_warpath_custom.lua"
|
||||
__TS__ClassExtends(modifier_bristleback_warpath_stacks, BaseModifier)
|
||||
function modifier_bristleback_warpath_stacks.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bristleback_warpath_stacks.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bristleback_warpath_stacks.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_bristleback_warpath_stacks.prototype.GetTexture(self)
|
||||
return "bristleback_warpath"
|
||||
end
|
||||
function modifier_bristleback_warpath_stacks.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self:SetStackCount(1)
|
||||
end
|
||||
function modifier_bristleback_warpath_stacks.prototype.OnRefresh(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self:SetStackCount(math.max(
|
||||
1,
|
||||
self:GetStackCount()
|
||||
))
|
||||
end
|
||||
function modifier_bristleback_warpath_stacks.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_PREATTACK_BONUS_DAMAGE, MODIFIER_PROPERTY_MOVESPEED_BONUS_CONSTANT}
|
||||
end
|
||||
function modifier_bristleback_warpath_stacks.prototype.GetModifierPreAttack_BonusDamage(self)
|
||||
if self:GetParent():PassivesDisabled() then
|
||||
return 0
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return 0
|
||||
end
|
||||
return self:GetStackCount() * getWarpathDamagePerStack(
|
||||
nil,
|
||||
ability,
|
||||
self:GetParent()
|
||||
)
|
||||
end
|
||||
function modifier_bristleback_warpath_stacks.prototype.GetModifierMoveSpeedBonus_Constant(self)
|
||||
if self:GetParent():PassivesDisabled() then
|
||||
return 0
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return 0
|
||||
end
|
||||
return self:GetStackCount() * getWarpathMoveSpeedPerStack(
|
||||
nil,
|
||||
ability,
|
||||
self:GetParent()
|
||||
)
|
||||
end
|
||||
modifier_bristleback_warpath_stacks = __TS__Decorate(
|
||||
modifier_bristleback_warpath_stacks,
|
||||
modifier_bristleback_warpath_stacks,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_bristleback_warpath_stacks"}
|
||||
)
|
||||
____exports.modifier_bristleback_warpath_stacks = modifier_bristleback_warpath_stacks
|
||||
return ____exports
|
||||
+687
@@ -0,0 +1,687 @@
|
||||
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 Set = ____lualib.Set
|
||||
local __TS__New = ____lualib.__TS__New
|
||||
local __TS__ArraySplice = ____lualib.__TS__ArraySplice
|
||||
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 ____modifier_general_froze = require("abilities.modifiers.modifier_general_froze")
|
||||
local modifier_general_froze = ____modifier_general_froze.modifier_general_froze
|
||||
____exports.ability_crystal_maiden_brilliance_aura_custom = __TS__Class()
|
||||
local ability_crystal_maiden_brilliance_aura_custom = ____exports.ability_crystal_maiden_brilliance_aura_custom
|
||||
ability_crystal_maiden_brilliance_aura_custom.name = "ability_crystal_maiden_brilliance_aura_custom"
|
||||
ability_crystal_maiden_brilliance_aura_custom.____file_path = "scripts/vscripts/abilities/heroes/crystal_maiden/ability_crystal_maiden_brilliance_aura_custom.lua"
|
||||
__TS__ClassExtends(ability_crystal_maiden_brilliance_aura_custom, BaseAbility)
|
||||
function ability_crystal_maiden_brilliance_aura_custom.prototype.GetIntrinsicModifierName(self)
|
||||
return "modifier_crystal_maiden_brilliance_aura_custom"
|
||||
end
|
||||
function ability_crystal_maiden_brilliance_aura_custom.prototype.GetAOERadius(self)
|
||||
return self:GetSpecialValueFor("active_radius")
|
||||
end
|
||||
function ability_crystal_maiden_brilliance_aura_custom.prototype.GetManaCost(self, level)
|
||||
local ____opt_0 = self:GetCaster()
|
||||
return (____opt_0 and ____opt_0:GetMana()) * (0.01 * self:GetSpecialValueFor("mana_cost_pct"))
|
||||
end
|
||||
function ability_crystal_maiden_brilliance_aura_custom.prototype.GetCastRange(self, location, target)
|
||||
if self:IsAltCastAbility() then
|
||||
return self:GetSpecialValueFor("teleport_range")
|
||||
end
|
||||
return 0
|
||||
end
|
||||
function ability_crystal_maiden_brilliance_aura_custom.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local point = self:GetCursorPosition()
|
||||
local isAltCast = self:IsAltCastAbility()
|
||||
if isAltCast then
|
||||
local teleportRange = self:GetSpecialValueFor("teleport_range")
|
||||
local casterPos = caster:GetAbsOrigin()
|
||||
local dx = point.x - casterPos.x
|
||||
local dy = point.y - casterPos.y
|
||||
local distance = math.sqrt(dx * dx + dy * dy)
|
||||
local targetPos
|
||||
if distance > teleportRange then
|
||||
local normalizedX = dx / distance
|
||||
local normalizedY = dy / distance
|
||||
targetPos = Vector(casterPos.x + normalizedX * teleportRange, casterPos.y + normalizedY * teleportRange, casterPos.z)
|
||||
else
|
||||
targetPos = point
|
||||
end
|
||||
caster:Stop()
|
||||
caster:StartGesture(ACT_DOTA_CAST_ABILITY_5)
|
||||
FindClearSpaceForUnit(caster, targetPos, false)
|
||||
local activeModifier = caster:FindModifierByName("modifier_crystal_maiden_brilliance_active")
|
||||
if activeModifier then
|
||||
activeModifier:SetDuration(
|
||||
self:GetSpecialValueFor("duration"),
|
||||
true
|
||||
)
|
||||
else
|
||||
caster:AddNewModifier(
|
||||
caster,
|
||||
self,
|
||||
"modifier_crystal_maiden_brilliance_active",
|
||||
{duration = self:GetSpecialValueFor("duration")}
|
||||
)
|
||||
end
|
||||
else
|
||||
local activeModifier = caster:FindModifierByName("modifier_crystal_maiden_brilliance_active")
|
||||
if activeModifier then
|
||||
activeModifier:SetDuration(
|
||||
self:GetSpecialValueFor("duration"),
|
||||
true
|
||||
)
|
||||
else
|
||||
caster:AddNewModifier(
|
||||
caster,
|
||||
self,
|
||||
"modifier_crystal_maiden_brilliance_active",
|
||||
{duration = self:GetSpecialValueFor("duration")}
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
ability_crystal_maiden_brilliance_aura_custom = __TS__Decorate(
|
||||
ability_crystal_maiden_brilliance_aura_custom,
|
||||
ability_crystal_maiden_brilliance_aura_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_crystal_maiden_brilliance_aura_custom"}
|
||||
)
|
||||
____exports.ability_crystal_maiden_brilliance_aura_custom = ability_crystal_maiden_brilliance_aura_custom
|
||||
____exports.modifier_crystal_maiden_brilliance_active = __TS__Class()
|
||||
local modifier_crystal_maiden_brilliance_active = ____exports.modifier_crystal_maiden_brilliance_active
|
||||
modifier_crystal_maiden_brilliance_active.name = "modifier_crystal_maiden_brilliance_active"
|
||||
modifier_crystal_maiden_brilliance_active.____file_path = "scripts/vscripts/abilities/heroes/crystal_maiden/ability_crystal_maiden_brilliance_aura_custom.lua"
|
||||
__TS__ClassExtends(modifier_crystal_maiden_brilliance_active, BaseModifier)
|
||||
function modifier_crystal_maiden_brilliance_active.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_crystal_maiden_brilliance_active.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_EVENT_ON_ABILITY_FULLY_CAST}
|
||||
end
|
||||
function modifier_crystal_maiden_brilliance_active.prototype.OnAbilityFullyCast(self, event)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if event.unit ~= self:GetParent() then
|
||||
return
|
||||
end
|
||||
if event.ability:IsItem() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
local hasCrystalAspect = ability:GetSpecialValueFor("crystal_aspect_enabled") > 0
|
||||
if hasCrystalAspect then
|
||||
local crystalDuration = ability:GetSpecialValueFor("crystal_duration")
|
||||
local crystalModifier = caster:FindModifierByName("modifier_crystal_maiden_brilliance_crystals")
|
||||
if crystalModifier then
|
||||
local currentStacks = crystalModifier:GetStackCount()
|
||||
local maxStacks = 5
|
||||
if currentStacks < maxStacks then
|
||||
crystalModifier:IncrementStackCount()
|
||||
crystalModifier:SyncCrystalsWithStacks()
|
||||
end
|
||||
crystalModifier:SetDuration(crystalDuration, true)
|
||||
else
|
||||
crystalModifier = caster:AddNewModifier(caster, ability, "modifier_crystal_maiden_brilliance_crystals", {duration = crystalDuration})
|
||||
if crystalModifier then
|
||||
crystalModifier:IncrementStackCount()
|
||||
crystalModifier:SyncCrystalsWithStacks()
|
||||
end
|
||||
end
|
||||
end
|
||||
local radius = ability:GetSpecialValueFor("active_radius")
|
||||
local damage = ability:GetSpecialValueFor("damage")
|
||||
local frost_stacks = ability:GetSpecialValueFor("frost_stacks_per_level")
|
||||
local intellect_per_damage = ability:GetSpecialValueFor("intellect_per_damage")
|
||||
EmitSoundOn("Hero_Crystal.CrystalNova", caster)
|
||||
local particleEffect = ParticleManager:CreateParticle("particles/econ/items/queen_of_pain/qop_2022_immortal/queen_2022_scream_of_pain_owner_blue.vpcf", PATTACH_WORLDORIGIN, nil)
|
||||
ParticleManager:SetParticleControl(
|
||||
particleEffect,
|
||||
0,
|
||||
caster:GetAbsOrigin()
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
particleEffect,
|
||||
1,
|
||||
Vector(radius, 1, 1)
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
particleEffect,
|
||||
2,
|
||||
caster:GetAbsOrigin()
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(particleEffect)
|
||||
local enemies = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
caster: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
|
||||
)
|
||||
__TS__ArrayForEach(
|
||||
enemies,
|
||||
function(____, enemy)
|
||||
if not enemy or enemy:IsNull() or not enemy:IsAlive() then
|
||||
return
|
||||
end
|
||||
local ____ApplyDamage_8 = ApplyDamage
|
||||
local ____enemy_7 = enemy
|
||||
local ____opt_2 = self:GetCaster()
|
||||
local ____temp_6 = ____opt_2 and ____opt_2:GetMaxMana()
|
||||
local ____opt_4 = self:GetCaster()
|
||||
____ApplyDamage_8({
|
||||
victim = ____enemy_7,
|
||||
attacker = caster,
|
||||
damage = damage + (____temp_6 - (____opt_4 and ____opt_4:GetMana())),
|
||||
damage_type = DAMAGE_TYPE_MAGICAL,
|
||||
ability = ability
|
||||
})
|
||||
local frostModifier = enemy:AddNewModifier(caster, ability, modifier_general_froze.name, {})
|
||||
if frostModifier then
|
||||
frostModifier:SetDuration(
|
||||
ability:GetSpecialValueFor("slow_duration"),
|
||||
true
|
||||
)
|
||||
local stacksToAdd = frost_stacks + getLuck(nil, caster) * 0.1
|
||||
do
|
||||
local i = 0
|
||||
while i < stacksToAdd do
|
||||
frostModifier:IncrementStackCount()
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
modifier_crystal_maiden_brilliance_active = __TS__Decorate(
|
||||
modifier_crystal_maiden_brilliance_active,
|
||||
modifier_crystal_maiden_brilliance_active,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_crystal_maiden_brilliance_active"}
|
||||
)
|
||||
____exports.modifier_crystal_maiden_brilliance_active = modifier_crystal_maiden_brilliance_active
|
||||
____exports.modifier_crystal_maiden_brilliance_crystals = __TS__Class()
|
||||
local modifier_crystal_maiden_brilliance_crystals = ____exports.modifier_crystal_maiden_brilliance_crystals
|
||||
modifier_crystal_maiden_brilliance_crystals.name = "modifier_crystal_maiden_brilliance_crystals"
|
||||
modifier_crystal_maiden_brilliance_crystals.____file_path = "scripts/vscripts/abilities/heroes/crystal_maiden/ability_crystal_maiden_brilliance_aura_custom.lua"
|
||||
__TS__ClassExtends(modifier_crystal_maiden_brilliance_crystals, BaseModifier)
|
||||
function modifier_crystal_maiden_brilliance_crystals.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.crystals = {}
|
||||
self.maxCrystals = self:GetAbility():GetSpecialValueFor("max_crystals")
|
||||
self.rotationRadius = 150
|
||||
self.rotationSpeed = 12000
|
||||
end
|
||||
function modifier_crystal_maiden_brilliance_crystals.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_crystal_maiden_brilliance_crystals.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_crystal_maiden_brilliance_crystals.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self:StartRotation()
|
||||
local initialStacks = self:GetStackCount()
|
||||
if initialStacks > 0 then
|
||||
do
|
||||
local i = 0
|
||||
while i < initialStacks do
|
||||
self:AddCrystal()
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
function modifier_crystal_maiden_brilliance_crystals.prototype.SyncCrystalsWithStacks(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local currentStacks = self:GetStackCount()
|
||||
while #self.crystals < currentStacks and #self.crystals < self.maxCrystals do
|
||||
self:AddCrystal()
|
||||
end
|
||||
while #self.crystals > currentStacks do
|
||||
self:RemoveCrystal(#self.crystals - 1)
|
||||
end
|
||||
self:RecalculateCrystalAngles()
|
||||
end
|
||||
function modifier_crystal_maiden_brilliance_crystals.prototype.OnRefresh(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self:SyncCrystalsWithStacks()
|
||||
end
|
||||
function modifier_crystal_maiden_brilliance_crystals.prototype.OnDestroy(self)
|
||||
if IsServer() then
|
||||
self:StopRotation()
|
||||
__TS__ArrayForEach(
|
||||
self.crystals,
|
||||
function(____, crystal)
|
||||
if crystal.particle ~= nil then
|
||||
ParticleManager:DestroyParticle(crystal.particle, false)
|
||||
ParticleManager:ReleaseParticleIndex(crystal.particle)
|
||||
end
|
||||
if crystal.radiusParticle ~= nil then
|
||||
ParticleManager:DestroyParticle(crystal.radiusParticle, false)
|
||||
ParticleManager:ReleaseParticleIndex(crystal.radiusParticle)
|
||||
end
|
||||
end
|
||||
)
|
||||
self.crystals = {}
|
||||
if self.lastDamageTime then
|
||||
self.lastDamageTime:clear()
|
||||
end
|
||||
end
|
||||
end
|
||||
function modifier_crystal_maiden_brilliance_crystals.prototype.RecalculateCrystalAngles(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
if not parent or parent:IsNull() then
|
||||
return
|
||||
end
|
||||
local parentPos = parent:GetAbsOrigin()
|
||||
local totalCrystals = #self.crystals
|
||||
if totalCrystals == 0 then
|
||||
return
|
||||
end
|
||||
local angleStep = 360 / totalCrystals
|
||||
__TS__ArrayForEach(
|
||||
self.crystals,
|
||||
function(____, crystal, index)
|
||||
local newAngle = index * angleStep
|
||||
crystal.angle = newAngle
|
||||
local radians = newAngle * math.pi / 180
|
||||
local newPos = Vector(
|
||||
parentPos.x + math.cos(radians) * self.rotationRadius,
|
||||
parentPos.y + math.sin(radians) * self.rotationRadius,
|
||||
parentPos.z + 50
|
||||
)
|
||||
crystal.position = newPos
|
||||
if crystal.particle ~= nil then
|
||||
ParticleManager:SetParticleControl(crystal.particle, 0, newPos)
|
||||
end
|
||||
if crystal.radiusParticle ~= nil then
|
||||
local radiusPos = Vector(newPos.x, newPos.y, newPos.z - 10)
|
||||
ParticleManager:SetParticleControl(crystal.radiusParticle, 0, radiusPos)
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
function modifier_crystal_maiden_brilliance_crystals.prototype.AddCrystal(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if #self.crystals >= self.maxCrystals then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
if not parent or parent:IsNull() then
|
||||
return
|
||||
end
|
||||
local parentPos = parent:GetAbsOrigin()
|
||||
local totalCrystals = #self.crystals + 1
|
||||
local angleStep = 360 / totalCrystals
|
||||
__TS__ArrayForEach(
|
||||
self.crystals,
|
||||
function(____, crystal, index)
|
||||
local newAngle = index * angleStep
|
||||
crystal.angle = newAngle
|
||||
local radians = newAngle * math.pi / 180
|
||||
local newPos = Vector(
|
||||
parentPos.x + math.cos(radians) * self.rotationRadius,
|
||||
parentPos.y + math.sin(radians) * self.rotationRadius,
|
||||
parentPos.z + 50
|
||||
)
|
||||
crystal.position = newPos
|
||||
if crystal.particle ~= nil then
|
||||
ParticleManager:SetParticleControl(crystal.particle, 0, newPos)
|
||||
end
|
||||
end
|
||||
)
|
||||
local newCrystalIndex = #self.crystals
|
||||
local startAngle = newCrystalIndex * angleStep
|
||||
local radians = startAngle * math.pi / 180
|
||||
local initialPos = Vector(
|
||||
parentPos.x + math.cos(radians) * self.rotationRadius,
|
||||
parentPos.y + math.sin(radians) * self.rotationRadius,
|
||||
parentPos.z + 50
|
||||
)
|
||||
local particle = ParticleManager:CreateParticle("particles/crystal_maiden_aspect_3.vpcf", PATTACH_CUSTOMORIGIN, nil)
|
||||
if not particle then
|
||||
return
|
||||
end
|
||||
ParticleManager:SetParticleControl(particle, 0, initialPos)
|
||||
ParticleManager:SetParticleControl(
|
||||
particle,
|
||||
3,
|
||||
Vector(100, 0, 0)
|
||||
)
|
||||
self:AddParticle(
|
||||
particle,
|
||||
false,
|
||||
false,
|
||||
-1,
|
||||
false,
|
||||
false
|
||||
)
|
||||
local ability = self:GetAbility()
|
||||
local damageRadius = ability and (ability:GetSpecialValueFor("crystal_explosion_radius") or 100) or 100
|
||||
local radiusParticle = ParticleManager:CreateParticle("particles/units/heroes/hero_crystalmaiden/maiden_crystal_nova.vpcf", PATTACH_CUSTOMORIGIN, nil)
|
||||
if radiusParticle ~= nil then
|
||||
local radiusPos = Vector(initialPos.x, initialPos.y, initialPos.z - 10)
|
||||
ParticleManager:SetParticleControl(radiusParticle, 0, radiusPos)
|
||||
ParticleManager:SetParticleControl(
|
||||
radiusParticle,
|
||||
1,
|
||||
Vector(damageRadius, 1, damageRadius)
|
||||
)
|
||||
self:AddParticle(
|
||||
radiusParticle,
|
||||
false,
|
||||
false,
|
||||
-1,
|
||||
false,
|
||||
false
|
||||
)
|
||||
end
|
||||
local ____self_crystals_9 = self.crystals
|
||||
____self_crystals_9[#____self_crystals_9 + 1] = {
|
||||
particle = particle,
|
||||
radiusParticle = radiusParticle,
|
||||
angle = startAngle,
|
||||
position = initialPos,
|
||||
hitEnemies = __TS__New(Set)
|
||||
}
|
||||
EmitSoundOn("Hero_Crystal.FreezingField.Explosion", parent)
|
||||
end
|
||||
function modifier_crystal_maiden_brilliance_crystals.prototype.StartRotation(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if self.rotationTimer then
|
||||
return
|
||||
end
|
||||
local modifier = self
|
||||
local parent = self:GetParent()
|
||||
local updateRotation
|
||||
updateRotation = function()
|
||||
if not modifier or not parent or parent:IsNull() then
|
||||
modifier.rotationTimer = nil
|
||||
return
|
||||
end
|
||||
if not modifier.crystals or #modifier.crystals == 0 then
|
||||
modifier.rotationTimer = nil
|
||||
return
|
||||
end
|
||||
local parentPos = parent:GetAbsOrigin()
|
||||
local deltaTime = 0.00033
|
||||
local angleDelta = modifier.rotationSpeed * deltaTime
|
||||
local ability = modifier:GetAbility()
|
||||
local caster = modifier:GetCaster()
|
||||
if not ability or not caster then
|
||||
modifier.rotationTimer = nil
|
||||
return
|
||||
end
|
||||
local damageRadius = ability:GetSpecialValueFor("crystal_explosion_radius")
|
||||
local crystalDamage = ability:GetSpecialValueFor("crystal_damage")
|
||||
local crystalFrostStacks = ability:GetSpecialValueFor("crystal_frost_stacks")
|
||||
local touchRadius = 90
|
||||
__TS__ArrayForEach(
|
||||
modifier.crystals,
|
||||
function(____, crystal, index)
|
||||
crystal.angle = crystal.angle + angleDelta
|
||||
if crystal.angle >= 360 then
|
||||
crystal.angle = crystal.angle - 360
|
||||
end
|
||||
local radians = crystal.angle * math.pi / 180
|
||||
local newPos = Vector(
|
||||
parentPos.x + math.cos(radians) * modifier.rotationRadius,
|
||||
parentPos.y + math.sin(radians) * modifier.rotationRadius,
|
||||
parentPos.z + 50
|
||||
)
|
||||
crystal.position = newPos
|
||||
if crystal.particle ~= nil then
|
||||
ParticleManager:SetParticleControl(crystal.particle, 0, newPos)
|
||||
else
|
||||
return
|
||||
end
|
||||
if crystal.radiusParticle ~= nil then
|
||||
local radiusPos = Vector(newPos.x, newPos.y, newPos.z - 10)
|
||||
ParticleManager:SetParticleControl(crystal.radiusParticle, 0, radiusPos)
|
||||
end
|
||||
local enemiesInRadius = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
newPos,
|
||||
nil,
|
||||
touchRadius,
|
||||
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 currentEnemiesInRadius = __TS__New(Set)
|
||||
__TS__ArrayForEach(
|
||||
enemiesInRadius,
|
||||
function(____, enemy)
|
||||
if not enemy or enemy:IsNull() or not enemy:IsAlive() then
|
||||
return
|
||||
end
|
||||
local enemyPos = enemy:GetAbsOrigin()
|
||||
local distance = (newPos - enemyPos):Length2D()
|
||||
if distance > touchRadius then
|
||||
return
|
||||
end
|
||||
local enemyIndex = enemy:GetEntityIndex()
|
||||
currentEnemiesInRadius:add(enemyIndex)
|
||||
if not crystal.hitEnemies:has(enemyIndex) then
|
||||
local ____ApplyDamage_17 = ApplyDamage
|
||||
local ____enemy_16 = enemy
|
||||
local ____temp_15 = modifier:GetAbility():GetSpecialValueFor("pct_25_mana_damage") * 0.01
|
||||
local ____opt_10 = modifier:GetParent()
|
||||
local ____temp_14 = ____opt_10 and ____opt_10:GetMaxMana()
|
||||
local ____opt_12 = modifier:GetParent()
|
||||
____ApplyDamage_17({
|
||||
victim = ____enemy_16,
|
||||
attacker = caster,
|
||||
damage = crystalDamage + ____temp_15 * (____temp_14 - (____opt_12 and ____opt_12:GetMana())),
|
||||
damage_type = DAMAGE_TYPE_MAGICAL,
|
||||
ability = ability
|
||||
})
|
||||
local frostModifier = enemy:FindModifierByName(modifier_general_froze.name)
|
||||
if not frostModifier then
|
||||
frostModifier = enemy:AddNewModifier(caster, ability, modifier_general_froze.name, {})
|
||||
end
|
||||
if frostModifier then
|
||||
frostModifier:SetDuration(1, true)
|
||||
do
|
||||
local i = 0
|
||||
while i < crystalFrostStacks do
|
||||
frostModifier:IncrementStackCount()
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
crystal.hitEnemies:add(enemyIndex)
|
||||
end
|
||||
end
|
||||
)
|
||||
crystal.hitEnemies:forEach(function(____, enemyIndex)
|
||||
if not currentEnemiesInRadius:has(enemyIndex) then
|
||||
crystal.hitEnemies:delete(enemyIndex)
|
||||
end
|
||||
end)
|
||||
end
|
||||
)
|
||||
Timers:CreateTimer(deltaTime, updateRotation)
|
||||
end
|
||||
modifier.rotationTimer = Timers:CreateTimer(0.00033, updateRotation)
|
||||
end
|
||||
function modifier_crystal_maiden_brilliance_crystals.prototype.StopRotation(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if self.rotationTimer ~= nil and self.rotationTimer ~= nil then
|
||||
do
|
||||
pcall(function()
|
||||
Timers:RemoveTimer(self.rotationTimer)
|
||||
end)
|
||||
end
|
||||
self.rotationTimer = nil
|
||||
end
|
||||
end
|
||||
function modifier_crystal_maiden_brilliance_crystals.prototype.RemoveCrystal(self, index)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if index < 0 or index >= #self.crystals then
|
||||
return
|
||||
end
|
||||
local crystal = self.crystals[index + 1]
|
||||
if crystal.particle ~= nil then
|
||||
ParticleManager:DestroyParticle(crystal.particle, false)
|
||||
ParticleManager:ReleaseParticleIndex(crystal.particle)
|
||||
end
|
||||
if crystal.radiusParticle ~= nil then
|
||||
ParticleManager:DestroyParticle(crystal.radiusParticle, false)
|
||||
ParticleManager:ReleaseParticleIndex(crystal.radiusParticle)
|
||||
end
|
||||
__TS__ArraySplice(self.crystals, index, 1)
|
||||
local parent = self:GetParent()
|
||||
if parent and not parent:IsNull() and #self.crystals > 0 then
|
||||
local parentPos = parent:GetAbsOrigin()
|
||||
local totalCrystals = #self.crystals
|
||||
local angleStep = 360 / totalCrystals
|
||||
__TS__ArrayForEach(
|
||||
self.crystals,
|
||||
function(____, crystal, newIndex)
|
||||
local newAngle = newIndex * angleStep
|
||||
crystal.angle = newAngle
|
||||
local radians = newAngle * math.pi / 180
|
||||
local newPos = Vector(
|
||||
parentPos.x + math.cos(radians) * self.rotationRadius,
|
||||
parentPos.y + math.sin(radians) * self.rotationRadius,
|
||||
parentPos.z + 50
|
||||
)
|
||||
crystal.position = newPos
|
||||
if crystal.particle ~= nil then
|
||||
ParticleManager:SetParticleControl(crystal.particle, 0, newPos)
|
||||
end
|
||||
if crystal.radiusParticle ~= nil then
|
||||
local radiusPos = Vector(newPos.x, newPos.y, newPos.z - 10)
|
||||
ParticleManager:SetParticleControl(crystal.radiusParticle, 0, radiusPos)
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
function modifier_crystal_maiden_brilliance_crystals.prototype.GetCrystalCount(self)
|
||||
return #self.crystals
|
||||
end
|
||||
modifier_crystal_maiden_brilliance_crystals = __TS__Decorate(
|
||||
modifier_crystal_maiden_brilliance_crystals,
|
||||
modifier_crystal_maiden_brilliance_crystals,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_crystal_maiden_brilliance_crystals"}
|
||||
)
|
||||
____exports.modifier_crystal_maiden_brilliance_crystals = modifier_crystal_maiden_brilliance_crystals
|
||||
____exports.modifier_crystal_maiden_brilliance_aura_custom = __TS__Class()
|
||||
local modifier_crystal_maiden_brilliance_aura_custom = ____exports.modifier_crystal_maiden_brilliance_aura_custom
|
||||
modifier_crystal_maiden_brilliance_aura_custom.name = "modifier_crystal_maiden_brilliance_aura_custom"
|
||||
modifier_crystal_maiden_brilliance_aura_custom.____file_path = "scripts/vscripts/abilities/heroes/crystal_maiden/ability_crystal_maiden_brilliance_aura_custom.lua"
|
||||
__TS__ClassExtends(modifier_crystal_maiden_brilliance_aura_custom, BaseModifier)
|
||||
function modifier_crystal_maiden_brilliance_aura_custom.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_crystal_maiden_brilliance_aura_custom.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_crystal_maiden_brilliance_aura_custom.prototype.IsAura(self)
|
||||
return true
|
||||
end
|
||||
function modifier_crystal_maiden_brilliance_aura_custom.prototype.GetAuraRadius(self)
|
||||
return self:GetAbility():GetSpecialValueFor("aura_radius")
|
||||
end
|
||||
function modifier_crystal_maiden_brilliance_aura_custom.prototype.GetAuraSearchTeam(self)
|
||||
return DOTA_UNIT_TARGET_TEAM_FRIENDLY
|
||||
end
|
||||
function modifier_crystal_maiden_brilliance_aura_custom.prototype.GetAuraSearchType(self)
|
||||
return bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC)
|
||||
end
|
||||
function modifier_crystal_maiden_brilliance_aura_custom.prototype.GetAuraSearchFlags(self)
|
||||
return DOTA_UNIT_TARGET_FLAG_NONE
|
||||
end
|
||||
function modifier_crystal_maiden_brilliance_aura_custom.prototype.GetModifierAura(self)
|
||||
return "modifier_crystal_maiden_brilliance_aura_buff"
|
||||
end
|
||||
function modifier_crystal_maiden_brilliance_aura_custom.prototype.GetAuraEntityReject(self, _hTarget)
|
||||
return self:GetParent():PassivesDisabled()
|
||||
end
|
||||
modifier_crystal_maiden_brilliance_aura_custom = __TS__Decorate(
|
||||
modifier_crystal_maiden_brilliance_aura_custom,
|
||||
modifier_crystal_maiden_brilliance_aura_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_crystal_maiden_brilliance_aura_custom"}
|
||||
)
|
||||
____exports.modifier_crystal_maiden_brilliance_aura_custom = modifier_crystal_maiden_brilliance_aura_custom
|
||||
____exports.modifier_crystal_maiden_brilliance_aura_buff = __TS__Class()
|
||||
local modifier_crystal_maiden_brilliance_aura_buff = ____exports.modifier_crystal_maiden_brilliance_aura_buff
|
||||
modifier_crystal_maiden_brilliance_aura_buff.name = "modifier_crystal_maiden_brilliance_aura_buff"
|
||||
modifier_crystal_maiden_brilliance_aura_buff.____file_path = "scripts/vscripts/abilities/heroes/crystal_maiden/ability_crystal_maiden_brilliance_aura_custom.lua"
|
||||
__TS__ClassExtends(modifier_crystal_maiden_brilliance_aura_buff, BaseModifier)
|
||||
function modifier_crystal_maiden_brilliance_aura_buff.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_crystal_maiden_brilliance_aura_buff.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_crystal_maiden_brilliance_aura_buff.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_crystal_maiden_brilliance_aura_buff.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_MANA_REGEN_TOTAL_PERCENTAGE}
|
||||
end
|
||||
function modifier_crystal_maiden_brilliance_aura_buff.prototype.GetModifierTotalPercentageManaRegen(self)
|
||||
local caster = self:GetCaster()
|
||||
if caster and caster:PassivesDisabled() then
|
||||
return 0
|
||||
end
|
||||
return self:GetAbility():GetSpecialValueFor("mana_regen_pct")
|
||||
end
|
||||
function modifier_crystal_maiden_brilliance_aura_buff.prototype.GetEffectName(self)
|
||||
return "particles/units/heroes/hero_crystalmaiden/maiden_brilliance_aura.vpcf"
|
||||
end
|
||||
function modifier_crystal_maiden_brilliance_aura_buff.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_ABSORIGIN_FOLLOW
|
||||
end
|
||||
modifier_crystal_maiden_brilliance_aura_buff = __TS__Decorate(
|
||||
modifier_crystal_maiden_brilliance_aura_buff,
|
||||
modifier_crystal_maiden_brilliance_aura_buff,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_crystal_maiden_brilliance_aura_buff"}
|
||||
)
|
||||
____exports.modifier_crystal_maiden_brilliance_aura_buff = modifier_crystal_maiden_brilliance_aura_buff
|
||||
return ____exports
|
||||
+211
@@ -0,0 +1,211 @@
|
||||
local ____lualib = require("lualib_bundle")
|
||||
local __TS__Class = ____lualib.__TS__Class
|
||||
local __TS__ClassExtends = ____lualib.__TS__ClassExtends
|
||||
local __TS__ArrayForEach = ____lualib.__TS__ArrayForEach
|
||||
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 ____modifier_general_froze = require("abilities.modifiers.modifier_general_froze")
|
||||
local modifier_general_froze = ____modifier_general_froze.modifier_general_froze
|
||||
local ____heal_tracker = require("utils.heal_tracker")
|
||||
local HealWithBattlePass = ____heal_tracker.HealWithBattlePass
|
||||
____exports.ability_crystal_maiden_crystal_nova_custom = __TS__Class()
|
||||
local ability_crystal_maiden_crystal_nova_custom = ____exports.ability_crystal_maiden_crystal_nova_custom
|
||||
ability_crystal_maiden_crystal_nova_custom.name = "ability_crystal_maiden_crystal_nova_custom"
|
||||
ability_crystal_maiden_crystal_nova_custom.____file_path = "scripts/vscripts/abilities/heroes/crystal_maiden/ability_crystal_maiden_crystal_nova_custom.lua"
|
||||
__TS__ClassExtends(ability_crystal_maiden_crystal_nova_custom, BaseAbility)
|
||||
function ability_crystal_maiden_crystal_nova_custom.prototype.GetAOERadius(self)
|
||||
return self:GetSpecialValueFor("radius")
|
||||
end
|
||||
function ability_crystal_maiden_crystal_nova_custom.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local point = self:GetCursorPosition()
|
||||
local radius = self:GetSpecialValueFor("radius")
|
||||
local damage = self:GetSpecialValueFor("damage")
|
||||
local slow_duration = self:GetSpecialValueFor("slow_duration")
|
||||
local frost_stacks = self:GetSpecialValueFor("frost_stacks_per_level")
|
||||
local intellect_per_damage = self:GetSpecialValueFor("intellect_per_damage")
|
||||
EmitSoundOnLocationWithCaster(point, "Hero_Crystal.CrystalNova", caster)
|
||||
local particleEffect = ParticleManager:CreateParticle("particles/units/heroes/hero_crystalmaiden/maiden_crystal_nova.vpcf", PATTACH_WORLDORIGIN, nil)
|
||||
ParticleManager:SetParticleControl(particleEffect, 0, point)
|
||||
ParticleManager:SetParticleControl(
|
||||
particleEffect,
|
||||
1,
|
||||
Vector(radius, 1, radius)
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
particleEffect,
|
||||
2,
|
||||
self:GetCursorPosition()
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(particleEffect)
|
||||
local units = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
point,
|
||||
nil,
|
||||
radius,
|
||||
DOTA_UNIT_TARGET_TEAM_BOTH,
|
||||
bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC),
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
local healAmount = self:GetSpecialValueFor("heal") or damage
|
||||
__TS__ArrayForEach(
|
||||
units,
|
||||
function(____, unit)
|
||||
if not unit or unit:IsNull() or not unit:IsAlive() then
|
||||
return
|
||||
end
|
||||
if unit == caster then
|
||||
return
|
||||
end
|
||||
local isAlly = unit:GetTeamNumber() == caster:GetTeamNumber()
|
||||
if isAlly then
|
||||
local healValue = healAmount + self:GetCaster():GetIntellect(true) * intellect_per_damage
|
||||
local maxShieldPctValue = self:GetSpecialValueFor("max_shield_pct")
|
||||
local hasShieldFromOverheal = maxShieldPctValue > 0
|
||||
if hasShieldFromOverheal then
|
||||
local healthBefore = unit:GetHealth()
|
||||
local maxHealth = unit:GetMaxHealth()
|
||||
local potentialHealthAfter = healthBefore + healValue
|
||||
local potentialOverheal = potentialHealthAfter - maxHealth
|
||||
HealWithBattlePass(
|
||||
nil,
|
||||
unit,
|
||||
healValue,
|
||||
self,
|
||||
caster
|
||||
)
|
||||
local healthAfter = unit:GetHealth()
|
||||
local actualOverheal = healthAfter - maxHealth
|
||||
local overheal = actualOverheal > 0 and actualOverheal or (potentialOverheal > 0 and potentialOverheal or 0)
|
||||
if overheal > 0 then
|
||||
local shieldModifier = unit:FindModifierByName("modifier_crystal_maiden_crystal_nova_shield")
|
||||
local maxShieldPct = self:GetSpecialValueFor("max_shield_pct")
|
||||
local maxShield = unit:GetMaxHealth() * (maxShieldPct / 100)
|
||||
local shieldDuration = self:GetSpecialValueFor("shield_duration")
|
||||
if shieldModifier then
|
||||
local currentShield = shieldModifier:GetStackCount()
|
||||
local newShield = math.min(currentShield + overheal, maxShield)
|
||||
shieldModifier:SetDuration(shieldDuration, true)
|
||||
shieldModifier:SetStackCount(newShield)
|
||||
else
|
||||
local newShieldModifier = unit:AddNewModifier(caster, self, "modifier_crystal_maiden_crystal_nova_shield", {duration = shieldDuration})
|
||||
if newShieldModifier ~= nil and newShieldModifier ~= nil then
|
||||
newShieldModifier:SetStackCount(overheal)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
HealWithBattlePass(
|
||||
nil,
|
||||
unit,
|
||||
healValue,
|
||||
self,
|
||||
caster
|
||||
)
|
||||
end
|
||||
else
|
||||
local damageValue = damage + self:GetCaster():GetIntellect(true) * intellect_per_damage
|
||||
ApplyDamage({
|
||||
victim = unit,
|
||||
attacker = caster,
|
||||
damage = damageValue,
|
||||
damage_type = DAMAGE_TYPE_MAGICAL,
|
||||
ability = self
|
||||
})
|
||||
local modifier = unit:AddNewModifier(caster, self, modifier_general_froze.name, {})
|
||||
if modifier then
|
||||
modifier:SetDuration(slow_duration, true)
|
||||
do
|
||||
local i = 0
|
||||
while i < frost_stacks + getLuck(
|
||||
nil,
|
||||
self:GetCaster()
|
||||
) * 0.1 do
|
||||
modifier:IncrementStackCount()
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
ability_crystal_maiden_crystal_nova_custom = __TS__Decorate(
|
||||
ability_crystal_maiden_crystal_nova_custom,
|
||||
ability_crystal_maiden_crystal_nova_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_crystal_maiden_crystal_nova_custom"}
|
||||
)
|
||||
____exports.ability_crystal_maiden_crystal_nova_custom = ability_crystal_maiden_crystal_nova_custom
|
||||
____exports.modifier_crystal_maiden_crystal_nova_shield = __TS__Class()
|
||||
local modifier_crystal_maiden_crystal_nova_shield = ____exports.modifier_crystal_maiden_crystal_nova_shield
|
||||
modifier_crystal_maiden_crystal_nova_shield.name = "modifier_crystal_maiden_crystal_nova_shield"
|
||||
modifier_crystal_maiden_crystal_nova_shield.____file_path = "scripts/vscripts/abilities/heroes/crystal_maiden/ability_crystal_maiden_crystal_nova_custom.lua"
|
||||
__TS__ClassExtends(modifier_crystal_maiden_crystal_nova_shield, BaseModifier)
|
||||
function modifier_crystal_maiden_crystal_nova_shield.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_crystal_maiden_crystal_nova_shield.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
function modifier_crystal_maiden_crystal_nova_shield.prototype.GetEffectName(self)
|
||||
return "particles/crystal_scepter_shield.vpcf"
|
||||
end
|
||||
function modifier_crystal_maiden_crystal_nova_shield.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_ABSORIGIN_FOLLOW
|
||||
end
|
||||
function modifier_crystal_maiden_crystal_nova_shield.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
end
|
||||
function modifier_crystal_maiden_crystal_nova_shield.prototype.OnRefresh(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
end
|
||||
function modifier_crystal_maiden_crystal_nova_shield.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_INCOMING_DAMAGE_CONSTANT}
|
||||
end
|
||||
function modifier_crystal_maiden_crystal_nova_shield.prototype.GetModifierIncomingDamageConstant(self, event)
|
||||
if IsClient() then
|
||||
return self:GetStackCount()
|
||||
end
|
||||
if not IsServer() then
|
||||
return 0
|
||||
end
|
||||
if event.inflictor and event.inflictor == self:GetAbility() then
|
||||
return 0
|
||||
end
|
||||
local currentShield = self:GetStackCount()
|
||||
if currentShield > event.damage then
|
||||
self:SetStackCount(currentShield - event.damage)
|
||||
return -event.damage
|
||||
else
|
||||
local absorbedDamage = currentShield
|
||||
self:SetStackCount(0)
|
||||
self:Destroy()
|
||||
return -absorbedDamage
|
||||
end
|
||||
end
|
||||
function modifier_crystal_maiden_crystal_nova_shield.prototype.GetTexture(self)
|
||||
return "crystal_maiden_crystal_nova"
|
||||
end
|
||||
modifier_crystal_maiden_crystal_nova_shield = __TS__Decorate(
|
||||
modifier_crystal_maiden_crystal_nova_shield,
|
||||
modifier_crystal_maiden_crystal_nova_shield,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_crystal_maiden_crystal_nova_shield"}
|
||||
)
|
||||
____exports.modifier_crystal_maiden_crystal_nova_shield = modifier_crystal_maiden_crystal_nova_shield
|
||||
return ____exports
|
||||
+432
@@ -0,0 +1,432 @@
|
||||
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__NumberToFixed = ____lualib.__TS__NumberToFixed
|
||||
local __TS__ArrayForEach = ____lualib.__TS__ArrayForEach
|
||||
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 ____modifier_general_froze = require("abilities.modifiers.modifier_general_froze")
|
||||
local modifier_general_froze = ____modifier_general_froze.modifier_general_froze
|
||||
local ____ability_alt_cast_manager = require("ability_alt_cast_manager")
|
||||
local AbilityAltCastManager = ____ability_alt_cast_manager.AbilityAltCastManager
|
||||
local ____heal_tracker = require("utils.heal_tracker")
|
||||
local HealWithBattlePass = ____heal_tracker.HealWithBattlePass
|
||||
____exports.ability_crystal_maiden_freezing_field_custom = __TS__Class()
|
||||
local ability_crystal_maiden_freezing_field_custom = ____exports.ability_crystal_maiden_freezing_field_custom
|
||||
ability_crystal_maiden_freezing_field_custom.name = "ability_crystal_maiden_freezing_field_custom"
|
||||
ability_crystal_maiden_freezing_field_custom.____file_path = "scripts/vscripts/abilities/heroes/crystal_maiden/ability_crystal_maiden_freezing_field_custom.lua"
|
||||
__TS__ClassExtends(ability_crystal_maiden_freezing_field_custom, BaseAbility)
|
||||
function ability_crystal_maiden_freezing_field_custom.prototype.____constructor(self, ...)
|
||||
BaseAbility.prototype.____constructor(self, ...)
|
||||
self.modifier = nil
|
||||
end
|
||||
function ability_crystal_maiden_freezing_field_custom.prototype.GetBehavior(self)
|
||||
if self:GetCaster():HasScepter() then
|
||||
return bit.bor(
|
||||
bit.bor(DOTA_ABILITY_BEHAVIOR_NO_TARGET, DOTA_ABILITY_BEHAVIOR_CHANNELLED),
|
||||
DOTA_ABILITY_BEHAVIOR_IMMEDIATE
|
||||
)
|
||||
end
|
||||
return bit.bor(DOTA_ABILITY_BEHAVIOR_NO_TARGET, DOTA_ABILITY_BEHAVIOR_CHANNELLED)
|
||||
end
|
||||
function ability_crystal_maiden_freezing_field_custom.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local duration = self:GetChannelTime()
|
||||
print("[FreezingField] OnSpellStart called, duration: " .. tostring(duration))
|
||||
self.modifier = caster:AddNewModifier(caster, self, ____exports.modifier_crystal_maiden_freezing_field_custom.name, {duration = duration})
|
||||
if not self.modifier or self.modifier:IsNull() then
|
||||
print("[FreezingField] ERROR: Failed to apply modifier!")
|
||||
else
|
||||
print("[FreezingField] Modifier applied successfully")
|
||||
end
|
||||
end
|
||||
function ability_crystal_maiden_freezing_field_custom.prototype.OnChannelFinish(self, interrupted)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
if self.modifier and not self.modifier:IsNull() then
|
||||
self.modifier:Destroy()
|
||||
end
|
||||
StopSoundOn("hero_Crystal.freezingField.wind", caster)
|
||||
end
|
||||
ability_crystal_maiden_freezing_field_custom = __TS__Decorate(
|
||||
ability_crystal_maiden_freezing_field_custom,
|
||||
ability_crystal_maiden_freezing_field_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_crystal_maiden_freezing_field_custom"}
|
||||
)
|
||||
____exports.ability_crystal_maiden_freezing_field_custom = ability_crystal_maiden_freezing_field_custom
|
||||
____exports.modifier_crystal_maiden_freezing_field_custom = __TS__Class()
|
||||
local modifier_crystal_maiden_freezing_field_custom = ____exports.modifier_crystal_maiden_freezing_field_custom
|
||||
modifier_crystal_maiden_freezing_field_custom.name = "modifier_crystal_maiden_freezing_field_custom"
|
||||
modifier_crystal_maiden_freezing_field_custom.____file_path = "scripts/vscripts/abilities/heroes/crystal_maiden/ability_crystal_maiden_freezing_field_custom.lua"
|
||||
__TS__ClassExtends(modifier_crystal_maiden_freezing_field_custom, BaseModifier)
|
||||
function modifier_crystal_maiden_freezing_field_custom.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.explosionInterval = 0.1
|
||||
self.radius = 0
|
||||
self.explosionRadius = 0
|
||||
self.explosionDamage = 0
|
||||
self.frostStacksPerExplosion = 0
|
||||
self.slowDuration = 0
|
||||
self.intellectPerDamage = 0
|
||||
self.lastCrystalNovaTime = 0
|
||||
self.crystalNovaChance = 0.5
|
||||
self.crystalNovaCheckInterval = 0.5
|
||||
end
|
||||
function modifier_crystal_maiden_freezing_field_custom.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_crystal_maiden_freezing_field_custom.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_crystal_maiden_freezing_field_custom.prototype.GetAttributes(self)
|
||||
return MODIFIER_ATTRIBUTE_MULTIPLE
|
||||
end
|
||||
function modifier_crystal_maiden_freezing_field_custom.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_crystal_maiden_freezing_field_custom.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
print("[FreezingField] Modifier OnCreated called")
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
print("[FreezingField] ERROR: Ability is null!")
|
||||
self:Destroy()
|
||||
return
|
||||
end
|
||||
self.radius = ability:GetSpecialValueFor("radius")
|
||||
self.explosionRadius = ability:GetSpecialValueFor("explosion_radius")
|
||||
self.explosionDamage = ability:GetSpecialValueFor("explosion_damage")
|
||||
self.frostStacksPerExplosion = ability:GetSpecialValueFor("frost_stacks_per_explosion")
|
||||
self.slowDuration = ability:GetSpecialValueFor("slow_duration")
|
||||
self.explosionInterval = ability:GetSpecialValueFor("explosion_interval")
|
||||
self.intellectPerDamage = ability:GetSpecialValueFor("intellect_per_damage")
|
||||
print((((("[FreezingField] Values loaded - radius: " .. tostring(self.radius)) .. ", interval: ") .. tostring(self.explosionInterval)) .. ", damage: ") .. tostring(self.explosionDamage))
|
||||
local parent = self:GetParent()
|
||||
self.mainParticleId = ParticleManager:CreateParticle("particles/units/heroes/hero_crystalmaiden/maiden_freezing_field_snow.vpcf", PATTACH_ABSORIGIN_FOLLOW, parent)
|
||||
ParticleManager:SetParticleControl(
|
||||
self.mainParticleId,
|
||||
0,
|
||||
parent:GetAbsOrigin()
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
self.mainParticleId,
|
||||
1,
|
||||
Vector(self.radius, 0, 0)
|
||||
)
|
||||
self:AddParticle(
|
||||
self.mainParticleId,
|
||||
false,
|
||||
false,
|
||||
-1,
|
||||
false,
|
||||
false
|
||||
)
|
||||
local caster = self:GetCaster()
|
||||
if caster then
|
||||
EmitSoundOn("hero_Crystal.freezingField.wind", caster)
|
||||
end
|
||||
self.lastCrystalNovaTime = GameRules:GetGameTime()
|
||||
self:StartIntervalThink(self.explosionInterval)
|
||||
end
|
||||
function modifier_crystal_maiden_freezing_field_custom.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local parent = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
if caster and (caster:IsStunned() or caster:IsHexed() or caster:IsSilenced()) then
|
||||
return
|
||||
end
|
||||
if not caster or caster:IsNull() or not parent or parent:IsNull() or not ability then
|
||||
return
|
||||
end
|
||||
local roll = math.random()
|
||||
print((("[FreezingField] Checking for mini Crystal Nova - roll: " .. __TS__NumberToFixed(roll, 3)) .. ", chance: ") .. tostring(self.crystalNovaChance))
|
||||
print("[FreezingField] Casting mini Crystal Nova!")
|
||||
if self:GetCaster():HasScepter() then
|
||||
self:CastMiniCrystalNova(caster, parent)
|
||||
end
|
||||
local casterPos = parent:GetAbsOrigin()
|
||||
local angle = RandomFloat(0, 360)
|
||||
local minDistance = 120
|
||||
local distance = RandomFloat(minDistance, self.radius)
|
||||
local radians = angle * math.pi / 180
|
||||
local explosionPos = Vector(
|
||||
casterPos.x + math.cos(radians) * distance,
|
||||
casterPos.y + math.sin(radians) * distance,
|
||||
casterPos.z
|
||||
)
|
||||
local particleEffect = ParticleManager:CreateParticle("particles/units/heroes/hero_crystalmaiden/maiden_freezing_field_explosion.vpcf", PATTACH_WORLDORIGIN, nil)
|
||||
ParticleManager:SetParticleControl(particleEffect, 0, explosionPos)
|
||||
ParticleManager:SetParticleControl(
|
||||
particleEffect,
|
||||
1,
|
||||
Vector(self.explosionRadius, 0, 0)
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(particleEffect)
|
||||
EmitSoundOnLocationWithCaster(explosionPos, "Hero_Crystal.FreezingField.Explosion", caster)
|
||||
local enemies = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
explosionPos,
|
||||
nil,
|
||||
self.explosionRadius,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC),
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
print(((((("[FreezingField] Explosion at (" .. __TS__NumberToFixed(explosionPos.x, 1)) .. ", ") .. __TS__NumberToFixed(explosionPos.y, 1)) .. ") - found ") .. tostring(#enemies)) .. " enemies")
|
||||
__TS__ArrayForEach(
|
||||
enemies,
|
||||
function(____, enemy)
|
||||
if not enemy or enemy:IsNull() or not enemy:IsAlive() then
|
||||
return
|
||||
end
|
||||
local damageValue = self.explosionDamage + caster:GetIntellect(true) * self.intellectPerDamage
|
||||
ApplyDamage({
|
||||
victim = enemy,
|
||||
attacker = caster,
|
||||
damage = damageValue,
|
||||
damage_type = DAMAGE_TYPE_MAGICAL,
|
||||
ability = ability
|
||||
})
|
||||
print(((("[FreezingField] Explosion hit enemy " .. enemy:GetUnitName()) .. " for ") .. __TS__NumberToFixed(damageValue, 1)) .. " damage")
|
||||
local frostModifier = enemy:FindModifierByName(modifier_general_froze.name)
|
||||
if not frostModifier then
|
||||
frostModifier = enemy:AddNewModifier(caster, ability, modifier_general_froze.name, {})
|
||||
end
|
||||
if frostModifier then
|
||||
frostModifier:SetDuration(self.slowDuration, true)
|
||||
local stacksToAdd = self.frostStacksPerExplosion + getLuck(nil, caster) * 0.1
|
||||
do
|
||||
local i = 0
|
||||
while i < stacksToAdd do
|
||||
frostModifier:IncrementStackCount()
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
print((("[FreezingField] Applied " .. __TS__NumberToFixed(stacksToAdd, 1)) .. " frost stacks to ") .. enemy:GetUnitName())
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
function modifier_crystal_maiden_freezing_field_custom.prototype.CastMiniCrystalNova(self, caster, parent)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
print("[FreezingField] CastMiniCrystalNova - ability is null!")
|
||||
return
|
||||
end
|
||||
local crystalNovaAbility = parent:FindAbilityByName("ability_crystal_maiden_crystal_nova_custom")
|
||||
if not crystalNovaAbility or crystalNovaAbility:GetLevel() == 0 then
|
||||
print("[FreezingField] CastMiniCrystalNova - Crystal Nova not learned or level 0")
|
||||
return
|
||||
end
|
||||
print("[FreezingField] CastMiniCrystalNova - Crystal Nova level: " .. tostring(crystalNovaAbility:GetLevel()))
|
||||
local casterPos = parent:GetAbsOrigin()
|
||||
local angle = RandomFloat(0, 360)
|
||||
local minDistance = 200
|
||||
local maxDistance = self.radius
|
||||
local distance = RandomFloat(minDistance, maxDistance)
|
||||
local radians = angle * math.pi / 180
|
||||
local novaPos = Vector(
|
||||
casterPos.x + math.cos(radians) * distance,
|
||||
casterPos.y + math.sin(radians) * distance,
|
||||
casterPos.z
|
||||
)
|
||||
local novaRadius = crystalNovaAbility:GetSpecialValueFor("radius") * (self:GetAbility():GetSpecialValueFor("mini_nova_pct") * 0.01)
|
||||
local novaDamage = crystalNovaAbility:GetSpecialValueFor("damage") * (self:GetAbility():GetSpecialValueFor("mini_nova_pct") * 0.01)
|
||||
local novaHeal = crystalNovaAbility:GetSpecialValueFor("heal") * (self:GetAbility():GetSpecialValueFor("mini_nova_pct") * 0.01)
|
||||
local novaSlowDuration = crystalNovaAbility:GetSpecialValueFor("slow_duration")
|
||||
local novaFrostStacks = math.floor(crystalNovaAbility:GetSpecialValueFor("frost_stacks_per_level") * (self:GetAbility():GetSpecialValueFor("mini_nova_pct") * 0.01))
|
||||
local novaIntellectPerDamage = crystalNovaAbility:GetSpecialValueFor("intellect_per_damage")
|
||||
EmitSoundOnLocationWithCaster(novaPos, "Hero_Crystal.CrystalNova", caster)
|
||||
local particleEffect = ParticleManager:CreateParticle("particles/units/heroes/hero_crystalmaiden/maiden_crystal_nova.vpcf", PATTACH_WORLDORIGIN, nil)
|
||||
ParticleManager:SetParticleControl(particleEffect, 0, novaPos)
|
||||
ParticleManager:SetParticleControl(
|
||||
particleEffect,
|
||||
1,
|
||||
Vector(novaRadius, 1, novaRadius)
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(particleEffect)
|
||||
local units = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
novaPos,
|
||||
nil,
|
||||
novaRadius,
|
||||
DOTA_UNIT_TARGET_TEAM_BOTH,
|
||||
bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC),
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
local frostbiteAbility = parent:FindAbilityByName("ability_crystal_maiden_frostbite_custom")
|
||||
local brillianceAbility = parent:FindAbilityByName("ability_crystal_maiden_brilliance_aura_custom")
|
||||
print(((((((("[FreezingField] CastMiniCrystalNova - Nova at (" .. __TS__NumberToFixed(novaPos.x, 1)) .. ", ") .. __TS__NumberToFixed(novaPos.y, 1)) .. "), radius: ") .. tostring(novaRadius)) .. ", found ") .. tostring(#units)) .. " units")
|
||||
print((("[FreezingField] CastMiniCrystalNova - Frostbite level: " .. tostring(frostbiteAbility and frostbiteAbility:GetLevel() or 0)) .. ", Brilliance level: ") .. tostring(brillianceAbility and brillianceAbility:GetLevel() or 0))
|
||||
__TS__ArrayForEach(
|
||||
units,
|
||||
function(____, unit)
|
||||
if not unit or unit:IsNull() or not unit:IsAlive() then
|
||||
return
|
||||
end
|
||||
if unit == parent then
|
||||
return
|
||||
end
|
||||
local isAlly = unit:GetTeamNumber() == caster:GetTeamNumber()
|
||||
if isAlly then
|
||||
local healValue = novaHeal + caster:GetIntellect(true) * novaIntellectPerDamage
|
||||
HealWithBattlePass(
|
||||
nil,
|
||||
unit,
|
||||
healValue,
|
||||
ability,
|
||||
caster
|
||||
)
|
||||
print((("[FreezingField] CastMiniCrystalNova - Healed ally " .. unit:GetUnitName()) .. " for ") .. __TS__NumberToFixed(healValue, 1))
|
||||
if frostbiteAbility and frostbiteAbility:GetLevel() > 0 then
|
||||
local playerId = caster:GetPlayerOwnerID()
|
||||
if playerId == -1 or playerId == nil then
|
||||
playerId = caster:GetPlayerID()
|
||||
end
|
||||
local isFrostbiteAltCast = false
|
||||
if playerId ~= -1 and playerId ~= nil then
|
||||
local altCastManager = AbilityAltCastManager:getInstance()
|
||||
isFrostbiteAltCast = altCastManager:getAltCastState(playerId, "ability_crystal_maiden_frostbite_custom")
|
||||
end
|
||||
if not isFrostbiteAltCast then
|
||||
local frostbiteDuration = frostbiteAbility:GetSpecialValueFor("duration") * 0.5
|
||||
unit:AddNewModifier(caster, ability, "modifier_crystal_maiden_frostbite_ally", {duration = frostbiteDuration})
|
||||
print(((("[FreezingField] CastMiniCrystalNova - Applied Frostbite (ally) to " .. unit:GetUnitName()) .. " for ") .. tostring(frostbiteDuration)) .. "s")
|
||||
else
|
||||
print(("[FreezingField] CastMiniCrystalNova - Skipped Frostbite (ally) for " .. unit:GetUnitName()) .. " because alt cast is enabled")
|
||||
end
|
||||
end
|
||||
if brillianceAbility and brillianceAbility:GetLevel() > 0 then
|
||||
unit:AddNewModifier(caster, ability, "modifier_crystal_maiden_brilliance_aura_buff", {duration = 3})
|
||||
print("[FreezingField] CastMiniCrystalNova - Applied Brilliance Aura buff to " .. unit:GetUnitName())
|
||||
end
|
||||
else
|
||||
local damageValue = novaDamage + caster:GetIntellect(true) * novaIntellectPerDamage
|
||||
ApplyDamage({
|
||||
victim = unit,
|
||||
attacker = caster,
|
||||
damage = damageValue,
|
||||
damage_type = DAMAGE_TYPE_MAGICAL,
|
||||
ability = ability
|
||||
})
|
||||
print((("[FreezingField] CastMiniCrystalNova - Damaged enemy " .. unit:GetUnitName()) .. " for ") .. __TS__NumberToFixed(damageValue, 1))
|
||||
local modifier = unit:AddNewModifier(caster, ability, modifier_general_froze.name, {})
|
||||
if modifier then
|
||||
modifier:SetDuration(novaSlowDuration, true)
|
||||
do
|
||||
local i = 0
|
||||
while i < novaFrostStacks do
|
||||
modifier:IncrementStackCount()
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
print((("[FreezingField] CastMiniCrystalNova - Applied " .. tostring(novaFrostStacks)) .. " frost stacks to ") .. unit:GetUnitName())
|
||||
end
|
||||
if frostbiteAbility and frostbiteAbility:GetLevel() > 0 then
|
||||
local frostbiteDuration = frostbiteAbility:GetSpecialValueFor("duration") * 0.5
|
||||
local frostbiteDamage = frostbiteAbility:GetSpecialValueFor("damage") * 0.5
|
||||
unit:AddNewModifier(caster, ability, "modifier_crystal_maiden_frostbite_enemy", {duration = frostbiteDuration, isAltCast = 0})
|
||||
ApplyDamage({
|
||||
victim = unit,
|
||||
attacker = caster,
|
||||
damage = frostbiteDamage,
|
||||
damage_type = DAMAGE_TYPE_MAGICAL,
|
||||
ability = ability
|
||||
})
|
||||
print((((("[FreezingField] CastMiniCrystalNova - Applied Frostbite (enemy) to " .. unit:GetUnitName()) .. " for ") .. tostring(frostbiteDuration)) .. "s, damage: ") .. __TS__NumberToFixed(frostbiteDamage, 1))
|
||||
end
|
||||
if brillianceAbility and brillianceAbility:GetLevel() > 0 then
|
||||
local brillianceRadius = brillianceAbility:GetSpecialValueFor("active_radius") * 0.5
|
||||
local brillianceDamage = brillianceAbility:GetSpecialValueFor("damage") * 0.5
|
||||
local brillianceFrostStacks = math.floor(brillianceAbility:GetSpecialValueFor("frost_stacks_per_level") * 0.5)
|
||||
local nearbyEnemies = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
unit:GetAbsOrigin(),
|
||||
nil,
|
||||
brillianceRadius,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC),
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
print(((("[FreezingField] CastMiniCrystalNova - Brilliance Aura effect from " .. unit:GetUnitName()) .. ", found ") .. tostring(#nearbyEnemies)) .. " nearby enemies")
|
||||
__TS__ArrayForEach(
|
||||
nearbyEnemies,
|
||||
function(____, enemy)
|
||||
if not enemy or enemy:IsNull() or not enemy:IsAlive() or enemy == unit then
|
||||
return
|
||||
end
|
||||
ApplyDamage({
|
||||
victim = enemy,
|
||||
attacker = caster,
|
||||
damage = brillianceDamage,
|
||||
damage_type = DAMAGE_TYPE_MAGICAL,
|
||||
ability = ability
|
||||
})
|
||||
local frostMod = enemy:FindModifierByName(modifier_general_froze.name)
|
||||
if not frostMod then
|
||||
frostMod = enemy:AddNewModifier(caster, ability, modifier_general_froze.name, {})
|
||||
end
|
||||
if frostMod then
|
||||
frostMod:SetDuration(1, true)
|
||||
do
|
||||
local i = 0
|
||||
while i < brillianceFrostStacks do
|
||||
frostMod:IncrementStackCount()
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
function modifier_crystal_maiden_freezing_field_custom.prototype.OnDestroy(self)
|
||||
if IsClient() then
|
||||
return
|
||||
end
|
||||
print("[FreezingField] Modifier OnDestroy called")
|
||||
if self.mainParticleId then
|
||||
ParticleManager:DestroyParticle(self.mainParticleId, false)
|
||||
ParticleManager:ReleaseParticleIndex(self.mainParticleId)
|
||||
end
|
||||
StopSoundOn(
|
||||
"hero_Crystal.freezingField.wind",
|
||||
self:GetParent()
|
||||
)
|
||||
end
|
||||
modifier_crystal_maiden_freezing_field_custom = __TS__Decorate(
|
||||
modifier_crystal_maiden_freezing_field_custom,
|
||||
modifier_crystal_maiden_freezing_field_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_crystal_maiden_freezing_field_custom"}
|
||||
)
|
||||
____exports.modifier_crystal_maiden_freezing_field_custom = modifier_crystal_maiden_freezing_field_custom
|
||||
return ____exports
|
||||
+910
@@ -0,0 +1,910 @@
|
||||
local ____lualib = require("lualib_bundle")
|
||||
local __TS__Class = ____lualib.__TS__Class
|
||||
local __TS__ClassExtends = ____lualib.__TS__ClassExtends
|
||||
local __TS__ArrayForEach = ____lualib.__TS__ArrayForEach
|
||||
local __TS__Decorate = ____lualib.__TS__Decorate
|
||||
local __TS__StringIncludes = ____lualib.__TS__StringIncludes
|
||||
local __TS__ArrayFind = ____lualib.__TS__ArrayFind
|
||||
local __TS__ArraySome = ____lualib.__TS__ArraySome
|
||||
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 ____modifier_general_froze = require("abilities.modifiers.modifier_general_froze")
|
||||
local modifier_general_froze = ____modifier_general_froze.modifier_general_froze
|
||||
local ____modifier_general_knockback = require("abilities.modifiers.modifier_general_knockback")
|
||||
local modifier_general_knockback = ____modifier_general_knockback.modifier_general_knockback
|
||||
local ____heal_tracker = require("utils.heal_tracker")
|
||||
local HealWithBattlePass = ____heal_tracker.HealWithBattlePass
|
||||
local ____incoming_damage_reduction_combine = require("utils.incoming_damage_reduction_combine")
|
||||
local removeIncomingDamageReductionSource = ____incoming_damage_reduction_combine.removeIncomingDamageReductionSource
|
||||
local setIncomingDamageReductionSource = ____incoming_damage_reduction_combine.setIncomingDamageReductionSource
|
||||
local FROSTBITE_ALLY_INCOMING_SOURCE = "modifier_crystal_maiden_frostbite_ally"
|
||||
____exports.ability_crystal_maiden_frostbite_custom = __TS__Class()
|
||||
local ability_crystal_maiden_frostbite_custom = ____exports.ability_crystal_maiden_frostbite_custom
|
||||
ability_crystal_maiden_frostbite_custom.name = "ability_crystal_maiden_frostbite_custom"
|
||||
ability_crystal_maiden_frostbite_custom.____file_path = "scripts/vscripts/abilities/heroes/crystal_maiden/ability_crystal_maiden_frostbite_custom.lua"
|
||||
__TS__ClassExtends(ability_crystal_maiden_frostbite_custom, BaseAbility)
|
||||
function ability_crystal_maiden_frostbite_custom.prototype.GetAOERadius(self)
|
||||
return self:GetSpecialValueFor("radius")
|
||||
end
|
||||
function ability_crystal_maiden_frostbite_custom.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local point = self:GetCursorPosition()
|
||||
local radius = self:GetSpecialValueFor("radius")
|
||||
local duration = self:GetSpecialValueFor("duration")
|
||||
local damage = self:GetSpecialValueFor("damage")
|
||||
local heal_per_second = self:GetSpecialValueFor("heal_per_second")
|
||||
local isAltCast = self:IsAltCastAbility()
|
||||
print(isAltCast)
|
||||
EmitSoundOnLocationWithCaster(point, "Hero_Crystal.Frostbite", caster)
|
||||
local ____isAltCast_0
|
||||
if isAltCast then
|
||||
____isAltCast_0 = DOTA_UNIT_TARGET_TEAM_ENEMY
|
||||
else
|
||||
____isAltCast_0 = DOTA_UNIT_TARGET_TEAM_BOTH
|
||||
end
|
||||
local targetTeam = ____isAltCast_0
|
||||
local units = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
point,
|
||||
nil,
|
||||
radius,
|
||||
targetTeam,
|
||||
bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC),
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
__TS__ArrayForEach(
|
||||
units,
|
||||
function(____, unit)
|
||||
if not unit or unit:IsNull() or not unit:IsAlive() then
|
||||
return
|
||||
end
|
||||
local isAlly = unit:GetTeamNumber() == caster:GetTeamNumber()
|
||||
if isAltCast and isAlly then
|
||||
return
|
||||
end
|
||||
if isAlly then
|
||||
unit:AddNewModifier(caster, self, "modifier_crystal_maiden_frostbite_ally", {duration = duration})
|
||||
else
|
||||
unit:AddNewModifier(caster, self, "modifier_crystal_maiden_frostbite_enemy", {duration = duration, isAltCast = isAltCast and 1 or 0})
|
||||
local initialDamage = isAltCast and damage * 2 or damage
|
||||
ApplyDamage({
|
||||
victim = unit,
|
||||
attacker = caster,
|
||||
damage = initialDamage,
|
||||
damage_type = DAMAGE_TYPE_MAGICAL,
|
||||
ability = self
|
||||
})
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
function ability_crystal_maiden_frostbite_custom.prototype.GetCastRange(self, location, target)
|
||||
return self:GetSpecialValueFor("cast_range")
|
||||
end
|
||||
function ability_crystal_maiden_frostbite_custom.prototype.OnProjectileHit(self, target, location)
|
||||
if not IsServer() then
|
||||
return false
|
||||
end
|
||||
if not target then
|
||||
return false
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
if not caster then
|
||||
return false
|
||||
end
|
||||
local damage = 100
|
||||
ApplyDamage({
|
||||
victim = target,
|
||||
attacker = caster,
|
||||
damage = damage,
|
||||
damage_type = DAMAGE_TYPE_MAGICAL,
|
||||
ability = self
|
||||
})
|
||||
local frostModifier = target:AddNewModifier(caster, self, modifier_general_froze.name, {})
|
||||
if frostModifier then
|
||||
do
|
||||
local i = 0
|
||||
while i < 10 do
|
||||
frostModifier:IncrementStackCount()
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
frostModifier:SetDuration(2, true)
|
||||
end
|
||||
local particle = ParticleManager:CreateParticle("particles/units/heroes/hero_crystalmaiden/crystal_maiden_frostbite.vpcf", PATTACH_ABSORIGIN_FOLLOW, target)
|
||||
ParticleManager:ReleaseParticleIndex(particle)
|
||||
EmitSoundOn("Hero_Crystal.Frostbite", target)
|
||||
return true
|
||||
end
|
||||
ability_crystal_maiden_frostbite_custom = __TS__Decorate(
|
||||
ability_crystal_maiden_frostbite_custom,
|
||||
ability_crystal_maiden_frostbite_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_crystal_maiden_frostbite_custom"}
|
||||
)
|
||||
____exports.ability_crystal_maiden_frostbite_custom = ability_crystal_maiden_frostbite_custom
|
||||
____exports.modifier_crystal_maiden_frostbite_ally = __TS__Class()
|
||||
local modifier_crystal_maiden_frostbite_ally = ____exports.modifier_crystal_maiden_frostbite_ally
|
||||
modifier_crystal_maiden_frostbite_ally.name = "modifier_crystal_maiden_frostbite_ally"
|
||||
modifier_crystal_maiden_frostbite_ally.____file_path = "scripts/vscripts/abilities/heroes/crystal_maiden/ability_crystal_maiden_frostbite_custom.lua"
|
||||
__TS__ClassExtends(modifier_crystal_maiden_frostbite_ally, BaseModifier)
|
||||
function modifier_crystal_maiden_frostbite_ally.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.healPerSecond = 0
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_ally.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_ally.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_ally.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_ally.prototype.GetAttributes(self)
|
||||
return MODIFIER_ATTRIBUTE_MULTIPLE
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_ally.prototype.OnCreated(self)
|
||||
if IsServer() then
|
||||
local ability = self:GetAbility()
|
||||
if ability then
|
||||
self.healPerSecond = ability:GetSpecialValueFor("heal_per_second")
|
||||
end
|
||||
setIncomingDamageReductionSource(
|
||||
nil,
|
||||
self:GetParent(),
|
||||
FROSTBITE_ALLY_INCOMING_SOURCE,
|
||||
function()
|
||||
local frostAbility = self:GetAbility()
|
||||
if not frostAbility then
|
||||
return 0
|
||||
end
|
||||
return math.max(
|
||||
0,
|
||||
frostAbility:GetSpecialValueFor("incoming_damage_pct")
|
||||
)
|
||||
end
|
||||
)
|
||||
self:StartIntervalThink(0.5)
|
||||
self:OnIntervalThink()
|
||||
end
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_ally.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
if not parent or parent:IsNull() or not parent:IsAlive() then
|
||||
return
|
||||
end
|
||||
HealWithBattlePass(
|
||||
nil,
|
||||
parent,
|
||||
self.healPerSecond * 0.5,
|
||||
self:GetAbility(),
|
||||
self:GetCaster()
|
||||
)
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_ally.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_ROOTED] = true, [MODIFIER_STATE_DISARMED] = false}
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_ally.prototype.GetEffectName(self)
|
||||
return "particles/econ/items/crystal_maiden/ti7_immortal_shoulder/cm_ti7_immortal_frostbite.vpcf"
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_ally.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_ABSORIGIN_FOLLOW
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_ally.prototype.OnDestroy(self)
|
||||
if IsClient() then
|
||||
return
|
||||
end
|
||||
removeIncomingDamageReductionSource(
|
||||
nil,
|
||||
self:GetParent(),
|
||||
FROSTBITE_ALLY_INCOMING_SOURCE
|
||||
)
|
||||
StopSoundOn(
|
||||
"Hero_Crystal.Frostbite",
|
||||
self:GetParent()
|
||||
)
|
||||
end
|
||||
modifier_crystal_maiden_frostbite_ally = __TS__Decorate(
|
||||
modifier_crystal_maiden_frostbite_ally,
|
||||
modifier_crystal_maiden_frostbite_ally,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_crystal_maiden_frostbite_ally"}
|
||||
)
|
||||
____exports.modifier_crystal_maiden_frostbite_ally = modifier_crystal_maiden_frostbite_ally
|
||||
____exports.modifier_crystal_maiden_frostbite_enemy = __TS__Class()
|
||||
local modifier_crystal_maiden_frostbite_enemy = ____exports.modifier_crystal_maiden_frostbite_enemy
|
||||
modifier_crystal_maiden_frostbite_enemy.name = "modifier_crystal_maiden_frostbite_enemy"
|
||||
modifier_crystal_maiden_frostbite_enemy.____file_path = "scripts/vscripts/abilities/heroes/crystal_maiden/ability_crystal_maiden_frostbite_custom.lua"
|
||||
__TS__ClassExtends(modifier_crystal_maiden_frostbite_enemy, BaseModifier)
|
||||
function modifier_crystal_maiden_frostbite_enemy.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.damage = 0
|
||||
self.frostStacksPerLevel = 0
|
||||
self.isAltCast = false
|
||||
self.executeThresholdPct = 0
|
||||
self.executeAllowed = false
|
||||
self.hasCreatedCopy = false
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_enemy.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_enemy.prototype.IsDebuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_enemy.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_enemy.prototype.GetAttributes(self)
|
||||
return MODIFIER_ATTRIBUTE_MULTIPLE
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_enemy.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_EVENT_ON_DEATH}
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_enemy.prototype.OnCreated(self, params)
|
||||
if IsServer() then
|
||||
local ability = self:GetAbility()
|
||||
if ability then
|
||||
self.damage = ability:GetSpecialValueFor("damage")
|
||||
self.frostStacksPerLevel = ability:GetSpecialValueFor("frost_stacks_per_level")
|
||||
self.executeThresholdPct = ability:GetSpecialValueFor("execute_threshold_pct") or 0
|
||||
end
|
||||
self.executeAllowed = self:canUseExecuteOnTarget(self:GetParent())
|
||||
self.isAltCast = params.isAltCast == 1
|
||||
self:StartIntervalThink(0.5)
|
||||
end
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_enemy.prototype.canUseExecuteOnTarget(self, target)
|
||||
local unitName = target:GetUnitName()
|
||||
if not unitName then
|
||||
return true
|
||||
end
|
||||
local lowerCaseName = string.lower(unitName)
|
||||
local denyNames = {
|
||||
"boss",
|
||||
"npc_fish_1",
|
||||
"npc_fish_2",
|
||||
"npc_bomb",
|
||||
"npc_wisps"
|
||||
}
|
||||
for ____, deny in ipairs(denyNames) do
|
||||
if __TS__StringIncludes(lowerCaseName, deny) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_enemy.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local caster = self:GetCaster()
|
||||
local ability = self:GetAbility()
|
||||
if not parent or parent:IsNull() or not parent:IsAlive() or not caster or not ability then
|
||||
return
|
||||
end
|
||||
if self.executeThresholdPct > 0 and self.executeAllowed then
|
||||
local currentHealth = parent:GetHealth()
|
||||
local maxHealth = parent:GetMaxHealth()
|
||||
local healthPct = currentHealth / maxHealth * 100
|
||||
if healthPct < self.executeThresholdPct and not self.hasCreatedCopy then
|
||||
local killPosition = parent:GetAbsOrigin()
|
||||
local killForward = parent:GetForwardVector()
|
||||
local unitName = parent:GetUnitName()
|
||||
self.hasCreatedCopy = true
|
||||
parent:Kill(
|
||||
self:GetAbility(),
|
||||
self:GetCaster()
|
||||
)
|
||||
parent:RemoveSelf()
|
||||
Timers:CreateTimer(
|
||||
0.1,
|
||||
function()
|
||||
if not parent or parent:IsNull() or not parent:IsAlive() then
|
||||
local frozenCopy = CreateUnitByName(
|
||||
unitName,
|
||||
killPosition,
|
||||
true,
|
||||
caster,
|
||||
caster,
|
||||
DOTA_TEAM_NEUTRALS
|
||||
)
|
||||
if IsServer() then
|
||||
frozenCopy:SetRenderColor(60, 120, 255)
|
||||
end
|
||||
if frozenCopy and IsValidEntity(frozenCopy) then
|
||||
frozenCopy:SetForwardVector(killForward)
|
||||
FindClearSpaceForUnit(frozenCopy, killPosition, true)
|
||||
frozenCopy:SetMinimumGoldBounty(0)
|
||||
frozenCopy:SetMaximumGoldBounty(0)
|
||||
frozenCopy:SetDeathXP(0)
|
||||
frozenCopy:SetMoveCapability(DOTA_UNIT_CAP_MOVE_NONE)
|
||||
frozenCopy:SetAttackCapability(DOTA_UNIT_CAP_NO_ATTACK)
|
||||
local frozenModifier = frozenCopy:AddNewModifier(caster, ability, modifier_general_froze.name, {})
|
||||
if frozenModifier then
|
||||
do
|
||||
local i = 0
|
||||
while i < 150 do
|
||||
frozenModifier:IncrementStackCount()
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
frozenModifier:SetDuration(-1, true)
|
||||
end
|
||||
frozenCopy:AddNewModifier(caster, ability, "modifier_crystal_maiden_frostbite_execute", {duration = 6})
|
||||
EmitSoundOn("Hero_Ancient_Apparition.ColdFeetCast", frozenCopy)
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
return
|
||||
end
|
||||
end
|
||||
local damageToDeal = self.isAltCast and self.damage * 2 or self.damage
|
||||
ApplyDamage({
|
||||
victim = parent,
|
||||
attacker = caster,
|
||||
damage = damageToDeal * 0.5,
|
||||
damage_type = DAMAGE_TYPE_MAGICAL,
|
||||
ability = ability
|
||||
})
|
||||
local frostModifier = parent:FindModifierByName(modifier_general_froze.name)
|
||||
if not frostModifier then
|
||||
frostModifier = parent:AddNewModifier(caster, ability, modifier_general_froze.name, {})
|
||||
end
|
||||
if frostModifier then
|
||||
do
|
||||
local i = 0
|
||||
while i < self.frostStacksPerLevel * 0.5 do
|
||||
frostModifier:IncrementStackCount()
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
frostModifier:SetDuration(1.01, true)
|
||||
end
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_enemy.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_ROOTED] = true}
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_enemy.prototype.GetEffectName(self)
|
||||
return "particles/units/heroes/hero_crystalmaiden/maiden_frostbite_buff.vpcf"
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_enemy.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_ABSORIGIN_FOLLOW
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_enemy.prototype.OnDeath(self, event)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if event.unit ~= self:GetParent() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local caster = self:GetCaster()
|
||||
local ability = self:GetAbility()
|
||||
if not parent or parent:IsNull() or not caster or not ability then
|
||||
return
|
||||
end
|
||||
if self.executeThresholdPct > 0 and self.executeAllowed and not self.hasCreatedCopy then
|
||||
self.hasCreatedCopy = true
|
||||
local deathPosition = parent:GetAbsOrigin()
|
||||
local deathForward = parent:GetForwardVector()
|
||||
local unitName = parent:GetUnitName()
|
||||
Timers:CreateTimer(
|
||||
0.1,
|
||||
function()
|
||||
local frozenCopy = CreateUnitByName(
|
||||
unitName,
|
||||
deathPosition,
|
||||
true,
|
||||
caster,
|
||||
caster,
|
||||
DOTA_TEAM_NEUTRALS
|
||||
)
|
||||
if IsServer() then
|
||||
frozenCopy:SetRenderColor(60, 120, 255)
|
||||
end
|
||||
if frozenCopy and IsValidEntity(frozenCopy) then
|
||||
frozenCopy:SetForwardVector(deathForward)
|
||||
FindClearSpaceForUnit(frozenCopy, deathPosition, true)
|
||||
frozenCopy:SetMinimumGoldBounty(0)
|
||||
frozenCopy:SetMaximumGoldBounty(0)
|
||||
frozenCopy:SetDeathXP(0)
|
||||
frozenCopy:SetMoveCapability(DOTA_UNIT_CAP_MOVE_NONE)
|
||||
frozenCopy:SetAttackCapability(DOTA_UNIT_CAP_NO_ATTACK)
|
||||
local frozenModifier = frozenCopy:AddNewModifier(caster, ability, modifier_general_froze.name, {})
|
||||
if frozenModifier then
|
||||
do
|
||||
local i = 0
|
||||
while i < 150 do
|
||||
frozenModifier:IncrementStackCount()
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
frozenModifier:SetDuration(-1, true)
|
||||
end
|
||||
frozenCopy:AddNewModifier(caster, ability, "modifier_crystal_maiden_frostbite_execute", {duration = 6})
|
||||
EmitSoundOn("Hero_Ancient_Apparition.ColdFeetCast", frozenCopy)
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_enemy.prototype.OnDestroy(self)
|
||||
if IsClient() then
|
||||
return
|
||||
end
|
||||
StopSoundOn(
|
||||
"Hero_Crystal.Frostbite",
|
||||
self:GetParent()
|
||||
)
|
||||
end
|
||||
modifier_crystal_maiden_frostbite_enemy = __TS__Decorate(
|
||||
modifier_crystal_maiden_frostbite_enemy,
|
||||
modifier_crystal_maiden_frostbite_enemy,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_crystal_maiden_frostbite_enemy"}
|
||||
)
|
||||
____exports.modifier_crystal_maiden_frostbite_enemy = modifier_crystal_maiden_frostbite_enemy
|
||||
____exports.modifier_crystal_maiden_frostbite_execute = __TS__Class()
|
||||
local modifier_crystal_maiden_frostbite_execute = ____exports.modifier_crystal_maiden_frostbite_execute
|
||||
modifier_crystal_maiden_frostbite_execute.name = "modifier_crystal_maiden_frostbite_execute"
|
||||
modifier_crystal_maiden_frostbite_execute.____file_path = "scripts/vscripts/abilities/heroes/crystal_maiden/ability_crystal_maiden_frostbite_custom.lua"
|
||||
__TS__ClassExtends(modifier_crystal_maiden_frostbite_execute, BaseModifier)
|
||||
function modifier_crystal_maiden_frostbite_execute.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.isFlying = false
|
||||
self.lastPosition = Vector(0, 0, 0)
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_execute.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_execute.prototype.IsDebuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_execute.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_execute.prototype.GetEffectName(self)
|
||||
return "particles/events/crownfall/survivors/abilities/crystal_maiden/crystal_maiden_frostbite.vpcf"
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_execute.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_ABSORIGIN_FOLLOW
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_execute.prototype.OnCreated(self)
|
||||
if IsServer() then
|
||||
self.lastPosition = self:GetParent():GetAbsOrigin()
|
||||
self:GetParent():RemoveAbility("ability_unit_less_laggy")
|
||||
end
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_execute.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_EVENT_ON_ATTACKED, MODIFIER_PROPERTY_MIN_HEALTH}
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_execute.prototype.GetMinHealth(self)
|
||||
return 1
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_execute.prototype.CheckState(self)
|
||||
return {
|
||||
[MODIFIER_STATE_FROZEN] = true,
|
||||
[MODIFIER_STATE_ROOTED] = true,
|
||||
[MODIFIER_STATE_DISARMED] = true,
|
||||
[MODIFIER_STATE_NO_HEALTH_BAR] = true,
|
||||
[MODIFIER_STATE_SILENCED] = true
|
||||
}
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_execute.prototype.OnAttacked(self, event)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if event.target ~= self:GetParent() then
|
||||
return
|
||||
end
|
||||
if self.isFlying then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local attacker = event.attacker
|
||||
if not parent or parent:IsNull() or not attacker or attacker:IsNull() then
|
||||
return
|
||||
end
|
||||
local direction = parent:GetAbsOrigin() - attacker:GetAbsOrigin()
|
||||
direction.z = 0
|
||||
local normalizedDirection = direction:Normalized()
|
||||
parent:SetMoveCapability(DOTA_UNIT_CAP_MOVE_GROUND)
|
||||
local capturedModifier = self
|
||||
local capturedParent = parent
|
||||
self.explosionTimer = Timers:CreateTimer(
|
||||
0.5,
|
||||
function()
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if not capturedModifier or capturedModifier:IsNull() then
|
||||
return
|
||||
end
|
||||
if not capturedParent or capturedParent:IsNull() or not IsValidEntity(capturedParent) then
|
||||
return
|
||||
end
|
||||
capturedModifier:Explode()
|
||||
capturedParent:RemoveSelf()
|
||||
end
|
||||
)
|
||||
self.isFlying = true
|
||||
self.lastPosition = parent:GetAbsOrigin()
|
||||
modifier_general_knockback:apply(
|
||||
parent,
|
||||
attacker,
|
||||
self:GetAbility(),
|
||||
{
|
||||
duration = 0.5,
|
||||
distance = 600,
|
||||
height = 0,
|
||||
direction_x = normalizedDirection.x,
|
||||
direction_y = normalizedDirection.y
|
||||
}
|
||||
)
|
||||
self:StartCollisionCheck()
|
||||
self:StartIntervalThink(0.1)
|
||||
self.knockbackEndTimer = Timers:CreateTimer(
|
||||
0.6,
|
||||
function()
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if not capturedModifier or capturedModifier:IsNull() then
|
||||
return
|
||||
end
|
||||
if not capturedParent or capturedParent:IsNull() or not IsValidEntity(capturedParent) then
|
||||
return
|
||||
end
|
||||
if capturedModifier.isFlying then
|
||||
capturedModifier.isFlying = false
|
||||
capturedModifier:Explode()
|
||||
capturedParent:RemoveSelf()
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_execute.prototype.OnDestroy(self)
|
||||
if IsServer() then
|
||||
if self.collisionCheckTimer then
|
||||
Timers:RemoveTimer(self.collisionCheckTimer)
|
||||
self.collisionCheckTimer = nil
|
||||
end
|
||||
if self.explosionTimer then
|
||||
Timers:RemoveTimer(self.explosionTimer)
|
||||
self.explosionTimer = nil
|
||||
end
|
||||
if self.knockbackEndTimer then
|
||||
Timers:RemoveTimer(self.knockbackEndTimer)
|
||||
self.knockbackEndTimer = nil
|
||||
end
|
||||
self:Explode()
|
||||
local parent = self:GetParent()
|
||||
if parent and not parent:IsNull() and IsValidEntity(parent) then
|
||||
parent:RemoveSelf()
|
||||
end
|
||||
end
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_execute.prototype.StartCollisionCheck(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local checkInterval = 0.05
|
||||
local checkCount = 0
|
||||
local checkCollision
|
||||
checkCollision = function()
|
||||
if not self or self:IsNull() then
|
||||
self.collisionCheckTimer = nil
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
if not parent or parent:IsNull() or not IsValidEntity(parent) or not self.isFlying then
|
||||
if self and not self:IsNull() then
|
||||
self.collisionCheckTimer = nil
|
||||
end
|
||||
return
|
||||
end
|
||||
local currentPos = parent:GetAbsOrigin()
|
||||
local lastPos = self.lastPosition
|
||||
checkCount = checkCount + 1
|
||||
local caster = self:GetCaster()
|
||||
local searchTeam = caster and caster:GetTeamNumber() or parent:GetTeamNumber()
|
||||
local nearbyUnits = FindUnitsInRadius(
|
||||
searchTeam,
|
||||
currentPos,
|
||||
nil,
|
||||
100,
|
||||
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 allNearbyUnits = FindUnitsInRadius(
|
||||
DOTA_TEAM_NEUTRALS,
|
||||
currentPos,
|
||||
nil,
|
||||
100,
|
||||
DOTA_UNIT_TARGET_TEAM_BOTH,
|
||||
bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC),
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
local otherFrozenCopy = __TS__ArrayFind(
|
||||
allNearbyUnits,
|
||||
function(____, unit) return unit ~= parent and unit:IsAlive() and unit:HasModifier("modifier_crystal_maiden_frostbite_execute") end
|
||||
)
|
||||
if otherFrozenCopy then
|
||||
local pushDirection = otherFrozenCopy:GetAbsOrigin() - currentPos
|
||||
pushDirection.z = 0
|
||||
local normalizedPushDirection = pushDirection:Normalized()
|
||||
local otherModifier = otherFrozenCopy:FindModifierByName("modifier_crystal_maiden_frostbite_execute")
|
||||
if otherModifier and not otherModifier.isFlying then
|
||||
otherFrozenCopy:SetMoveCapability(DOTA_UNIT_CAP_MOVE_GROUND)
|
||||
modifier_general_knockback:apply(
|
||||
otherFrozenCopy,
|
||||
parent,
|
||||
self:GetAbility(),
|
||||
{
|
||||
duration = 0.5,
|
||||
distance = 400,
|
||||
height = 0,
|
||||
direction_x = normalizedPushDirection.x,
|
||||
direction_y = normalizedPushDirection.y
|
||||
}
|
||||
)
|
||||
otherModifier.isFlying = true
|
||||
otherModifier.lastPosition = otherFrozenCopy:GetAbsOrigin()
|
||||
otherModifier:StartCollisionCheck()
|
||||
otherModifier:StartIntervalThink(0.1)
|
||||
local capturedModifier = otherModifier
|
||||
local capturedUnit = otherFrozenCopy
|
||||
otherModifier.knockbackEndTimer = Timers:CreateTimer(
|
||||
0.6,
|
||||
function()
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if not capturedModifier or capturedModifier:IsNull() then
|
||||
return
|
||||
end
|
||||
if not capturedUnit or capturedUnit:IsNull() or not IsValidEntity(capturedUnit) then
|
||||
return
|
||||
end
|
||||
if capturedModifier.isFlying then
|
||||
capturedModifier.isFlying = false
|
||||
capturedModifier:Explode()
|
||||
capturedUnit:RemoveSelf()
|
||||
end
|
||||
end
|
||||
)
|
||||
otherModifier.explosionTimer = Timers:CreateTimer(
|
||||
0.5,
|
||||
function()
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if not capturedModifier or capturedModifier:IsNull() then
|
||||
return
|
||||
end
|
||||
if not capturedUnit or capturedUnit:IsNull() or not IsValidEntity(capturedUnit) then
|
||||
return
|
||||
end
|
||||
capturedModifier:Explode()
|
||||
capturedUnit:RemoveSelf()
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
local hasUnitCollision = __TS__ArraySome(
|
||||
nearbyUnits,
|
||||
function(____, unit) return unit ~= parent and unit:IsAlive() end
|
||||
)
|
||||
local isBlocked = not GridNav:CanFindPath(currentPos, currentPos)
|
||||
local isStuck = false
|
||||
if checkCount > 5 then
|
||||
local distanceMoved = (currentPos - lastPos):Length2D()
|
||||
isStuck = distanceMoved < 10 and self.isFlying
|
||||
end
|
||||
if hasUnitCollision or isBlocked or isStuck then
|
||||
self:Explode()
|
||||
return
|
||||
end
|
||||
self.lastPosition = currentPos
|
||||
self.collisionCheckTimer = Timers:CreateTimer(checkInterval, checkCollision)
|
||||
end
|
||||
self.collisionCheckTimer = Timers:CreateTimer(0.1, checkCollision)
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_execute.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if not self or self:IsNull() then
|
||||
return
|
||||
end
|
||||
if not self.isFlying then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
if not parent or parent:IsNull() or not IsValidEntity(parent) then
|
||||
self:Destroy()
|
||||
return
|
||||
end
|
||||
local knockbackModifier = parent:FindModifierByName("modifier_general_knockback")
|
||||
if not knockbackModifier and self.isFlying then
|
||||
self.isFlying = false
|
||||
Timers:CreateTimer(
|
||||
0.1,
|
||||
function()
|
||||
if self and not self:IsNull() then
|
||||
local checkParent = self:GetParent()
|
||||
if checkParent and not checkParent:IsNull() and IsValidEntity(checkParent) then
|
||||
self:CheckFinalCollision()
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_execute.prototype.CheckFinalCollision(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if self:IsNull() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
if not parent or parent:IsNull() or not IsValidEntity(parent) then
|
||||
return
|
||||
end
|
||||
if not self.isFlying then
|
||||
return
|
||||
end
|
||||
local currentPos = parent:GetAbsOrigin()
|
||||
local caster = self:GetCaster()
|
||||
local searchTeam = caster and caster:GetTeamNumber() or parent:GetTeamNumber()
|
||||
local nearbyUnits = FindUnitsInRadius(
|
||||
searchTeam,
|
||||
currentPos,
|
||||
nil,
|
||||
100,
|
||||
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 hasCollision = __TS__ArraySome(
|
||||
nearbyUnits,
|
||||
function(____, unit) return unit ~= parent and unit:IsAlive() end
|
||||
)
|
||||
if hasCollision then
|
||||
self:Explode()
|
||||
local parent = self:GetParent()
|
||||
if parent and not parent:IsNull() and IsValidEntity(parent) then
|
||||
parent:RemoveSelf()
|
||||
end
|
||||
else
|
||||
self:Explode()
|
||||
local parent = self:GetParent()
|
||||
if parent and not parent:IsNull() and IsValidEntity(parent) then
|
||||
parent:RemoveSelf()
|
||||
end
|
||||
end
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_execute.prototype.Explode(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if self.collisionCheckTimer then
|
||||
Timers:RemoveTimer(self.collisionCheckTimer)
|
||||
self.collisionCheckTimer = nil
|
||||
end
|
||||
if self.explosionTimer then
|
||||
Timers:RemoveTimer(self.explosionTimer)
|
||||
self.explosionTimer = nil
|
||||
end
|
||||
if self.knockbackEndTimer then
|
||||
Timers:RemoveTimer(self.knockbackEndTimer)
|
||||
self.knockbackEndTimer = nil
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
if not parent or parent:IsNull() then
|
||||
return
|
||||
end
|
||||
local explosionPos = parent:GetAbsOrigin()
|
||||
local caster = self:GetCaster()
|
||||
local ability = self:GetAbility()
|
||||
if not caster or not ability then
|
||||
return
|
||||
end
|
||||
local knockbackModifier = parent:FindModifierByName("modifier_general_knockback")
|
||||
if knockbackModifier then
|
||||
knockbackModifier:Destroy()
|
||||
end
|
||||
local explosionParticle = ParticleManager:CreateParticle("particles/econ/items/effigies/status_fx_effigies/frosty_base_statue_destruction_dire.vpcf", PATTACH_CUSTOMORIGIN, nil)
|
||||
ParticleManager:SetParticleControl(explosionParticle, 0, explosionPos)
|
||||
ParticleManager:ReleaseParticleIndex(explosionParticle)
|
||||
EmitSoundOnLocationWithCaster(explosionPos, "Hero_Ancient_Apparition.IceBlast.Target", caster)
|
||||
local modifierAbility = self:GetAbility()
|
||||
self:ExplodeInRadius(explosionPos, caster, modifierAbility or ability)
|
||||
Timers:CreateTimer(
|
||||
0.1,
|
||||
function()
|
||||
if IsServer() and parent and not parent:IsNull() and IsValidEntity(parent) then
|
||||
parent:RemoveSelf()
|
||||
end
|
||||
end
|
||||
)
|
||||
self:Destroy()
|
||||
end
|
||||
function modifier_crystal_maiden_frostbite_execute.prototype.ExplodeInRadius(self, position, caster, ability)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local explosionRadius = ability:GetSpecialValueFor("explosion_radius")
|
||||
local damage = ability:GetCaster():GetMana() * (0.01 * ability:GetSpecialValueFor("damage_per_mana"))
|
||||
local enemies = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
position,
|
||||
nil,
|
||||
explosionRadius,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC),
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
__TS__ArrayForEach(
|
||||
enemies,
|
||||
function(____, enemy)
|
||||
if not enemy or enemy:IsNull() or not enemy:IsAlive() then
|
||||
return
|
||||
end
|
||||
ApplyDamage({
|
||||
victim = enemy,
|
||||
attacker = caster,
|
||||
damage = damage,
|
||||
damage_type = DAMAGE_TYPE_MAGICAL,
|
||||
ability = ability
|
||||
})
|
||||
local frostModifier = enemy:AddNewModifier(caster, ability, modifier_general_froze.name, {})
|
||||
if frostModifier then
|
||||
do
|
||||
local i = 0
|
||||
while i < 15 do
|
||||
frostModifier:IncrementStackCount()
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
frostModifier:SetDuration(3, true)
|
||||
end
|
||||
local particle = ParticleManager:CreateParticle("particles/econ/items/crystal_maiden/crystal_maiden_maiden_of_icewrack/maiden_freezing_field_explosion_c_arcana1.vpcf", PATTACH_ABSORIGIN_FOLLOW, enemy)
|
||||
ParticleManager:ReleaseParticleIndex(particle)
|
||||
EmitSoundOn("Hero_Crystal.Frostbite", enemy)
|
||||
end
|
||||
)
|
||||
end
|
||||
modifier_crystal_maiden_frostbite_execute = __TS__Decorate(
|
||||
modifier_crystal_maiden_frostbite_execute,
|
||||
modifier_crystal_maiden_frostbite_execute,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_crystal_maiden_frostbite_execute"}
|
||||
)
|
||||
____exports.modifier_crystal_maiden_frostbite_execute = modifier_crystal_maiden_frostbite_execute
|
||||
return ____exports
|
||||
+293
@@ -0,0 +1,293 @@
|
||||
local ____lualib = require("lualib_bundle")
|
||||
local __TS__Class = ____lualib.__TS__Class
|
||||
local __TS__ClassExtends = ____lualib.__TS__ClassExtends
|
||||
local __TS__ArrayFind = ____lualib.__TS__ArrayFind
|
||||
local __TS__Decorate = ____lualib.__TS__Decorate
|
||||
local __TS__Delete = ____lualib.__TS__Delete
|
||||
local ____exports = {}
|
||||
local modifier_ability_drow_ranger_frost_arrows
|
||||
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 ____modifier_general_froze = require("abilities.modifiers.modifier_general_froze")
|
||||
local modifier_general_froze = ____modifier_general_froze.modifier_general_froze
|
||||
____exports.ability_drow_ranger_frost_arrows_custom = __TS__Class()
|
||||
local ability_drow_ranger_frost_arrows_custom = ____exports.ability_drow_ranger_frost_arrows_custom
|
||||
ability_drow_ranger_frost_arrows_custom.name = "ability_drow_ranger_frost_arrows_custom"
|
||||
ability_drow_ranger_frost_arrows_custom.____file_path = "scripts/vscripts/abilities/heroes/drow_ranger/ability_drow_ranger_frost_arrows_custom.lua"
|
||||
__TS__ClassExtends(ability_drow_ranger_frost_arrows_custom, BaseAbility)
|
||||
function ability_drow_ranger_frost_arrows_custom.prototype.GetIntrinsicModifierName(self)
|
||||
return modifier_ability_drow_ranger_frost_arrows.name
|
||||
end
|
||||
function ability_drow_ranger_frost_arrows_custom.prototype.OnOrbFire(self, params)
|
||||
local sound_cast = "Hero_DrowRanger.FrostArrows"
|
||||
EmitSoundOn(
|
||||
sound_cast,
|
||||
self:GetCaster()
|
||||
)
|
||||
end
|
||||
function ability_drow_ranger_frost_arrows_custom.prototype.OnOrbImpact(self, params)
|
||||
local target = params.target
|
||||
if not target then
|
||||
return
|
||||
end
|
||||
local modifier = target:AddNewModifier(
|
||||
self:GetCaster(),
|
||||
self,
|
||||
modifier_general_froze.name,
|
||||
{}
|
||||
)
|
||||
if modifier then
|
||||
local stacksPerLevel = self:GetSpecialValueFor("frost_stacks_per_level")
|
||||
do
|
||||
local i = 0
|
||||
while i < stacksPerLevel do
|
||||
modifier:IncrementStackCount()
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
if not self:GetCaster():HasScepter() then
|
||||
return
|
||||
end
|
||||
local ricochetChance = self:GetSpecialValueFor("ricochet_chance")
|
||||
if RandomInt(1, 100) > ricochetChance then
|
||||
return
|
||||
end
|
||||
local ricochetRadius = 500
|
||||
local enemies = FindUnitsInRadius(
|
||||
self:GetCaster():GetTeamNumber(),
|
||||
target:GetAbsOrigin(),
|
||||
nil,
|
||||
ricochetRadius,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC,
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_CLOSEST,
|
||||
false
|
||||
)
|
||||
local nearestEnemy = __TS__ArrayFind(
|
||||
enemies,
|
||||
function(____, enemy) return enemy ~= target end
|
||||
)
|
||||
if nearestEnemy then
|
||||
local particlePath = "particles/units/heroes/hero_drow/drow_frost_arrow.vpcf"
|
||||
ProjectileManager:CreateTrackingProjectile({
|
||||
Source = target,
|
||||
Target = nearestEnemy,
|
||||
Ability = self,
|
||||
EffectName = particlePath,
|
||||
iMoveSpeed = 1250,
|
||||
bDodgeable = true,
|
||||
bVisibleToEnemies = true,
|
||||
bReplaceExisting = false,
|
||||
bProvidesVision = false
|
||||
})
|
||||
Timers:CreateTimer(
|
||||
0.2,
|
||||
function()
|
||||
if nearestEnemy and nearestEnemy:IsAlive() then
|
||||
local ricochetModifier = nearestEnemy:AddNewModifier(
|
||||
self:GetCaster(),
|
||||
self,
|
||||
modifier_general_froze.name,
|
||||
{}
|
||||
)
|
||||
if ricochetModifier ~= nil then
|
||||
local stacksPerLevel = self:GetSpecialValueFor("frost_stacks_per_level")
|
||||
do
|
||||
local i = 0
|
||||
while i < stacksPerLevel do
|
||||
ricochetModifier:IncrementStackCount()
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
self:GetCaster():PerformAttack(
|
||||
nearestEnemy,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
)
|
||||
EmitSoundOn("Hero_DrowRanger.FrostArrows.Impact", nearestEnemy)
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
ability_drow_ranger_frost_arrows_custom = __TS__Decorate(
|
||||
ability_drow_ranger_frost_arrows_custom,
|
||||
ability_drow_ranger_frost_arrows_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_drow_ranger_frost_arrows_custom"}
|
||||
)
|
||||
____exports.ability_drow_ranger_frost_arrows_custom = ability_drow_ranger_frost_arrows_custom
|
||||
modifier_ability_drow_ranger_frost_arrows = __TS__Class()
|
||||
modifier_ability_drow_ranger_frost_arrows.name = "modifier_ability_drow_ranger_frost_arrows"
|
||||
modifier_ability_drow_ranger_frost_arrows.____file_path = "scripts/vscripts/abilities/heroes/drow_ranger/ability_drow_ranger_frost_arrows_custom.lua"
|
||||
__TS__ClassExtends(modifier_ability_drow_ranger_frost_arrows, BaseModifier)
|
||||
function modifier_ability_drow_ranger_frost_arrows.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.cast = false
|
||||
self.records = {}
|
||||
end
|
||||
function modifier_ability_drow_ranger_frost_arrows.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_ability_drow_ranger_frost_arrows.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_ability_drow_ranger_frost_arrows.prototype.IsPurgeException(self)
|
||||
return false
|
||||
end
|
||||
function modifier_ability_drow_ranger_frost_arrows.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_ability_drow_ranger_frost_arrows.prototype.IsBuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_ability_drow_ranger_frost_arrows.prototype.RemoveOnDeath(self)
|
||||
return false
|
||||
end
|
||||
function modifier_ability_drow_ranger_frost_arrows.prototype.GetAttributes(self)
|
||||
return MODIFIER_ATTRIBUTE_PERMANENT
|
||||
end
|
||||
function modifier_ability_drow_ranger_frost_arrows.prototype.DeclareFunctions(self)
|
||||
return {
|
||||
MODIFIER_EVENT_ON_ATTACK,
|
||||
MODIFIER_EVENT_ON_ATTACK_FAIL,
|
||||
MODIFIER_EVENT_ON_ATTACK_LANDED,
|
||||
MODIFIER_PROPERTY_PROCATTACK_FEEDBACK,
|
||||
MODIFIER_EVENT_ON_ATTACK_RECORD_DESTROY,
|
||||
MODIFIER_EVENT_ON_ORDER,
|
||||
MODIFIER_PROPERTY_PROJECTILE_NAME,
|
||||
MODIFIER_PROPERTY_PREATTACK_BONUS_DAMAGE
|
||||
}
|
||||
end
|
||||
function modifier_ability_drow_ranger_frost_arrows.prototype.OnCreated(self)
|
||||
self.ability = self:GetAbility()
|
||||
self.cast = false
|
||||
self.records = {}
|
||||
end
|
||||
function modifier_ability_drow_ranger_frost_arrows.prototype.GetModifierPreAttack_BonusDamage(self)
|
||||
if self:GetParent():PassivesDisabled() then
|
||||
return 0
|
||||
end
|
||||
if self:GetParent():IsIllusion() then
|
||||
return 0
|
||||
end
|
||||
if IsServer() then
|
||||
local target = self:GetParent():GetAggroTarget()
|
||||
if self:ShouldLaunch(target) then
|
||||
return self:GetParent():GetAttackDamage() * (self:GetAbility():GetSpecialValueFor("damage_pct") * 0.01)
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
function modifier_ability_drow_ranger_frost_arrows.prototype.OnAttack(self, params)
|
||||
if params.attacker ~= self:GetParent() then
|
||||
return
|
||||
end
|
||||
if self:GetParent():PassivesDisabled() then
|
||||
return
|
||||
end
|
||||
if self:GetParent():IsIllusion() then
|
||||
return
|
||||
end
|
||||
if self:ShouldLaunch(params.target) then
|
||||
self.ability:UseResources(true, false, true, true)
|
||||
self.records[params.record] = true
|
||||
if self.ability.OnOrbFire ~= nil then
|
||||
self.ability:OnOrbFire(params)
|
||||
end
|
||||
end
|
||||
self.cast = false
|
||||
end
|
||||
function modifier_ability_drow_ranger_frost_arrows.prototype.OnAttackLanded(self, event)
|
||||
if event.attacker ~= self:GetParent() then
|
||||
return
|
||||
end
|
||||
if self:GetParent():PassivesDisabled() then
|
||||
return
|
||||
end
|
||||
if self:GetParent():IsIllusion() then
|
||||
return
|
||||
end
|
||||
if self:GetAbility():GetSpecialValueFor("crit_multiplier") > 0 then
|
||||
self:IncrementStackCount()
|
||||
if self:GetStackCount() > 3 then
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local mult = self:GetAbility():GetSpecialValueFor("crit_multiplier")
|
||||
local stackingCritMod = self:GetCaster():FindModifierByName("modifier_stacking_crit")
|
||||
if stackingCritMod then
|
||||
local ability = self
|
||||
stackingCritMod:AddCustomCrit(100, mult, "drow_ranger_frost_arrows")
|
||||
end
|
||||
if self:GetStackCount() > 4 then
|
||||
self:SetStackCount(1)
|
||||
if stackingCritMod then
|
||||
stackingCritMod:RemoveCrit("drow_ranger_frost_arrows")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
function modifier_ability_drow_ranger_frost_arrows.prototype.GetModifierProcAttack_Feedback(self, params)
|
||||
if self:GetParent():PassivesDisabled() then
|
||||
return 0
|
||||
end
|
||||
if self:GetParent():IsIllusion() then
|
||||
return 0
|
||||
end
|
||||
if self.records[params.record] then
|
||||
if self.ability.OnOrbImpact ~= nil then
|
||||
self.ability:OnOrbImpact(params)
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
function modifier_ability_drow_ranger_frost_arrows.prototype.OnAttackFail(self, params)
|
||||
if self.records[params.record] then
|
||||
__TS__Delete(self.records, params.record)
|
||||
end
|
||||
end
|
||||
function modifier_ability_drow_ranger_frost_arrows.prototype.OnAttackRecordDestroy(self, params)
|
||||
self.records[params.record] = false
|
||||
end
|
||||
function modifier_ability_drow_ranger_frost_arrows.prototype.OnOrder(self, params)
|
||||
return
|
||||
end
|
||||
function modifier_ability_drow_ranger_frost_arrows.prototype.ShouldLaunch(self, target)
|
||||
if not target then
|
||||
return false
|
||||
end
|
||||
return self.ability:GetCooldownTimeRemaining() == 0
|
||||
end
|
||||
function modifier_ability_drow_ranger_frost_arrows.prototype.GetModifierProjectileName(self)
|
||||
if self:GetParent():PassivesDisabled() then
|
||||
return ""
|
||||
end
|
||||
local target = self:GetParent():GetAggroTarget()
|
||||
if not target then
|
||||
return ""
|
||||
end
|
||||
if self:ShouldLaunch(target) then
|
||||
return "particles/units/heroes/hero_drow/drow_frost_arrow.vpcf"
|
||||
end
|
||||
return ""
|
||||
end
|
||||
modifier_ability_drow_ranger_frost_arrows = __TS__Decorate(
|
||||
modifier_ability_drow_ranger_frost_arrows,
|
||||
modifier_ability_drow_ranger_frost_arrows,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_ability_drow_ranger_frost_arrows"}
|
||||
)
|
||||
return ____exports
|
||||
@@ -0,0 +1,151 @@
|
||||
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 ____modifier_general_froze = require("abilities.modifiers.modifier_general_froze")
|
||||
local modifier_general_froze = ____modifier_general_froze.modifier_general_froze
|
||||
local ____modifier_general_knockback = require("abilities.modifiers.modifier_general_knockback")
|
||||
local modifier_general_knockback = ____modifier_general_knockback.modifier_general_knockback
|
||||
____exports.ability_drow_ranger_gust_custom = __TS__Class()
|
||||
local ability_drow_ranger_gust_custom = ____exports.ability_drow_ranger_gust_custom
|
||||
ability_drow_ranger_gust_custom.name = "ability_drow_ranger_gust_custom"
|
||||
ability_drow_ranger_gust_custom.____file_path = "scripts/vscripts/abilities/heroes/drow_ranger/ability_drow_ranger_gust_custom.lua"
|
||||
__TS__ClassExtends(ability_drow_ranger_gust_custom, BaseAbility)
|
||||
function ability_drow_ranger_gust_custom.prototype.OnSpellStart(self)
|
||||
local caster = self:GetCaster()
|
||||
local point = self:GetCursorPosition()
|
||||
local casterPos = caster:GetAbsOrigin()
|
||||
local speed = self:GetSpecialValueFor("gust_speed")
|
||||
local width = self:GetSpecialValueFor("gust_width")
|
||||
local distance = self:GetSpecialValueFor("gust_distance")
|
||||
local direction = point - casterPos
|
||||
direction.z = 0
|
||||
direction = direction:Normalized()
|
||||
local projectileInfo = {
|
||||
Source = caster,
|
||||
Ability = self,
|
||||
vSpawnOrigin = casterPos,
|
||||
bDeleteOnHit = false,
|
||||
iUnitTargetTeam = DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
iUnitTargetFlags = DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
iUnitTargetType = bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC),
|
||||
EffectName = "particles/units/heroes/hero_drow/drow_silence_wave.vpcf",
|
||||
fDistance = distance,
|
||||
fStartRadius = width,
|
||||
fEndRadius = width,
|
||||
vVelocity = direction * speed,
|
||||
ExtraData = {x = casterPos.x, y = casterPos.y}
|
||||
}
|
||||
ProjectileManager:CreateLinearProjectile(projectileInfo)
|
||||
EmitSoundOn("Hero_DrowRanger.Silence", caster)
|
||||
end
|
||||
function ability_drow_ranger_gust_custom.prototype.OnProjectileHit_ExtraData(self, target, location, extraData)
|
||||
if not target then
|
||||
return false
|
||||
end
|
||||
if target:GetTeamNumber() == self:GetCaster():GetTeamNumber() then
|
||||
return false
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local knockback_duration = self:GetSpecialValueFor("knockback_duration")
|
||||
local knockback_distance = self:GetSpecialValueFor("knockback_distance")
|
||||
local damage = self:GetSpecialValueFor("damage")
|
||||
local modifier = target:AddNewModifier(
|
||||
self:GetCaster(),
|
||||
self,
|
||||
modifier_general_froze.name,
|
||||
{}
|
||||
)
|
||||
local stacksPerLevel = self:GetSpecialValueFor("frost_stacks_per_level")
|
||||
do
|
||||
local i = 0
|
||||
while i < stacksPerLevel do
|
||||
modifier:IncrementStackCount()
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
if self:GetSpecialValueFor("frozen_son_duration") > 0 then
|
||||
target:AddNewModifier(
|
||||
self:GetCaster(),
|
||||
self,
|
||||
____exports.modifier_general_frozen_son.name,
|
||||
{duration = self:GetSpecialValueFor("frozen_son_duration")}
|
||||
)
|
||||
end
|
||||
ApplyDamage({
|
||||
victim = target,
|
||||
attacker = caster,
|
||||
damage = damage,
|
||||
damage_type = DAMAGE_TYPE_MAGICAL,
|
||||
ability = self
|
||||
})
|
||||
local knockbackDirection = target:GetAbsOrigin() - Vector(extraData.x, extraData.y, 0)
|
||||
knockbackDirection.z = 0
|
||||
knockbackDirection = knockbackDirection:Normalized()
|
||||
modifier_general_knockback:apply(target, caster, self, {duration = knockback_duration, distance = knockback_distance, direction_x = knockbackDirection.x, direction_y = knockbackDirection.y})
|
||||
local effect_cast = ParticleManager:CreateParticle("particles/units/heroes/hero_drow/drow_silence.vpcf", PATTACH_ABSORIGIN_FOLLOW, target)
|
||||
ParticleManager:ReleaseParticleIndex(effect_cast)
|
||||
return false
|
||||
end
|
||||
ability_drow_ranger_gust_custom = __TS__Decorate(
|
||||
ability_drow_ranger_gust_custom,
|
||||
ability_drow_ranger_gust_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_drow_ranger_gust_custom"}
|
||||
)
|
||||
____exports.ability_drow_ranger_gust_custom = ability_drow_ranger_gust_custom
|
||||
____exports.modifier_general_frozen_son = __TS__Class()
|
||||
local modifier_general_frozen_son = ____exports.modifier_general_frozen_son
|
||||
modifier_general_frozen_son.name = "modifier_general_frozen_son"
|
||||
modifier_general_frozen_son.____file_path = "scripts/vscripts/abilities/heroes/drow_ranger/ability_drow_ranger_gust_custom.lua"
|
||||
__TS__ClassExtends(modifier_general_frozen_son, BaseModifier)
|
||||
function modifier_general_frozen_son.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_general_frozen_son.prototype.IsDebuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_general_frozen_son.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
function modifier_general_frozen_son.prototype.GetEffectName(self)
|
||||
return "particles/units/heroes/hero_crystalmaiden/maiden_frostbite_buff.vpcf"
|
||||
end
|
||||
function modifier_general_frozen_son.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_ABSORIGIN_FOLLOW
|
||||
end
|
||||
function modifier_general_frozen_son.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_ROOTED] = true, [MODIFIER_STATE_DISARMED] = true}
|
||||
end
|
||||
function modifier_general_frozen_son.prototype.OnCreated(self, params)
|
||||
if IsClient() then
|
||||
return
|
||||
end
|
||||
EmitSoundOn(
|
||||
"Hero_Crystal.Frostbite",
|
||||
self:GetParent()
|
||||
)
|
||||
end
|
||||
function modifier_general_frozen_son.prototype.OnDestroy(self)
|
||||
if IsClient() then
|
||||
return
|
||||
end
|
||||
StopSoundOn(
|
||||
"Hero_Crystal.Frostbite",
|
||||
self:GetParent()
|
||||
)
|
||||
end
|
||||
modifier_general_frozen_son = __TS__Decorate(
|
||||
modifier_general_frozen_son,
|
||||
modifier_general_frozen_son,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_general_frozen_son"}
|
||||
)
|
||||
____exports.modifier_general_frozen_son = modifier_general_frozen_son
|
||||
return ____exports
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
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
|
||||
____exports.ability_drow_ranger_marksmanship_custom = __TS__Class()
|
||||
local ability_drow_ranger_marksmanship_custom = ____exports.ability_drow_ranger_marksmanship_custom
|
||||
ability_drow_ranger_marksmanship_custom.name = "ability_drow_ranger_marksmanship_custom"
|
||||
ability_drow_ranger_marksmanship_custom.____file_path = "scripts/vscripts/abilities/heroes/drow_ranger/ability_drow_ranger_marksmanship_custom.lua"
|
||||
__TS__ClassExtends(ability_drow_ranger_marksmanship_custom, BaseAbility)
|
||||
function ability_drow_ranger_marksmanship_custom.prototype.____constructor(self, ...)
|
||||
BaseAbility.prototype.____constructor(self, ...)
|
||||
self.modifier = "modifier_drow_ranger_marksmanship_custom"
|
||||
end
|
||||
function ability_drow_ranger_marksmanship_custom.prototype.GetIntrinsicModifierName(self)
|
||||
return self.modifier
|
||||
end
|
||||
ability_drow_ranger_marksmanship_custom = __TS__Decorate(
|
||||
ability_drow_ranger_marksmanship_custom,
|
||||
ability_drow_ranger_marksmanship_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_drow_ranger_marksmanship_custom"}
|
||||
)
|
||||
____exports.ability_drow_ranger_marksmanship_custom = ability_drow_ranger_marksmanship_custom
|
||||
____exports.modifier_drow_ranger_marksmanship_custom = __TS__Class()
|
||||
local modifier_drow_ranger_marksmanship_custom = ____exports.modifier_drow_ranger_marksmanship_custom
|
||||
modifier_drow_ranger_marksmanship_custom.name = "modifier_drow_ranger_marksmanship_custom"
|
||||
modifier_drow_ranger_marksmanship_custom.____file_path = "scripts/vscripts/abilities/heroes/drow_ranger/ability_drow_ranger_marksmanship_custom.lua"
|
||||
__TS__ClassExtends(modifier_drow_ranger_marksmanship_custom, BaseModifier)
|
||||
function modifier_drow_ranger_marksmanship_custom.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.hits = 0
|
||||
self.bonusDamage = 0
|
||||
self.isActive = true
|
||||
self.checkRadius = self:GetAbility():GetSpecialValueFor("disable_range")
|
||||
end
|
||||
function modifier_drow_ranger_marksmanship_custom.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_drow_ranger_marksmanship_custom.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_drow_ranger_marksmanship_custom.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_drow_ranger_marksmanship_custom.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
self.hits = ability:GetSpecialValueFor("hits")
|
||||
self.bonusDamage = ability:GetSpecialValueFor("bonus_damage")
|
||||
self.checkRadius = ability:GetSpecialValueFor("disable_range")
|
||||
self:SetStackCount(self.hits)
|
||||
self:StartIntervalThink(0.1)
|
||||
end
|
||||
function modifier_drow_ranger_marksmanship_custom.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if self:GetParent():PassivesDisabled() then
|
||||
self.isActive = false
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local enemies = FindUnitsInRadius(
|
||||
parent:GetTeamNumber(),
|
||||
parent:GetAbsOrigin(),
|
||||
nil,
|
||||
self.checkRadius,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC),
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
self.isActive = #enemies == 0
|
||||
end
|
||||
function modifier_drow_ranger_marksmanship_custom.prototype.OnDestroy(self)
|
||||
if IsClient() then
|
||||
return
|
||||
end
|
||||
end
|
||||
function modifier_drow_ranger_marksmanship_custom.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_PROJECTILE_NAME, MODIFIER_EVENT_ON_ATTACK, MODIFIER_PROPERTY_BASEATTACK_BONUSDAMAGE}
|
||||
end
|
||||
function modifier_drow_ranger_marksmanship_custom.prototype.OnAttack(self, event)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if self:GetParent():PassivesDisabled() then
|
||||
return
|
||||
end
|
||||
if not self.isActive then
|
||||
return
|
||||
end
|
||||
if self:GetParent():IsIllusion() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
if event.attacker ~= parent then
|
||||
return
|
||||
end
|
||||
if self:GetStackCount() == 1 then
|
||||
self:SetStackCount(self.hits)
|
||||
EmitSoundOn("Hero_DrowRanger.Marksmanship.Target", event.target)
|
||||
if self:GetAbility():GetSpecialValueFor("marksmanship_master") > 0 then
|
||||
event.target:AddNewModifier(
|
||||
parent,
|
||||
self:GetAbility(),
|
||||
"modifier_drow_ranger_marksmanship_custom_armor",
|
||||
{duration = 0.5}
|
||||
)
|
||||
end
|
||||
return
|
||||
end
|
||||
self:DecrementStackCount()
|
||||
end
|
||||
function modifier_drow_ranger_marksmanship_custom.prototype.GetPriority(self)
|
||||
local ____temp_0
|
||||
if self:GetStackCount() == 1 then
|
||||
____temp_0 = MODIFIER_PRIORITY_ULTRA
|
||||
else
|
||||
____temp_0 = MODIFIER_PRIORITY_NORMAL
|
||||
end
|
||||
return ____temp_0
|
||||
end
|
||||
function modifier_drow_ranger_marksmanship_custom.prototype.GetModifierProjectileName(self)
|
||||
if self:GetParent():PassivesDisabled() then
|
||||
return ""
|
||||
end
|
||||
if self:GetParent():IsIllusion() then
|
||||
return ""
|
||||
end
|
||||
if not self.isActive then
|
||||
return ""
|
||||
end
|
||||
return self:GetStackCount() == 1 and "particles/units/heroes/hero_drow/drow_marksmanship_attack.vpcf" or ""
|
||||
end
|
||||
function modifier_drow_ranger_marksmanship_custom.prototype.GetModifierBaseAttack_BonusDamage(self)
|
||||
if self:GetParent():PassivesDisabled() then
|
||||
return 0
|
||||
end
|
||||
if self:GetParent():IsIllusion() then
|
||||
return 0
|
||||
end
|
||||
if not self.isActive then
|
||||
return 0
|
||||
end
|
||||
return self:GetStackCount() == 1 and self.bonusDamage or 0
|
||||
end
|
||||
modifier_drow_ranger_marksmanship_custom = __TS__Decorate(
|
||||
modifier_drow_ranger_marksmanship_custom,
|
||||
modifier_drow_ranger_marksmanship_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_drow_ranger_marksmanship_custom"}
|
||||
)
|
||||
____exports.modifier_drow_ranger_marksmanship_custom = modifier_drow_ranger_marksmanship_custom
|
||||
____exports.modifier_drow_ranger_marksmanship_custom_armor = __TS__Class()
|
||||
local modifier_drow_ranger_marksmanship_custom_armor = ____exports.modifier_drow_ranger_marksmanship_custom_armor
|
||||
modifier_drow_ranger_marksmanship_custom_armor.name = "modifier_drow_ranger_marksmanship_custom_armor"
|
||||
modifier_drow_ranger_marksmanship_custom_armor.____file_path = "scripts/vscripts/abilities/heroes/drow_ranger/ability_drow_ranger_marksmanship_custom.lua"
|
||||
__TS__ClassExtends(modifier_drow_ranger_marksmanship_custom_armor, BaseModifier)
|
||||
function modifier_drow_ranger_marksmanship_custom_armor.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_drow_ranger_marksmanship_custom_armor.prototype.IsDebuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_drow_ranger_marksmanship_custom_armor.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_drow_ranger_marksmanship_custom_armor.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_IGNORE_PHYSICAL_ARMOR}
|
||||
end
|
||||
function modifier_drow_ranger_marksmanship_custom_armor.prototype.GetModifierIgnorePhysicalArmor(self)
|
||||
return 1
|
||||
end
|
||||
function modifier_drow_ranger_marksmanship_custom_armor.prototype.GetAttributes(self)
|
||||
return MODIFIER_ATTRIBUTE_IGNORE_INVULNERABLE
|
||||
end
|
||||
modifier_drow_ranger_marksmanship_custom_armor = __TS__Decorate(
|
||||
modifier_drow_ranger_marksmanship_custom_armor,
|
||||
modifier_drow_ranger_marksmanship_custom_armor,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_drow_ranger_marksmanship_custom_armor"}
|
||||
)
|
||||
____exports.modifier_drow_ranger_marksmanship_custom_armor = modifier_drow_ranger_marksmanship_custom_armor
|
||||
return ____exports
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
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 ____modifier_general_froze = require("abilities.modifiers.modifier_general_froze")
|
||||
local modifier_general_froze = ____modifier_general_froze.modifier_general_froze
|
||||
____exports.ability_drow_ranger_multishot_custom = __TS__Class()
|
||||
local ability_drow_ranger_multishot_custom = ____exports.ability_drow_ranger_multishot_custom
|
||||
ability_drow_ranger_multishot_custom.name = "ability_drow_ranger_multishot_custom"
|
||||
ability_drow_ranger_multishot_custom.____file_path = "scripts/vscripts/abilities/heroes/drow_ranger/ability_drow_ranger_multishot_custom.lua"
|
||||
__TS__ClassExtends(ability_drow_ranger_multishot_custom, BaseAbility)
|
||||
function ability_drow_ranger_multishot_custom.prototype.____constructor(self, ...)
|
||||
BaseAbility.prototype.____constructor(self, ...)
|
||||
self.modifier = nil
|
||||
end
|
||||
function ability_drow_ranger_multishot_custom.prototype.OnSpellStart(self)
|
||||
local caster = self:GetCaster()
|
||||
local point = self:GetCursorPosition()
|
||||
local duration = self:GetChannelTime()
|
||||
caster:AddNewModifier(caster, self, ____exports.modifier_drow_ranger_multishot_damage_custom.name, {duration = 1.75})
|
||||
self.modifier = ____exports.modifier_drow_ranger_multishot_custom:apply(caster, caster, self, {duration = duration, x = point.x, y = point.y, z = point.z})
|
||||
EmitSoundOn("Hero_DrowRanger.Multishot.Channel", caster)
|
||||
end
|
||||
function ability_drow_ranger_multishot_custom.prototype.OnChannelFinish(self)
|
||||
if self.modifier ~= nil then
|
||||
self.modifier:Destroy()
|
||||
end
|
||||
self:GetCaster():RemoveModifierByName(____exports.modifier_drow_ranger_multishot_damage_custom.name)
|
||||
end
|
||||
function ability_drow_ranger_multishot_custom.prototype.OnProjectileHit_ExtraData(self, target)
|
||||
if not target then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local modifier = target:AddNewModifier(
|
||||
self:GetCaster(),
|
||||
self,
|
||||
modifier_general_froze.name,
|
||||
{}
|
||||
)
|
||||
local stacksPerLevel = self:GetSpecialValueFor("frost_stacks_per_level")
|
||||
do
|
||||
local i = 0
|
||||
while i < stacksPerLevel do
|
||||
modifier:IncrementStackCount()
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
caster:PerformAttack(
|
||||
target,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
)
|
||||
EmitSoundOn("Hero_DrowRanger.ProjectileImpact", target)
|
||||
if self:GetSpecialValueFor("piercing_arrows") > 0 then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
ability_drow_ranger_multishot_custom = __TS__Decorate(
|
||||
ability_drow_ranger_multishot_custom,
|
||||
ability_drow_ranger_multishot_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_drow_ranger_multishot_custom"}
|
||||
)
|
||||
____exports.ability_drow_ranger_multishot_custom = ability_drow_ranger_multishot_custom
|
||||
____exports.modifier_drow_ranger_multishot_custom = __TS__Class()
|
||||
local modifier_drow_ranger_multishot_custom = ____exports.modifier_drow_ranger_multishot_custom
|
||||
modifier_drow_ranger_multishot_custom.name = "modifier_drow_ranger_multishot_custom"
|
||||
modifier_drow_ranger_multishot_custom.____file_path = "scripts/vscripts/abilities/heroes/drow_ranger/ability_drow_ranger_multishot_custom.lua"
|
||||
__TS__ClassExtends(modifier_drow_ranger_multishot_custom, BaseModifier)
|
||||
function modifier_drow_ranger_multishot_custom.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.arrowDelay = 0.01
|
||||
self.direction = Vector(0, 0, 0)
|
||||
self.currentArrows = 0
|
||||
self.currentWave = 0
|
||||
self.arrowsPerWave = 0
|
||||
self.waveCount = 0
|
||||
self.waveDelay = 0
|
||||
self.speed = 0
|
||||
self.angle = 0
|
||||
end
|
||||
function modifier_drow_ranger_multishot_custom.prototype.OnCreated(self, params)
|
||||
local caster = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
local range = ability:GetSpecialValueFor("arrow_range_multiplier")
|
||||
local width = ability:GetSpecialValueFor("arrow_width")
|
||||
self.speed = ability:GetSpecialValueFor("arrow_speed")
|
||||
if IsClient() then
|
||||
return
|
||||
end
|
||||
local vision = 100
|
||||
local delay = 0.1
|
||||
local wave = ability:GetSpecialValueFor("wave_count")
|
||||
local waveInterval = 0.4375
|
||||
self.arrowsPerWave = ability:GetSpecialValueFor("arrow_count_per_wave")
|
||||
self.waveCount = wave
|
||||
self.waveDelay = waveInterval - self.arrowDelay * (self.arrowsPerWave - 1)
|
||||
self.angle = 30
|
||||
local point = Vector(params.x, params.y, params.z)
|
||||
self.direction = point - caster:GetOrigin()
|
||||
self.direction.z = 0
|
||||
self.direction = self.direction:Normalized()
|
||||
self.info = {
|
||||
Source = caster,
|
||||
Ability = ability,
|
||||
vSpawnOrigin = caster:GetAttachmentOrigin(caster:ScriptLookupAttachment("attach_attack1")),
|
||||
iUnitTargetTeam = DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
iUnitTargetType = DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC,
|
||||
iUnitTargetFlags = DOTA_UNIT_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES,
|
||||
EffectName = "particles/units/heroes/hero_drow/drow_multishot_proj_linear_proj.vpcf",
|
||||
fDistance = caster:Script_GetAttackRange() * range,
|
||||
fStartRadius = width,
|
||||
fEndRadius = width,
|
||||
bProvidesVision = true,
|
||||
iVisionRadius = vision,
|
||||
iVisionTeamNumber = caster:GetTeamNumber()
|
||||
}
|
||||
self:StartIntervalThink(delay)
|
||||
end
|
||||
function modifier_drow_ranger_multishot_custom.prototype.OnDestroy(self)
|
||||
if IsClient() then
|
||||
return
|
||||
end
|
||||
StopSoundOn(
|
||||
"Hero_DrowRanger.Multishot.Channel",
|
||||
self:GetParent()
|
||||
)
|
||||
end
|
||||
function modifier_drow_ranger_multishot_custom.prototype.OnIntervalThink(self)
|
||||
if self.currentWave >= 3 then
|
||||
return
|
||||
end
|
||||
if self.currentArrows < self.arrowsPerWave then
|
||||
self:StartIntervalThink(self.arrowDelay)
|
||||
else
|
||||
self.currentArrows = 0
|
||||
self.currentWave = self.currentWave + 1
|
||||
if self.currentWave < 3 then
|
||||
self:StartIntervalThink(self.waveDelay)
|
||||
end
|
||||
return
|
||||
end
|
||||
local step = self.angle / (self.arrowsPerWave - 1)
|
||||
local angle = -self.angle / 2 + self.currentArrows * step
|
||||
local projectileDirection = rotatePositionYaw(
|
||||
nil,
|
||||
Vector(0, 0, 0),
|
||||
angle,
|
||||
self.direction
|
||||
)
|
||||
self.info.vVelocity = projectileDirection * self.speed
|
||||
ProjectileManager:CreateLinearProjectile(self.info)
|
||||
EmitSoundOn(
|
||||
"Hero_DrowRanger.Multishot.Attack",
|
||||
self:GetParent()
|
||||
)
|
||||
self.currentArrows = self.currentArrows + 1
|
||||
end
|
||||
modifier_drow_ranger_multishot_custom = __TS__Decorate(
|
||||
modifier_drow_ranger_multishot_custom,
|
||||
modifier_drow_ranger_multishot_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_drow_ranger_multishot_custom"}
|
||||
)
|
||||
____exports.modifier_drow_ranger_multishot_custom = modifier_drow_ranger_multishot_custom
|
||||
____exports.modifier_drow_ranger_multishot_damage_custom = __TS__Class()
|
||||
local modifier_drow_ranger_multishot_damage_custom = ____exports.modifier_drow_ranger_multishot_damage_custom
|
||||
modifier_drow_ranger_multishot_damage_custom.name = "modifier_drow_ranger_multishot_damage_custom"
|
||||
modifier_drow_ranger_multishot_damage_custom.____file_path = "scripts/vscripts/abilities/heroes/drow_ranger/ability_drow_ranger_multishot_custom.lua"
|
||||
__TS__ClassExtends(modifier_drow_ranger_multishot_damage_custom, BaseModifier)
|
||||
function modifier_drow_ranger_multishot_damage_custom.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_drow_ranger_multishot_damage_custom.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_DAMAGEOUTGOING_PERCENTAGE}
|
||||
end
|
||||
function modifier_drow_ranger_multishot_damage_custom.prototype.GetModifierDamageOutgoing_Percentage(self, event)
|
||||
local damage = self:GetAbility():GetSpecialValueFor("arrow_damage_pct") - 100
|
||||
return damage
|
||||
end
|
||||
modifier_drow_ranger_multishot_damage_custom = __TS__Decorate(
|
||||
modifier_drow_ranger_multishot_damage_custom,
|
||||
modifier_drow_ranger_multishot_damage_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_drow_ranger_multishot_damage_custom"}
|
||||
)
|
||||
____exports.modifier_drow_ranger_multishot_damage_custom = modifier_drow_ranger_multishot_damage_custom
|
||||
return ____exports
|
||||
@@ -0,0 +1,95 @@
|
||||
local ____lualib = require("lualib_bundle")
|
||||
local __TS__Class = ____lualib.__TS__Class
|
||||
local __TS__ClassExtends = ____lualib.__TS__ClassExtends
|
||||
local __TS__ArrayForEach = ____lualib.__TS__ArrayForEach
|
||||
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
|
||||
____exports.grimstroke_dark_portrait_custom = __TS__Class()
|
||||
local grimstroke_dark_portrait_custom = ____exports.grimstroke_dark_portrait_custom
|
||||
grimstroke_dark_portrait_custom.name = "grimstroke_dark_portrait_custom"
|
||||
grimstroke_dark_portrait_custom.____file_path = "scripts/vscripts/abilities/heroes/grimstroke/grimstroke_dark_portrait_custom.lua"
|
||||
__TS__ClassExtends(grimstroke_dark_portrait_custom, BaseAbility)
|
||||
function grimstroke_dark_portrait_custom.prototype.Precache(self, context)
|
||||
PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_terrorblade.vsndevts", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_terrorblade/terrorblade_mirror_image.vpcf", context)
|
||||
end
|
||||
function grimstroke_dark_portrait_custom.prototype.OnSpellStart(self)
|
||||
local caster = self:GetCaster()
|
||||
local target = self:GetCursorTarget()
|
||||
if not target or not target:IsInstance(CDOTA_BaseNPC_Hero) then
|
||||
return
|
||||
end
|
||||
local duration = self:GetSpecialValueFor("illusion_duration")
|
||||
local outgoing = self:GetSpecialValueFor("illusion_outgoing_damage")
|
||||
local incoming = self:GetSpecialValueFor("illusion_incoming_damage")
|
||||
local illusion = CreateIllusions(
|
||||
caster,
|
||||
target,
|
||||
{incoming_damage = incoming, outgoing_damage = outgoing, duration = duration},
|
||||
1,
|
||||
caster:GetHullRadius(),
|
||||
false,
|
||||
true
|
||||
)
|
||||
__TS__ArrayForEach(
|
||||
target:FindAllModifiers(),
|
||||
function(____, modifier)
|
||||
illusion[1]:AddNewModifier(
|
||||
caster,
|
||||
self,
|
||||
modifier:GetName(),
|
||||
{}
|
||||
)
|
||||
end
|
||||
)
|
||||
EmitSoundOn("Hero_Terrorblade.ConjureImage", illusion[1])
|
||||
end
|
||||
grimstroke_dark_portrait_custom = __TS__Decorate(
|
||||
grimstroke_dark_portrait_custom,
|
||||
grimstroke_dark_portrait_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "grimstroke_dark_portrait_custom"}
|
||||
)
|
||||
____exports.grimstroke_dark_portrait_custom = grimstroke_dark_portrait_custom
|
||||
____exports.modifier_grimstroke_dark_portrait_custom = __TS__Class()
|
||||
local modifier_grimstroke_dark_portrait_custom = ____exports.modifier_grimstroke_dark_portrait_custom
|
||||
modifier_grimstroke_dark_portrait_custom.name = "modifier_grimstroke_dark_portrait_custom"
|
||||
modifier_grimstroke_dark_portrait_custom.____file_path = "scripts/vscripts/abilities/heroes/grimstroke/grimstroke_dark_portrait_custom.lua"
|
||||
__TS__ClassExtends(modifier_grimstroke_dark_portrait_custom, BaseModifier)
|
||||
function modifier_grimstroke_dark_portrait_custom.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_grimstroke_dark_portrait_custom.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_grimstroke_dark_portrait_custom.prototype.IsStunDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_grimstroke_dark_portrait_custom.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_grimstroke_dark_portrait_custom.prototype.GetEffectName(self)
|
||||
return "particles/units/heroes/hero_terrorblade/terrorblade_mirror_image.vpcf"
|
||||
end
|
||||
function modifier_grimstroke_dark_portrait_custom.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_ABSORIGIN_FOLLOW
|
||||
end
|
||||
function modifier_grimstroke_dark_portrait_custom.prototype.GetStatusEffectName(self)
|
||||
return "particles/status_fx/status_effect_terrorblade_reflection.vpcf"
|
||||
end
|
||||
function modifier_grimstroke_dark_portrait_custom.prototype.StatusEffectPriority(self)
|
||||
return MODIFIER_PRIORITY_SUPER_ULTRA
|
||||
end
|
||||
modifier_grimstroke_dark_portrait_custom = __TS__Decorate(
|
||||
modifier_grimstroke_dark_portrait_custom,
|
||||
modifier_grimstroke_dark_portrait_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_grimstroke_dark_portrait_custom"}
|
||||
)
|
||||
____exports.modifier_grimstroke_dark_portrait_custom = modifier_grimstroke_dark_portrait_custom
|
||||
return ____exports
|
||||
@@ -0,0 +1,231 @@
|
||||
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 ____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
|
||||
____exports.grimstroke_ink_swell_custom = __TS__Class()
|
||||
local grimstroke_ink_swell_custom = ____exports.grimstroke_ink_swell_custom
|
||||
grimstroke_ink_swell_custom.name = "grimstroke_ink_swell_custom"
|
||||
grimstroke_ink_swell_custom.____file_path = "scripts/vscripts/abilities/heroes/grimstroke/grimstroke_ink_swell_custom.lua"
|
||||
__TS__ClassExtends(grimstroke_ink_swell_custom, BaseAbility)
|
||||
function grimstroke_ink_swell_custom.prototype.OnSpellStart(self)
|
||||
local caster = self:GetCaster()
|
||||
local target = self:GetCursorTarget()
|
||||
if not target then
|
||||
return
|
||||
end
|
||||
local duration = self:GetSpecialValueFor("buff_duration")
|
||||
target:AddNewModifier(caster, self, ____exports.modifier_grimstroke_ink_swell.name, {duration = duration})
|
||||
local effectCast = ParticleManager:CreateParticle("particles/units/heroes/hero_grimstroke/grimstroke_cast_ink_swell.vpcf", PATTACH_ABSORIGIN_FOLLOW, caster)
|
||||
ParticleManager:ReleaseParticleIndex(effectCast)
|
||||
EmitSoundOn("Hero_Grimstroke.InkSwell.Cast", caster)
|
||||
end
|
||||
grimstroke_ink_swell_custom = __TS__Decorate(
|
||||
grimstroke_ink_swell_custom,
|
||||
grimstroke_ink_swell_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "grimstroke_ink_swell_custom"}
|
||||
)
|
||||
____exports.grimstroke_ink_swell_custom = grimstroke_ink_swell_custom
|
||||
____exports.modifier_grimstroke_ink_swell = __TS__Class()
|
||||
local modifier_grimstroke_ink_swell = ____exports.modifier_grimstroke_ink_swell
|
||||
modifier_grimstroke_ink_swell.name = "modifier_grimstroke_ink_swell"
|
||||
modifier_grimstroke_ink_swell.____file_path = "scripts/vscripts/abilities/heroes/grimstroke/grimstroke_ink_swell_custom.lua"
|
||||
__TS__ClassExtends(modifier_grimstroke_ink_swell, BaseModifier)
|
||||
function modifier_grimstroke_ink_swell.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.counter = 0
|
||||
end
|
||||
function modifier_grimstroke_ink_swell.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_grimstroke_ink_swell.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_grimstroke_ink_swell.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
function modifier_grimstroke_ink_swell.prototype.OnCreated(self)
|
||||
if IsClient() then
|
||||
return
|
||||
end
|
||||
local interval = self:GetAbility():GetSpecialValueFor("tick_rate")
|
||||
local radius = self:GetAbility():GetSpecialValueFor("radius")
|
||||
self:StartIntervalThink(interval)
|
||||
local effectCast = ParticleManager:CreateParticle(
|
||||
"particles/units/heroes/hero_grimstroke/grimstroke_ink_swell_buff.vpcf",
|
||||
PATTACH_OVERHEAD_FOLLOW,
|
||||
self:GetParent()
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
effectCast,
|
||||
2,
|
||||
Vector(radius, radius, radius)
|
||||
)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
effectCast,
|
||||
3,
|
||||
self:GetParent(),
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
"attach_attack1",
|
||||
self:GetParent():GetOrigin(),
|
||||
true
|
||||
)
|
||||
self:AddParticle(
|
||||
effectCast,
|
||||
false,
|
||||
false,
|
||||
-1,
|
||||
false,
|
||||
true
|
||||
)
|
||||
EmitSoundOn(
|
||||
"Hero_Grimstroke.InkSwell.Target",
|
||||
self:GetParent()
|
||||
)
|
||||
end
|
||||
function modifier_grimstroke_ink_swell.prototype.OnDestroy(self)
|
||||
if IsClient() then
|
||||
return
|
||||
end
|
||||
local radius = self:GetAbility():GetSpecialValueFor("radius")
|
||||
local enemies = FindUnitsInRadius(
|
||||
self:GetParent():GetTeamNumber(),
|
||||
self:GetParent():GetOrigin(),
|
||||
nil,
|
||||
radius,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
bit.bor(DOTA_UNIT_TARGET_BASIC, DOTA_UNIT_TARGET_HERO),
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
local baseStun = self:GetAbility():GetSpecialValueFor("debuff_duration")
|
||||
local maxMultiplier = self:GetAbility():GetSpecialValueFor("max_bonus_multiplier")
|
||||
local baseDamage = self:GetAbility():GetSpecialValueFor("damage")
|
||||
local maxCounter = self:GetAbility():GetSpecialValueFor("max_counter")
|
||||
local multiplier = self.counter / maxCounter * maxMultiplier
|
||||
local finalMultiplier = math.min(maxMultiplier, multiplier + 1)
|
||||
local stun = baseStun * finalMultiplier
|
||||
local damage = baseDamage * finalMultiplier
|
||||
__TS__ArrayForEach(
|
||||
enemies,
|
||||
function(____, enemy)
|
||||
ApplyDamage({
|
||||
victim = enemy,
|
||||
attacker = self:GetParent(),
|
||||
damage = damage,
|
||||
damage_type = DAMAGE_TYPE_MAGICAL,
|
||||
ability = self:GetAbility()
|
||||
})
|
||||
enemy:AddNewModifier(
|
||||
self:GetParent(),
|
||||
self:GetAbility(),
|
||||
"modifier_stunned",
|
||||
{duration = stun}
|
||||
)
|
||||
end
|
||||
)
|
||||
StopSoundOn(
|
||||
"Hero_Grimstroke.InkSwell.Target",
|
||||
self:GetParent()
|
||||
)
|
||||
local effectCast = ParticleManager:CreateParticle(
|
||||
"particles/units/heroes/hero_grimstroke/grimstroke_ink_swell_aoe.vpcf",
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
self:GetParent()
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
effectCast,
|
||||
2,
|
||||
Vector(radius, radius, radius)
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(effectCast)
|
||||
EmitSoundOn(
|
||||
"Hero_Grimstroke.InkSwell.Stun",
|
||||
self:GetParent()
|
||||
)
|
||||
end
|
||||
function modifier_grimstroke_ink_swell.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE}
|
||||
end
|
||||
function modifier_grimstroke_ink_swell.prototype.GetModifierMoveSpeedBonus_Percentage(self)
|
||||
return self:GetAbility():GetSpecialValueFor("movespeed_bonus_pct")
|
||||
end
|
||||
function modifier_grimstroke_ink_swell.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_ATTACK_IMMUNE] = true, [MODIFIER_STATE_DISARMED] = true, [MODIFIER_STATE_SILENCED] = true}
|
||||
end
|
||||
function modifier_grimstroke_ink_swell.prototype.OnIntervalThink(self)
|
||||
if IsClient() then
|
||||
return
|
||||
end
|
||||
local maxCounter = self:GetAbility():GetSpecialValueFor("max_counter")
|
||||
local radius = self:GetAbility():GetSpecialValueFor("radius")
|
||||
local maxMultiplier = self:GetAbility():GetSpecialValueFor("max_bonus_multiplier")
|
||||
local multiplier = self.counter / maxCounter * maxMultiplier
|
||||
local damagePerTick = self:GetAbility():GetSpecialValueFor("tick_dps_tooltip") * self:GetAbility():GetSpecialValueFor("tick_rate")
|
||||
local damage = damagePerTick * math.min(maxMultiplier, multiplier + 1)
|
||||
local enemies = FindUnitsInRadius(
|
||||
self:GetParent():GetTeamNumber(),
|
||||
self:GetParent():GetOrigin(),
|
||||
nil,
|
||||
radius,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
DOTA_UNIT_TARGET_ALL,
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
__TS__ArrayForEach(
|
||||
enemies,
|
||||
function(____, enemy)
|
||||
ApplyDamage({
|
||||
victim = enemy,
|
||||
attacker = self:GetParent(),
|
||||
damage = damage,
|
||||
damage_type = DAMAGE_TYPE_MAGICAL,
|
||||
ability = self:GetAbility()
|
||||
})
|
||||
self.counter = self.counter + 1
|
||||
local effectCast = ParticleManager:CreateParticle(
|
||||
"particles/units/heroes/hero_grimstroke/grimstroke_ink_swell_tick_damage.vpcf",
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
self:GetParent()
|
||||
)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
effectCast,
|
||||
0,
|
||||
self:GetParent(),
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
"attach_hitloc",
|
||||
Vector(0, 0, 0),
|
||||
true
|
||||
)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
effectCast,
|
||||
1,
|
||||
enemy,
|
||||
PATTACH_POINT_FOLLOW,
|
||||
"attach_hitloc",
|
||||
Vector(0, 0, 0),
|
||||
true
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(effectCast)
|
||||
EmitSoundOn("Hero_Grimstroke.InkSwell.Damage", enemy)
|
||||
end
|
||||
)
|
||||
end
|
||||
modifier_grimstroke_ink_swell = __TS__Decorate(
|
||||
modifier_grimstroke_ink_swell,
|
||||
modifier_grimstroke_ink_swell,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_grimstroke_ink_swell"}
|
||||
)
|
||||
____exports.modifier_grimstroke_ink_swell = modifier_grimstroke_ink_swell
|
||||
return ____exports
|
||||
@@ -0,0 +1,316 @@
|
||||
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 BaseModifierMotionHorizontal = ____dota_ts_adapter.BaseModifierMotionHorizontal
|
||||
local registerAbility = ____dota_ts_adapter.registerAbility
|
||||
local registerModifier = ____dota_ts_adapter.registerModifier
|
||||
____exports.grimstroke_phantoms_embrace_custom = __TS__Class()
|
||||
local grimstroke_phantoms_embrace_custom = ____exports.grimstroke_phantoms_embrace_custom
|
||||
grimstroke_phantoms_embrace_custom.name = "grimstroke_phantoms_embrace_custom"
|
||||
grimstroke_phantoms_embrace_custom.____file_path = "scripts/vscripts/abilities/heroes/grimstroke/grimstroke_phantoms_embrace_custom.lua"
|
||||
__TS__ClassExtends(grimstroke_phantoms_embrace_custom, BaseAbility)
|
||||
function grimstroke_phantoms_embrace_custom.prototype.____constructor(self, ...)
|
||||
BaseAbility.prototype.____constructor(self, ...)
|
||||
self.returned = false
|
||||
end
|
||||
function grimstroke_phantoms_embrace_custom.prototype.OnSpellStart(self)
|
||||
local target = self:GetCursorTarget()
|
||||
if not target then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local spawnPos = caster:GetOrigin() + caster:GetForwardVector() * 150
|
||||
self.returned = false
|
||||
local phantom = CreateUnitByName(
|
||||
"npc_dota_grimstroke_ink_creature",
|
||||
spawnPos,
|
||||
true,
|
||||
nil,
|
||||
nil,
|
||||
caster:GetTeamNumber()
|
||||
)
|
||||
phantom:AddNewModifier(
|
||||
caster,
|
||||
self,
|
||||
____exports.modifier_grimstroke_phantoms_embrace_custom_thinker.name,
|
||||
{target = target:entindex()}
|
||||
)
|
||||
local effectCast = ParticleManager:CreateParticle("particles/units/heroes/hero_grimstroke/grimstroke_cast_phantom.vpcf", PATTACH_ABSORIGIN_FOLLOW, caster)
|
||||
ParticleManager:ReleaseParticleIndex(effectCast)
|
||||
self:SetActivated(false)
|
||||
EmitSoundOn("Hero_Grimstroke.InkCreature.Cast", caster)
|
||||
end
|
||||
function grimstroke_phantoms_embrace_custom.prototype.OnProjectileHit(self, _target)
|
||||
local caster = self:GetCaster()
|
||||
self:EndCooldown()
|
||||
self:SetActivated(true)
|
||||
self.returned = true
|
||||
Timers:CreateTimer(
|
||||
self:GetSpecialValueFor("return_time_free"),
|
||||
function()
|
||||
self.returned = false
|
||||
return nil
|
||||
end
|
||||
)
|
||||
EmitSoundOn("Hero_Grimstroke.InkCreature.Returned", caster)
|
||||
end
|
||||
grimstroke_phantoms_embrace_custom = __TS__Decorate(
|
||||
grimstroke_phantoms_embrace_custom,
|
||||
grimstroke_phantoms_embrace_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "grimstroke_phantoms_embrace_custom"}
|
||||
)
|
||||
____exports.grimstroke_phantoms_embrace_custom = grimstroke_phantoms_embrace_custom
|
||||
____exports.modifier_grimstroke_phantoms_embrace_custom_thinker = __TS__Class()
|
||||
local modifier_grimstroke_phantoms_embrace_custom_thinker = ____exports.modifier_grimstroke_phantoms_embrace_custom_thinker
|
||||
modifier_grimstroke_phantoms_embrace_custom_thinker.name = "modifier_grimstroke_phantoms_embrace_custom_thinker"
|
||||
modifier_grimstroke_phantoms_embrace_custom_thinker.____file_path = "scripts/vscripts/abilities/heroes/grimstroke/grimstroke_phantoms_embrace_custom.lua"
|
||||
__TS__ClassExtends(modifier_grimstroke_phantoms_embrace_custom_thinker, BaseModifierMotionHorizontal)
|
||||
function modifier_grimstroke_phantoms_embrace_custom_thinker.prototype.____constructor(self, ...)
|
||||
BaseModifierMotionHorizontal.prototype.____constructor(self, ...)
|
||||
self.latchOffset = 80
|
||||
self.latchDuration = 0
|
||||
self.speed = 0
|
||||
self.tick = 0
|
||||
self.latching = false
|
||||
end
|
||||
function modifier_grimstroke_phantoms_embrace_custom_thinker.prototype.OnCreated(self, params)
|
||||
if IsClient() then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
self.target = EntIndexToHScript(params.target)
|
||||
self.speed = ability:GetSpecialValueFor("speed")
|
||||
self.latchDuration = ability:GetSpecialValueFor("latch_duration")
|
||||
self.tick = ability:GetSpecialValueFor("tick")
|
||||
if not self:ApplyHorizontalMotionController() then
|
||||
self:Destroy()
|
||||
end
|
||||
end
|
||||
function modifier_grimstroke_phantoms_embrace_custom_thinker.prototype.OnDestroy(self)
|
||||
if IsClient() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
parent:InterruptMotionControllers(true)
|
||||
self.target:RemoveModifierByName(____exports.modifier_grimstroke_phantoms_embrace_custom_debuff.name)
|
||||
local effectCast = ParticleManager:CreateParticle("particles/units/heroes/hero_grimstroke/grimstroke_phantom_death.vpcf", PATTACH_ABSORIGIN_FOLLOW, parent)
|
||||
ParticleManager:ReleaseParticleIndex(effectCast)
|
||||
EmitSoundOn("Hero_Grimstroke.InkCreature.Death", parent)
|
||||
if not self.latching then
|
||||
local ____opt_0 = self:GetAbility()
|
||||
if ____opt_0 ~= nil then
|
||||
____opt_0:SetActivated(true)
|
||||
end
|
||||
end
|
||||
parent:ForceKill(false)
|
||||
end
|
||||
function modifier_grimstroke_phantoms_embrace_custom_thinker.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_INVULNERABLE] = true, [MODIFIER_STATE_NO_HEALTH_BAR] = true}
|
||||
end
|
||||
function modifier_grimstroke_phantoms_embrace_custom_thinker.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_OVERRIDE_ANIMATION}
|
||||
end
|
||||
function modifier_grimstroke_phantoms_embrace_custom_thinker.prototype.GetOverrideAnimation(self)
|
||||
local ____table_latching_2
|
||||
if self.latching then
|
||||
____table_latching_2 = ACT_DOTA_CAPTURE
|
||||
else
|
||||
____table_latching_2 = ACT_DOTA_RUN
|
||||
end
|
||||
return ____table_latching_2
|
||||
end
|
||||
function modifier_grimstroke_phantoms_embrace_custom_thinker.prototype.UpdateHorizontalMotion(self, me, dt)
|
||||
if self.latching then
|
||||
self:latch(me, dt)
|
||||
return
|
||||
end
|
||||
if (self.target:GetOrigin() - self:GetParent():GetOrigin()):Length2D() < self.latchOffset then
|
||||
self:setLatching()
|
||||
return
|
||||
end
|
||||
self:charge(me, dt)
|
||||
end
|
||||
function modifier_grimstroke_phantoms_embrace_custom_thinker.prototype.OnHorizontalMotionInterrupted(self)
|
||||
if IsServer() then
|
||||
self:Destroy()
|
||||
end
|
||||
end
|
||||
function modifier_grimstroke_phantoms_embrace_custom_thinker.prototype.charge(self, _me, dt)
|
||||
local parent = self:GetParent()
|
||||
local point = parent:GetOrigin()
|
||||
local targetPoint = self.target:GetOrigin()
|
||||
local direction = targetPoint - point
|
||||
direction.z = 0
|
||||
local target = point + direction:Normalized() * (self.speed * dt)
|
||||
parent:SetOrigin(target)
|
||||
parent:FaceTowards(targetPoint)
|
||||
end
|
||||
function modifier_grimstroke_phantoms_embrace_custom_thinker.prototype.latch(self, _me, _dt)
|
||||
local parent = self:GetParent()
|
||||
local target = self.target:GetOrigin() + self.target:GetForwardVector() * self.latchOffset
|
||||
parent:SetOrigin(target)
|
||||
parent:FaceTowards(self.target:GetOrigin())
|
||||
end
|
||||
function modifier_grimstroke_phantoms_embrace_custom_thinker.prototype.setLatching(self)
|
||||
if not self.target:IsAlive() then
|
||||
self:Destroy()
|
||||
return
|
||||
end
|
||||
self.latching = true
|
||||
self:SetStackCount(1)
|
||||
self:SetDuration(self.latchDuration, false)
|
||||
____exports.modifier_grimstroke_phantoms_embrace_custom_debuff:apply(
|
||||
self.target,
|
||||
self:GetCaster(),
|
||||
self:GetAbility(),
|
||||
{phantom = self:GetParent():entindex()}
|
||||
)
|
||||
EmitSoundOn(
|
||||
"Hero_Grimstroke.InkCreature.Attach",
|
||||
self:GetParent()
|
||||
)
|
||||
end
|
||||
modifier_grimstroke_phantoms_embrace_custom_thinker = __TS__Decorate(
|
||||
modifier_grimstroke_phantoms_embrace_custom_thinker,
|
||||
modifier_grimstroke_phantoms_embrace_custom_thinker,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_grimstroke_phantoms_embrace_custom_thinker"}
|
||||
)
|
||||
____exports.modifier_grimstroke_phantoms_embrace_custom_thinker = modifier_grimstroke_phantoms_embrace_custom_thinker
|
||||
____exports.modifier_grimstroke_phantoms_embrace_custom_debuff = __TS__Class()
|
||||
local modifier_grimstroke_phantoms_embrace_custom_debuff = ____exports.modifier_grimstroke_phantoms_embrace_custom_debuff
|
||||
modifier_grimstroke_phantoms_embrace_custom_debuff.name = "modifier_grimstroke_phantoms_embrace_custom_debuff"
|
||||
modifier_grimstroke_phantoms_embrace_custom_debuff.____file_path = "scripts/vscripts/abilities/heroes/grimstroke/grimstroke_phantoms_embrace_custom.lua"
|
||||
__TS__ClassExtends(modifier_grimstroke_phantoms_embrace_custom_debuff, BaseModifier)
|
||||
function modifier_grimstroke_phantoms_embrace_custom_debuff.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.tick = 0
|
||||
self.damage = 0
|
||||
self.radiusFind = 0
|
||||
self.damageIncoming = 0
|
||||
end
|
||||
function modifier_grimstroke_phantoms_embrace_custom_debuff.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_grimstroke_phantoms_embrace_custom_debuff.prototype.RemoveOnDeath(self)
|
||||
return true
|
||||
end
|
||||
function modifier_grimstroke_phantoms_embrace_custom_debuff.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_INCOMING_DAMAGE_PERCENTAGE}
|
||||
end
|
||||
function modifier_grimstroke_phantoms_embrace_custom_debuff.prototype.GetModifierIncomingDamage_Percentage(self, event)
|
||||
if event.target == self:GetParent() and event.attacker == self:GetCaster() then
|
||||
return self.damageIncoming
|
||||
end
|
||||
return 0
|
||||
end
|
||||
function modifier_grimstroke_phantoms_embrace_custom_debuff.prototype.OnCreated(self, params)
|
||||
local ability = self:GetAbility()
|
||||
self.tick = ability:GetSpecialValueFor("tick")
|
||||
self.damage = ability:GetSpecialValueFor("damage")
|
||||
self.radiusFind = ability:GetSpecialValueFor("radius_find")
|
||||
self.damageIncoming = ability:GetSpecialValueFor("damage_incoming")
|
||||
if IsClient() then
|
||||
return
|
||||
end
|
||||
self.phantom = EntIndexToHScript(params.phantom)
|
||||
local parent = self:GetParent()
|
||||
local effectCast = ParticleManager:CreateParticle("particles/units/heroes/hero_grimstroke/grimstroke_phantom_ambient.vpcf", PATTACH_ABSORIGIN_FOLLOW, parent)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
effectCast,
|
||||
0,
|
||||
parent,
|
||||
PATTACH_POINT_FOLLOW,
|
||||
"attach_hitloc",
|
||||
Vector(0, 0, 0),
|
||||
true
|
||||
)
|
||||
self:AddParticle(
|
||||
effectCast,
|
||||
false,
|
||||
false,
|
||||
-1,
|
||||
false,
|
||||
false
|
||||
)
|
||||
EmitSoundOn("Hero_Grimstroke.InkCreature.Spawn", parent)
|
||||
self:StartIntervalThink(self.tick)
|
||||
end
|
||||
function modifier_grimstroke_phantoms_embrace_custom_debuff.prototype.OnDestroy(self)
|
||||
if IsClient() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
self.phantom:ForceKill(false)
|
||||
if parent:IsAlive() then
|
||||
ability:SetActivated(true)
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local enemy = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
parent:GetOrigin(),
|
||||
nil,
|
||||
self.radiusFind,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC),
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_CLOSEST,
|
||||
false
|
||||
)[1]
|
||||
if enemy then
|
||||
UTIL_Remove(self.phantom)
|
||||
local spawnPos = parent:GetOrigin() + parent:GetForwardVector() * 150
|
||||
local phantom = CreateUnitByName(
|
||||
"npc_dota_grimstroke_ink_creature",
|
||||
spawnPos,
|
||||
true,
|
||||
nil,
|
||||
nil,
|
||||
caster:GetTeamNumber()
|
||||
)
|
||||
phantom:AddNewModifier(
|
||||
caster,
|
||||
ability,
|
||||
____exports.modifier_grimstroke_phantoms_embrace_custom_thinker.name,
|
||||
{target = enemy:entindex()}
|
||||
)
|
||||
return
|
||||
end
|
||||
ProjectileManager:CreateTrackingProjectile({
|
||||
Target = caster,
|
||||
Source = parent,
|
||||
Ability = ability,
|
||||
EffectName = "particles/units/heroes/hero_grimstroke/grimstroke_phantom_return.vpcf",
|
||||
iMoveSpeed = ability:GetSpecialValueFor("speed"),
|
||||
bDodgeable = true
|
||||
})
|
||||
end
|
||||
function modifier_grimstroke_phantoms_embrace_custom_debuff.prototype.OnIntervalThink(self)
|
||||
if IsClient() then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
ApplyDamage({
|
||||
victim = self:GetParent(),
|
||||
attacker = self:GetCaster(),
|
||||
damage = self.damage * self.tick,
|
||||
damage_type = ability:GetAbilityDamageType(),
|
||||
ability = ability
|
||||
})
|
||||
end
|
||||
modifier_grimstroke_phantoms_embrace_custom_debuff = __TS__Decorate(
|
||||
modifier_grimstroke_phantoms_embrace_custom_debuff,
|
||||
modifier_grimstroke_phantoms_embrace_custom_debuff,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_grimstroke_phantoms_embrace_custom_debuff"}
|
||||
)
|
||||
____exports.modifier_grimstroke_phantoms_embrace_custom_debuff = modifier_grimstroke_phantoms_embrace_custom_debuff
|
||||
return ____exports
|
||||
@@ -0,0 +1,315 @@
|
||||
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
|
||||
____exports.grimstroke_stroke_of_fate_custom = __TS__Class()
|
||||
local grimstroke_stroke_of_fate_custom = ____exports.grimstroke_stroke_of_fate_custom
|
||||
grimstroke_stroke_of_fate_custom.name = "grimstroke_stroke_of_fate_custom"
|
||||
grimstroke_stroke_of_fate_custom.____file_path = "scripts/vscripts/abilities/heroes/grimstroke/grimstroke_stroke_of_fate_custom.lua"
|
||||
__TS__ClassExtends(grimstroke_stroke_of_fate_custom, BaseAbility)
|
||||
function grimstroke_stroke_of_fate_custom.prototype.____constructor(self, ...)
|
||||
BaseAbility.prototype.____constructor(self, ...)
|
||||
self.activeProj = {}
|
||||
self.bentWaveId = 0
|
||||
self.projectileWaveId = {}
|
||||
self.waveState = {}
|
||||
end
|
||||
function grimstroke_stroke_of_fate_custom.prototype.GetVectorTargetRange(self)
|
||||
return self:GetCastRange(
|
||||
self:GetCaster():GetAbsOrigin(),
|
||||
nil
|
||||
)
|
||||
end
|
||||
function grimstroke_stroke_of_fate_custom.prototype.GetVectorTargetStartRadius(self)
|
||||
return self:GetSpecialValueFor("start_radius")
|
||||
end
|
||||
function grimstroke_stroke_of_fate_custom.prototype.GetVectorTargetEndRadius(self)
|
||||
return self:GetSpecialValueFor("end_radius")
|
||||
end
|
||||
function grimstroke_stroke_of_fate_custom.prototype.GetVectorPosition(self)
|
||||
local ____self_vectorTargetPosition_0 = self.vectorTargetPosition
|
||||
if ____self_vectorTargetPosition_0 == nil then
|
||||
____self_vectorTargetPosition_0 = self:GetCursorPosition()
|
||||
end
|
||||
return ____self_vectorTargetPosition_0
|
||||
end
|
||||
function grimstroke_stroke_of_fate_custom.prototype.GetVector2Position(self)
|
||||
local ____self_vectorTargetPosition2_1 = self.vectorTargetPosition2
|
||||
if ____self_vectorTargetPosition2_1 == nil then
|
||||
____self_vectorTargetPosition2_1 = self:GetCursorPosition()
|
||||
end
|
||||
return ____self_vectorTargetPosition2_1
|
||||
end
|
||||
function grimstroke_stroke_of_fate_custom.prototype.GetVectorDirection(self)
|
||||
local ____self_vectorTargetDirection_2 = self.vectorTargetDirection
|
||||
if ____self_vectorTargetDirection_2 == nil then
|
||||
____self_vectorTargetDirection_2 = self:GetCaster():GetForwardVector()
|
||||
end
|
||||
return ____self_vectorTargetDirection_2
|
||||
end
|
||||
function grimstroke_stroke_of_fate_custom.prototype.UpdateVectorValues(self)
|
||||
CustomNetTables:SetTableValue(
|
||||
"vector_targeting",
|
||||
tostring(self:entindex()),
|
||||
{
|
||||
startWidth = self:GetVectorTargetStartRadius(),
|
||||
endWidth = self:GetVectorTargetEndRadius(),
|
||||
castLength = self:GetVectorTargetRange(),
|
||||
dual = false,
|
||||
ignoreArrow = false
|
||||
}
|
||||
)
|
||||
end
|
||||
function grimstroke_stroke_of_fate_custom.prototype.IsDualVectorDirection(self)
|
||||
return true
|
||||
end
|
||||
function grimstroke_stroke_of_fate_custom.prototype.IgnoreVectorArrowWidth(self)
|
||||
return true
|
||||
end
|
||||
function grimstroke_stroke_of_fate_custom.prototype.GetCastRange(self, _location, _target)
|
||||
return self:GetSpecialValueFor("range") > 0 and self:GetSpecialValueFor("range") or BaseAbility.prototype.GetCastRange(self, _location, _target)
|
||||
end
|
||||
function grimstroke_stroke_of_fate_custom.prototype.OnAbilityPhaseStart(self)
|
||||
local particleCast = "particles/units/heroes/hero_grimstroke/grimstroke_cast2_ground.vpcf"
|
||||
local soundPrecast = "Hero_Grimstroke.DarkArtistry.PreCastPoint"
|
||||
self.effectCast = ParticleManager:CreateParticle(
|
||||
particleCast,
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
self:GetCaster()
|
||||
)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
self.effectCast,
|
||||
0,
|
||||
self:GetCaster(),
|
||||
PATTACH_POINT_FOLLOW,
|
||||
"attach_attack2",
|
||||
Vector(0, 1, 1),
|
||||
true
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(self.effectCast)
|
||||
EmitSoundOn(
|
||||
soundPrecast,
|
||||
self:GetCaster()
|
||||
)
|
||||
return true
|
||||
end
|
||||
function grimstroke_stroke_of_fate_custom.prototype.OnAbilityPhaseInterrupted(self)
|
||||
local soundPrecast = "Hero_Grimstroke.DarkArtistry.PreCastPoint"
|
||||
if self.effectCast then
|
||||
ParticleManager:DestroyParticle(self.effectCast, true)
|
||||
end
|
||||
StopSoundOn(
|
||||
soundPrecast,
|
||||
self:GetCaster()
|
||||
)
|
||||
end
|
||||
function grimstroke_stroke_of_fate_custom.prototype.OnVectorCastStart(self, vStartLocation, vDirection)
|
||||
local caster = self:GetCaster()
|
||||
local castOrigin = caster:GetAbsOrigin()
|
||||
local startPoint = vStartLocation
|
||||
local bendDirection = vDirection
|
||||
local projectileName = "particles/units/heroes/hero_grimstroke/grimstroke_darkartistry_proj.vpcf"
|
||||
local secondSegmentDistance = self:GetSpecialValueFor("range")
|
||||
local startRadius = self:GetSpecialValueFor("start_radius")
|
||||
local endRadius = self:GetSpecialValueFor("end_radius")
|
||||
local speed = self:GetSpecialValueFor("projectile_speed")
|
||||
bendDirection.z = 0
|
||||
if bendDirection:Length2D() < 1 then
|
||||
bendDirection = caster:GetForwardVector()
|
||||
bendDirection.z = 0
|
||||
end
|
||||
bendDirection = bendDirection:Normalized()
|
||||
local firstSegmentDirection = startPoint - castOrigin
|
||||
firstSegmentDirection.z = 0
|
||||
if firstSegmentDirection:Length2D() < 1 then
|
||||
firstSegmentDirection = caster:GetForwardVector()
|
||||
firstSegmentDirection.z = 0
|
||||
end
|
||||
local firstSegmentDistance = firstSegmentDirection:Length2D()
|
||||
firstSegmentDirection = firstSegmentDirection:Normalized()
|
||||
self.bentWaveId = self.bentWaveId + 1
|
||||
local waveId = self.bentWaveId
|
||||
self.waveState[waveId] = {hitCounter = 0, hitTargets = {}}
|
||||
if firstSegmentDistance > 1 then
|
||||
local firstProjectile = ProjectileManager:CreateLinearProjectile({
|
||||
Source = caster,
|
||||
Ability = self,
|
||||
vSpawnOrigin = castOrigin,
|
||||
iUnitTargetTeam = DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
iUnitTargetFlags = DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
iUnitTargetType = bit.bor(DOTA_UNIT_TARGET_BASIC, DOTA_UNIT_TARGET_HERO),
|
||||
EffectName = projectileName,
|
||||
fDistance = firstSegmentDistance,
|
||||
fStartRadius = startRadius,
|
||||
fEndRadius = endRadius,
|
||||
vVelocity = firstSegmentDirection * speed,
|
||||
bHasFrontalCone = false,
|
||||
bProvidesVision = false
|
||||
})
|
||||
self.projectileWaveId[firstProjectile] = waveId
|
||||
end
|
||||
local secondDelay = firstSegmentDistance > 1 and firstSegmentDistance / speed or 0
|
||||
Timers:CreateTimer(
|
||||
secondDelay,
|
||||
function()
|
||||
if self.bentWaveId ~= waveId or not caster or caster:IsNull() then
|
||||
return nil
|
||||
end
|
||||
local secondProjectile = ProjectileManager:CreateLinearProjectile({
|
||||
Source = caster,
|
||||
Ability = self,
|
||||
vSpawnOrigin = startPoint,
|
||||
iUnitTargetTeam = DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
iUnitTargetFlags = DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
iUnitTargetType = bit.bor(DOTA_UNIT_TARGET_BASIC, DOTA_UNIT_TARGET_HERO),
|
||||
EffectName = projectileName,
|
||||
fDistance = secondSegmentDistance,
|
||||
fStartRadius = startRadius,
|
||||
fEndRadius = endRadius,
|
||||
vVelocity = bendDirection * speed,
|
||||
bHasFrontalCone = false,
|
||||
bProvidesVision = false
|
||||
})
|
||||
self.projectileWaveId[secondProjectile] = waveId
|
||||
return nil
|
||||
end
|
||||
)
|
||||
EmitSoundOn("Hero_Grimstroke.DarkArtistry.Cast", caster)
|
||||
EmitSoundOn("Hero_Grimstroke.DarkArtistry.Cast.Layer", caster)
|
||||
end
|
||||
function grimstroke_stroke_of_fate_custom.prototype.OnSpellStart(self)
|
||||
local caster = self:GetCaster()
|
||||
local cursorPoint = self:GetCursorPosition()
|
||||
local fallbackDirection = cursorPoint - caster:GetAbsOrigin()
|
||||
self:OnVectorCastStart(cursorPoint, fallbackDirection)
|
||||
end
|
||||
function grimstroke_stroke_of_fate_custom.prototype.OnProjectileHitHandle(self, target, _location, projectileHandle)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local waveId = self.projectileWaveId[projectileHandle]
|
||||
local ____temp_3
|
||||
if waveId ~= nil then
|
||||
____temp_3 = self.waveState[waveId]
|
||||
else
|
||||
____temp_3 = nil
|
||||
end
|
||||
local state = ____temp_3
|
||||
if not target then
|
||||
self.activeProj[projectileHandle] = nil
|
||||
self.projectileWaveId[projectileHandle] = nil
|
||||
return true
|
||||
end
|
||||
if state then
|
||||
local targetId = target:entindex()
|
||||
if state.hitTargets[targetId] then
|
||||
return
|
||||
end
|
||||
state.hitTargets[targetId] = true
|
||||
elseif not self.activeProj[projectileHandle] then
|
||||
self.activeProj[projectileHandle] = 0
|
||||
end
|
||||
local multiplier = state and state.hitCounter or (self.activeProj[projectileHandle] or 0)
|
||||
local baseDamage = self:GetAbilityDamage()
|
||||
local bonusDamage = self:GetSpecialValueFor("bonus_damage_per_target")
|
||||
local duration = self:GetSpecialValueFor("duration")
|
||||
ApplyDamage({
|
||||
victim = target,
|
||||
attacker = self:GetCaster(),
|
||||
damage = baseDamage + bonusDamage * multiplier,
|
||||
damage_type = DAMAGE_TYPE_MAGICAL,
|
||||
ability = self
|
||||
})
|
||||
target:AddNewModifier(
|
||||
self:GetCaster(),
|
||||
self,
|
||||
____exports.modifier_grimstroke_stroke_of_fate_root.name,
|
||||
{duration = duration}
|
||||
)
|
||||
local effectCast = ParticleManager:CreateParticle("particles/units/heroes/hero_grimstroke/grimstroke_darkartistry_dmg.vpcf", PATTACH_ABSORIGIN_FOLLOW, target)
|
||||
ParticleManager:ReleaseParticleIndex(effectCast)
|
||||
if state then
|
||||
state.hitCounter = state.hitCounter + 1
|
||||
else
|
||||
local ____self_activeProj_4, ____projectileHandle_5 = self.activeProj, projectileHandle
|
||||
____self_activeProj_4[____projectileHandle_5] = ____self_activeProj_4[____projectileHandle_5] + 1
|
||||
end
|
||||
EmitSoundOn(
|
||||
target:IsCreep() and "Hero_Grimstroke.DarkArtistry.Damage.Creep" or "Hero_Grimstroke.DarkArtistry.Damage",
|
||||
target
|
||||
)
|
||||
end
|
||||
grimstroke_stroke_of_fate_custom = __TS__Decorate(
|
||||
grimstroke_stroke_of_fate_custom,
|
||||
grimstroke_stroke_of_fate_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "grimstroke_stroke_of_fate_custom"}
|
||||
)
|
||||
____exports.grimstroke_stroke_of_fate_custom = grimstroke_stroke_of_fate_custom
|
||||
____exports.modifier_grimstroke_stroke_of_fate_root = __TS__Class()
|
||||
local modifier_grimstroke_stroke_of_fate_root = ____exports.modifier_grimstroke_stroke_of_fate_root
|
||||
modifier_grimstroke_stroke_of_fate_root.name = "modifier_grimstroke_stroke_of_fate_root"
|
||||
modifier_grimstroke_stroke_of_fate_root.____file_path = "scripts/vscripts/abilities/heroes/grimstroke/grimstroke_stroke_of_fate_custom.lua"
|
||||
__TS__ClassExtends(modifier_grimstroke_stroke_of_fate_root, BaseModifier)
|
||||
function modifier_grimstroke_stroke_of_fate_root.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_ROOTED] = true}
|
||||
end
|
||||
function modifier_grimstroke_stroke_of_fate_root.prototype.OnCreated(self)
|
||||
self:StartIntervalThink(0.5)
|
||||
if IsClient() then
|
||||
return
|
||||
end
|
||||
self.effectCast = ParticleManager:CreateParticle(
|
||||
"particles/units/heroes/hero_grimstroke/grimstroke_soulchain_debuff.vpcf",
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
self:GetParent()
|
||||
)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
self.effectCast,
|
||||
2,
|
||||
self:GetParent(),
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
"attach_hitloc",
|
||||
self:GetParent():GetOrigin(),
|
||||
true
|
||||
)
|
||||
self:AddParticle(
|
||||
self.effectCast,
|
||||
false,
|
||||
false,
|
||||
-1,
|
||||
false,
|
||||
false
|
||||
)
|
||||
end
|
||||
function modifier_grimstroke_stroke_of_fate_root.prototype.OnDestroy(self)
|
||||
if self.effectCast ~= nil then
|
||||
ParticleManager:DestroyParticle(self.effectCast, false)
|
||||
end
|
||||
end
|
||||
function modifier_grimstroke_stroke_of_fate_root.prototype.OnIntervalThink(self)
|
||||
if IsClient() then
|
||||
return
|
||||
end
|
||||
ApplyDamage({
|
||||
victim = self:GetParent(),
|
||||
attacker = self:GetCaster(),
|
||||
damage = self:GetAbility():GetSpecialValueFor("interval_damage"),
|
||||
damage_type = DAMAGE_TYPE_MAGICAL,
|
||||
ability = self:GetAbility()
|
||||
})
|
||||
end
|
||||
modifier_grimstroke_stroke_of_fate_root = __TS__Decorate(
|
||||
modifier_grimstroke_stroke_of_fate_root,
|
||||
modifier_grimstroke_stroke_of_fate_root,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_grimstroke_stroke_of_fate_root"}
|
||||
)
|
||||
____exports.modifier_grimstroke_stroke_of_fate_root = modifier_grimstroke_stroke_of_fate_root
|
||||
return ____exports
|
||||
@@ -0,0 +1,487 @@
|
||||
local ____lualib = require("lualib_bundle")
|
||||
local __TS__Class = ____lualib.__TS__Class
|
||||
local __TS__ClassExtends = ____lualib.__TS__ClassExtends
|
||||
local __TS__ArrayFilter = ____lualib.__TS__ArrayFilter
|
||||
local __TS__Delete = ____lualib.__TS__Delete
|
||||
local __TS__ObjectAssign = ____lualib.__TS__ObjectAssign
|
||||
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 ____hoodwink_sharpshooter_custom = require("abilities.heroes.hoodwink.hoodwink_sharpshooter_custom")
|
||||
local hoodwink_sharpshooter_custom = ____hoodwink_sharpshooter_custom.hoodwink_sharpshooter_custom
|
||||
local ____luck = require("utils.luck")
|
||||
local rollLuckChance = ____luck.rollLuckChance
|
||||
____exports.hoodwink_acorn_shot_custom = __TS__Class()
|
||||
local hoodwink_acorn_shot_custom = ____exports.hoodwink_acorn_shot_custom
|
||||
hoodwink_acorn_shot_custom.name = "hoodwink_acorn_shot_custom"
|
||||
hoodwink_acorn_shot_custom.____file_path = "scripts/vscripts/abilities/heroes/hoodwink/hoodwink_acorn_shot_custom.lua"
|
||||
__TS__ClassExtends(hoodwink_acorn_shot_custom, BaseAbility)
|
||||
function hoodwink_acorn_shot_custom.prototype.getClosestTree(self, location, radius)
|
||||
local trees = GridNav:GetAllTreesAroundPoint(location, radius, true)
|
||||
if #trees == 0 then
|
||||
return nil
|
||||
end
|
||||
local bestTree = trees[1]
|
||||
local bestDist = bestTree:GetAbsOrigin():__sub(location):Length2D()
|
||||
do
|
||||
local i = 1
|
||||
while i < #trees do
|
||||
local dist = trees[i + 1]:GetAbsOrigin():__sub(location):Length2D()
|
||||
if dist < bestDist then
|
||||
bestDist = dist
|
||||
bestTree = trees[i + 1]
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
return bestTree
|
||||
end
|
||||
function hoodwink_acorn_shot_custom.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/tree_fx/tree_simple_explosion.vpcf", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_hoodwink/hoodwink_acorn_shot_tree.vpcf", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_hoodwink/hoodwink_acorn_shot_impact.vpcf", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_hoodwink/hoodwink_acorn_shot_slow.vpcf", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_hoodwink/hoodwink_acorn_shot_tracking.vpcf", context)
|
||||
end
|
||||
function hoodwink_acorn_shot_custom.prototype.GetIntrinsicModifierName(self)
|
||||
return ____exports.modifier_hoodwink_acorn_shot_custom_scepter.name
|
||||
end
|
||||
function hoodwink_acorn_shot_custom.prototype.OnSpellStart(self)
|
||||
local caster = self:GetCaster()
|
||||
local target = self:GetCursorTarget()
|
||||
local point = self:GetCursorPosition()
|
||||
local bounceCount = self:GetSpecialValueFor("bounce_count")
|
||||
local info = {
|
||||
Ability = self,
|
||||
Source = caster,
|
||||
iMoveSpeed = self:GetSpecialValueFor("projectile_speed"),
|
||||
bDodgeable = true,
|
||||
iSourceAttachment = DOTA_PROJECTILE_ATTACHMENT_HITLOCATION,
|
||||
bVisibleToEnemies = true,
|
||||
bProvidesVision = true,
|
||||
iVisionRadius = 200,
|
||||
iVisionTeamNumber = caster:GetTeamNumber(),
|
||||
EffectName = "particles/units/heroes/hero_hoodwink/hoodwink_acorn_shot_tracking.vpcf",
|
||||
ExtraData = {count = bounceCount, nextTree = 0}
|
||||
}
|
||||
caster:EmitSound("Hero_Hoodwink.AcornShot.Cast")
|
||||
if target then
|
||||
info.Target = target
|
||||
ProjectileManager:CreateTrackingProjectile(info)
|
||||
return
|
||||
end
|
||||
point.z = point.z + 40
|
||||
info.vTargetLoc = point
|
||||
ProjectileManager:CreateTrackingProjectile(info)
|
||||
end
|
||||
function hoodwink_acorn_shot_custom.prototype.ProjectileFindAndHitEnemy(self, target, location, data)
|
||||
local caster = self:GetCaster()
|
||||
local radius = self:GetSpecialValueFor("bounce_range")
|
||||
local bounceDelay = self:GetSpecialValueFor("bounce_delay")
|
||||
Timers:CreateTimer(
|
||||
bounceDelay,
|
||||
function()
|
||||
local enemies = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
location,
|
||||
nil,
|
||||
radius,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC),
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_CLOSEST,
|
||||
false
|
||||
)
|
||||
local canBounceTrees = self:GetSpecialValueFor("can_bounce_off_of_trees") > 0
|
||||
local candidates = __TS__ArrayFilter(
|
||||
enemies,
|
||||
function(____, unit) return unit:GetEntityIndex() ~= (target and target:GetEntityIndex()) end
|
||||
)
|
||||
local nonRepeated = __TS__ArrayFilter(
|
||||
candidates,
|
||||
function(____, unit) return unit:GetEntityIndex() ~= data.lastEnemy end
|
||||
)
|
||||
local enemy = nonRepeated[1] or candidates[1]
|
||||
if enemy ~= nil then
|
||||
__TS__Delete(data, "targetTree")
|
||||
local enemyInfo = {
|
||||
Ability = self,
|
||||
Target = enemy,
|
||||
EffectName = "particles/units/heroes/hero_hoodwink/hoodwink_acorn_shot_tracking.vpcf",
|
||||
iMoveSpeed = self:GetSpecialValueFor("projectile_speed") / 2,
|
||||
bDodgeable = true,
|
||||
iSourceAttachment = DOTA_PROJECTILE_ATTACHMENT_HITLOCATION,
|
||||
bVisibleToEnemies = true,
|
||||
bProvidesVision = true,
|
||||
iVisionRadius = 200,
|
||||
iVisionTeamNumber = caster:GetTeamNumber(),
|
||||
ExtraData = __TS__ObjectAssign({}, data, {nextTree = canBounceTrees and 1 or 0})
|
||||
}
|
||||
if target then
|
||||
enemyInfo.Source = target
|
||||
target:EmitSound("Hero_Hoodwink.AcornShot.Bounce")
|
||||
else
|
||||
enemyInfo.vSourceLoc = location
|
||||
end
|
||||
ProjectileManager:CreateTrackingProjectile(enemyInfo)
|
||||
return
|
||||
end
|
||||
local shouldGoTree = canBounceTrees and data.nextTree == 1
|
||||
if not shouldGoTree then
|
||||
return
|
||||
end
|
||||
local tree = self:getClosestTree(location, radius)
|
||||
if tree == nil then
|
||||
return
|
||||
end
|
||||
local treeLoc = tree:GetAbsOrigin()
|
||||
local treeTarget = CreateModifierThinker(
|
||||
self:GetCaster(),
|
||||
self,
|
||||
"modifier_kill",
|
||||
{duration = 1},
|
||||
treeLoc,
|
||||
self:GetCaster():GetTeamNumber(),
|
||||
false
|
||||
)
|
||||
local treeInfo = {
|
||||
Ability = self,
|
||||
Target = treeTarget,
|
||||
EffectName = "particles/units/heroes/hero_hoodwink/hoodwink_acorn_shot_tracking.vpcf",
|
||||
iMoveSpeed = self:GetSpecialValueFor("projectile_speed") / 2,
|
||||
bDodgeable = true,
|
||||
iSourceAttachment = DOTA_PROJECTILE_ATTACHMENT_HITLOCATION,
|
||||
bVisibleToEnemies = true,
|
||||
bProvidesVision = true,
|
||||
iVisionRadius = 200,
|
||||
iVisionTeamNumber = caster:GetTeamNumber(),
|
||||
ExtraData = __TS__ObjectAssign({}, data, {targetTree = 1, nextTree = 0})
|
||||
}
|
||||
if target then
|
||||
treeInfo.Source = target
|
||||
target:EmitSound("Hero_Hoodwink.AcornShot.Bounce")
|
||||
else
|
||||
treeInfo.vSourceLoc = location
|
||||
end
|
||||
ProjectileManager:CreateTrackingProjectile(treeInfo)
|
||||
end
|
||||
)
|
||||
end
|
||||
function hoodwink_acorn_shot_custom.prototype.OnProjectileHit_ExtraData(self, target, location, data)
|
||||
local caster = self:GetCaster()
|
||||
local hitLoc = target and target:GetAbsOrigin() or location
|
||||
if data.targetTree ~= 1 then
|
||||
if not target then
|
||||
CreateModifierThinker(
|
||||
caster,
|
||||
self,
|
||||
____exports.modifier_hoodwink_acorn_shot_custom_thinker_tree.name,
|
||||
{duration = self:GetSpecialValueFor("tree_duration")},
|
||||
hitLoc,
|
||||
caster:GetTeamNumber(),
|
||||
false
|
||||
)
|
||||
else
|
||||
if not target:TriggerSpellAbsorb(self) then
|
||||
local modifier = ____exports.modifier_hoodwink_acorn_shot_custom_bonus_damage:apply(caster, caster, self, {})
|
||||
caster:PerformAttack(
|
||||
target,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
)
|
||||
modifier:Destroy()
|
||||
local reduceCooldownUltimate = self:GetSpecialValueFor("reduce_cooldown_ultimate")
|
||||
if reduceCooldownUltimate > 0 then
|
||||
local ultimate = caster:FindAbilityByName(hoodwink_sharpshooter_custom.name)
|
||||
if ultimate and not ultimate:IsCooldownReady() then
|
||||
local time = ultimate:GetCooldownTimeRemaining()
|
||||
ultimate:EndCooldown()
|
||||
ultimate:StartCooldown(math.max(0, time - reduceCooldownUltimate))
|
||||
end
|
||||
end
|
||||
end
|
||||
target:EmitSound("Hero_Hoodwink.AcornShot.Target")
|
||||
local particle = ParticleManager:CreateParticle("particles/units/heroes/hero_hoodwink/hoodwink_acorn_shot_impact.vpcf", PATTACH_CUSTOMORIGIN, target)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
particle,
|
||||
0,
|
||||
target,
|
||||
PATTACH_POINT_FOLLOW,
|
||||
"attach_hitloc",
|
||||
target:GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(particle)
|
||||
____exports.modifier_hoodwink_acorn_shot_custom_debuff:apply(
|
||||
target,
|
||||
caster,
|
||||
self,
|
||||
{duration = self:GetSpecialValueFor("debuff_duration")}
|
||||
)
|
||||
data.lastEnemy = target:GetEntityIndex()
|
||||
data.nextTree = self:GetSpecialValueFor("can_bounce_off_of_trees") > 0 and 1 or 0
|
||||
end
|
||||
else
|
||||
EmitSoundOnLocationWithCaster(hitLoc, "Hero_Hoodwink.AcornShot.Target", caster)
|
||||
data.nextTree = 0
|
||||
end
|
||||
data.count = data.count - 1
|
||||
if data.count <= 0 then
|
||||
return
|
||||
end
|
||||
self:ProjectileFindAndHitEnemy(target, hitLoc, data)
|
||||
end
|
||||
hoodwink_acorn_shot_custom = __TS__Decorate(
|
||||
hoodwink_acorn_shot_custom,
|
||||
hoodwink_acorn_shot_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "hoodwink_acorn_shot_custom"}
|
||||
)
|
||||
____exports.hoodwink_acorn_shot_custom = hoodwink_acorn_shot_custom
|
||||
____exports.modifier_hoodwink_acorn_shot_custom_debuff = __TS__Class()
|
||||
local modifier_hoodwink_acorn_shot_custom_debuff = ____exports.modifier_hoodwink_acorn_shot_custom_debuff
|
||||
modifier_hoodwink_acorn_shot_custom_debuff.name = "modifier_hoodwink_acorn_shot_custom_debuff"
|
||||
modifier_hoodwink_acorn_shot_custom_debuff.____file_path = "scripts/vscripts/abilities/heroes/hoodwink/hoodwink_acorn_shot_custom.lua"
|
||||
__TS__ClassExtends(modifier_hoodwink_acorn_shot_custom_debuff, BaseModifier)
|
||||
function modifier_hoodwink_acorn_shot_custom_debuff.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.slow = 0
|
||||
end
|
||||
function modifier_hoodwink_acorn_shot_custom_debuff.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_hoodwink_acorn_shot_custom_debuff.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
function modifier_hoodwink_acorn_shot_custom_debuff.prototype.RemoveOnDeath(self)
|
||||
return true
|
||||
end
|
||||
function modifier_hoodwink_acorn_shot_custom_debuff.prototype.OnCreated(self)
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
self.slow = ability:GetSpecialValueFor("slow")
|
||||
end
|
||||
function modifier_hoodwink_acorn_shot_custom_debuff.prototype.GetEffectName(self)
|
||||
return "particles/units/heroes/hero_hoodwink/hoodwink_acorn_shot_slow.vpcf"
|
||||
end
|
||||
function modifier_hoodwink_acorn_shot_custom_debuff.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE}
|
||||
end
|
||||
function modifier_hoodwink_acorn_shot_custom_debuff.prototype.GetModifierMoveSpeedBonus_Percentage(self)
|
||||
return -self.slow
|
||||
end
|
||||
modifier_hoodwink_acorn_shot_custom_debuff = __TS__Decorate(
|
||||
modifier_hoodwink_acorn_shot_custom_debuff,
|
||||
modifier_hoodwink_acorn_shot_custom_debuff,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_hoodwink_acorn_shot_custom_debuff"}
|
||||
)
|
||||
____exports.modifier_hoodwink_acorn_shot_custom_debuff = modifier_hoodwink_acorn_shot_custom_debuff
|
||||
____exports.modifier_hoodwink_acorn_shot_custom_bonus_damage = __TS__Class()
|
||||
local modifier_hoodwink_acorn_shot_custom_bonus_damage = ____exports.modifier_hoodwink_acorn_shot_custom_bonus_damage
|
||||
modifier_hoodwink_acorn_shot_custom_bonus_damage.name = "modifier_hoodwink_acorn_shot_custom_bonus_damage"
|
||||
modifier_hoodwink_acorn_shot_custom_bonus_damage.____file_path = "scripts/vscripts/abilities/heroes/hoodwink/hoodwink_acorn_shot_custom.lua"
|
||||
__TS__ClassExtends(modifier_hoodwink_acorn_shot_custom_bonus_damage, BaseModifier)
|
||||
function modifier_hoodwink_acorn_shot_custom_bonus_damage.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.bonusDamage = 0
|
||||
self.damagePercentage = 0
|
||||
end
|
||||
function modifier_hoodwink_acorn_shot_custom_bonus_damage.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_hoodwink_acorn_shot_custom_bonus_damage.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_hoodwink_acorn_shot_custom_bonus_damage.prototype.IsPurgeException(self)
|
||||
return false
|
||||
end
|
||||
function modifier_hoodwink_acorn_shot_custom_bonus_damage.prototype.RemoveOnDeath(self)
|
||||
return false
|
||||
end
|
||||
function modifier_hoodwink_acorn_shot_custom_bonus_damage.prototype.OnCreated(self)
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
self.bonusDamage = ability:GetSpecialValueFor("acorn_shot_damage")
|
||||
self.damagePercentage = ability:GetSpecialValueFor("base_damage_pct") - 100
|
||||
end
|
||||
function modifier_hoodwink_acorn_shot_custom_bonus_damage.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_PREATTACK_BONUS_DAMAGE, MODIFIER_PROPERTY_DAMAGEOUTGOING_PERCENTAGE}
|
||||
end
|
||||
function modifier_hoodwink_acorn_shot_custom_bonus_damage.prototype.GetModifierPreAttack_BonusDamage(self)
|
||||
return self.bonusDamage
|
||||
end
|
||||
function modifier_hoodwink_acorn_shot_custom_bonus_damage.prototype.GetModifierDamageOutgoing_Percentage(self)
|
||||
return self.damagePercentage
|
||||
end
|
||||
modifier_hoodwink_acorn_shot_custom_bonus_damage = __TS__Decorate(
|
||||
modifier_hoodwink_acorn_shot_custom_bonus_damage,
|
||||
modifier_hoodwink_acorn_shot_custom_bonus_damage,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_hoodwink_acorn_shot_custom_bonus_damage"}
|
||||
)
|
||||
____exports.modifier_hoodwink_acorn_shot_custom_bonus_damage = modifier_hoodwink_acorn_shot_custom_bonus_damage
|
||||
____exports.modifier_hoodwink_acorn_shot_custom_thinker_tree = __TS__Class()
|
||||
local modifier_hoodwink_acorn_shot_custom_thinker_tree = ____exports.modifier_hoodwink_acorn_shot_custom_thinker_tree
|
||||
modifier_hoodwink_acorn_shot_custom_thinker_tree.name = "modifier_hoodwink_acorn_shot_custom_thinker_tree"
|
||||
modifier_hoodwink_acorn_shot_custom_thinker_tree.____file_path = "scripts/vscripts/abilities/heroes/hoodwink/hoodwink_acorn_shot_custom.lua"
|
||||
__TS__ClassExtends(modifier_hoodwink_acorn_shot_custom_thinker_tree, BaseModifier)
|
||||
function modifier_hoodwink_acorn_shot_custom_thinker_tree.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.treeOrigin = Vector(0, 0, 0)
|
||||
end
|
||||
function modifier_hoodwink_acorn_shot_custom_thinker_tree.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_hoodwink_acorn_shot_custom_thinker_tree.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
local caster = self:GetCaster()
|
||||
if not ability or not caster then
|
||||
return
|
||||
end
|
||||
self.treeOrigin = parent:GetAbsOrigin()
|
||||
CreateTempTree(
|
||||
self.treeOrigin,
|
||||
ability:GetSpecialValueFor("tree_duration")
|
||||
)
|
||||
self.viewer = AddFOWViewer(
|
||||
caster:GetTeamNumber(),
|
||||
self.treeOrigin,
|
||||
ability:GetSpecialValueFor("tree_radius"),
|
||||
ability:GetSpecialValueFor("tree_duration"),
|
||||
false
|
||||
)
|
||||
local units = FindUnitsInRadius(
|
||||
parent:GetTeamNumber(),
|
||||
self.treeOrigin,
|
||||
nil,
|
||||
100,
|
||||
DOTA_UNIT_TARGET_TEAM_BOTH,
|
||||
DOTA_UNIT_TARGET_ALL,
|
||||
DOTA_UNIT_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES,
|
||||
FIND_CLOSEST,
|
||||
false
|
||||
)
|
||||
for ____, unit in ipairs(units) do
|
||||
FindClearSpaceForUnit(
|
||||
unit,
|
||||
unit:GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
end
|
||||
local particle2 = ParticleManager:CreateParticle("particles/tree_fx/tree_simple_explosion.vpcf", PATTACH_WORLDORIGIN, nil)
|
||||
ParticleManager:SetParticleControl(
|
||||
particle2,
|
||||
0,
|
||||
self.treeOrigin:__add(Vector(1, 0, 0))
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(particle2)
|
||||
end
|
||||
function modifier_hoodwink_acorn_shot_custom_thinker_tree.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
GridNav:DestroyTreesAroundPoint(
|
||||
self:GetParent():GetAbsOrigin(),
|
||||
10,
|
||||
true
|
||||
)
|
||||
local caster = self:GetCaster()
|
||||
if self.viewer and caster then
|
||||
RemoveFOWViewer(
|
||||
caster:GetTeamNumber(),
|
||||
self.viewer
|
||||
)
|
||||
self.viewer = nil
|
||||
end
|
||||
end
|
||||
modifier_hoodwink_acorn_shot_custom_thinker_tree = __TS__Decorate(
|
||||
modifier_hoodwink_acorn_shot_custom_thinker_tree,
|
||||
modifier_hoodwink_acorn_shot_custom_thinker_tree,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_hoodwink_acorn_shot_custom_thinker_tree"}
|
||||
)
|
||||
____exports.modifier_hoodwink_acorn_shot_custom_thinker_tree = modifier_hoodwink_acorn_shot_custom_thinker_tree
|
||||
____exports.modifier_hoodwink_acorn_shot_custom_scepter = __TS__Class()
|
||||
local modifier_hoodwink_acorn_shot_custom_scepter = ____exports.modifier_hoodwink_acorn_shot_custom_scepter
|
||||
modifier_hoodwink_acorn_shot_custom_scepter.name = "modifier_hoodwink_acorn_shot_custom_scepter"
|
||||
modifier_hoodwink_acorn_shot_custom_scepter.____file_path = "scripts/vscripts/abilities/heroes/hoodwink/hoodwink_acorn_shot_custom.lua"
|
||||
__TS__ClassExtends(modifier_hoodwink_acorn_shot_custom_scepter, BaseModifier)
|
||||
function modifier_hoodwink_acorn_shot_custom_scepter.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_hoodwink_acorn_shot_custom_scepter.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_hoodwink_acorn_shot_custom_scepter.prototype.IsPurgeException(self)
|
||||
return false
|
||||
end
|
||||
function modifier_hoodwink_acorn_shot_custom_scepter.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_EVENT_ON_ATTACK_LANDED}
|
||||
end
|
||||
function modifier_hoodwink_acorn_shot_custom_scepter.prototype.OnAttackLanded(self, event)
|
||||
local parent = self:GetParent()
|
||||
if parent:PassivesDisabled() then
|
||||
return
|
||||
end
|
||||
if event.attacker ~= parent or not parent:HasScepter() or event.no_attack_cooldown then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
if not parent:IsHero() then
|
||||
return
|
||||
end
|
||||
local scepterChancePct = ability:GetSpecialValueFor("scepter_chance")
|
||||
if rollLuckChance(nil, parent, scepterChancePct / 100) then
|
||||
local target = event.target
|
||||
if not target then
|
||||
return
|
||||
end
|
||||
local info = {
|
||||
Ability = ability,
|
||||
Source = parent,
|
||||
Target = target,
|
||||
EffectName = "particles/units/heroes/hero_hoodwink/hoodwink_acorn_shot_tracking.vpcf",
|
||||
iMoveSpeed = ability:GetSpecialValueFor("projectile_speed"),
|
||||
bDodgeable = true,
|
||||
iSourceAttachment = DOTA_PROJECTILE_ATTACHMENT_HITLOCATION,
|
||||
bVisibleToEnemies = true,
|
||||
bProvidesVision = true,
|
||||
iVisionRadius = 200,
|
||||
iVisionTeamNumber = parent:GetTeamNumber(),
|
||||
ExtraData = {
|
||||
count = ability:GetSpecialValueFor("bounce_count_scepter"),
|
||||
nextTree = 0
|
||||
}
|
||||
}
|
||||
ProjectileManager:CreateTrackingProjectile(info)
|
||||
end
|
||||
end
|
||||
modifier_hoodwink_acorn_shot_custom_scepter = __TS__Decorate(
|
||||
modifier_hoodwink_acorn_shot_custom_scepter,
|
||||
modifier_hoodwink_acorn_shot_custom_scepter,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_hoodwink_acorn_shot_custom_scepter"}
|
||||
)
|
||||
____exports.modifier_hoodwink_acorn_shot_custom_scepter = modifier_hoodwink_acorn_shot_custom_scepter
|
||||
return ____exports
|
||||
@@ -0,0 +1,378 @@
|
||||
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 BaseModifierMotionBoth = ____dota_ts_adapter.BaseModifierMotionBoth
|
||||
local registerAbility = ____dota_ts_adapter.registerAbility
|
||||
local registerModifier = ____dota_ts_adapter.registerModifier
|
||||
____exports.hoodwink_bushwhack_custom = __TS__Class()
|
||||
local hoodwink_bushwhack_custom = ____exports.hoodwink_bushwhack_custom
|
||||
hoodwink_bushwhack_custom.name = "hoodwink_bushwhack_custom"
|
||||
hoodwink_bushwhack_custom.____file_path = "scripts/vscripts/abilities/heroes/hoodwink/hoodwink_bushwhack_custom.lua"
|
||||
__TS__ClassExtends(hoodwink_bushwhack_custom, BaseAbility)
|
||||
function hoodwink_bushwhack_custom.prototype.GetAOERadius(self)
|
||||
return self:GetSpecialValueFor("radius")
|
||||
end
|
||||
function hoodwink_bushwhack_custom.prototype.OnSpellStart(self)
|
||||
local caster = self:GetCaster()
|
||||
local point = self:GetCursorPosition()
|
||||
local thinker = CreateModifierThinker(
|
||||
caster,
|
||||
self,
|
||||
"modifier_kill",
|
||||
{duration = 2},
|
||||
point,
|
||||
caster:GetTeamNumber(),
|
||||
false
|
||||
)
|
||||
local projectile = {
|
||||
Target = thinker,
|
||||
iMoveSpeed = self:GetSpecialValueFor("projectile_speed"),
|
||||
bDodgeable = true,
|
||||
iSourceAttachment = DOTA_PROJECTILE_ATTACHMENT_ATTACK_1,
|
||||
EffectName = "particles/units/heroes/hero_hoodwink/hoodwink_bushwhack_projectile.vpcf",
|
||||
Ability = self,
|
||||
Source = caster
|
||||
}
|
||||
ProjectileManager:CreateTrackingProjectile(projectile)
|
||||
caster:EmitSound("Hero_Hoodwink.Bushwhack.Cast")
|
||||
end
|
||||
function hoodwink_bushwhack_custom.prototype.OnProjectileHit(self, target, location)
|
||||
local impactLocation = target and target:GetAbsOrigin() or location
|
||||
local caster = self:GetCaster()
|
||||
local radius = self:GetSpecialValueFor("radius")
|
||||
local duration = self:GetSpecialValueFor("duration")
|
||||
AddFOWViewer(
|
||||
caster:GetTeamNumber(),
|
||||
impactLocation,
|
||||
radius,
|
||||
duration,
|
||||
true
|
||||
)
|
||||
local enemies = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
impactLocation,
|
||||
nil,
|
||||
radius,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC,
|
||||
DOTA_UNIT_TARGET_FLAG_NO_INVIS,
|
||||
FIND_CLOSEST,
|
||||
false
|
||||
)
|
||||
local trees = GridNav:GetAllTreesAroundPoint(impactLocation, radius, false)
|
||||
local isSuccess = #trees > 0 and #enemies > 0
|
||||
if isSuccess then
|
||||
local tree = trees[1]
|
||||
for ____, enemy in ipairs(enemies) do
|
||||
if enemy:IsBoss() then
|
||||
enemy:AddNewModifier(
|
||||
caster,
|
||||
self,
|
||||
____exports.modifier_hoodwink_bushwhack_custom_boss.name,
|
||||
{
|
||||
duration = duration,
|
||||
tree = tree:entindex()
|
||||
}
|
||||
)
|
||||
else
|
||||
enemy:AddNewModifier(
|
||||
caster,
|
||||
self,
|
||||
____exports.modifier_hoodwink_bushwhack_custom.name,
|
||||
{
|
||||
duration = duration,
|
||||
tree = tree:entindex()
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
local effectName = isSuccess and "particles/units/heroes/hero_hoodwink/hoodwink_bushwhack.vpcf" or "particles/units/heroes/hero_hoodwink/hoodwink_bushwhack_fail.vpcf"
|
||||
local particle = ParticleManager:CreateParticle(effectName, PATTACH_WORLDORIGIN, nil)
|
||||
ParticleManager:SetParticleControl(particle, 0, impactLocation)
|
||||
ParticleManager:SetParticleControl(
|
||||
particle,
|
||||
1,
|
||||
Vector(radius, 0, 0)
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(particle)
|
||||
caster:StopSound("Hero_Hoodwink.Bushwhack.Cast")
|
||||
EmitSoundOnLocationWithCaster(impactLocation, "Hero_Hoodwink.Bushwhack.Impact", caster)
|
||||
end
|
||||
hoodwink_bushwhack_custom = __TS__Decorate(
|
||||
hoodwink_bushwhack_custom,
|
||||
hoodwink_bushwhack_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "hoodwink_bushwhack_custom"}
|
||||
)
|
||||
____exports.hoodwink_bushwhack_custom = hoodwink_bushwhack_custom
|
||||
____exports.modifier_hoodwink_bushwhack_custom = __TS__Class()
|
||||
local modifier_hoodwink_bushwhack_custom = ____exports.modifier_hoodwink_bushwhack_custom
|
||||
modifier_hoodwink_bushwhack_custom.name = "modifier_hoodwink_bushwhack_custom"
|
||||
modifier_hoodwink_bushwhack_custom.____file_path = "scripts/vscripts/abilities/heroes/hoodwink/hoodwink_bushwhack_custom.lua"
|
||||
__TS__ClassExtends(modifier_hoodwink_bushwhack_custom, BaseModifierMotionBoth)
|
||||
function modifier_hoodwink_bushwhack_custom.prototype.____constructor(self, ...)
|
||||
BaseModifierMotionBoth.prototype.____constructor(self, ...)
|
||||
self.treeOrigin = Vector(0, 0, 0)
|
||||
self.speed = 900
|
||||
self.distance = 150
|
||||
self.damage = 0
|
||||
self.interval = 0
|
||||
self.tickCount = 0
|
||||
self.rate = 0.3
|
||||
self.height = 50
|
||||
self.damageMultiplier = 0
|
||||
end
|
||||
function modifier_hoodwink_bushwhack_custom.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_hoodwink_bushwhack_custom.prototype.IsStunDebuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_hoodwink_bushwhack_custom.prototype.OnCreated(self, params)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local tree = EntIndexToHScript(params.tree)
|
||||
local ability = self:GetAbility()
|
||||
if not tree or tree:IsNull() or not ability then
|
||||
self:Destroy()
|
||||
return
|
||||
end
|
||||
self.treeOrigin = tree:GetAbsOrigin()
|
||||
self.damage = ability:GetSpecialValueFor("total_damage")
|
||||
self.interval = ability:GetSpecialValueFor("interval")
|
||||
self.tickCount = ability:GetSpecialValueFor("duration") / self.interval
|
||||
self.damage = self.damage / self.tickCount
|
||||
self.damageMultiplier = ability:GetSpecialValueFor("damage_multiplier")
|
||||
if not self:ApplyHorizontalMotionController() then
|
||||
self:Destroy()
|
||||
return
|
||||
end
|
||||
self:StartIntervalThink(self.interval)
|
||||
self:PlayEffects()
|
||||
end
|
||||
function modifier_hoodwink_bushwhack_custom.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self:GetParent():RemoveHorizontalMotionController(self)
|
||||
end
|
||||
function modifier_hoodwink_bushwhack_custom.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
local caster = self:GetCaster()
|
||||
if not ability or not caster then
|
||||
return
|
||||
end
|
||||
local damage = self.damage
|
||||
local hasShard = HasShard(nil, caster)
|
||||
if hasShard then
|
||||
damage = damage + caster:GetAverageTrueAttackDamage(self:GetParent()) * self.damageMultiplier / 100
|
||||
end
|
||||
ApplyDamage({
|
||||
victim = self:GetParent(),
|
||||
attacker = caster,
|
||||
damage = damage,
|
||||
damage_type = ability:GetAbilityDamageType(),
|
||||
ability = ability
|
||||
})
|
||||
end
|
||||
function modifier_hoodwink_bushwhack_custom.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_OVERRIDE_ANIMATION, MODIFIER_PROPERTY_OVERRIDE_ANIMATION_RATE, MODIFIER_PROPERTY_VISUAL_Z_DELTA}
|
||||
end
|
||||
function modifier_hoodwink_bushwhack_custom.prototype.GetOverrideAnimation(self)
|
||||
return ACT_DOTA_FLAIL
|
||||
end
|
||||
function modifier_hoodwink_bushwhack_custom.prototype.GetOverrideAnimationRate(self)
|
||||
return self.rate
|
||||
end
|
||||
function modifier_hoodwink_bushwhack_custom.prototype.GetVisualZDelta(self)
|
||||
return self.height
|
||||
end
|
||||
function modifier_hoodwink_bushwhack_custom.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_STUNNED] = true}
|
||||
end
|
||||
function modifier_hoodwink_bushwhack_custom.prototype.UpdateHorizontalMotion(self, me, dt)
|
||||
local origin = me:GetAbsOrigin()
|
||||
local dir = self.treeOrigin:__sub(origin)
|
||||
local dist = dir:Length2D()
|
||||
dir.z = 0
|
||||
dir = dir:Normalized()
|
||||
if dist < self.distance then
|
||||
self:GetParent():RemoveHorizontalMotionController(self)
|
||||
return
|
||||
end
|
||||
local target = dir:__mul(self.speed * dt)
|
||||
local newPos = origin:__add(target)
|
||||
local newDist = self.treeOrigin:__sub(newPos):Length2D()
|
||||
if newDist < self.distance then
|
||||
local finalPos = self.treeOrigin:__sub(dir:__mul(self.distance))
|
||||
me:SetAbsOrigin(finalPos)
|
||||
else
|
||||
me:SetAbsOrigin(newPos)
|
||||
end
|
||||
end
|
||||
function modifier_hoodwink_bushwhack_custom.prototype.OnHorizontalMotionInterrupted(self)
|
||||
self:GetParent():RemoveHorizontalMotionController(self)
|
||||
end
|
||||
function modifier_hoodwink_bushwhack_custom.prototype.PlayEffects(self)
|
||||
local effect = ParticleManager:CreateParticle(
|
||||
"particles/units/heroes/hero_hoodwink/hoodwink_bushwhack_target.vpcf",
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
self:GetParent()
|
||||
)
|
||||
ParticleManager:SetParticleControl(effect, 15, self.treeOrigin)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
effect,
|
||||
1,
|
||||
self:GetParent(),
|
||||
PATTACH_POINT_FOLLOW,
|
||||
"attach_hitloc",
|
||||
self:GetParent():GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
effect,
|
||||
4,
|
||||
self:GetParent(),
|
||||
PATTACH_POINT_FOLLOW,
|
||||
"attach_hitloc",
|
||||
self:GetParent():GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
self:AddParticle(
|
||||
effect,
|
||||
false,
|
||||
false,
|
||||
-1,
|
||||
false,
|
||||
false
|
||||
)
|
||||
EmitSoundOn(
|
||||
"Hero_Hoodwink.Bushwhack.Target",
|
||||
self:GetParent()
|
||||
)
|
||||
end
|
||||
modifier_hoodwink_bushwhack_custom = __TS__Decorate(
|
||||
modifier_hoodwink_bushwhack_custom,
|
||||
modifier_hoodwink_bushwhack_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_hoodwink_bushwhack_custom"}
|
||||
)
|
||||
____exports.modifier_hoodwink_bushwhack_custom = modifier_hoodwink_bushwhack_custom
|
||||
____exports.modifier_hoodwink_bushwhack_custom_boss = __TS__Class()
|
||||
local modifier_hoodwink_bushwhack_custom_boss = ____exports.modifier_hoodwink_bushwhack_custom_boss
|
||||
modifier_hoodwink_bushwhack_custom_boss.name = "modifier_hoodwink_bushwhack_custom_boss"
|
||||
modifier_hoodwink_bushwhack_custom_boss.____file_path = "scripts/vscripts/abilities/heroes/hoodwink/hoodwink_bushwhack_custom.lua"
|
||||
__TS__ClassExtends(modifier_hoodwink_bushwhack_custom_boss, BaseModifier)
|
||||
function modifier_hoodwink_bushwhack_custom_boss.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.damage = 0
|
||||
self.interval = 0
|
||||
self.tickCount = 0
|
||||
self.damageMultiplier = 0
|
||||
end
|
||||
function modifier_hoodwink_bushwhack_custom_boss.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_hoodwink_bushwhack_custom_boss.prototype.IsStunDebuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_hoodwink_bushwhack_custom_boss.prototype.OnCreated(self, params)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local tree = EntIndexToHScript(params.tree)
|
||||
local ability = self:GetAbility()
|
||||
if not tree or tree:IsNull() or not ability then
|
||||
self:Destroy()
|
||||
return
|
||||
end
|
||||
self.interval = ability:GetSpecialValueFor("interval")
|
||||
self.damage = ability:GetSpecialValueFor("total_damage")
|
||||
self.tickCount = ability:GetSpecialValueFor("duration") / self.interval
|
||||
self.damage = self.damage / self.tickCount
|
||||
self.damageMultiplier = ability:GetSpecialValueFor("damage_multiplier")
|
||||
local effect = ParticleManager:CreateParticle(
|
||||
"particles/units/heroes/hero_hoodwink/hoodwink_bushwhack_target.vpcf",
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
self:GetParent()
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
effect,
|
||||
15,
|
||||
tree:GetAbsOrigin()
|
||||
)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
effect,
|
||||
1,
|
||||
self:GetParent(),
|
||||
PATTACH_POINT_FOLLOW,
|
||||
"attach_hitloc",
|
||||
self:GetParent():GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
effect,
|
||||
4,
|
||||
self:GetParent(),
|
||||
PATTACH_POINT_FOLLOW,
|
||||
"attach_hitloc",
|
||||
self:GetParent():GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
self:AddParticle(
|
||||
effect,
|
||||
false,
|
||||
false,
|
||||
-1,
|
||||
false,
|
||||
false
|
||||
)
|
||||
EmitSoundOn(
|
||||
"Hero_Hoodwink.Bushwhack.Target",
|
||||
self:GetParent()
|
||||
)
|
||||
self:StartIntervalThink(self.interval)
|
||||
end
|
||||
function modifier_hoodwink_bushwhack_custom_boss.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
local caster = self:GetCaster()
|
||||
local hasShard = HasShard(nil, caster)
|
||||
if not ability or not caster then
|
||||
return
|
||||
end
|
||||
local damage = self.damage
|
||||
if hasShard then
|
||||
damage = damage + caster:GetAverageTrueAttackDamage(self:GetParent()) * self.damageMultiplier / 100
|
||||
end
|
||||
ApplyDamage({
|
||||
victim = self:GetParent(),
|
||||
attacker = caster,
|
||||
damage = damage,
|
||||
damage_type = ability:GetAbilityDamageType(),
|
||||
ability = ability
|
||||
})
|
||||
end
|
||||
function modifier_hoodwink_bushwhack_custom_boss.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_STUNNED] = true}
|
||||
end
|
||||
modifier_hoodwink_bushwhack_custom_boss = __TS__Decorate(
|
||||
modifier_hoodwink_bushwhack_custom_boss,
|
||||
modifier_hoodwink_bushwhack_custom_boss,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_hoodwink_bushwhack_custom_boss"}
|
||||
)
|
||||
____exports.modifier_hoodwink_bushwhack_custom_boss = modifier_hoodwink_bushwhack_custom_boss
|
||||
return ____exports
|
||||
@@ -0,0 +1,108 @@
|
||||
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 ____luck = require("utils.luck")
|
||||
local addLuck = ____luck.addLuck
|
||||
local reduceLuck = ____luck.reduceLuck
|
||||
____exports.hoodwink_lucky_innate_custom = __TS__Class()
|
||||
local hoodwink_lucky_innate_custom = ____exports.hoodwink_lucky_innate_custom
|
||||
hoodwink_lucky_innate_custom.name = "hoodwink_lucky_innate_custom"
|
||||
hoodwink_lucky_innate_custom.____file_path = "scripts/vscripts/abilities/heroes/hoodwink/hoodwink_lucky_innate_custom.lua"
|
||||
__TS__ClassExtends(hoodwink_lucky_innate_custom, BaseAbility)
|
||||
function hoodwink_lucky_innate_custom.prototype.GetIntrinsicModifierName(self)
|
||||
return ____exports.modifier_hoodwink_lucky_innate_custom.name
|
||||
end
|
||||
hoodwink_lucky_innate_custom = __TS__Decorate(
|
||||
hoodwink_lucky_innate_custom,
|
||||
hoodwink_lucky_innate_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "hoodwink_lucky_innate_custom"}
|
||||
)
|
||||
____exports.hoodwink_lucky_innate_custom = hoodwink_lucky_innate_custom
|
||||
____exports.modifier_hoodwink_lucky_innate_custom = __TS__Class()
|
||||
local modifier_hoodwink_lucky_innate_custom = ____exports.modifier_hoodwink_lucky_innate_custom
|
||||
modifier_hoodwink_lucky_innate_custom.name = "modifier_hoodwink_lucky_innate_custom"
|
||||
modifier_hoodwink_lucky_innate_custom.____file_path = "scripts/vscripts/abilities/heroes/hoodwink/hoodwink_lucky_innate_custom.lua"
|
||||
__TS__ClassExtends(modifier_hoodwink_lucky_innate_custom, BaseModifier)
|
||||
function modifier_hoodwink_lucky_innate_custom.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.appliedLuckBonus = 0
|
||||
end
|
||||
function modifier_hoodwink_lucky_innate_custom.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_hoodwink_lucky_innate_custom.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_hoodwink_lucky_innate_custom.prototype.OnGameEvent(self)
|
||||
ListenToGameEvent(
|
||||
"dota_player_gained_level",
|
||||
function(event)
|
||||
local parent = self:GetParent()
|
||||
if not parent or not parent:IsHero() or parent:IsIllusion() then
|
||||
return
|
||||
end
|
||||
local playerId = parent:GetPlayerOwnerID()
|
||||
if playerId == nil or playerId == nil or playerId < 0 then
|
||||
return
|
||||
end
|
||||
if event.player ~= playerId then
|
||||
return
|
||||
end
|
||||
self:UpdateLuckFromHeroLevel()
|
||||
end,
|
||||
nil
|
||||
)
|
||||
end
|
||||
function modifier_hoodwink_lucky_innate_custom.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self:UpdateLuckFromHeroLevel()
|
||||
self.levelUpListener = ListenToGameEvent(
|
||||
"dota_player_gained_level",
|
||||
function()
|
||||
local parent = self:GetParent()
|
||||
if not parent or not parent:IsHero() or parent:IsIllusion() then
|
||||
return
|
||||
end
|
||||
self:UpdateLuckFromHeroLevel()
|
||||
end,
|
||||
nil
|
||||
)
|
||||
end
|
||||
function modifier_hoodwink_lucky_innate_custom.prototype.UpdateLuckFromHeroLevel(self)
|
||||
local parent = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
if not parent:IsHero() or parent:IsIllusion() or not ability then
|
||||
return
|
||||
end
|
||||
local hero = parent
|
||||
local luckPerLevel = math.max(
|
||||
0,
|
||||
ability:GetSpecialValueFor("luck_per_level")
|
||||
)
|
||||
local desiredLuckBonus = hero:GetLevel() * luckPerLevel
|
||||
local delta = desiredLuckBonus - self.appliedLuckBonus
|
||||
if delta > 0 then
|
||||
addLuck(nil, hero, delta)
|
||||
elseif delta < 0 then
|
||||
reduceLuck(nil, hero, -delta)
|
||||
end
|
||||
self.appliedLuckBonus = desiredLuckBonus
|
||||
end
|
||||
modifier_hoodwink_lucky_innate_custom = __TS__Decorate(
|
||||
modifier_hoodwink_lucky_innate_custom,
|
||||
modifier_hoodwink_lucky_innate_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_hoodwink_lucky_innate_custom"}
|
||||
)
|
||||
____exports.modifier_hoodwink_lucky_innate_custom = modifier_hoodwink_lucky_innate_custom
|
||||
return ____exports
|
||||
@@ -0,0 +1,237 @@
|
||||
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 ____luck = require("utils.luck")
|
||||
local getLuck = ____luck.getLuck
|
||||
local ____incoming_damage_reduction_combine = require("utils.incoming_damage_reduction_combine")
|
||||
local removeIncomingDamageReductionSource = ____incoming_damage_reduction_combine.removeIncomingDamageReductionSource
|
||||
local setIncomingDamageReductionSource = ____incoming_damage_reduction_combine.setIncomingDamageReductionSource
|
||||
local HOODWINK_SCURRY_BUFF_INCOMING_SOURCE = "modifier_hoodwink_scurry_custom_buff"
|
||||
____exports.hoodwink_scurry_custom = __TS__Class()
|
||||
local hoodwink_scurry_custom = ____exports.hoodwink_scurry_custom
|
||||
hoodwink_scurry_custom.name = "hoodwink_scurry_custom"
|
||||
hoodwink_scurry_custom.____file_path = "scripts/vscripts/abilities/heroes/hoodwink/hoodwink_scurry_custom.lua"
|
||||
__TS__ClassExtends(hoodwink_scurry_custom, BaseAbility)
|
||||
function hoodwink_scurry_custom.prototype.GetCastRange(self, _location, _target)
|
||||
return self:GetSpecialValueFor("radius")
|
||||
end
|
||||
function hoodwink_scurry_custom.prototype.GetIntrinsicModifierName(self)
|
||||
return ____exports.modifier_hoodwink_scurry_custom.name
|
||||
end
|
||||
function hoodwink_scurry_custom.prototype.OnSpellStart(self)
|
||||
local caster = self:GetCaster()
|
||||
caster:AddNewModifier(
|
||||
caster,
|
||||
self,
|
||||
____exports.modifier_hoodwink_scurry_custom_active.name,
|
||||
{duration = self:GetSpecialValueFor("duration")}
|
||||
)
|
||||
caster:AddNewModifier(caster, self, ____exports.modifier_hoodwink_scurry_custom_buff.name, {})
|
||||
EmitSoundOn("Hero_Hoodwink.Scurry.Cast", caster)
|
||||
end
|
||||
hoodwink_scurry_custom = __TS__Decorate(
|
||||
hoodwink_scurry_custom,
|
||||
hoodwink_scurry_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "hoodwink_scurry_custom"}
|
||||
)
|
||||
____exports.hoodwink_scurry_custom = hoodwink_scurry_custom
|
||||
____exports.modifier_hoodwink_scurry_custom = __TS__Class()
|
||||
local modifier_hoodwink_scurry_custom = ____exports.modifier_hoodwink_scurry_custom
|
||||
modifier_hoodwink_scurry_custom.name = "modifier_hoodwink_scurry_custom"
|
||||
modifier_hoodwink_scurry_custom.____file_path = "scripts/vscripts/abilities/heroes/hoodwink/hoodwink_scurry_custom.lua"
|
||||
__TS__ClassExtends(modifier_hoodwink_scurry_custom, BaseModifier)
|
||||
function modifier_hoodwink_scurry_custom.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.radius = 0
|
||||
end
|
||||
function modifier_hoodwink_scurry_custom.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_hoodwink_scurry_custom.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_hoodwink_scurry_custom.prototype.IsPurgeException(self)
|
||||
return false
|
||||
end
|
||||
function modifier_hoodwink_scurry_custom.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
self.radius = ability:GetSpecialValueFor("radius")
|
||||
self:StartIntervalThink(0.2)
|
||||
end
|
||||
function modifier_hoodwink_scurry_custom.prototype.OnRefresh(self)
|
||||
self:OnCreated()
|
||||
end
|
||||
function modifier_hoodwink_scurry_custom.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
if GridNav:IsNearbyTree(
|
||||
parent:GetAbsOrigin(),
|
||||
self.radius,
|
||||
true
|
||||
) or parent:HasModifier(____exports.modifier_hoodwink_scurry_custom_active.name) then
|
||||
parent:AddNewModifier(
|
||||
parent,
|
||||
self:GetAbility(),
|
||||
____exports.modifier_hoodwink_scurry_custom_buff.name,
|
||||
{}
|
||||
)
|
||||
else
|
||||
parent:RemoveModifierByName(____exports.modifier_hoodwink_scurry_custom_buff.name)
|
||||
end
|
||||
end
|
||||
modifier_hoodwink_scurry_custom = __TS__Decorate(
|
||||
modifier_hoodwink_scurry_custom,
|
||||
modifier_hoodwink_scurry_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_hoodwink_scurry_custom"}
|
||||
)
|
||||
____exports.modifier_hoodwink_scurry_custom = modifier_hoodwink_scurry_custom
|
||||
____exports.modifier_hoodwink_scurry_custom_buff = __TS__Class()
|
||||
local modifier_hoodwink_scurry_custom_buff = ____exports.modifier_hoodwink_scurry_custom_buff
|
||||
modifier_hoodwink_scurry_custom_buff.name = "modifier_hoodwink_scurry_custom_buff"
|
||||
modifier_hoodwink_scurry_custom_buff.____file_path = "scripts/vscripts/abilities/heroes/hoodwink/hoodwink_scurry_custom.lua"
|
||||
__TS__ClassExtends(modifier_hoodwink_scurry_custom_buff, BaseModifier)
|
||||
function modifier_hoodwink_scurry_custom_buff.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.bonusAttackSpeed = 0
|
||||
self.bonusDamage = 0
|
||||
self.luckEvasionMultiplier = 1
|
||||
self.maxLuckEvasion = 75
|
||||
end
|
||||
function modifier_hoodwink_scurry_custom_buff.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_hoodwink_scurry_custom_buff.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_ATTACKSPEED_BONUS_CONSTANT, MODIFIER_PROPERTY_PREATTACK_BONUS_DAMAGE, MODIFIER_PROPERTY_EVASION_CONSTANT}
|
||||
end
|
||||
function modifier_hoodwink_scurry_custom_buff.prototype.OnCreated(self)
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
self.bonusAttackSpeed = ability:GetSpecialValueFor("bonus_attack_speed")
|
||||
self.bonusDamage = ability:GetSpecialValueFor("bonus_damage")
|
||||
self.luckEvasionMultiplier = ability:GetSpecialValueFor("luck_evasion_multiplier")
|
||||
self.maxLuckEvasion = ability:GetSpecialValueFor("max_luck_evasion")
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
setIncomingDamageReductionSource(
|
||||
nil,
|
||||
self:GetParent(),
|
||||
HOODWINK_SCURRY_BUFF_INCOMING_SOURCE,
|
||||
function()
|
||||
if self:GetParent():PassivesDisabled() then
|
||||
return 0
|
||||
end
|
||||
local buffAbility = self:GetAbility()
|
||||
if not buffAbility then
|
||||
return 0
|
||||
end
|
||||
local reductionPct = buffAbility:GetSpecialValueFor("incoming_damage_reduction_pct")
|
||||
return reductionPct > 0 and reductionPct or 0
|
||||
end
|
||||
)
|
||||
end
|
||||
function modifier_hoodwink_scurry_custom_buff.prototype.OnRefresh(self)
|
||||
self:OnCreated()
|
||||
end
|
||||
function modifier_hoodwink_scurry_custom_buff.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
removeIncomingDamageReductionSource(
|
||||
nil,
|
||||
self:GetParent(),
|
||||
HOODWINK_SCURRY_BUFF_INCOMING_SOURCE
|
||||
)
|
||||
end
|
||||
function modifier_hoodwink_scurry_custom_buff.prototype.GetModifierPreAttack_BonusDamage(self)
|
||||
if self:GetParent():PassivesDisabled() then
|
||||
return 0
|
||||
end
|
||||
return self.bonusDamage
|
||||
end
|
||||
function modifier_hoodwink_scurry_custom_buff.prototype.GetModifierAttackSpeedBonus_Constant(self)
|
||||
if self:GetParent():PassivesDisabled() then
|
||||
return 0
|
||||
end
|
||||
return self.bonusAttackSpeed
|
||||
end
|
||||
function modifier_hoodwink_scurry_custom_buff.prototype.GetModifierEvasion_Constant(self)
|
||||
if self:GetParent():PassivesDisabled() then
|
||||
return 0
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
if not parent:IsHero() then
|
||||
return 0
|
||||
end
|
||||
local luck = getLuck(nil, parent)
|
||||
self:SetStackCount(math.floor(math.max(0, luck)))
|
||||
return math.min(self.maxLuckEvasion, luck * self.luckEvasionMultiplier)
|
||||
end
|
||||
function modifier_hoodwink_scurry_custom_buff.prototype.GetEffectName(self)
|
||||
return "particles/units/heroes/hero_hoodwink/hoodwink_scurry_passive.vpcf"
|
||||
end
|
||||
function modifier_hoodwink_scurry_custom_buff.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_ABSORIGIN_FOLLOW
|
||||
end
|
||||
modifier_hoodwink_scurry_custom_buff = __TS__Decorate(
|
||||
modifier_hoodwink_scurry_custom_buff,
|
||||
modifier_hoodwink_scurry_custom_buff,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_hoodwink_scurry_custom_buff"}
|
||||
)
|
||||
____exports.modifier_hoodwink_scurry_custom_buff = modifier_hoodwink_scurry_custom_buff
|
||||
____exports.modifier_hoodwink_scurry_custom_active = __TS__Class()
|
||||
local modifier_hoodwink_scurry_custom_active = ____exports.modifier_hoodwink_scurry_custom_active
|
||||
modifier_hoodwink_scurry_custom_active.name = "modifier_hoodwink_scurry_custom_active"
|
||||
modifier_hoodwink_scurry_custom_active.____file_path = "scripts/vscripts/abilities/heroes/hoodwink/hoodwink_scurry_custom.lua"
|
||||
__TS__ClassExtends(modifier_hoodwink_scurry_custom_active, BaseModifier)
|
||||
function modifier_hoodwink_scurry_custom_active.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.bonusMovementSpeed = 0
|
||||
end
|
||||
function modifier_hoodwink_scurry_custom_active.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_hoodwink_scurry_custom_active.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_NO_UNIT_COLLISION] = true}
|
||||
end
|
||||
function modifier_hoodwink_scurry_custom_active.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE}
|
||||
end
|
||||
function modifier_hoodwink_scurry_custom_active.prototype.OnCreated(self)
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
self.bonusMovementSpeed = ability:GetSpecialValueFor("movement_speed_pct")
|
||||
end
|
||||
function modifier_hoodwink_scurry_custom_active.prototype.GetModifierMoveSpeedBonus_Percentage(self)
|
||||
return self.bonusMovementSpeed
|
||||
end
|
||||
modifier_hoodwink_scurry_custom_active = __TS__Decorate(
|
||||
modifier_hoodwink_scurry_custom_active,
|
||||
modifier_hoodwink_scurry_custom_active,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_hoodwink_scurry_custom_active"}
|
||||
)
|
||||
____exports.modifier_hoodwink_scurry_custom_active = modifier_hoodwink_scurry_custom_active
|
||||
return ____exports
|
||||
@@ -0,0 +1,527 @@
|
||||
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 modifier_hoodwink_sharpshooter_custom_debuff, modifier_hoodwink_sharpshooter_custom_sound
|
||||
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
|
||||
____exports.hoodwink_sharpshooter_custom = __TS__Class()
|
||||
local hoodwink_sharpshooter_custom = ____exports.hoodwink_sharpshooter_custom
|
||||
hoodwink_sharpshooter_custom.name = "hoodwink_sharpshooter_custom"
|
||||
hoodwink_sharpshooter_custom.____file_path = "scripts/vscripts/abilities/heroes/hoodwink/hoodwink_sharpshooter_custom.lua"
|
||||
__TS__ClassExtends(hoodwink_sharpshooter_custom, BaseAbility)
|
||||
function hoodwink_sharpshooter_custom.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_hoodwink/hoodwink_sharpshooter.vpcf", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_hoodwink/hoodwink_sharpshooter_projectile.vpcf", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_hoodwink/hoodwink_sharpshooter_impact.vpcf", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_hoodwink/hoodwink_sharpshooter_debuff.vpcf", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_hoodwink/hoodwink_sharpshooter_timer.vpcf", context)
|
||||
end
|
||||
function hoodwink_sharpshooter_custom.prototype.OnSpellStart(self)
|
||||
local point = self:GetCursorPosition()
|
||||
local duration = self:GetSpecialValueFor("misfire_time")
|
||||
local caster = self:GetCaster()
|
||||
caster:StartGesture(ACT_DOTA_CHANNEL_ABILITY_6)
|
||||
caster:AddNewModifier(caster, self, ____exports.modifier_hoodwink_sharpshooter_custom.name, {duration = duration, x = point.x, y = point.y})
|
||||
end
|
||||
function hoodwink_sharpshooter_custom.prototype.OnProjectileThink_ExtraData(self, location, extraData)
|
||||
local sound = EntIndexToHScript(extraData.sound)
|
||||
if not sound or sound:IsNull() then
|
||||
return
|
||||
end
|
||||
sound:SetOrigin(location)
|
||||
end
|
||||
function hoodwink_sharpshooter_custom.prototype.OnProjectileHit_ExtraData(self, target, _location, extraData)
|
||||
local sound = EntIndexToHScript(extraData.sound)
|
||||
if sound and not sound:IsNull() then
|
||||
sound:StopSound("Hero_Hoodwink.Sharpshooter.Projectile")
|
||||
UTIL_Remove(sound)
|
||||
end
|
||||
if not target then
|
||||
return false
|
||||
end
|
||||
modifier_hoodwink_sharpshooter_custom_debuff:apply(
|
||||
target,
|
||||
self:GetCaster(),
|
||||
self,
|
||||
{duration = extraData.duration, x = extraData.x, y = extraData.y}
|
||||
)
|
||||
local real = ApplyDamage({
|
||||
victim = target,
|
||||
attacker = self:GetCaster(),
|
||||
damage = extraData.damage + self:GetCaster():GetAverageTrueAttackDamage(target) * self:GetSpecialValueFor("damage_multiplier") * 0.01,
|
||||
damage_type = self:GetAbilityDamageType(),
|
||||
ability = self
|
||||
})
|
||||
SendOverheadEventMessage(
|
||||
nil,
|
||||
OVERHEAD_ALERT_BONUS_SPELL_DAMAGE,
|
||||
target,
|
||||
real,
|
||||
self:GetCaster():GetPlayerOwner()
|
||||
)
|
||||
AddFOWViewer(
|
||||
self:GetCaster():GetTeamNumber(),
|
||||
target:GetOrigin(),
|
||||
300,
|
||||
4,
|
||||
false
|
||||
)
|
||||
local direction = Vector(extraData.x, extraData.y, 0):Normalized()
|
||||
local effect = ParticleManager:CreateParticle("particles/units/heroes/hero_hoodwink/hoodwink_sharpshooter_impact.vpcf", PATTACH_ABSORIGIN_FOLLOW, target)
|
||||
ParticleManager:SetParticleControl(
|
||||
effect,
|
||||
0,
|
||||
target:GetOrigin()
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
effect,
|
||||
1,
|
||||
target:GetOrigin()
|
||||
)
|
||||
ParticleManager:SetParticleControlForward(effect, 1, direction)
|
||||
ParticleManager:ReleaseParticleIndex(effect)
|
||||
target:EmitSound("Hero_Hoodwink.Sharpshooter.Target")
|
||||
end
|
||||
hoodwink_sharpshooter_custom = __TS__Decorate(
|
||||
hoodwink_sharpshooter_custom,
|
||||
hoodwink_sharpshooter_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "hoodwink_sharpshooter_custom"}
|
||||
)
|
||||
____exports.hoodwink_sharpshooter_custom = hoodwink_sharpshooter_custom
|
||||
____exports.hoodwink_sharpshooter_release_custom = __TS__Class()
|
||||
local hoodwink_sharpshooter_release_custom = ____exports.hoodwink_sharpshooter_release_custom
|
||||
hoodwink_sharpshooter_release_custom.name = "hoodwink_sharpshooter_release_custom"
|
||||
hoodwink_sharpshooter_release_custom.____file_path = "scripts/vscripts/abilities/heroes/hoodwink/hoodwink_sharpshooter_custom.lua"
|
||||
__TS__ClassExtends(hoodwink_sharpshooter_release_custom, BaseAbility)
|
||||
function hoodwink_sharpshooter_release_custom.prototype.OnSpellStart(self)
|
||||
local mod = self:GetCaster():FindModifierByName(____exports.modifier_hoodwink_sharpshooter_custom.name)
|
||||
if not mod then
|
||||
return
|
||||
end
|
||||
mod:Destroy()
|
||||
end
|
||||
hoodwink_sharpshooter_release_custom = __TS__Decorate(
|
||||
hoodwink_sharpshooter_release_custom,
|
||||
hoodwink_sharpshooter_release_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "hoodwink_sharpshooter_release_custom"}
|
||||
)
|
||||
____exports.hoodwink_sharpshooter_release_custom = hoodwink_sharpshooter_release_custom
|
||||
____exports.modifier_hoodwink_sharpshooter_custom = __TS__Class()
|
||||
local modifier_hoodwink_sharpshooter_custom = ____exports.modifier_hoodwink_sharpshooter_custom
|
||||
modifier_hoodwink_sharpshooter_custom.name = "modifier_hoodwink_sharpshooter_custom"
|
||||
modifier_hoodwink_sharpshooter_custom.____file_path = "scripts/vscripts/abilities/heroes/hoodwink/hoodwink_sharpshooter_custom.lua"
|
||||
__TS__ClassExtends(modifier_hoodwink_sharpshooter_custom, BaseModifier)
|
||||
function modifier_hoodwink_sharpshooter_custom.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.charge = 0
|
||||
self.base = 0
|
||||
self.damage = 0
|
||||
self.duration = 0
|
||||
self.turnRate = 0
|
||||
self.recoilDistance = 0
|
||||
self.recoilDuration = 0
|
||||
self.recoilHeight = 0
|
||||
self.interval = 0.03
|
||||
self.projectileSpeed = 0
|
||||
self.projectileRange = 0
|
||||
self.projectileWidth = 0
|
||||
self.targetDir = Vector(0, 0, 0)
|
||||
self.currentDir = Vector(0, 0, 0)
|
||||
self.faceTarget = false
|
||||
self.charged = false
|
||||
self.half = false
|
||||
self.turnSpeed = 0
|
||||
self.info = {}
|
||||
self.previewEffect = -1
|
||||
end
|
||||
function modifier_hoodwink_sharpshooter_custom.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_hoodwink_sharpshooter_custom.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_hoodwink_sharpshooter_custom.prototype.OnCreated(self, kv)
|
||||
local parent = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
self.charge = ability:GetSpecialValueFor("max_charge_time")
|
||||
self.base = ability:GetSpecialValueFor("base_power")
|
||||
self.damage = ability:GetSpecialValueFor("max_damage")
|
||||
self.duration = ability:GetSpecialValueFor("max_slow_debuff_duration")
|
||||
self.turnRate = ability:GetSpecialValueFor("turn_rate")
|
||||
self.recoilDistance = ability:GetSpecialValueFor("recoil_distance")
|
||||
self.recoilDuration = ability:GetSpecialValueFor("recoil_duration")
|
||||
self.recoilHeight = ability:GetSpecialValueFor("recoil_height")
|
||||
self.projectileSpeed = ability:GetSpecialValueFor("arrow_speed")
|
||||
self.projectileRange = ability:GetSpecialValueFor("arrow_range")
|
||||
self.projectileWidth = ability:GetSpecialValueFor("arrow_width")
|
||||
self:StartIntervalThink(self.interval)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local projectileVision = ability:GetSpecialValueFor("arrow_vision")
|
||||
self:SetDirection(Vector(kv.x, kv.y, 0))
|
||||
self.currentDir = self.targetDir
|
||||
self.faceTarget = true
|
||||
parent:SetForwardVector(self.currentDir)
|
||||
self.turnSpeed = self.interval * self.turnRate
|
||||
self.info = {
|
||||
Source = parent,
|
||||
Ability = ability,
|
||||
bDeleteOnHit = false,
|
||||
iUnitTargetTeam = DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
iUnitTargetFlags = DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
iUnitTargetType = DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC,
|
||||
EffectName = "particles/units/heroes/hero_hoodwink/hoodwink_sharpshooter_projectile.vpcf",
|
||||
fDistance = self.projectileRange,
|
||||
fStartRadius = self.projectileWidth,
|
||||
fEndRadius = self.projectileWidth,
|
||||
bHasFrontalCone = false,
|
||||
bReplaceExisting = false,
|
||||
bProvidesVision = true,
|
||||
bVisibleToEnemies = true,
|
||||
iVisionRadius = projectileVision,
|
||||
iVisionTeamNumber = parent:GetTeamNumber()
|
||||
}
|
||||
parent:SwapAbilities(____exports.hoodwink_sharpshooter_custom.name, ____exports.hoodwink_sharpshooter_release_custom.name, false, true)
|
||||
local effect = ParticleManager:CreateParticle("particles/units/heroes/hero_hoodwink/hoodwink_sharpshooter.vpcf", PATTACH_ABSORIGIN_FOLLOW, parent)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
effect,
|
||||
1,
|
||||
parent,
|
||||
PATTACH_POINT_FOLLOW,
|
||||
"attach_hitloc",
|
||||
parent:GetOrigin(),
|
||||
true
|
||||
)
|
||||
self:AddParticle(
|
||||
effect,
|
||||
false,
|
||||
false,
|
||||
-1,
|
||||
false,
|
||||
false
|
||||
)
|
||||
self.previewEffect = effect
|
||||
EmitSoundOn("Hero_Hoodwink.Sharpshooter.Channel", parent)
|
||||
end
|
||||
function modifier_hoodwink_sharpshooter_custom.prototype.Shoot(self, newPct)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local pct = newPct or math.min(
|
||||
1,
|
||||
math.min(
|
||||
self:GetElapsedTime(),
|
||||
self.charge
|
||||
) / self.charge + self.base
|
||||
)
|
||||
local realPct = pct == 0 and 1 or pct
|
||||
local duration = self.duration * realPct
|
||||
local damage = self.damage * realPct
|
||||
self.info.vSpawnOrigin = parent:GetOrigin()
|
||||
self.info.vVelocity = self.currentDir:__mul(self.projectileSpeed)
|
||||
local sound = CreateModifierThinker(
|
||||
parent,
|
||||
self:GetAbility(),
|
||||
modifier_hoodwink_sharpshooter_custom_sound.name,
|
||||
{},
|
||||
parent:GetOrigin(),
|
||||
parent:GetTeamNumber(),
|
||||
false
|
||||
)
|
||||
sound:EmitSound("Hero_Hoodwink.Sharpshooter.Projectile")
|
||||
self.info.ExtraData = {
|
||||
damage = damage,
|
||||
pct = realPct,
|
||||
duration = duration,
|
||||
x = self.currentDir.x,
|
||||
y = self.currentDir.y,
|
||||
sound = sound:entindex()
|
||||
}
|
||||
ProjectileManager:CreateLinearProjectile(self.info)
|
||||
end
|
||||
function modifier_hoodwink_sharpshooter_custom.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
StopSoundOn("Hero_Hoodwink.Sharpshooter.Channel", parent)
|
||||
if parent:IsIllusion() and not parent:IsAlive() then
|
||||
return
|
||||
end
|
||||
self:Shoot()
|
||||
parent:RemoveGesture(ACT_DOTA_CHANNEL_ABILITY_6)
|
||||
local bumpPoint = parent:GetAbsOrigin() + self.currentDir * self.recoilDistance
|
||||
local mod = parent:AddNewModifier(
|
||||
parent,
|
||||
self:GetAbility(),
|
||||
"modifier_knockback",
|
||||
{
|
||||
duration = self.recoilDuration,
|
||||
knockback_height = self.recoilHeight,
|
||||
knockback_distance = self.recoilDistance,
|
||||
knockback_duration = self.recoilDuration,
|
||||
center_x = bumpPoint.x,
|
||||
center_y = bumpPoint.y,
|
||||
center_z = bumpPoint.z
|
||||
}
|
||||
)
|
||||
parent:SwapAbilities(____exports.hoodwink_sharpshooter_release_custom.name, ____exports.hoodwink_sharpshooter_custom.name, false, true)
|
||||
if mod ~= nil then
|
||||
local effect = ParticleManager:CreateParticle("particles/items_fx/force_staff.vpcf", PATTACH_ABSORIGIN_FOLLOW, parent)
|
||||
mod:AddParticle(
|
||||
effect,
|
||||
false,
|
||||
false,
|
||||
-1,
|
||||
false,
|
||||
false
|
||||
)
|
||||
end
|
||||
parent:StopSound("Hero_Hoodwink.Sharpshooter.Cast")
|
||||
parent:EmitSound("Hero_Hoodwink.Sharpshooter.Cast")
|
||||
end
|
||||
function modifier_hoodwink_sharpshooter_custom.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_EVENT_ON_ORDER, MODIFIER_PROPERTY_DISABLE_TURNING, MODIFIER_PROPERTY_MOVESPEED_LIMIT, MODIFIER_PROPERTY_OVERRIDE_ANIMATION}
|
||||
end
|
||||
function modifier_hoodwink_sharpshooter_custom.prototype.GetOverrideAnimation(self)
|
||||
return ACT_DOTA_CHANNEL_ABILITY_6
|
||||
end
|
||||
function modifier_hoodwink_sharpshooter_custom.prototype.GetPriority(self)
|
||||
return MODIFIER_PRIORITY_ULTRA
|
||||
end
|
||||
function modifier_hoodwink_sharpshooter_custom.prototype.OnOrder(self, params)
|
||||
if self:GetParent():IsIllusion() then
|
||||
return
|
||||
end
|
||||
if params.unit ~= self:GetParent() then
|
||||
return
|
||||
end
|
||||
if params.order_type == DOTA_UNIT_ORDER_MOVE_TO_POSITION or params.order_type == DOTA_UNIT_ORDER_MOVE_TO_DIRECTION then
|
||||
self:SetDirection(params.new_pos)
|
||||
elseif params.order_type == DOTA_UNIT_ORDER_MOVE_TO_TARGET or params.order_type == DOTA_UNIT_ORDER_ATTACK_TARGET then
|
||||
local target = params.target
|
||||
if target ~= nil then
|
||||
self:SetDirection(target:GetOrigin())
|
||||
end
|
||||
end
|
||||
end
|
||||
function modifier_hoodwink_sharpshooter_custom.prototype.GetModifierMoveSpeed_Limit(self)
|
||||
return 0.1
|
||||
end
|
||||
function modifier_hoodwink_sharpshooter_custom.prototype.GetModifierTurnRate_Percentage(self)
|
||||
return -self.turnRate
|
||||
end
|
||||
function modifier_hoodwink_sharpshooter_custom.prototype.GetModifierDisableTurning(self)
|
||||
return 1
|
||||
end
|
||||
function modifier_hoodwink_sharpshooter_custom.prototype.SetDirection(self, vec)
|
||||
local parent = self:GetParent()
|
||||
if not parent or parent:IsNull() then
|
||||
return
|
||||
end
|
||||
if vec.x == parent:GetAbsOrigin().x and vec.y == parent:GetAbsOrigin().y then
|
||||
vec = parent:GetAbsOrigin() + 100 * parent:GetForwardVector()
|
||||
end
|
||||
self.targetDir = ((vec - parent:GetOrigin()) * Vector(1, 1, 0)):Normalized()
|
||||
self.faceTarget = false
|
||||
end
|
||||
function modifier_hoodwink_sharpshooter_custom.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_DISARMED] = true}
|
||||
end
|
||||
function modifier_hoodwink_sharpshooter_custom.prototype.TurnLogic(self)
|
||||
local parent = self:GetParent()
|
||||
if not parent or parent:IsNull() or self.faceTarget then
|
||||
return
|
||||
end
|
||||
local currentAngle = VectorToAngles(self.currentDir).y
|
||||
local targetAngle = VectorToAngles(self.targetDir).y
|
||||
local angleDiff = AngleDiff(currentAngle, targetAngle)
|
||||
local sign = angleDiff < 0 and 1 or -1
|
||||
if math.abs(angleDiff) < 1.1 * self.turnSpeed then
|
||||
self.currentDir = self.targetDir
|
||||
self.faceTarget = true
|
||||
else
|
||||
self.currentDir = RotatePosition(
|
||||
Vector(0, 0, 0),
|
||||
QAngle(0, sign * self.turnSpeed, 0),
|
||||
self.currentDir
|
||||
)
|
||||
end
|
||||
local hasMotion = parent:IsCurrentlyHorizontalMotionControlled() or parent:IsCurrentlyVerticalMotionControlled()
|
||||
if not hasMotion then
|
||||
parent:SetForwardVector(self.currentDir)
|
||||
end
|
||||
end
|
||||
function modifier_hoodwink_sharpshooter_custom.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self:UpdateStack()
|
||||
local parent = self:GetParent()
|
||||
if not parent or parent:IsNull() then
|
||||
return
|
||||
end
|
||||
self:TurnLogic()
|
||||
self:UpdateEffect()
|
||||
local chargeThreshold = self.charge * (1 - self.base)
|
||||
if not self.charged and self:GetElapsedTime() > chargeThreshold then
|
||||
self.charged = true
|
||||
parent:EmitSound("Hero_Hoodwink.Sharpshooter.MaxCharge")
|
||||
end
|
||||
local remaining = self:GetRemainingTime()
|
||||
local seconds = math.ceil(remaining)
|
||||
local isHalf = seconds - remaining > 0.5
|
||||
if isHalf then
|
||||
seconds = seconds - 1
|
||||
end
|
||||
if self.half ~= isHalf then
|
||||
self.half = isHalf
|
||||
local mid = isHalf and 8 or 1
|
||||
local len = 2
|
||||
if seconds < 1 then
|
||||
len = 1
|
||||
if not isHalf then
|
||||
return
|
||||
end
|
||||
end
|
||||
local effect = ParticleManager:CreateParticleForTeam(
|
||||
"particles/units/heroes/hero_hoodwink/hoodwink_sharpshooter_timer.vpcf",
|
||||
PATTACH_OVERHEAD_FOLLOW,
|
||||
parent,
|
||||
parent:GetTeamNumber()
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
effect,
|
||||
1,
|
||||
Vector(1, seconds, mid)
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
effect,
|
||||
2,
|
||||
Vector(len, 0, 0)
|
||||
)
|
||||
end
|
||||
end
|
||||
function modifier_hoodwink_sharpshooter_custom.prototype.UpdateStack(self)
|
||||
local fullTime = self.charge * (1 - self.base)
|
||||
local pct = math.min(
|
||||
1,
|
||||
self:GetElapsedTime() / fullTime
|
||||
)
|
||||
pct = math.floor(pct * 100)
|
||||
self:SetStackCount(pct)
|
||||
end
|
||||
function modifier_hoodwink_sharpshooter_custom.prototype.UpdateEffect(self)
|
||||
if self.previewEffect == -1 then
|
||||
return
|
||||
end
|
||||
local startPos = self:GetParent():GetAbsOrigin()
|
||||
local endPos = startPos:__add(self.currentDir:__mul(self.projectileRange))
|
||||
ParticleManager:SetParticleControl(self.previewEffect, 0, startPos)
|
||||
ParticleManager:SetParticleControl(self.previewEffect, 1, endPos)
|
||||
end
|
||||
modifier_hoodwink_sharpshooter_custom = __TS__Decorate(
|
||||
modifier_hoodwink_sharpshooter_custom,
|
||||
modifier_hoodwink_sharpshooter_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_hoodwink_sharpshooter_custom"}
|
||||
)
|
||||
____exports.modifier_hoodwink_sharpshooter_custom = modifier_hoodwink_sharpshooter_custom
|
||||
modifier_hoodwink_sharpshooter_custom_debuff = __TS__Class()
|
||||
modifier_hoodwink_sharpshooter_custom_debuff.name = "modifier_hoodwink_sharpshooter_custom_debuff"
|
||||
modifier_hoodwink_sharpshooter_custom_debuff.____file_path = "scripts/vscripts/abilities/heroes/hoodwink/hoodwink_sharpshooter_custom.lua"
|
||||
__TS__ClassExtends(modifier_hoodwink_sharpshooter_custom_debuff, BaseModifier)
|
||||
function modifier_hoodwink_sharpshooter_custom_debuff.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.slow = 0
|
||||
end
|
||||
function modifier_hoodwink_sharpshooter_custom_debuff.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_hoodwink_sharpshooter_custom_debuff.prototype.IsDebuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_hoodwink_sharpshooter_custom_debuff.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
function modifier_hoodwink_sharpshooter_custom_debuff.prototype.OnCreated(self, kv)
|
||||
local parent = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
self.slow = -ability:GetSpecialValueFor("slow_move_pct")
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local direction = Vector(kv.x, kv.y, 0):Normalized()
|
||||
local effect = ParticleManager:CreateParticle("particles/units/heroes/hero_hoodwink/hoodwink_sharpshooter_debuff.vpcf", PATTACH_POINT_FOLLOW, parent)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
effect,
|
||||
0,
|
||||
parent,
|
||||
PATTACH_POINT_FOLLOW,
|
||||
"attach_hitloc",
|
||||
parent:GetOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:SetParticleControlForward(effect, 1, direction)
|
||||
self:AddParticle(
|
||||
effect,
|
||||
false,
|
||||
false,
|
||||
-1,
|
||||
false,
|
||||
false
|
||||
)
|
||||
end
|
||||
function modifier_hoodwink_sharpshooter_custom_debuff.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE}
|
||||
end
|
||||
function modifier_hoodwink_sharpshooter_custom_debuff.prototype.GetModifierMoveSpeedBonus_Percentage(self)
|
||||
return self.slow
|
||||
end
|
||||
function modifier_hoodwink_sharpshooter_custom_debuff.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_PASSIVES_DISABLED] = true}
|
||||
end
|
||||
function modifier_hoodwink_sharpshooter_custom_debuff.prototype.GetEffectName(self)
|
||||
return "particles/generic_gameplay/generic_break.vpcf"
|
||||
end
|
||||
function modifier_hoodwink_sharpshooter_custom_debuff.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_OVERHEAD_FOLLOW
|
||||
end
|
||||
modifier_hoodwink_sharpshooter_custom_debuff = __TS__Decorate(
|
||||
modifier_hoodwink_sharpshooter_custom_debuff,
|
||||
modifier_hoodwink_sharpshooter_custom_debuff,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_hoodwink_sharpshooter_custom_debuff"}
|
||||
)
|
||||
modifier_hoodwink_sharpshooter_custom_sound = __TS__Class()
|
||||
modifier_hoodwink_sharpshooter_custom_sound.name = "modifier_hoodwink_sharpshooter_custom_sound"
|
||||
modifier_hoodwink_sharpshooter_custom_sound.____file_path = "scripts/vscripts/abilities/heroes/hoodwink/hoodwink_sharpshooter_custom.lua"
|
||||
__TS__ClassExtends(modifier_hoodwink_sharpshooter_custom_sound, BaseModifier)
|
||||
function modifier_hoodwink_sharpshooter_custom_sound.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_hoodwink_sharpshooter_custom_sound.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
modifier_hoodwink_sharpshooter_custom_sound = __TS__Decorate(
|
||||
modifier_hoodwink_sharpshooter_custom_sound,
|
||||
modifier_hoodwink_sharpshooter_custom_sound,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_hoodwink_sharpshooter_custom_sound"}
|
||||
)
|
||||
return ____exports
|
||||
+309
@@ -0,0 +1,309 @@
|
||||
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 ____vampirism = require("utils.vampirism")
|
||||
local addPhysicalVampirism = ____vampirism.addPhysicalVampirism
|
||||
local reducePhysicalVampirism = ____vampirism.reducePhysicalVampirism
|
||||
____exports.ability_juggernaut_blade_dance_custom = __TS__Class()
|
||||
local ability_juggernaut_blade_dance_custom = ____exports.ability_juggernaut_blade_dance_custom
|
||||
ability_juggernaut_blade_dance_custom.name = "ability_juggernaut_blade_dance_custom"
|
||||
ability_juggernaut_blade_dance_custom.____file_path = "scripts/vscripts/abilities/heroes/juggernaut/ability_juggernaut_blade_dance_custom.lua"
|
||||
__TS__ClassExtends(ability_juggernaut_blade_dance_custom, BaseAbility)
|
||||
function ability_juggernaut_blade_dance_custom.prototype.GetIntrinsicModifierName(self)
|
||||
return "modifier_juggernaut_blade_dance_custom"
|
||||
end
|
||||
function ability_juggernaut_blade_dance_custom.prototype.GetCooldown(self, _level)
|
||||
return self:GetSpecialValueFor("AbilityCooldown")
|
||||
end
|
||||
function ability_juggernaut_blade_dance_custom.prototype.GetManaCost(self, _level)
|
||||
return self:GetSpecialValueFor("AbilityManaCost")
|
||||
end
|
||||
function ability_juggernaut_blade_dance_custom.prototype.GetCastRange(self, _location, _target)
|
||||
return self:GetSpecialValueFor("AbilityCastRange")
|
||||
end
|
||||
function ability_juggernaut_blade_dance_custom.prototype.PerformSlash(self, caster, startPos, endPos)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local radius = self:GetSpecialValueFor("astral_slash_radius")
|
||||
local debuffDuration = self:GetSpecialValueFor("astral_slash_debuff_duration")
|
||||
local groundEnd = GetGroundPosition(endPos, nil)
|
||||
caster:StartGestureWithPlaybackRate(ACT_DOTA_TELEPORT_END, 1)
|
||||
FindClearSpaceForUnit(caster, groundEnd, true)
|
||||
local enemies = FindUnitsInLine(
|
||||
caster:GetTeamNumber(),
|
||||
startPos,
|
||||
groundEnd,
|
||||
nil,
|
||||
radius,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC),
|
||||
DOTA_UNIT_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES
|
||||
)
|
||||
for ____, enemy in ipairs(enemies) do
|
||||
do
|
||||
if not enemy or not enemy:IsAlive() then
|
||||
goto __continue8
|
||||
end
|
||||
local isAltCast = self:IsAltCastAbility()
|
||||
if self:GetSpecialValueFor("juggernaut_blade_dance_jugg_step") > 0 then
|
||||
enemy:AddNewModifier(caster, self, ____exports.modifier_juggernaut_blade_dance_astral_slash_debuff.name, {duration = debuffDuration})
|
||||
if not isAltCast then
|
||||
enemy:AddNewModifier(caster, self, ____exports.modifier_juggernaut_blade_dance_astral_slash_debuff.name, {duration = debuffDuration})
|
||||
end
|
||||
end
|
||||
local count = self:GetSpecialValueFor("astral_slash_attack_count")
|
||||
if self:IsAltCastAbility() then
|
||||
count = math.max(
|
||||
1,
|
||||
math.floor(count / 2)
|
||||
)
|
||||
end
|
||||
do
|
||||
local i = 0
|
||||
while i < count do
|
||||
caster:PerformAttack(
|
||||
enemy,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
)
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
::__continue8::
|
||||
end
|
||||
self:PlayEffects(startPos, groundEnd)
|
||||
end
|
||||
function ability_juggernaut_blade_dance_custom.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local point = self:GetCursorPosition()
|
||||
local origin = caster:GetAbsOrigin()
|
||||
local isAltCast = self:IsAltCastAbility()
|
||||
local minDist = self:GetSpecialValueFor("min_travel_distance")
|
||||
local maxDist = self:GetSpecialValueFor("max_travel_distance")
|
||||
local direction = point - origin
|
||||
direction.z = 0
|
||||
local dist2D = direction:Length2D()
|
||||
local dist = math.max(
|
||||
math.min(maxDist, dist2D),
|
||||
minDist
|
||||
)
|
||||
local dirNorm = direction:Normalized()
|
||||
local endPos = Vector(origin.x + dirNorm.x * dist, origin.y + dirNorm.y * dist, origin.z)
|
||||
local targetPos = GetGroundPosition(endPos, nil)
|
||||
self:PerformSlash(caster, origin, targetPos)
|
||||
if isAltCast then
|
||||
local returnPos = GetGroundPosition(origin, nil)
|
||||
caster:FaceTowards(returnPos)
|
||||
caster:AddNewModifier(caster, self, ____exports.modifier_juggernaut_blade_dance_return.name, {duration = 0.25, x = returnPos.x, y = returnPos.y, z = returnPos.z})
|
||||
end
|
||||
end
|
||||
function ability_juggernaut_blade_dance_custom.prototype.PlayEffects(self, origin, target)
|
||||
local particleCast = "particles/juggernaut_step.vpcf"
|
||||
local effectCast = ParticleManager:CreateParticle(particleCast, PATTACH_WORLDORIGIN, nil)
|
||||
ParticleManager:SetParticleControl(effectCast, 0, origin)
|
||||
ParticleManager:SetParticleControl(effectCast, 1, target)
|
||||
ParticleManager:ReleaseParticleIndex(effectCast)
|
||||
EmitSoundOnLocationWithCaster(
|
||||
origin,
|
||||
"Hero_Juggernaut.OmniSlash",
|
||||
self:GetCaster()
|
||||
)
|
||||
EmitSoundOnLocationWithCaster(
|
||||
target,
|
||||
"Hero_Juggernaut.OmniSlash.End",
|
||||
self:GetCaster()
|
||||
)
|
||||
end
|
||||
ability_juggernaut_blade_dance_custom = __TS__Decorate(
|
||||
ability_juggernaut_blade_dance_custom,
|
||||
ability_juggernaut_blade_dance_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_juggernaut_blade_dance_custom"}
|
||||
)
|
||||
____exports.ability_juggernaut_blade_dance_custom = ability_juggernaut_blade_dance_custom
|
||||
____exports.modifier_juggernaut_blade_dance_custom = __TS__Class()
|
||||
local modifier_juggernaut_blade_dance_custom = ____exports.modifier_juggernaut_blade_dance_custom
|
||||
modifier_juggernaut_blade_dance_custom.name = "modifier_juggernaut_blade_dance_custom"
|
||||
modifier_juggernaut_blade_dance_custom.____file_path = "scripts/vscripts/abilities/heroes/juggernaut/ability_juggernaut_blade_dance_custom.lua"
|
||||
__TS__ClassExtends(modifier_juggernaut_blade_dance_custom, BaseModifier)
|
||||
function modifier_juggernaut_blade_dance_custom.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.lifestealAppliedAmount = 0
|
||||
end
|
||||
function modifier_juggernaut_blade_dance_custom.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_juggernaut_blade_dance_custom.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_juggernaut_blade_dance_custom.prototype.RemoveOnDeath(self)
|
||||
return false
|
||||
end
|
||||
function modifier_juggernaut_blade_dance_custom.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
if not ability or not parent:IsRealHero() then
|
||||
return
|
||||
end
|
||||
local critMod = parent:FindModifierByName("modifier_stacking_crit")
|
||||
local critChance = ability:GetSpecialValueFor("crit_chance")
|
||||
local critMult = ability:GetSpecialValueFor("crit_mult")
|
||||
if parent:PassivesDisabled() then
|
||||
if critMod ~= nil then
|
||||
critMod:RemoveCrit("blade_dance")
|
||||
end
|
||||
if self.lifestealAppliedAmount > 0 then
|
||||
reducePhysicalVampirism(nil, parent, self.lifestealAppliedAmount)
|
||||
self.lifestealAppliedAmount = 0
|
||||
end
|
||||
return
|
||||
end
|
||||
if critMod then
|
||||
critMod:UpdateCrit(
|
||||
0,
|
||||
0,
|
||||
critChance,
|
||||
critMult,
|
||||
"blade_dance"
|
||||
)
|
||||
end
|
||||
local lifesteal = ability:GetSpecialValueFor("lifesteal_percent")
|
||||
if lifesteal > 0 and self.lifestealAppliedAmount == 0 then
|
||||
addPhysicalVampirism(nil, parent, lifesteal)
|
||||
self.lifestealAppliedAmount = lifesteal
|
||||
end
|
||||
end
|
||||
function modifier_juggernaut_blade_dance_custom.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self:StartIntervalThink(0.2)
|
||||
self:OnIntervalThink()
|
||||
end
|
||||
function modifier_juggernaut_blade_dance_custom.prototype.OnRefresh(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self:OnIntervalThink()
|
||||
end
|
||||
function modifier_juggernaut_blade_dance_custom.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local ____opt_2 = parent and parent:FindModifierByName("modifier_stacking_crit")
|
||||
if ____opt_2 ~= nil then
|
||||
____opt_2:RemoveCrit("blade_dance")
|
||||
end
|
||||
if self.lifestealAppliedAmount > 0 and (parent and parent:IsRealHero()) then
|
||||
reducePhysicalVampirism(nil, parent, self.lifestealAppliedAmount)
|
||||
end
|
||||
end
|
||||
modifier_juggernaut_blade_dance_custom = __TS__Decorate(
|
||||
modifier_juggernaut_blade_dance_custom,
|
||||
modifier_juggernaut_blade_dance_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_juggernaut_blade_dance_custom"}
|
||||
)
|
||||
____exports.modifier_juggernaut_blade_dance_custom = modifier_juggernaut_blade_dance_custom
|
||||
____exports.modifier_juggernaut_blade_dance_return = __TS__Class()
|
||||
local modifier_juggernaut_blade_dance_return = ____exports.modifier_juggernaut_blade_dance_return
|
||||
modifier_juggernaut_blade_dance_return.name = "modifier_juggernaut_blade_dance_return"
|
||||
modifier_juggernaut_blade_dance_return.____file_path = "scripts/vscripts/abilities/heroes/juggernaut/ability_juggernaut_blade_dance_custom.lua"
|
||||
__TS__ClassExtends(modifier_juggernaut_blade_dance_return, BaseModifier)
|
||||
function modifier_juggernaut_blade_dance_return.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_juggernaut_blade_dance_return.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_juggernaut_blade_dance_return.prototype.OnCreated(self, params)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local x = params.x or 0
|
||||
local y = params.y or 0
|
||||
local z = params.z or 0
|
||||
self.returnPos = Vector(x, y, z)
|
||||
end
|
||||
function modifier_juggernaut_blade_dance_return.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
if parent and parent:IsAlive() and ability then
|
||||
local startPos = parent:GetAbsOrigin()
|
||||
ability:PerformSlash(parent, startPos, self.returnPos)
|
||||
end
|
||||
end
|
||||
modifier_juggernaut_blade_dance_return = __TS__Decorate(
|
||||
modifier_juggernaut_blade_dance_return,
|
||||
modifier_juggernaut_blade_dance_return,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_juggernaut_blade_dance_return"}
|
||||
)
|
||||
____exports.modifier_juggernaut_blade_dance_return = modifier_juggernaut_blade_dance_return
|
||||
____exports.modifier_juggernaut_blade_dance_astral_slash_debuff = __TS__Class()
|
||||
local modifier_juggernaut_blade_dance_astral_slash_debuff = ____exports.modifier_juggernaut_blade_dance_astral_slash_debuff
|
||||
modifier_juggernaut_blade_dance_astral_slash_debuff.name = "modifier_juggernaut_blade_dance_astral_slash_debuff"
|
||||
modifier_juggernaut_blade_dance_astral_slash_debuff.____file_path = "scripts/vscripts/abilities/heroes/juggernaut/ability_juggernaut_blade_dance_custom.lua"
|
||||
__TS__ClassExtends(modifier_juggernaut_blade_dance_astral_slash_debuff, BaseModifier)
|
||||
function modifier_juggernaut_blade_dance_astral_slash_debuff.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_juggernaut_blade_dance_astral_slash_debuff.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
function modifier_juggernaut_blade_dance_astral_slash_debuff.prototype.GetEffectName(self)
|
||||
return "particles/econ/items/void_spirit/void_spirit_immortal_2021/void_spirit_immortal_2021_astral_step_debuff.vpcf"
|
||||
end
|
||||
function modifier_juggernaut_blade_dance_astral_slash_debuff.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_ABSORIGIN_FOLLOW
|
||||
end
|
||||
function modifier_juggernaut_blade_dance_astral_slash_debuff.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_PHYSICAL_ARMOR_BONUS}
|
||||
end
|
||||
function modifier_juggernaut_blade_dance_astral_slash_debuff.prototype.GetAttributes(self)
|
||||
return MODIFIER_ATTRIBUTE_MULTIPLE
|
||||
end
|
||||
function modifier_juggernaut_blade_dance_astral_slash_debuff.prototype.GetModifierPhysicalArmorBonus(self, event)
|
||||
local parent = self:GetParent()
|
||||
if not parent then
|
||||
return 0
|
||||
end
|
||||
if not parent:IsRealHero() then
|
||||
return 0
|
||||
end
|
||||
local hero = parent
|
||||
local armor = hero:GetPhysicalArmorBaseValue()
|
||||
return -armor * (self:GetAbility():GetSpecialValueFor("juggernaut_blade_dance_jugg_step_armor_reduce") * 0.01)
|
||||
end
|
||||
modifier_juggernaut_blade_dance_astral_slash_debuff = __TS__Decorate(
|
||||
modifier_juggernaut_blade_dance_astral_slash_debuff,
|
||||
modifier_juggernaut_blade_dance_astral_slash_debuff,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_juggernaut_blade_dance_astral_slash_debuff"}
|
||||
)
|
||||
____exports.modifier_juggernaut_blade_dance_astral_slash_debuff = modifier_juggernaut_blade_dance_astral_slash_debuff
|
||||
return ____exports
|
||||
+730
@@ -0,0 +1,730 @@
|
||||
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 ____luck = require("utils.luck")
|
||||
local rollLuckChance = ____luck.rollLuckChance
|
||||
____exports.ability_juggernaut_blade_fury_custom = __TS__Class()
|
||||
local ability_juggernaut_blade_fury_custom = ____exports.ability_juggernaut_blade_fury_custom
|
||||
ability_juggernaut_blade_fury_custom.name = "ability_juggernaut_blade_fury_custom"
|
||||
ability_juggernaut_blade_fury_custom.____file_path = "scripts/vscripts/abilities/heroes/juggernaut/ability_juggernaut_blade_fury_custom.lua"
|
||||
__TS__ClassExtends(ability_juggernaut_blade_fury_custom, BaseAbility)
|
||||
function ability_juggernaut_blade_fury_custom.prototype.GetIntrinsicModifierName(self)
|
||||
return "modifier_juggernaut_blade_fury_custom_passive"
|
||||
end
|
||||
function ability_juggernaut_blade_fury_custom.prototype.GetAOERadius(self)
|
||||
return self:GetSpecialValueFor("radius")
|
||||
end
|
||||
function ability_juggernaut_blade_fury_custom.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local duration = self:GetSpecialValueFor("duration")
|
||||
caster:Purge(
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
)
|
||||
local isAltCast = self:IsAltCastAbility()
|
||||
if isAltCast then
|
||||
local point = self:GetCursorPosition()
|
||||
local casterPos = caster:GetAbsOrigin()
|
||||
local castRange = self:GetCastRange(casterPos, nil)
|
||||
local delta = Vector(point.x - casterPos.x, point.y - casterPos.y, 0)
|
||||
local dist2D = delta:Length2D()
|
||||
local clampedPoint = dist2D > castRange and dist2D > 0 and Vector(casterPos.x + delta.x / dist2D * castRange, casterPos.y + delta.y / dist2D * castRange, point.z) or point
|
||||
local groundPoint = GetGroundPosition(clampedPoint, nil)
|
||||
local thinker = CreateModifierThinker(
|
||||
caster,
|
||||
self,
|
||||
"modifier_juggernaut_blade_fury_thinker",
|
||||
{x = groundPoint.x, y = groundPoint.y, z = groundPoint.z},
|
||||
caster:GetAbsOrigin(),
|
||||
caster:GetTeamNumber(),
|
||||
false
|
||||
)
|
||||
local ____opt_0 = self:GetCaster()
|
||||
if ____opt_0 ~= nil then
|
||||
____opt_0:RemoveGesture(ACT_DOTA_ATTACK)
|
||||
end
|
||||
if thinker and IsValidEntity(thinker) then
|
||||
thinker:AddNewModifier(caster, self, "modifier_juggernaut_blade_fury_sword_unit", {})
|
||||
thinker:AddNewModifier(caster, self, "modifier_juggernaut_blade_fury_custom_point", {})
|
||||
thinker:AddNewModifier(caster, self, "modifier_juggernaut_blade_fury_fly", {x = groundPoint.x, y = groundPoint.y})
|
||||
EmitSoundOn("Hero_Juggernaut.BladeFuryStart", thinker)
|
||||
end
|
||||
return
|
||||
else
|
||||
caster:AddNewModifier(caster, self, "modifier_juggernaut_blade_fury_custom", {duration = duration})
|
||||
EmitSoundOn("Hero_Juggernaut.BladeFuryStart", caster)
|
||||
end
|
||||
end
|
||||
ability_juggernaut_blade_fury_custom = __TS__Decorate(
|
||||
ability_juggernaut_blade_fury_custom,
|
||||
ability_juggernaut_blade_fury_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_juggernaut_blade_fury_custom"}
|
||||
)
|
||||
____exports.ability_juggernaut_blade_fury_custom = ability_juggernaut_blade_fury_custom
|
||||
____exports.modifier_juggernaut_blade_fury_custom = __TS__Class()
|
||||
local modifier_juggernaut_blade_fury_custom = ____exports.modifier_juggernaut_blade_fury_custom
|
||||
modifier_juggernaut_blade_fury_custom.name = "modifier_juggernaut_blade_fury_custom"
|
||||
modifier_juggernaut_blade_fury_custom.____file_path = "scripts/vscripts/abilities/heroes/juggernaut/ability_juggernaut_blade_fury_custom.lua"
|
||||
__TS__ClassExtends(modifier_juggernaut_blade_fury_custom, BaseModifier)
|
||||
function modifier_juggernaut_blade_fury_custom.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.damageInterval = 1 / self:GetCaster():GetAttacksPerSecond(false)
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_custom.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_custom.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_custom.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_DEBUFF_IMMUNE] = true}
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_custom.prototype.OnCreated(self, params)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
local ____opt_2 = self:GetCaster()
|
||||
if ____opt_2 ~= nil then
|
||||
____opt_2:StartGesture(ACT_DOTA_OVERRIDE_ABILITY_1)
|
||||
end
|
||||
local particleId = ParticleManager:CreateParticle(
|
||||
"particles/units/heroes/hero_juggernaut/juggernaut_blade_fury.vpcf",
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
self:GetParent()
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
particleId,
|
||||
0,
|
||||
self:GetParent():GetAbsOrigin()
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
particleId,
|
||||
1,
|
||||
self:GetParent():GetAbsOrigin()
|
||||
)
|
||||
self.particleId = particleId
|
||||
self:StartIntervalThink(self.damageInterval)
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_custom.prototype.OnRefresh(self, params)
|
||||
StopSoundOn(
|
||||
"Hero_Juggernaut.BladeFuryStart",
|
||||
self:GetParent()
|
||||
)
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_custom.prototype.OnDestroy(self)
|
||||
if IsClient() then
|
||||
return
|
||||
end
|
||||
local ____opt_4 = self:GetCaster()
|
||||
if ____opt_4 ~= nil then
|
||||
____opt_4:RemoveGesture(ACT_DOTA_OVERRIDE_ABILITY_1)
|
||||
end
|
||||
StopSoundOn(
|
||||
"Hero_Juggernaut.BladeFuryStart",
|
||||
self:GetParent()
|
||||
)
|
||||
if self.particleId ~= nil then
|
||||
ParticleManager:DestroyParticle(self.particleId, false)
|
||||
ParticleManager:ReleaseParticleIndex(self.particleId)
|
||||
self.particleId = nil
|
||||
end
|
||||
self:GetParent():Purge(
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
)
|
||||
EmitSoundOn(
|
||||
"Hero_Juggernaut.BladeFuryStop",
|
||||
self:GetParent()
|
||||
)
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_custom.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local caster = self:GetCaster()
|
||||
local ability = self:GetAbility()
|
||||
if not ability or not caster or not caster:IsAlive() or not parent:IsAlive() then
|
||||
return
|
||||
end
|
||||
local radius = ability:GetSpecialValueFor("radius")
|
||||
local damagePerTick = ability:GetSpecialValueFor("damage_per_tick")
|
||||
local enemies = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
parent: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
|
||||
)
|
||||
for ____, enemy in ipairs(enemies) do
|
||||
if enemy and enemy:IsAlive() and not enemy:IsInvulnerable() then
|
||||
if self:GetAbility():GetSpecialValueFor("leap_sword") > 0 then
|
||||
caster:PerformAttack(
|
||||
enemy,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
)
|
||||
end
|
||||
ApplyDamage({
|
||||
victim = enemy,
|
||||
attacker = caster,
|
||||
damage = damagePerTick,
|
||||
damage_type = ability:GetAbilityDamageType(),
|
||||
ability = ability
|
||||
})
|
||||
local finaldamage
|
||||
if self:GetAbility():GetSpecialValueFor("leap_sword") > 0 then
|
||||
finaldamage = damagePerTick + caster:GetAverageTrueAttackDamage(enemy)
|
||||
else
|
||||
finaldamage = damagePerTick
|
||||
end
|
||||
SendOverheadEventMessage(
|
||||
nil,
|
||||
OVERHEAD_ALERT_DAMAGE,
|
||||
enemy,
|
||||
finaldamage,
|
||||
nil
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
modifier_juggernaut_blade_fury_custom = __TS__Decorate(
|
||||
modifier_juggernaut_blade_fury_custom,
|
||||
modifier_juggernaut_blade_fury_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_juggernaut_blade_fury_custom"}
|
||||
)
|
||||
____exports.modifier_juggernaut_blade_fury_custom = modifier_juggernaut_blade_fury_custom
|
||||
____exports.modifier_juggernaut_blade_fury_custom_passive = __TS__Class()
|
||||
local modifier_juggernaut_blade_fury_custom_passive = ____exports.modifier_juggernaut_blade_fury_custom_passive
|
||||
modifier_juggernaut_blade_fury_custom_passive.name = "modifier_juggernaut_blade_fury_custom_passive"
|
||||
modifier_juggernaut_blade_fury_custom_passive.____file_path = "scripts/vscripts/abilities/heroes/juggernaut/ability_juggernaut_blade_fury_custom.lua"
|
||||
__TS__ClassExtends(modifier_juggernaut_blade_fury_custom_passive, BaseModifier)
|
||||
function modifier_juggernaut_blade_fury_custom_passive.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_custom_passive.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_custom_passive.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_EVENT_ON_ATTACK_LANDED}
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_custom_passive.prototype.OnAttackLanded(self, event)
|
||||
if not HasShard(
|
||||
nil,
|
||||
self:GetCaster()
|
||||
) then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
if not caster then
|
||||
return
|
||||
end
|
||||
if caster:PassivesDisabled() then
|
||||
return
|
||||
end
|
||||
if event.attacker ~= self:GetParent() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
if not parent or not parent:IsRealHero() then
|
||||
return
|
||||
end
|
||||
local hero = parent
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
local baseChancePercent = ability:GetSpecialValueFor("proc_chance")
|
||||
local baseChance = baseChancePercent / 100
|
||||
if rollLuckChance(nil, hero, baseChance) then
|
||||
if self:GetCaster():HasModifier("modifier_juggernaut_blade_fury_custom") then
|
||||
caster:AddNewModifier(
|
||||
caster,
|
||||
ability,
|
||||
"modifier_juggernaut_blade_fury_custom",
|
||||
{duration = self:GetCaster():FindModifierByName("modifier_juggernaut_blade_fury_custom"):GetRemainingTime() + 1 / self:GetCaster():GetAttacksPerSecond(false)}
|
||||
)
|
||||
else
|
||||
caster:AddNewModifier(
|
||||
caster,
|
||||
ability,
|
||||
"modifier_juggernaut_blade_fury_custom",
|
||||
{duration = 1 / self:GetCaster():GetAttacksPerSecond(false)}
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
modifier_juggernaut_blade_fury_custom_passive = __TS__Decorate(
|
||||
modifier_juggernaut_blade_fury_custom_passive,
|
||||
modifier_juggernaut_blade_fury_custom_passive,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_juggernaut_blade_fury_custom_passive"}
|
||||
)
|
||||
____exports.modifier_juggernaut_blade_fury_custom_passive = modifier_juggernaut_blade_fury_custom_passive
|
||||
____exports.modifier_juggernaut_blade_fury_sword_unit = __TS__Class()
|
||||
local modifier_juggernaut_blade_fury_sword_unit = ____exports.modifier_juggernaut_blade_fury_sword_unit
|
||||
modifier_juggernaut_blade_fury_sword_unit.name = "modifier_juggernaut_blade_fury_sword_unit"
|
||||
modifier_juggernaut_blade_fury_sword_unit.____file_path = "scripts/vscripts/abilities/heroes/juggernaut/ability_juggernaut_blade_fury_custom.lua"
|
||||
__TS__ClassExtends(modifier_juggernaut_blade_fury_sword_unit, BaseModifier)
|
||||
function modifier_juggernaut_blade_fury_sword_unit.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_sword_unit.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_sword_unit.prototype.CheckState(self)
|
||||
return {
|
||||
[MODIFIER_STATE_INVULNERABLE] = true,
|
||||
[MODIFIER_STATE_UNSELECTABLE] = true,
|
||||
[MODIFIER_STATE_UNTARGETABLE] = true,
|
||||
[MODIFIER_STATE_NO_HEALTH_BAR] = true,
|
||||
[MODIFIER_STATE_NOT_ON_MINIMAP] = true,
|
||||
[MODIFIER_STATE_NO_UNIT_COLLISION] = true
|
||||
}
|
||||
end
|
||||
modifier_juggernaut_blade_fury_sword_unit = __TS__Decorate(
|
||||
modifier_juggernaut_blade_fury_sword_unit,
|
||||
modifier_juggernaut_blade_fury_sword_unit,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_juggernaut_blade_fury_sword_unit"}
|
||||
)
|
||||
____exports.modifier_juggernaut_blade_fury_sword_unit = modifier_juggernaut_blade_fury_sword_unit
|
||||
local BLADE_FURY_FLY_SPEED = 800
|
||||
local BLADE_FURY_FLY_INTERVAL = 0.03
|
||||
local BLADE_FURY_THINKER_MOD = "modifier_juggernaut_blade_fury_thinker"
|
||||
--- Точечный режим на thinker: при смерти/пропажи кастера убираем юнит, иначе вихрь крутится вечно.
|
||||
local function cleanupBladeFuryThinkerAfterCasterLost(self, parent, caster)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if caster ~= nil and caster ~= nil and caster:IsAlive() then
|
||||
return
|
||||
end
|
||||
local thinkerMod = parent:FindModifierByName(BLADE_FURY_THINKER_MOD)
|
||||
if thinkerMod then
|
||||
thinkerMod:Destroy()
|
||||
return
|
||||
end
|
||||
StopSoundOn("Hero_Juggernaut.BladeFuryStart", parent)
|
||||
EmitSoundOn("Hero_Juggernaut.BladeFuryStop", parent)
|
||||
if IsValidEntity(parent) then
|
||||
UTIL_Remove(parent)
|
||||
end
|
||||
end
|
||||
____exports.modifier_juggernaut_blade_fury_custom_point = __TS__Class()
|
||||
local modifier_juggernaut_blade_fury_custom_point = ____exports.modifier_juggernaut_blade_fury_custom_point
|
||||
modifier_juggernaut_blade_fury_custom_point.name = "modifier_juggernaut_blade_fury_custom_point"
|
||||
modifier_juggernaut_blade_fury_custom_point.____file_path = "scripts/vscripts/abilities/heroes/juggernaut/ability_juggernaut_blade_fury_custom.lua"
|
||||
__TS__ClassExtends(modifier_juggernaut_blade_fury_custom_point, BaseModifier)
|
||||
function modifier_juggernaut_blade_fury_custom_point.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.radius = 0
|
||||
self.damageTickInterval = 0.2
|
||||
self.lastDamageTime = 0
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_custom_point.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_custom_point.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_custom_point.prototype.OnCreated(self, _params)
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
self.radius = ability:GetSpecialValueFor("radius")
|
||||
local caster = self:GetCaster()
|
||||
if caster then
|
||||
self.damageTickInterval = 0.1 + 1 / caster:GetAttacksPerSecond(false)
|
||||
end
|
||||
self.lastDamageTime = GameRules:GetGameTime()
|
||||
if IsServer() then
|
||||
local particleName = "particles/units/heroes/hero_juggernaut/juggernaut_blade_fury.vpcf"
|
||||
local fx = ParticleManager:CreateParticle(
|
||||
particleName,
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
self:GetParent()
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
fx,
|
||||
5,
|
||||
Vector(self.radius * 1.2, 0, 0)
|
||||
)
|
||||
self:AddParticle(
|
||||
fx,
|
||||
false,
|
||||
false,
|
||||
-1,
|
||||
false,
|
||||
false
|
||||
)
|
||||
self.particleId = fx
|
||||
end
|
||||
self:StartIntervalThink(0.1)
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_custom_point.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
local caster = self:GetCaster()
|
||||
if not ability or not parent:IsAlive() then
|
||||
return
|
||||
end
|
||||
if not caster or not caster:IsAlive() then
|
||||
cleanupBladeFuryThinkerAfterCasterLost(nil, parent, caster)
|
||||
return
|
||||
end
|
||||
local pos = parent:GetAbsOrigin()
|
||||
local currentTime = GameRules:GetGameTime()
|
||||
if currentTime - self.lastDamageTime >= self.damageTickInterval then
|
||||
self.lastDamageTime = currentTime
|
||||
local damagePerTick = ability:GetSpecialValueFor("damage_per_tick")
|
||||
local enemies = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
pos,
|
||||
nil,
|
||||
self.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
|
||||
)
|
||||
for ____, enemy in ipairs(enemies) do
|
||||
if enemy and enemy:IsAlive() and not enemy:IsInvulnerable() then
|
||||
if self:GetAbility():GetSpecialValueFor("leap_sword") > 0 then
|
||||
caster:PerformAttack(
|
||||
enemy,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
)
|
||||
end
|
||||
ApplyDamage({
|
||||
victim = enemy,
|
||||
attacker = caster,
|
||||
damage = damagePerTick,
|
||||
damage_type = ability:GetAbilityDamageType(),
|
||||
ability = ability
|
||||
})
|
||||
local finaldamage = damagePerTick + caster:GetAverageTrueAttackDamage(enemy)
|
||||
SendOverheadEventMessage(
|
||||
nil,
|
||||
OVERHEAD_ALERT_DAMAGE,
|
||||
enemy,
|
||||
finaldamage,
|
||||
nil
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_custom_point.prototype.OnDestroy(self)
|
||||
if IsServer() and self.particleId ~= nil then
|
||||
ParticleManager:DestroyParticle(self.particleId, false)
|
||||
ParticleManager:ReleaseParticleIndex(self.particleId)
|
||||
self.particleId = nil
|
||||
end
|
||||
end
|
||||
modifier_juggernaut_blade_fury_custom_point = __TS__Decorate(
|
||||
modifier_juggernaut_blade_fury_custom_point,
|
||||
modifier_juggernaut_blade_fury_custom_point,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_juggernaut_blade_fury_custom_point"}
|
||||
)
|
||||
____exports.modifier_juggernaut_blade_fury_custom_point = modifier_juggernaut_blade_fury_custom_point
|
||||
____exports.modifier_juggernaut_blade_fury_fly = __TS__Class()
|
||||
local modifier_juggernaut_blade_fury_fly = ____exports.modifier_juggernaut_blade_fury_fly
|
||||
modifier_juggernaut_blade_fury_fly.name = "modifier_juggernaut_blade_fury_fly"
|
||||
modifier_juggernaut_blade_fury_fly.____file_path = "scripts/vscripts/abilities/heroes/juggernaut/ability_juggernaut_blade_fury_custom.lua"
|
||||
__TS__ClassExtends(modifier_juggernaut_blade_fury_fly, BaseModifier)
|
||||
function modifier_juggernaut_blade_fury_fly.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.speed = BLADE_FURY_FLY_SPEED
|
||||
self.interval = BLADE_FURY_FLY_INTERVAL
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_fly.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_fly.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_fly.prototype.RemoveOnDeath(self)
|
||||
return false
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_fly.prototype.OnCreated(self, params)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local z = GetGroundPosition(
|
||||
Vector(params.x or 0, params.y or 0, 0),
|
||||
nil
|
||||
).z
|
||||
self.targetPos = Vector(params.x or 0, params.y or 0, z)
|
||||
self:StartIntervalThink(self.interval)
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_fly.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local caster = self:GetCaster()
|
||||
if not caster or not caster:IsAlive() then
|
||||
cleanupBladeFuryThinkerAfterCasterLost(nil, parent, caster)
|
||||
return
|
||||
end
|
||||
local pos = parent:GetAbsOrigin()
|
||||
local delta = Vector(self.targetPos.x - pos.x, self.targetPos.y - pos.y, self.targetPos.z - pos.z)
|
||||
local dist = delta:Length2D()
|
||||
local dir = delta:Normalized()
|
||||
local step = self.speed * self.interval
|
||||
if dist <= step + 5 then
|
||||
parent:SetAbsOrigin(self.targetPos)
|
||||
local ability = self:GetAbility()
|
||||
local duration = ability and ability:GetSpecialValueFor("duration") or 5
|
||||
parent:AddNewModifier(
|
||||
self:GetCaster(),
|
||||
ability,
|
||||
"modifier_juggernaut_blade_fury_pause",
|
||||
{duration = duration}
|
||||
)
|
||||
self:Destroy()
|
||||
return
|
||||
end
|
||||
local newPos = Vector(pos.x + dir.x * step, pos.y + dir.y * step, pos.z + dir.z * step)
|
||||
parent:SetAbsOrigin(GetGroundPosition(newPos, nil))
|
||||
end
|
||||
modifier_juggernaut_blade_fury_fly = __TS__Decorate(
|
||||
modifier_juggernaut_blade_fury_fly,
|
||||
modifier_juggernaut_blade_fury_fly,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_juggernaut_blade_fury_fly"}
|
||||
)
|
||||
____exports.modifier_juggernaut_blade_fury_fly = modifier_juggernaut_blade_fury_fly
|
||||
____exports.modifier_juggernaut_blade_fury_pause = __TS__Class()
|
||||
local modifier_juggernaut_blade_fury_pause = ____exports.modifier_juggernaut_blade_fury_pause
|
||||
modifier_juggernaut_blade_fury_pause.name = "modifier_juggernaut_blade_fury_pause"
|
||||
modifier_juggernaut_blade_fury_pause.____file_path = "scripts/vscripts/abilities/heroes/juggernaut/ability_juggernaut_blade_fury_custom.lua"
|
||||
__TS__ClassExtends(modifier_juggernaut_blade_fury_pause, BaseModifier)
|
||||
function modifier_juggernaut_blade_fury_pause.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_pause.prototype.RemoveOnDeath(self)
|
||||
return false
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_pause.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_pause.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
if not caster or not caster:IsAlive() then
|
||||
return
|
||||
end
|
||||
self:GetParent():AddNewModifier(
|
||||
caster,
|
||||
self:GetAbility(),
|
||||
"modifier_juggernaut_blade_fury_fly_back",
|
||||
{}
|
||||
)
|
||||
end
|
||||
modifier_juggernaut_blade_fury_pause = __TS__Decorate(
|
||||
modifier_juggernaut_blade_fury_pause,
|
||||
modifier_juggernaut_blade_fury_pause,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_juggernaut_blade_fury_pause"}
|
||||
)
|
||||
____exports.modifier_juggernaut_blade_fury_pause = modifier_juggernaut_blade_fury_pause
|
||||
____exports.modifier_juggernaut_blade_fury_fly_back = __TS__Class()
|
||||
local modifier_juggernaut_blade_fury_fly_back = ____exports.modifier_juggernaut_blade_fury_fly_back
|
||||
modifier_juggernaut_blade_fury_fly_back.name = "modifier_juggernaut_blade_fury_fly_back"
|
||||
modifier_juggernaut_blade_fury_fly_back.____file_path = "scripts/vscripts/abilities/heroes/juggernaut/ability_juggernaut_blade_fury_custom.lua"
|
||||
__TS__ClassExtends(modifier_juggernaut_blade_fury_fly_back, BaseModifier)
|
||||
function modifier_juggernaut_blade_fury_fly_back.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.interval = BLADE_FURY_FLY_INTERVAL
|
||||
self.speed = BLADE_FURY_FLY_SPEED
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_fly_back.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_fly_back.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_fly_back.prototype.RemoveOnDeath(self)
|
||||
return false
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_fly_back.prototype.OnCreated(self, _params)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self:StartIntervalThink(self.interval)
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_fly_back.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local caster = self:GetCaster()
|
||||
if not caster or not caster:IsAlive() then
|
||||
cleanupBladeFuryThinkerAfterCasterLost(nil, parent, caster)
|
||||
return
|
||||
end
|
||||
local pos = parent:GetAbsOrigin()
|
||||
local casterPos = caster:GetAbsOrigin()
|
||||
local delta = Vector(casterPos.x - pos.x, casterPos.y - pos.y, casterPos.z - pos.z)
|
||||
local dist = delta:Length2D()
|
||||
local dir = delta:Normalized()
|
||||
local step = self.speed * self.interval
|
||||
if dist <= 50 then
|
||||
local thinkerMod = parent:FindModifierByName("modifier_juggernaut_blade_fury_thinker")
|
||||
if thinkerMod then
|
||||
thinkerMod:Destroy()
|
||||
end
|
||||
self:Destroy()
|
||||
return
|
||||
end
|
||||
local move = math.min(step, dist)
|
||||
local newPos = Vector(pos.x + dir.x * move, pos.y + dir.y * move, pos.z + dir.z * move)
|
||||
parent:SetAbsOrigin(GetGroundPosition(newPos, nil))
|
||||
end
|
||||
modifier_juggernaut_blade_fury_fly_back = __TS__Decorate(
|
||||
modifier_juggernaut_blade_fury_fly_back,
|
||||
modifier_juggernaut_blade_fury_fly_back,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_juggernaut_blade_fury_fly_back"}
|
||||
)
|
||||
____exports.modifier_juggernaut_blade_fury_fly_back = modifier_juggernaut_blade_fury_fly_back
|
||||
____exports.ability_juggernaut_blade_fury_custom_alt = __TS__Class()
|
||||
local ability_juggernaut_blade_fury_custom_alt = ____exports.ability_juggernaut_blade_fury_custom_alt
|
||||
ability_juggernaut_blade_fury_custom_alt.name = "ability_juggernaut_blade_fury_custom_alt"
|
||||
ability_juggernaut_blade_fury_custom_alt.____file_path = "scripts/vscripts/abilities/heroes/juggernaut/ability_juggernaut_blade_fury_custom.lua"
|
||||
__TS__ClassExtends(ability_juggernaut_blade_fury_custom_alt, BaseAbility)
|
||||
function ability_juggernaut_blade_fury_custom_alt.prototype.GetIntrinsicModifierName(self)
|
||||
return "modifier_juggernaut_blade_fury_custom_passive"
|
||||
end
|
||||
function ability_juggernaut_blade_fury_custom_alt.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local point = self:GetCursorPosition()
|
||||
local casterPos = caster:GetAbsOrigin()
|
||||
local castRange = self:GetCastRange(casterPos, nil)
|
||||
local delta = Vector(point.x - casterPos.x, point.y - casterPos.y, 0)
|
||||
local dist2D = delta:Length2D()
|
||||
local clampedPoint = dist2D > castRange and dist2D > 0 and Vector(casterPos.x + delta.x / dist2D * castRange, casterPos.y + delta.y / dist2D * castRange, point.z) or point
|
||||
local groundPoint = GetGroundPosition(clampedPoint, nil)
|
||||
local thinker = CreateModifierThinker(
|
||||
caster,
|
||||
self,
|
||||
"modifier_juggernaut_blade_fury_thinker",
|
||||
{x = groundPoint.x, y = groundPoint.y, z = groundPoint.z},
|
||||
caster:GetAbsOrigin(),
|
||||
caster:GetTeamNumber(),
|
||||
false
|
||||
)
|
||||
if thinker and IsValidEntity(thinker) then
|
||||
thinker:AddNewModifier(caster, self, "modifier_juggernaut_blade_fury_sword_unit", {})
|
||||
thinker:AddNewModifier(caster, self, "modifier_juggernaut_blade_fury_custom_point", {})
|
||||
thinker:AddNewModifier(caster, self, "modifier_juggernaut_blade_fury_fly", {x = groundPoint.x, y = groundPoint.y})
|
||||
EmitSoundOn("Hero_Juggernaut.BladeFuryStart", thinker)
|
||||
end
|
||||
end
|
||||
ability_juggernaut_blade_fury_custom_alt = __TS__Decorate(
|
||||
ability_juggernaut_blade_fury_custom_alt,
|
||||
ability_juggernaut_blade_fury_custom_alt,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_juggernaut_blade_fury_custom_alt"}
|
||||
)
|
||||
____exports.ability_juggernaut_blade_fury_custom_alt = ability_juggernaut_blade_fury_custom_alt
|
||||
____exports.modifier_juggernaut_blade_fury_thinker = __TS__Class()
|
||||
local modifier_juggernaut_blade_fury_thinker = ____exports.modifier_juggernaut_blade_fury_thinker
|
||||
modifier_juggernaut_blade_fury_thinker.name = "modifier_juggernaut_blade_fury_thinker"
|
||||
modifier_juggernaut_blade_fury_thinker.____file_path = "scripts/vscripts/abilities/heroes/juggernaut/ability_juggernaut_blade_fury_custom.lua"
|
||||
__TS__ClassExtends(modifier_juggernaut_blade_fury_thinker, BaseModifier)
|
||||
function modifier_juggernaut_blade_fury_thinker.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_thinker.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_thinker.prototype.OnCreated(self, _params)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
if not caster or not caster:IsAlive() then
|
||||
self:Destroy()
|
||||
end
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_thinker.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
if not caster or not caster:IsAlive() then
|
||||
self:Destroy()
|
||||
end
|
||||
end
|
||||
function modifier_juggernaut_blade_fury_thinker.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
StopSoundOn("Hero_Juggernaut.BladeFuryStart", parent)
|
||||
EmitSoundOn("Hero_Juggernaut.BladeFuryStop", parent)
|
||||
if parent and IsValidEntity(parent) then
|
||||
UTIL_Remove(parent)
|
||||
end
|
||||
end
|
||||
modifier_juggernaut_blade_fury_thinker = __TS__Decorate(
|
||||
modifier_juggernaut_blade_fury_thinker,
|
||||
modifier_juggernaut_blade_fury_thinker,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_juggernaut_blade_fury_thinker"}
|
||||
)
|
||||
____exports.modifier_juggernaut_blade_fury_thinker = modifier_juggernaut_blade_fury_thinker
|
||||
return ____exports
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
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 ____heal_tracker = require("utils.heal_tracker")
|
||||
local HealWithBattlePass = ____heal_tracker.HealWithBattlePass
|
||||
____exports.ability_juggernaut_healing_ward_custom = __TS__Class()
|
||||
local ability_juggernaut_healing_ward_custom = ____exports.ability_juggernaut_healing_ward_custom
|
||||
ability_juggernaut_healing_ward_custom.name = "ability_juggernaut_healing_ward_custom"
|
||||
ability_juggernaut_healing_ward_custom.____file_path = "scripts/vscripts/abilities/heroes/juggernaut/ability_juggernaut_healing_ward_custom.lua"
|
||||
__TS__ClassExtends(ability_juggernaut_healing_ward_custom, BaseAbility)
|
||||
function ability_juggernaut_healing_ward_custom.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local point = self:GetCursorPosition()
|
||||
local duration = self:GetSpecialValueFor("ward_duration")
|
||||
local ward = CreateUnitByName(
|
||||
"npc_dota_juggernaut_healing_ward_custom",
|
||||
point,
|
||||
true,
|
||||
caster,
|
||||
caster,
|
||||
caster:GetTeamNumber()
|
||||
)
|
||||
if not ward or not IsValidEntity(ward) then
|
||||
return
|
||||
end
|
||||
Timers:CreateTimer(
|
||||
0.2,
|
||||
function()
|
||||
ward:MoveToNPC(caster)
|
||||
return nil
|
||||
end
|
||||
)
|
||||
ward:SetOwner(caster)
|
||||
ward:SetControllableByPlayer(
|
||||
caster:GetPlayerOwnerID(),
|
||||
false
|
||||
)
|
||||
ward:AddNewModifier(caster, self, "modifier_juggernaut_healing_ward_custom", {duration = duration})
|
||||
EmitSoundOn("Hero_Juggernaut.HealingWard.Cast", caster)
|
||||
EmitSoundOn("Hero_Juggernaut.HealingWard.Loop", ward)
|
||||
end
|
||||
ability_juggernaut_healing_ward_custom = __TS__Decorate(
|
||||
ability_juggernaut_healing_ward_custom,
|
||||
ability_juggernaut_healing_ward_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_juggernaut_healing_ward_custom"}
|
||||
)
|
||||
____exports.ability_juggernaut_healing_ward_custom = ability_juggernaut_healing_ward_custom
|
||||
____exports.modifier_juggernaut_healing_ward_custom = __TS__Class()
|
||||
local modifier_juggernaut_healing_ward_custom = ____exports.modifier_juggernaut_healing_ward_custom
|
||||
modifier_juggernaut_healing_ward_custom.name = "modifier_juggernaut_healing_ward_custom"
|
||||
modifier_juggernaut_healing_ward_custom.____file_path = "scripts/vscripts/abilities/heroes/juggernaut/ability_juggernaut_healing_ward_custom.lua"
|
||||
__TS__ClassExtends(modifier_juggernaut_healing_ward_custom, BaseModifier)
|
||||
function modifier_juggernaut_healing_ward_custom.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.healInterval = 1
|
||||
end
|
||||
function modifier_juggernaut_healing_ward_custom.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_juggernaut_healing_ward_custom.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_juggernaut_healing_ward_custom.prototype.OnCreated(self, params)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
self.particleId = ParticleManager:CreateParticle("particles/units/heroes/hero_juggernaut/juggernaut_healing_ward.vpcf", PATTACH_ABSORIGIN_FOLLOW, parent)
|
||||
self:StartIntervalThink(self.healInterval)
|
||||
end
|
||||
function modifier_juggernaut_healing_ward_custom.prototype.OnDestroy(self)
|
||||
if self.particleId ~= nil then
|
||||
ParticleManager:DestroyParticle(self.particleId, false)
|
||||
ParticleManager:ReleaseParticleIndex(self.particleId)
|
||||
self.particleId = nil
|
||||
self:GetParent():Kill(
|
||||
self:GetAbility(),
|
||||
nil
|
||||
)
|
||||
end
|
||||
StopSoundOn(
|
||||
"Hero_Juggernaut.HealingWard.Loop",
|
||||
self:GetParent()
|
||||
)
|
||||
end
|
||||
function modifier_juggernaut_healing_ward_custom.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local caster = self:GetCaster()
|
||||
local ability = self:GetAbility()
|
||||
if not ability or not caster or not parent:IsAlive() then
|
||||
return
|
||||
end
|
||||
local radius = ability:GetSpecialValueFor("heal_radius")
|
||||
local healPerSecond = ability:GetSpecialValueFor("heal_per_second")
|
||||
local allies = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
parent:GetAbsOrigin(),
|
||||
nil,
|
||||
radius,
|
||||
DOTA_UNIT_TARGET_TEAM_FRIENDLY,
|
||||
bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC),
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
for ____, ally in ipairs(allies) do
|
||||
if ally and ally:IsAlive() then
|
||||
local finalheal = healPerSecond * 0.01 * ally:GetMaxHealth()
|
||||
HealWithBattlePass(
|
||||
nil,
|
||||
ally,
|
||||
finalheal,
|
||||
ability,
|
||||
caster
|
||||
)
|
||||
local healParticle = ParticleManager:CreateParticle("particles/generic_gameplay/generic_lifesteal.vpcf", PATTACH_ABSORIGIN_FOLLOW, ally)
|
||||
ParticleManager:ReleaseParticleIndex(healParticle)
|
||||
SendOverheadEventMessage(
|
||||
nil,
|
||||
OVERHEAD_ALERT_HEAL,
|
||||
ally,
|
||||
finalheal,
|
||||
nil
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
function modifier_juggernaut_healing_ward_custom.prototype.GetEffectName(self)
|
||||
return "particles/units/heroes/hero_juggernaut/juggernaut_healing_ward.vpcf"
|
||||
end
|
||||
function modifier_juggernaut_healing_ward_custom.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_ABSORIGIN_FOLLOW
|
||||
end
|
||||
function modifier_juggernaut_healing_ward_custom.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_NO_HEALTH_BAR] = true, [MODIFIER_STATE_INVULNERABLE] = true}
|
||||
end
|
||||
modifier_juggernaut_healing_ward_custom = __TS__Decorate(
|
||||
modifier_juggernaut_healing_ward_custom,
|
||||
modifier_juggernaut_healing_ward_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_juggernaut_healing_ward_custom"}
|
||||
)
|
||||
____exports.modifier_juggernaut_healing_ward_custom = modifier_juggernaut_healing_ward_custom
|
||||
return ____exports
|
||||
@@ -0,0 +1,348 @@
|
||||
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
|
||||
____exports.ability_juggernaut_omnislash_custom = __TS__Class()
|
||||
local ability_juggernaut_omnislash_custom = ____exports.ability_juggernaut_omnislash_custom
|
||||
ability_juggernaut_omnislash_custom.name = "ability_juggernaut_omnislash_custom"
|
||||
ability_juggernaut_omnislash_custom.____file_path = "scripts/vscripts/abilities/heroes/juggernaut/ability_juggernaut_omnislash_custom.lua"
|
||||
__TS__ClassExtends(ability_juggernaut_omnislash_custom, BaseAbility)
|
||||
function ability_juggernaut_omnislash_custom.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local target = self:GetCursorTarget()
|
||||
if not target or not target:IsAlive() then
|
||||
return
|
||||
end
|
||||
local duration = self:GetSpecialValueFor("duration")
|
||||
caster:AddNewModifier(
|
||||
caster,
|
||||
self,
|
||||
"modifier_juggernaut_omnislash_custom",
|
||||
{
|
||||
target = target:entindex(),
|
||||
duration = duration
|
||||
}
|
||||
)
|
||||
EmitSoundOn("Hero_Juggernaut.Omnislash", caster)
|
||||
end
|
||||
ability_juggernaut_omnislash_custom = __TS__Decorate(
|
||||
ability_juggernaut_omnislash_custom,
|
||||
ability_juggernaut_omnislash_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_juggernaut_omnislash_custom"}
|
||||
)
|
||||
____exports.ability_juggernaut_omnislash_custom = ability_juggernaut_omnislash_custom
|
||||
____exports.ability_juggernaut_miniomnislash_custom = __TS__Class()
|
||||
local ability_juggernaut_miniomnislash_custom = ____exports.ability_juggernaut_miniomnislash_custom
|
||||
ability_juggernaut_miniomnislash_custom.name = "ability_juggernaut_miniomnislash_custom"
|
||||
ability_juggernaut_miniomnislash_custom.____file_path = "scripts/vscripts/abilities/heroes/juggernaut/ability_juggernaut_omnislash_custom.lua"
|
||||
__TS__ClassExtends(ability_juggernaut_miniomnislash_custom, BaseAbility)
|
||||
function ability_juggernaut_miniomnislash_custom.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local target = self:GetCursorTarget()
|
||||
if not target or not target:IsAlive() then
|
||||
return
|
||||
end
|
||||
local duration = self:GetSpecialValueFor("duration")
|
||||
caster:AddNewModifier(
|
||||
caster,
|
||||
self,
|
||||
"modifier_juggernaut_omnislash_custom",
|
||||
{
|
||||
target = target:entindex(),
|
||||
duration = duration
|
||||
}
|
||||
)
|
||||
EmitSoundOn("Hero_Juggernaut.Omnislash", caster)
|
||||
end
|
||||
ability_juggernaut_miniomnislash_custom = __TS__Decorate(
|
||||
ability_juggernaut_miniomnislash_custom,
|
||||
ability_juggernaut_miniomnislash_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_juggernaut_miniomnislash_custom"}
|
||||
)
|
||||
____exports.ability_juggernaut_miniomnislash_custom = ability_juggernaut_miniomnislash_custom
|
||||
____exports.modifier_juggernaut_omnislash_custom = __TS__Class()
|
||||
local modifier_juggernaut_omnislash_custom = ____exports.modifier_juggernaut_omnislash_custom
|
||||
modifier_juggernaut_omnislash_custom.name = "modifier_juggernaut_omnislash_custom"
|
||||
modifier_juggernaut_omnislash_custom.____file_path = "scripts/vscripts/abilities/heroes/juggernaut/ability_juggernaut_omnislash_custom.lua"
|
||||
__TS__ClassExtends(modifier_juggernaut_omnislash_custom, BaseModifier)
|
||||
function modifier_juggernaut_omnislash_custom.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.attackCount = 0
|
||||
self.interval = 0
|
||||
self.radius = 0
|
||||
self.endInterval = 0.15
|
||||
end
|
||||
function modifier_juggernaut_omnislash_custom.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_juggernaut_omnislash_custom.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_juggernaut_omnislash_custom.prototype.CheckState(self)
|
||||
return {
|
||||
[MODIFIER_STATE_NO_UNIT_COLLISION] = true,
|
||||
[MODIFIER_STATE_INVULNERABLE] = true,
|
||||
[MODIFIER_STATE_NO_HEALTH_BAR] = true,
|
||||
[MODIFIER_STATE_DISARMED] = true,
|
||||
[MODIFIER_STATE_ROOTED] = true,
|
||||
[MODIFIER_STATE_SILENCED] = true
|
||||
}
|
||||
end
|
||||
function modifier_juggernaut_omnislash_custom.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_IGNORE_CAST_ANGLE}
|
||||
end
|
||||
function modifier_juggernaut_omnislash_custom.prototype.GetModifierIgnoreCastAngle(self)
|
||||
return 1
|
||||
end
|
||||
function modifier_juggernaut_omnislash_custom.prototype.OnCreated(self, params)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
if not caster or not ability then
|
||||
return
|
||||
end
|
||||
self.interval = 1 / self:GetCaster():GetAttacksPerSecond(false) / ability:GetSpecialValueFor("slash_interval_mult")
|
||||
self.radius = ability:GetSpecialValueFor("bounce_radius")
|
||||
if params.target ~= nil then
|
||||
self.currentTarget = EntIndexToHScript(params.target)
|
||||
end
|
||||
caster:StartGesture(ACT_DOTA_OVERRIDE_ABILITY_4)
|
||||
self:OnIntervalThink()
|
||||
self:StartIntervalThink(self.interval)
|
||||
end
|
||||
function modifier_juggernaut_omnislash_custom.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self.attackCount = self.attackCount + 1
|
||||
local caster = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
if not caster or not caster:IsAlive() or not ability then
|
||||
self:Destroy()
|
||||
return
|
||||
end
|
||||
if self:GetRemainingTime() <= 0 then
|
||||
self:Destroy()
|
||||
return
|
||||
end
|
||||
local enemy = nil
|
||||
local final = self.currentTarget == nil
|
||||
if self.currentTarget and IsValidEntity(self.currentTarget) and self.currentTarget:IsAlive() and not self.currentTarget:IsOutOfGame() and (not self.currentTarget:IsInvisible() or caster:CanEntityBeSeenByMyTeam(self.currentTarget)) then
|
||||
local distance = (self.currentTarget:GetAbsOrigin() - caster:GetAbsOrigin()):Length2D()
|
||||
if distance <= self.radius or self.attackCount == 1 then
|
||||
enemy = self.currentTarget
|
||||
end
|
||||
end
|
||||
if not enemy then
|
||||
local targets = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
caster:GetAbsOrigin(),
|
||||
nil,
|
||||
self.radius,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC),
|
||||
bit.bor(
|
||||
bit.bor(DOTA_UNIT_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES, DOTA_UNIT_TARGET_FLAG_NO_INVIS),
|
||||
DOTA_UNIT_TARGET_FLAG_INVULNERABLE
|
||||
),
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
if targets and #targets > 0 then
|
||||
enemy = targets[1]
|
||||
end
|
||||
end
|
||||
if not enemy then
|
||||
if final then
|
||||
self:Destroy()
|
||||
return
|
||||
end
|
||||
self.currentTarget = nil
|
||||
self:StartIntervalThink(self.endInterval)
|
||||
return
|
||||
end
|
||||
self.currentTarget = enemy
|
||||
self:PlayEffect(enemy)
|
||||
self:DealAttack(enemy)
|
||||
if self:GetRemainingTime() > self.interval then
|
||||
self:StartIntervalThink(self.interval)
|
||||
end
|
||||
end
|
||||
function modifier_juggernaut_omnislash_custom.prototype.PlayEffect(self, enemy)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetParent()
|
||||
caster:RemoveGesture(ACT_DOTA_OVERRIDE_ABILITY_4)
|
||||
caster:StartGesture(ACT_DOTA_OVERRIDE_ABILITY_4)
|
||||
local position1 = caster:GetAbsOrigin()
|
||||
local turn = (enemy:GetAbsOrigin() - position1):Normalized()
|
||||
turn.z = 0
|
||||
local linePos = enemy:GetAbsOrigin() + turn * 90
|
||||
local angle = -45 + 90 * RandomInt(0, 1)
|
||||
local finalPos = rotatePositionYaw(
|
||||
nil,
|
||||
enemy:GetAbsOrigin(),
|
||||
angle,
|
||||
linePos
|
||||
)
|
||||
caster:SetAbsOrigin(finalPos)
|
||||
local position2 = caster:GetAbsOrigin()
|
||||
local direction = (enemy:GetAbsOrigin() - caster:GetAbsOrigin()):Normalized()
|
||||
direction.z = 0
|
||||
caster:SetForwardVector(direction)
|
||||
caster:FaceTowards(enemy:GetAbsOrigin())
|
||||
local trailParticle = ParticleManager:CreateParticle("particles/units/heroes/hero_juggernaut/juggernaut_omni_slash_trail.vpcf", PATTACH_ABSORIGIN, caster)
|
||||
ParticleManager:SetParticleControl(trailParticle, 0, position1)
|
||||
ParticleManager:SetParticleControl(trailParticle, 1, position2)
|
||||
ParticleManager:ReleaseParticleIndex(trailParticle)
|
||||
if self.attackCount == 1 then
|
||||
local dashParticle = ParticleManager:CreateParticle("particles/units/heroes/hero_juggernaut/juggernaut_omni_dash.vpcf", PATTACH_CUSTOMORIGIN, caster)
|
||||
local vDirection = (position2 - position1):Normalized()
|
||||
ParticleManager:SetParticleControl(dashParticle, 0, position1)
|
||||
ParticleManager:SetParticleControlForward(dashParticle, 0, vDirection * -1)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
dashParticle,
|
||||
1,
|
||||
enemy,
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
"attach_hitloc",
|
||||
enemy:GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
dashParticle,
|
||||
2,
|
||||
enemy,
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
"attach_hitloc",
|
||||
enemy:GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(dashParticle)
|
||||
end
|
||||
end
|
||||
function modifier_juggernaut_omnislash_custom.prototype.DealAttack(self, target)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
if not caster or not ability then
|
||||
return
|
||||
end
|
||||
local attackModifier = caster:AddNewModifier(caster, ability, "modifier_juggernaut_omnislash_attack", {})
|
||||
caster:PerformAttack(
|
||||
target,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
)
|
||||
if attackModifier ~= nil then
|
||||
attackModifier:Destroy()
|
||||
end
|
||||
local tgtParticle = ParticleManager:CreateParticle("particles/units/heroes/hero_juggernaut/juggernaut_omni_slash_tgt.vpcf", PATTACH_ABSORIGIN_FOLLOW, target)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
tgtParticle,
|
||||
0,
|
||||
target,
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
"attach_hitloc",
|
||||
target:GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
tgtParticle,
|
||||
1,
|
||||
target,
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
"attach_hitloc",
|
||||
target:GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(tgtParticle)
|
||||
EmitSoundOn("Hero_Juggernaut.OmniSlash", target)
|
||||
end
|
||||
function modifier_juggernaut_omnislash_custom.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetParent()
|
||||
if caster and caster:IsAlive() then
|
||||
caster:FadeGesture(ACT_DOTA_OVERRIDE_ABILITY_4)
|
||||
caster:Stop()
|
||||
end
|
||||
EmitSoundOn("Hero_Juggernaut.Omnislash.End", caster)
|
||||
end
|
||||
function modifier_juggernaut_omnislash_custom.prototype.GetStatusEffectName(self)
|
||||
return "particles/status_fx/status_effect_omnislash.vpcf"
|
||||
end
|
||||
function modifier_juggernaut_omnislash_custom.prototype.StatusEffectPriority(self)
|
||||
return 10
|
||||
end
|
||||
modifier_juggernaut_omnislash_custom = __TS__Decorate(
|
||||
modifier_juggernaut_omnislash_custom,
|
||||
modifier_juggernaut_omnislash_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_juggernaut_omnislash_custom"}
|
||||
)
|
||||
____exports.modifier_juggernaut_omnislash_custom = modifier_juggernaut_omnislash_custom
|
||||
____exports.modifier_juggernaut_omnislash_attack = __TS__Class()
|
||||
local modifier_juggernaut_omnislash_attack = ____exports.modifier_juggernaut_omnislash_attack
|
||||
modifier_juggernaut_omnislash_attack.name = "modifier_juggernaut_omnislash_attack"
|
||||
modifier_juggernaut_omnislash_attack.____file_path = "scripts/vscripts/abilities/heroes/juggernaut/ability_juggernaut_omnislash_custom.lua"
|
||||
__TS__ClassExtends(modifier_juggernaut_omnislash_attack, BaseModifier)
|
||||
function modifier_juggernaut_omnislash_attack.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.damagePercent = 0
|
||||
end
|
||||
function modifier_juggernaut_omnislash_attack.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_juggernaut_omnislash_attack.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
self.damagePercent = ability:GetSpecialValueFor("damage") - 100
|
||||
end
|
||||
function modifier_juggernaut_omnislash_attack.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_DAMAGEOUTGOING_PERCENTAGE}
|
||||
end
|
||||
function modifier_juggernaut_omnislash_attack.prototype.GetModifierDamageOutgoing_Percentage(self, event)
|
||||
if event.inflictor then
|
||||
return 0
|
||||
end
|
||||
return self.damagePercent
|
||||
end
|
||||
modifier_juggernaut_omnislash_attack = __TS__Decorate(
|
||||
modifier_juggernaut_omnislash_attack,
|
||||
modifier_juggernaut_omnislash_attack,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_juggernaut_omnislash_attack"}
|
||||
)
|
||||
____exports.modifier_juggernaut_omnislash_attack = modifier_juggernaut_omnislash_attack
|
||||
return ____exports
|
||||
@@ -0,0 +1,193 @@
|
||||
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
|
||||
____exports.ability_juggernaut_samurai_soul = __TS__Class()
|
||||
local ability_juggernaut_samurai_soul = ____exports.ability_juggernaut_samurai_soul
|
||||
ability_juggernaut_samurai_soul.name = "ability_juggernaut_samurai_soul"
|
||||
ability_juggernaut_samurai_soul.____file_path = "scripts/vscripts/abilities/heroes/juggernaut/ability_juggernaut_samurai_soul.lua"
|
||||
__TS__ClassExtends(ability_juggernaut_samurai_soul, BaseAbility)
|
||||
function ability_juggernaut_samurai_soul.prototype.GetIntrinsicModifierName(self)
|
||||
return "modifier_juggernaut_samurai_soul"
|
||||
end
|
||||
ability_juggernaut_samurai_soul = __TS__Decorate(
|
||||
ability_juggernaut_samurai_soul,
|
||||
ability_juggernaut_samurai_soul,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_juggernaut_samurai_soul"}
|
||||
)
|
||||
____exports.ability_juggernaut_samurai_soul = ability_juggernaut_samurai_soul
|
||||
____exports.modifier_juggernaut_samurai_soul = __TS__Class()
|
||||
local modifier_juggernaut_samurai_soul = ____exports.modifier_juggernaut_samurai_soul
|
||||
modifier_juggernaut_samurai_soul.name = "modifier_juggernaut_samurai_soul"
|
||||
modifier_juggernaut_samurai_soul.____file_path = "scripts/vscripts/abilities/heroes/juggernaut/ability_juggernaut_samurai_soul.lua"
|
||||
__TS__ClassExtends(modifier_juggernaut_samurai_soul, BaseModifier)
|
||||
function modifier_juggernaut_samurai_soul.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.lastAttackTime = 0
|
||||
self.RESET_TIMEOUT = self:GetAbility():GetSpecialValueFor("cooldown")
|
||||
self.maxRestoreCount = 2
|
||||
end
|
||||
function modifier_juggernaut_samurai_soul.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_juggernaut_samurai_soul.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_juggernaut_samurai_soul.prototype.IsDebuff(self)
|
||||
if self:GetStackCount() > 0 then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
function modifier_juggernaut_samurai_soul.prototype.RemoveOnDeath(self)
|
||||
return false
|
||||
end
|
||||
function modifier_juggernaut_samurai_soul.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
if ability then
|
||||
local configuredLimit = ability:GetSpecialValueFor("max_restore_count")
|
||||
if configuredLimit > 0 then
|
||||
self.maxRestoreCount = configuredLimit
|
||||
end
|
||||
end
|
||||
self:StartIntervalThink(0.1)
|
||||
end
|
||||
function modifier_juggernaut_samurai_soul.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local currentTime = GameRules:GetGameTime()
|
||||
local timeSinceLastAttack = currentTime - self.lastAttackTime
|
||||
if timeSinceLastAttack >= self.RESET_TIMEOUT and self:GetStackCount() > 0 then
|
||||
local parent = self:GetParent()
|
||||
if parent and parent:IsRealHero() then
|
||||
local hero = parent
|
||||
self:SetStackCount(0)
|
||||
hero:CalculateStatBonus(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
function modifier_juggernaut_samurai_soul.prototype.DeclareFunctions(self)
|
||||
return {
|
||||
MODIFIER_PROPERTY_MIN_HEALTH,
|
||||
MODIFIER_PROPERTY_STATS_AGILITY_BONUS,
|
||||
MODIFIER_PROPERTY_STATS_INTELLECT_BONUS,
|
||||
MODIFIER_PROPERTY_STATS_STRENGTH_BONUS,
|
||||
MODIFIER_EVENT_ON_DEATH
|
||||
}
|
||||
end
|
||||
function modifier_juggernaut_samurai_soul.prototype.OnDeath(self, event)
|
||||
if event.unit == self:GetParent() then
|
||||
self:GetCaster():RemoveModifierByName("modifier_juggernaut_samurai_soul")
|
||||
end
|
||||
end
|
||||
function modifier_juggernaut_samurai_soul.prototype.GetModifierBonusStats_Strength(self)
|
||||
if not IsServer() then
|
||||
return 0
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
if not parent then
|
||||
return 0
|
||||
end
|
||||
if parent:PassivesDisabled() then
|
||||
return 0
|
||||
end
|
||||
if not parent:IsRealHero() then
|
||||
return 0
|
||||
end
|
||||
local hero = parent
|
||||
local baseStr = hero:GetBaseStrength()
|
||||
return -baseStr * (self:GetStackCount() * (0.01 * self:GetAbility():GetSpecialValueFor("debuff_pct")))
|
||||
end
|
||||
function modifier_juggernaut_samurai_soul.prototype.GetModifierBonusStats_Agility(self)
|
||||
if not IsServer() then
|
||||
return 0
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
if not parent then
|
||||
return 0
|
||||
end
|
||||
if parent:PassivesDisabled() then
|
||||
return 0
|
||||
end
|
||||
if not parent:IsRealHero() then
|
||||
return 0
|
||||
end
|
||||
local hero = parent
|
||||
local baseAgi = hero:GetBaseAgility()
|
||||
return -baseAgi * (self:GetStackCount() * (0.01 * self:GetAbility():GetSpecialValueFor("debuff_pct")))
|
||||
end
|
||||
function modifier_juggernaut_samurai_soul.prototype.GetModifierBonusStats_Intellect(self)
|
||||
if not IsServer() then
|
||||
return 0
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
if not parent then
|
||||
return 0
|
||||
end
|
||||
if parent:PassivesDisabled() then
|
||||
return 0
|
||||
end
|
||||
if not parent:IsRealHero() then
|
||||
return 0
|
||||
end
|
||||
local hero = parent
|
||||
local baseInt = hero:GetBaseIntellect()
|
||||
return -baseInt * (self:GetStackCount() * (0.01 * self:GetAbility():GetSpecialValueFor("debuff_pct")))
|
||||
end
|
||||
function modifier_juggernaut_samurai_soul.prototype.GetMinHealth(self)
|
||||
if self:GetParent():PassivesDisabled() then
|
||||
return 0
|
||||
end
|
||||
self:SetDuration(11, true)
|
||||
self.lastAttackTime = GameRules:GetGameTime()
|
||||
if self:GetStackCount() >= self.maxRestoreCount then
|
||||
return 0
|
||||
end
|
||||
if self:GetCaster():GetHealth() < 10 then
|
||||
self:GetCaster():SetHealth(self:GetCaster():GetMaxHealth())
|
||||
self:IncrementStackCount()
|
||||
self:GetCaster():CalculateStatBonus(true)
|
||||
end
|
||||
return 1
|
||||
end
|
||||
function modifier_juggernaut_samurai_soul.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local ability = self:GetAbility()
|
||||
if caster and not caster:IsNull() and caster:IsAlive() and ability and not ability:IsNull() then
|
||||
caster:AddNewModifier(
|
||||
self:GetParent(),
|
||||
ability,
|
||||
"modifier_juggernaut_samurai_soul",
|
||||
{}
|
||||
)
|
||||
end
|
||||
end
|
||||
function modifier_juggernaut_samurai_soul.prototype.GetTexture(self)
|
||||
if self:GetStackCount() > 0 then
|
||||
return "new_heroes/juggernaut_death"
|
||||
else
|
||||
return "new_heroes/juggernaut_miracle"
|
||||
end
|
||||
end
|
||||
modifier_juggernaut_samurai_soul = __TS__Decorate(
|
||||
modifier_juggernaut_samurai_soul,
|
||||
modifier_juggernaut_samurai_soul,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_juggernaut_samurai_soul"}
|
||||
)
|
||||
____exports.modifier_juggernaut_samurai_soul = modifier_juggernaut_samurai_soul
|
||||
return ____exports
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
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
|
||||
____exports.keeper_of_the_light_blinding_light_custom = __TS__Class()
|
||||
local keeper_of_the_light_blinding_light_custom = ____exports.keeper_of_the_light_blinding_light_custom
|
||||
keeper_of_the_light_blinding_light_custom.name = "keeper_of_the_light_blinding_light_custom"
|
||||
keeper_of_the_light_blinding_light_custom.____file_path = "scripts/vscripts/abilities/heroes/keeper_of_the_light/keeper_of_the_light_blinding_light_custom.lua"
|
||||
__TS__ClassExtends(keeper_of_the_light_blinding_light_custom, BaseAbility)
|
||||
function keeper_of_the_light_blinding_light_custom.prototype.GetAOERadius(self)
|
||||
return self:GetSpecialValueFor("radius")
|
||||
end
|
||||
function keeper_of_the_light_blinding_light_custom.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/econ/items/keeper_of_the_light/kotl_ti10_immortal/kotl_ti10_blinding_light.vpcf", context)
|
||||
end
|
||||
function keeper_of_the_light_blinding_light_custom.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local point = self:GetCursorPosition()
|
||||
local particle = ParticleManager:CreateParticle("particles/econ/items/keeper_of_the_light/kotl_ti10_immortal/kotl_ti10_blinding_light.vpcf", PATTACH_CUSTOMORIGIN, caster)
|
||||
ParticleManager:SetParticleControl(particle, 1, point)
|
||||
ParticleManager:ReleaseParticleIndex(particle)
|
||||
local enemies = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
point,
|
||||
nil,
|
||||
self:GetSpecialValueFor("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
|
||||
)
|
||||
EmitSoundOnLocationWithCaster(point, "Hero_KeeperOfTheLight.BlindingLight", caster)
|
||||
for ____, enemy in ipairs(enemies) do
|
||||
do
|
||||
ApplyDamage({
|
||||
victim = enemy,
|
||||
attacker = caster,
|
||||
damage = self:GetSpecialValueFor("damage"),
|
||||
damage_type = self:GetAbilityDamageType(),
|
||||
ability = self
|
||||
})
|
||||
if not enemy:IsAlive() then
|
||||
goto __continue6
|
||||
end
|
||||
if not enemy:IsBossCreature() then
|
||||
enemy:AddNewModifier(
|
||||
caster,
|
||||
self,
|
||||
"modifier_knockback",
|
||||
{
|
||||
center_x = point.x,
|
||||
center_y = point.y,
|
||||
center_z = point.z,
|
||||
duration = self:GetSpecialValueFor("knockback_duration"),
|
||||
knockback_duration = self:GetSpecialValueFor("knockback_duration"),
|
||||
knockback_distance = self:GetSpecialValueFor("knockback_distance"),
|
||||
knockback_height = 0
|
||||
}
|
||||
)
|
||||
end
|
||||
local debuff = enemy:AddNewModifier(
|
||||
caster,
|
||||
self,
|
||||
____exports.modifier_keeper_of_the_light_blinding_light_custom.name,
|
||||
{duration = self:GetSpecialValueFor("duration_stack")}
|
||||
)
|
||||
if not debuff then
|
||||
goto __continue6
|
||||
end
|
||||
local stacks = enemy:IsBossCreature() and self:GetSpecialValueFor("miss_stack_boss") or self:GetSpecialValueFor("miss_stack")
|
||||
debuff:SetStackCount(debuff:GetStackCount() + stacks)
|
||||
end
|
||||
::__continue6::
|
||||
end
|
||||
end
|
||||
keeper_of_the_light_blinding_light_custom = __TS__Decorate(
|
||||
keeper_of_the_light_blinding_light_custom,
|
||||
keeper_of_the_light_blinding_light_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "keeper_of_the_light_blinding_light_custom"}
|
||||
)
|
||||
____exports.keeper_of_the_light_blinding_light_custom = keeper_of_the_light_blinding_light_custom
|
||||
____exports.modifier_keeper_of_the_light_blinding_light_custom = __TS__Class()
|
||||
local modifier_keeper_of_the_light_blinding_light_custom = ____exports.modifier_keeper_of_the_light_blinding_light_custom
|
||||
modifier_keeper_of_the_light_blinding_light_custom.name = "modifier_keeper_of_the_light_blinding_light_custom"
|
||||
modifier_keeper_of_the_light_blinding_light_custom.____file_path = "scripts/vscripts/abilities/heroes/keeper_of_the_light/keeper_of_the_light_blinding_light_custom.lua"
|
||||
__TS__ClassExtends(modifier_keeper_of_the_light_blinding_light_custom, BaseModifier)
|
||||
function modifier_keeper_of_the_light_blinding_light_custom.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_MISS_PERCENTAGE, MODIFIER_EVENT_ON_ATTACK_FAIL}
|
||||
end
|
||||
function modifier_keeper_of_the_light_blinding_light_custom.prototype.GetModifierMiss_Percentage(self)
|
||||
return self:GetStackCount() > 0 and self:GetAbility():GetSpecialValueFor("miss_pct") or 0
|
||||
end
|
||||
function modifier_keeper_of_the_light_blinding_light_custom.prototype.OnAttackFail(self, event)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if event.attacker ~= self:GetParent() then
|
||||
return
|
||||
end
|
||||
if self:GetStackCount() > 0 then
|
||||
self:DecrementStackCount()
|
||||
end
|
||||
if self:GetStackCount() <= 0 then
|
||||
self:Destroy()
|
||||
end
|
||||
end
|
||||
function modifier_keeper_of_the_light_blinding_light_custom.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
function modifier_keeper_of_the_light_blinding_light_custom.prototype.IsPurgeException(self)
|
||||
return true
|
||||
end
|
||||
function modifier_keeper_of_the_light_blinding_light_custom.prototype.GetEffectName(self)
|
||||
return "particles/units/heroes/hero_keeper_of_the_light/keeper_of_the_light_blinding_light_debuff.vpcf"
|
||||
end
|
||||
modifier_keeper_of_the_light_blinding_light_custom = __TS__Decorate(
|
||||
modifier_keeper_of_the_light_blinding_light_custom,
|
||||
modifier_keeper_of_the_light_blinding_light_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_keeper_of_the_light_blinding_light_custom"}
|
||||
)
|
||||
____exports.modifier_keeper_of_the_light_blinding_light_custom = modifier_keeper_of_the_light_blinding_light_custom
|
||||
return ____exports
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
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 ____incoming_damage_reduction_combine = require("utils.incoming_damage_reduction_combine")
|
||||
local removeIncomingDamageReductionSource = ____incoming_damage_reduction_combine.removeIncomingDamageReductionSource
|
||||
local setIncomingDamageReductionSource = ____incoming_damage_reduction_combine.setIncomingDamageReductionSource
|
||||
local CHAKRA_MAGIC_INCOMING_SOURCE = "modifier_keeper_of_the_light_chakra_magic_buff"
|
||||
____exports.keeper_of_the_light_chakra_magic_custom = __TS__Class()
|
||||
local keeper_of_the_light_chakra_magic_custom = ____exports.keeper_of_the_light_chakra_magic_custom
|
||||
keeper_of_the_light_chakra_magic_custom.name = "keeper_of_the_light_chakra_magic_custom"
|
||||
keeper_of_the_light_chakra_magic_custom.____file_path = "scripts/vscripts/abilities/heroes/keeper_of_the_light/keeper_of_the_light_chakra_magic_custom.lua"
|
||||
__TS__ClassExtends(keeper_of_the_light_chakra_magic_custom, BaseAbility)
|
||||
function keeper_of_the_light_chakra_magic_custom.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local target = self:GetCursorTarget()
|
||||
if not target then
|
||||
return
|
||||
end
|
||||
self:OnCustomSpellStart(target)
|
||||
end
|
||||
function keeper_of_the_light_chakra_magic_custom.prototype.OnCustomSpellStart(self, target)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
target:GiveMana(self:GetSpecialValueFor("mana_restore"))
|
||||
do
|
||||
local i = 0
|
||||
while i < target:GetAbilityCount() - 1 do
|
||||
do
|
||||
local ability = target:GetAbilityByIndex(i)
|
||||
if not ability or ability:IsItem() or ability:GetName() == self:GetName() then
|
||||
goto __continue7
|
||||
end
|
||||
local cooldownReduction = self:GetSpecialValueFor("cooldown_reduction")
|
||||
local cooldownRemaining = ability:GetCooldownTimeRemaining()
|
||||
if cooldownRemaining > 0 then
|
||||
ability:EndCooldown()
|
||||
ability:StartCooldown(math.max(cooldownRemaining - cooldownReduction, 0))
|
||||
end
|
||||
local maxCharges = ability:GetMaxAbilityCharges(ability:GetLevel())
|
||||
if self:GetSpecialValueFor("refresh_charges") > 0 and maxCharges > 0 then
|
||||
ability:SetCurrentAbilityCharges(math.min(
|
||||
ability:GetCurrentAbilityCharges() + 1,
|
||||
maxCharges
|
||||
))
|
||||
end
|
||||
end
|
||||
::__continue7::
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
if self:GetSpecialValueFor("strong_dispel") > 0 then
|
||||
target:Purge(
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true
|
||||
)
|
||||
end
|
||||
local buffDuration = self:GetSpecialValueFor("damage_reduction_duration")
|
||||
if buffDuration > 0 then
|
||||
____exports.modifier_keeper_of_the_light_chakra_magic_buff:apply(target, caster, self, {duration = buffDuration})
|
||||
end
|
||||
local chakraCast = ParticleManager:CreateParticle("particles/units/heroes/hero_keeper_of_the_light/keeper_chakra_magic.vpcf", PATTACH_POINT_FOLLOW, caster)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
chakraCast,
|
||||
0,
|
||||
caster,
|
||||
PATTACH_POINT_FOLLOW,
|
||||
"attach_attack1",
|
||||
caster:GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
chakraCast,
|
||||
1,
|
||||
target,
|
||||
PATTACH_POINT_FOLLOW,
|
||||
"attach_hitloc",
|
||||
target:GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(chakraCast)
|
||||
EmitSoundOn("Hero_KeeperOfTheLight.ChakraMagic.Target", target)
|
||||
end
|
||||
keeper_of_the_light_chakra_magic_custom = __TS__Decorate(
|
||||
keeper_of_the_light_chakra_magic_custom,
|
||||
keeper_of_the_light_chakra_magic_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "keeper_of_the_light_chakra_magic_custom"}
|
||||
)
|
||||
____exports.keeper_of_the_light_chakra_magic_custom = keeper_of_the_light_chakra_magic_custom
|
||||
____exports.modifier_keeper_of_the_light_chakra_magic_buff = __TS__Class()
|
||||
local modifier_keeper_of_the_light_chakra_magic_buff = ____exports.modifier_keeper_of_the_light_chakra_magic_buff
|
||||
modifier_keeper_of_the_light_chakra_magic_buff.name = "modifier_keeper_of_the_light_chakra_magic_buff"
|
||||
modifier_keeper_of_the_light_chakra_magic_buff.____file_path = "scripts/vscripts/abilities/heroes/keeper_of_the_light/keeper_of_the_light_chakra_magic_custom.lua"
|
||||
__TS__ClassExtends(modifier_keeper_of_the_light_chakra_magic_buff, BaseModifier)
|
||||
function modifier_keeper_of_the_light_chakra_magic_buff.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.damageReductionPct = 0
|
||||
end
|
||||
function modifier_keeper_of_the_light_chakra_magic_buff.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_keeper_of_the_light_chakra_magic_buff.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_keeper_of_the_light_chakra_magic_buff.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
function modifier_keeper_of_the_light_chakra_magic_buff.prototype.IsPurgeException(self)
|
||||
return false
|
||||
end
|
||||
function modifier_keeper_of_the_light_chakra_magic_buff.prototype.GetAttributes(self)
|
||||
return MODIFIER_ATTRIBUTE_MULTIPLE
|
||||
end
|
||||
function modifier_keeper_of_the_light_chakra_magic_buff.prototype.OnCreated(self)
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
self.damageReductionPct = ability:GetSpecialValueFor("incoming_damage_reduction_pct")
|
||||
self:SetStackCount(self.damageReductionPct)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
setIncomingDamageReductionSource(
|
||||
nil,
|
||||
self:GetParent(),
|
||||
CHAKRA_MAGIC_INCOMING_SOURCE,
|
||||
function() return math.max(0, self.damageReductionPct) end
|
||||
)
|
||||
end
|
||||
function modifier_keeper_of_the_light_chakra_magic_buff.prototype.OnRefresh(self)
|
||||
self:OnCreated()
|
||||
end
|
||||
function modifier_keeper_of_the_light_chakra_magic_buff.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
removeIncomingDamageReductionSource(
|
||||
nil,
|
||||
self:GetParent(),
|
||||
CHAKRA_MAGIC_INCOMING_SOURCE
|
||||
)
|
||||
end
|
||||
function modifier_keeper_of_the_light_chakra_magic_buff.prototype.GetEffectName(self)
|
||||
return "particles/units/heroes/hero_keeper_of_the_light/keeper_dazzling.vpcf"
|
||||
end
|
||||
function modifier_keeper_of_the_light_chakra_magic_buff.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_ABSORIGIN_FOLLOW
|
||||
end
|
||||
modifier_keeper_of_the_light_chakra_magic_buff = __TS__Decorate(
|
||||
modifier_keeper_of_the_light_chakra_magic_buff,
|
||||
modifier_keeper_of_the_light_chakra_magic_buff,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_keeper_of_the_light_chakra_magic_buff"}
|
||||
)
|
||||
____exports.modifier_keeper_of_the_light_chakra_magic_buff = modifier_keeper_of_the_light_chakra_magic_buff
|
||||
return ____exports
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
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 registerAbility = ____dota_ts_adapter.registerAbility
|
||||
local ____heal_tracker = require("utils.heal_tracker")
|
||||
local HealWithBattlePass = ____heal_tracker.HealWithBattlePass
|
||||
____exports.keeper_of_the_light_illuminate_custom = __TS__Class()
|
||||
local keeper_of_the_light_illuminate_custom = ____exports.keeper_of_the_light_illuminate_custom
|
||||
keeper_of_the_light_illuminate_custom.name = "keeper_of_the_light_illuminate_custom"
|
||||
keeper_of_the_light_illuminate_custom.____file_path = "scripts/vscripts/abilities/heroes/keeper_of_the_light/keeper_of_the_light_illuminate_custom.lua"
|
||||
__TS__ClassExtends(keeper_of_the_light_illuminate_custom, BaseAbility)
|
||||
function keeper_of_the_light_illuminate_custom.prototype.OnSpellStart(self)
|
||||
local caster = self:GetCaster()
|
||||
caster:EmitSound("Hero_KeeperOfTheLight.Illuminate.Charge")
|
||||
caster:SwapAbilities(
|
||||
self:GetAbilityName(),
|
||||
"keeper_of_the_light_illuminate_end",
|
||||
false,
|
||||
true
|
||||
)
|
||||
end
|
||||
function keeper_of_the_light_illuminate_custom.prototype.OnChannelFinish(self, _interrupted)
|
||||
local caster = self:GetCaster()
|
||||
local casterPoint = caster:GetOrigin()
|
||||
local distance = self:GetCastRange(casterPoint, nil)
|
||||
local speed = self:GetSpecialValueFor("speed")
|
||||
local radius = self:GetSpecialValueFor("radius")
|
||||
local multiple = (GameRules:GetGameTime() - self:GetChannelStartTime()) / self:GetChannelTime()
|
||||
caster:EmitSound("Hero_KeeperOfTheLight.Illuminate.Discharge")
|
||||
local direction = self:GetCursorPosition() - casterPoint
|
||||
direction.z = 0
|
||||
direction = direction:Normalized() * distance
|
||||
local particle = ParticleManager:CreateParticle("particles/units/heroes/hero_keeper_of_the_light/kotl_illuminate.vpcf", PATTACH_ABSORIGIN_FOLLOW, caster)
|
||||
ParticleManager:SetParticleControl(
|
||||
particle,
|
||||
1,
|
||||
direction:Normalized() * speed
|
||||
)
|
||||
ParticleManager:SetParticleControl(particle, 3, casterPoint)
|
||||
ProjectileManager:CreateLinearProjectile({
|
||||
Ability = self,
|
||||
vSpawnOrigin = casterPoint,
|
||||
vVelocity = direction:Normalized() * speed,
|
||||
fDistance = distance,
|
||||
fStartRadius = radius,
|
||||
fEndRadius = radius,
|
||||
Source = caster,
|
||||
iUnitTargetTeam = DOTA_UNIT_TARGET_TEAM_BOTH,
|
||||
iUnitTargetFlags = DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
iUnitTargetType = bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC),
|
||||
bProvidesVision = true,
|
||||
iVisionRadius = self:GetSpecialValueFor("vision_radius"),
|
||||
iVisionTeamNumber = caster:GetTeamNumber(),
|
||||
ExtraData = {particleId = particle, multiple = multiple}
|
||||
})
|
||||
caster:SwapAbilities(
|
||||
self:GetAbilityName(),
|
||||
"keeper_of_the_light_illuminate_end",
|
||||
true,
|
||||
false
|
||||
)
|
||||
caster:EmitSound("keeper_of_the_light_keep_illuminate_05")
|
||||
end
|
||||
function keeper_of_the_light_illuminate_custom.prototype.OnProjectileHit_ExtraData(self, target, _location, extraData)
|
||||
if not target then
|
||||
ParticleManager:DestroyParticle(extraData.particleId, false)
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local minDamage = self:GetSpecialValueFor("min_damage")
|
||||
local scaledDamage = (self:GetSpecialValueFor("max_damage") - minDamage) * extraData.multiple
|
||||
local totalValue = scaledDamage + minDamage
|
||||
target:EmitSound("Hero_KeeperOfTheLight.Illuminate.Target")
|
||||
local impactFx = ParticleManager:CreateParticle("particles/units/heroes/hero_keeper_of_the_light/keeper_of_the_light_illuminate_impact.vpcf", PATTACH_CUSTOMORIGIN, target)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
impactFx,
|
||||
0,
|
||||
target,
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
"attach_hitloc",
|
||||
target:GetOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(impactFx)
|
||||
if target:GetTeamNumber() == caster:GetTeamNumber() then
|
||||
local healPct = self:GetSpecialValueFor("heal_percent")
|
||||
local heal = totalValue * (caster:GetSpellAmplification(false) + 1) * healPct / 100
|
||||
HealWithBattlePass(
|
||||
nil,
|
||||
target,
|
||||
heal,
|
||||
self,
|
||||
caster
|
||||
)
|
||||
return
|
||||
end
|
||||
ApplyDamage({
|
||||
victim = target,
|
||||
attacker = caster,
|
||||
damage = totalValue + self:GetCaster():GetMaxMana() * 0.5,
|
||||
damage_type = self:GetAbilityDamageType(),
|
||||
ability = self
|
||||
})
|
||||
end
|
||||
keeper_of_the_light_illuminate_custom = __TS__Decorate(
|
||||
keeper_of_the_light_illuminate_custom,
|
||||
keeper_of_the_light_illuminate_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "keeper_of_the_light_illuminate_custom"}
|
||||
)
|
||||
____exports.keeper_of_the_light_illuminate_custom = keeper_of_the_light_illuminate_custom
|
||||
return ____exports
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
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
|
||||
____exports.keeper_of_the_light_radiant_bind_custom = __TS__Class()
|
||||
local keeper_of_the_light_radiant_bind_custom = ____exports.keeper_of_the_light_radiant_bind_custom
|
||||
keeper_of_the_light_radiant_bind_custom.name = "keeper_of_the_light_radiant_bind_custom"
|
||||
keeper_of_the_light_radiant_bind_custom.____file_path = "scripts/vscripts/abilities/heroes/keeper_of_the_light/keeper_of_the_light_radiant_bind_custom.lua"
|
||||
__TS__ClassExtends(keeper_of_the_light_radiant_bind_custom, BaseAbility)
|
||||
function keeper_of_the_light_radiant_bind_custom.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local target = self:GetCursorTarget()
|
||||
local caster = self:GetCaster()
|
||||
if not target then
|
||||
return
|
||||
end
|
||||
EmitSoundOn("Hero_KeeperOfTheLight.ManaLeak.Cast", caster)
|
||||
local particle = ParticleManager:CreateParticle("particles/units/heroes/hero_keeper_of_the_light/keeper_radiant_bind_cast.vpcf", PATTACH_CUSTOMORIGIN, caster)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
particle,
|
||||
0,
|
||||
caster,
|
||||
PATTACH_POINT_FOLLOW,
|
||||
"attach_attack1",
|
||||
caster:GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
particle,
|
||||
1,
|
||||
target:GetAbsOrigin()
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(particle)
|
||||
____exports.modifier_keeper_of_the_light_radiant_bind_custom:apply(
|
||||
target,
|
||||
caster,
|
||||
self,
|
||||
{duration = self:GetSpecialValueFor("duration")}
|
||||
)
|
||||
end
|
||||
keeper_of_the_light_radiant_bind_custom = __TS__Decorate(
|
||||
keeper_of_the_light_radiant_bind_custom,
|
||||
keeper_of_the_light_radiant_bind_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "keeper_of_the_light_radiant_bind_custom"}
|
||||
)
|
||||
____exports.keeper_of_the_light_radiant_bind_custom = keeper_of_the_light_radiant_bind_custom
|
||||
____exports.modifier_keeper_of_the_light_radiant_bind_custom = __TS__Class()
|
||||
local modifier_keeper_of_the_light_radiant_bind_custom = ____exports.modifier_keeper_of_the_light_radiant_bind_custom
|
||||
modifier_keeper_of_the_light_radiant_bind_custom.name = "modifier_keeper_of_the_light_radiant_bind_custom"
|
||||
modifier_keeper_of_the_light_radiant_bind_custom.____file_path = "scripts/vscripts/abilities/heroes/keeper_of_the_light/keeper_of_the_light_radiant_bind_custom.lua"
|
||||
__TS__ClassExtends(modifier_keeper_of_the_light_radiant_bind_custom, BaseModifier)
|
||||
function modifier_keeper_of_the_light_radiant_bind_custom.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.totalDistanceMoved = 0
|
||||
self.movespeedPct = 0
|
||||
end
|
||||
function modifier_keeper_of_the_light_radiant_bind_custom.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE, MODIFIER_PROPERTY_MOVESPEED_ABSOLUTE_MAX, MODIFIER_PROPERTY_IGNORE_MOVESPEED_LIMIT, MODIFIER_PROPERTY_MAGICAL_RESISTANCE_BONUS}
|
||||
end
|
||||
function modifier_keeper_of_the_light_radiant_bind_custom.prototype.IsDebuff(self)
|
||||
local caster = self:GetCaster()
|
||||
local parent = self:GetParent()
|
||||
if not caster then
|
||||
return true
|
||||
end
|
||||
return caster:GetTeamNumber() ~= parent:GetTeamNumber()
|
||||
end
|
||||
function modifier_keeper_of_the_light_radiant_bind_custom.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
function modifier_keeper_of_the_light_radiant_bind_custom.prototype.IsPurgeException(self)
|
||||
return true
|
||||
end
|
||||
function modifier_keeper_of_the_light_radiant_bind_custom.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
self.particle = ParticleManager:CreateParticle("particles/units/heroes/hero_keeper_of_the_light/keeper_of_the_light_radiant_bind_debuff.vpcf", PATTACH_CUSTOMORIGIN_FOLLOW, parent)
|
||||
self:AddParticle(
|
||||
self.particle,
|
||||
false,
|
||||
false,
|
||||
0,
|
||||
false,
|
||||
false
|
||||
)
|
||||
self.lastPosition = parent:GetAbsOrigin()
|
||||
self:StartIntervalThink(0.1)
|
||||
end
|
||||
function modifier_keeper_of_the_light_radiant_bind_custom.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
local parent = self:GetParent()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
local distanceForSlow = ability:GetSpecialValueFor("distance_for_slow")
|
||||
local slowPctPerDistance = ability:GetSpecialValueFor("slow_pct_per_distance")
|
||||
local newPos = parent:GetAbsOrigin()
|
||||
if self.lastPosition then
|
||||
self.totalDistanceMoved = self.totalDistanceMoved + (newPos - self.lastPosition):Length2D()
|
||||
end
|
||||
self.lastPosition = newPos
|
||||
local slowStacks = math.floor(self.totalDistanceMoved / distanceForSlow)
|
||||
if slowStacks < 1 then
|
||||
self.movespeedPct = 0
|
||||
return
|
||||
end
|
||||
if self.particle then
|
||||
ParticleManager:SetParticleControl(
|
||||
self.particle,
|
||||
0,
|
||||
parent:GetAbsOrigin()
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
self.particle,
|
||||
1,
|
||||
Vector(
|
||||
math.max(
|
||||
math.floor(slowStacks / 1.5),
|
||||
1
|
||||
),
|
||||
0,
|
||||
0
|
||||
)
|
||||
)
|
||||
end
|
||||
self.movespeedPct = slowStacks * slowPctPerDistance
|
||||
end
|
||||
function modifier_keeper_of_the_light_radiant_bind_custom.prototype.GetModifierMoveSpeedBonus_Percentage(self)
|
||||
return self:IsDebuff() and -self.movespeedPct or self.movespeedPct
|
||||
end
|
||||
function modifier_keeper_of_the_light_radiant_bind_custom.prototype.GetModifierIgnoreMovespeedLimit(self)
|
||||
return 1
|
||||
end
|
||||
function modifier_keeper_of_the_light_radiant_bind_custom.prototype.GetModifierMoveSpeed_AbsoluteMax(self)
|
||||
local ability = self:GetAbility()
|
||||
if not ability or self:IsDebuff() then
|
||||
return 0
|
||||
end
|
||||
return ability:GetSpecialValueFor("movespeed_limit")
|
||||
end
|
||||
function modifier_keeper_of_the_light_radiant_bind_custom.prototype.GetModifierMagicalResistanceBonus(self)
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return 0
|
||||
end
|
||||
local value = ability:GetSpecialValueFor("magres_pct")
|
||||
return self:IsDebuff() and -value or value
|
||||
end
|
||||
modifier_keeper_of_the_light_radiant_bind_custom = __TS__Decorate(
|
||||
modifier_keeper_of_the_light_radiant_bind_custom,
|
||||
modifier_keeper_of_the_light_radiant_bind_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_keeper_of_the_light_radiant_bind_custom"}
|
||||
)
|
||||
____exports.modifier_keeper_of_the_light_radiant_bind_custom = modifier_keeper_of_the_light_radiant_bind_custom
|
||||
return ____exports
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
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
|
||||
____exports.keeper_of_the_light_recall_custom = __TS__Class()
|
||||
local keeper_of_the_light_recall_custom = ____exports.keeper_of_the_light_recall_custom
|
||||
keeper_of_the_light_recall_custom.name = "keeper_of_the_light_recall_custom"
|
||||
keeper_of_the_light_recall_custom.____file_path = "scripts/vscripts/abilities/heroes/keeper_of_the_light/keeper_of_the_light_recall_custom.lua"
|
||||
__TS__ClassExtends(keeper_of_the_light_recall_custom, BaseAbility)
|
||||
function keeper_of_the_light_recall_custom.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local cursorTarget = self:GetCursorTarget()
|
||||
if not cursorTarget then
|
||||
return
|
||||
end
|
||||
EmitSoundOn("Hero_KeeperOfTheLight.Recall.Cast", caster)
|
||||
local recallCast = ParticleManager:CreateParticle("particles/units/heroes/hero_keeper_of_the_light/keeper_of_the_light_recall_cast.vpcf", PATTACH_CUSTOMORIGIN, caster)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
recallCast,
|
||||
0,
|
||||
caster,
|
||||
PATTACH_POINT_FOLLOW,
|
||||
"attach_origin",
|
||||
caster:GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
recallCast,
|
||||
1,
|
||||
caster,
|
||||
PATTACH_POINT_FOLLOW,
|
||||
"attach_origin",
|
||||
caster:GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(recallCast)
|
||||
local duration = self:GetSpecialValueFor("teleport_delay")
|
||||
if self:GetAutoCastState() then
|
||||
EmitSoundOn("Hero_KeeperOfTheLight.Recall.Target", caster)
|
||||
caster:AddNewModifier(
|
||||
caster,
|
||||
self,
|
||||
____exports.modifier_keeper_of_the_light_recall_custom.name,
|
||||
{
|
||||
duration = duration,
|
||||
target = cursorTarget:GetEntityIndex()
|
||||
}
|
||||
)
|
||||
return
|
||||
end
|
||||
EmitSoundOn("Hero_KeeperOfTheLight.Recall.Target", cursorTarget)
|
||||
cursorTarget:AddNewModifier(
|
||||
caster,
|
||||
self,
|
||||
____exports.modifier_keeper_of_the_light_recall_custom.name,
|
||||
{
|
||||
duration = duration,
|
||||
target = caster:GetEntityIndex()
|
||||
}
|
||||
)
|
||||
end
|
||||
function keeper_of_the_light_recall_custom.prototype.CastFilterResultTarget(self, target)
|
||||
if self:GetCaster() == target then
|
||||
return UF_FAIL_OTHER
|
||||
end
|
||||
return UnitFilter(
|
||||
target,
|
||||
DOTA_UNIT_TARGET_TEAM_FRIENDLY,
|
||||
DOTA_UNIT_TARGET_HERO,
|
||||
DOTA_UNIT_TARGET_FLAG_NOT_CREEP_HERO,
|
||||
self:GetCaster():GetTeamNumber()
|
||||
)
|
||||
end
|
||||
keeper_of_the_light_recall_custom = __TS__Decorate(
|
||||
keeper_of_the_light_recall_custom,
|
||||
keeper_of_the_light_recall_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "keeper_of_the_light_recall_custom"}
|
||||
)
|
||||
____exports.keeper_of_the_light_recall_custom = keeper_of_the_light_recall_custom
|
||||
____exports.modifier_keeper_of_the_light_recall_custom = __TS__Class()
|
||||
local modifier_keeper_of_the_light_recall_custom = ____exports.modifier_keeper_of_the_light_recall_custom
|
||||
modifier_keeper_of_the_light_recall_custom.name = "modifier_keeper_of_the_light_recall_custom"
|
||||
modifier_keeper_of_the_light_recall_custom.____file_path = "scripts/vscripts/abilities/heroes/keeper_of_the_light/keeper_of_the_light_recall_custom.lua"
|
||||
__TS__ClassExtends(modifier_keeper_of_the_light_recall_custom, BaseModifier)
|
||||
function modifier_keeper_of_the_light_recall_custom.prototype.OnCreated(self, params)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self.target = EntIndexToHScript(params.target)
|
||||
local parent = self:GetParent()
|
||||
local caster = self:GetCaster()
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
parent:AddNewModifier(
|
||||
caster,
|
||||
ability,
|
||||
____exports.modifier_keeper_of_the_light_recall_custom_speed.name,
|
||||
{duration = ability:GetSpecialValueFor("movespeed_bonus_duration")}
|
||||
)
|
||||
end
|
||||
function modifier_keeper_of_the_light_recall_custom.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if not self.target or self.target:IsNull() then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
local parent = self:GetParent()
|
||||
local caster = self:GetCaster()
|
||||
if not ability or not caster then
|
||||
return
|
||||
end
|
||||
StopSoundOn("Hero_KeeperOfTheLight.Recall.Target", parent)
|
||||
EmitSoundOn("Hero_KeeperOfTheLight.Recall.End", parent)
|
||||
local poof = ParticleManager:CreateParticle("particles/units/heroes/hero_keeper_of_the_light/keeper_of_the_light_recall_poof.vpcf", PATTACH_ABSORIGIN_FOLLOW, parent)
|
||||
ParticleManager:ReleaseParticleIndex(poof)
|
||||
FindClearSpaceForUnit(
|
||||
parent,
|
||||
self.target:GetAbsOrigin(),
|
||||
false
|
||||
)
|
||||
local speedDuration = ability:GetSpecialValueFor("movespeed_bonus_duration")
|
||||
parent:AddNewModifier(caster, ability, ____exports.modifier_keeper_of_the_light_recall_custom_speed.name, {duration = speedDuration})
|
||||
self.target:AddNewModifier(caster, ability, ____exports.modifier_keeper_of_the_light_recall_custom_speed.name, {duration = speedDuration})
|
||||
local chakraAbility = caster:FindAbilityByName("keeper_of_the_light_chakra_magic_custom")
|
||||
if chakraAbility and chakraAbility:GetLevel() > 0 then
|
||||
chakraAbility:OnCustomSpellStart(parent)
|
||||
end
|
||||
end
|
||||
function modifier_keeper_of_the_light_recall_custom.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_keeper_of_the_light_recall_custom.prototype.GetEffectName(self)
|
||||
return "particles/units/heroes/hero_keeper_of_the_light/keeper_of_the_light_recall.vpcf"
|
||||
end
|
||||
function modifier_keeper_of_the_light_recall_custom.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_ABSORIGIN_FOLLOW
|
||||
end
|
||||
modifier_keeper_of_the_light_recall_custom = __TS__Decorate(
|
||||
modifier_keeper_of_the_light_recall_custom,
|
||||
modifier_keeper_of_the_light_recall_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_keeper_of_the_light_recall_custom"}
|
||||
)
|
||||
____exports.modifier_keeper_of_the_light_recall_custom = modifier_keeper_of_the_light_recall_custom
|
||||
____exports.modifier_keeper_of_the_light_recall_custom_speed = __TS__Class()
|
||||
local modifier_keeper_of_the_light_recall_custom_speed = ____exports.modifier_keeper_of_the_light_recall_custom_speed
|
||||
modifier_keeper_of_the_light_recall_custom_speed.name = "modifier_keeper_of_the_light_recall_custom_speed"
|
||||
modifier_keeper_of_the_light_recall_custom_speed.____file_path = "scripts/vscripts/abilities/heroes/keeper_of_the_light/keeper_of_the_light_recall_custom.lua"
|
||||
__TS__ClassExtends(modifier_keeper_of_the_light_recall_custom_speed, BaseModifier)
|
||||
function modifier_keeper_of_the_light_recall_custom_speed.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.allyMoveSpeed = 0
|
||||
end
|
||||
function modifier_keeper_of_the_light_recall_custom_speed.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE}
|
||||
end
|
||||
function modifier_keeper_of_the_light_recall_custom_speed.prototype.OnCreated(self)
|
||||
local ____opt_0 = self:GetAbility()
|
||||
self.allyMoveSpeed = ____opt_0 and ____opt_0:GetSpecialValueFor("ally_movespeed_pct") or 0
|
||||
end
|
||||
function modifier_keeper_of_the_light_recall_custom_speed.prototype.GetModifierMoveSpeedBonus_Percentage(self)
|
||||
return self.allyMoveSpeed
|
||||
end
|
||||
modifier_keeper_of_the_light_recall_custom_speed = __TS__Decorate(
|
||||
modifier_keeper_of_the_light_recall_custom_speed,
|
||||
modifier_keeper_of_the_light_recall_custom_speed,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_keeper_of_the_light_recall_custom_speed"}
|
||||
)
|
||||
____exports.modifier_keeper_of_the_light_recall_custom_speed = modifier_keeper_of_the_light_recall_custom_speed
|
||||
return ____exports
|
||||
+205
@@ -0,0 +1,205 @@
|
||||
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__NumberToFixed = ____lualib.__TS__NumberToFixed
|
||||
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
|
||||
____exports.keeper_of_the_light_will_o_wisp_custom = __TS__Class()
|
||||
local keeper_of_the_light_will_o_wisp_custom = ____exports.keeper_of_the_light_will_o_wisp_custom
|
||||
keeper_of_the_light_will_o_wisp_custom.name = "keeper_of_the_light_will_o_wisp_custom"
|
||||
keeper_of_the_light_will_o_wisp_custom.____file_path = "scripts/vscripts/abilities/heroes/keeper_of_the_light/keeper_of_the_light_will_o_wisp_custom.lua"
|
||||
__TS__ClassExtends(keeper_of_the_light_will_o_wisp_custom, BaseAbility)
|
||||
function keeper_of_the_light_will_o_wisp_custom.prototype.GetAOERadius(self)
|
||||
return self:GetSpecialValueFor("radius")
|
||||
end
|
||||
function keeper_of_the_light_will_o_wisp_custom.prototype.Precache(self, context)
|
||||
PrecacheResource("model", "models/heroes/keeper_of_the_light/kotl_wisp.vmdl", context)
|
||||
end
|
||||
function keeper_of_the_light_will_o_wisp_custom.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local point = self:GetCursorPosition()
|
||||
local unit = CreateUnitByName(
|
||||
"npc_kotl_wisp",
|
||||
point,
|
||||
true,
|
||||
caster,
|
||||
caster,
|
||||
caster:GetTeamNumber()
|
||||
)
|
||||
____exports.modifier_keeper_of_the_light_will_o_wisp_custom:apply(unit, caster, self, {})
|
||||
end
|
||||
keeper_of_the_light_will_o_wisp_custom = __TS__Decorate(
|
||||
keeper_of_the_light_will_o_wisp_custom,
|
||||
keeper_of_the_light_will_o_wisp_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "keeper_of_the_light_will_o_wisp_custom"}
|
||||
)
|
||||
____exports.keeper_of_the_light_will_o_wisp_custom = keeper_of_the_light_will_o_wisp_custom
|
||||
____exports.modifier_keeper_of_the_light_will_o_wisp_custom = __TS__Class()
|
||||
local modifier_keeper_of_the_light_will_o_wisp_custom = ____exports.modifier_keeper_of_the_light_will_o_wisp_custom
|
||||
modifier_keeper_of_the_light_will_o_wisp_custom.name = "modifier_keeper_of_the_light_will_o_wisp_custom"
|
||||
modifier_keeper_of_the_light_will_o_wisp_custom.____file_path = "scripts/vscripts/abilities/heroes/keeper_of_the_light/keeper_of_the_light_will_o_wisp_custom.lua"
|
||||
__TS__ClassExtends(modifier_keeper_of_the_light_will_o_wisp_custom, BaseModifier)
|
||||
function modifier_keeper_of_the_light_will_o_wisp_custom.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.radius = 0
|
||||
self.delay = 0
|
||||
self.activeDuration = 0
|
||||
self.activeTick = 0
|
||||
self.activeChannel = 0
|
||||
self.damage = 0
|
||||
self.hitCount = 0
|
||||
end
|
||||
function modifier_keeper_of_the_light_will_o_wisp_custom.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_INVULNERABLE] = true, [MODIFIER_STATE_NO_HEALTH_BAR] = true, [MODIFIER_STATE_UNSELECTABLE] = true}
|
||||
end
|
||||
function modifier_keeper_of_the_light_will_o_wisp_custom.prototype.GetEffectName(self)
|
||||
return "particles/units/heroes/hero_keeper_of_the_light/keeper_dazzling.vpcf"
|
||||
end
|
||||
function modifier_keeper_of_the_light_will_o_wisp_custom.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
self.radius = ability:GetSpecialValueFor("radius")
|
||||
self.delay = ability:GetSpecialValueFor("delay")
|
||||
self.activeDuration = tonumber(__TS__NumberToFixed(
|
||||
ability:GetSpecialValueFor("active_duration"),
|
||||
1
|
||||
)) or 0
|
||||
self.activeTick = ability:GetSpecialValueFor("active_tick")
|
||||
self.damage = ability:GetSpecialValueFor("damage")
|
||||
self.particleAura = ParticleManager:CreateParticle("particles/units/heroes/hero_keeper_of_the_light/keeper_dazzling_aoe_ring.vpcf", PATTACH_CUSTOMORIGIN, parent)
|
||||
ParticleManager:SetParticleControl(
|
||||
self.particleAura,
|
||||
0,
|
||||
parent:GetAbsOrigin()
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
self.particleAura,
|
||||
1,
|
||||
Vector(self.radius, self.radius, self.radius)
|
||||
)
|
||||
EmitSoundOn("Hero_KeeperOfTheLight.Wisp.Spawn", parent)
|
||||
self:StartIntervalThink(self.delay)
|
||||
end
|
||||
function modifier_keeper_of_the_light_will_o_wisp_custom.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if self.particleAura ~= nil then
|
||||
ParticleManager:DestroyParticle(self.particleAura, false)
|
||||
ParticleManager:ReleaseParticleIndex(self.particleAura)
|
||||
self.particleAura = nil
|
||||
end
|
||||
if self.particleActive ~= nil then
|
||||
ParticleManager:DestroyParticle(self.particleActive, false)
|
||||
ParticleManager:ReleaseParticleIndex(self.particleActive)
|
||||
self.particleActive = nil
|
||||
end
|
||||
end
|
||||
function modifier_keeper_of_the_light_will_o_wisp_custom.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
if self.activeChannel == 0 then
|
||||
EmitSoundOn("Hero_KeeperOfTheLight.Wisp.Active", parent)
|
||||
end
|
||||
self:StartIntervalThink(self.activeTick)
|
||||
self.activeChannel = self.activeChannel + self.activeTick
|
||||
if not self.particleActive then
|
||||
self.particleActive = ParticleManager:CreateParticle("particles/units/heroes/hero_keeper_of_the_light/keeper_dazzling_on.vpcf", PATTACH_ABSORIGIN_FOLLOW, parent)
|
||||
end
|
||||
local enemies = FindUnitsInRadius(
|
||||
parent:GetTeamNumber(),
|
||||
parent:GetOrigin(),
|
||||
nil,
|
||||
self.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
|
||||
)
|
||||
for ____, enemy in ipairs(enemies) do
|
||||
local manaDamagePerSecond = self:GetCaster():GetMana() * 0.25
|
||||
ApplyDamage({
|
||||
victim = enemy,
|
||||
attacker = parent,
|
||||
damage = (self.damage + manaDamagePerSecond) * self.activeTick,
|
||||
damage_type = ability:GetAbilityDamageType(),
|
||||
ability = ability
|
||||
})
|
||||
____exports.modifier_keeper_will_o_wisp_debuff:apply(
|
||||
enemy,
|
||||
self:GetCaster(),
|
||||
ability,
|
||||
{duration = self.activeTick + 0.1}
|
||||
)
|
||||
end
|
||||
if self.activeChannel < self.activeDuration then
|
||||
return
|
||||
end
|
||||
self:StartIntervalThink(self.delay)
|
||||
if self.particleActive ~= nil then
|
||||
ParticleManager:DestroyParticle(self.particleActive, false)
|
||||
ParticleManager:ReleaseParticleIndex(self.particleActive)
|
||||
self.particleActive = nil
|
||||
end
|
||||
self.activeChannel = 0
|
||||
self.hitCount = self.hitCount + 1
|
||||
if ability:GetSpecialValueFor("hit_count") <= self.hitCount then
|
||||
parent:ForceKill(true)
|
||||
EmitSoundOn("Hero_KeeperOfTheLight.Wisp.Destroy", parent)
|
||||
end
|
||||
end
|
||||
modifier_keeper_of_the_light_will_o_wisp_custom = __TS__Decorate(
|
||||
modifier_keeper_of_the_light_will_o_wisp_custom,
|
||||
modifier_keeper_of_the_light_will_o_wisp_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_keeper_of_the_light_will_o_wisp_custom"}
|
||||
)
|
||||
____exports.modifier_keeper_of_the_light_will_o_wisp_custom = modifier_keeper_of_the_light_will_o_wisp_custom
|
||||
____exports.modifier_keeper_will_o_wisp_debuff = __TS__Class()
|
||||
local modifier_keeper_will_o_wisp_debuff = ____exports.modifier_keeper_will_o_wisp_debuff
|
||||
modifier_keeper_will_o_wisp_debuff.name = "modifier_keeper_will_o_wisp_debuff"
|
||||
modifier_keeper_will_o_wisp_debuff.____file_path = "scripts/vscripts/abilities/heroes/keeper_of_the_light/keeper_of_the_light_will_o_wisp_custom.lua"
|
||||
__TS__ClassExtends(modifier_keeper_will_o_wisp_debuff, BaseModifier)
|
||||
function modifier_keeper_will_o_wisp_debuff.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE}
|
||||
end
|
||||
function modifier_keeper_will_o_wisp_debuff.prototype.GetModifierMoveSpeedBonus_Percentage(self)
|
||||
local ____opt_0 = self:GetAbility()
|
||||
return ____opt_0 and ____opt_0:GetSpecialValueFor("slow_movespeed") or 0
|
||||
end
|
||||
function modifier_keeper_will_o_wisp_debuff.prototype.GetStatusEffectName(self)
|
||||
return "particles/status_fx/status_effect_keeper_dazzle.vpcf"
|
||||
end
|
||||
function modifier_keeper_will_o_wisp_debuff.prototype.StatusEffectPriority(self)
|
||||
return MODIFIER_PRIORITY_HIGH
|
||||
end
|
||||
modifier_keeper_will_o_wisp_debuff = __TS__Decorate(
|
||||
modifier_keeper_will_o_wisp_debuff,
|
||||
modifier_keeper_will_o_wisp_debuff,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_keeper_will_o_wisp_debuff"}
|
||||
)
|
||||
____exports.modifier_keeper_will_o_wisp_debuff = modifier_keeper_will_o_wisp_debuff
|
||||
return ____exports
|
||||
+601
@@ -0,0 +1,601 @@
|
||||
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 DUEL_DEBUG_ENABLED = true
|
||||
local DUEL_DEBUG_PREFIX = "[LC_DUEL_DEBUG]"
|
||||
local function duelDebug(self, message)
|
||||
if not DUEL_DEBUG_ENABLED then
|
||||
return
|
||||
end
|
||||
print((DUEL_DEBUG_PREFIX .. " ") .. message)
|
||||
end
|
||||
local function removeDuelModifiersFrom(self, unit)
|
||||
duelDebug(
|
||||
nil,
|
||||
(("removeDuelModifiersFrom unit=" .. unit:GetUnitName()) .. "#") .. tostring(unit:entindex())
|
||||
)
|
||||
while unit:HasModifier(____exports.modifier_legion_commander_duel_custom.name) do
|
||||
unit:RemoveModifierByName(____exports.modifier_legion_commander_duel_custom.name)
|
||||
end
|
||||
local ____this_1
|
||||
____this_1 = unit
|
||||
local ____opt_0 = ____this_1.SetForceAttackTarget
|
||||
if ____opt_0 ~= nil then
|
||||
____opt_0(____this_1, nil)
|
||||
end
|
||||
end
|
||||
local DUEL_RESOLUTION_LOCK_DURATION = 0.3
|
||||
____exports.ability_legion_commander_duel_custom = __TS__Class()
|
||||
local ability_legion_commander_duel_custom = ____exports.ability_legion_commander_duel_custom
|
||||
ability_legion_commander_duel_custom.name = "ability_legion_commander_duel_custom"
|
||||
ability_legion_commander_duel_custom.____file_path = "scripts/vscripts/abilities/heroes/legion_commander/ability_legion_commander_duel_custom.lua"
|
||||
__TS__ClassExtends(ability_legion_commander_duel_custom, BaseAbility)
|
||||
function ability_legion_commander_duel_custom.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_legion_commander/legion_commander_duel_victory.vpcf", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_legion_commander/legion_commander_press_a.vpcf", context)
|
||||
PrecacheResource("particle", "particles/status_fx/status_effect_legion_commander_duel.vpcf", context)
|
||||
PrecacheResource("particle", "particles/econ/items/legion/legion_weapon_voth_domosh/legion_duel_ring_arcana.vpcf", context)
|
||||
PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_legion_commander.vsndevts", context)
|
||||
end
|
||||
function ability_legion_commander_duel_custom.prototype.GetCastAnimation(self)
|
||||
return ACT_DOTA_CAST_ABILITY_4
|
||||
end
|
||||
function ability_legion_commander_duel_custom.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local target = self:GetCursorTarget()
|
||||
duelDebug(
|
||||
nil,
|
||||
(((((("OnSpellStart caster=" .. (caster and caster:GetUnitName())) .. "#") .. tostring(caster and caster:entindex())) .. " target=") .. tostring(target and target:GetUnitName())) .. "#") .. tostring(target and target:entindex())
|
||||
)
|
||||
if not caster or not caster:IsAlive() or not target or not target:IsAlive() or target:IsCourier() then
|
||||
return
|
||||
end
|
||||
if target:GetTeamNumber() == caster:GetTeamNumber() then
|
||||
return
|
||||
end
|
||||
if target:TriggerSpellAbsorb(self) then
|
||||
return
|
||||
end
|
||||
self:startDuel(caster, target)
|
||||
end
|
||||
function ability_legion_commander_duel_custom.prototype.startDuel(self, caster, target)
|
||||
local duration = self:GetSpecialValueFor("duration")
|
||||
if caster:IsIllusion() then
|
||||
duration = 1.5
|
||||
end
|
||||
local durTarget = duration * (1 - target:GetStatusResistance())
|
||||
duelDebug(
|
||||
nil,
|
||||
(((((("startDuel caster=" .. tostring(caster:entindex())) .. " target=") .. tostring(target:entindex())) .. " duration=") .. tostring(duration)) .. " durTarget=") .. tostring(durTarget)
|
||||
)
|
||||
EmitSoundOn("Hero_LegionCommander.Duel.Cast", caster)
|
||||
caster:AddNewModifier(
|
||||
caster,
|
||||
self,
|
||||
____exports.modifier_legion_commander_duel_custom.name,
|
||||
{
|
||||
duration = duration,
|
||||
counterpart_entindex = target:entindex()
|
||||
}
|
||||
)
|
||||
target:AddNewModifier(
|
||||
caster,
|
||||
self,
|
||||
____exports.modifier_legion_commander_duel_custom.name,
|
||||
{
|
||||
duration = durTarget,
|
||||
counterpart_entindex = caster:entindex()
|
||||
}
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(ParticleManager:CreateParticle("particles/units/heroes/hero_legion_commander/legion_commander_press_a.vpcf", PATTACH_ABSORIGIN_FOLLOW, caster))
|
||||
ParticleManager:ReleaseParticleIndex(ParticleManager:CreateParticle("particles/units/heroes/hero_legion_commander/legion_commander_press_a.vpcf", PATTACH_ABSORIGIN_FOLLOW, target))
|
||||
EmitSoundOn("Hero_LegionCommander.Duel", caster)
|
||||
ExecuteOrderFromTable({
|
||||
UnitIndex = caster:entindex(),
|
||||
OrderType = DOTA_UNIT_ORDER_ATTACK_TARGET,
|
||||
TargetIndex = target:entindex(),
|
||||
Queue = false
|
||||
})
|
||||
ExecuteOrderFromTable({
|
||||
UnitIndex = target:entindex(),
|
||||
OrderType = DOTA_UNIT_ORDER_ATTACK_TARGET,
|
||||
TargetIndex = caster:entindex(),
|
||||
Queue = false
|
||||
})
|
||||
end
|
||||
function ability_legion_commander_duel_custom.prototype.completeDuelWithWinner(self, winner, loser)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
duelDebug(
|
||||
nil,
|
||||
(("completeDuelWithWinner winner=" .. tostring(winner:entindex())) .. " loser=") .. tostring(loser:entindex())
|
||||
)
|
||||
local hasResolutionLock = winner:HasModifier(____exports.modifier_legion_commander_duel_resolution_lock_custom.name)
|
||||
removeDuelModifiersFrom(nil, winner)
|
||||
removeDuelModifiersFrom(nil, loser)
|
||||
if hasResolutionLock then
|
||||
return
|
||||
end
|
||||
winner:AddNewModifier(winner, self, ____exports.modifier_legion_commander_duel_resolution_lock_custom.name, {duration = DUEL_RESOLUTION_LOCK_DURATION})
|
||||
self:grantDuelVictoryStacks(winner, loser)
|
||||
end
|
||||
function ability_legion_commander_duel_custom.prototype.grantDuelVictoryStacks(self, winner, loser)
|
||||
duelDebug(
|
||||
nil,
|
||||
(("grantDuelVictoryStacks winner=" .. tostring(winner and winner:entindex())) .. " loser=") .. tostring(loser and loser:entindex())
|
||||
)
|
||||
if not winner or not winner:IsAlive() then
|
||||
return
|
||||
end
|
||||
if not loser or loser:IsIllusion() then
|
||||
return
|
||||
end
|
||||
if loser:IsHero() and loser:IsTempestDouble() then
|
||||
return
|
||||
end
|
||||
local isCreepLike = loser:IsCreep() and not loser:IsHero()
|
||||
local add = math.floor(isCreepLike and self:GetSpecialValueFor("reward_damage_creep") or self:GetSpecialValueFor("reward_damage"))
|
||||
if add <= 0 then
|
||||
return
|
||||
end
|
||||
local cap = self:GetSpecialValueFor("max_stack_damage")
|
||||
local stackMod = winner:FindModifierByName(____exports.modifier_legion_commander_duel_damage_stack_custom.name)
|
||||
if not stackMod then
|
||||
winner:AddNewModifier(winner, self, ____exports.modifier_legion_commander_duel_damage_stack_custom.name, {})
|
||||
stackMod = winner:FindModifierByName(____exports.modifier_legion_commander_duel_damage_stack_custom.name)
|
||||
end
|
||||
if stackMod then
|
||||
local next = stackMod:GetStackCount() + add
|
||||
stackMod:SetStackCount(cap > 0 and math.min(cap, next) or next)
|
||||
end
|
||||
local randomStatPerWin = math.max(
|
||||
0,
|
||||
math.floor(self:GetSpecialValueFor("reward_random_stat"))
|
||||
)
|
||||
if randomStatPerWin > 0 and winner:IsRealHero() then
|
||||
self:grantRandomStat(winner, randomStatPerWin)
|
||||
end
|
||||
local pWin = ParticleManager:CreateParticle("particles/units/heroes/hero_legion_commander/legion_commander_duel_victory.vpcf", PATTACH_OVERHEAD_FOLLOW, winner)
|
||||
ParticleManager:ReleaseParticleIndex(pWin)
|
||||
EmitSoundOn("Hero_LegionCommander.Duel.Victory", winner)
|
||||
end
|
||||
function ability_legion_commander_duel_custom.prototype.grantRandomStat(self, winner, amount)
|
||||
local roll = RandomInt(1, 3)
|
||||
local modName = ____exports.modifier_legion_commander_duel_random_stat_str_custom.name
|
||||
if roll == 2 then
|
||||
modName = ____exports.modifier_legion_commander_duel_random_stat_agi_custom.name
|
||||
end
|
||||
if roll == 3 then
|
||||
modName = ____exports.modifier_legion_commander_duel_random_stat_int_custom.name
|
||||
end
|
||||
local mod = winner:FindModifierByName(modName)
|
||||
if not mod then
|
||||
winner:AddNewModifier(winner, self, modName, {})
|
||||
mod = winner:FindModifierByName(modName)
|
||||
end
|
||||
if mod then
|
||||
mod:SetStackCount(mod:GetStackCount() + amount)
|
||||
end
|
||||
end
|
||||
function ability_legion_commander_duel_custom.prototype.terminateDuelNoReward(self, attackerSide, defenderSide)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
removeDuelModifiersFrom(nil, attackerSide)
|
||||
removeDuelModifiersFrom(nil, defenderSide)
|
||||
end
|
||||
ability_legion_commander_duel_custom = __TS__Decorate(
|
||||
ability_legion_commander_duel_custom,
|
||||
ability_legion_commander_duel_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_legion_commander_duel_custom"}
|
||||
)
|
||||
____exports.ability_legion_commander_duel_custom = ability_legion_commander_duel_custom
|
||||
____exports.modifier_legion_commander_duel_damage_stack_custom = __TS__Class()
|
||||
local modifier_legion_commander_duel_damage_stack_custom = ____exports.modifier_legion_commander_duel_damage_stack_custom
|
||||
modifier_legion_commander_duel_damage_stack_custom.name = "modifier_legion_commander_duel_damage_stack_custom"
|
||||
modifier_legion_commander_duel_damage_stack_custom.____file_path = "scripts/vscripts/abilities/heroes/legion_commander/ability_legion_commander_duel_custom.lua"
|
||||
__TS__ClassExtends(modifier_legion_commander_duel_damage_stack_custom, BaseModifier)
|
||||
function modifier_legion_commander_duel_damage_stack_custom.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_legion_commander_duel_damage_stack_custom.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_legion_commander_duel_damage_stack_custom.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_legion_commander_duel_damage_stack_custom.prototype.RemoveOnDeath(self)
|
||||
return false
|
||||
end
|
||||
function modifier_legion_commander_duel_damage_stack_custom.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_PREATTACK_BONUS_DAMAGE}
|
||||
end
|
||||
function modifier_legion_commander_duel_damage_stack_custom.prototype.GetModifierPreAttack_BonusDamage(self)
|
||||
return self:GetStackCount()
|
||||
end
|
||||
function modifier_legion_commander_duel_damage_stack_custom.prototype.GetTexture(self)
|
||||
return "legion_commander_duel"
|
||||
end
|
||||
modifier_legion_commander_duel_damage_stack_custom = __TS__Decorate(
|
||||
modifier_legion_commander_duel_damage_stack_custom,
|
||||
modifier_legion_commander_duel_damage_stack_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_legion_commander_duel_damage_stack_custom"}
|
||||
)
|
||||
____exports.modifier_legion_commander_duel_damage_stack_custom = modifier_legion_commander_duel_damage_stack_custom
|
||||
____exports.modifier_legion_commander_duel_resolution_lock_custom = __TS__Class()
|
||||
local modifier_legion_commander_duel_resolution_lock_custom = ____exports.modifier_legion_commander_duel_resolution_lock_custom
|
||||
modifier_legion_commander_duel_resolution_lock_custom.name = "modifier_legion_commander_duel_resolution_lock_custom"
|
||||
modifier_legion_commander_duel_resolution_lock_custom.____file_path = "scripts/vscripts/abilities/heroes/legion_commander/ability_legion_commander_duel_custom.lua"
|
||||
__TS__ClassExtends(modifier_legion_commander_duel_resolution_lock_custom, BaseModifier)
|
||||
function modifier_legion_commander_duel_resolution_lock_custom.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_legion_commander_duel_resolution_lock_custom.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
modifier_legion_commander_duel_resolution_lock_custom = __TS__Decorate(
|
||||
modifier_legion_commander_duel_resolution_lock_custom,
|
||||
modifier_legion_commander_duel_resolution_lock_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_legion_commander_duel_resolution_lock_custom"}
|
||||
)
|
||||
____exports.modifier_legion_commander_duel_resolution_lock_custom = modifier_legion_commander_duel_resolution_lock_custom
|
||||
____exports.modifier_legion_commander_duel_random_stat_str_custom = __TS__Class()
|
||||
local modifier_legion_commander_duel_random_stat_str_custom = ____exports.modifier_legion_commander_duel_random_stat_str_custom
|
||||
modifier_legion_commander_duel_random_stat_str_custom.name = "modifier_legion_commander_duel_random_stat_str_custom"
|
||||
modifier_legion_commander_duel_random_stat_str_custom.____file_path = "scripts/vscripts/abilities/heroes/legion_commander/ability_legion_commander_duel_custom.lua"
|
||||
__TS__ClassExtends(modifier_legion_commander_duel_random_stat_str_custom, BaseModifier)
|
||||
function modifier_legion_commander_duel_random_stat_str_custom.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_legion_commander_duel_random_stat_str_custom.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_legion_commander_duel_random_stat_str_custom.prototype.RemoveOnDeath(self)
|
||||
return false
|
||||
end
|
||||
function modifier_legion_commander_duel_random_stat_str_custom.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_STATS_STRENGTH_BONUS}
|
||||
end
|
||||
function modifier_legion_commander_duel_random_stat_str_custom.prototype.GetModifierBonusStats_Strength(self)
|
||||
return self:GetStackCount()
|
||||
end
|
||||
function modifier_legion_commander_duel_random_stat_str_custom.prototype.GetTexture(self)
|
||||
return "legion_commander_duel"
|
||||
end
|
||||
modifier_legion_commander_duel_random_stat_str_custom = __TS__Decorate(
|
||||
modifier_legion_commander_duel_random_stat_str_custom,
|
||||
modifier_legion_commander_duel_random_stat_str_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_legion_commander_duel_random_stat_str_custom"}
|
||||
)
|
||||
____exports.modifier_legion_commander_duel_random_stat_str_custom = modifier_legion_commander_duel_random_stat_str_custom
|
||||
____exports.modifier_legion_commander_duel_random_stat_agi_custom = __TS__Class()
|
||||
local modifier_legion_commander_duel_random_stat_agi_custom = ____exports.modifier_legion_commander_duel_random_stat_agi_custom
|
||||
modifier_legion_commander_duel_random_stat_agi_custom.name = "modifier_legion_commander_duel_random_stat_agi_custom"
|
||||
modifier_legion_commander_duel_random_stat_agi_custom.____file_path = "scripts/vscripts/abilities/heroes/legion_commander/ability_legion_commander_duel_custom.lua"
|
||||
__TS__ClassExtends(modifier_legion_commander_duel_random_stat_agi_custom, BaseModifier)
|
||||
function modifier_legion_commander_duel_random_stat_agi_custom.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_legion_commander_duel_random_stat_agi_custom.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_legion_commander_duel_random_stat_agi_custom.prototype.RemoveOnDeath(self)
|
||||
return false
|
||||
end
|
||||
function modifier_legion_commander_duel_random_stat_agi_custom.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_STATS_AGILITY_BONUS}
|
||||
end
|
||||
function modifier_legion_commander_duel_random_stat_agi_custom.prototype.GetModifierBonusStats_Agility(self)
|
||||
return self:GetStackCount()
|
||||
end
|
||||
function modifier_legion_commander_duel_random_stat_agi_custom.prototype.GetTexture(self)
|
||||
return "legion_commander_duel"
|
||||
end
|
||||
modifier_legion_commander_duel_random_stat_agi_custom = __TS__Decorate(
|
||||
modifier_legion_commander_duel_random_stat_agi_custom,
|
||||
modifier_legion_commander_duel_random_stat_agi_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_legion_commander_duel_random_stat_agi_custom"}
|
||||
)
|
||||
____exports.modifier_legion_commander_duel_random_stat_agi_custom = modifier_legion_commander_duel_random_stat_agi_custom
|
||||
____exports.modifier_legion_commander_duel_random_stat_int_custom = __TS__Class()
|
||||
local modifier_legion_commander_duel_random_stat_int_custom = ____exports.modifier_legion_commander_duel_random_stat_int_custom
|
||||
modifier_legion_commander_duel_random_stat_int_custom.name = "modifier_legion_commander_duel_random_stat_int_custom"
|
||||
modifier_legion_commander_duel_random_stat_int_custom.____file_path = "scripts/vscripts/abilities/heroes/legion_commander/ability_legion_commander_duel_custom.lua"
|
||||
__TS__ClassExtends(modifier_legion_commander_duel_random_stat_int_custom, BaseModifier)
|
||||
function modifier_legion_commander_duel_random_stat_int_custom.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_legion_commander_duel_random_stat_int_custom.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_legion_commander_duel_random_stat_int_custom.prototype.RemoveOnDeath(self)
|
||||
return false
|
||||
end
|
||||
function modifier_legion_commander_duel_random_stat_int_custom.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_STATS_INTELLECT_BONUS}
|
||||
end
|
||||
function modifier_legion_commander_duel_random_stat_int_custom.prototype.GetModifierBonusStats_Intellect(self)
|
||||
return self:GetStackCount()
|
||||
end
|
||||
function modifier_legion_commander_duel_random_stat_int_custom.prototype.GetTexture(self)
|
||||
return "legion_commander_duel"
|
||||
end
|
||||
modifier_legion_commander_duel_random_stat_int_custom = __TS__Decorate(
|
||||
modifier_legion_commander_duel_random_stat_int_custom,
|
||||
modifier_legion_commander_duel_random_stat_int_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_legion_commander_duel_random_stat_int_custom"}
|
||||
)
|
||||
____exports.modifier_legion_commander_duel_random_stat_int_custom = modifier_legion_commander_duel_random_stat_int_custom
|
||||
____exports.modifier_legion_commander_duel_custom = __TS__Class()
|
||||
local modifier_legion_commander_duel_custom = ____exports.modifier_legion_commander_duel_custom
|
||||
modifier_legion_commander_duel_custom.name = "modifier_legion_commander_duel_custom"
|
||||
modifier_legion_commander_duel_custom.____file_path = "scripts/vscripts/abilities/heroes/legion_commander/ability_legion_commander_duel_custom.lua"
|
||||
__TS__ClassExtends(modifier_legion_commander_duel_custom, BaseModifier)
|
||||
function modifier_legion_commander_duel_custom.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.thinkInterval = 0.1
|
||||
self.nextDebugAt = 0
|
||||
self.nextShardAutoOddsAt = 0
|
||||
self.finished = false
|
||||
end
|
||||
function modifier_legion_commander_duel_custom.prototype.getShardOverwhelmingInterval(self)
|
||||
local ab = self:GetAbility()
|
||||
if not ab then
|
||||
return 2
|
||||
end
|
||||
return math.max(
|
||||
0.1,
|
||||
ab:GetSpecialValueFor("shard_overwhelming_interval")
|
||||
)
|
||||
end
|
||||
function modifier_legion_commander_duel_custom.prototype.getCounterpart(self)
|
||||
if self.counterpartEi == nil or self.counterpartEi == -1 then
|
||||
return nil
|
||||
end
|
||||
local unit = EntIndexToHScript(self.counterpartEi)
|
||||
if not unit or unit:IsNull() then
|
||||
return nil
|
||||
end
|
||||
return unit
|
||||
end
|
||||
function modifier_legion_commander_duel_custom.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_legion_commander_duel_custom.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_legion_commander_duel_custom.prototype.IsDebuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_legion_commander_duel_custom.prototype.RemoveOnDeath(self)
|
||||
return false
|
||||
end
|
||||
function modifier_legion_commander_duel_custom.prototype.GetTexture(self)
|
||||
return "legion_commander_duel"
|
||||
end
|
||||
function modifier_legion_commander_duel_custom.prototype.GetStatusEffectName(self)
|
||||
return "particles/status_fx/status_effect_legion_commander_duel.vpcf"
|
||||
end
|
||||
function modifier_legion_commander_duel_custom.prototype.StatusEffectPriority(self)
|
||||
return MODIFIER_PRIORITY_ULTRA
|
||||
end
|
||||
function modifier_legion_commander_duel_custom.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_COMMAND_RESTRICTED] = true, [MODIFIER_STATE_TAUNTED] = true, [MODIFIER_STATE_SILENCED] = true}
|
||||
end
|
||||
function modifier_legion_commander_duel_custom.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_ATTACKSPEED_BONUS_CONSTANT, MODIFIER_PROPERTY_MAGICAL_RESISTANCE_BONUS, MODIFIER_EVENT_ON_DEATH}
|
||||
end
|
||||
function modifier_legion_commander_duel_custom.prototype.GetModifierAttackSpeedBonus_Constant(self)
|
||||
local ab = self:GetAbility()
|
||||
if not ab then
|
||||
return 0
|
||||
end
|
||||
return ab:GetSpecialValueFor("bonus_attack_speed")
|
||||
end
|
||||
function modifier_legion_commander_duel_custom.prototype.GetModifierMagicalResistanceBonus(self)
|
||||
local parent = self:GetParent()
|
||||
local caster = self:GetCaster()
|
||||
local ab = self:GetAbility()
|
||||
if not ab or not caster or not parent or parent ~= caster then
|
||||
return 0
|
||||
end
|
||||
if not caster:HasScepter() then
|
||||
return 0
|
||||
end
|
||||
return ab:GetSpecialValueFor("scepter_magic_resist")
|
||||
end
|
||||
function modifier_legion_commander_duel_custom.prototype.OnCreated(self, params)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self.counterpartEi = params.counterpart_entindex or -1
|
||||
duelDebug(
|
||||
nil,
|
||||
(("modifier_duel.OnCreated parent=" .. tostring(self:GetParent():entindex())) .. " counterpartEi=") .. tostring(self.counterpartEi)
|
||||
)
|
||||
if self.counterpartEi == -1 then
|
||||
self:breakDuelNoReward()
|
||||
return
|
||||
end
|
||||
local ab = self:GetAbility()
|
||||
self.victoryRange = ab and ab:GetSpecialValueFor("victory_range") or 1500
|
||||
local parent = self:GetParent()
|
||||
local counterpart = self:getCounterpart()
|
||||
if counterpart and not parent:IsCreep() then
|
||||
local ____opt_14 = parent.SetForceAttackTarget
|
||||
if ____opt_14 ~= nil then
|
||||
____opt_14(parent, counterpart)
|
||||
end
|
||||
parent:MoveToTargetToAttack(counterpart)
|
||||
end
|
||||
if self:GetCaster() == parent then
|
||||
self.nextShardAutoOddsAt = GameRules:GetGameTime() + self:getShardOverwhelmingInterval()
|
||||
end
|
||||
if counterpart and self:GetCaster() == parent then
|
||||
local center = GetGroundPosition(
|
||||
parent:GetAbsOrigin(),
|
||||
parent
|
||||
)
|
||||
local ringFx = ParticleManager:CreateParticle("particles/econ/items/legion/legion_weapon_voth_domosh/legion_duel_ring_arcana.vpcf", PATTACH_WORLDORIGIN, parent)
|
||||
ParticleManager:SetParticleControl(ringFx, 0, center)
|
||||
ParticleManager:SetParticleControl(ringFx, 7, center)
|
||||
self:AddParticle(
|
||||
ringFx,
|
||||
false,
|
||||
false,
|
||||
-1,
|
||||
true,
|
||||
false
|
||||
)
|
||||
end
|
||||
self:StartIntervalThink(self.thinkInterval)
|
||||
end
|
||||
function modifier_legion_commander_duel_custom.prototype.OnIntervalThink(self)
|
||||
if not IsServer() or self.finished then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local counterpart = self:getCounterpart()
|
||||
local now = GameRules:GetGameTime()
|
||||
if now >= self.nextDebugAt then
|
||||
self.nextDebugAt = now + 1
|
||||
duelDebug(
|
||||
nil,
|
||||
(((((("modifier_duel.Tick parent=" .. tostring(parent:entindex())) .. " alive=") .. tostring(parent:IsAlive())) .. " counterpart=") .. tostring(counterpart and counterpart:entindex())) .. " counterpartAlive=") .. tostring(counterpart and counterpart:IsAlive())
|
||||
)
|
||||
end
|
||||
if not parent or not parent:IsAlive() then
|
||||
return
|
||||
end
|
||||
if not counterpart then
|
||||
self:breakDuelNoReward()
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
if not counterpart:IsAlive() then
|
||||
if ability then
|
||||
ability:completeDuelWithWinner(parent, counterpart)
|
||||
else
|
||||
self:breakDuelNoReward()
|
||||
end
|
||||
return
|
||||
end
|
||||
if not counterpart:HasModifier(____exports.modifier_legion_commander_duel_custom.name) then
|
||||
self:breakDuelNoReward()
|
||||
return
|
||||
end
|
||||
local d = parent:GetAbsOrigin():__sub(counterpart:GetAbsOrigin()):Length2D()
|
||||
if d > self.victoryRange then
|
||||
self:breakDuelNoReward()
|
||||
return
|
||||
end
|
||||
if self:GetCaster() == parent and HasShard(nil, parent) then
|
||||
local gameTime = GameRules:GetGameTime()
|
||||
if gameTime >= self.nextShardAutoOddsAt then
|
||||
self.nextShardAutoOddsAt = gameTime + self:getShardOverwhelmingInterval()
|
||||
local odds = parent:FindAbilityByName("ability_legion_commander_overwhelming_odds_custom")
|
||||
if odds and odds:GetLevel() > 0 then
|
||||
odds:ExecuteBurstAt(GetGroundPosition(
|
||||
parent:GetAbsOrigin(),
|
||||
parent
|
||||
))
|
||||
end
|
||||
end
|
||||
end
|
||||
if not parent:IsCreep() then
|
||||
local ____opt_20 = parent.SetForceAttackTarget
|
||||
if ____opt_20 ~= nil then
|
||||
____opt_20(parent, counterpart)
|
||||
end
|
||||
parent:MoveToTargetToAttack(counterpart)
|
||||
end
|
||||
end
|
||||
function modifier_legion_commander_duel_custom.prototype.OnDeath(self, event)
|
||||
if not IsServer() or self.finished then
|
||||
return
|
||||
end
|
||||
local deadUnit = event.unit
|
||||
if not deadUnit or deadUnit:entindex() ~= self:GetParent():entindex() then
|
||||
return
|
||||
end
|
||||
local ____duelDebug_25 = duelDebug
|
||||
local ____temp_24 = deadUnit:entindex()
|
||||
local ____opt_22 = self:getCounterpart()
|
||||
____duelDebug_25(
|
||||
nil,
|
||||
(("modifier_duel.OnDeath dead=" .. tostring(____temp_24)) .. " counterpart=") .. tostring(____opt_22 and ____opt_22:entindex())
|
||||
)
|
||||
local ability = self:GetAbility()
|
||||
local loser = self:GetParent()
|
||||
local winner = self:getCounterpart()
|
||||
if not ability or not winner then
|
||||
removeDuelModifiersFrom(nil, loser)
|
||||
return
|
||||
end
|
||||
self.finished = true
|
||||
if winner:IsAlive() then
|
||||
ability:completeDuelWithWinner(winner, loser)
|
||||
else
|
||||
ability:terminateDuelNoReward(loser, winner)
|
||||
end
|
||||
end
|
||||
function modifier_legion_commander_duel_custom.prototype.breakDuelNoReward(self)
|
||||
if not IsServer() or self.finished then
|
||||
return
|
||||
end
|
||||
self.finished = true
|
||||
local ____duelDebug_29 = duelDebug
|
||||
local ____temp_28 = self:GetParent():entindex()
|
||||
local ____opt_26 = self:getCounterpart()
|
||||
____duelDebug_29(
|
||||
nil,
|
||||
(("modifier_duel.breakDuelNoReward parent=" .. tostring(____temp_28)) .. " counterpart=") .. tostring(____opt_26 and ____opt_26:entindex())
|
||||
)
|
||||
local parent = self:GetParent()
|
||||
local counterpart = self:getCounterpart()
|
||||
local ability = self:GetAbility()
|
||||
if not ability or not counterpart then
|
||||
removeDuelModifiersFrom(nil, parent)
|
||||
return
|
||||
end
|
||||
ability:terminateDuelNoReward(parent, counterpart)
|
||||
end
|
||||
function modifier_legion_commander_duel_custom.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
duelDebug(
|
||||
nil,
|
||||
"modifier_duel.OnDestroy parent=" .. tostring(parent:entindex())
|
||||
)
|
||||
local ____opt_30 = parent.SetForceAttackTarget
|
||||
if ____opt_30 ~= nil then
|
||||
____opt_30(parent, nil)
|
||||
end
|
||||
end
|
||||
modifier_legion_commander_duel_custom = __TS__Decorate(
|
||||
modifier_legion_commander_duel_custom,
|
||||
modifier_legion_commander_duel_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_legion_commander_duel_custom"}
|
||||
)
|
||||
____exports.modifier_legion_commander_duel_custom = modifier_legion_commander_duel_custom
|
||||
return ____exports
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
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 ____luck = require("utils.luck")
|
||||
local calculateLuckChance = ____luck.calculateLuckChance
|
||||
____exports.ability_legion_commander_moment_of_courage_custom = __TS__Class()
|
||||
local ability_legion_commander_moment_of_courage_custom = ____exports.ability_legion_commander_moment_of_courage_custom
|
||||
ability_legion_commander_moment_of_courage_custom.name = "ability_legion_commander_moment_of_courage_custom"
|
||||
ability_legion_commander_moment_of_courage_custom.____file_path = "scripts/vscripts/abilities/heroes/legion_commander/ability_legion_commander_moment_of_courage_custom.lua"
|
||||
__TS__ClassExtends(ability_legion_commander_moment_of_courage_custom, BaseAbility)
|
||||
function ability_legion_commander_moment_of_courage_custom.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_legion_commander/legion_commander_courage_cnt.vpcf", context)
|
||||
PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_legion_commander.vsndevts", context)
|
||||
end
|
||||
function ability_legion_commander_moment_of_courage_custom.prototype.GetIntrinsicModifierName(self)
|
||||
return ____exports.modifier_legion_commander_moment_of_courage_intrinsic.name
|
||||
end
|
||||
ability_legion_commander_moment_of_courage_custom = __TS__Decorate(
|
||||
ability_legion_commander_moment_of_courage_custom,
|
||||
ability_legion_commander_moment_of_courage_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_legion_commander_moment_of_courage_custom"}
|
||||
)
|
||||
____exports.ability_legion_commander_moment_of_courage_custom = ability_legion_commander_moment_of_courage_custom
|
||||
____exports.modifier_legion_commander_moment_of_courage_intrinsic = __TS__Class()
|
||||
local modifier_legion_commander_moment_of_courage_intrinsic = ____exports.modifier_legion_commander_moment_of_courage_intrinsic
|
||||
modifier_legion_commander_moment_of_courage_intrinsic.name = "modifier_legion_commander_moment_of_courage_intrinsic"
|
||||
modifier_legion_commander_moment_of_courage_intrinsic.____file_path = "scripts/vscripts/abilities/heroes/legion_commander/ability_legion_commander_moment_of_courage_custom.lua"
|
||||
__TS__ClassExtends(modifier_legion_commander_moment_of_courage_intrinsic, BaseModifier)
|
||||
function modifier_legion_commander_moment_of_courage_intrinsic.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.lastProcGameTime = -999
|
||||
end
|
||||
function modifier_legion_commander_moment_of_courage_intrinsic.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_legion_commander_moment_of_courage_intrinsic.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_legion_commander_moment_of_courage_intrinsic.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_EVENT_ON_ATTACK_LANDED}
|
||||
end
|
||||
function modifier_legion_commander_moment_of_courage_intrinsic.prototype.OnAttackLanded(self, event)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
if parent:PassivesDisabled() or not ability then
|
||||
return
|
||||
end
|
||||
if event.target ~= parent then
|
||||
return
|
||||
end
|
||||
local attacker = event.attacker
|
||||
if not attacker or attacker == parent or not attacker:IsAlive() then
|
||||
return
|
||||
end
|
||||
if attacker:GetTeamNumber() == parent:GetTeamNumber() then
|
||||
return
|
||||
end
|
||||
local icd = ability:GetSpecialValueFor("proc_cooldown")
|
||||
local now = GameRules:GetGameTime()
|
||||
if now < self.lastProcGameTime + icd then
|
||||
return
|
||||
end
|
||||
local basePct = ability:GetSpecialValueFor("trigger_chance_pct")
|
||||
--- Слот псевдослучайности движка (не выносим в KV — не показывать игроку в тултипе).
|
||||
local prdSlot = 27
|
||||
local pid = parent:GetPlayerOwnerID()
|
||||
local isPlayerHero = pid >= 0 and parent:IsRealHero()
|
||||
local rolled = false
|
||||
if isPlayerHero then
|
||||
local luckPct = math.floor(calculateLuckChance(nil, parent, basePct / 100) * 100)
|
||||
local clamped = math.min(
|
||||
95,
|
||||
math.max(5, luckPct)
|
||||
)
|
||||
rolled = RollPseudoRandomPercentage(clamped, prdSlot, parent)
|
||||
else
|
||||
rolled = RollPercentage(basePct)
|
||||
end
|
||||
if not rolled then
|
||||
return
|
||||
end
|
||||
self.lastProcGameTime = now
|
||||
self:procCounter(ability, attacker)
|
||||
end
|
||||
function modifier_legion_commander_moment_of_courage_intrinsic.prototype.procCounter(self, ability, mainTarget)
|
||||
local parent = self:GetParent()
|
||||
local lsPct = ability:GetSpecialValueFor("counter_lifesteal_pct")
|
||||
local swings = math.max(
|
||||
1,
|
||||
math.floor(ability:GetSpecialValueFor("attack_count"))
|
||||
)
|
||||
local buffDur = ability:GetSpecialValueFor("buff_duration")
|
||||
local buffMs = ability:GetSpecialValueFor("buff_bonus_movespeed")
|
||||
local pfx = ParticleManager:CreateParticle("particles/units/heroes/hero_legion_commander/legion_commander_courage_cnt.vpcf", PATTACH_ABSORIGIN_FOLLOW, parent)
|
||||
ParticleManager:ReleaseParticleIndex(pfx)
|
||||
EmitSoundOn("Hero_LegionCommander.MomentOfCourage", parent)
|
||||
EmitSoundOn("Hero_LegionCommander.Courage", parent)
|
||||
parent:StartGestureWithPlaybackRate(ACT_DOTA_ATTACK, 2)
|
||||
parent:AddNewModifier(parent, ability, ____exports.modifier_legion_commander_moment_proc_haste.name, {duration = buffDur, ms = buffMs})
|
||||
do
|
||||
local i = 0
|
||||
while i < swings do
|
||||
parent:PerformAttack(
|
||||
mainTarget,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
)
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
local estDamage = math.max(
|
||||
1,
|
||||
parent:GetAverageTrueAttackDamage(mainTarget)
|
||||
)
|
||||
local heal = estDamage * swings * lsPct / 100
|
||||
if heal > 0 and parent:IsAlive() then
|
||||
parent:Heal(heal, ability)
|
||||
end
|
||||
end
|
||||
modifier_legion_commander_moment_of_courage_intrinsic = __TS__Decorate(
|
||||
modifier_legion_commander_moment_of_courage_intrinsic,
|
||||
modifier_legion_commander_moment_of_courage_intrinsic,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_legion_commander_moment_of_courage_intrinsic"}
|
||||
)
|
||||
____exports.modifier_legion_commander_moment_of_courage_intrinsic = modifier_legion_commander_moment_of_courage_intrinsic
|
||||
____exports.modifier_legion_commander_moment_proc_haste = __TS__Class()
|
||||
local modifier_legion_commander_moment_proc_haste = ____exports.modifier_legion_commander_moment_proc_haste
|
||||
modifier_legion_commander_moment_proc_haste.name = "modifier_legion_commander_moment_proc_haste"
|
||||
modifier_legion_commander_moment_proc_haste.____file_path = "scripts/vscripts/abilities/heroes/legion_commander/ability_legion_commander_moment_of_courage_custom.lua"
|
||||
__TS__ClassExtends(modifier_legion_commander_moment_proc_haste, BaseModifier)
|
||||
function modifier_legion_commander_moment_proc_haste.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.ms = 0
|
||||
end
|
||||
function modifier_legion_commander_moment_proc_haste.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_legion_commander_moment_proc_haste.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
function modifier_legion_commander_moment_proc_haste.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_legion_commander_moment_proc_haste.prototype.OnCreated(self, params)
|
||||
self.ms = params.ms or 0
|
||||
end
|
||||
function modifier_legion_commander_moment_proc_haste.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_MOVESPEED_BONUS_CONSTANT}
|
||||
end
|
||||
function modifier_legion_commander_moment_proc_haste.prototype.GetModifierMoveSpeedBonus_Constant(self)
|
||||
return self.ms
|
||||
end
|
||||
function modifier_legion_commander_moment_proc_haste.prototype.GetTexture(self)
|
||||
return "legion_commander_moment_of_courage"
|
||||
end
|
||||
modifier_legion_commander_moment_proc_haste = __TS__Decorate(
|
||||
modifier_legion_commander_moment_proc_haste,
|
||||
modifier_legion_commander_moment_proc_haste,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_legion_commander_moment_proc_haste"}
|
||||
)
|
||||
____exports.modifier_legion_commander_moment_proc_haste = modifier_legion_commander_moment_proc_haste
|
||||
return ____exports
|
||||
+307
@@ -0,0 +1,307 @@
|
||||
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
|
||||
____exports.ability_legion_commander_overwhelming_odds_custom = __TS__Class()
|
||||
local ability_legion_commander_overwhelming_odds_custom = ____exports.ability_legion_commander_overwhelming_odds_custom
|
||||
ability_legion_commander_overwhelming_odds_custom.name = "ability_legion_commander_overwhelming_odds_custom"
|
||||
ability_legion_commander_overwhelming_odds_custom.____file_path = "scripts/vscripts/abilities/heroes/legion_commander/ability_legion_commander_overwhelming_odds_custom.lua"
|
||||
__TS__ClassExtends(ability_legion_commander_overwhelming_odds_custom, BaseAbility)
|
||||
function ability_legion_commander_overwhelming_odds_custom.prototype.GetAOERadius(self)
|
||||
return self:GetSpecialValueFor("radius")
|
||||
end
|
||||
function ability_legion_commander_overwhelming_odds_custom.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_legion_commander/legion_commander_odds_cast.vpcf", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_legion_commander/legion_commander_odds.vpcf", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_legion_commander/legion_commander_odds_dmga.vpcf", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_legion_commander/legion_commander_odds_buff.vpcf", context)
|
||||
PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_legion_commander.vsndevts", context)
|
||||
end
|
||||
function ability_legion_commander_overwhelming_odds_custom.prototype.OnAbilityPhaseStart(self)
|
||||
if not IsServer() then
|
||||
return true
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
if not caster then
|
||||
return true
|
||||
end
|
||||
local p = ParticleManager:CreateParticle("particles/units/heroes/hero_legion_commander/legion_commander_odds_cast.vpcf", PATTACH_CUSTOMORIGIN, caster)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
p,
|
||||
1,
|
||||
caster,
|
||||
PATTACH_POINT_FOLLOW,
|
||||
"attach_attack1",
|
||||
caster:GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(p)
|
||||
EmitSoundOn("Hero_LegionCommander.Overwhelming.Cast", caster)
|
||||
return true
|
||||
end
|
||||
function ability_legion_commander_overwhelming_odds_custom.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self:ExecuteBurstAt(self:GetCursorPosition())
|
||||
end
|
||||
function ability_legion_commander_overwhelming_odds_custom.prototype.ExecuteBurstAt(self, groundPoint)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
if not caster or not caster:IsAlive() then
|
||||
return
|
||||
end
|
||||
local point = GetGroundPosition(groundPoint, nil)
|
||||
local radius = self:GetSpecialValueFor("radius")
|
||||
local dmgBase = self:GetSpecialValueFor("damage_base")
|
||||
local dmgPerEnemy = self:GetSpecialValueFor("damage_per_enemy")
|
||||
local armorDur = self:GetSpecialValueFor("armor_buff_duration")
|
||||
local debuffDurBase = self:GetSpecialValueFor("debuff_duration")
|
||||
local slowPct = self:GetSpecialValueFor("enemy_movespeed_slow")
|
||||
local selfDur = self:GetSpecialValueFor("self_buff_duration")
|
||||
local selfAs = self:GetSpecialValueFor("self_bonus_attack_speed")
|
||||
local selfMs = self:GetSpecialValueFor("self_bonus_movespeed")
|
||||
EmitSoundOnLocationWithCaster(point, "Hero_LegionCommander.Overwhelming.Location", caster)
|
||||
local pFx = ParticleManager:CreateParticle("particles/units/heroes/hero_legion_commander/legion_commander_odds.vpcf", PATTACH_WORLDORIGIN, nil)
|
||||
ParticleManager:SetParticleControl(pFx, 0, point)
|
||||
ParticleManager:SetParticleControl(
|
||||
pFx,
|
||||
1,
|
||||
caster:GetAbsOrigin()
|
||||
)
|
||||
ParticleManager:SetParticleControl(pFx, 2, point)
|
||||
ParticleManager:SetParticleControl(pFx, 3, point)
|
||||
ParticleManager:SetParticleControl(
|
||||
pFx,
|
||||
4,
|
||||
Vector(radius, radius, radius)
|
||||
)
|
||||
ParticleManager:SetParticleControl(pFx, 6, point)
|
||||
ParticleManager:ReleaseParticleIndex(pFx)
|
||||
local enemies = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
point,
|
||||
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 counted = 0
|
||||
for ____, u in ipairs(enemies) do
|
||||
do
|
||||
if not u or not u:IsAlive() or u:IsCourier() then
|
||||
goto __continue12
|
||||
end
|
||||
counted = counted + 1
|
||||
end
|
||||
::__continue12::
|
||||
end
|
||||
local totalDamage = dmgBase + dmgPerEnemy * counted
|
||||
for ____, enemy in ipairs(enemies) do
|
||||
do
|
||||
if not enemy or not enemy:IsAlive() or enemy:IsCourier() then
|
||||
goto __continue15
|
||||
end
|
||||
local pHit = ParticleManager:CreateParticle("particles/units/heroes/hero_legion_commander/legion_commander_odds_dmga.vpcf", PATTACH_ABSORIGIN_FOLLOW, enemy)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
pHit,
|
||||
0,
|
||||
enemy,
|
||||
PATTACH_POINT_FOLLOW,
|
||||
"attach_hitloc",
|
||||
enemy:GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
pHit,
|
||||
1,
|
||||
enemy,
|
||||
PATTACH_POINT_FOLLOW,
|
||||
"attach_hitloc",
|
||||
enemy:GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
pHit,
|
||||
3,
|
||||
caster,
|
||||
PATTACH_POINT_FOLLOW,
|
||||
"attach_hitloc",
|
||||
caster:GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(pHit)
|
||||
EmitSoundOn("Hero_LegionCommander.Overwhelming.Creep", enemy)
|
||||
ApplyDamage({
|
||||
victim = enemy,
|
||||
attacker = caster,
|
||||
damage = totalDamage,
|
||||
damage_type = DAMAGE_TYPE_MAGICAL,
|
||||
ability = self
|
||||
})
|
||||
local durSlow = debuffDurBase * (1 - enemy:GetStatusResistance())
|
||||
enemy:AddNewModifier(caster, self, ____exports.modifier_legion_commander_overwhelming_slow.name, {duration = durSlow, slow = slowPct})
|
||||
end
|
||||
::__continue15::
|
||||
end
|
||||
if counted > 0 then
|
||||
caster:RemoveModifierByName(____exports.modifier_legion_commander_overwhelming_armor.name)
|
||||
local mod = caster:AddNewModifier(caster, self, ____exports.modifier_legion_commander_overwhelming_armor.name, {duration = armorDur})
|
||||
if mod ~= nil and mod ~= nil then
|
||||
mod:SetStackCount(math.min(counted, 20))
|
||||
end
|
||||
end
|
||||
caster:AddNewModifier(caster, self, ____exports.modifier_legion_commander_overwhelming_self.name, {duration = selfDur, as = selfAs, ms = selfMs})
|
||||
end
|
||||
function ability_legion_commander_overwhelming_odds_custom.tryAutoCastAfterDuelCast(self, caster)
|
||||
if not caster or not caster:IsRealHero() or not caster:IsAlive() then
|
||||
return
|
||||
end
|
||||
local odds = caster:FindAbilityByName("ability_legion_commander_overwhelming_odds_custom")
|
||||
if not odds or odds:GetLevel() <= 0 then
|
||||
return
|
||||
end
|
||||
if not odds:IsCooldownReady() then
|
||||
return
|
||||
end
|
||||
local idx = odds:GetLevel() - 1
|
||||
local manaNeed = odds:GetManaCost(math.max(0, idx))
|
||||
if caster:GetMana() < manaNeed then
|
||||
return
|
||||
end
|
||||
local at = caster:GetAbsOrigin()
|
||||
odds:ExecuteBurstAt(GetGroundPosition(at, caster))
|
||||
odds:UseResources(true, false, false, true)
|
||||
end
|
||||
ability_legion_commander_overwhelming_odds_custom = __TS__Decorate(
|
||||
ability_legion_commander_overwhelming_odds_custom,
|
||||
ability_legion_commander_overwhelming_odds_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_legion_commander_overwhelming_odds_custom"}
|
||||
)
|
||||
____exports.ability_legion_commander_overwhelming_odds_custom = ability_legion_commander_overwhelming_odds_custom
|
||||
____exports.modifier_legion_commander_overwhelming_slow = __TS__Class()
|
||||
local modifier_legion_commander_overwhelming_slow = ____exports.modifier_legion_commander_overwhelming_slow
|
||||
modifier_legion_commander_overwhelming_slow.name = "modifier_legion_commander_overwhelming_slow"
|
||||
modifier_legion_commander_overwhelming_slow.____file_path = "scripts/vscripts/abilities/heroes/legion_commander/ability_legion_commander_overwhelming_odds_custom.lua"
|
||||
__TS__ClassExtends(modifier_legion_commander_overwhelming_slow, BaseModifier)
|
||||
function modifier_legion_commander_overwhelming_slow.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_legion_commander_overwhelming_slow.prototype.IsDebuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_legion_commander_overwhelming_slow.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
function modifier_legion_commander_overwhelming_slow.prototype.OnCreated(self, params)
|
||||
self.slowPct = params.slow or 40
|
||||
end
|
||||
function modifier_legion_commander_overwhelming_slow.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE}
|
||||
end
|
||||
function modifier_legion_commander_overwhelming_slow.prototype.GetModifierMoveSpeedBonus_Percentage(self)
|
||||
return -math.abs(self.slowPct)
|
||||
end
|
||||
function modifier_legion_commander_overwhelming_slow.prototype.GetStatusEffectName(self)
|
||||
return "particles/status_fx/status_effect_snapfire_slow.vpcf"
|
||||
end
|
||||
function modifier_legion_commander_overwhelming_slow.prototype.GetTexture(self)
|
||||
return "legion_commander_overwhelming_odds"
|
||||
end
|
||||
modifier_legion_commander_overwhelming_slow = __TS__Decorate(
|
||||
modifier_legion_commander_overwhelming_slow,
|
||||
modifier_legion_commander_overwhelming_slow,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_legion_commander_overwhelming_slow"}
|
||||
)
|
||||
____exports.modifier_legion_commander_overwhelming_slow = modifier_legion_commander_overwhelming_slow
|
||||
____exports.modifier_legion_commander_overwhelming_self = __TS__Class()
|
||||
local modifier_legion_commander_overwhelming_self = ____exports.modifier_legion_commander_overwhelming_self
|
||||
modifier_legion_commander_overwhelming_self.name = "modifier_legion_commander_overwhelming_self"
|
||||
modifier_legion_commander_overwhelming_self.____file_path = "scripts/vscripts/abilities/heroes/legion_commander/ability_legion_commander_overwhelming_odds_custom.lua"
|
||||
__TS__ClassExtends(modifier_legion_commander_overwhelming_self, BaseModifier)
|
||||
function modifier_legion_commander_overwhelming_self.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.as = 0
|
||||
self.ms = 0
|
||||
end
|
||||
function modifier_legion_commander_overwhelming_self.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_legion_commander_overwhelming_self.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
function modifier_legion_commander_overwhelming_self.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_legion_commander_overwhelming_self.prototype.OnCreated(self, params)
|
||||
self.as = params.as or 0
|
||||
self.ms = params.ms or 0
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetParent()
|
||||
local pBuff = ParticleManager:CreateParticle("particles/units/heroes/hero_legion_commander/legion_commander_odds_buff.vpcf", PATTACH_ABSORIGIN_FOLLOW, caster)
|
||||
ParticleManager:ReleaseParticleIndex(pBuff)
|
||||
end
|
||||
function modifier_legion_commander_overwhelming_self.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_ATTACKSPEED_BONUS_CONSTANT, MODIFIER_PROPERTY_MOVESPEED_BONUS_CONSTANT}
|
||||
end
|
||||
function modifier_legion_commander_overwhelming_self.prototype.GetModifierAttackSpeedBonus_Constant(self)
|
||||
return self.as
|
||||
end
|
||||
function modifier_legion_commander_overwhelming_self.prototype.GetModifierMoveSpeedBonus_Constant(self)
|
||||
return self.ms
|
||||
end
|
||||
function modifier_legion_commander_overwhelming_self.prototype.GetTexture(self)
|
||||
return "legion_commander_overwhelming_odds"
|
||||
end
|
||||
modifier_legion_commander_overwhelming_self = __TS__Decorate(
|
||||
modifier_legion_commander_overwhelming_self,
|
||||
modifier_legion_commander_overwhelming_self,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_legion_commander_overwhelming_self"}
|
||||
)
|
||||
____exports.modifier_legion_commander_overwhelming_self = modifier_legion_commander_overwhelming_self
|
||||
____exports.modifier_legion_commander_overwhelming_armor = __TS__Class()
|
||||
local modifier_legion_commander_overwhelming_armor = ____exports.modifier_legion_commander_overwhelming_armor
|
||||
modifier_legion_commander_overwhelming_armor.name = "modifier_legion_commander_overwhelming_armor"
|
||||
modifier_legion_commander_overwhelming_armor.____file_path = "scripts/vscripts/abilities/heroes/legion_commander/ability_legion_commander_overwhelming_odds_custom.lua"
|
||||
__TS__ClassExtends(modifier_legion_commander_overwhelming_armor, BaseModifier)
|
||||
function modifier_legion_commander_overwhelming_armor.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_legion_commander_overwhelming_armor.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
function modifier_legion_commander_overwhelming_armor.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_PHYSICAL_ARMOR_BONUS}
|
||||
end
|
||||
function modifier_legion_commander_overwhelming_armor.prototype.GetModifierPhysicalArmorBonus(self)
|
||||
local ab = self:GetAbility()
|
||||
local per = ab and ab:GetSpecialValueFor("armor_per_enemy") or 0
|
||||
return per * self:GetStackCount()
|
||||
end
|
||||
function modifier_legion_commander_overwhelming_armor.prototype.GetTexture(self)
|
||||
return "legion_commander_overwhelming_odds"
|
||||
end
|
||||
modifier_legion_commander_overwhelming_armor = __TS__Decorate(
|
||||
modifier_legion_commander_overwhelming_armor,
|
||||
modifier_legion_commander_overwhelming_armor,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_legion_commander_overwhelming_armor"}
|
||||
)
|
||||
____exports.modifier_legion_commander_overwhelming_armor = modifier_legion_commander_overwhelming_armor
|
||||
return ____exports
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
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
|
||||
____exports.ability_legion_commander_press_the_attack_custom = __TS__Class()
|
||||
local ability_legion_commander_press_the_attack_custom = ____exports.ability_legion_commander_press_the_attack_custom
|
||||
ability_legion_commander_press_the_attack_custom.name = "ability_legion_commander_press_the_attack_custom"
|
||||
ability_legion_commander_press_the_attack_custom.____file_path = "scripts/vscripts/abilities/heroes/legion_commander/ability_legion_commander_press_the_attack_custom.lua"
|
||||
__TS__ClassExtends(ability_legion_commander_press_the_attack_custom, BaseAbility)
|
||||
function ability_legion_commander_press_the_attack_custom.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_legion_commander/legion_commander_press_halo.vpcf", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_legion_commander/legion_commander_press.vpcf", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_legion_commander/legion_commander_press_a.vpcf", context)
|
||||
PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_legion_commander.vsndevts", context)
|
||||
end
|
||||
function ability_legion_commander_press_the_attack_custom.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local target = self:GetCursorTarget()
|
||||
if not caster or not caster:IsAlive() then
|
||||
return
|
||||
end
|
||||
if not target or not target:IsAlive() then
|
||||
target = caster
|
||||
end
|
||||
if not target:IsHero() then
|
||||
return
|
||||
end
|
||||
if target:GetTeamNumber() ~= caster:GetTeamNumber() then
|
||||
return
|
||||
end
|
||||
target:Purge(
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
)
|
||||
local duration = self:GetSpecialValueFor("duration")
|
||||
target:AddNewModifier(caster, self, ____exports.modifier_legion_commander_press_the_attack_buff.name, {duration = duration})
|
||||
local halo = ParticleManager:CreateParticle("particles/units/heroes/hero_legion_commander/legion_commander_press_halo.vpcf", PATTACH_ABSORIGIN_FOLLOW, caster)
|
||||
ParticleManager:ReleaseParticleIndex(halo)
|
||||
EmitSoundOn("Hero_LegionCommander.PressTheAttack", target)
|
||||
end
|
||||
ability_legion_commander_press_the_attack_custom = __TS__Decorate(
|
||||
ability_legion_commander_press_the_attack_custom,
|
||||
ability_legion_commander_press_the_attack_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_legion_commander_press_the_attack_custom"}
|
||||
)
|
||||
____exports.ability_legion_commander_press_the_attack_custom = ability_legion_commander_press_the_attack_custom
|
||||
____exports.modifier_legion_commander_press_the_attack_buff = __TS__Class()
|
||||
local modifier_legion_commander_press_the_attack_buff = ____exports.modifier_legion_commander_press_the_attack_buff
|
||||
modifier_legion_commander_press_the_attack_buff.name = "modifier_legion_commander_press_the_attack_buff"
|
||||
modifier_legion_commander_press_the_attack_buff.____file_path = "scripts/vscripts/abilities/heroes/legion_commander/ability_legion_commander_press_the_attack_custom.lua"
|
||||
__TS__ClassExtends(modifier_legion_commander_press_the_attack_buff, BaseModifier)
|
||||
function modifier_legion_commander_press_the_attack_buff.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_legion_commander_press_the_attack_buff.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_legion_commander_press_the_attack_buff.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
function modifier_legion_commander_press_the_attack_buff.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_ATTACKSPEED_BONUS_CONSTANT, MODIFIER_PROPERTY_HEALTH_REGEN_CONSTANT, MODIFIER_PROPERTY_MOVESPEED_BONUS_CONSTANT}
|
||||
end
|
||||
function modifier_legion_commander_press_the_attack_buff.prototype.GetModifierAttackSpeedBonus_Constant(self)
|
||||
local ab = self:GetAbility()
|
||||
return ab and ab:GetSpecialValueFor("attack_speed_bonus") or 0
|
||||
end
|
||||
function modifier_legion_commander_press_the_attack_buff.prototype.GetModifierConstantHealthRegen(self)
|
||||
local ab = self:GetAbility()
|
||||
return ab and ab:GetSpecialValueFor("hp_regen") or 0
|
||||
end
|
||||
function modifier_legion_commander_press_the_attack_buff.prototype.GetModifierMoveSpeedBonus_Constant(self)
|
||||
local ab = self:GetAbility()
|
||||
return ab and ab:GetSpecialValueFor("movespeed_bonus") or 0
|
||||
end
|
||||
function modifier_legion_commander_press_the_attack_buff.prototype.GetEffectName(self)
|
||||
return "particles/units/heroes/hero_legion_commander/legion_commander_press.vpcf"
|
||||
end
|
||||
function modifier_legion_commander_press_the_attack_buff.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_ABSORIGIN_FOLLOW
|
||||
end
|
||||
function modifier_legion_commander_press_the_attack_buff.prototype.GetTexture(self)
|
||||
return "legion_commander_press_the_attack"
|
||||
end
|
||||
modifier_legion_commander_press_the_attack_buff = __TS__Decorate(
|
||||
modifier_legion_commander_press_the_attack_buff,
|
||||
modifier_legion_commander_press_the_attack_buff,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_legion_commander_press_the_attack_buff"}
|
||||
)
|
||||
____exports.modifier_legion_commander_press_the_attack_buff = modifier_legion_commander_press_the_attack_buff
|
||||
return ____exports
|
||||
@@ -0,0 +1,134 @@
|
||||
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 ____modifier_general_fired = require("abilities.modifiers.modifier_general_fired")
|
||||
local modifier_general_fired = ____modifier_general_fired.modifier_general_fired
|
||||
local ____lina_mana_bonus = require("abilities.heroes.lina.lina_mana_bonus")
|
||||
local getLinaManaFlatDamage = ____lina_mana_bonus.getLinaManaFlatDamage
|
||||
____exports.ability_lina_dragon_slave_custom = __TS__Class()
|
||||
local ability_lina_dragon_slave_custom = ____exports.ability_lina_dragon_slave_custom
|
||||
ability_lina_dragon_slave_custom.name = "ability_lina_dragon_slave_custom"
|
||||
ability_lina_dragon_slave_custom.____file_path = "scripts/vscripts/abilities/heroes/lina/ability_lina_dragon_slave_custom.lua"
|
||||
__TS__ClassExtends(ability_lina_dragon_slave_custom, BaseAbility)
|
||||
function ability_lina_dragon_slave_custom.prototype.GetIntrinsicModifierName(self)
|
||||
return "modifier_lina_dragon_slave_custom_passive"
|
||||
end
|
||||
function ability_lina_dragon_slave_custom.prototype.OnSpellStart(self)
|
||||
local caster = self:GetCaster()
|
||||
local point = self:GetCursorPosition()
|
||||
local toPoint = point - caster:GetAbsOrigin()
|
||||
local direction = toPoint:Length2D() < 1 and caster:GetForwardVector() or toPoint:Normalized()
|
||||
local speed = self:GetSpecialValueFor("dragon_slave_speed")
|
||||
local width_initial = self:GetSpecialValueFor("dragon_slave_width_initial")
|
||||
local width_end = self:GetSpecialValueFor("dragon_slave_width_end")
|
||||
local distance = self:GetSpecialValueFor("dragon_slave_distance")
|
||||
ProjectileManager:CreateLinearProjectile({
|
||||
Ability = self,
|
||||
EffectName = "particles/units/heroes/hero_lina/lina_spell_dragon_slave.vpcf",
|
||||
vSpawnOrigin = caster:GetAbsOrigin(),
|
||||
fDistance = distance,
|
||||
fStartRadius = width_initial,
|
||||
fEndRadius = width_end,
|
||||
Source = caster,
|
||||
bHasFrontalCone = true,
|
||||
iUnitTargetTeam = DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
iUnitTargetType = DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC,
|
||||
vVelocity = direction * speed,
|
||||
bProvidesVision = true,
|
||||
iVisionRadius = 150,
|
||||
iVisionTeamNumber = caster:GetTeamNumber()
|
||||
})
|
||||
EmitSoundOn("Hero_Lina.DragonSlave", caster)
|
||||
end
|
||||
function ability_lina_dragon_slave_custom.prototype.OnProjectileHit(self, target, location)
|
||||
if not target then
|
||||
return false
|
||||
end
|
||||
if IsServer() then
|
||||
local caster = self:GetCaster()
|
||||
local damage = self:GetSpecialValueFor("dragon_slave_damage") + caster:GetAttackDamage() + getLinaManaFlatDamage(nil, self, caster)
|
||||
ApplyDamage({
|
||||
victim = target,
|
||||
attacker = self:GetCaster(),
|
||||
damage = damage,
|
||||
damage_type = self:GetAbilityDamageType(),
|
||||
ability = self
|
||||
})
|
||||
local modifier = target:AddNewModifier(
|
||||
self:GetCaster(),
|
||||
self,
|
||||
modifier_general_fired.name,
|
||||
{}
|
||||
)
|
||||
if modifier then
|
||||
local stacksPerLevel = self:GetSpecialValueFor("fire_stacks_per_level")
|
||||
do
|
||||
local i = 0
|
||||
while i < stacksPerLevel do
|
||||
modifier:IncrementStackCount()
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
local particleHit = ParticleManager:CreateParticle("particles/units/heroes/hero_lina/lina_spell_dragon_slave_impact.vpcf", PATTACH_ABSORIGIN, target)
|
||||
ParticleManager:ReleaseParticleIndex(particleHit)
|
||||
end
|
||||
return false
|
||||
end
|
||||
ability_lina_dragon_slave_custom = __TS__Decorate(
|
||||
ability_lina_dragon_slave_custom,
|
||||
ability_lina_dragon_slave_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_lina_dragon_slave_custom"}
|
||||
)
|
||||
____exports.ability_lina_dragon_slave_custom = ability_lina_dragon_slave_custom
|
||||
____exports.modifier_lina_dragon_slave_custom_passive = __TS__Class()
|
||||
local modifier_lina_dragon_slave_custom_passive = ____exports.modifier_lina_dragon_slave_custom_passive
|
||||
modifier_lina_dragon_slave_custom_passive.name = "modifier_lina_dragon_slave_custom_passive"
|
||||
modifier_lina_dragon_slave_custom_passive.____file_path = "scripts/vscripts/abilities/heroes/lina/ability_lina_dragon_slave_custom.lua"
|
||||
__TS__ClassExtends(modifier_lina_dragon_slave_custom_passive, BaseModifier)
|
||||
function modifier_lina_dragon_slave_custom_passive.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_EVENT_ON_ATTACK_LANDED}
|
||||
end
|
||||
function modifier_lina_dragon_slave_custom_passive.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_lina_dragon_slave_custom_passive.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_lina_dragon_slave_custom_passive.prototype.OnAttackLanded(self, event)
|
||||
if IsServer() then
|
||||
if self:GetParent():PassivesDisabled() then
|
||||
return
|
||||
end
|
||||
local ____temp_3 = event.attacker == self:GetParent()
|
||||
if ____temp_3 then
|
||||
local ____RandomInt_result_2 = RandomInt(1, 100)
|
||||
local ____opt_0 = self:GetAbility()
|
||||
____temp_3 = ____RandomInt_result_2 <= (____opt_0 and ____opt_0:GetSpecialValueFor("proc_chance"))
|
||||
end
|
||||
if ____temp_3 then
|
||||
local ability = self:GetParent():FindAbilityByName("ability_lina_dragon_slave_custom")
|
||||
if ability and event.target:IsAlive() and not event.attacker:IsIllusion() then
|
||||
local targetPosition = event.target:GetAbsOrigin()
|
||||
ability:GetCaster():SetCursorPosition(targetPosition)
|
||||
ability:OnSpellStart()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
modifier_lina_dragon_slave_custom_passive = __TS__Decorate(
|
||||
modifier_lina_dragon_slave_custom_passive,
|
||||
modifier_lina_dragon_slave_custom_passive,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_lina_dragon_slave_custom_passive"}
|
||||
)
|
||||
____exports.modifier_lina_dragon_slave_custom_passive = modifier_lina_dragon_slave_custom_passive
|
||||
return ____exports
|
||||
@@ -0,0 +1,290 @@
|
||||
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 ____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 ____modifier_general_fired = require("abilities.modifiers.modifier_general_fired")
|
||||
local modifier_general_fired = ____modifier_general_fired.modifier_general_fired
|
||||
local ____lina_mana_bonus = require("abilities.heroes.lina.lina_mana_bonus")
|
||||
local getLinaManaFlatDamage = ____lina_mana_bonus.getLinaManaFlatDamage
|
||||
____exports.ability_lina_flame_cloak_custom = __TS__Class()
|
||||
local ability_lina_flame_cloak_custom = ____exports.ability_lina_flame_cloak_custom
|
||||
ability_lina_flame_cloak_custom.name = "ability_lina_flame_cloak_custom"
|
||||
ability_lina_flame_cloak_custom.____file_path = "scripts/vscripts/abilities/heroes/lina/ability_lina_flame_cloak_custom.lua"
|
||||
__TS__ClassExtends(ability_lina_flame_cloak_custom, BaseAbility)
|
||||
function ability_lina_flame_cloak_custom.prototype.GetAOERadius(self)
|
||||
return self:GetSpecialValueFor("radius")
|
||||
end
|
||||
function ability_lina_flame_cloak_custom.prototype.OnSpellStart(self)
|
||||
local caster = self:GetCaster()
|
||||
self.duration = self:GetSpecialValueFor("duration")
|
||||
caster:AddNewModifier(caster, self, "modifier_lina_flame_cloak_custom", {duration = self.duration})
|
||||
EmitSoundOn("Hero_Lina.FlameCloak.Cast", caster)
|
||||
end
|
||||
function ability_lina_flame_cloak_custom.prototype.GetIntrinsicModifierName(self)
|
||||
return "modifier_lina_flame_cloak_custom_passive"
|
||||
end
|
||||
ability_lina_flame_cloak_custom = __TS__Decorate(
|
||||
ability_lina_flame_cloak_custom,
|
||||
ability_lina_flame_cloak_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_lina_flame_cloak_custom"}
|
||||
)
|
||||
____exports.ability_lina_flame_cloak_custom = ability_lina_flame_cloak_custom
|
||||
____exports.modifier_lina_flame_cloak_custom = __TS__Class()
|
||||
local modifier_lina_flame_cloak_custom = ____exports.modifier_lina_flame_cloak_custom
|
||||
modifier_lina_flame_cloak_custom.name = "modifier_lina_flame_cloak_custom"
|
||||
modifier_lina_flame_cloak_custom.____file_path = "scripts/vscripts/abilities/heroes/lina/ability_lina_flame_cloak_custom.lua"
|
||||
__TS__ClassExtends(modifier_lina_flame_cloak_custom, BaseModifier)
|
||||
function modifier_lina_flame_cloak_custom.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.thinkInterval = 0.33
|
||||
end
|
||||
function modifier_lina_flame_cloak_custom.prototype.OnCreated(self)
|
||||
if IsServer() then
|
||||
self:StartIntervalThink(self.thinkInterval)
|
||||
local parent = self:GetParent()
|
||||
local flameCloakParticle = ParticleManager:CreateParticle("particles/units/heroes/hero_lina/lina_flame_cloak.vpcf", PATTACH_ABSORIGIN_FOLLOW, parent)
|
||||
ParticleManager:SetParticleControl(
|
||||
flameCloakParticle,
|
||||
0,
|
||||
parent:GetAbsOrigin()
|
||||
)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
flameCloakParticle,
|
||||
1,
|
||||
parent,
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
"attach_hitloc",
|
||||
parent:GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
flameCloakParticle,
|
||||
3,
|
||||
parent:GetAbsOrigin()
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
flameCloakParticle,
|
||||
4,
|
||||
parent:GetAbsOrigin()
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
flameCloakParticle,
|
||||
10,
|
||||
parent:GetAbsOrigin()
|
||||
)
|
||||
self:AddParticle(
|
||||
flameCloakParticle,
|
||||
false,
|
||||
false,
|
||||
-1,
|
||||
false,
|
||||
false
|
||||
)
|
||||
end
|
||||
end
|
||||
function modifier_lina_flame_cloak_custom.prototype.OnDestroy(self)
|
||||
if IsClient() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local currentPos = parent:GetAbsOrigin()
|
||||
local groundPos = GetGroundPosition(currentPos, parent)
|
||||
parent:SetAbsOrigin(groundPos)
|
||||
end
|
||||
function modifier_lina_flame_cloak_custom.prototype.CheckState(self)
|
||||
return {[MODIFIER_STATE_FLYING] = true, [MODIFIER_STATE_NO_UNIT_COLLISION] = true}
|
||||
end
|
||||
function modifier_lina_flame_cloak_custom.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE, MODIFIER_PROPERTY_VISUAL_Z_DELTA}
|
||||
end
|
||||
function modifier_lina_flame_cloak_custom.prototype.GetVisualZDelta(self)
|
||||
local ____opt_0 = self:GetAbility()
|
||||
return ____opt_0 and ____opt_0:GetSpecialValueFor("visualzdelta") or 0
|
||||
end
|
||||
function modifier_lina_flame_cloak_custom.prototype.GetModifierMoveSpeedBonus_Percentage(self)
|
||||
local ____opt_2 = self:GetAbility()
|
||||
return ____opt_2 and ____opt_2:GetSpecialValueFor("movespeed_bonus") or 0
|
||||
end
|
||||
function modifier_lina_flame_cloak_custom.prototype.GetEffectName(self)
|
||||
return "particles/econ/items/ember_spirit/ember_ti9/ember_ti9_flameguard.vpcf"
|
||||
end
|
||||
function modifier_lina_flame_cloak_custom.prototype.GetEffectAttachType(self)
|
||||
return PATTACH_ABSORIGIN_FOLLOW
|
||||
end
|
||||
function modifier_lina_flame_cloak_custom.prototype.OnIntervalThink(self)
|
||||
if IsServer() then
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
if not caster then
|
||||
return
|
||||
end
|
||||
local damage = ability:GetSpecialValueFor("damage_per_second") * self.thinkInterval + getLinaManaFlatDamage(nil, ability, caster)
|
||||
local ____FindUnitsInRadius_8 = FindUnitsInRadius
|
||||
local ____temp_6 = caster:GetTeamNumber()
|
||||
local ____temp_7 = caster:GetAbsOrigin()
|
||||
local ____opt_4 = self:GetAbility()
|
||||
local units = ____FindUnitsInRadius_8(
|
||||
____temp_6,
|
||||
____temp_7,
|
||||
nil,
|
||||
____opt_4 and ____opt_4:GetSpecialValueFor("radius"),
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC,
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
__TS__ArrayForEach(
|
||||
units,
|
||||
function(____, unit)
|
||||
ApplyDamage({
|
||||
victim = unit,
|
||||
attacker = caster,
|
||||
damage = damage,
|
||||
damage_type = DAMAGE_TYPE_MAGICAL,
|
||||
ability = ability
|
||||
})
|
||||
local modifier = unit:AddNewModifier(caster, ability, modifier_general_fired.name, {})
|
||||
if modifier then
|
||||
modifier:IncrementStackCount()
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
modifier_lina_flame_cloak_custom = __TS__Decorate(
|
||||
modifier_lina_flame_cloak_custom,
|
||||
modifier_lina_flame_cloak_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_lina_flame_cloak_custom"}
|
||||
)
|
||||
____exports.modifier_lina_flame_cloak_custom = modifier_lina_flame_cloak_custom
|
||||
____exports.modifier_lina_flame_cloak_custom_passive = __TS__Class()
|
||||
local modifier_lina_flame_cloak_custom_passive = ____exports.modifier_lina_flame_cloak_custom_passive
|
||||
modifier_lina_flame_cloak_custom_passive.name = "modifier_lina_flame_cloak_custom_passive"
|
||||
modifier_lina_flame_cloak_custom_passive.____file_path = "scripts/vscripts/abilities/heroes/lina/ability_lina_flame_cloak_custom.lua"
|
||||
__TS__ClassExtends(modifier_lina_flame_cloak_custom_passive, BaseModifier)
|
||||
function modifier_lina_flame_cloak_custom_passive.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_lina_flame_cloak_custom_passive.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_lina_flame_cloak_custom_passive.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_EVENT_ON_TAKEDAMAGE}
|
||||
end
|
||||
function modifier_lina_flame_cloak_custom_passive.prototype.OnTakeDamage(self, event)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if self:GetParent():PassivesDisabled() then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
if event.inflictor == ability then
|
||||
return
|
||||
end
|
||||
local unit = event.unit
|
||||
if unit == self:GetParent() or unit:GetTeamNumber() == self:GetParent():GetTeamNumber() or self:GetParent() ~= event.attacker then
|
||||
return
|
||||
end
|
||||
local modifier = self:GetParent():FindModifierByName("modifier_lina_flame_cloak_custom_passive_buff")
|
||||
local ____modifier_12 = modifier
|
||||
if ____modifier_12 then
|
||||
local ____temp_11 = modifier:GetStackCount()
|
||||
local ____opt_9 = self:GetAbility()
|
||||
____modifier_12 = ____temp_11 == (____opt_9 and ____opt_9:GetSpecialValueFor("max_stacks"))
|
||||
end
|
||||
if ____modifier_12 then
|
||||
local ____self_15 = self:GetParent():AddNewModifier(
|
||||
self:GetParent(),
|
||||
self:GetAbility(),
|
||||
"modifier_lina_flame_cloak_custom_passive_buff",
|
||||
{duration = 5}
|
||||
)
|
||||
local ____self_15_SetStackCount_16 = ____self_15.SetStackCount
|
||||
local ____opt_13 = self:GetAbility()
|
||||
____self_15_SetStackCount_16(
|
||||
____self_15,
|
||||
____opt_13 and ____opt_13:GetSpecialValueFor("max_stacks")
|
||||
)
|
||||
else
|
||||
self:GetParent():AddNewModifier(
|
||||
self:GetParent(),
|
||||
self:GetAbility(),
|
||||
"modifier_lina_flame_cloak_custom_passive_buff",
|
||||
{duration = 5}
|
||||
):IncrementStackCount()
|
||||
end
|
||||
end
|
||||
modifier_lina_flame_cloak_custom_passive = __TS__Decorate(
|
||||
modifier_lina_flame_cloak_custom_passive,
|
||||
modifier_lina_flame_cloak_custom_passive,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_lina_flame_cloak_custom_passive"}
|
||||
)
|
||||
____exports.modifier_lina_flame_cloak_custom_passive = modifier_lina_flame_cloak_custom_passive
|
||||
____exports.modifier_lina_flame_cloak_custom_passive_buff = __TS__Class()
|
||||
local modifier_lina_flame_cloak_custom_passive_buff = ____exports.modifier_lina_flame_cloak_custom_passive_buff
|
||||
modifier_lina_flame_cloak_custom_passive_buff.name = "modifier_lina_flame_cloak_custom_passive_buff"
|
||||
modifier_lina_flame_cloak_custom_passive_buff.____file_path = "scripts/vscripts/abilities/heroes/lina/ability_lina_flame_cloak_custom.lua"
|
||||
__TS__ClassExtends(modifier_lina_flame_cloak_custom_passive_buff, BaseModifier)
|
||||
function modifier_lina_flame_cloak_custom_passive_buff.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_lina_flame_cloak_custom_passive_buff.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_lina_flame_cloak_custom_passive_buff.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE, MODIFIER_PROPERTY_ATTACKSPEED_PERCENTAGE, MODIFIER_PROPERTY_SPELL_AMPLIFY_PERCENTAGE, MODIFIER_PROPERTY_MAGICAL_RESISTANCE_BONUS}
|
||||
end
|
||||
function modifier_lina_flame_cloak_custom_passive_buff.prototype.GetModifierMoveSpeedBonus_Percentage(self)
|
||||
if self:GetParent():PassivesDisabled() then
|
||||
return 0
|
||||
end
|
||||
local ____opt_17 = self:GetAbility()
|
||||
return (____opt_17 and ____opt_17:GetSpecialValueFor("movespeed_bonus")) * self:GetStackCount()
|
||||
end
|
||||
function modifier_lina_flame_cloak_custom_passive_buff.prototype.GetModifierAttackSpeedPercentage(self)
|
||||
if self:GetParent():PassivesDisabled() then
|
||||
return 0
|
||||
end
|
||||
local ____opt_19 = self:GetAbility()
|
||||
return (____opt_19 and ____opt_19:GetSpecialValueFor("attackspeed_bonus")) * self:GetStackCount()
|
||||
end
|
||||
function modifier_lina_flame_cloak_custom_passive_buff.prototype.GetModifierSpellAmplify_Percentage(self)
|
||||
if self:GetParent():PassivesDisabled() then
|
||||
return 0
|
||||
end
|
||||
local ____opt_21 = self:GetAbility()
|
||||
return (____opt_21 and ____opt_21:GetSpecialValueFor("spell_amplify")) * self:GetStackCount()
|
||||
end
|
||||
function modifier_lina_flame_cloak_custom_passive_buff.prototype.GetModifierMagicalResistanceBonus(self)
|
||||
if self:GetParent():PassivesDisabled() then
|
||||
return 0
|
||||
end
|
||||
local ____opt_23 = self:GetAbility()
|
||||
return (____opt_23 and ____opt_23:GetSpecialValueFor("magical_resistance")) * self:GetStackCount()
|
||||
end
|
||||
modifier_lina_flame_cloak_custom_passive_buff = __TS__Decorate(
|
||||
modifier_lina_flame_cloak_custom_passive_buff,
|
||||
modifier_lina_flame_cloak_custom_passive_buff,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_lina_flame_cloak_custom_passive_buff"}
|
||||
)
|
||||
____exports.modifier_lina_flame_cloak_custom_passive_buff = modifier_lina_flame_cloak_custom_passive_buff
|
||||
return ____exports
|
||||
@@ -0,0 +1,150 @@
|
||||
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 ____lina_mana_bonus = require("abilities.heroes.lina.lina_mana_bonus")
|
||||
local getLinaManaFlatDamage = ____lina_mana_bonus.getLinaManaFlatDamage
|
||||
____exports.ability_lina_laguna_blade_custom = __TS__Class()
|
||||
local ability_lina_laguna_blade_custom = ____exports.ability_lina_laguna_blade_custom
|
||||
ability_lina_laguna_blade_custom.name = "ability_lina_laguna_blade_custom"
|
||||
ability_lina_laguna_blade_custom.____file_path = "scripts/vscripts/abilities/heroes/lina/ability_lina_laguna_blade_custom.lua"
|
||||
__TS__ClassExtends(ability_lina_laguna_blade_custom, BaseAbility)
|
||||
function ability_lina_laguna_blade_custom.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local target = self:GetCursorTarget()
|
||||
if not caster or not target then
|
||||
return
|
||||
end
|
||||
if caster:HasScepter() and self:GetSpecialValueFor("mana_cost_facet") > 0 then
|
||||
caster:AddNewModifier(
|
||||
caster,
|
||||
self,
|
||||
"modifier_lina_laguna_blade_custom_buff",
|
||||
{duration = self:GetSpecialValueFor("duration")}
|
||||
)
|
||||
end
|
||||
EmitSoundOn("Ability.LagunaBlade", caster)
|
||||
EmitSoundOn("Ability.LagunaBladeImpact", target)
|
||||
local particleId = ParticleManager:CreateParticle("particles/units/heroes/hero_lina/lina_spell_laguna_blade.vpcf", PATTACH_CUSTOMORIGIN, caster)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
particleId,
|
||||
0,
|
||||
caster,
|
||||
PATTACH_POINT_FOLLOW,
|
||||
"attach_attack1",
|
||||
caster:GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
particleId,
|
||||
1,
|
||||
target,
|
||||
PATTACH_POINT_FOLLOW,
|
||||
"attach_hitloc",
|
||||
target:GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
Timers:CreateTimer(
|
||||
0.5,
|
||||
function()
|
||||
ParticleManager:DestroyParticle(particleId, false)
|
||||
ParticleManager:ReleaseParticleIndex(particleId)
|
||||
return nil
|
||||
end
|
||||
)
|
||||
Timers:CreateTimer(
|
||||
self:GetSpecialValueFor("damage_delay"),
|
||||
function()
|
||||
if target and not target:IsNull() and target:IsAlive() then
|
||||
local ____caster_HasScepter_result_0
|
||||
if caster:HasScepter() then
|
||||
____caster_HasScepter_result_0 = DAMAGE_TYPE_PURE
|
||||
else
|
||||
____caster_HasScepter_result_0 = DAMAGE_TYPE_MAGICAL
|
||||
end
|
||||
local damageType = ____caster_HasScepter_result_0
|
||||
local damage = self:GetSpecialValueFor("damage") + getLinaManaFlatDamage(nil, self, caster)
|
||||
if caster:HasScepter() then
|
||||
damage = damage + caster:GetIntellect(true) * self:GetSpecialValueFor("damage_mult_agh")
|
||||
end
|
||||
ApplyDamage({
|
||||
victim = target,
|
||||
attacker = caster,
|
||||
damage = damage,
|
||||
damage_type = damageType,
|
||||
ability = self
|
||||
})
|
||||
end
|
||||
return nil
|
||||
end
|
||||
)
|
||||
end
|
||||
function ability_lina_laguna_blade_custom.prototype.GetManaCost(self, level)
|
||||
local caster = self:GetCaster()
|
||||
if caster and caster:HasScepter() and self:GetSpecialValueFor("mana_cost_facet") > 0 then
|
||||
return caster:GetMana() * (1 - self:GetSpecialValueFor("mana_cost_facet_tooltip") * 0.01) + self:GetSpecialValueFor("mana_cost")
|
||||
end
|
||||
return self:GetSpecialValueFor("mana_cost")
|
||||
end
|
||||
ability_lina_laguna_blade_custom = __TS__Decorate(
|
||||
ability_lina_laguna_blade_custom,
|
||||
ability_lina_laguna_blade_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_lina_laguna_blade_custom"}
|
||||
)
|
||||
____exports.ability_lina_laguna_blade_custom = ability_lina_laguna_blade_custom
|
||||
____exports.modifier_lina_laguna_blade_custom_buff = __TS__Class()
|
||||
local modifier_lina_laguna_blade_custom_buff = ____exports.modifier_lina_laguna_blade_custom_buff
|
||||
modifier_lina_laguna_blade_custom_buff.name = "modifier_lina_laguna_blade_custom_buff"
|
||||
modifier_lina_laguna_blade_custom_buff.____file_path = "scripts/vscripts/abilities/heroes/lina/ability_lina_laguna_blade_custom.lua"
|
||||
__TS__ClassExtends(modifier_lina_laguna_blade_custom_buff, BaseModifier)
|
||||
function modifier_lina_laguna_blade_custom_buff.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_lina_laguna_blade_custom_buff.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_lina_laguna_blade_custom_buff.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_lina_laguna_blade_custom_buff.prototype.RemoveOnDeath(self)
|
||||
return true
|
||||
end
|
||||
function modifier_lina_laguna_blade_custom_buff.prototype.OnCreated(self, params)
|
||||
self:StartIntervalThink(0.1)
|
||||
end
|
||||
function modifier_lina_laguna_blade_custom_buff.prototype.OnIntervalThink(self)
|
||||
local ____self_SetStackCount_9 = self.SetStackCount
|
||||
local ____opt_1 = self:GetCaster()
|
||||
local ____temp_5 = ____opt_1 and ____opt_1:GetMaxMana()
|
||||
local ____opt_3 = self:GetCaster()
|
||||
local ____temp_8 = ____temp_5 - (____opt_3 and ____opt_3:GetMana())
|
||||
local ____opt_6 = self:GetAbility()
|
||||
____self_SetStackCount_9(
|
||||
self,
|
||||
____temp_8 * (____opt_6 and ____opt_6:GetSpecialValueFor("spell_amplify")) * 0.01
|
||||
)
|
||||
end
|
||||
function modifier_lina_laguna_blade_custom_buff.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_SPELL_AMPLIFY_PERCENTAGE}
|
||||
end
|
||||
function modifier_lina_laguna_blade_custom_buff.prototype.GetModifierSpellAmplify_Percentage(self, event)
|
||||
return self:GetStackCount()
|
||||
end
|
||||
modifier_lina_laguna_blade_custom_buff = __TS__Decorate(
|
||||
modifier_lina_laguna_blade_custom_buff,
|
||||
modifier_lina_laguna_blade_custom_buff,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_lina_laguna_blade_custom_buff"}
|
||||
)
|
||||
____exports.modifier_lina_laguna_blade_custom_buff = modifier_lina_laguna_blade_custom_buff
|
||||
return ____exports
|
||||
@@ -0,0 +1,158 @@
|
||||
local ____lualib = require("lualib_bundle")
|
||||
local __TS__Class = ____lualib.__TS__Class
|
||||
local __TS__ClassExtends = ____lualib.__TS__ClassExtends
|
||||
local __TS__ArrayForEach = ____lualib.__TS__ArrayForEach
|
||||
local __TS__Decorate = ____lualib.__TS__Decorate
|
||||
local ____exports = {}
|
||||
local ____dota_ts_adapter = require("lib.dota_ts_adapter")
|
||||
local BaseAbility = ____dota_ts_adapter.BaseAbility
|
||||
local registerAbility = ____dota_ts_adapter.registerAbility
|
||||
local ____modifier_general_fired = require("abilities.modifiers.modifier_general_fired")
|
||||
local modifier_general_fired = ____modifier_general_fired.modifier_general_fired
|
||||
local ____lina_mana_bonus = require("abilities.heroes.lina.lina_mana_bonus")
|
||||
local getLinaManaFlatDamage = ____lina_mana_bonus.getLinaManaFlatDamage
|
||||
____exports.ability_lina_light_strike_array_custom = __TS__Class()
|
||||
local ability_lina_light_strike_array_custom = ____exports.ability_lina_light_strike_array_custom
|
||||
ability_lina_light_strike_array_custom.name = "ability_lina_light_strike_array_custom"
|
||||
ability_lina_light_strike_array_custom.____file_path = "scripts/vscripts/abilities/heroes/lina/ability_lina_light_strike_array_custom.lua"
|
||||
__TS__ClassExtends(ability_lina_light_strike_array_custom, BaseAbility)
|
||||
function ability_lina_light_strike_array_custom.prototype.GetAOERadius(self)
|
||||
return 250
|
||||
end
|
||||
function ability_lina_light_strike_array_custom.prototype.OnSpellStart(self)
|
||||
local caster = self:GetCaster()
|
||||
local point = self:GetCursorPosition()
|
||||
local delay = self:GetSpecialValueFor("light_strike_array_delay_time")
|
||||
local radius = self:GetSpecialValueFor("light_strike_array_aoe")
|
||||
local stun_duration = self:GetSpecialValueFor("light_strike_array_stun_duration")
|
||||
local multicast_max = self:GetLevel()
|
||||
local multicast_chance = self:GetSpecialValueFor("light_strike_chance")
|
||||
self:CastLSA(point, delay, radius, stun_duration)
|
||||
local additional_casts = 0
|
||||
do
|
||||
local i = 1
|
||||
while i < multicast_max do
|
||||
local roll = RandomInt(1, 100)
|
||||
if roll <= multicast_chance then
|
||||
additional_casts = additional_casts + 1
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
do
|
||||
local i = 0
|
||||
while i < additional_casts do
|
||||
Timers:CreateTimer(
|
||||
delay * (i + 1),
|
||||
function()
|
||||
self:CastLSA(point, delay, radius, stun_duration)
|
||||
end
|
||||
)
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
function ability_lina_light_strike_array_custom.prototype.CastLSA(self, point, delay, radius, stun_duration)
|
||||
local caster = self:GetCaster()
|
||||
local particleWarning = ParticleManager:CreateParticle("particles/units/heroes/hero_lina/lina_spell_light_strike_array_ray_team.vpcf", PATTACH_WORLDORIGIN, nil)
|
||||
ParticleManager:SetParticleControl(particleWarning, 0, point)
|
||||
ParticleManager:SetParticleControl(
|
||||
particleWarning,
|
||||
1,
|
||||
Vector(radius, 0, 0)
|
||||
)
|
||||
local particleRay = ParticleManager:CreateParticle("particles/units/heroes/hero_lina/lina_spell_light_strike_array_ray.vpcf", PATTACH_WORLDORIGIN, nil)
|
||||
ParticleManager:SetParticleControl(particleRay, 0, point)
|
||||
ParticleManager:SetParticleControl(
|
||||
particleRay,
|
||||
1,
|
||||
Vector(radius, 0, 0)
|
||||
)
|
||||
EmitSoundOnLocationWithCaster(point, "Ability.PreLightStrikeArray", caster)
|
||||
Timers:CreateTimer(
|
||||
delay,
|
||||
function()
|
||||
self:CreateExplosion(point, radius, stun_duration, caster)
|
||||
if not HasTalent(
|
||||
nil,
|
||||
self:GetCaster(),
|
||||
"special_bonus_unique_lina_light_strike_array_five"
|
||||
) then
|
||||
return
|
||||
end
|
||||
local sideDistance = radius / 2
|
||||
local leftPoint = Vector(point.x - sideDistance, point.y, point.z)
|
||||
self:CreateExplosion(leftPoint, radius, stun_duration, caster)
|
||||
local rightPoint = Vector(point.x + sideDistance, point.y, point.z)
|
||||
self:CreateExplosion(rightPoint, radius, stun_duration, caster)
|
||||
local topPoint = Vector(point.x, point.y + sideDistance, point.z)
|
||||
self:CreateExplosion(topPoint, radius, stun_duration, caster)
|
||||
local bottomPoint = Vector(point.x, point.y - sideDistance, point.z)
|
||||
self:CreateExplosion(bottomPoint, radius, stun_duration, caster)
|
||||
ParticleManager:DestroyParticle(particleWarning, false)
|
||||
ParticleManager:ReleaseParticleIndex(particleWarning)
|
||||
ParticleManager:DestroyParticle(particleRay, false)
|
||||
ParticleManager:ReleaseParticleIndex(particleRay)
|
||||
end
|
||||
)
|
||||
end
|
||||
function ability_lina_light_strike_array_custom.prototype.CreateExplosion(self, point, radius, stun_duration, caster)
|
||||
local damage = self:GetSpecialValueFor("light_strike_array_damage") + getLinaManaFlatDamage(nil, self, caster)
|
||||
local units = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
point,
|
||||
nil,
|
||||
radius,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC,
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
__TS__ArrayForEach(
|
||||
units,
|
||||
function(____, unit)
|
||||
ApplyDamage({
|
||||
victim = unit,
|
||||
attacker = caster,
|
||||
damage = damage,
|
||||
damage_type = self:GetAbilityDamageType(),
|
||||
ability = self
|
||||
})
|
||||
local modifier = unit:AddNewModifier(
|
||||
self:GetCaster(),
|
||||
self,
|
||||
modifier_general_fired.name,
|
||||
{}
|
||||
)
|
||||
if modifier then
|
||||
local stacksPerLevel = self:GetSpecialValueFor("fire_stacks_per_level")
|
||||
do
|
||||
local i = 0
|
||||
while i < stacksPerLevel do
|
||||
modifier:IncrementStackCount()
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
unit:AddNewModifier(caster, self, "modifier_stunned", {duration = stun_duration})
|
||||
end
|
||||
)
|
||||
local particleEffect = ParticleManager:CreateParticle("particles/units/heroes/hero_lina/lina_spell_light_strike_array.vpcf", PATTACH_WORLDORIGIN, nil)
|
||||
ParticleManager:SetParticleControl(particleEffect, 0, point)
|
||||
ParticleManager:SetParticleControl(
|
||||
particleEffect,
|
||||
1,
|
||||
Vector(radius, 0, 0)
|
||||
)
|
||||
ParticleManager:ReleaseParticleIndex(particleEffect)
|
||||
EmitSoundOnLocationWithCaster(point, "Ability.LightStrikeArray", caster)
|
||||
end
|
||||
ability_lina_light_strike_array_custom = __TS__Decorate(
|
||||
ability_lina_light_strike_array_custom,
|
||||
ability_lina_light_strike_array_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_lina_light_strike_array_custom"}
|
||||
)
|
||||
____exports.ability_lina_light_strike_array_custom = ability_lina_light_strike_array_custom
|
||||
return ____exports
|
||||
@@ -0,0 +1,158 @@
|
||||
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 ____modifier_general_fired = require("abilities.modifiers.modifier_general_fired")
|
||||
local modifier_general_fired = ____modifier_general_fired.modifier_general_fired
|
||||
local ____incoming_damage_reduction_combine = require("utils.incoming_damage_reduction_combine")
|
||||
local removeIncomingDamageReductionSource = ____incoming_damage_reduction_combine.removeIncomingDamageReductionSource
|
||||
local setIncomingDamageReductionSource = ____incoming_damage_reduction_combine.setIncomingDamageReductionSource
|
||||
local LINA_SCORCH_AFFINITY_INCOMING_SOURCE = "modifier_lina_scorch_affinity_innate"
|
||||
____exports.ability_lina_scorch_affinity_innate = __TS__Class()
|
||||
local ability_lina_scorch_affinity_innate = ____exports.ability_lina_scorch_affinity_innate
|
||||
ability_lina_scorch_affinity_innate.name = "ability_lina_scorch_affinity_innate"
|
||||
ability_lina_scorch_affinity_innate.____file_path = "scripts/vscripts/abilities/heroes/lina/ability_lina_scorch_affinity_innate.lua"
|
||||
__TS__ClassExtends(ability_lina_scorch_affinity_innate, BaseAbility)
|
||||
function ability_lina_scorch_affinity_innate.prototype.GetIntrinsicModifierName(self)
|
||||
return ____exports.modifier_lina_scorch_affinity_innate.name
|
||||
end
|
||||
function ability_lina_scorch_affinity_innate.prototype.GetAOERadius(self)
|
||||
return self:GetSpecialValueFor("radius")
|
||||
end
|
||||
ability_lina_scorch_affinity_innate = __TS__Decorate(
|
||||
ability_lina_scorch_affinity_innate,
|
||||
ability_lina_scorch_affinity_innate,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_lina_scorch_affinity_innate"}
|
||||
)
|
||||
____exports.ability_lina_scorch_affinity_innate = ability_lina_scorch_affinity_innate
|
||||
____exports.modifier_lina_scorch_affinity_innate = __TS__Class()
|
||||
local modifier_lina_scorch_affinity_innate = ____exports.modifier_lina_scorch_affinity_innate
|
||||
modifier_lina_scorch_affinity_innate.name = "modifier_lina_scorch_affinity_innate"
|
||||
modifier_lina_scorch_affinity_innate.____file_path = "scripts/vscripts/abilities/heroes/lina/ability_lina_scorch_affinity_innate.lua"
|
||||
__TS__ClassExtends(modifier_lina_scorch_affinity_innate, BaseModifier)
|
||||
function modifier_lina_scorch_affinity_innate.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.burningUnitCount = 0
|
||||
self.totalBonusPct = 0
|
||||
end
|
||||
function modifier_lina_scorch_affinity_innate.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_lina_scorch_affinity_innate.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_lina_scorch_affinity_innate.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_lina_scorch_affinity_innate.prototype.RemoveOnDeath(self)
|
||||
return false
|
||||
end
|
||||
function modifier_lina_scorch_affinity_innate.prototype.OnCreated(self)
|
||||
self:refreshBurningUnits()
|
||||
self:StartIntervalThink(0.25)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
setIncomingDamageReductionSource(
|
||||
nil,
|
||||
self:GetParent(),
|
||||
LINA_SCORCH_AFFINITY_INCOMING_SOURCE,
|
||||
function()
|
||||
if self:GetParent():PassivesDisabled() then
|
||||
return 0
|
||||
end
|
||||
return math.max(0, self.totalBonusPct)
|
||||
end
|
||||
)
|
||||
end
|
||||
function modifier_lina_scorch_affinity_innate.prototype.OnRefresh(self)
|
||||
self:refreshBurningUnits()
|
||||
end
|
||||
function modifier_lina_scorch_affinity_innate.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
removeIncomingDamageReductionSource(
|
||||
nil,
|
||||
self:GetParent(),
|
||||
LINA_SCORCH_AFFINITY_INCOMING_SOURCE
|
||||
)
|
||||
end
|
||||
function modifier_lina_scorch_affinity_innate.prototype.OnIntervalThink(self)
|
||||
self:refreshBurningUnits()
|
||||
end
|
||||
function modifier_lina_scorch_affinity_innate.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_DAMAGEOUTGOING_PERCENTAGE, MODIFIER_PROPERTY_SPELL_AMPLIFY_PERCENTAGE}
|
||||
end
|
||||
function modifier_lina_scorch_affinity_innate.prototype.refreshBurningUnits(self)
|
||||
local parent = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
if not ability or not parent or parent:IsNull() then
|
||||
self.burningUnitCount = 0
|
||||
self.totalBonusPct = 0
|
||||
self:SetStackCount(0)
|
||||
return
|
||||
end
|
||||
local radius = ability:GetSpecialValueFor("radius")
|
||||
local basePct = ability:GetSpecialValueFor("bonus_pct_base")
|
||||
local pctPerHeroLevel = ability:GetSpecialValueFor("bonus_pct_per_hero_level")
|
||||
local heroLevel = parent:IsHero() and parent:GetLevel() or 1
|
||||
local pctPerUnit = basePct + heroLevel * pctPerHeroLevel
|
||||
local count = 0
|
||||
local units = FindUnitsInRadius(
|
||||
parent:GetTeamNumber(),
|
||||
parent:GetAbsOrigin(),
|
||||
nil,
|
||||
radius,
|
||||
DOTA_UNIT_TARGET_TEAM_BOTH,
|
||||
bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC),
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
for ____, unit in ipairs(units) do
|
||||
do
|
||||
if not unit or unit:IsNull() or not unit:IsAlive() then
|
||||
goto __continue19
|
||||
end
|
||||
if unit == parent then
|
||||
goto __continue19
|
||||
end
|
||||
local fired = unit:FindModifierByName(modifier_general_fired.name)
|
||||
if fired and not fired:IsNull() then
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
::__continue19::
|
||||
end
|
||||
self.burningUnitCount = count
|
||||
self.totalBonusPct = count * pctPerUnit
|
||||
self:SetStackCount(count)
|
||||
end
|
||||
function modifier_lina_scorch_affinity_innate.prototype.GetModifierDamageOutgoing_Percentage(self)
|
||||
if self:GetParent():PassivesDisabled() then
|
||||
return 0
|
||||
end
|
||||
return self.totalBonusPct
|
||||
end
|
||||
function modifier_lina_scorch_affinity_innate.prototype.GetModifierSpellAmplify_Percentage(self, event)
|
||||
if self:GetParent():PassivesDisabled() then
|
||||
return 0
|
||||
end
|
||||
return self.totalBonusPct
|
||||
end
|
||||
modifier_lina_scorch_affinity_innate = __TS__Decorate(
|
||||
modifier_lina_scorch_affinity_innate,
|
||||
modifier_lina_scorch_affinity_innate,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_lina_scorch_affinity_innate"}
|
||||
)
|
||||
____exports.modifier_lina_scorch_affinity_innate = modifier_lina_scorch_affinity_innate
|
||||
return ____exports
|
||||
@@ -0,0 +1,14 @@
|
||||
--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
|
||||
local ____exports = {}
|
||||
--- Доп. урон кастомок Лины: текущая мана × (`mana_damage_from_current_pct` / 100) из AbilityValues.
|
||||
function ____exports.getLinaManaFlatDamage(self, ability, caster)
|
||||
if caster == nil or not caster:IsAlive() or not caster:IsHero() then
|
||||
return 0
|
||||
end
|
||||
local pct = ability:GetSpecialValueFor("mana_damage_from_current_pct")
|
||||
if pct <= 0 then
|
||||
return 0
|
||||
end
|
||||
return caster:GetMana() * (pct * 0.01)
|
||||
end
|
||||
return ____exports
|
||||
@@ -0,0 +1,182 @@
|
||||
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 ____luna_lucent_beam_shared = require("abilities.heroes.luna.luna_lucent_beam_shared")
|
||||
local applyLucentBeamHit = ____luna_lucent_beam_shared.applyLucentBeamHit
|
||||
local getLunaLucentBeamAbility = ____luna_lucent_beam_shared.getLunaLucentBeamAbility
|
||||
____exports.ability_luna_eclipse_custom = __TS__Class()
|
||||
local ability_luna_eclipse_custom = ____exports.ability_luna_eclipse_custom
|
||||
ability_luna_eclipse_custom.name = "ability_luna_eclipse_custom"
|
||||
ability_luna_eclipse_custom.____file_path = "scripts/vscripts/abilities/heroes/luna/ability_luna_eclipse_custom.lua"
|
||||
__TS__ClassExtends(ability_luna_eclipse_custom, BaseAbility)
|
||||
function ability_luna_eclipse_custom.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_luna/luna_eclipse.vpcf", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_luna/luna_lucent_beam.vpcf", context)
|
||||
end
|
||||
function ability_luna_eclipse_custom.prototype.GetBehavior(self)
|
||||
return DOTA_ABILITY_BEHAVIOR_NO_TARGET
|
||||
end
|
||||
function ability_luna_eclipse_custom.prototype.OnUpgrade(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local lucentBeam = getLunaLucentBeamAbility(nil, caster)
|
||||
if not lucentBeam or lucentBeam:GetLevel() > 0 then
|
||||
return
|
||||
end
|
||||
if self:GetLevel() > 0 then
|
||||
self:SetLevel(0)
|
||||
if caster:IsRealHero() then
|
||||
local heroCaster = caster
|
||||
heroCaster:SetAbilityPoints(heroCaster:GetAbilityPoints() + 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
function ability_luna_eclipse_custom.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local duration = self:GetSpecialValueFor("eclipse_duration")
|
||||
local radius = self:GetSpecialValueFor("eclipse_radius")
|
||||
local eclipsePfx = ParticleManager:CreateParticle("particles/units/heroes/hero_luna/luna_eclipse.vpcf", PATTACH_ABSORIGIN_FOLLOW, caster)
|
||||
ParticleManager:SetParticleControl(
|
||||
eclipsePfx,
|
||||
0,
|
||||
caster:GetAbsOrigin()
|
||||
)
|
||||
ParticleManager:SetParticleControl(
|
||||
eclipsePfx,
|
||||
1,
|
||||
Vector(radius, 0, 0)
|
||||
)
|
||||
Timers:CreateTimer(
|
||||
duration + 0.5,
|
||||
function()
|
||||
ParticleManager:DestroyParticle(eclipsePfx, false)
|
||||
ParticleManager:ReleaseParticleIndex(eclipsePfx)
|
||||
return nil
|
||||
end
|
||||
)
|
||||
caster:AddNewModifier(caster, self, ____exports.modifier_luna_eclipse_active.name, {duration = duration})
|
||||
EmitSoundOn("Hero_Luna.Eclipse", caster)
|
||||
end
|
||||
ability_luna_eclipse_custom = __TS__Decorate(
|
||||
ability_luna_eclipse_custom,
|
||||
ability_luna_eclipse_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_luna_eclipse_custom"}
|
||||
)
|
||||
____exports.ability_luna_eclipse_custom = ability_luna_eclipse_custom
|
||||
____exports.modifier_luna_eclipse_active = __TS__Class()
|
||||
local modifier_luna_eclipse_active = ____exports.modifier_luna_eclipse_active
|
||||
modifier_luna_eclipse_active.name = "modifier_luna_eclipse_active"
|
||||
modifier_luna_eclipse_active.____file_path = "scripts/vscripts/abilities/heroes/luna/ability_luna_eclipse_custom.lua"
|
||||
__TS__ClassExtends(modifier_luna_eclipse_active, BaseModifier)
|
||||
function modifier_luna_eclipse_active.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.beamsDone = 0
|
||||
end
|
||||
function modifier_luna_eclipse_active.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_luna_eclipse_active.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_luna_eclipse_active.prototype.GetAttributes(self)
|
||||
return MODIFIER_ATTRIBUTE_MULTIPLE
|
||||
end
|
||||
function modifier_luna_eclipse_active.prototype.OnCreated(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local interval = self:GetAbility():GetSpecialValueFor("beam_interval")
|
||||
self:StartIntervalThink(interval)
|
||||
self:OnIntervalThink()
|
||||
end
|
||||
function modifier_luna_eclipse_active.prototype.OnIntervalThink(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
local parent = self:GetParent()
|
||||
if not ability or not parent then
|
||||
self:Destroy()
|
||||
return
|
||||
end
|
||||
local facetWaves = parent:HasScepter() and math.floor(ability:GetSpecialValueFor("eclipse_facet_wave_count")) or 0
|
||||
local maxBeams = facetWaves > 0 and facetWaves or math.floor(ability:GetSpecialValueFor("eclipse_beam_count"))
|
||||
if self.beamsDone >= maxBeams then
|
||||
self:Destroy()
|
||||
return
|
||||
end
|
||||
local radius = ability:GetSpecialValueFor("eclipse_radius")
|
||||
local lucentBeam = getLunaLucentBeamAbility(nil, parent)
|
||||
local hasLeveledLucentBeam = not not lucentBeam and lucentBeam:GetLevel() > 0
|
||||
local damage = hasLeveledLucentBeam and lucentBeam:GetSpecialValueFor("lucent_beam_damage") or 0
|
||||
local stun = hasLeveledLucentBeam and lucentBeam:GetSpecialValueFor("stun_duration") or 0
|
||||
local enemies = FindUnitsInRadius(
|
||||
parent:GetTeamNumber(),
|
||||
parent:GetAbsOrigin(),
|
||||
nil,
|
||||
radius,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC,
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
if #enemies == 0 then
|
||||
self.beamsDone = self.beamsDone + 1
|
||||
return
|
||||
end
|
||||
if facetWaves > 0 then
|
||||
for ____, victim in ipairs(enemies) do
|
||||
do
|
||||
if not victim or not victim:IsAlive() then
|
||||
goto __continue23
|
||||
end
|
||||
applyLucentBeamHit(
|
||||
nil,
|
||||
parent,
|
||||
ability,
|
||||
victim,
|
||||
damage,
|
||||
stun
|
||||
)
|
||||
end
|
||||
::__continue23::
|
||||
end
|
||||
else
|
||||
local victim = enemies[RandomInt(0, #enemies - 1) + 1]
|
||||
if not victim or not victim:IsAlive() then
|
||||
self.beamsDone = self.beamsDone + 1
|
||||
return
|
||||
end
|
||||
applyLucentBeamHit(
|
||||
nil,
|
||||
parent,
|
||||
ability,
|
||||
victim,
|
||||
damage,
|
||||
stun
|
||||
)
|
||||
end
|
||||
self.beamsDone = self.beamsDone + 1
|
||||
end
|
||||
modifier_luna_eclipse_active = __TS__Decorate(
|
||||
modifier_luna_eclipse_active,
|
||||
modifier_luna_eclipse_active,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_luna_eclipse_active"}
|
||||
)
|
||||
____exports.modifier_luna_eclipse_active = modifier_luna_eclipse_active
|
||||
return ____exports
|
||||
@@ -0,0 +1,78 @@
|
||||
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 registerAbility = ____dota_ts_adapter.registerAbility
|
||||
local ____luna_lucent_beam_shared = require("abilities.heroes.luna.luna_lucent_beam_shared")
|
||||
local applyLucentBeamHit = ____luna_lucent_beam_shared.applyLucentBeamHit
|
||||
____exports.ability_luna_lucent_beam_custom = __TS__Class()
|
||||
local ability_luna_lucent_beam_custom = ____exports.ability_luna_lucent_beam_custom
|
||||
ability_luna_lucent_beam_custom.name = "ability_luna_lucent_beam_custom"
|
||||
ability_luna_lucent_beam_custom.____file_path = "scripts/vscripts/abilities/heroes/luna/ability_luna_lucent_beam_custom.lua"
|
||||
__TS__ClassExtends(ability_luna_lucent_beam_custom, BaseAbility)
|
||||
function ability_luna_lucent_beam_custom.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_luna/luna_lucent_beam.vpcf", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_luna/luna_lucent_beam_precast.vpcf", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_luna/luna_lucent_beam_cast.vpcf", context)
|
||||
end
|
||||
function ability_luna_lucent_beam_custom.prototype.GetBehavior(self)
|
||||
return DOTA_ABILITY_BEHAVIOR_UNIT_TARGET
|
||||
end
|
||||
function ability_luna_lucent_beam_custom.prototype.GetAOERadius(self)
|
||||
local r = self:GetSpecialValueFor("lucent_beam_aoe_radius")
|
||||
return r > 0 and r or 0
|
||||
end
|
||||
function ability_luna_lucent_beam_custom.prototype.OnAbilityPhaseStart(self)
|
||||
if not IsServer() then
|
||||
return true
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
self.precastPfx = ParticleManager:CreateParticle("particles/units/heroes/hero_luna/luna_lucent_beam_precast.vpcf", PATTACH_ABSORIGIN_FOLLOW, caster)
|
||||
return true
|
||||
end
|
||||
function ability_luna_lucent_beam_custom.prototype.OnAbilityPhaseInterrupted(self)
|
||||
self:clearPrecastPfx()
|
||||
end
|
||||
function ability_luna_lucent_beam_custom.prototype.clearPrecastPfx(self)
|
||||
if self.precastPfx == nil then
|
||||
return
|
||||
end
|
||||
ParticleManager:DestroyParticle(self.precastPfx, false)
|
||||
ParticleManager:ReleaseParticleIndex(self.precastPfx)
|
||||
self.precastPfx = nil
|
||||
end
|
||||
function ability_luna_lucent_beam_custom.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self:clearPrecastPfx()
|
||||
local caster = self:GetCaster()
|
||||
local target = self:GetCursorTarget()
|
||||
if not target or target:IsNull() or not target:IsAlive() then
|
||||
return
|
||||
end
|
||||
local castPfx = ParticleManager:CreateParticle("particles/units/heroes/hero_luna/luna_lucent_beam_cast.vpcf", PATTACH_ABSORIGIN_FOLLOW, caster)
|
||||
ParticleManager:ReleaseParticleIndex(castPfx)
|
||||
EmitSoundOn("Hero_Luna.LucentBeam.Cast", caster)
|
||||
local damage = self:GetSpecialValueFor("lucent_beam_damage") + caster:GetMaxMana() * (self:GetSpecialValueFor("mana_damage_pct") / 100)
|
||||
local stun = self:GetSpecialValueFor("stun_duration")
|
||||
applyLucentBeamHit(
|
||||
nil,
|
||||
caster,
|
||||
self,
|
||||
target,
|
||||
damage,
|
||||
stun
|
||||
)
|
||||
end
|
||||
ability_luna_lucent_beam_custom = __TS__Decorate(
|
||||
ability_luna_lucent_beam_custom,
|
||||
ability_luna_lucent_beam_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_luna_lucent_beam_custom"}
|
||||
)
|
||||
____exports.ability_luna_lucent_beam_custom = ability_luna_lucent_beam_custom
|
||||
return ____exports
|
||||
@@ -0,0 +1,134 @@
|
||||
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 ____moon_sisters_synergy = require("abilities.heroes.mirana_luna.moon_sisters_synergy")
|
||||
local ensureMoonSistersInnateModifier = ____moon_sisters_synergy.ensureMoonSistersInnateModifier
|
||||
local removeMoonSistersInnateModifier = ____moon_sisters_synergy.removeMoonSistersInnateModifier
|
||||
____exports.ability_luna_lunar_blessing_custom = __TS__Class()
|
||||
local ability_luna_lunar_blessing_custom = ____exports.ability_luna_lunar_blessing_custom
|
||||
ability_luna_lunar_blessing_custom.name = "ability_luna_lunar_blessing_custom"
|
||||
ability_luna_lunar_blessing_custom.____file_path = "scripts/vscripts/abilities/heroes/luna/ability_luna_lunar_blessing_custom.lua"
|
||||
__TS__ClassExtends(ability_luna_lunar_blessing_custom, BaseAbility)
|
||||
function ability_luna_lunar_blessing_custom.prototype.GetIntrinsicModifierName(self)
|
||||
return ____exports.modifier_luna_lunar_blessing_aura_custom.name
|
||||
end
|
||||
function ability_luna_lunar_blessing_custom.prototype.GetAOERadius(self)
|
||||
return self:GetSpecialValueFor("blessing_radius")
|
||||
end
|
||||
ability_luna_lunar_blessing_custom = __TS__Decorate(
|
||||
ability_luna_lunar_blessing_custom,
|
||||
ability_luna_lunar_blessing_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_luna_lunar_blessing_custom"}
|
||||
)
|
||||
____exports.ability_luna_lunar_blessing_custom = ability_luna_lunar_blessing_custom
|
||||
____exports.modifier_luna_lunar_blessing_aura_custom = __TS__Class()
|
||||
local modifier_luna_lunar_blessing_aura_custom = ____exports.modifier_luna_lunar_blessing_aura_custom
|
||||
modifier_luna_lunar_blessing_aura_custom.name = "modifier_luna_lunar_blessing_aura_custom"
|
||||
modifier_luna_lunar_blessing_aura_custom.____file_path = "scripts/vscripts/abilities/heroes/luna/ability_luna_lunar_blessing_custom.lua"
|
||||
__TS__ClassExtends(modifier_luna_lunar_blessing_aura_custom, BaseModifier)
|
||||
function modifier_luna_lunar_blessing_aura_custom.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_luna_lunar_blessing_aura_custom.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_luna_lunar_blessing_aura_custom.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_luna_lunar_blessing_aura_custom.prototype.IsAura(self)
|
||||
return true
|
||||
end
|
||||
function modifier_luna_lunar_blessing_aura_custom.prototype.GetModifierAura(self)
|
||||
return ____exports.modifier_luna_lunar_blessing_buff_custom.name
|
||||
end
|
||||
function modifier_luna_lunar_blessing_aura_custom.prototype.GetAuraRadius(self)
|
||||
return self:GetAbility():GetSpecialValueFor("blessing_radius")
|
||||
end
|
||||
function modifier_luna_lunar_blessing_aura_custom.prototype.GetAuraDuration(self)
|
||||
return 0.5
|
||||
end
|
||||
function modifier_luna_lunar_blessing_aura_custom.prototype.GetAuraSearchTeam(self)
|
||||
return DOTA_UNIT_TARGET_TEAM_FRIENDLY
|
||||
end
|
||||
function modifier_luna_lunar_blessing_aura_custom.prototype.GetAuraSearchType(self)
|
||||
return DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC
|
||||
end
|
||||
function modifier_luna_lunar_blessing_aura_custom.prototype.GetAuraSearchFlags(self)
|
||||
return DOTA_UNIT_TARGET_FLAG_NONE
|
||||
end
|
||||
function modifier_luna_lunar_blessing_aura_custom.prototype.OnCreated(self)
|
||||
local parent = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
if ability then
|
||||
ensureMoonSistersInnateModifier(nil, parent, ability)
|
||||
end
|
||||
end
|
||||
function modifier_luna_lunar_blessing_aura_custom.prototype.OnDestroy(self)
|
||||
removeMoonSistersInnateModifier(
|
||||
nil,
|
||||
self:GetParent()
|
||||
)
|
||||
end
|
||||
function modifier_luna_lunar_blessing_aura_custom.prototype.GetAuraEntityReject(self, _hTarget)
|
||||
return self:GetParent():PassivesDisabled()
|
||||
end
|
||||
modifier_luna_lunar_blessing_aura_custom = __TS__Decorate(
|
||||
modifier_luna_lunar_blessing_aura_custom,
|
||||
modifier_luna_lunar_blessing_aura_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_luna_lunar_blessing_aura_custom"}
|
||||
)
|
||||
____exports.modifier_luna_lunar_blessing_aura_custom = modifier_luna_lunar_blessing_aura_custom
|
||||
____exports.modifier_luna_lunar_blessing_buff_custom = __TS__Class()
|
||||
local modifier_luna_lunar_blessing_buff_custom = ____exports.modifier_luna_lunar_blessing_buff_custom
|
||||
modifier_luna_lunar_blessing_buff_custom.name = "modifier_luna_lunar_blessing_buff_custom"
|
||||
modifier_luna_lunar_blessing_buff_custom.____file_path = "scripts/vscripts/abilities/heroes/luna/ability_luna_lunar_blessing_custom.lua"
|
||||
__TS__ClassExtends(modifier_luna_lunar_blessing_buff_custom, BaseModifier)
|
||||
function modifier_luna_lunar_blessing_buff_custom.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_luna_lunar_blessing_buff_custom.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_luna_lunar_blessing_buff_custom.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_luna_lunar_blessing_buff_custom.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_PREATTACK_BONUS_DAMAGE, MODIFIER_PROPERTY_SPELL_AMPLIFY_PERCENTAGE}
|
||||
end
|
||||
function modifier_luna_lunar_blessing_buff_custom.prototype.GetModifierPreAttack_BonusDamage(self)
|
||||
local caster = self:GetCaster()
|
||||
if caster and caster:PassivesDisabled() then
|
||||
return 0
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return 0
|
||||
end
|
||||
return ability:GetSpecialValueFor("blessing_bonus_damage") * self:GetParent():GetLevel()
|
||||
end
|
||||
function modifier_luna_lunar_blessing_buff_custom.prototype.GetModifierSpellAmplify_Percentage(self)
|
||||
local caster = self:GetCaster()
|
||||
if caster and caster:PassivesDisabled() then
|
||||
return 0
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
local perLevel = ability:GetSpecialValueFor("spell_amp_per_level")
|
||||
return perLevel * self:GetParent():GetLevel()
|
||||
end
|
||||
modifier_luna_lunar_blessing_buff_custom = __TS__Decorate(
|
||||
modifier_luna_lunar_blessing_buff_custom,
|
||||
modifier_luna_lunar_blessing_buff_custom,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_luna_lunar_blessing_buff_custom"}
|
||||
)
|
||||
____exports.modifier_luna_lunar_blessing_buff_custom = modifier_luna_lunar_blessing_buff_custom
|
||||
return ____exports
|
||||
@@ -0,0 +1,192 @@
|
||||
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
|
||||
____exports.ability_luna_moon_glaive_custom = __TS__Class()
|
||||
local ability_luna_moon_glaive_custom = ____exports.ability_luna_moon_glaive_custom
|
||||
ability_luna_moon_glaive_custom.name = "ability_luna_moon_glaive_custom"
|
||||
ability_luna_moon_glaive_custom.____file_path = "scripts/vscripts/abilities/heroes/luna/ability_luna_moon_glaive_custom.lua"
|
||||
__TS__ClassExtends(ability_luna_moon_glaive_custom, BaseAbility)
|
||||
function ability_luna_moon_glaive_custom.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_luna/luna_moon_glaive.vpcf", context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_luna/luna_base_attack.vpcf", context)
|
||||
end
|
||||
function ability_luna_moon_glaive_custom.prototype.GetIntrinsicModifierName(self)
|
||||
return ____exports.modifier_luna_moon_glaive_passive.name
|
||||
end
|
||||
function ability_luna_moon_glaive_custom.prototype.LaunchGlaive(self, from, to, state)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self.glaiveChain = state
|
||||
local caster = self:GetCaster()
|
||||
local speed = math.max(
|
||||
600,
|
||||
caster:GetProjectileSpeed()
|
||||
)
|
||||
ProjectileManager:CreateTrackingProjectile({
|
||||
Ability = self,
|
||||
EffectName = "particles/units/heroes/hero_luna/luna_moon_glaive.vpcf",
|
||||
Source = from,
|
||||
Target = to,
|
||||
iMoveSpeed = speed,
|
||||
bDodgeable = true,
|
||||
bVisibleToEnemies = true
|
||||
})
|
||||
end
|
||||
function ability_luna_moon_glaive_custom.prototype.OnProjectileHit(self, target, location)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if not target or target:IsNull() or not target:IsAlive() then
|
||||
self.glaiveChain = nil
|
||||
return
|
||||
end
|
||||
local state = self.glaiveChain
|
||||
local ability = self
|
||||
local caster = ability:GetCaster()
|
||||
if not state or not caster then
|
||||
self.glaiveChain = nil
|
||||
return
|
||||
end
|
||||
local atk = state.attacker:GetAverageTrueAttackDamage(target)
|
||||
local dmg = atk * state.damageMult
|
||||
ApplyDamage({
|
||||
victim = target,
|
||||
attacker = state.attacker,
|
||||
damage = dmg,
|
||||
damage_type = DAMAGE_TYPE_PHYSICAL,
|
||||
damage_flags = bit.bor(DOTA_DAMAGE_FLAG_NO_SPELL_AMPLIFICATION, DOTA_DAMAGE_FLAG_ATTACK_MODIFIER),
|
||||
ability = ability
|
||||
})
|
||||
EmitSoundOn("Hero_Luna.MoonGlaive", target)
|
||||
local bounceRange = ability:GetSpecialValueFor("bounce_range")
|
||||
local falloff = state.falloffPct / 100
|
||||
local nextMult = state.damageMult * falloff
|
||||
if state.bouncesRemaining <= 0 then
|
||||
self.glaiveChain = nil
|
||||
return
|
||||
end
|
||||
local enemies = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
target:GetAbsOrigin(),
|
||||
nil,
|
||||
bounceRange,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC,
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_CLOSEST,
|
||||
false
|
||||
)
|
||||
local next
|
||||
local targetIdx = target:entindex()
|
||||
for ____, e in ipairs(enemies) do
|
||||
do
|
||||
if not e or not e:IsAlive() then
|
||||
goto __continue11
|
||||
end
|
||||
if e:entindex() == targetIdx then
|
||||
goto __continue11
|
||||
end
|
||||
next = e
|
||||
break
|
||||
end
|
||||
::__continue11::
|
||||
end
|
||||
if not next then
|
||||
self.glaiveChain = nil
|
||||
return
|
||||
end
|
||||
local nextState = {attacker = state.attacker, bouncesRemaining = state.bouncesRemaining - 1, damageMult = nextMult, falloffPct = state.falloffPct}
|
||||
self:LaunchGlaive(target, next, nextState)
|
||||
end
|
||||
ability_luna_moon_glaive_custom = __TS__Decorate(
|
||||
ability_luna_moon_glaive_custom,
|
||||
ability_luna_moon_glaive_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_luna_moon_glaive_custom"}
|
||||
)
|
||||
____exports.ability_luna_moon_glaive_custom = ability_luna_moon_glaive_custom
|
||||
____exports.modifier_luna_moon_glaive_passive = __TS__Class()
|
||||
local modifier_luna_moon_glaive_passive = ____exports.modifier_luna_moon_glaive_passive
|
||||
modifier_luna_moon_glaive_passive.name = "modifier_luna_moon_glaive_passive"
|
||||
modifier_luna_moon_glaive_passive.____file_path = "scripts/vscripts/abilities/heroes/luna/ability_luna_moon_glaive_custom.lua"
|
||||
__TS__ClassExtends(modifier_luna_moon_glaive_passive, BaseModifier)
|
||||
function modifier_luna_moon_glaive_passive.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_luna_moon_glaive_passive.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_luna_moon_glaive_passive.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_EVENT_ON_ATTACK_LANDED}
|
||||
end
|
||||
function modifier_luna_moon_glaive_passive.prototype.OnAttackLanded(self, event)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
if parent:PassivesDisabled() then
|
||||
return
|
||||
end
|
||||
if event.attacker ~= parent then
|
||||
return
|
||||
end
|
||||
local primary = event.target
|
||||
if not primary or not primary:IsAlive() then
|
||||
return
|
||||
end
|
||||
local ability = self:GetAbility()
|
||||
if not ability then
|
||||
return
|
||||
end
|
||||
local bounceRange = ability:GetSpecialValueFor("bounce_range")
|
||||
local totalBounces = math.floor(ability:GetSpecialValueFor("moon_glaive_bounce_count"))
|
||||
local basePct = ability:GetSpecialValueFor("bounce_damage_pct") / 100
|
||||
local falloff = ability:GetSpecialValueFor("bounce_falloff_pct")
|
||||
local enemies = FindUnitsInRadius(
|
||||
parent:GetTeamNumber(),
|
||||
primary:GetAbsOrigin(),
|
||||
nil,
|
||||
bounceRange,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC,
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_CLOSEST,
|
||||
false
|
||||
)
|
||||
local first
|
||||
local primaryIdx = primary:entindex()
|
||||
for ____, e in ipairs(enemies) do
|
||||
do
|
||||
if not e or not e:IsAlive() then
|
||||
goto __continue25
|
||||
end
|
||||
if e:entindex() == primaryIdx then
|
||||
goto __continue25
|
||||
end
|
||||
first = e
|
||||
break
|
||||
end
|
||||
::__continue25::
|
||||
end
|
||||
if not first or totalBounces <= 0 then
|
||||
return
|
||||
end
|
||||
local state = {attacker = parent, bouncesRemaining = totalBounces - 1, damageMult = basePct, falloffPct = falloff}
|
||||
ability:LaunchGlaive(primary, first, state)
|
||||
end
|
||||
modifier_luna_moon_glaive_passive = __TS__Decorate(
|
||||
modifier_luna_moon_glaive_passive,
|
||||
modifier_luna_moon_glaive_passive,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_luna_moon_glaive_passive"}
|
||||
)
|
||||
____exports.modifier_luna_moon_glaive_passive = modifier_luna_moon_glaive_passive
|
||||
return ____exports
|
||||
@@ -0,0 +1,509 @@
|
||||
local ____lualib = require("lualib_bundle")
|
||||
local __TS__Class = ____lualib.__TS__Class
|
||||
local __TS__ClassExtends = ____lualib.__TS__ClassExtends
|
||||
local __TS__Decorate = ____lualib.__TS__Decorate
|
||||
local Set = ____lualib.Set
|
||||
local __TS__New = ____lualib.__TS__New
|
||||
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 function applyTwinGlaiveFacetVuln(self, caster, victim, ability)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if not victim or victim:IsNull() or not victim:IsAlive() then
|
||||
return
|
||||
end
|
||||
local dur = ability:GetSpecialValueFor("twin_facet_vuln_duration")
|
||||
if dur <= 0 then
|
||||
return
|
||||
end
|
||||
victim:AddNewModifier(caster, ability, ____exports.modifier_luna_twin_glaives_facet_vuln.name, {duration = dur})
|
||||
end
|
||||
--- Как в ванили Wild Axes / примере temple_guardian: vpcf цепляется к юниту оси, а не к linear projectile.
|
||||
local WILD_AXE_PARTICLE = "particles/twin_glaves.vpcf"
|
||||
local BEASTMASTER_AXE_UNIT = "npc_dota_beastmaster_axe"
|
||||
local THINK_INTERVAL = 0.03
|
||||
--- Близко к владельцу — считаем, что ось вернулась
|
||||
local RETURN_ARRIVE_DIST = 96
|
||||
--- Минимальный радиус «круга» урона вокруг оси (шире узкой линии)
|
||||
local MIN_DAMAGE_RADIUS = 140
|
||||
--- Множитель к projectile_width для зоны урона
|
||||
local DAMAGE_RADIUS_WIDTH_MULT = 2.25
|
||||
--- Слот W: слева/справа → дугой к цели (не прямиком) → перекрёстом назад; широкий круг урона; главная цель не промахивается.
|
||||
____exports.ability_luna_twin_glaives_custom = __TS__Class()
|
||||
local ability_luna_twin_glaives_custom = ____exports.ability_luna_twin_glaives_custom
|
||||
ability_luna_twin_glaives_custom.name = "ability_luna_twin_glaives_custom"
|
||||
ability_luna_twin_glaives_custom.____file_path = "scripts/vscripts/abilities/heroes/luna/ability_luna_twin_glaives_custom.lua"
|
||||
__TS__ClassExtends(ability_luna_twin_glaives_custom, BaseAbility)
|
||||
function ability_luna_twin_glaives_custom.prototype.Precache(self, context)
|
||||
PrecacheResource("particle", WILD_AXE_PARTICLE, context)
|
||||
end
|
||||
function ability_luna_twin_glaives_custom.prototype.GetCastRange(self, location, target)
|
||||
return self:GetSpecialValueFor("cast_range")
|
||||
end
|
||||
function ability_luna_twin_glaives_custom.prototype.OnSpellStart(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local caster = self:GetCaster()
|
||||
local cursor = self:GetCursorPosition()
|
||||
local unitUnderCursor = self:GetCursorTarget()
|
||||
local primaryIdx = unitUnderCursor and not unitUnderCursor:IsNull() and unitUnderCursor:IsAlive() and unitUnderCursor:GetTeamNumber() ~= caster:GetTeamNumber() and unitUnderCursor:entindex() or -1
|
||||
local origin = caster:GetAbsOrigin()
|
||||
local speed = self:GetSpecialValueFor("projectile_speed")
|
||||
local maxRange = self:GetSpecialValueFor("projectile_range")
|
||||
local width = self:GetSpecialValueFor("projectile_width")
|
||||
local spread = self:GetSpecialValueFor("axe_spread")
|
||||
local damage = self:GetSpecialValueFor("glaive_damage")
|
||||
local toTarget = cursor - origin
|
||||
local flat = Vector(toTarget.x, toTarget.y, 0)
|
||||
local len2d = flat:Length2D()
|
||||
local fwd = len2d < 16 and Vector(
|
||||
caster:GetForwardVector().x,
|
||||
caster:GetForwardVector().y,
|
||||
0
|
||||
):Normalized() or flat:Normalized()
|
||||
local perp = Vector(-fwd.y, fwd.x, 0)
|
||||
local lift = 72
|
||||
local left = Vector(origin.x + perp.x * spread, origin.y + perp.y * spread, origin.z + lift)
|
||||
local right = Vector(origin.x - perp.x * spread, origin.y - perp.y * spread, origin.z + lift)
|
||||
local mid = Vector((left.x + right.x) / 2, (left.y + right.y) / 2, (left.z + right.z) / 2)
|
||||
local toEnemy = cursor - mid
|
||||
local flatEn = Vector(toEnemy.x, toEnemy.y, 0)
|
||||
local distEn = flatEn:Length2D()
|
||||
local dirToEnemy = distEn < 8 and fwd or flatEn:Normalized()
|
||||
local travel = math.min(distEn, maxRange)
|
||||
local endPoint = Vector(
|
||||
mid.x + dirToEnemy.x * travel,
|
||||
mid.y + dirToEnemy.y * travel,
|
||||
mid.z + toEnemy.z / math.max(distEn, 1) * travel * 0.35
|
||||
)
|
||||
caster:AddNewModifier(caster, self, ____exports.modifier_luna_twin_glaives_flight.name, {
|
||||
s = speed,
|
||||
w = width,
|
||||
sp = spread,
|
||||
dmg = damage,
|
||||
lx = left.x,
|
||||
ly = left.y,
|
||||
lz = left.z,
|
||||
rx = right.x,
|
||||
ry = right.y,
|
||||
rz = right.z,
|
||||
ex = endPoint.x,
|
||||
ey = endPoint.y,
|
||||
ez = endPoint.z,
|
||||
te = primaryIdx
|
||||
})
|
||||
EmitSoundOn("Hero_Beastmaster.WildAxes", caster)
|
||||
end
|
||||
ability_luna_twin_glaives_custom = __TS__Decorate(
|
||||
ability_luna_twin_glaives_custom,
|
||||
ability_luna_twin_glaives_custom,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_luna_twin_glaives_custom"}
|
||||
)
|
||||
____exports.ability_luna_twin_glaives_custom = ability_luna_twin_glaives_custom
|
||||
____exports.modifier_luna_twin_glaives_flight = __TS__Class()
|
||||
local modifier_luna_twin_glaives_flight = ____exports.modifier_luna_twin_glaives_flight
|
||||
modifier_luna_twin_glaives_flight.name = "modifier_luna_twin_glaives_flight"
|
||||
modifier_luna_twin_glaives_flight.____file_path = "scripts/vscripts/abilities/heroes/luna/ability_luna_twin_glaives_custom.lua"
|
||||
__TS__ClassExtends(modifier_luna_twin_glaives_flight, BaseModifier)
|
||||
function modifier_luna_twin_glaives_flight.prototype.____constructor(self, ...)
|
||||
BaseModifier.prototype.____constructor(self, ...)
|
||||
self.outSegmentLen = 1
|
||||
self.outT = 0
|
||||
self.returning = false
|
||||
self.hitForward = __TS__New(Set)
|
||||
self.hitBackward = __TS__New(Set)
|
||||
self.primaryEntIndex = -1
|
||||
self.primaryOutboundEnsured = false
|
||||
self.finished = false
|
||||
end
|
||||
function modifier_luna_twin_glaives_flight.prototype.setupAxeDummy(self, unit, caster, ability)
|
||||
unit:AddNewModifier(caster, ability, "modifier_invulnerable", {})
|
||||
unit:AddNewModifier(caster, ability, "modifier_attack_immune", {})
|
||||
unit:AddNewModifier(caster, ability, "modifier_no_healthbar", {})
|
||||
unit:AddNewModifier(caster, ability, "modifier_phased", {})
|
||||
unit:SetDayTimeVisionRange(0)
|
||||
unit:SetNightTimeVisionRange(0)
|
||||
unit:SetAcquisitionRange(0)
|
||||
end
|
||||
function modifier_luna_twin_glaives_flight.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_luna_twin_glaives_flight.prototype.IsDebuff(self)
|
||||
return false
|
||||
end
|
||||
function modifier_luna_twin_glaives_flight.prototype.RemoveOnDeath(self)
|
||||
return true
|
||||
end
|
||||
function modifier_luna_twin_glaives_flight.prototype.OnCreated(self, params)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
if not ability or not parent or not parent:IsAlive() then
|
||||
self:Destroy()
|
||||
return
|
||||
end
|
||||
self.speed = params.s
|
||||
self.damageRadius = math.max(MIN_DAMAGE_RADIUS, params.w * DAMAGE_RADIUS_WIDTH_MULT)
|
||||
self.spread = params.sp
|
||||
self.damage = params.dmg
|
||||
self.startL = Vector(params.lx, params.ly, params.lz)
|
||||
self.startR = Vector(params.rx, params.ry, params.rz)
|
||||
self.posL = self.startL
|
||||
self.posR = self.startR
|
||||
self.endPoint = Vector(params.ex, params.ey, params.ez)
|
||||
self.primaryEntIndex = params.te
|
||||
self.controlL = self:bezierControlOutward(self.startL, self.endPoint, 1)
|
||||
self.controlR = self:bezierControlOutward(self.startR, self.endPoint, -1)
|
||||
local lenL = (self.endPoint - self.startL):Length()
|
||||
local lenR = (self.endPoint - self.startR):Length()
|
||||
self.outSegmentLen = math.max(48, (lenL + lenR) / 2 * 1.22)
|
||||
local team = parent:GetTeamNumber()
|
||||
self.hammerL = CreateUnitByName(
|
||||
BEASTMASTER_AXE_UNIT,
|
||||
self.startL,
|
||||
false,
|
||||
nil,
|
||||
nil,
|
||||
team
|
||||
)
|
||||
self.hammerR = CreateUnitByName(
|
||||
BEASTMASTER_AXE_UNIT,
|
||||
self.startR,
|
||||
false,
|
||||
nil,
|
||||
nil,
|
||||
team
|
||||
)
|
||||
if not self.hammerL or not self.hammerR then
|
||||
self:cleanup()
|
||||
self:Destroy()
|
||||
return
|
||||
end
|
||||
self.hammerL:AddEffects(EF_NODRAW)
|
||||
self.hammerR:AddEffects(EF_NODRAW)
|
||||
self:setupAxeDummy(self.hammerL, parent, ability)
|
||||
self:setupAxeDummy(self.hammerR, parent, ability)
|
||||
self.pfxL = ParticleManager:CreateParticle(WILD_AXE_PARTICLE, PATTACH_CUSTOMORIGIN, nil)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
self.pfxL,
|
||||
0,
|
||||
self.hammerL,
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
nil,
|
||||
self.hammerL:GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
self.pfxR = ParticleManager:CreateParticle(WILD_AXE_PARTICLE, PATTACH_CUSTOMORIGIN, nil)
|
||||
ParticleManager:SetParticleControlEnt(
|
||||
self.pfxR,
|
||||
0,
|
||||
self.hammerR,
|
||||
PATTACH_ABSORIGIN_FOLLOW,
|
||||
nil,
|
||||
self.hammerR:GetAbsOrigin(),
|
||||
true
|
||||
)
|
||||
self:StartIntervalThink(THINK_INTERVAL)
|
||||
end
|
||||
function modifier_luna_twin_glaives_flight.prototype.bezierControlOutward(self, start, ____end, sideSign)
|
||||
local dx = ____end.x - start.x
|
||||
local dy = ____end.y - start.y
|
||||
local len2d = math.sqrt(dx * dx + dy * dy)
|
||||
local midZ = (start.z + ____end.z) / 2
|
||||
local mid = Vector((start.x + ____end.x) / 2, (start.y + ____end.y) / 2, midZ)
|
||||
if len2d < 12 then
|
||||
return mid
|
||||
end
|
||||
local nx = -dy / len2d
|
||||
local ny = dx / len2d
|
||||
local bulge = math.min(len2d * 0.44, self.spread * 2.6)
|
||||
return Vector(mid.x + nx * bulge * sideSign, mid.y + ny * bulge * sideSign, mid.z)
|
||||
end
|
||||
function modifier_luna_twin_glaives_flight.prototype.bezierQuadratic(self, p0, p1, p2, t)
|
||||
local u = math.min(
|
||||
1,
|
||||
math.max(0, 1 - t)
|
||||
)
|
||||
local s = math.min(
|
||||
1,
|
||||
math.max(0, t)
|
||||
)
|
||||
return Vector(u * u * p0.x + 2 * u * s * p1.x + s * s * p2.x, u * u * p0.y + 2 * u * s * p1.y + s * s * p2.y, u * u * p0.z + 2 * u * s * p1.z + s * s * p2.z)
|
||||
end
|
||||
function modifier_luna_twin_glaives_flight.prototype.OnIntervalThink(self)
|
||||
if not IsServer() or self.finished then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
local ability = self:GetAbility()
|
||||
if not parent or not parent:IsAlive() or not ability or not self.hammerL or not self.hammerR then
|
||||
self:finish()
|
||||
return
|
||||
end
|
||||
local dt = THINK_INTERVAL
|
||||
if not self.returning then
|
||||
self.outT = self.outT + self.speed * dt / self.outSegmentLen
|
||||
if self.outT >= 1 then
|
||||
self.outT = 1
|
||||
self.posL = self.endPoint
|
||||
self.posR = self.endPoint
|
||||
self.returning = true
|
||||
else
|
||||
self.posL = self:bezierQuadratic(self.startL, self.controlL, self.endPoint, self.outT)
|
||||
self.posR = self:bezierQuadratic(self.startR, self.controlR, self.endPoint, self.outT)
|
||||
end
|
||||
self.hammerL:SetAbsOrigin(self.posL)
|
||||
self.hammerR:SetAbsOrigin(self.posR)
|
||||
self:damageAlongAxe(
|
||||
self.hammerL:GetAbsOrigin(),
|
||||
self.hitForward,
|
||||
parent,
|
||||
ability
|
||||
)
|
||||
self:damageAlongAxe(
|
||||
self.hammerR:GetAbsOrigin(),
|
||||
self.hitForward,
|
||||
parent,
|
||||
ability
|
||||
)
|
||||
if self.returning then
|
||||
self:ensurePrimaryOutboundHit(parent, ability)
|
||||
end
|
||||
return
|
||||
end
|
||||
local lift = 72
|
||||
local fwd2 = Vector(
|
||||
parent:GetForwardVector().x,
|
||||
parent:GetForwardVector().y,
|
||||
0
|
||||
)
|
||||
local len = fwd2:Length2D()
|
||||
local fwd = len > 0.01 and fwd2:Normalized() or Vector(1, 0, 0)
|
||||
local perp = Vector(-fwd.y, fwd.x, 0)
|
||||
local o = parent:GetAbsOrigin()
|
||||
local shoulderL = Vector(o.x + perp.x * self.spread, o.y + perp.y * self.spread, o.z + lift)
|
||||
local shoulderR = Vector(o.x - perp.x * self.spread, o.y - perp.y * self.spread, o.z + lift)
|
||||
self.posL = self:moveTowards(self.posL, shoulderR, self.speed, dt)
|
||||
self.posR = self:moveTowards(self.posR, shoulderL, self.speed, dt)
|
||||
self.hammerL:SetAbsOrigin(self.posL)
|
||||
self.hammerR:SetAbsOrigin(self.posR)
|
||||
self:damageAlongAxe(
|
||||
self.hammerL:GetAbsOrigin(),
|
||||
self.hitBackward,
|
||||
parent,
|
||||
ability
|
||||
)
|
||||
self:damageAlongAxe(
|
||||
self.hammerR:GetAbsOrigin(),
|
||||
self.hitBackward,
|
||||
parent,
|
||||
ability
|
||||
)
|
||||
local dL = (shoulderR - self.posL):Length2D()
|
||||
local dR = (shoulderL - self.posR):Length2D()
|
||||
if dL < RETURN_ARRIVE_DIST and dR < RETURN_ARRIVE_DIST then
|
||||
self:finish()
|
||||
end
|
||||
end
|
||||
function modifier_luna_twin_glaives_flight.prototype.ensurePrimaryOutboundHit(self, caster, ability)
|
||||
if self.primaryOutboundEnsured then
|
||||
return
|
||||
end
|
||||
local pe = self.primaryEntIndex
|
||||
if pe < 0 then
|
||||
return
|
||||
end
|
||||
local ent = EntIndexToHScript(self.primaryEntIndex)
|
||||
if not ent or ent:IsNull() or not ent:IsAlive() then
|
||||
self.primaryOutboundEnsured = true
|
||||
return
|
||||
end
|
||||
if ent:GetTeamNumber() == caster:GetTeamNumber() then
|
||||
self.primaryOutboundEnsured = true
|
||||
return
|
||||
end
|
||||
if self.hitForward:has(self.primaryEntIndex) then
|
||||
self.primaryOutboundEnsured = true
|
||||
return
|
||||
end
|
||||
ApplyDamage({
|
||||
victim = ent,
|
||||
attacker = caster,
|
||||
damage = self.damage + self:GetCaster():GetAverageTrueAttackDamage(ent) * 0.5,
|
||||
damage_type = DAMAGE_TYPE_PHYSICAL,
|
||||
damage_flags = DOTA_DAMAGE_FLAG_NO_SPELL_AMPLIFICATION,
|
||||
ability = ability
|
||||
})
|
||||
ApplyDamage({
|
||||
victim = ent,
|
||||
attacker = caster,
|
||||
damage = self.damage + self:GetCaster():GetAverageTrueAttackDamage(ent) * 0.5,
|
||||
damage_type = DAMAGE_TYPE_MAGICAL,
|
||||
damage_flags = DOTA_DAMAGE_FLAG_ATTACK_MODIFIER,
|
||||
ability = ability
|
||||
})
|
||||
applyTwinGlaiveFacetVuln(nil, caster, ent, ability)
|
||||
self.hitForward:add(self.primaryEntIndex)
|
||||
self.primaryOutboundEnsured = true
|
||||
end
|
||||
function modifier_luna_twin_glaives_flight.prototype.moveTowards(self, from, to, speed, dt)
|
||||
local delta = to - from
|
||||
local dist = delta:Length()
|
||||
if dist < 1 then
|
||||
return to
|
||||
end
|
||||
local step = math.min(speed * dt, dist)
|
||||
local dir = delta:Normalized()
|
||||
return from + dir * step
|
||||
end
|
||||
function modifier_luna_twin_glaives_flight.prototype.damageAlongAxe(self, pos, hitSet, caster, ability)
|
||||
local enemies = FindUnitsInRadius(
|
||||
caster:GetTeamNumber(),
|
||||
pos,
|
||||
nil,
|
||||
self.damageRadius,
|
||||
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
||||
DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC,
|
||||
DOTA_UNIT_TARGET_FLAG_NONE,
|
||||
FIND_ANY_ORDER,
|
||||
false
|
||||
)
|
||||
for ____, enemy in ipairs(enemies) do
|
||||
do
|
||||
if not enemy or enemy:IsNull() or not enemy:IsAlive() then
|
||||
goto __continue38
|
||||
end
|
||||
local idx = enemy:entindex()
|
||||
if hitSet:has(idx) then
|
||||
goto __continue38
|
||||
end
|
||||
hitSet:add(idx)
|
||||
ApplyDamage({
|
||||
victim = enemy,
|
||||
attacker = caster,
|
||||
damage = self.damage + self:GetCaster():GetAverageTrueAttackDamage(enemy) * 0.5,
|
||||
damage_type = DAMAGE_TYPE_PHYSICAL,
|
||||
damage_flags = DOTA_DAMAGE_FLAG_NO_SPELL_AMPLIFICATION,
|
||||
ability = ability
|
||||
})
|
||||
ApplyDamage({
|
||||
victim = enemy,
|
||||
attacker = caster,
|
||||
damage = self.damage + self:GetCaster():GetAverageTrueAttackDamage(enemy) * 0.5,
|
||||
damage_type = DAMAGE_TYPE_MAGICAL,
|
||||
damage_flags = DOTA_DAMAGE_FLAG_ATTACK_MODIFIER,
|
||||
ability = ability
|
||||
})
|
||||
applyTwinGlaiveFacetVuln(nil, caster, enemy, ability)
|
||||
end
|
||||
::__continue38::
|
||||
end
|
||||
end
|
||||
function modifier_luna_twin_glaives_flight.prototype.OnDestroy(self)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
self:cleanup()
|
||||
end
|
||||
function modifier_luna_twin_glaives_flight.prototype.finish(self)
|
||||
if self.finished then
|
||||
return
|
||||
end
|
||||
self.finished = true
|
||||
self:StartIntervalThink(-1)
|
||||
self:Destroy()
|
||||
end
|
||||
function modifier_luna_twin_glaives_flight.prototype.cleanup(self)
|
||||
if self.pfxL ~= nil then
|
||||
ParticleManager:DestroyParticle(self.pfxL, false)
|
||||
ParticleManager:ReleaseParticleIndex(self.pfxL)
|
||||
self.pfxL = nil
|
||||
end
|
||||
if self.pfxR ~= nil then
|
||||
ParticleManager:DestroyParticle(self.pfxR, false)
|
||||
ParticleManager:ReleaseParticleIndex(self.pfxR)
|
||||
self.pfxR = nil
|
||||
end
|
||||
if self.hammerL then
|
||||
UTIL_Remove(self.hammerL)
|
||||
self.hammerL = nil
|
||||
end
|
||||
if self.hammerR then
|
||||
UTIL_Remove(self.hammerR)
|
||||
self.hammerR = nil
|
||||
end
|
||||
end
|
||||
modifier_luna_twin_glaives_flight = __TS__Decorate(
|
||||
modifier_luna_twin_glaives_flight,
|
||||
modifier_luna_twin_glaives_flight,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_luna_twin_glaives_flight"}
|
||||
)
|
||||
____exports.modifier_luna_twin_glaives_flight = modifier_luna_twin_glaives_flight
|
||||
____exports.modifier_luna_twin_glaives_facet_vuln = __TS__Class()
|
||||
local modifier_luna_twin_glaives_facet_vuln = ____exports.modifier_luna_twin_glaives_facet_vuln
|
||||
modifier_luna_twin_glaives_facet_vuln.name = "modifier_luna_twin_glaives_facet_vuln"
|
||||
modifier_luna_twin_glaives_facet_vuln.____file_path = "scripts/vscripts/abilities/heroes/luna/ability_luna_twin_glaives_custom.lua"
|
||||
__TS__ClassExtends(modifier_luna_twin_glaives_facet_vuln, BaseModifier)
|
||||
function modifier_luna_twin_glaives_facet_vuln.prototype.IsHidden(self)
|
||||
return false
|
||||
end
|
||||
function modifier_luna_twin_glaives_facet_vuln.prototype.IsDebuff(self)
|
||||
return true
|
||||
end
|
||||
function modifier_luna_twin_glaives_facet_vuln.prototype.IsPurgable(self)
|
||||
return true
|
||||
end
|
||||
function modifier_luna_twin_glaives_facet_vuln.prototype.OnRefresh(self, params)
|
||||
local ____self_SetDuration_2 = self.SetDuration
|
||||
local ____opt_0 = self:GetAbility()
|
||||
____self_SetDuration_2(
|
||||
self,
|
||||
____opt_0 and ____opt_0:GetSpecialValueFor("twin_facet_vuln_duration"),
|
||||
true
|
||||
)
|
||||
self:IncrementStackCount()
|
||||
end
|
||||
function modifier_luna_twin_glaives_facet_vuln.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_INCOMING_DAMAGE_PERCENTAGE, MODIFIER_PROPERTY_INCOMING_PHYSICAL_DAMAGE_PERCENTAGE}
|
||||
end
|
||||
function modifier_luna_twin_glaives_facet_vuln.prototype.GetModifierIncomingDamage_Percentage(self, event)
|
||||
local ab = self:GetAbility()
|
||||
if not ab then
|
||||
return 0
|
||||
end
|
||||
local pct = ab:GetSpecialValueFor("twin_facet_pct")
|
||||
local value = self:GetStackCount() * (pct / 100)
|
||||
print(value)
|
||||
return self:GetStackCount() * pct
|
||||
end
|
||||
function modifier_luna_twin_glaives_facet_vuln.prototype.GetModifierIncomingPhysicalDamage_Percentage(self, event)
|
||||
local ab = self:GetAbility()
|
||||
if not ab then
|
||||
return 0
|
||||
end
|
||||
local pct = ab:GetSpecialValueFor("twin_facet_pct")
|
||||
local value = self:GetStackCount() * (pct / 100)
|
||||
print(value)
|
||||
return self:GetStackCount() * pct
|
||||
end
|
||||
function modifier_luna_twin_glaives_facet_vuln.prototype.GetTexture(self)
|
||||
return "luna_lunar_orbit"
|
||||
end
|
||||
modifier_luna_twin_glaives_facet_vuln = __TS__Decorate(
|
||||
modifier_luna_twin_glaives_facet_vuln,
|
||||
modifier_luna_twin_glaives_facet_vuln,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_luna_twin_glaives_facet_vuln"}
|
||||
)
|
||||
____exports.modifier_luna_twin_glaives_facet_vuln = modifier_luna_twin_glaives_facet_vuln
|
||||
return ____exports
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user