Files
2026-05-29 15:11:31 +07:00

513 lines
18 KiB
Lua

local ____lualib = require("lualib_bundle")
local __TS__Class = ____lualib.__TS__Class
local Map = ____lualib.Map
local __TS__New = ____lualib.__TS__New
local __TS__Iterator = ____lualib.__TS__Iterator
local __TS__ArrayIncludes = ____lualib.__TS__ArrayIncludes
local ____exports = {}
local ____WaveManager = require("WaveManager")
local WaveManager = ____WaveManager.WaveManager
local ____blackshop = require("blackshop")
local BlackShop = ____blackshop.BlackShop
local ____custom_game_events = require("custom_game_events")
local broadcastZiCyclePhase = ____custom_game_events.broadcastZiCyclePhase
local ____CardSystem = require("cards.CardSystem")
local shouldPlayerSkipDawnCardSelection = ____CardSystem.shouldPlayerSkipDawnCardSelection
local ShowCardSelectionToPlayer = ____CardSystem.ShowCardSelectionToPlayer
local ____card_22 = require("cards.examples.card_22")
local notifyCard22MorningStarted = ____card_22.notifyCard22MorningStarted
local notifyCard22NightStarted = ____card_22.notifyCard22NightStarted
local ____card_38 = require("cards.examples.card_38")
local notifyCard38MorningStarted = ____card_38.notifyCard38MorningStarted
local ____card_45 = require("cards.examples.card_45")
local notifyCard45MorningStarted = ____card_45.notifyCard45MorningStarted
local ____card_49 = require("cards.examples.card_49")
local notifyCard49MorningStarted = ____card_49.notifyCard49MorningStarted
local ____card_51 = require("cards.examples.card_51")
local notifyCard51MorningStarted = ____card_51.notifyCard51MorningStarted
local ____card_63 = require("cards.examples.card_63")
local notifyCard63MorningStarted = ____card_63.notifyCard63MorningStarted
local notifyCard63NightStarted = ____card_63.notifyCard63NightStarted
local ____card_52_effects = require("cards.card_52_effects")
local CARD_52_MIN_NIGHT_DURATION_SEC = ____card_52_effects.CARD_52_MIN_NIGHT_DURATION_SEC
local getNightDurationReductionSecFromCard52 = ____card_52_effects.getNightDurationReductionSecFromCard52
local ____modifier_stats_multiplier = require("modifiers.modifier_stats_multiplier")
local invalidateStatsMultiplierSumCache = ____modifier_stats_multiplier.invalidateStatsMultiplierSumCache
--- Номер финальной ночи (совпадает с WAVE_SETTINGS в WaveManager).
local FINAL_NIGHT_NUMBER = 7
--- Сколько секунд длится эта ночь на таймере UI и до утра.
local FINAL_NIGHT_DURATION_SEC = 9999
--- Огонь на зомби в дневной фазе (Crownfall).
local DAYTIME_ZOMBIE_BURN_FIRE_PFX = "particles/econ/items/wraith_king/wraith_king_ti6_bracer/wraith_king_ti6_hellfireblast_debuff_fire.vpcf"
local ENABLE_DAY_NIGHT_DEBUG_LOG = false
____exports.DayNightCycleManager = __TS__Class()
local DayNightCycleManager = ____exports.DayNightCycleManager
DayNightCycleManager.name = "DayNightCycleManager"
DayNightCycleManager.____file_path = "scripts/vscripts/DayNightCycleManager.lua"
function DayNightCycleManager.prototype.____constructor(self)
self.dayNightCycleDuration = 300
self.isDayTime = false
self.dayNightTimer = "DayNightTimer"
self.dayDuration = 300
self.nightDuration = 300
self.nextDayDuration = 300
self.nextNightDuration = 300
self.timeLeft = 0
self.morningSequence = 0
self.cutsceneDayNightFreezeDepth = 0
self.freezeTimeLeftDecrement = false
self.zombieUnitNames = {
"npc_wave_zombie",
"npc_wave_dead_skeleton",
"npc_wave_half_zombie",
"npc_wave_bearst_zombie",
"npc_wave_toxin_zombie",
"npc_wave_skeleton_zombie",
"npc_wave_skeleton_zombie_half",
"npc_wave_dead_skeleton_archer"
}
self.zombieDayBurnFireByEnt = __TS__New(Map)
end
function DayNightCycleManager.prototype.clearAllDayBurnFireParticles(self)
for ____, ____value in __TS__Iterator(self.zombieDayBurnFireByEnt) do
local pfx = ____value[2]
ParticleManager:DestroyParticle(pfx, false)
ParticleManager:ReleaseParticleIndex(pfx)
end
self.zombieDayBurnFireByEnt:clear()
end
function DayNightCycleManager.prototype.pruneDayBurnFireParticles(self)
local toRemove = {}
for ____, ____value in __TS__Iterator(self.zombieDayBurnFireByEnt) do
local ei = ____value[1]
local pfx = ____value[2]
local u = EntIndexToHScript(ei)
if not u or not IsValidEntity(u) or not u:IsAlive() then
ParticleManager:DestroyParticle(pfx, false)
ParticleManager:ReleaseParticleIndex(pfx)
toRemove[#toRemove + 1] = ei
end
end
for ____, ei in ipairs(toRemove) do
self.zombieDayBurnFireByEnt:delete(ei)
end
end
function DayNightCycleManager.prototype.ensureDayBurnFireOnZombie(self, zombieNPC)
local ei = zombieNPC:entindex()
if self.zombieDayBurnFireByEnt:has(ei) then
return
end
local pfx = ParticleManager:CreateParticle(DAYTIME_ZOMBIE_BURN_FIRE_PFX, PATTACH_ABSORIGIN_FOLLOW, zombieNPC)
self.zombieDayBurnFireByEnt:set(ei, pfx)
end
function DayNightCycleManager.prototype.applyDaytimeZombieBurnDamage(self)
if GetMapName() ~= "invasion" then
return
end
self:pruneDayBurnFireParticles()
for ____, zombieNPC in ipairs(WaveManager:getInstance():getAliveSpawnedWaveUnits()) do
do
if not __TS__ArrayIncludes(
self.zombieUnitNames,
zombieNPC:GetUnitName()
) then
goto __continue16
end
ApplyDamage({
victim = zombieNPC,
attacker = zombieNPC,
damage = 50 + zombieNPC:GetMaxHealth() * 0.02,
damage_type = DAMAGE_TYPE_PURE
})
self:ensureDayBurnFireOnZombie(zombieNPC)
end
::__continue16::
end
end
function DayNightCycleManager.getInstance(self)
if not ____exports.DayNightCycleManager.instance then
____exports.DayNightCycleManager.instance = __TS__New(____exports.DayNightCycleManager)
end
return ____exports.DayNightCycleManager.instance
end
function DayNightCycleManager.prototype.Initialize(self)
self.isDayTime = false
self.dayDuration = 300
self.nightDuration = 300
self.nextDayDuration = 300
self.nextNightDuration = 300
self:StartDayNightCycle()
end
function DayNightCycleManager.prototype.StartDayNightCycle(self)
self.timeLeft = self.isDayTime and self.dayDuration or self.nightDuration
Timers:CreateTimer(
"TimeLeftTimer",
{
callback = function()
if not self.freezeTimeLeftDecrement then
self.timeLeft = self.timeLeft - 1
end
do
pcall(function()
CustomGameEventManager:Send_ServerToAllClients("day_night_timer_update", {isDay = self.isDayTime, timeLeft = self.timeLeft})
end)
end
return 1
end,
endTime = 0
}
)
Timers:CreateTimer(
self.dayNightTimer,
{
callback = function() return self:SwitchDayNight() end,
endTime = 0
}
)
end
function DayNightCycleManager.prototype.runMorningSequenceAndPostNightCardSelection(self)
self.morningSequence = self.morningSequence + 1
local nightForCards = WaveManager:getInstance():GetCurrentNight()
notifyCard22MorningStarted(nil, nightForCards, self.morningSequence)
notifyCard38MorningStarted(nil, nightForCards, self.morningSequence)
notifyCard45MorningStarted(nil, self.morningSequence)
notifyCard49MorningStarted(nil, self.morningSequence)
notifyCard63MorningStarted(nil, self.morningSequence)
notifyCard51MorningStarted(nil, self.morningSequence)
broadcastZiCyclePhase(nil, {isDay = true, morningSequence = self.morningSequence, nightIndex = nightForCards})
self:ShowCardSelectionAfterNight()
end
function DayNightCycleManager.prototype.SwitchDayNight(self)
self.isDayTime = not self.isDayTime
BlackShop:getInstance():RefreshShop()
self:refreshHeroStatsAfterDayNightSwitch()
if self.isDayTime then
GameRules:SetTimeOfDay(0.75)
self.dayDuration = self.nextDayDuration
self.timeLeft = self.dayDuration
self:runMorningSequenceAndPostNightCardSelection()
self:clearAllDayBurnFireParticles()
Timers:CreateTimer(
"ZombieBurnTimer",
{
callback = function()
if self.isDayTime then
self:applyDaytimeZombieBurnDamage()
return 1
end
return nil
end,
endTime = 0
}
)
return self.dayDuration
else
broadcastZiCyclePhase(
nil,
{
isDay = false,
morningSequence = self.morningSequence,
nightIndex = WaveManager:getInstance():GetCurrentNight()
}
)
GameRules:SetTimeOfDay(0.25)
local card52NightReduce = getNightDurationReductionSecFromCard52(nil)
self.nightDuration = math.max(CARD_52_MIN_NIGHT_DURATION_SEC, self.nextNightDuration - card52NightReduce)
self.timeLeft = self.nightDuration
notifyCard63NightStarted(nil)
notifyCard22NightStarted(nil)
Timers:RemoveTimer("ZombieBurnTimer")
self:clearAllDayBurnFireParticles()
local waveManager = WaveManager:getInstance()
Timers:CreateTimer(
0.1,
function()
waveManager:SetCurrentNight(waveManager:GetCurrentNight() + 1)
local night = waveManager:GetCurrentNight()
if night == FINAL_NIGHT_NUMBER then
self:SetNightDuration(FINAL_NIGHT_DURATION_SEC)
self.nightDuration = FINAL_NIGHT_DURATION_SEC
self:SetTimeLeft(FINAL_NIGHT_DURATION_SEC)
end
waveManager:SpawnNextWave()
CustomGameEventManager:Send_ServerToAllClients(
"wave_started",
{night = waveManager:GetCurrentNight()}
)
return nil
end
)
return self.nightDuration
end
end
function DayNightCycleManager.prototype.refreshHeroStatsAfterDayNightSwitch(self)
local n = 0
do
local i = 0
while i < DOTA_MAX_PLAYERS do
local hero = PlayerResource:GetSelectedHeroEntity(i)
if hero and IsValidEntity(hero) and hero:IsRealHero() then
invalidateStatsMultiplierSumCache(nil, hero)
hero:CalculateStatBonus(true)
n = n + 1
end
i = i + 1
end
end
if ENABLE_DAY_NIGHT_DEBUG_LOG then
print((("[DayNight] refreshHeroStatsAfterDayNightSwitch isDay=" .. tostring(self.isDayTime)) .. " героев=") .. tostring(n))
end
end
function DayNightCycleManager.prototype.SetDayNightCycleDuration(self, seconds)
if seconds < 60 then
seconds = 60
end
self.dayNightCycleDuration = seconds
Timers:RemoveTimer(self.dayNightTimer)
Timers:RemoveTimer("TimeLeftTimer")
self:StartDayNightCycle()
end
function DayNightCycleManager.prototype.GetDayNightCycleDuration(self)
return self.dayNightCycleDuration
end
function DayNightCycleManager.prototype.IsDaytime(self)
return self.isDayTime
end
function DayNightCycleManager.prototype.CanVoteSkipToNight(self)
if GameRules:State_Get() ~= DOTA_GAMERULES_STATE_GAME_IN_PROGRESS then
return false
end
if not self.isDayTime then
return false
end
if self.cutsceneDayNightFreezeDepth > 0 then
return false
end
if self.freezeTimeLeftDecrement then
return false
end
return true
end
function DayNightCycleManager.prototype.SetDayDuration(self, seconds)
if seconds < 60 then
seconds = 60
end
self.nextDayDuration = seconds
end
function DayNightCycleManager.prototype.adjustNextDayDurationBy(self, deltaSec)
if deltaSec == 0 then
return
end
self:SetDayDuration(self.nextDayDuration + deltaSec)
end
function DayNightCycleManager.prototype.SetNightDuration(self, seconds)
if seconds < 60 then
seconds = 60
end
self.nextNightDuration = seconds
end
function DayNightCycleManager.prototype.GetDayDuration(self)
return self.dayDuration
end
function DayNightCycleManager.prototype.GetNightDuration(self)
return self.nightDuration
end
function DayNightCycleManager.prototype.GetTimeLeft(self)
return self.timeLeft
end
function DayNightCycleManager.prototype.onCutsceneStarted(self)
if GameRules:State_Get() < DOTA_GAMERULES_STATE_GAME_IN_PROGRESS then
return
end
self.cutsceneDayNightFreezeDepth = self.cutsceneDayNightFreezeDepth + 1
if self.cutsceneDayNightFreezeDepth ~= 1 then
return
end
Timers:RemoveTimer(self.dayNightTimer)
self.freezeTimeLeftDecrement = true
GameRules:SetTimeOfDay(0.25)
end
function DayNightCycleManager.prototype.onCutsceneEnded(self)
self.cutsceneDayNightFreezeDepth = self.cutsceneDayNightFreezeDepth - 1
if self.cutsceneDayNightFreezeDepth < 0 then
self.cutsceneDayNightFreezeDepth = 0
end
if self.cutsceneDayNightFreezeDepth > 0 then
return
end
self.freezeTimeLeftDecrement = false
if GameRules:State_Get() < DOTA_GAMERULES_STATE_GAME_IN_PROGRESS then
return
end
GameRules:SetTimeOfDay(self.isDayTime and 0.75 or 0.25)
Timers:RemoveTimer(self.dayNightTimer)
Timers:CreateTimer(
self.dayNightTimer,
{
callback = function() return self:SwitchDayNight() end,
endTime = self.timeLeft
}
)
do
pcall(function()
CustomGameEventManager:Send_ServerToAllClients("day_night_timer_update", {isDay = self.isDayTime, timeLeft = self.timeLeft})
end)
end
end
function DayNightCycleManager.prototype.SyncStateToPlayer(self, playerId)
do
local ____try, ____hasReturned, ____returnValue = pcall(function()
local player = PlayerResource:GetPlayer(playerId)
if not player then
return true
end
CustomGameEventManager:Send_ServerToPlayer(player, "day_night_timer_update", {isDay = self.isDayTime, timeLeft = self.timeLeft})
end)
if ____try and ____hasReturned then
return ____returnValue
end
end
end
function DayNightCycleManager.prototype.AdjustTimeLeft(self, delta)
local minTime = 1
local maxTime = 356400
local newTimeLeft = self.timeLeft + delta
if newTimeLeft < minTime then
newTimeLeft = minTime
elseif newTimeLeft > maxTime then
newTimeLeft = maxTime
end
self.timeLeft = newTimeLeft
Timers:RemoveTimer(self.dayNightTimer)
Timers:CreateTimer(
self.dayNightTimer,
{
callback = function() return self:SwitchDayNight() end,
endTime = self.timeLeft
}
)
end
function DayNightCycleManager.prototype.SetTimeLeft(self, seconds)
local minTime = 1
local maxTime = 356400
local newTimeLeft = math.floor(seconds)
if newTimeLeft < minTime then
newTimeLeft = minTime
elseif newTimeLeft > maxTime then
newTimeLeft = maxTime
end
self.timeLeft = newTimeLeft
Timers:RemoveTimer(self.dayNightTimer)
Timers:CreateTimer(
self.dayNightTimer,
{
callback = function() return self:SwitchDayNight() end,
endTime = self.timeLeft
}
)
end
function DayNightCycleManager.prototype.TryInstantSwitchDayToNightFromVote(self)
if GameRules:State_Get() ~= DOTA_GAMERULES_STATE_GAME_IN_PROGRESS then
return false
end
if not self.isDayTime then
return false
end
if self.cutsceneDayNightFreezeDepth > 0 or self.freezeTimeLeftDecrement then
return false
end
Timers:RemoveTimer(self.dayNightTimer)
self:SwitchDayNight()
Timers:CreateTimer(
self.dayNightTimer,
{
callback = function() return self:SwitchDayNight() end,
endTime = self.timeLeft
}
)
do
pcall(function()
CustomGameEventManager:Send_ServerToAllClients("day_night_timer_update", {isDay = self.isDayTime, timeLeft = self.timeLeft})
end)
end
return true
end
function DayNightCycleManager.prototype.ForceNightIn(self, seconds)
if not self.isDayTime then
return
end
local delay = math.max(
1,
math.floor(seconds)
)
self.timeLeft = delay
Timers:RemoveTimer(self.dayNightTimer)
Timers:CreateTimer(
self.dayNightTimer,
{
callback = function() return self:SwitchDayNight() end,
endTime = delay
}
)
end
function DayNightCycleManager.prototype.ForceDay(self)
if self.isDayTime then
return
end
Timers:RemoveTimer(self.dayNightTimer)
Timers:RemoveTimer("ZombieBurnTimer")
self:clearAllDayBurnFireParticles()
self.isDayTime = true
BlackShop:getInstance():RefreshShop()
self:refreshHeroStatsAfterDayNightSwitch()
GameRules:SetTimeOfDay(0.75)
self.dayDuration = self.nextDayDuration
self.timeLeft = self.dayDuration
self:runMorningSequenceAndPostNightCardSelection()
Timers:CreateTimer(
"ZombieBurnTimer",
{
callback = function()
if self.isDayTime then
self:applyDaytimeZombieBurnDamage()
return 1
end
return nil
end,
endTime = 0
}
)
Timers:CreateTimer(
self.dayNightTimer,
{
callback = function() return self:SwitchDayNight() end,
endTime = self.dayDuration
}
)
end
function DayNightCycleManager.prototype.ShowCardSelectionAfterNight(self)
do
local playerId = 0
while playerId < PlayerResource:GetPlayerCount() do
do
local pid = playerId
local player = PlayerResource:GetPlayer(pid)
if not player then
goto __continue92
end
local hero = player:GetAssignedHero()
if not hero or not hero:IsAlive() then
goto __continue92
end
if shouldPlayerSkipDawnCardSelection(nil, pid) then
goto __continue92
end
ShowCardSelectionToPlayer(nil, pid, 3, "night_completion")
end
::__continue92::
playerId = playerId + 1
end
end
end
return ____exports