diff --git a/source/ps/Game.cpp b/source/ps/Game.cpp index e2a1a3dc8c..304dca1e65 100644 --- a/source/ps/Game.cpp +++ b/source/ps/Game.cpp @@ -74,6 +74,7 @@ CGame::CGame(bool replayLog): // should be created outside only if needed. m_GameView(CRenderer::IsInitialised() ? new CGameView(g_VideoMode.GetBackendDevice(), this) : nullptr), m_GameStarted(false), + m_CheatsEnabled(false), m_Paused(false), m_SimRate(1.0f), m_PlayerID(-1), @@ -221,6 +222,15 @@ void CGame::RegisterInit(const JS::HandleValue attribs, const std::string& saved std::string mapType; Script::GetProperty(rq, attribs, "mapType", mapType); + JS::RootedValue settings(rq.cx); + Script::GetProperty(rq, attribs, "settings", &settings); + + if (Script::HasProperty(rq, attribs, "settings") && + Script::HasProperty(rq, settings, "CheatsEnabled")) + { + Script::GetProperty(rq, settings, "CheatsEnabled", m_CheatsEnabled); + } + float speed; if (Script::HasProperty(rq, attribs, "gameSpeed")) { @@ -249,19 +259,14 @@ void CGame::RegisterInit(const JS::HandleValue attribs, const std::string& saved { // Load random map attributes std::wstring scriptFile; - JS::RootedValue settings(rq.cx); - Script::GetProperty(rq, attribs, "script", scriptFile); - Script::GetProperty(rq, attribs, "settings", &settings); m_World->RegisterInitRMS(scriptFile, scriptInterface.GetContext(), settings, m_PlayerID); } else { std::wstring mapFile; - JS::RootedValue settings(rq.cx); Script::GetProperty(rq, attribs, "map", mapFile); - Script::GetProperty(rq, attribs, "settings", &settings); m_World->RegisterInit(mapFile, scriptInterface.GetContext(), settings, m_PlayerID); } @@ -379,6 +384,11 @@ void CGame::SetViewedPlayerID(player_id_t playerID) m_ViewedPlayerID = playerID; } +bool CGame::CheatsEnabled() const +{ + return m_CheatsEnabled; +} + void CGame::StartGame(JS::MutableHandleValue attribs, const std::string& savedState) { if (m_ReplayLogger) @@ -475,3 +485,13 @@ bool CGame::IsGameFinished() const return false; } + +bool CGame::PlayerFinished(player_id_t playerID) const +{ + CmpPtr cmpPlayerManager(*m_Simulation2, SYSTEM_ENTITY); + if (!cmpPlayerManager) + return false; + + CmpPtr cmpPlayer(*m_Simulation2, cmpPlayerManager->GetPlayerByID(playerID)); + return cmpPlayer && cmpPlayer->GetState() != "active"; +} diff --git a/source/ps/Game.h b/source/ps/Game.h index c73265bdc8..9dd390802c 100644 --- a/source/ps/Game.h +++ b/source/ps/Game.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 Wildfire Games. +/* Copyright (C) 2024 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -114,6 +114,8 @@ public: int GetViewedPlayerID(); void SetViewedPlayerID(player_id_t playerID); + bool CheatsEnabled() const; + /** * Check if the game is finished by testing if there's a winner. * It is used to end a non visual autostarted game. @@ -122,6 +124,11 @@ public: */ bool IsGameFinished() const; + /** + * Check if the given player has been defeated or won the game. + */ + bool PlayerFinished(player_id_t playerID) const; + /** * Retrieving player colors from scripts is slow, so this updates an * internal cache of all players' colors. @@ -214,6 +221,8 @@ private: int LoadInitialState(const std::string& savedState); bool m_IsSavedGame; // true if loading a saved game; false for a new game + bool m_CheatsEnabled; + int LoadVisualReplayData(); OsPath m_ReplayPath; bool m_IsVisualReplay; diff --git a/source/ps/scripting/JSInterface_Game.cpp b/source/ps/scripting/JSInterface_Game.cpp index 12de30f67d..c888d19e41 100644 --- a/source/ps/scripting/JSInterface_Game.cpp +++ b/source/ps/scripting/JSInterface_Game.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 Wildfire Games. +/* Copyright (C) 2024 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -67,20 +67,35 @@ int GetPlayerID() return g_Game->GetPlayerID(); } -void SetPlayerID(int id) +void SetPlayerID(const ScriptRequest& rq, int id) { if (!g_Game) return; - g_Game->SetPlayerID(id); + int currentID = g_Game->GetPlayerID(); + if (currentID == id) + return; + + if (g_Game->CheatsEnabled()) + g_Game->SetPlayerID(id); + else + ScriptException::Raise(rq, "Changing player ID with cheats disabled is prohibited"); } -void SetViewedPlayer(int id) +void SetViewedPlayer(const ScriptRequest& rq, int id) { if (!g_Game) return; - g_Game->SetViewedPlayerID(id); + int playerID = g_Game->GetPlayerID(); + if (playerID == id) + return; + + // Forbid active players to reveal the map by changing perspective, unless cheats are allowed. + if (playerID == -1 || g_Game->CheatsEnabled() || g_Game->PlayerFinished(playerID)) + g_Game->SetViewedPlayerID(id); + else + ScriptException::Raise(rq, "Changing the perspective with cheats disabled is prohibited"); } float GetSimRate()