initial commit
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
|
||||
local ____exports = {}
|
||||
local DEFAULT_TINT = 255
|
||||
local TINT_EPS = 1.5
|
||||
local function isAlreadyRenderTinted(self, unit)
|
||||
local u = unit
|
||||
if u.GetRenderColor == nil then
|
||||
return false
|
||||
end
|
||||
local c = u:GetRenderColor()
|
||||
if c == nil then
|
||||
return false
|
||||
end
|
||||
local dr = DEFAULT_TINT
|
||||
return math.abs(c.x - dr) > TINT_EPS or math.abs(c.y - dr) > TINT_EPS or math.abs(c.z - dr) > TINT_EPS
|
||||
end
|
||||
---
|
||||
-- @returns true если цвет реально выставили
|
||||
function ____exports.trySetIntrinsicCreepRenderColor(self, unit, r, g, b)
|
||||
if not IsServer() or not IsValidEntity(unit) then
|
||||
return false
|
||||
end
|
||||
if isAlreadyRenderTinted(nil, unit) then
|
||||
return false
|
||||
end
|
||||
unit:SetRenderColor(r, g, b)
|
||||
return true
|
||||
end
|
||||
return ____exports
|
||||
@@ -0,0 +1,68 @@
|
||||
--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
|
||||
local ____exports = {}
|
||||
local CRIT_MULT_KEY_PREFIX
|
||||
require("lib.dota_ts_adapter")
|
||||
function ____exports.getCritMult(self, hero)
|
||||
if not hero or not IsValidEntity(hero) then
|
||||
return 100
|
||||
end
|
||||
local playerId = hero:GetPlayerOwnerID()
|
||||
if playerId == nil or playerId == nil or playerId < 0 then
|
||||
return 100
|
||||
end
|
||||
local key = CRIT_MULT_KEY_PREFIX .. tostring(playerId)
|
||||
local data = CustomNetTables:GetTableValue("custom_stats", key)
|
||||
local ____data_0
|
||||
if data then
|
||||
____data_0 = data.value
|
||||
else
|
||||
____data_0 = 100
|
||||
end
|
||||
return ____data_0
|
||||
end
|
||||
CRIT_MULT_KEY_PREFIX = "crit_mult_"
|
||||
function ____exports.setCritMult(self, hero, critMult)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local playerId = hero:GetPlayerOwnerID()
|
||||
if playerId == nil or playerId == nil or playerId < 0 then
|
||||
return
|
||||
end
|
||||
local key = CRIT_MULT_KEY_PREFIX .. tostring(playerId)
|
||||
CustomNetTables:SetTableValue("custom_stats", key, {value = critMult})
|
||||
end
|
||||
function ____exports.addCritMult(self, hero, delta)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local current = ____exports.getCritMult(nil, hero)
|
||||
____exports.setCritMult(nil, hero, current + delta)
|
||||
end
|
||||
function ____exports.reduceCritMult(self, hero, delta)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
____exports.addCritMult(
|
||||
nil,
|
||||
hero,
|
||||
-math.abs(delta)
|
||||
)
|
||||
end
|
||||
--- Итоговый множитель урона крита (физика и магия): sum(mult/100 из источников) × (крит_мульт героя / 100).
|
||||
--
|
||||
-- @param stackedCritMultSum сумма mult/100 по сработавшим источникам (например 1.75 + 1.6)
|
||||
function ____exports.getFinalStackingCritMultiplier(self, unit, stackedCritMultSum)
|
||||
if stackedCritMultSum <= 0 then
|
||||
return 0
|
||||
end
|
||||
local critMultStat = unit:IsHero() and ____exports.getCritMult(nil, unit) or 100
|
||||
return stackedCritMultSum * (critMultStat / 100)
|
||||
end
|
||||
local g = _G
|
||||
g.setCritMult = ____exports.setCritMult
|
||||
g.addCritMult = ____exports.addCritMult
|
||||
g.reduceCritMult = ____exports.reduceCritMult
|
||||
g.getCritMult = ____exports.getCritMult
|
||||
g.getFinalStackingCritMultiplier = ____exports.getFinalStackingCritMultiplier
|
||||
return ____exports
|
||||
@@ -0,0 +1,63 @@
|
||||
--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
|
||||
local ____exports = {}
|
||||
--- Привязка entity_hurt к игроку для статистики урона.
|
||||
-- GetRawPlayerDamage в кастомке часто пустой; BP раньше смотрел только IsRealHero + GetOwner — терялся урон с иллюзий/призывов.
|
||||
local function resolvePlayerIdFromUnitChain(self, start)
|
||||
if not start or not IsValidEntity(start) then
|
||||
return nil
|
||||
end
|
||||
local current = start
|
||||
do
|
||||
local depth = 0
|
||||
while depth < 8 and current and IsValidEntity(current) do
|
||||
local pid = current:GetPlayerOwnerID()
|
||||
if pid ~= nil and pid >= 0 then
|
||||
return pid
|
||||
end
|
||||
local anyC = current
|
||||
local owner = type(anyC.GetOwnerEntity) == "function" and anyC:GetOwnerEntity() or type(anyC.GetOwner) == "function" and anyC:GetOwner()
|
||||
if not owner or not IsValidEntity(owner) then
|
||||
break
|
||||
end
|
||||
current = owner
|
||||
depth = depth + 1
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
--- Атакующий юнит (меч, иллюзия, призванное).
|
||||
function ____exports.resolvePlayerIdFromDamageAttacker(self, attacker)
|
||||
return resolvePlayerIdFromUnitChain(nil, attacker)
|
||||
end
|
||||
--- Источник нанесённого урона из события: сначала attacker, если пусто/без владельца — inflictor (способность/предмет) → кастер.
|
||||
function ____exports.resolvePlayerIdFromEntityHurtForOutgoing(self, event)
|
||||
local aIdx = event.entindex_attacker
|
||||
if aIdx ~= nil and aIdx ~= nil and tonumber(tostring(aIdx)) > 0 then
|
||||
local ent = EntIndexToHScript(aIdx)
|
||||
if ent and IsValidEntity(ent) then
|
||||
local p = ____exports.resolvePlayerIdFromDamageAttacker(nil, ent)
|
||||
if p ~= nil and p >= 0 then
|
||||
return p
|
||||
end
|
||||
end
|
||||
end
|
||||
local iIdx = event.entindex_inflictor
|
||||
if iIdx ~= nil and iIdx ~= nil and tonumber(tostring(iIdx)) > 0 then
|
||||
local inf = EntIndexToHScript(iIdx)
|
||||
if not inf or not IsValidEntity(inf) then
|
||||
return nil
|
||||
end
|
||||
local caster
|
||||
if type(inf.GetCaster) == "function" then
|
||||
caster = inf:GetCaster()
|
||||
end
|
||||
if (not caster or not IsValidEntity(caster)) and type(inf.GetOwner) == "function" then
|
||||
caster = inf:GetOwner()
|
||||
end
|
||||
if caster and IsValidEntity(caster) then
|
||||
return ____exports.resolvePlayerIdFromDamageAttacker(nil, caster)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
return ____exports
|
||||
@@ -0,0 +1,23 @@
|
||||
--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
|
||||
local ____exports = {}
|
||||
--- Tools / `-cheats`: `GameRules.IsCheatMode()` или `sv_cheats 1`.
|
||||
function ____exports.isDevCheatSession(self)
|
||||
if GameRules:IsCheatMode() then
|
||||
return true
|
||||
end
|
||||
do
|
||||
local function ____catch(_error)
|
||||
return true, false
|
||||
end
|
||||
local ____try, ____hasReturned, ____returnValue = pcall(function()
|
||||
return true, Convars:GetBool("sv_cheats") == true
|
||||
end)
|
||||
if not ____try then
|
||||
____hasReturned, ____returnValue = ____catch(____hasReturned)
|
||||
end
|
||||
if ____hasReturned then
|
||||
return ____returnValue
|
||||
end
|
||||
end
|
||||
end
|
||||
return ____exports
|
||||
@@ -0,0 +1,44 @@
|
||||
--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
|
||||
local ____exports = {}
|
||||
--- Аналог Entities.FindAllByClassnameWithin без VectorWS: обход через FindAllByClassname + дистанция.
|
||||
function ____exports.findAllByClassnameInRadius(classname, center, radius)
|
||||
local all = Entities:FindAllByClassname(classname)
|
||||
local result = {}
|
||||
local r2 = radius * radius
|
||||
for ____, ent in ipairs(all) do
|
||||
do
|
||||
if not ent or ent:IsNull() then
|
||||
goto __continue3
|
||||
end
|
||||
local pos = ent:GetAbsOrigin()
|
||||
local dx = pos.x - center.x
|
||||
local dy = pos.y - center.y
|
||||
if dx * dx + dy * dy <= r2 then
|
||||
result[#result + 1] = ent
|
||||
end
|
||||
end
|
||||
::__continue3::
|
||||
end
|
||||
return result
|
||||
end
|
||||
--- Аналог Entities.FindByClassnameNearest: ближайшая сущность класса в пределах maxRadius.
|
||||
function ____exports.findNearestByClassname(classname, origin, maxRadius)
|
||||
local all = Entities:FindAllByClassname(classname)
|
||||
local best
|
||||
local bestDist = maxRadius + 1
|
||||
for ____, ent in ipairs(all) do
|
||||
do
|
||||
if not ent or ent:IsNull() then
|
||||
goto __continue8
|
||||
end
|
||||
local d = (origin - ent:GetAbsOrigin()):Length2D()
|
||||
if d <= maxRadius and d < bestDist then
|
||||
bestDist = d
|
||||
best = ent
|
||||
end
|
||||
end
|
||||
::__continue8::
|
||||
end
|
||||
return best
|
||||
end
|
||||
return ____exports
|
||||
@@ -0,0 +1,17 @@
|
||||
--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
|
||||
local ____exports = {}
|
||||
--- Поиск npc_homer без FindUnitsInRadius на весь мир (тяжёлый запрос при каждом тике карт 55/56).
|
||||
function ____exports.findHomerNpc(self)
|
||||
local byName = Entities:FindByName(nil, "npc_homer")
|
||||
if byName and IsValidEntity(byName) and not byName:IsNull() and byName:GetUnitName() == "npc_homer" then
|
||||
return byName
|
||||
end
|
||||
local allUnits = Entities:FindAllByClassname("npc_dota_creature")
|
||||
for ____, unit in ipairs(allUnits) do
|
||||
if unit and IsValidEntity(unit) and not unit:IsNull() and unit:GetUnitName() == "npc_homer" and unit:IsAlive() then
|
||||
return unit
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
return ____exports
|
||||
@@ -0,0 +1,30 @@
|
||||
--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
|
||||
local ____exports = {}
|
||||
--- Как в defension (lib/utils.ts + lib/Filters.ts): от юнита к «настоящему» герою игрока для учёта урона.
|
||||
function ____exports.getTrueHeroFromEntity(self, ent)
|
||||
if not ent then
|
||||
return nil
|
||||
end
|
||||
if ent:IsBaseNPC() then
|
||||
local npc = ent
|
||||
if npc:IsRealHero() then
|
||||
return npc
|
||||
end
|
||||
local ownerEnt = npc:GetOwner()
|
||||
if ownerEnt and ownerEnt:IsBaseNPC() then
|
||||
local owner = ownerEnt
|
||||
if owner:IsRealHero() then
|
||||
return owner
|
||||
end
|
||||
end
|
||||
local player = npc:GetPlayerOwner()
|
||||
if player ~= nil then
|
||||
local hero = player:GetAssignedHero()
|
||||
if hero and hero:IsRealHero() then
|
||||
return hero
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
return ____exports
|
||||
@@ -0,0 +1,40 @@
|
||||
--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
|
||||
local ____exports = {}
|
||||
local ____battle_pass_server = require("battle_pass_server")
|
||||
local BattlePassServer = ____battle_pass_server.BattlePassServer
|
||||
--- Лечит юнита и учитывает лечение союзников для Battle Pass.
|
||||
-- Вызывайте вместо HealWithParams когда healer — герой игрока, а target — союзник (не сам healer).
|
||||
function ____exports.HealWithBattlePass(self, target, amount, ability, healer, showOverhead)
|
||||
if showOverhead == nil then
|
||||
showOverhead = true
|
||||
end
|
||||
target:HealWithParams(
|
||||
amount,
|
||||
ability,
|
||||
false,
|
||||
showOverhead,
|
||||
healer,
|
||||
false
|
||||
)
|
||||
local ____temp_2 = amount > 0 and healer
|
||||
if ____temp_2 then
|
||||
local ____this_1
|
||||
____this_1 = healer
|
||||
local ____opt_0 = ____this_1.IsRealHero
|
||||
if ____opt_0 ~= nil then
|
||||
____opt_0 = ____opt_0(____this_1)
|
||||
end
|
||||
____temp_2 = ____opt_0
|
||||
end
|
||||
if ____temp_2 and target ~= healer then
|
||||
do
|
||||
pcall(function()
|
||||
local playerId = healer:GetPlayerOwnerID()
|
||||
if playerId >= 0 then
|
||||
BattlePassServer:getInstance():onHealAlly(playerId, amount)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
return ____exports
|
||||
@@ -0,0 +1,162 @@
|
||||
local ____lualib = require("lualib_bundle")
|
||||
local __TS__NumberIsFinite = ____lualib.__TS__NumberIsFinite
|
||||
local __TS__ObjectKeys = ____lualib.__TS__ObjectKeys
|
||||
local __TS__ObjectValues = ____lualib.__TS__ObjectValues
|
||||
local __TS__Delete = ____lualib.__TS__Delete
|
||||
local __TS__Class = ____lualib.__TS__Class
|
||||
local __TS__ClassExtends = ____lualib.__TS__ClassExtends
|
||||
local __TS__Decorate = ____lualib.__TS__Decorate
|
||||
local ____exports = {}
|
||||
local ____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
|
||||
--- Верхний предел после убывания (синхрон с `src/panorama/arsenal.ts`).
|
||||
____exports.MAX_EFFECTIVE_INCOMING_REDUCTION_FROM_LINEAR_SUM_PCT = 92
|
||||
--- «Бумажная» сумма % снижения входящего → эффективный % в бою (убывающая отдача).
|
||||
--
|
||||
-- @param linearSumPositivePct сумма вкладов из KV/арсенала (+20+20+…)
|
||||
function ____exports.resolveIncomingDamageReductionPctFromLinearSum(self, linearSumPositivePct)
|
||||
if not __TS__NumberIsFinite(linearSumPositivePct) or linearSumPositivePct <= 0 then
|
||||
return 0
|
||||
end
|
||||
local s = linearSumPositivePct
|
||||
local hyperbolic = 100 * s / (100 + s)
|
||||
return math.min(____exports.MAX_EFFECTIVE_INCOMING_REDUCTION_FROM_LINEAR_SUM_PCT, hyperbolic)
|
||||
end
|
||||
--- Значение для MODIFIER_PROPERTY_INCOMING_DAMAGE_PERCENTAGE (отрицательное = меньше урона).
|
||||
function ____exports.incomingDamageReductionModifierValue(self, linearSumPositivePct)
|
||||
local effective = ____exports.resolveIncomingDamageReductionPctFromLinearSum(nil, linearSumPositivePct)
|
||||
return effective > 0 and -effective or 0
|
||||
end
|
||||
____exports.MODIFIER_INCOMING_DAMAGE_REDUCTION = "modifier_incoming_damage_reduction"
|
||||
local STATS_MULTIPLIER_INCOMING_PREFIX = "stats_multiplier:"
|
||||
local function getStorage(self, unit)
|
||||
local holder = unit
|
||||
if not holder.__incomingDamageReductionStorage then
|
||||
holder.__incomingDamageReductionStorage = {sources = {}, eventSources = {}}
|
||||
end
|
||||
return holder.__incomingDamageReductionStorage
|
||||
end
|
||||
local function hasAnySources(self, unit)
|
||||
local storage = getStorage(nil, unit)
|
||||
return #__TS__ObjectKeys(storage.sources) > 0 or #__TS__ObjectKeys(storage.eventSources) > 0
|
||||
end
|
||||
local function resolveSourceAbility(self, unit)
|
||||
return unit:FindAbilityByName("ability_incoming_damage_reduction") or unit:FindAbilityByName("ability_stats_multiplier") or unit:FindAbilityByName("ability_stacking_crit") or nil
|
||||
end
|
||||
local function ensureCombineModifier(self, unit)
|
||||
if unit:FindModifierByName(____exports.MODIFIER_INCOMING_DAMAGE_REDUCTION) then
|
||||
return
|
||||
end
|
||||
local sourceAbility = resolveSourceAbility(nil, unit)
|
||||
unit:AddNewModifier(unit, sourceAbility, ____exports.MODIFIER_INCOMING_DAMAGE_REDUCTION, {})
|
||||
end
|
||||
local function removeCombineModifierIfUnused(self, unit)
|
||||
if hasAnySources(nil, unit) then
|
||||
return
|
||||
end
|
||||
local mod = unit:FindModifierByName(____exports.MODIFIER_INCOMING_DAMAGE_REDUCTION)
|
||||
if mod then
|
||||
mod:Destroy()
|
||||
end
|
||||
end
|
||||
--- Сумма «бумажных» % снижения (до формулы убывания).
|
||||
function ____exports.computeIncomingDamageReductionLinearSum(self, unit, event)
|
||||
local storage = getStorage(nil, unit)
|
||||
local sum = 0
|
||||
for ____, entry in ipairs(__TS__ObjectValues(storage.sources)) do
|
||||
local value = entry:resolver()
|
||||
if __TS__NumberIsFinite(value) and value > 0 then
|
||||
sum = sum + value
|
||||
end
|
||||
end
|
||||
if event then
|
||||
for ____, entry in ipairs(__TS__ObjectValues(storage.eventSources)) do
|
||||
local value = entry:resolver(event)
|
||||
if __TS__NumberIsFinite(value) and value > 0 then
|
||||
sum = sum + value
|
||||
end
|
||||
end
|
||||
end
|
||||
return sum
|
||||
end
|
||||
--- Статический/динамический источник: resolver возвращает положительный % снижения.
|
||||
function ____exports.setIncomingDamageReductionSource(self, unit, sourceId, resolver)
|
||||
if not unit or not IsValidEntity(unit) then
|
||||
return
|
||||
end
|
||||
getStorage(nil, unit).sources[sourceId] = {resolver = resolver}
|
||||
ensureCombineModifier(nil, unit)
|
||||
end
|
||||
--- Источник, зависящий от удара (угол, золото, мана и т.д.).
|
||||
function ____exports.setIncomingDamageReductionEventSource(self, unit, sourceId, resolver)
|
||||
if not unit or not IsValidEntity(unit) then
|
||||
return
|
||||
end
|
||||
getStorage(nil, unit).eventSources[sourceId] = {resolver = resolver}
|
||||
ensureCombineModifier(nil, unit)
|
||||
end
|
||||
function ____exports.removeIncomingDamageReductionSource(self, unit, sourceId)
|
||||
if not unit or not IsValidEntity(unit) then
|
||||
return
|
||||
end
|
||||
local storage = getStorage(nil, unit)
|
||||
__TS__Delete(storage.sources, sourceId)
|
||||
__TS__Delete(storage.eventSources, sourceId)
|
||||
removeCombineModifierIfUnused(nil, unit)
|
||||
end
|
||||
function ____exports.setStatsMultiplierIncomingDamageReductionSource(self, hero, sourceId, resolver)
|
||||
____exports.setIncomingDamageReductionSource(nil, hero, STATS_MULTIPLIER_INCOMING_PREFIX .. sourceId, resolver)
|
||||
end
|
||||
function ____exports.removeStatsMultiplierIncomingDamageReductionSource(self, hero, sourceId)
|
||||
____exports.removeIncomingDamageReductionSource(nil, hero, STATS_MULTIPLIER_INCOMING_PREFIX .. sourceId)
|
||||
end
|
||||
local ability_incoming_damage_reduction = __TS__Class()
|
||||
ability_incoming_damage_reduction.name = "ability_incoming_damage_reduction"
|
||||
ability_incoming_damage_reduction.____file_path = "scripts/vscripts/utils/incoming_damage_reduction_combine.lua"
|
||||
__TS__ClassExtends(ability_incoming_damage_reduction, BaseAbility)
|
||||
function ability_incoming_damage_reduction.prototype.GetIntrinsicModifierName(self)
|
||||
return ____exports.MODIFIER_INCOMING_DAMAGE_REDUCTION
|
||||
end
|
||||
function ability_incoming_damage_reduction.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
ability_incoming_damage_reduction = __TS__Decorate(
|
||||
ability_incoming_damage_reduction,
|
||||
ability_incoming_damage_reduction,
|
||||
{registerAbility(nil)},
|
||||
{kind = "class", name = "ability_incoming_damage_reduction"}
|
||||
)
|
||||
local modifier_incoming_damage_reduction = __TS__Class()
|
||||
modifier_incoming_damage_reduction.name = "modifier_incoming_damage_reduction"
|
||||
modifier_incoming_damage_reduction.____file_path = "scripts/vscripts/utils/incoming_damage_reduction_combine.lua"
|
||||
__TS__ClassExtends(modifier_incoming_damage_reduction, BaseModifier)
|
||||
function modifier_incoming_damage_reduction.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_incoming_damage_reduction.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_incoming_damage_reduction.prototype.RemoveOnDeath(self)
|
||||
return false
|
||||
end
|
||||
function modifier_incoming_damage_reduction.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_PROPERTY_INCOMING_DAMAGE_PERCENTAGE}
|
||||
end
|
||||
function modifier_incoming_damage_reduction.prototype.GetModifierIncomingDamage_Percentage(self, event)
|
||||
local unit = self:GetParent()
|
||||
if not unit or not IsValidEntity(unit) then
|
||||
return 0
|
||||
end
|
||||
local linearSum = ____exports.computeIncomingDamageReductionLinearSum(nil, unit, event)
|
||||
return ____exports.incomingDamageReductionModifierValue(nil, linearSum)
|
||||
end
|
||||
modifier_incoming_damage_reduction = __TS__Decorate(
|
||||
modifier_incoming_damage_reduction,
|
||||
modifier_incoming_damage_reduction,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_incoming_damage_reduction"}
|
||||
)
|
||||
return ____exports
|
||||
@@ -0,0 +1,48 @@
|
||||
--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
|
||||
local ____exports = {}
|
||||
--- Серверный спавн карты `light_fix` в центре мира по X/Y, якорь Z = LIGHT_FIX_SPAWN_Z под землёй.
|
||||
-- Один экземпляр на матч: повторные вызовы снимают предыдущий spawn group и спавнят снова.
|
||||
local function ceilMapNumber(____, value)
|
||||
return value + (64 - value % 64) % 64
|
||||
end
|
||||
--- Опциональная точка на карте маппера (если есть — X/Y, со снапом по сетке; Z задаётся отдельно).
|
||||
local LIGHT_FIX_WORLD_ENTITY = "point_light_fix_world_center"
|
||||
--- Высота якоря под картой: не видно игрокам, логика освещения может подтягиваться.
|
||||
local LIGHT_FIX_SPAWN_Z = -1000
|
||||
local lightFixSpawnGroupHandle
|
||||
--- Центр мира по границам карты + снап 64 по X/Y; Z под землёй.
|
||||
local function getLightFixSpawnOrigin(self)
|
||||
local named = Entities:FindByName(nil, LIGHT_FIX_WORLD_ENTITY)
|
||||
if named and named.GetAbsOrigin then
|
||||
local o = named:GetAbsOrigin()
|
||||
return Vector(
|
||||
ceilMapNumber(nil, o.x),
|
||||
ceilMapNumber(nil, o.y),
|
||||
LIGHT_FIX_SPAWN_Z
|
||||
)
|
||||
end
|
||||
local cx = (GetWorldMinX() + GetWorldMaxX()) * 0.5
|
||||
local cy = (GetWorldMinY() + GetWorldMaxY()) * 0.5
|
||||
local x = ceilMapNumber(nil, cx)
|
||||
local y = ceilMapNumber(nil, cy)
|
||||
return Vector(x, y, LIGHT_FIX_SPAWN_Z)
|
||||
end
|
||||
--- Спавнит `light_fix` в центре мира (коллбеки пустые — полный цикл загрузки).
|
||||
-- `playerId` оставлен в сигнатуре для вызывающего кода; позиция не зависит от игрока.
|
||||
function ____exports.spawnLightFixForPlayer(self, _playerId)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local origin = getLightFixSpawnOrigin(nil)
|
||||
lightFixSpawnGroupHandle = DOTA_SpawnMapAtPosition(
|
||||
"light_fix",
|
||||
origin,
|
||||
false,
|
||||
function()
|
||||
end,
|
||||
function()
|
||||
end,
|
||||
nil
|
||||
)
|
||||
end
|
||||
return ____exports
|
||||
@@ -0,0 +1,119 @@
|
||||
local ____lualib = require("lualib_bundle")
|
||||
local __TS__NumberIsFinite = ____lualib.__TS__NumberIsFinite
|
||||
local ____exports = {}
|
||||
require("lib.dota_ts_adapter")
|
||||
--- Удача только из NetTable (без бонуса арсенала) — для setLuck/addLuck.
|
||||
local function getLuckBaseFromTable(self, hero)
|
||||
if not hero or not IsValidEntity(hero) then
|
||||
return 0
|
||||
end
|
||||
local playerId = hero:GetPlayerOwnerID()
|
||||
if playerId == nil or playerId == nil or playerId < 0 then
|
||||
return 0
|
||||
end
|
||||
local key = "luck_" .. tostring(playerId)
|
||||
local data = CustomNetTables:GetTableValue("custom_stats", key)
|
||||
local ____data_0
|
||||
if data then
|
||||
____data_0 = data.value
|
||||
else
|
||||
____data_0 = 0
|
||||
end
|
||||
return ____data_0
|
||||
end
|
||||
function ____exports.setLuck(self, hero, luck)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local playerId = hero:GetPlayerOwnerID()
|
||||
if playerId == nil or playerId == nil or playerId < 0 then
|
||||
return
|
||||
end
|
||||
local key = "luck_" .. tostring(playerId)
|
||||
CustomNetTables:SetTableValue("custom_stats", key, {value = luck})
|
||||
end
|
||||
function ____exports.addLuck(self, hero, delta)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local current = getLuckBaseFromTable(nil, hero)
|
||||
____exports.setLuck(nil, hero, current + delta)
|
||||
end
|
||||
function ____exports.reduceLuck(self, hero, delta)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
____exports.addLuck(
|
||||
nil,
|
||||
hero,
|
||||
-math.abs(delta)
|
||||
)
|
||||
end
|
||||
function ____exports.getLuck(self, hero)
|
||||
if not hero or not IsValidEntity(hero) then
|
||||
return 0
|
||||
end
|
||||
local playerId = hero:GetPlayerOwnerID()
|
||||
if playerId == nil or playerId == nil or playerId < 0 then
|
||||
return 0
|
||||
end
|
||||
return getLuckBaseFromTable(nil, hero)
|
||||
end
|
||||
--- По умолчанию удача не может поднять шанс выше этого множителя от базы (15% → макс. 30%).
|
||||
local DEFAULT_MAX_CHANCE_VS_BASE = 2
|
||||
--- Рассчитывает шанс с учетом удачи героя и базового значения
|
||||
--
|
||||
-- @param hero - герой для которого рассчитывается шанс
|
||||
-- @param baseValue - базовое значение шанса (0-1)
|
||||
-- @param luckMultiplier - аддитивный бонус за 1 удачу (в долях, по умолчанию 0.01 = +1 п.п.)
|
||||
-- @param maxChanceVsBaseMult - потолок: итог не выше baseValue * этого числа (по умолчанию 2). Для baseValue <= 0 потолок не применяется.
|
||||
-- @returns итоговый шанс (0-1)
|
||||
function ____exports.calculateLuckChance(self, hero, baseValue, luckMultiplier, maxChanceVsBaseMult)
|
||||
if luckMultiplier == nil then
|
||||
luckMultiplier = 0.01
|
||||
end
|
||||
if maxChanceVsBaseMult == nil then
|
||||
maxChanceVsBaseMult = DEFAULT_MAX_CHANCE_VS_BASE
|
||||
end
|
||||
local luck = ____exports.getLuck(nil, hero)
|
||||
local finalChance = baseValue + luck * luckMultiplier
|
||||
if baseValue > 0 and __TS__NumberIsFinite(maxChanceVsBaseMult) and maxChanceVsBaseMult > 0 and maxChanceVsBaseMult < 1000000000 then
|
||||
local ceiling = baseValue * maxChanceVsBaseMult
|
||||
finalChance = math.min(finalChance, ceiling)
|
||||
end
|
||||
return math.max(
|
||||
0,
|
||||
math.min(1, finalChance)
|
||||
)
|
||||
end
|
||||
--- Проверяет, сработал ли шанс с учетом удачи
|
||||
--
|
||||
-- @param hero - герой для которого проверяется шанс
|
||||
-- @param baseValue - базовое значение шанса (0-1)
|
||||
-- @param luckMultiplier - аддитивный бонус за 1 удачу (по умолчанию 0.01)
|
||||
-- @param maxChanceVsBaseMult - см. calculateLuckChance (по умолчанию ×2 от базы)
|
||||
-- @returns true если шанс сработал
|
||||
function ____exports.rollLuckChance(self, hero, baseValue, luckMultiplier, maxChanceVsBaseMult)
|
||||
if luckMultiplier == nil then
|
||||
luckMultiplier = 0.01
|
||||
end
|
||||
if maxChanceVsBaseMult == nil then
|
||||
maxChanceVsBaseMult = DEFAULT_MAX_CHANCE_VS_BASE
|
||||
end
|
||||
local chance = ____exports.calculateLuckChance(
|
||||
nil,
|
||||
hero,
|
||||
baseValue,
|
||||
luckMultiplier,
|
||||
maxChanceVsBaseMult
|
||||
)
|
||||
return math.random() < chance
|
||||
end
|
||||
local g = _G
|
||||
g.setLuck = ____exports.setLuck
|
||||
g.addLuck = ____exports.addLuck
|
||||
g.reduceLuck = ____exports.reduceLuck
|
||||
g.getLuck = ____exports.getLuck
|
||||
g.calculateLuckChance = ____exports.calculateLuckChance
|
||||
g.rollLuckChance = ____exports.rollLuckChance
|
||||
return ____exports
|
||||
@@ -0,0 +1,28 @@
|
||||
--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
|
||||
local ____exports = {}
|
||||
--- Значения GetConnectionState(playerId) (сервер) / DOTA_CONNECTION_STATE.
|
||||
-- UNKNOWN 0, NOT_YET_CONNECTED 1, CONNECTED 2, DISCONNECTED 3, ABANDONED 4, LOADING 5, FAILED 6
|
||||
____exports.DOTA_CONNECTION_STATE = {
|
||||
UNKNOWN = 0,
|
||||
NOT_YET_CONNECTED = 1,
|
||||
CONNECTED = 2,
|
||||
DISCONNECTED = 3,
|
||||
ABANDONED = 4,
|
||||
LOADING = 5,
|
||||
FAILED = 6
|
||||
}
|
||||
--- Ещё в сессии / может вернуться (в т.ч. DISCONNECTED). Лив = ABANDONED; FAILED = сессия мёртва.
|
||||
function ____exports.isConnectionStateEffectivelyInGame(self, cs)
|
||||
return cs ~= ____exports.DOTA_CONNECTION_STATE.ABANDONED and cs ~= ____exports.DOTA_CONNECTION_STATE.FAILED
|
||||
end
|
||||
--- Игрок на связи с сервером матча (heartbeat, таймер лива). DISCONNECTED = уже вышел.
|
||||
function ____exports.isConnectionStateActivelyConnected(self, cs)
|
||||
return cs == ____exports.DOTA_CONNECTION_STATE.CONNECTED
|
||||
end
|
||||
function ____exports.isConnectionStateAbandoned(self, cs)
|
||||
return cs == ____exports.DOTA_CONNECTION_STATE.ABANDONED
|
||||
end
|
||||
function ____exports.isConnectionStateDropped(self, cs)
|
||||
return cs == ____exports.DOTA_CONNECTION_STATE.ABANDONED or cs == ____exports.DOTA_CONNECTION_STATE.FAILED
|
||||
end
|
||||
return ____exports
|
||||
@@ -0,0 +1,56 @@
|
||||
local ____lualib = require("lualib_bundle")
|
||||
local __TS__ArraySort = ____lualib.__TS__ArraySort
|
||||
local ____exports = {}
|
||||
--- Прекеш юнитов по имени (PrecacheUnitByNameAsync) из объединённого npc_units_custom.
|
||||
-- Вызывать только из GameMode.Precache — иначе движок ругается: «PrecacheResource must be passed a valid precache context».
|
||||
-- Волны/spawn/event/quest/summons — через #base в npc_units_custom.txt. Герои из npc_heroes_custom сюда не входят.
|
||||
local LOG_PFX = "[precache_all_npc_units]"
|
||||
--- Единый источник, как у precache_models_from_kv: движок подмешивает все #base.
|
||||
local NPC_UNITS_CUSTOM_KV = "scripts/npc/npc_units_custom.txt"
|
||||
--- Таблица определений юнитов: обычно kv["DOTAUnits"], но LoadKeyValues в части сборок
|
||||
-- отдаёт те же пары имя→блок сразу в корне (см. admin_menu: перебор верхнего уровня kv).
|
||||
local function getUnitDefinitionsRoot(self, kv)
|
||||
local wrapped = kv.DOTAUnits
|
||||
if wrapped ~= nil and type(wrapped) == "table" then
|
||||
return wrapped
|
||||
end
|
||||
return kv
|
||||
end
|
||||
local function collectUnitNamesFromNpcUnitsCustomKv(self, kvPath)
|
||||
local kv = LoadKeyValues(kvPath)
|
||||
if kv == nil then
|
||||
print((LOG_PFX .. " не удалось загрузить ") .. kvPath)
|
||||
return {}
|
||||
end
|
||||
local block = getUnitDefinitionsRoot(nil, kv)
|
||||
if block == nil then
|
||||
print((LOG_PFX .. " пустой KV ") .. kvPath)
|
||||
return {}
|
||||
end
|
||||
local names = {}
|
||||
for key in pairs(block) do
|
||||
local child = block[key]
|
||||
if child ~= nil and child ~= nil and type(child) == "table" then
|
||||
names[#names + 1] = key
|
||||
end
|
||||
end
|
||||
if #names == 0 then
|
||||
print(((LOG_PFX .. " не найдено ни одного юнита в ") .. kvPath) .. " (ожидан DOTAUnits или плоский корень)")
|
||||
end
|
||||
return names
|
||||
end
|
||||
--- Имена кастомных юнитов из npc_units_custom.txt (+ #base), без npc_heroes.
|
||||
function ____exports.getWaveAndSpawnManagerUnitNamesFromKv(self)
|
||||
local names = collectUnitNamesFromNpcUnitsCustomKv(nil, NPC_UNITS_CUSTOM_KV)
|
||||
__TS__ArraySort(names)
|
||||
return names
|
||||
end
|
||||
--- Запрашивает прекеш полного набора ресурсов юнита по определению в KV.
|
||||
function ____exports.precacheAllCustomUnitsByNameAsync(self, context)
|
||||
local names = ____exports.getWaveAndSpawnManagerUnitNamesFromKv(nil)
|
||||
print((LOG_PFX .. " PrecacheUnitByNameSync (npc_units_custom + #base) × ") .. tostring(#names))
|
||||
for ____, name in ipairs(names) do
|
||||
PrecacheUnitByNameSync(name, context)
|
||||
end
|
||||
end
|
||||
return ____exports
|
||||
@@ -0,0 +1,195 @@
|
||||
--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
|
||||
local ____exports = {}
|
||||
local ____blackshop = require("blackshop")
|
||||
local precacheBlackshopParticles = ____blackshop.precacheBlackshopParticles
|
||||
local ____QuestEventHandlers = require("quests.QuestEventHandlers")
|
||||
local precacheQuestBountyHunterParticles = ____QuestEventHandlers.precacheQuestBountyHunterParticles
|
||||
local ____SpawnManager = require("SpawnManager")
|
||||
local precacheSpawnManagerEffectParticles = ____SpawnManager.precacheSpawnManagerEffectParticles
|
||||
local ____SoundSystem = require("SoundSystem")
|
||||
local precacheWeatherParticles = ____SoundSystem.precacheWeatherParticles
|
||||
local precacheKittyFlexResources = ____SoundSystem.precacheKittyFlexResources
|
||||
local ____vampirism = require("utils.vampirism")
|
||||
local precacheVampirismParticle = ____vampirism.precacheVampirismParticle
|
||||
local ____CutsceneFunctions = require("cutscenes.CutsceneFunctions")
|
||||
local precacheCutsceneParticles = ____CutsceneFunctions.precacheCutsceneParticles
|
||||
--- Полный набор частиц из бывшего GameMode.Precache (способности/предметы/резерв).
|
||||
-- Дубликаты с precache* ниже безопасны. Постепенно переносить в Precache() конкретных ability/item.
|
||||
local LEGACY_GAMEMODE_PARTICLES = {
|
||||
"particles/econ/items/monkey_king/mk_ti9_immortal/arcana_death/mk_ti9_immortal_arcana_death_weapon_ambient_fire.vpcf",
|
||||
"particles/econ/items/monkey_king/mk_ti9_immortal/mk_ti9_immortal_weapon_ambient_fire.vpcf",
|
||||
"particles/red_diff.vpcf",
|
||||
"particles/impossible.vpcf",
|
||||
"particles/econ/items/phoenix/phoenix_ti10_immortal/phoenix_ti10_fire_spirit_launch_elements.vpcf",
|
||||
"particles/econ/items/queen_of_pain/qop_arcana/qop_arcana_tgt_death_fire.vpcf",
|
||||
"particles/units/heroes/hero_skeletonking/wraith_king_curse_overhead_skull.vpcf",
|
||||
"particles/econ/items/bounty_hunter/bounty_hunter_hunters_hoard/bounty_hunter_hoard_shield_mark.vpcf",
|
||||
"particles/econ/items/axe/ti9_jungle_axe/ti9_jungle_axe_culling_blade_sprint_fire.vpcf",
|
||||
"particles/generic_gameplay/generic_has_quest.vpcf",
|
||||
"particles/units/heroes/hero_kunkka/kunkka_spell_torrent_splash.vpcf",
|
||||
"particles/units/heroes/hero_morphling/morphling_adaptive_strike_agi_proj.vpcf",
|
||||
"particles/econ/items/crystal_maiden/crystal_maiden_maiden_of_icewrack/maiden_freezing_field_explosion_c_arcana1.vpcf",
|
||||
"particles/units/heroes/hero_skeletonking/skeletonking_hellfireblast.vpcf",
|
||||
"particles/econ/items/wraith_king/wraith_king_arcana/wk_arc_weapon_blur_critical.vpcf",
|
||||
"particles/econ/events/fall_2022/maelstrom/maelstrom_arcs_fall2022.vpcf",
|
||||
"particles/units/heroes/hero_bounty_hunter/bounty_hunter_track_trail_circle.vpcf",
|
||||
"particles/units/heroes/hero_bounty_hunter/bounty_hunter_track_shield_mark.vpcf",
|
||||
"particles/units/heroes/hero_phantom_assassin/phantom_assassin_stifling_dagger_explosion.vpcf",
|
||||
"particles/units/heroes/hero_primal_beast/primal_beast_onslaught_chargeup.vpcf",
|
||||
"particles/units/heroes/hero_abaddon/abaddon_death_coil.vpcf",
|
||||
"particles/econ/items/huskar/huskar_2021_immortal/huskar_2021_immortal_burning_spear_debuff.vpcf",
|
||||
"particles/units/heroes/hero_axe/axe_culling_blade.vpcf",
|
||||
"particles/units/heroes/hero_phoenix/phoenix_supernova_reborn.vpcf",
|
||||
"particles/bloodstone_full_screen_effect.vpcf",
|
||||
"particles/econ/items/bloodseeker/bloodseeker_eztzhok_weapon/bloodseeker_bloodbath_eztzhok.vpcf",
|
||||
"particles/units/heroes/hero_muerta/muerta_ultimate_form_screen_effect.vpcf",
|
||||
"particles/units/heroes/hero_huskar/huskar_burning_spear_debuff.vpcf",
|
||||
"particles/units/heroes/hero_skeletonking/wraith_king_curse_debuff_slash.vpcf",
|
||||
"particles/econ/items/shadow_fiend/sf_desolation/sf_base_attack_desolation.vpcf",
|
||||
"particles/econ/items/templar_assassin/ta_2022_immortal/ta_2022_immortal_trap_crimson.vpcf",
|
||||
"particles/econ/items/templar_assassin/ta_2022_immortal/ta_2022_immortal_trap_gold.vpcf",
|
||||
"particles/econ/items/lanaya/lanaya_epit_trap/templar_assassin_epit_trap.vpcf",
|
||||
"particles/units/heroes/hero_ancient_apparition/ancient_apparition_freeze_stacks_smoke_b.vpcf",
|
||||
"particles/units/heroes/hero_ancient_apparition/ancient_apparition_ice_blast_main.vpcf",
|
||||
"particles/units/heroes/hero_jakiro/jakiro_liquid_fire_explosion.vpcf",
|
||||
"particles/econ/items/faceless_void/faceless_void_arcana/faceless_void_arcana_deny_v2_symbol_question.vpcf",
|
||||
"particles/units/heroes/hero_largo/largo_catchy_lick.vpcf",
|
||||
"particles/ui_mouseactions/range_finder_cone_dual.vpcf",
|
||||
"particles/units/heroes/hero_invoker/invoker_forged_spirit_projectile.vpcf",
|
||||
"particles/msg_fx/msg_mana_add.vpcf",
|
||||
"particles/units/heroes/hero_morphling/morphling_adaptive_strike.vpcf",
|
||||
"particles/items3_fx/mango_active.vpcf",
|
||||
"particles/units/heroes/hero_brewmaster/brewmaster_drunken_haze_debuff.vpcf",
|
||||
"models/heroes/phantom_assassin_persona/debut/particles/pa_debutdash/pa_debutdash_fragments.vpcf",
|
||||
"particles/items_fx/chain_lightning.vpcf",
|
||||
"particles/items_fx/phylactery_target.vpcf",
|
||||
"particles/items_fx/phylactery.vpcf",
|
||||
"particles/units/heroes/hero_huskar/huskar_inner_fire_debuff_flame.vpcf",
|
||||
"particles/items_fx/battlefury_cleave.vpcf",
|
||||
"particles/econ/items/queen_of_pain/qop_2022_immortal/queen_2022_scream_of_pain_owner_blue.vpcf",
|
||||
"particles/darkmoon_creep_warning.vpcf",
|
||||
"particles/crystal_scepter_shield_ring.vpcf",
|
||||
"particles/crystal_scepter_shield.vpcf",
|
||||
"particles/fish_screen_effect.vpcf",
|
||||
"particles/econ/items/lifestealer/lifestealer_immortal_backbone_gold/lifestealer_immortal_backbone_gold_rage.vpcf",
|
||||
"particles/units/heroes/hero_omniknight/omniknight_heavenly_grace_buff.vpcf",
|
||||
"particles/econ/items/omniknight/omniknight_fall20_immortal/omniknight_fall20_immortal_degen_aura_debuff.vpcf",
|
||||
"particles/econ/items/omniknight/omni_crimson_witness_2021/omniknight_crimson_witness_2021_degen_aura_debuff.vpcf",
|
||||
"particles/econ/events/fall_2021/fall_2021_emblem_game_effect.vpcf",
|
||||
"particles/blue_gems_effect.vpcf",
|
||||
"particles/sponsor_effect.vpcf",
|
||||
"particles/lotus_effect.vpcf",
|
||||
"particles/effect_battlepass_red.vpcf",
|
||||
"particles/battlepass_garden_effect.vpcf",
|
||||
"particles/battlepass_golden_effect.vpcf",
|
||||
"particles/units/heroes/hero_witchdoctor/witchdoctor_maledict_projectile.vpcf",
|
||||
"particles/econ/items/ember_spirit/ember_ti9/ember_ti9_flameguard.vpcf",
|
||||
"particles/units/heroes/hero_ember_spirit/ember_spirit_fire_remnant_flames.vpcf",
|
||||
"particles/world_environmental_fx/map_riverflow.vpcf",
|
||||
"particles/econ/events/diretide_2020/emblem/fall20_emblem_effect.vpcf",
|
||||
"particles/econ/events/diretide_2020/emblem/fall20_emblem_v1_effect.vpcf",
|
||||
"particles/econ/events/diretide_2020/emblem/fall20_emblem_v2_effect.vpcf",
|
||||
"particles/econ/events/diretide_2020/emblem/fall20_emblem_v3_effect.vpcf",
|
||||
"particles/econ/items/kunkka/divine_anchor/hero_kunkka_dafx_skills/kunkka_spell_x_spot_mark_red_fxset.vpcf",
|
||||
"particles/econ/events/ti9/shovel_dig.vpcf",
|
||||
"particles/econ/events/ti9/shovel_revealed_baby_roshan.vpcf",
|
||||
"particles/econ/events/darkmoon_2017/darkmoon_calldown_marker_arrows.vpcf",
|
||||
"particles/econ/items/void_spirit/void_spirit_immortal_2021/void_spirit_immortal_2021_astral_step_debuff.vpcf",
|
||||
"particles/econ/items/viper/viper_ti7_immortal/viper_poison_debuff_ti7.vpcf",
|
||||
"particles/econ/events/darkmoon_2017/darkmoon_generic_aoe.vpcf",
|
||||
"particles/econ/items/clinkz/clinkz_maraxiform/clinkz_maraxiform_searing_arrow_deso.vpcf",
|
||||
"particles/econ/items/centaur/centaur_crownfall_belt/centaur_crownfall_belt_retaliate.vpcf",
|
||||
"particles/units/heroes/hero_medusa/medusa_mana_shield_buff.vpcf",
|
||||
"particles/ui_mouseactions/range_finder_tower_aoe_target_ring.vpcf",
|
||||
"particles/units/heroes/hero_omniknight/omniknight_guardian_angel_wings.vpcf",
|
||||
"particles/econ/items/bristleback/ti7_head_nasal_goo/bristleback_ti7_crimson_nasal_goo_proj.vpcf",
|
||||
"particles/units/heroes/hero_axe/axe_beserkers_call_owner.vpcf",
|
||||
"particles/econ/items/bloodseeker/bloodseeker_eztzhok_weapon/bloodseeker_bloodrage_eztzhok.vpcf",
|
||||
"particles/units/heroes/hero_chaos_knight/chaos_knight_phantasm.vpcf",
|
||||
"particles/units/heroes/hero_bloodseeker/bloodseeker_bloodritual_ring_lv.vpcf",
|
||||
"particles/units/heroes/hero_bloodseeker/bloodseeker_spell_bloodbath_bubbles_lv.vpcf",
|
||||
"particles/econ/items/lifestealer/ls_ti10_immortal/ls_ti10_immortal_infest_gold.vpcf",
|
||||
"particles/units/heroes/hero_ursa/ursa_enrage_buff_2.vpcf",
|
||||
"particles/units/heroes/heroes_underlord/abbysal_underlord_portal_ambient.vpcf",
|
||||
"particles/bloodbath_circle.vpcf",
|
||||
"particles/units/heroes/hero_pudge/pudge_meathook.vpcf",
|
||||
"particles/econ/items/pudge/pudge_ti6_immortal/pudge_meathook_impact_ti6.vpcf",
|
||||
"particles/units/heroes/hero_wisp/wisp_ambient.vpcf",
|
||||
"particles/econ/events/fall_2021/fountain_regen_fall_2021_lvl3.vpcf",
|
||||
"particles/crystal_maiden_aspect_3.vpcf",
|
||||
"particles/econ/items/crystal_maiden/ti7_immortal_shoulder/cm_ti7_immortal_frostbite.vpcf",
|
||||
"particles/econ/courier/courier_golden_doomling/courier_golden_doomling_ambient.vpcf",
|
||||
"particles/units/heroes/hero_terrorblade/terrorblade_metamorphosis.vpcf",
|
||||
"particles/econ/items/sven/sven_ti7_sword/sven_ti7_sword_spell_great_cleave_gods_strength_crit_b.vpcf",
|
||||
"particles/econ/items/huskar/huskar_2021_immortal/huskar_2021_immortal_burning_spear_debuff_flame_circulate.vpcf",
|
||||
"particles/units/heroes/hero_doom_bringer/doom_scorched_earth.vpcf",
|
||||
"particles/econ/items/wraith_king/wraith_king_ti6_bracer/wraith_king_ti6_ambient_fireball_lava.vpcf",
|
||||
"particles/units/heroes/hero_doom_bringer/doom_bringer_devour.vpcf",
|
||||
"particles/econ/items/zeus/arcana_chariot/zeus_arcana_kill_explosion.vpcf",
|
||||
"particles/econ/items/warlock/warlock_ti9/warlock_ti9_shadow_word_buff.vpcf",
|
||||
"particles/units/heroes/hero_centaur/centaur_shard_buff_strength_counter_stack.vpcf",
|
||||
"particles/units/heroes/hero_dragon_knight/dragon_knight_shard_fireball.vpcf",
|
||||
"particles/nagash_world_full.vpcf",
|
||||
"particles/events/crownfall/survivors/status/status_effect_burn.vpcf",
|
||||
"particles/creeps/lane_creeps/creep_radiant_hulk_swipe_left.vpcf",
|
||||
"particles/creeps/lane_creeps/creep_radiant_hulk_swipe_right.vpcf",
|
||||
"particles/econ/items/bloodseeker/bloodseeker_crownfall_immortal/bloodseeker_crownfall_immortal_splash_ring.vpcf",
|
||||
"particles/units/heroes/hero_witchdoctor/witchdoctor_voodoo_restoration.vpcf",
|
||||
"particles/econ/items/winter_wyvern/winter_wyvern_ti7/wyvern_cold_embrace_ti7buff.vpcf",
|
||||
"particles/econ/items/ancient_apparition/ancient_apparation_ti8/ancient_ice_vortex_ti8.vpcf",
|
||||
"particles/generic_gameplay/generic_slowed_cold.vpcf",
|
||||
"particles/econ/items/crystal_maiden/crystal_maiden_maiden_of_icewrack/maiden_freezing_field_snow_arcana1.vpcf",
|
||||
"particles/units/heroes/hero_crystalmaiden/maiden_frostbite_buff.vpcf",
|
||||
"particles/econ/items/crystal_maiden/ti9_immortal_staff/cm_ti9_staff_lvlup_globe.vpcf",
|
||||
"particles/econ/events/snowball/snowball_projectile.vpcf",
|
||||
"particles/units/heroes/hero_ancient_apparition/ancient_apparition_ice_blast_debuff.vpcf",
|
||||
"particles/rain_fx/econ_weather_sirocco.vpcf",
|
||||
"particles/rain_fx/econ_weather_pestilence.vpcf",
|
||||
"particles/winter_fx/weather_plateau_snow.vpcf",
|
||||
"particles/rain_fx/econ_rain.vpcf",
|
||||
"particles/units/heroes/hero_clinkz/clinkz_searing_arrow_linear_proj.vpcf",
|
||||
"particles/econ/items/grimstroke/gs_fall20_immortal/gs_fall20_immortal_soul_debuff.vpcf",
|
||||
"particles/econ/items/grimstroke/gs_fall20_immortal/gs_fall20_immortal_soul_dragon_model.vpcf",
|
||||
"particles/econ/items/legion/legion_fallen/legion_fallen_press_buff.vpcf",
|
||||
"particles/heroes/dragon_knight_breathe_fire_meta.vpcf",
|
||||
"particles/units/heroes/hero_dragon_knight/dragon_knight_breathe_fire.vpcf",
|
||||
"particles/generic_gameplay/generic_lifesteal.vpcf",
|
||||
"particles/econ/items/medusa/medusa_daughters/medusa_daughters_mana_shield.vpcf",
|
||||
"particles/econ/items/gyrocopter/hero_gyrocopter_gyrotechnics/gyro_base_attack.vpcf",
|
||||
"particles/units/heroes/hero_jakiro/jakiro_liquid_fire_debuff.vpcf",
|
||||
"particles/econ/items/jakiro/jakiro_ti10_immortal/jakiro_ti10_macropyre.vpcf",
|
||||
"particles/units/heroes/hero_warlock/warlock_rain_of_chaos_start.vpcf",
|
||||
"particles/juggernaut_step.vpcf",
|
||||
"particles/econ/items/warlock/warlock_ti10_head/warlock_ti_10_fatal_bonds_icon.vpcf",
|
||||
"particles/units/heroes/hero_bloodseeker/bloodseeker_bloodbath.vpcf",
|
||||
"particles/ping_player_clown.vpcf",
|
||||
"particles/store.vpcf",
|
||||
"particles/boss_tinker_laser_preview_vector.vpcf",
|
||||
"particles/econ/items/axe/ti9_jungle_axe/ti9_jungle_axe_culling_blade_sprint_fire.vpcf",
|
||||
"particles/econ/items/templar_assassin/templar_assassin_butterfly/templar_assassin_meld_attack_butterfly.vpcf"
|
||||
}
|
||||
--- Число записей PrecacheResource("particle") по модулям (для лога; совпадает с телами функций ниже).
|
||||
local PARTICLE_PRECACHE_COUNTS = {
|
||||
weather = 4,
|
||||
spawnManager = 3,
|
||||
blackshop = 6,
|
||||
questBountyHunter = 2,
|
||||
vampirism = 1,
|
||||
cutscenes = 2
|
||||
}
|
||||
function ____exports.precacheMiscDistributedAndOrphanParticles(self, context)
|
||||
precacheWeatherParticles(nil, context)
|
||||
precacheKittyFlexResources(nil, context)
|
||||
precacheSpawnManagerEffectParticles(nil, context)
|
||||
precacheBlackshopParticles(nil, context)
|
||||
precacheQuestBountyHunterParticles(nil, context)
|
||||
precacheVampirismParticle(nil, context)
|
||||
precacheCutsceneParticles(nil, context)
|
||||
for ____, p in ipairs(LEGACY_GAMEMODE_PARTICLES) do
|
||||
PrecacheResource("particle", p, context)
|
||||
end
|
||||
local legacyN = #LEGACY_GAMEMODE_PARTICLES
|
||||
local sum = PARTICLE_PRECACHE_COUNTS.weather + PARTICLE_PRECACHE_COUNTS.spawnManager + PARTICLE_PRECACHE_COUNTS.blackshop + PARTICLE_PRECACHE_COUNTS.questBountyHunter + PARTICLE_PRECACHE_COUNTS.vampirism + PARTICLE_PRECACHE_COUNTS.cutscenes + legacyN
|
||||
print(((((((((((((((("[Precache] Частицы: SoundSystem/погода=" .. tostring(PARTICLE_PRECACHE_COUNTS.weather)) .. ", SpawnManager=") .. tostring(PARTICLE_PRECACHE_COUNTS.spawnManager)) .. ", BlackShop=") .. tostring(PARTICLE_PRECACHE_COUNTS.blackshop)) .. ", Quest(BH)=") .. tostring(PARTICLE_PRECACHE_COUNTS.questBountyHunter)) .. ", vampirism=") .. tostring(PARTICLE_PRECACHE_COUNTS.vampirism)) .. ", cutscenes=") .. tostring(PARTICLE_PRECACHE_COUNTS.cutscenes)) .. ", legacy-список=") .. tostring(legacyN)) .. " → сумма вызовов Precache(particle) в этом блоке: ") .. tostring(sum)) .. " (дубликаты путей между модулями и legacy возможны)")
|
||||
end
|
||||
return ____exports
|
||||
@@ -0,0 +1,135 @@
|
||||
local ____lualib = require("lualib_bundle")
|
||||
local __TS__TypeOf = ____lualib.__TS__TypeOf
|
||||
local __TS__StringEndsWith = ____lualib.__TS__StringEndsWith
|
||||
local __TS__ArraySort = ____lualib.__TS__ArraySort
|
||||
local ____exports = {}
|
||||
--- Модели, которых нет в npc_units_custom / #base, но нужны коду (дроп, эффекты).
|
||||
local EXTRA_PRECACHE_MODELS = {
|
||||
"models/props_gameplay/roshans_banner.vmdl",
|
||||
"models/development/invisiblebox.vmdl",
|
||||
"models/props_gameplay/lantern/lantern_of_sight.vmdl",
|
||||
"models/heroes/undying/undying_tower.vmdl",
|
||||
"models/items/lifestealer/promo_bloody_ripper_belt/promo_bloody_ripper_belt.vmdl",
|
||||
"models/items/warlock/golem/puppet_summoner_golem/puppet_summoner_golem.vmdl",
|
||||
"models/items/lifestealer/promo_bloody_ripper_head/promo_bloody_ripper_head.vmdl",
|
||||
"models/items/lifestealer/promo_bloody_ripper_back/promo_bloody_ripper_back.vmdl",
|
||||
"models/items/lifestealer/promo_bloody_ripper_arms/promo_bloody_ripper_arms.vmdl",
|
||||
"models/items/kunkka/spiritoftide_gloves/spiritoftide_gloves.vmdl",
|
||||
"models/items/kunkka/kunkka_bandana.vmdl",
|
||||
"models/items/kunkka/claddish_shoulder/claddish_shoulder.vmdl",
|
||||
"models/items/kunkka/arm_lev_pipe/arm_lev_pipe.vmdl",
|
||||
"models/heroes/kunkka/kunkka_shoulders.vmdl",
|
||||
"models/items/kunkka/kunkka_shadow_blade/kunkka_shadow_blade.vmdl",
|
||||
"models/heroes/dragon_knight_persona/dk_persona_armor.vmdl",
|
||||
"models/heroes/dragon_knight_persona/dk_persona_head_hair.vmdl",
|
||||
"models/items/omniknight/stalwart_back/stalwart_back.vmdl",
|
||||
"models/heroes/omniknight/head.vmdl",
|
||||
"models/items/omniknight/grey_night_head/grey_night_head.vmdl",
|
||||
"models/items/lina/magnificentflame_head/magnificentflame_head.vmdl",
|
||||
"models/items/lina/pw_fire_lotus/lina_belt.vmdl",
|
||||
"models/items/lina/everlastingheat_arms/everlastingheat_arms.vmdl",
|
||||
"models/items/lina/ember_crane_neck/ember_crane_neck.vmdl",
|
||||
"models/items/crystal_maiden/esl_frozen_lotus_head/esl_frozen_lotus_head.vmdl",
|
||||
"models/items/crystal_maiden/esl_frozen_lotus_shoulder/esl_frozen_lotus_shoulder.vmdl",
|
||||
"models/items/crystal_maiden/esl_frozen_lotus_arms/esl_frozen_lotus_arms.vmdl",
|
||||
"models/items/crystal_maiden/esl_frozen_lotus_back/esl_frozen_lotus_back.vmdl",
|
||||
"models/heroes/bard/bard_frog_upperbody.vmdl",
|
||||
"models/heroes/bard/bard_frog_lowerbody.vmdl",
|
||||
"models/heroes/bard/bard_frog_weapon.vmdl",
|
||||
"models/items/witchdoctor/bonkers_the_mad/bonkers_the_mad.vmdl",
|
||||
"models/items/witchdoctor/teller_of_the_auspice_belt/teller_of_the_auspice_belt.vmdl",
|
||||
"models/items/witchdoctor/teller_of_the_auspice_head/teller_of_the_auspice_head.vmdl",
|
||||
"models/items/witchdoctor/teller_of_the_auspice_weapon/teller_of_the_auspice_weapon.vmdl",
|
||||
"models/items/grimstroke/grimreaver_hood/grimreaver_hood.vmdl",
|
||||
"models/items/grimstroke/the_chained_scribe_head/the_chained_scribe_head.vmdl",
|
||||
"models/items/grimstroke/the_chained_scribe_belt/the_chained_scribe_belt.vmdl",
|
||||
"models/items/grimstroke/the_chained_scribe_armor/the_chained_scribe_armor.vmdl",
|
||||
"models/items/grimstroke/grimreaver_scythe/grimreaver_scythe.vmdl",
|
||||
"models/items/death_prophet/woman_of_the_snow_belt/woman_of_the_snow_belt.vmdl",
|
||||
"models/items/death_prophet/woman_of_the_snow_legs/woman_of_the_snow_legs.vmdl",
|
||||
"models/items/death_prophet/woman_of_the_snow_misc/woman_of_the_snow_misc.vmdl",
|
||||
"models/items/death_prophet/woman_of_the_snow_head/woman_of_the_snow_head.vmdl",
|
||||
"models/items/death_prophet/woman_of_the_snow_armor/woman_of_the_snow_armor.vmdl"
|
||||
}
|
||||
local function collectVmdlStringsFromKv(self, value, bucket)
|
||||
if value == nil or value == nil then
|
||||
return
|
||||
end
|
||||
local t = __TS__TypeOf(value)
|
||||
if t == "string" then
|
||||
local s = value
|
||||
if __TS__StringEndsWith(s, ".vmdl") then
|
||||
bucket[s] = true
|
||||
end
|
||||
return
|
||||
end
|
||||
if t == "object" then
|
||||
local o = value
|
||||
for key in pairs(o) do
|
||||
collectVmdlStringsFromKv(nil, o[key], bucket)
|
||||
end
|
||||
end
|
||||
end
|
||||
--- KV, из которых LoadKeyValues подмешивает #base и собирает дерево как у движка.
|
||||
local NPC_KV_MODEL_SOURCES = {"scripts/npc/npc_units_custom.txt", "scripts/npc/npc_items_custom.txt"}
|
||||
local LOG_PFX = "[precache_models_from_kv]"
|
||||
local function sortedModelPaths(self, bucket)
|
||||
local out = {}
|
||||
for p in pairs(bucket) do
|
||||
out[#out + 1] = p
|
||||
end
|
||||
__TS__ArraySort(out)
|
||||
return out
|
||||
end
|
||||
local function printModelList(self, title, paths)
|
||||
print(((((LOG_PFX .. " --- ") .. title) .. " (") .. tostring(#paths)) .. ") ---")
|
||||
for ____, p in ipairs(paths) do
|
||||
print((LOG_PFX .. " model: ") .. p)
|
||||
end
|
||||
end
|
||||
--- Прекеширует все .vmdl из npc_units_custom и npc_items_custom (с их #base).
|
||||
function ____exports.precacheModelsFromNpcUnitsCustomKv(self, context)
|
||||
local bucket = {}
|
||||
--- Модели только из этого корневого KV (для лога; дубликаты между файлами возможны).
|
||||
local perSource = {}
|
||||
for ____, path in ipairs(NPC_KV_MODEL_SOURCES) do
|
||||
local kv = LoadKeyValues(path)
|
||||
if kv == nil then
|
||||
print((LOG_PFX .. " Не удалось загрузить ") .. path)
|
||||
else
|
||||
local ____local = {}
|
||||
collectVmdlStringsFromKv(nil, kv, ____local)
|
||||
perSource[path] = ____local
|
||||
for m in pairs(____local) do
|
||||
bucket[m] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
local extraBucket = {}
|
||||
for ____, path in ipairs(EXTRA_PRECACHE_MODELS) do
|
||||
bucket[path] = true
|
||||
extraBucket[path] = true
|
||||
end
|
||||
print(LOG_PFX .. " Список моделей к прекешу:")
|
||||
for ____, path in ipairs(NPC_KV_MODEL_SOURCES) do
|
||||
local ____local = perSource[path]
|
||||
if ____local ~= nil then
|
||||
printModelList(
|
||||
nil,
|
||||
path,
|
||||
sortedModelPaths(nil, ____local)
|
||||
)
|
||||
end
|
||||
end
|
||||
printModelList(
|
||||
nil,
|
||||
"EXTRA_PRECACHE_MODELS (не из KV)",
|
||||
sortedModelPaths(nil, extraBucket)
|
||||
)
|
||||
local allPaths = sortedModelPaths(nil, bucket)
|
||||
for ____, modelPath in ipairs(allPaths) do
|
||||
PrecacheResource("model", modelPath, context)
|
||||
end
|
||||
print((LOG_PFX .. " Итого запрошено PrecacheResource(model): ") .. tostring(#allPaths))
|
||||
end
|
||||
return ____exports
|
||||
@@ -0,0 +1,66 @@
|
||||
local ____lualib = require("lualib_bundle")
|
||||
local __TS__ArraySort = ____lualib.__TS__ArraySort
|
||||
local ____exports = {}
|
||||
--- Кто считается «реальным» игроком в лобби (совпадает с бывшей логикой GameMode).
|
||||
-- Без проверки контроллера в список попадают пустые/«невидимые» слоты на CUSTOM_GAME_SETUP.
|
||||
function ____exports.isRealLobbyPlayer(self, playerId)
|
||||
if not PlayerResource:IsValidPlayerID(playerId) then
|
||||
return false
|
||||
end
|
||||
if not PlayerResource:IsValidPlayer(playerId) then
|
||||
return false
|
||||
end
|
||||
if PlayerResource:IsFakeClient(playerId) then
|
||||
return false
|
||||
end
|
||||
return PlayerResource:GetPlayer(playerId) ~= nil
|
||||
end
|
||||
function ____exports.countRealLobbyPlayers(self)
|
||||
local n = 0
|
||||
do
|
||||
local playerId = 0
|
||||
while playerId < DOTA_MAX_PLAYERS do
|
||||
if ____exports.isRealLobbyPlayer(nil, playerId) then
|
||||
n = n + 1
|
||||
end
|
||||
playerId = playerId + 1
|
||||
end
|
||||
end
|
||||
return n
|
||||
end
|
||||
--- Игроки, участвующие в PvE-статистике матча: реальное лобби + команда
|
||||
-- (если слотов Dire нет — только GOODGUYS).
|
||||
function ____exports.collectStatsEligiblePlayerIds(self)
|
||||
local ids = {}
|
||||
local badMax = GameRules:GetCustomGameTeamMaxPlayers(DOTA_TEAM_BADGUYS)
|
||||
do
|
||||
local i = 0
|
||||
while i < DOTA_MAX_PLAYERS do
|
||||
do
|
||||
local pid = i
|
||||
if not ____exports.isRealLobbyPlayer(nil, pid) then
|
||||
goto __continue10
|
||||
end
|
||||
local team = PlayerResource:GetTeam(pid)
|
||||
if badMax == 0 then
|
||||
if team ~= DOTA_TEAM_GOODGUYS then
|
||||
goto __continue10
|
||||
end
|
||||
else
|
||||
if team ~= DOTA_TEAM_GOODGUYS and team ~= DOTA_TEAM_BADGUYS then
|
||||
goto __continue10
|
||||
end
|
||||
end
|
||||
ids[#ids + 1] = pid
|
||||
end
|
||||
::__continue10::
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
__TS__ArraySort(
|
||||
ids,
|
||||
function(____, a, b) return a - b end
|
||||
)
|
||||
return ids
|
||||
end
|
||||
return ____exports
|
||||
@@ -0,0 +1,75 @@
|
||||
--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
|
||||
local ____exports = {}
|
||||
local ____store_manager = require("store_manager")
|
||||
local StoreManager = ____store_manager.StoreManager
|
||||
--- Микрокэш на один вызов GetGameTime() — снижает давление CustomNetTables при частых GetEffectName.
|
||||
local clientGetEffectCacheKey = ""
|
||||
local clientGetEffectCacheTime = -999999
|
||||
local clientGetEffectCacheValue = nil
|
||||
--- Получить все активные эффекты героя
|
||||
--
|
||||
-- @param hero - Герой игрока
|
||||
-- @returns Объект с активными эффектами по типам: { "effect": "skin_effect_fire", "wings": "wings_demon", "model": null }
|
||||
function ____exports.GetAllEffects(self, hero)
|
||||
local playerId = hero:GetPlayerOwnerID()
|
||||
if playerId < 0 then
|
||||
return {}
|
||||
end
|
||||
local storeManager = StoreManager:getInstance()
|
||||
return storeManager:getPlayerActiveEffects(playerId)
|
||||
end
|
||||
--- Получить активный эффект конкретного типа для героя
|
||||
--
|
||||
-- @param hero - Герой игрока
|
||||
-- @param effectType - Тип эффекта ("effect", "wings", "model")
|
||||
-- @returns ID эффекта или null
|
||||
function ____exports.GetEffect(self, hero, effectType)
|
||||
local playerId = hero:GetPlayerOwnerID()
|
||||
if playerId < 0 then
|
||||
return nil
|
||||
end
|
||||
if IsClient() then
|
||||
local now = GameRules:GetGameTime()
|
||||
local cacheKey = (tostring(playerId) .. ":") .. effectType
|
||||
if clientGetEffectCacheKey == cacheKey and clientGetEffectCacheTime == now then
|
||||
return clientGetEffectCacheValue
|
||||
end
|
||||
local playerInfo = CustomNetTables:GetTableValue(
|
||||
"player_info",
|
||||
tostring(playerId)
|
||||
)
|
||||
local value = nil
|
||||
if playerInfo and playerInfo.active_effects and playerInfo.active_effects[effectType] then
|
||||
value = playerInfo.active_effects[effectType]
|
||||
end
|
||||
clientGetEffectCacheKey = cacheKey
|
||||
clientGetEffectCacheTime = now
|
||||
clientGetEffectCacheValue = value
|
||||
return value
|
||||
end
|
||||
local storeManager = StoreManager:getInstance()
|
||||
return storeManager:getPlayerActiveEffect(playerId, effectType)
|
||||
end
|
||||
--- Проверить, надёт ли конкретный эффект на героя
|
||||
--
|
||||
-- @param hero - Герой игрока
|
||||
-- @param effectId - ID эффекта
|
||||
-- @param effectType - Опционально: тип эффекта для проверки конкретного типа
|
||||
-- @returns true если эффект надёт, false если нет
|
||||
function ____exports.HasEffect(self, hero, effectId, effectType)
|
||||
local playerId = hero:GetPlayerOwnerID()
|
||||
if playerId < 0 then
|
||||
return false
|
||||
end
|
||||
local storeManager = StoreManager:getInstance()
|
||||
return storeManager:isEffectEquipped(playerId, effectId, effectType)
|
||||
end
|
||||
local g = _G
|
||||
g.GetAllEffects = ____exports.GetAllEffects
|
||||
g.GetEffect = ____exports.GetEffect
|
||||
g.HasEffect = ____exports.HasEffect
|
||||
local env = getfenv(nil, 0)
|
||||
env.GetAllEffects = ____exports.GetAllEffects
|
||||
env.GetEffect = ____exports.GetEffect
|
||||
env.HasEffect = ____exports.HasEffect
|
||||
return ____exports
|
||||
@@ -0,0 +1,115 @@
|
||||
local ____lualib = require("lualib_bundle")
|
||||
local Set = ____lualib.Set
|
||||
local __TS__New = ____lualib.__TS__New
|
||||
local __TS__ObjectAssign = ____lualib.__TS__ObjectAssign
|
||||
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 OWNER_ATTACK_FORWARD_EXCLUDED_MODIFIERS = __TS__New(Set, {"modifier_stacking_crit", "modifier_summon_owner_attack_proxy"})
|
||||
--- Герой-владелец для призыва или illusions-like юнита с SetOwner.
|
||||
function ____exports.getRealOwnerHeroFromUnit(self, unit)
|
||||
if not unit or not IsValidEntity(unit) then
|
||||
return nil
|
||||
end
|
||||
if unit:IsRealHero() then
|
||||
return unit
|
||||
end
|
||||
local owner = unit:GetOwner()
|
||||
if not owner or not IsValidEntity(owner) or not owner:IsRealHero() then
|
||||
return nil
|
||||
end
|
||||
return owner
|
||||
end
|
||||
local function forEachOwnerAttackModifier(self, owner, visitor)
|
||||
local seenNames = __TS__New(Set)
|
||||
do
|
||||
local i = 0
|
||||
while i < owner:GetModifierCount() do
|
||||
do
|
||||
local modifierName = owner:GetModifierNameByIndex(i)
|
||||
if not modifierName or OWNER_ATTACK_FORWARD_EXCLUDED_MODIFIERS:has(modifierName) or seenNames:has(modifierName) then
|
||||
goto __continue7
|
||||
end
|
||||
seenNames:add(modifierName)
|
||||
local modifier = owner:FindModifierByName(modifierName)
|
||||
if not modifier or not IsValidEntity(modifier) then
|
||||
goto __continue7
|
||||
end
|
||||
visitor(nil, modifier, modifierName)
|
||||
end
|
||||
::__continue7::
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
--- Пробрасывает атаку призыва в on-hit/on-attack модификаторы героя-владельца.
|
||||
function ____exports.forwardOwnerAttackModifiersFromSummon(self, owner, summonEvent, hook)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if not owner or not IsValidEntity(owner) or not owner:IsAlive() then
|
||||
return
|
||||
end
|
||||
local ownerEvent = __TS__ObjectAssign({}, summonEvent, {attacker = owner})
|
||||
forEachOwnerAttackModifier(
|
||||
nil,
|
||||
owner,
|
||||
function(____, modifier)
|
||||
local hookFn = modifier[hook]
|
||||
if type(hookFn) ~= "function" then
|
||||
return
|
||||
end
|
||||
hookFn(modifier, ownerEvent)
|
||||
end
|
||||
)
|
||||
end
|
||||
____exports.modifier_summon_owner_attack_proxy = __TS__Class()
|
||||
local modifier_summon_owner_attack_proxy = ____exports.modifier_summon_owner_attack_proxy
|
||||
modifier_summon_owner_attack_proxy.name = "modifier_summon_owner_attack_proxy"
|
||||
modifier_summon_owner_attack_proxy.____file_path = "scripts/vscripts/utils/summon_owner_attack_proxy.lua"
|
||||
__TS__ClassExtends(modifier_summon_owner_attack_proxy, BaseModifier)
|
||||
function modifier_summon_owner_attack_proxy.prototype.IsHidden(self)
|
||||
return true
|
||||
end
|
||||
function modifier_summon_owner_attack_proxy.prototype.IsPurgable(self)
|
||||
return false
|
||||
end
|
||||
function modifier_summon_owner_attack_proxy.prototype.RemoveOnDeath(self)
|
||||
return false
|
||||
end
|
||||
function modifier_summon_owner_attack_proxy.prototype.DeclareFunctions(self)
|
||||
return {MODIFIER_EVENT_ON_ATTACK, MODIFIER_EVENT_ON_ATTACK_LANDED}
|
||||
end
|
||||
function modifier_summon_owner_attack_proxy.prototype.forwardToOwner(self, event, hook)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local parent = self:GetParent()
|
||||
if not parent or not IsValidEntity(parent) or event.attacker ~= parent then
|
||||
return
|
||||
end
|
||||
local owner = ____exports.getRealOwnerHeroFromUnit(nil, parent)
|
||||
if not owner then
|
||||
return
|
||||
end
|
||||
____exports.forwardOwnerAttackModifiersFromSummon(nil, owner, event, hook)
|
||||
end
|
||||
function modifier_summon_owner_attack_proxy.prototype.OnAttack(self, event)
|
||||
self:forwardToOwner(event, "OnAttack")
|
||||
end
|
||||
function modifier_summon_owner_attack_proxy.prototype.OnAttackLanded(self, event)
|
||||
self:forwardToOwner(event, "OnAttackLanded")
|
||||
end
|
||||
modifier_summon_owner_attack_proxy = __TS__Decorate(
|
||||
modifier_summon_owner_attack_proxy,
|
||||
modifier_summon_owner_attack_proxy,
|
||||
{registerModifier(nil)},
|
||||
{kind = "class", name = "modifier_summon_owner_attack_proxy"}
|
||||
)
|
||||
____exports.modifier_summon_owner_attack_proxy = modifier_summon_owner_attack_proxy
|
||||
return ____exports
|
||||
@@ -0,0 +1,125 @@
|
||||
local ____lualib = require("lualib_bundle")
|
||||
local __TS__ArrayFilter = ____lualib.__TS__ArrayFilter
|
||||
local ____exports = {}
|
||||
--- Модификаторы призыва, которые нельзя снимать при синхронизации.
|
||||
local SUMMON_ONLY_MODIFIER_NAMES = {modifier_dark_friends = true, modifier_stacking_crit = true}
|
||||
--- Модификаторы владельца, которые не копируем на призыв.
|
||||
local OWNER_MODIFIER_SYNC_EXCLUDED = {modifier_dark_friends = true, modifier_dark_friends_create = true}
|
||||
local function isOwnerModifierEligibleForSummonSync(self, mod)
|
||||
local name = mod:GetName()
|
||||
if OWNER_MODIFIER_SYNC_EXCLUDED[name] then
|
||||
return false
|
||||
end
|
||||
if mod.IsDebuff and mod:IsDebuff() then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
local function groupModifiersByName(self, modifiers)
|
||||
local groups = {}
|
||||
for ____, mod in ipairs(modifiers) do
|
||||
local name = mod:GetName()
|
||||
if not groups[name] then
|
||||
groups[name] = {}
|
||||
end
|
||||
local ____groups_name_0 = groups[name]
|
||||
____groups_name_0[#____groups_name_0 + 1] = mod
|
||||
end
|
||||
return groups
|
||||
end
|
||||
local function syncStackingCritState(self, ownerMod, summonMod)
|
||||
summonMod.critModifiers = ownerMod.critModifiers
|
||||
local ____summonMod_2 = summonMod
|
||||
local ____ownerMod_guaranteedCritCount_1 = ownerMod.guaranteedCritCount
|
||||
if ____ownerMod_guaranteedCritCount_1 == nil then
|
||||
____ownerMod_guaranteedCritCount_1 = 0
|
||||
end
|
||||
____summonMod_2.guaranteedCritCount = ____ownerMod_guaranteedCritCount_1
|
||||
local ____summonMod_4 = summonMod
|
||||
local ____ownerMod_guaranteedCritUntilDestroy_3 = ownerMod.guaranteedCritUntilDestroy
|
||||
if ____ownerMod_guaranteedCritUntilDestroy_3 == nil then
|
||||
____ownerMod_guaranteedCritUntilDestroy_3 = false
|
||||
end
|
||||
____summonMod_4.guaranteedCritUntilDestroy = ____ownerMod_guaranteedCritUntilDestroy_3
|
||||
end
|
||||
local function syncModifierInstance(self, ownerMod, summonMod)
|
||||
local ownerStacks = ownerMod:GetStackCount()
|
||||
if summonMod:GetStackCount() ~= ownerStacks then
|
||||
summonMod:SetStackCount(ownerStacks)
|
||||
end
|
||||
local ownerDuration = ownerMod:GetDuration()
|
||||
if ownerDuration > 0 then
|
||||
local remaining = ownerMod:GetRemainingTime()
|
||||
if remaining > 0 and math.abs(summonMod:GetRemainingTime() - remaining) > 0.15 then
|
||||
summonMod:SetDuration(remaining, false)
|
||||
end
|
||||
end
|
||||
if ownerMod:GetName() == "modifier_stacking_crit" then
|
||||
syncStackingCritState(nil, ownerMod, summonMod)
|
||||
end
|
||||
end
|
||||
--- Копирует все бафф-модификаторы владельца на призыв (стаки, длительность, криты).
|
||||
function ____exports.syncOwnerModifiersToSummon(self, owner, summon)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
if not owner or not summon or not IsValidEntity(owner) or not IsValidEntity(summon) then
|
||||
return
|
||||
end
|
||||
local ownerEligible = {}
|
||||
for ____, mod in ipairs(owner:FindAllModifiers()) do
|
||||
if isOwnerModifierEligibleForSummonSync(nil, mod) then
|
||||
ownerEligible[#ownerEligible + 1] = mod
|
||||
end
|
||||
end
|
||||
local ownerGroups = groupModifiersByName(nil, ownerEligible)
|
||||
local summonGroups = groupModifiersByName(
|
||||
nil,
|
||||
summon:FindAllModifiers()
|
||||
)
|
||||
for name in pairs(summonGroups) do
|
||||
do
|
||||
if SUMMON_ONLY_MODIFIER_NAMES[name] then
|
||||
goto __continue21
|
||||
end
|
||||
if not ownerGroups[name] then
|
||||
for ____, mod in ipairs(summonGroups[name]) do
|
||||
mod:Destroy()
|
||||
end
|
||||
end
|
||||
end
|
||||
::__continue21::
|
||||
end
|
||||
for name in pairs(ownerGroups) do
|
||||
local ownerInstances = ownerGroups[name]
|
||||
local summonInstances = __TS__ArrayFilter(
|
||||
summon:FindAllModifiers(),
|
||||
function(____, mod) return mod:GetName() == name end
|
||||
)
|
||||
while #summonInstances < #ownerInstances do
|
||||
local source = ownerInstances[#summonInstances + 1]
|
||||
local caster = source:GetCaster() or owner
|
||||
local ability = source:GetAbility() or nil
|
||||
local duration = source:GetDuration() > 0 and source:GetRemainingTime() or -1
|
||||
local added = summon:AddNewModifier(caster, ability, name, {duration = duration})
|
||||
if not added then
|
||||
break
|
||||
end
|
||||
summonInstances[#summonInstances + 1] = added
|
||||
end
|
||||
while #summonInstances > #ownerInstances do
|
||||
local extra = table.remove(summonInstances)
|
||||
if extra ~= nil then
|
||||
extra:Destroy()
|
||||
end
|
||||
end
|
||||
do
|
||||
local i = 0
|
||||
while i < #ownerInstances do
|
||||
syncModifierInstance(nil, ownerInstances[i + 1], summonInstances[i + 1])
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return ____exports
|
||||
@@ -0,0 +1,123 @@
|
||||
--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
|
||||
local ____exports = {}
|
||||
function ____exports.SetGoldUsually(self, unit)
|
||||
local difficulty_multiplier = 1
|
||||
local baseMinGold = unit:GetMinimumGoldBounty()
|
||||
local baseMaxGold = unit:GetMaximumGoldBounty()
|
||||
local expirience = unit:GetDeathXP()
|
||||
local expirience_full = math.floor(expirience * difficulty_multiplier)
|
||||
local minGold = math.floor(baseMinGold * difficulty_multiplier)
|
||||
local maxGold = math.floor(baseMaxGold * difficulty_multiplier)
|
||||
unit:SetDeathXP(expirience_full)
|
||||
unit:SetMinimumGoldBounty(minGold)
|
||||
unit:SetMaximumGoldBounty(maxGold)
|
||||
end
|
||||
function ____exports.GiveGoldPlayers(self, amount)
|
||||
local players = PlayerResource:GetPlayerCountForTeam(DOTA_TEAM_GOODGUYS)
|
||||
do
|
||||
local i = 0
|
||||
while i < players do
|
||||
local player = PlayerResource:GetPlayer(i)
|
||||
if player then
|
||||
local hero = player:GetAssignedHero()
|
||||
if hero ~= nil and hero ~= nil then
|
||||
hero:ModifyGold(amount, true, DOTA_ModifyGold_Unspecified)
|
||||
end
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
_G.HasShard = function(____, unit)
|
||||
if unit:HasModifier("modifier_item_aghanims_shard") then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
_G.HasScepter = function(____, unit)
|
||||
if not unit or unit:IsNull() then
|
||||
return false
|
||||
end
|
||||
if unit:HasModifier("modifier_item_aghanims_scepter") then
|
||||
return true
|
||||
end
|
||||
if unit:HasModifier("modifier_item_ultimate_scepter") then
|
||||
return true
|
||||
end
|
||||
if unit:HasModifier("modifier_item_ultimate_scepter_consumed") then
|
||||
return true
|
||||
end
|
||||
return unit:HasScepter()
|
||||
end
|
||||
_G.HasTalent = function(____, unit, talentName)
|
||||
local talent = unit:FindAbilityByName(talentName)
|
||||
return talent ~= nil and talent:GetLevel() > 0
|
||||
end
|
||||
____exports.SpawnUnitsAndAttack = function(____, name, location, count, data, team)
|
||||
local ____data_0 = data
|
||||
local attackPoint = ____data_0.attackPoint
|
||||
local target = ____data_0.target
|
||||
local units = {}
|
||||
do
|
||||
local index = 0
|
||||
while index < count do
|
||||
local unit = CreateUnitByName(
|
||||
name,
|
||||
location,
|
||||
true,
|
||||
nil,
|
||||
nil,
|
||||
team
|
||||
)
|
||||
units[#units + 1] = unit
|
||||
if attackPoint then
|
||||
Timers:CreateTimer(
|
||||
0.1,
|
||||
function()
|
||||
unit:Stop()
|
||||
ExecuteOrderFromTable({
|
||||
UnitIndex = unit:entindex(),
|
||||
Queue = false,
|
||||
OrderType = DOTA_UNIT_ORDER_ATTACK_MOVE,
|
||||
Position = attackPoint
|
||||
})
|
||||
end
|
||||
)
|
||||
elseif target then
|
||||
unit:SetContextThink(
|
||||
unit:GetUnitName(),
|
||||
function()
|
||||
ExecuteOrderFromTable({
|
||||
UnitIndex = unit:entindex(),
|
||||
OrderType = DOTA_UNIT_ORDER_ATTACK_TARGET,
|
||||
TargetIndex = target:entindex()
|
||||
})
|
||||
return 0.25
|
||||
end,
|
||||
0.2
|
||||
)
|
||||
end
|
||||
index = index + 1
|
||||
end
|
||||
end
|
||||
return units
|
||||
end
|
||||
____exports.GetAllHeroes = function()
|
||||
local heroes = {}
|
||||
do
|
||||
local index = 0
|
||||
while index < PlayerResource:GetPlayerCount() do
|
||||
do
|
||||
local hero = PlayerResource:GetSelectedHeroEntity(index)
|
||||
if not hero then
|
||||
goto __continue22
|
||||
end
|
||||
heroes[#heroes + 1] = hero
|
||||
end
|
||||
::__continue22::
|
||||
index = index + 1
|
||||
end
|
||||
end
|
||||
return heroes
|
||||
end
|
||||
return ____exports
|
||||
@@ -0,0 +1,147 @@
|
||||
--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
|
||||
local ____exports = {}
|
||||
local manageVampirismModifier
|
||||
require("lib.dota_ts_adapter")
|
||||
local ____modifier_vampirism = require("abilities.modifiers.modifier_vampirism")
|
||||
local modifier_vampirism = ____modifier_vampirism.modifier_vampirism
|
||||
function ____exports.getPhysicalVampirism(self, hero)
|
||||
if not hero or not IsValidEntity(hero) then
|
||||
return 0
|
||||
end
|
||||
local playerId = hero:GetPlayerOwnerID()
|
||||
if playerId == nil or playerId == nil or playerId < 0 then
|
||||
return 0
|
||||
end
|
||||
local key = "physical_vampirism_" .. tostring(playerId)
|
||||
local data = CustomNetTables:GetTableValue("custom_stats", key)
|
||||
local ____data_0
|
||||
if data then
|
||||
____data_0 = data.value
|
||||
else
|
||||
____data_0 = 0
|
||||
end
|
||||
local value = ____data_0
|
||||
return value
|
||||
end
|
||||
function ____exports.getMagicalVampirism(self, hero)
|
||||
if not hero or not IsValidEntity(hero) then
|
||||
return 0
|
||||
end
|
||||
local playerId = hero:GetPlayerOwnerID()
|
||||
if playerId == nil or playerId == nil or playerId < 0 then
|
||||
return 0
|
||||
end
|
||||
local key = "magical_vampirism_" .. tostring(playerId)
|
||||
local data = CustomNetTables:GetTableValue("custom_stats", key)
|
||||
local ____data_1
|
||||
if data then
|
||||
____data_1 = data.value
|
||||
else
|
||||
____data_1 = 0
|
||||
end
|
||||
local value = ____data_1
|
||||
return value
|
||||
end
|
||||
function manageVampirismModifier(self, hero)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local physicalVampirism = ____exports.getPhysicalVampirism(nil, hero)
|
||||
local magicalVampirism = ____exports.getMagicalVampirism(nil, hero)
|
||||
local hasVampirism = physicalVampirism > 0 or magicalVampirism > 0
|
||||
local hasModifier = hero:HasModifier("modifier_vampirism")
|
||||
if hasVampirism and not hasModifier then
|
||||
hero:AddNewModifier(
|
||||
hero,
|
||||
getModifierSourceAbility(nil, hero),
|
||||
modifier_vampirism.name,
|
||||
{}
|
||||
)
|
||||
elseif not hasVampirism and hasModifier then
|
||||
hero:RemoveModifierByName("modifier_vampirism")
|
||||
end
|
||||
end
|
||||
--- Партикл эффекта вампиризма (modifier без своего Precache в KV)
|
||||
function ____exports.precacheVampirismParticle(self, context)
|
||||
PrecacheResource("particle", "particles/units/heroes/hero_bloodseeker/bloodseeker_bloodbath.vpcf", context)
|
||||
end
|
||||
local playerIdToPhysicalVampirism = {}
|
||||
local playerIdToMagicalVampirism = {}
|
||||
if not playerIdToPhysicalVampirism then
|
||||
playerIdToPhysicalVampirism = {}
|
||||
end
|
||||
if not playerIdToMagicalVampirism then
|
||||
playerIdToMagicalVampirism = {}
|
||||
end
|
||||
local function getPlayerKey(self, hero, ____type)
|
||||
local playerId = hero:GetPlayerOwnerID()
|
||||
if playerId == nil or playerId == nil or playerId < 0 then
|
||||
return nil
|
||||
end
|
||||
return (____type .. "_") .. tostring(playerId)
|
||||
end
|
||||
function ____exports.setPhysicalVampirism(self, hero, vampirism)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local playerId = hero:GetPlayerOwnerID()
|
||||
local key = "physical_vampirism_" .. tostring(playerId)
|
||||
CustomNetTables:SetTableValue("custom_stats", key, {value = vampirism})
|
||||
end
|
||||
function ____exports.addPhysicalVampirism(self, hero, delta)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local current = ____exports.getPhysicalVampirism(nil, hero)
|
||||
local newValue = current + delta
|
||||
____exports.setPhysicalVampirism(nil, hero, newValue)
|
||||
manageVampirismModifier(nil, hero)
|
||||
end
|
||||
function ____exports.reducePhysicalVampirism(self, hero, delta)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local current = ____exports.getPhysicalVampirism(nil, hero)
|
||||
local newValue = math.max(0, current - delta)
|
||||
____exports.setPhysicalVampirism(nil, hero, newValue)
|
||||
manageVampirismModifier(nil, hero)
|
||||
end
|
||||
function ____exports.setMagicalVampirism(self, hero, vampirism)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local playerId = hero:GetPlayerOwnerID()
|
||||
if playerId == nil or playerId == nil or playerId < 0 then
|
||||
return
|
||||
end
|
||||
local key = "magical_vampirism_" .. tostring(playerId)
|
||||
CustomNetTables:SetTableValue("custom_stats", key, {value = vampirism})
|
||||
end
|
||||
function ____exports.addMagicalVampirism(self, hero, delta)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local current = ____exports.getMagicalVampirism(nil, hero)
|
||||
local newValue = current + delta
|
||||
____exports.setMagicalVampirism(nil, hero, newValue)
|
||||
manageVampirismModifier(nil, hero)
|
||||
end
|
||||
function ____exports.reduceMagicalVampirism(self, hero, delta)
|
||||
if not IsServer() then
|
||||
return
|
||||
end
|
||||
local current = ____exports.getMagicalVampirism(nil, hero)
|
||||
local newValue = math.max(0, current - delta)
|
||||
____exports.setMagicalVampirism(nil, hero, newValue)
|
||||
manageVampirismModifier(nil, hero)
|
||||
end
|
||||
local g = _G
|
||||
g.setPhysicalVampirism = ____exports.setPhysicalVampirism
|
||||
g.addPhysicalVampirism = ____exports.addPhysicalVampirism
|
||||
g.reducePhysicalVampirism = ____exports.reducePhysicalVampirism
|
||||
g.getPhysicalVampirism = ____exports.getPhysicalVampirism
|
||||
g.setMagicalVampirism = ____exports.setMagicalVampirism
|
||||
g.addMagicalVampirism = ____exports.addMagicalVampirism
|
||||
g.reduceMagicalVampirism = ____exports.reduceMagicalVampirism
|
||||
g.getMagicalVampirism = ____exports.getMagicalVampirism
|
||||
return ____exports
|
||||
@@ -0,0 +1,23 @@
|
||||
--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
|
||||
local ____exports = {}
|
||||
local g = _G
|
||||
local function toVectorWSImpl(_self, v)
|
||||
local ctor = g.VectorWS
|
||||
if type(ctor) == "function" then
|
||||
return ctor(v.x, v.y, v.z)
|
||||
end
|
||||
return v
|
||||
end
|
||||
--- Поворот точки вокруг origin только по yaw (град), эквивалент RotatePosition(origin, QAngle(0, yaw, 0), point) без нативного API.
|
||||
local function rotatePositionYawImpl(_self, origin, yawDegrees, point)
|
||||
local rad = yawDegrees * (math.pi / 180)
|
||||
local off = point - origin
|
||||
local c = math.cos(rad)
|
||||
local s = math.sin(rad)
|
||||
local rx = off.x * c - off.y * s
|
||||
local ry = off.x * s + off.y * c
|
||||
return Vector(origin.x + rx, origin.y + ry, origin.z + off.z)
|
||||
end
|
||||
g.toVectorWS = toVectorWSImpl
|
||||
g.rotatePositionYaw = rotatePositionYawImpl
|
||||
return ____exports
|
||||
Reference in New Issue
Block a user