513 lines
18 KiB
Lua
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
|