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