local ____lualib = require("lualib_bundle") local __TS__Class = ____lualib.__TS__Class local Set = ____lualib.Set local __TS__New = ____lualib.__TS__New local __TS__ArrayMap = ____lualib.__TS__ArrayMap local __TS__StringTrim = ____lualib.__TS__StringTrim local __TS__Decorate = ____lualib.__TS__Decorate local ____exports = {} local ____DayNightCycleManager = require("DayNightCycleManager") local DayNightCycleManager = ____DayNightCycleManager.DayNightCycleManager local ____modifier_cutscene_lock = require("modifiers.modifier_cutscene_lock") local modifier_cutscene_lock = ____modifier_cutscene_lock.modifier_cutscene_lock local ____modifier_cutscene_npc_lock = require("modifiers.modifier_cutscene_npc_lock") local modifier_cutscene_npc_lock = ____modifier_cutscene_npc_lock.modifier_cutscene_npc_lock local ____tstl_2Dutils = require("lib.tstl-utils") local reloadable = ____tstl_2Dutils.reloadable local ____player_connection_state = require("utils.player_connection_state") local DOTA_CONNECTION_STATE = ____player_connection_state.DOTA_CONNECTION_STATE local ____CutsceneFunctions = require("cutscenes.CutsceneFunctions") local finishCameraStartEndingTeleportNow = ____CutsceneFunctions.finishCameraStartEndingTeleportNow local ____CutsceneTable = require("cutscenes.CutsceneTable") local CutsceneTable = ____CutsceneTable.CutsceneTable local CUTSCENE_AUTO_SKIP_POLL_TIMER = "CutsceneDisconnectAutoSkip" ____exports.CutsceneManager = __TS__Class() local CutsceneManager = ____exports.CutsceneManager CutsceneManager.name = "CutsceneManager" CutsceneManager.____file_path = "scripts/vscripts/cutscenes/CutsceneManager.lua" function CutsceneManager.prototype.____constructor(self) self.playedSceneIds = __TS__New(Set) self.isGameEnding = false self:registerEvents() end function CutsceneManager.prototype.isPlayerOfflineForCutsceneSkip(self, playerId) if not PlayerResource:IsValidPlayerID(playerId) then return true end return PlayerResource:GetConnectionState(playerId) ~= DOTA_CONNECTION_STATE.CONNECTED end function CutsceneManager.prototype.startAutoSkipDisconnectPoll(self) Timers:RemoveTimer(CUTSCENE_AUTO_SKIP_POLL_TIMER) Timers:CreateTimer( CUTSCENE_AUTO_SKIP_POLL_TIMER, { callback = function() if not self.active then return nil end self:applyAutoSkipVotesForDisconnectedParticipants() return 0.25 end, endTime = 0 } ) end function CutsceneManager.prototype.applyAutoSkipVotesForDisconnectedParticipants(self) if not self.active then return end for ____, playerId in ipairs(self.active.participantPlayerIds) do do if self.active.votedPlayers:has(playerId) then goto __continue10 end if not self:isPlayerOfflineForCutsceneSkip(playerId) then goto __continue10 end self.active.votedPlayers:add(playerId) CustomGameEventManager:Send_ServerToAllClients("cutscene_vote_to_skip_success", {playerId = playerId}) end ::__continue10:: end self:tryFinishCutsceneIfAllParticipantsVoted() end function CutsceneManager.prototype.tryFinishCutsceneIfAllParticipantsVoted(self) if not self.active then return end if #self.active.participantPlayerIds == 0 then return end for ____, playerId in ipairs(self.active.participantPlayerIds) do if not self.active.votedPlayers:has(playerId) then return end end self:finishCutscene() end function CutsceneManager.prototype.registerEvents(self) CustomGameEventManager:RegisterListener( "end_cutscene", function() return self:finishCutscene() end ) CustomGameEventManager:RegisterListener( "cutscene_camera_change", function(_, data) return self:onCameraChange(data) end ) CustomGameEventManager:RegisterListener( "cutscene_dialog_action", function(_, data) return self:onDialogAction(data) end ) CustomGameEventManager:RegisterListener( "cutscene_dialog_function", function(_, data) return self:onDialogFunction(data) end ) CustomGameEventManager:RegisterListener( "cutscene_vote_to_skip", function(_, data) return self:onVoteToSkip(data) end ) end function CutsceneManager.prototype.notifyPlayerConnected(self, playerId) if not self.active then return end local player = PlayerResource:GetPlayer(playerId) if not player then return end local ____CustomGameEventManager_Send_ServerToPlayer_2 = CustomGameEventManager.Send_ServerToPlayer local ____opt_0 = self.active.npc ____CustomGameEventManager_Send_ServerToPlayer_2( CustomGameEventManager, player, "start_cutscene", { npc = ____opt_0 and ____opt_0:entindex(), scene = self.active.scene } ) end function CutsceneManager.prototype.startScene(self, sceneId, options) if self.active then print(((("[CutsceneManager] startScene(\"" .. sceneId) .. "\") отклонено: уже идёт \"") .. self.active.sceneId) .. "\"") return false end local scene = self:getSceneConfig(sceneId) if not scene then print(("[CutsceneManager] startScene(\"" .. sceneId) .. "\") отклонено: нет сцены в CutsceneTable") return false end if not (options and options.force) and self.playedSceneIds:has(sceneId) then print(("[CutsceneManager] startScene(\"" .. sceneId) .. "\") отклонено: уже в playedSceneIds (без force)") return false end local ____scene_npc_5 if scene.npc then ____scene_npc_5 = Entities:FindByName(nil, scene.npc) or nil else ____scene_npc_5 = nil end local npc = ____scene_npc_5 local participantPlayerIds = __TS__ArrayMap( self:getPlayers(), function(____, p) return p.playerId end ) self.active = { sceneId = sceneId, scene = scene, npc = npc, votedPlayers = __TS__New(Set), participantPlayerIds = participantPlayerIds, onEnd = options and options.onEnd } self.playedSceneIds:add(sceneId) DayNightCycleManager:getInstance():onCutsceneStarted() self:applyAutoSkipVotesForDisconnectedParticipants() self:startAutoSkipDisconnectPoll() self:applySceneState() CustomGameEventManager:Send_ServerToAllClients( "start_cutscene", { npc = npc and npc:entindex(), scene = scene } ) local ____print_14 = print local ____sceneId_13 = sceneId local ____temp_12 = options and options.force if ____temp_12 == nil then ____temp_12 = false end ____print_14((((("[CutsceneManager] startScene(\"" .. ____sceneId_13) .. "\") OK, force=") .. tostring(____temp_12)) .. ", npc=") .. (scene.npc or "none")) return true end function CutsceneManager.prototype.getSceneConfig(self, sceneId) local loaded = require("cutscenes.CutsceneTable") local ____opt_15 = loaded.CutsceneTable return ____opt_15 and ____opt_15[sceneId] or CutsceneTable[sceneId] end function CutsceneManager.prototype.startEndingCutscene(self, victory) if self.isGameEnding then return end self.isGameEnding = true self:startScene( victory and "ending_victory" or "ending_defeat", { force = true, onEnd = function() local ____GameRules_SetGameWinner_18 = GameRules.SetGameWinner local ____victory_17 if victory then ____victory_17 = DOTA_TEAM_GOODGUYS else ____victory_17 = DOTA_TEAM_BADGUYS end ____GameRules_SetGameWinner_18(GameRules, ____victory_17) end } ) end function CutsceneManager.prototype.triggerMidWaveWarning(self, night, waveIndex) if night == 1 and waveIndex == 2 then self:startScene("mid_wave_warning") end end function CutsceneManager.prototype.isCutsceneActive(self) return self.active ~= nil end function CutsceneManager.prototype.resolveInitialCameraTarget(self, scene, hero, npc) local d1 = scene.dialogs[1] local cam = d1 and d1.camera if cam and cam ~= "default" and type(cam) == "table" and cam.name ~= nil and string.lower(__TS__StringTrim(cam.name)) == "hero" then return hero end return npc end function CutsceneManager.prototype.applySceneState(self) if not self.active then return end local ____self_active_21 = self.active local scene = ____self_active_21.scene local npc = ____self_active_21.npc local players = self:getPlayers() local npcPos = npc and npc:GetAbsOrigin() local forward = npc and npc:GetForwardVector() local npcAngles = npc and npc:GetAngles() for ____, ____value in ipairs(players) do local playerId = ____value.playerId local hero = ____value.hero do if not hero then goto __continue45 end local camTarget = self:resolveInitialCameraTarget(scene, hero, npc) if camTarget ~= nil then PlayerResource:SetCameraTarget(playerId, camTarget) end hero:AddNewModifier( hero, getModifierSourceAbility(nil, hero), modifier_cutscene_lock.name, {} ) hero:SetControllableByPlayer(playerId, false) hero:Stop() if not scene.disableTeleport and npcPos and forward and npcAngles then local distance = 260 + playerId * 15 hero:SetAngles(0, npcAngles.y + 180, 0) FindClearSpaceForUnit(hero, npcPos + forward * distance, true) end end ::__continue45:: end if scene.lockNpc and npc then npc:AddNewModifier( npc, getModifierSourceAbility(nil, npc), modifier_cutscene_npc_lock.name, {} ) end end function CutsceneManager.prototype.clearSceneState(self) if not self.active then return end local players = self:getPlayers() for ____, ____value in ipairs(players) do local playerId = ____value.playerId local hero = ____value.hero do if not hero then goto __continue53 end PlayerResource:SetCameraTarget(playerId, nil) CenterCameraOnUnit(playerId, hero) hero:SetControllableByPlayer(playerId, true) hero:RemoveModifierByName(modifier_cutscene_lock.name) end ::__continue53:: end if self.active.scene.lockNpc and self.active.npc then self.active.npc:RemoveModifierByName(modifier_cutscene_npc_lock.name) end end function CutsceneManager.prototype.finishCutscene(self) if not self.active then return end local finished = self.active Timers:RemoveTimer(CUTSCENE_AUTO_SKIP_POLL_TIMER) self:clearSceneState() self.active = nil DayNightCycleManager:getInstance():onCutsceneEnded() do pcall(function() CustomGameEventManager:Send_ServerToAllClients("cutscene_closed", {}) end) end if finished.sceneId == "camera_start_ending" then finishCameraStartEndingTeleportNow(nil) end local ____this_29 ____this_29 = finished.scene local ____opt_28 = ____this_29.onSuccess if ____opt_28 ~= nil then ____opt_28(____this_29) end local ____opt_30 = finished.onEnd if ____opt_30 ~= nil then ____opt_30(finished) end end function CutsceneManager.prototype.onDialogAction(self, data) local action = data.dialog.action if not action then return end local unit = self:resolveDialogNpc(data.dialog.npc) if not unit then return end unit:StartGestureWithPlaybackRate(action.name, action.rate) end function CutsceneManager.prototype.onDialogFunction(self, data) if not self.active then return end local ____opt_34 = self.active.scene.dialogs[data.dialogId] local ____opt_32 = ____opt_34 and ____opt_34.onStart if ____opt_32 ~= nil then ____opt_32(____opt_34) end end function CutsceneManager.prototype.onCameraChange(self, data) if not self.active then return end local camera = data.dialog.camera if not camera then return end local players = self:getPlayers() if camera == "default" then for ____, ____value in ipairs(players) do local playerId = ____value.playerId PlayerResource:SetCameraTarget(playerId, self.active.npc) end return end if string.lower(__TS__StringTrim(camera.name)) == "hero" then for ____, ____value in ipairs(players) do local playerId = ____value.playerId local hero = ____value.hero do if not hero then goto __continue73 end PlayerResource:SetCameraTarget(playerId, hero) end ::__continue73:: end return end local target = Entities:FindByName(nil, camera.name) if not target and self.active.npc ~= nil and IsValidEntity(self.active.npc) then target = self.active.npc end if not target then return end for ____, ____value in ipairs(players) do local playerId = ____value.playerId PlayerResource:SetCameraTarget(playerId, target) end if camera.vision and data.visionDuration and data.visionDuration > 0 then local team = DOTA_TEAM_GOODGUYS local ____opt_36 = players[1] local hero = ____opt_36 and ____opt_36.hero if hero then team = hero:GetTeamNumber() end local ____opt_38 = Entities:FindByName(nil, camera.vision.name) local visionPoint = ____opt_38 and ____opt_38:GetAbsOrigin() or target:GetAbsOrigin() AddFOWViewer( team, visionPoint, camera.vision.radius, data.visionDuration, false ) end end function CutsceneManager.prototype.onVoteToSkip(self, data) if not self.active then return end if self.active.votedPlayers:has(data.playerId) then return end self.active.votedPlayers:add(data.playerId) CustomGameEventManager:Send_ServerToAllClients("cutscene_vote_to_skip_success", {playerId = data.playerId}) self:applyAutoSkipVotesForDisconnectedParticipants() self:tryFinishCutsceneIfAllParticipantsVoted() end function CutsceneManager.prototype.resolveDialogNpc(self, name) if name then return Entities:FindByName(nil, name) or nil end local ____opt_40 = self.active return ____opt_40 and ____opt_40.npc end function CutsceneManager.prototype.getPlayers(self) local players = {} do local playerId = 0 while playerId < DOTA_MAX_PLAYERS do do if not PlayerResource:IsValidPlayerID(playerId) then goto __continue88 end local player = PlayerResource:GetPlayer(playerId) if not player then goto __continue88 end players[#players + 1] = { playerId = playerId, hero = PlayerResource:GetSelectedHeroEntity(playerId) } end ::__continue88:: playerId = playerId + 1 end end return players end CutsceneManager = __TS__Decorate(CutsceneManager, CutsceneManager, {reloadable}, {kind = "class", name = "CutsceneManager"}) ____exports.CutsceneManager = CutsceneManager return ____exports