2023-06-14 00:06:22 -07:00
|
|
|
|
/* Copyright (C) 2023 Wildfire Games.
|
2023-07-27 13:54:46 -07:00
|
|
|
|
* This file is part of 0 A.D.
|
2009-04-18 10:00:33 -07:00
|
|
|
|
*
|
2023-07-27 13:54:46 -07:00
|
|
|
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
2009-04-18 10:00:33 -07:00
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
|
*
|
2023-07-27 13:54:46 -07:00
|
|
|
|
* 0 A.D. is distributed in the hope that it will be useful,
|
2009-04-18 10:00:33 -07:00
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
|
*
|
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
2023-07-27 13:54:46 -07:00
|
|
|
|
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
2009-04-18 10:00:33 -07:00
|
|
|
|
*/
|
|
|
|
|
|
|
2004-07-27 14:00:53 -07:00
|
|
|
|
#include "precompiled.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include "Game.h"
|
|
|
|
|
|
|
2007-01-07 17:56:46 -08:00
|
|
|
|
#include "graphics/GameView.h"
|
2011-02-02 17:12:24 -08:00
|
|
|
|
#include "graphics/LOSTexture.h"
|
2011-04-03 12:15:15 -07:00
|
|
|
|
#include "graphics/ParticleManager.h"
|
2007-01-07 17:56:46 -08:00
|
|
|
|
#include "graphics/UnitManager.h"
|
2012-08-14 17:10:44 -07:00
|
|
|
|
#include "gui/GUIManager.h"
|
2014-01-04 02:14:53 -08:00
|
|
|
|
#include "gui/CGUI.h"
|
2012-08-31 12:08:41 -07:00
|
|
|
|
#include "lib/config2.h"
|
2007-01-07 17:56:46 -08:00
|
|
|
|
#include "lib/timer.h"
|
2008-06-16 11:19:35 -07:00
|
|
|
|
#include "network/NetClient.h"
|
2010-06-30 14:41:04 -07:00
|
|
|
|
#include "network/NetServer.h"
|
2007-01-07 17:56:46 -08:00
|
|
|
|
#include "ps/CConsole.h"
|
|
|
|
|
|
#include "ps/CLogger.h"
|
|
|
|
|
|
#include "ps/CStr.h"
|
2023-06-14 00:06:22 -07:00
|
|
|
|
#include "ps/GameSetup/GameSetup.h"
|
2007-01-07 17:56:46 -08:00
|
|
|
|
#include "ps/Loader.h"
|
|
|
|
|
|
#include "ps/Profile.h"
|
2010-08-06 15:16:05 -07:00
|
|
|
|
#include "ps/Replay.h"
|
2006-03-21 12:55:45 -08:00
|
|
|
|
#include "ps/World.h"
|
2023-06-14 00:06:22 -07:00
|
|
|
|
#include "ps/VideoMode.h"
|
2011-04-03 12:15:15 -07:00
|
|
|
|
#include "renderer/Renderer.h"
|
2022-01-04 05:29:01 -08:00
|
|
|
|
#include "renderer/SceneRenderer.h"
|
2012-08-06 12:10:47 -07:00
|
|
|
|
#include "renderer/TimeManager.h"
|
2014-07-03 13:07:15 -07:00
|
|
|
|
#include "renderer/WaterManager.h"
|
2021-05-01 07:04:53 -07:00
|
|
|
|
#include "scriptinterface/FunctionWrapper.h"
|
2010-06-30 14:41:04 -07:00
|
|
|
|
#include "scriptinterface/ScriptInterface.h"
|
2021-05-14 03:18:03 -07:00
|
|
|
|
#include "scriptinterface/JSON.h"
|
2010-01-09 11:20:14 -08:00
|
|
|
|
#include "simulation2/Simulation2.h"
|
2010-06-30 14:41:04 -07:00
|
|
|
|
#include "simulation2/components/ICmpPlayer.h"
|
|
|
|
|
|
#include "simulation2/components/ICmpPlayerManager.h"
|
2017-01-23 18:04:50 -08:00
|
|
|
|
#include "simulation2/system/ReplayTurnManager.h"
|
2013-06-06 04:13:57 -07:00
|
|
|
|
#include "soundmanager/ISoundManager.h"
|
2013-12-04 08:52:44 -08:00
|
|
|
|
#include "tools/atlas/GameInterface/GameLoop.h"
|
|
|
|
|
|
|
2021-03-03 14:38:59 -08:00
|
|
|
|
#include <fstream>
|
|
|
|
|
|
|
2013-12-04 08:52:44 -08:00
|
|
|
|
extern GameLoopState* g_AtlasGameLoop;
|
2006-10-07 20:07:54 -07:00
|
|
|
|
|
2007-01-31 17:34:17 -08:00
|
|
|
|
/**
|
|
|
|
|
|
* Globally accessible pointer to the CGame object.
|
|
|
|
|
|
**/
|
2004-07-31 08:57:18 -07:00
|
|
|
|
CGame *g_Game=NULL;
|
2004-07-27 14:00:53 -07:00
|
|
|
|
|
2020-01-15 08:00:37 -08:00
|
|
|
|
const CStr CGame::EventNameSimulationUpdate = "SimulationUpdate";
|
|
|
|
|
|
|
2007-01-31 17:34:17 -08:00
|
|
|
|
/**
|
|
|
|
|
|
* Constructor
|
|
|
|
|
|
*
|
|
|
|
|
|
**/
|
2019-08-25 04:02:55 -07:00
|
|
|
|
CGame::CGame(bool replayLog):
|
2023-10-02 12:47:31 -07:00
|
|
|
|
m_World(new CWorld(*this)),
|
2023-09-26 13:10:40 -07:00
|
|
|
|
m_Simulation2(new CSimulation2(&m_World->GetUnitManager(), g_ScriptContext, &m_World->GetTerrain())),
|
2023-06-14 00:06:22 -07:00
|
|
|
|
// TODO: we need to remove that global dependency. Maybe the game view
|
|
|
|
|
|
// should be created outside only if needed.
|
|
|
|
|
|
m_GameView(CRenderer::IsInitialised() ? new CGameView(g_VideoMode.GetBackendDevice(), this) : nullptr),
|
2006-01-01 19:07:29 -08:00
|
|
|
|
m_GameStarted(false),
|
2006-01-22 19:56:48 -08:00
|
|
|
|
m_Paused(false),
|
2010-06-30 14:41:04 -07:00
|
|
|
|
m_SimRate(1.0f),
|
2011-12-22 06:04:32 -08:00
|
|
|
|
m_PlayerID(-1),
|
2016-05-21 13:24:38 -07:00
|
|
|
|
m_ViewedPlayerID(-1),
|
2015-06-06 01:45:49 -07:00
|
|
|
|
m_IsSavedGame(false),
|
2015-09-13 12:03:33 -07:00
|
|
|
|
m_IsVisualReplay(false),
|
2015-06-06 01:45:49 -07:00
|
|
|
|
m_ReplayStream(NULL)
|
2004-08-05 06:07:51 -07:00
|
|
|
|
{
|
2010-08-06 15:16:05 -07:00
|
|
|
|
// TODO: should use CDummyReplayLogger unless activated by cmd-line arg, perhaps?
|
2015-06-06 01:45:49 -07:00
|
|
|
|
if (replayLog)
|
|
|
|
|
|
m_ReplayLogger = new CReplayLogger(m_Simulation2->GetScriptInterface());
|
|
|
|
|
|
else
|
|
|
|
|
|
m_ReplayLogger = new CDummyReplayLogger();
|
2010-08-06 15:16:05 -07:00
|
|
|
|
|
2007-01-13 14:44:42 -08:00
|
|
|
|
// Need to set the CObjectManager references after various objects have
|
|
|
|
|
|
// been initialised, so do it here rather than via the initialisers above.
|
2010-06-30 14:41:04 -07:00
|
|
|
|
if (m_GameView)
|
|
|
|
|
|
m_World->GetUnitManager().SetObjectManager(m_GameView->GetObjectManager());
|
2010-01-09 11:20:14 -08:00
|
|
|
|
|
2017-01-23 18:04:50 -08:00
|
|
|
|
m_TurnManager = new CLocalTurnManager(*m_Simulation2, GetReplayLogger()); // this will get replaced if we're a net server/client
|
2010-05-19 17:59:01 -07:00
|
|
|
|
|
2010-05-20 11:09:23 -07:00
|
|
|
|
m_Simulation2->LoadDefaultScripts();
|
2004-08-05 06:07:51 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2007-01-31 17:34:17 -08:00
|
|
|
|
/**
|
|
|
|
|
|
* Destructor
|
|
|
|
|
|
*
|
|
|
|
|
|
**/
|
2004-08-05 06:07:51 -07:00
|
|
|
|
CGame::~CGame()
|
|
|
|
|
|
{
|
2005-03-30 08:14:19 -08:00
|
|
|
|
// Again, the in-game call tree is going to be different to the main menu one.
|
2010-06-30 14:41:04 -07:00
|
|
|
|
if (CProfileManager::IsInitialised())
|
|
|
|
|
|
g_Profiler.StructuralReset();
|
2006-03-21 12:55:45 -08:00
|
|
|
|
|
2019-09-25 09:41:14 -07:00
|
|
|
|
if (m_ReplayLogger && m_GameStarted)
|
2019-09-25 03:06:12 -07:00
|
|
|
|
m_ReplayLogger->SaveMetadata(*m_Simulation2);
|
|
|
|
|
|
|
2010-05-19 17:59:01 -07:00
|
|
|
|
delete m_TurnManager;
|
2006-03-21 12:55:45 -08:00
|
|
|
|
delete m_GameView;
|
2010-01-09 11:20:14 -08:00
|
|
|
|
delete m_Simulation2;
|
2006-03-21 12:55:45 -08:00
|
|
|
|
delete m_World;
|
2010-08-06 15:16:05 -07:00
|
|
|
|
delete m_ReplayLogger;
|
2015-06-06 01:45:49 -07:00
|
|
|
|
delete m_ReplayStream;
|
2004-08-05 06:07:51 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-01-23 18:04:50 -08:00
|
|
|
|
void CGame::SetTurnManager(CTurnManager* turnManager)
|
2010-05-19 17:59:01 -07:00
|
|
|
|
{
|
|
|
|
|
|
if (m_TurnManager)
|
|
|
|
|
|
delete m_TurnManager;
|
2010-06-30 14:41:04 -07:00
|
|
|
|
|
2010-05-19 17:59:01 -07:00
|
|
|
|
m_TurnManager = turnManager;
|
2010-06-30 14:41:04 -07:00
|
|
|
|
|
|
|
|
|
|
if (m_TurnManager)
|
|
|
|
|
|
m_TurnManager->SetPlayerID(m_PlayerID);
|
2010-05-19 17:59:01 -07:00
|
|
|
|
}
|
2005-03-21 18:17:55 -08:00
|
|
|
|
|
2015-09-13 12:03:33 -07:00
|
|
|
|
int CGame::LoadVisualReplayData()
|
2015-06-06 01:45:49 -07:00
|
|
|
|
{
|
2015-09-13 12:03:33 -07:00
|
|
|
|
ENSURE(m_IsVisualReplay);
|
2015-06-06 01:45:49 -07:00
|
|
|
|
ENSURE(!m_ReplayPath.empty());
|
2015-07-30 09:43:22 -07:00
|
|
|
|
ENSURE(m_ReplayStream);
|
2015-06-06 01:45:49 -07:00
|
|
|
|
|
2017-01-23 18:04:50 -08:00
|
|
|
|
CReplayTurnManager* replayTurnMgr = static_cast<CReplayTurnManager*>(GetTurnManager());
|
2015-06-06 01:45:49 -07:00
|
|
|
|
|
|
|
|
|
|
u32 currentTurn = 0;
|
|
|
|
|
|
std::string type;
|
|
|
|
|
|
while ((*m_ReplayStream >> type).good())
|
|
|
|
|
|
{
|
|
|
|
|
|
if (type == "turn")
|
|
|
|
|
|
{
|
|
|
|
|
|
u32 turn = 0;
|
|
|
|
|
|
u32 turnLength = 0;
|
|
|
|
|
|
*m_ReplayStream >> turn >> turnLength;
|
2015-10-11 04:12:27 -07:00
|
|
|
|
ENSURE(turn == currentTurn && "You tried to replay a commands.txt file of a rejoined client. Please use the host's file.");
|
2015-06-06 01:45:49 -07:00
|
|
|
|
replayTurnMgr->StoreReplayTurnLength(currentTurn, turnLength);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (type == "cmd")
|
|
|
|
|
|
{
|
|
|
|
|
|
player_id_t player;
|
|
|
|
|
|
*m_ReplayStream >> player;
|
|
|
|
|
|
|
|
|
|
|
|
std::string line;
|
|
|
|
|
|
std::getline(*m_ReplayStream, line);
|
|
|
|
|
|
replayTurnMgr->StoreReplayCommand(currentTurn, player, line);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (type == "hash" || type == "hash-quick")
|
|
|
|
|
|
{
|
|
|
|
|
|
bool quick = (type == "hash-quick");
|
|
|
|
|
|
std::string replayHash;
|
|
|
|
|
|
*m_ReplayStream >> replayHash;
|
|
|
|
|
|
replayTurnMgr->StoreReplayHash(currentTurn, replayHash, quick);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (type == "end")
|
2015-07-30 09:43:22 -07:00
|
|
|
|
++currentTurn;
|
2015-06-06 01:45:49 -07:00
|
|
|
|
else
|
|
|
|
|
|
CancelLoad(L"Failed to load replay data (unrecognized content)");
|
|
|
|
|
|
}
|
2015-07-30 09:43:22 -07:00
|
|
|
|
SAFE_DELETE(m_ReplayStream);
|
2016-02-09 04:09:17 -08:00
|
|
|
|
m_FinalReplayTurn = currentTurn > 0 ? currentTurn - 1 : 0;
|
2015-11-04 12:33:34 -08:00
|
|
|
|
replayTurnMgr->StoreFinalReplayTurn(m_FinalReplayTurn);
|
2015-06-06 01:45:49 -07:00
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-06-25 07:54:00 -07:00
|
|
|
|
bool CGame::StartVisualReplay(const OsPath& replayPath)
|
2015-06-06 01:45:49 -07:00
|
|
|
|
{
|
2017-06-25 07:54:00 -07:00
|
|
|
|
debug_printf("Starting to replay %s\n", replayPath.string8().c_str());
|
2016-03-21 05:09:21 -07:00
|
|
|
|
|
2015-09-13 12:03:33 -07:00
|
|
|
|
m_IsVisualReplay = true;
|
2015-06-06 01:45:49 -07:00
|
|
|
|
|
2017-01-23 18:04:50 -08:00
|
|
|
|
SetTurnManager(new CReplayTurnManager(*m_Simulation2, GetReplayLogger()));
|
2015-06-06 01:45:49 -07:00
|
|
|
|
|
|
|
|
|
|
m_ReplayPath = replayPath;
|
2017-06-25 07:54:00 -07:00
|
|
|
|
m_ReplayStream = new std::ifstream(OsString(replayPath).c_str());
|
2015-06-06 01:45:49 -07:00
|
|
|
|
|
|
|
|
|
|
std::string type;
|
|
|
|
|
|
ENSURE((*m_ReplayStream >> type).good() && type == "start");
|
|
|
|
|
|
|
|
|
|
|
|
std::string line;
|
|
|
|
|
|
std::getline(*m_ReplayStream, line);
|
2016-09-18 02:34:45 -07:00
|
|
|
|
|
2017-11-24 22:49:58 -08:00
|
|
|
|
const ScriptInterface& scriptInterface = m_Simulation2->GetScriptInterface();
|
Improve JS Exception handling.
- Check for pending exceptions after function calls and script
executions.
- Call LOGERROR instead of JS_ReportError when there is a conversion
error in FromJSVal, since that can only be called from C++ (where JS
errors don't really make sense). Instead, C++ callers of FromJSVal
should handle the failure and, themselves, either report an error or
simply do something else.
- Wrap JS_ReportError since that makes updating it later easier.
This isn't a systematical fix since ToJSVal also ought return a boolean
for failures, and we probably should trigger errors instead of warnings
on 'implicit' conversions, rather a preparation diff.
Part of the SM52 migration, stage: SM45 compatible (actually SM52
incompatible, too).
Based on a patch by: Itms
Comments by: Vladislavbelov, Stan`
Refs #742, #4893
Differential Revision: https://code.wildfiregames.com/D3093
This was SVN commit r24187.
2020-11-15 10:29:17 -08:00
|
|
|
|
ScriptRequest rq(scriptInterface);
|
2016-09-18 02:34:45 -07:00
|
|
|
|
|
2020-11-13 05:18:22 -08:00
|
|
|
|
JS::RootedValue attribs(rq.cx);
|
2021-05-14 03:18:03 -07:00
|
|
|
|
Script::ParseJSON(rq, line, &attribs);
|
2015-06-06 01:45:49 -07:00
|
|
|
|
StartGame(&attribs, "");
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2005-03-21 18:17:55 -08:00
|
|
|
|
|
2007-01-31 17:34:17 -08:00
|
|
|
|
/**
|
|
|
|
|
|
* Initializes the game with the set of attributes provided.
|
|
|
|
|
|
* Makes calls to initialize the game view, world, and simulation objects.
|
|
|
|
|
|
* Calls are made to facilitate progress reporting of the initialization.
|
|
|
|
|
|
**/
|
2014-07-26 15:33:16 -07:00
|
|
|
|
void CGame::RegisterInit(const JS::HandleValue attribs, const std::string& savedState)
|
2005-03-21 18:17:55 -08:00
|
|
|
|
{
|
2017-11-24 22:49:58 -08:00
|
|
|
|
const ScriptInterface& scriptInterface = m_Simulation2->GetScriptInterface();
|
Improve JS Exception handling.
- Check for pending exceptions after function calls and script
executions.
- Call LOGERROR instead of JS_ReportError when there is a conversion
error in FromJSVal, since that can only be called from C++ (where JS
errors don't really make sense). Instead, C++ callers of FromJSVal
should handle the failure and, themselves, either report an error or
simply do something else.
- Wrap JS_ReportError since that makes updating it later easier.
This isn't a systematical fix since ToJSVal also ought return a boolean
for failures, and we probably should trigger errors instead of warnings
on 'implicit' conversions, rather a preparation diff.
Part of the SM52 migration, stage: SM45 compatible (actually SM52
incompatible, too).
Based on a patch by: Itms
Comments by: Vladislavbelov, Stan`
Refs #742, #4893
Differential Revision: https://code.wildfiregames.com/D3093
This was SVN commit r24187.
2020-11-15 10:29:17 -08:00
|
|
|
|
ScriptRequest rq(scriptInterface);
|
2016-11-23 06:09:58 -08:00
|
|
|
|
|
2011-12-22 06:04:32 -08:00
|
|
|
|
m_IsSavedGame = !savedState.empty();
|
2011-10-29 17:07:28 -07:00
|
|
|
|
|
2015-01-24 06:46:52 -08:00
|
|
|
|
m_Simulation2->SetInitAttributes(attribs);
|
2011-01-12 04:29:00 -08:00
|
|
|
|
|
2010-10-29 21:02:42 -07:00
|
|
|
|
std::string mapType;
|
2021-05-13 10:23:52 -07:00
|
|
|
|
Script::GetProperty(rq, attribs, "mapType", mapType);
|
2010-06-30 14:41:04 -07:00
|
|
|
|
|
2013-04-03 16:24:22 -07:00
|
|
|
|
float speed;
|
2021-05-13 10:23:52 -07:00
|
|
|
|
if (Script::HasProperty(rq, attribs, "gameSpeed"))
|
2021-05-04 06:59:30 -07:00
|
|
|
|
{
|
2021-05-13 10:23:52 -07:00
|
|
|
|
if (Script::GetProperty(rq, attribs, "gameSpeed", speed))
|
2021-05-04 06:59:30 -07:00
|
|
|
|
SetSimRate(speed);
|
|
|
|
|
|
else
|
|
|
|
|
|
LOGERROR("GameSpeed could not be parsed.");
|
|
|
|
|
|
}
|
2013-04-03 16:24:22 -07:00
|
|
|
|
|
2005-03-21 18:17:55 -08:00
|
|
|
|
LDR_BeginRegistering();
|
|
|
|
|
|
|
2023-06-26 11:35:34 -07:00
|
|
|
|
LDR_Register([this](const double)
|
|
|
|
|
|
{
|
|
|
|
|
|
return m_Simulation2->ProgressiveLoad();
|
|
|
|
|
|
}, L"Simulation init", 1000);
|
2011-03-04 17:56:59 -08:00
|
|
|
|
|
2007-01-31 17:34:17 -08:00
|
|
|
|
// RC, 040804 - GameView needs to be initialized before World, otherwise GameView initialization
|
2005-03-21 18:17:55 -08:00
|
|
|
|
// overwrites anything stored in the map file that gets loaded by CWorld::Initialize with default
|
2010-06-30 14:41:04 -07:00
|
|
|
|
// values. At the minute, it's just lighting settings, but could be extended to store camera position.
|
|
|
|
|
|
// Storing lighting settings in the game view seems a little odd, but it's no big deal; maybe move it at
|
2005-03-21 18:17:55 -08:00
|
|
|
|
// some point to be stored in the world object?
|
2010-06-30 14:41:04 -07:00
|
|
|
|
if (m_GameView)
|
|
|
|
|
|
m_GameView->RegisterInit();
|
2010-10-29 21:02:42 -07:00
|
|
|
|
|
Implements skirmish maps, based on patch by sanderd17, fixes #1198. Skirmish maps are like scenarios, except the player can choose their civ during match setup. To create a skirmish map: place some skirmish entities for each player in Atlas (see templates/skirmish/* for examples), uncheck the player's civ in Atlas' player panel if desired, and save in the maps/skirmishes directory. The map will appear in match setup under the "Skirmish" match type.
Implements custom, VFS-based map load/save dialogs for Atlas (replaces
broken native file dialogs), fixes #631, #889.
Fixes map loading/saving to handle arbitrary subdirectories for better
organization.
Adds default settings to Atlas player panel, fixes #1872. Each setting
now has a checkbox to choose whether it should be saved with the map
(avoids writing lots of useless default data for each map).
Adds map preview setting to Atlas, refs #1745.
Cleans up and simplifies some duplicate code.
Fixes optional serialization performance test.
This was SVN commit r13938.
2013-10-03 19:29:16 -07:00
|
|
|
|
if (mapType == "random")
|
2010-10-29 21:02:42 -07:00
|
|
|
|
{
|
2011-03-21 18:34:45 -07:00
|
|
|
|
// Load random map attributes
|
|
|
|
|
|
std::wstring scriptFile;
|
2020-11-13 05:18:22 -08:00
|
|
|
|
JS::RootedValue settings(rq.cx);
|
2011-03-21 18:34:45 -07:00
|
|
|
|
|
2021-05-13 10:23:52 -07:00
|
|
|
|
Script::GetProperty(rq, attribs, "script", scriptFile);
|
|
|
|
|
|
Script::GetProperty(rq, attribs, "settings", &settings);
|
2011-03-21 18:34:45 -07:00
|
|
|
|
|
2020-11-18 06:39:04 -08:00
|
|
|
|
m_World->RegisterInitRMS(scriptFile, *scriptInterface.GetContext(), settings, m_PlayerID);
|
2010-10-29 21:02:42 -07:00
|
|
|
|
}
|
Implements skirmish maps, based on patch by sanderd17, fixes #1198. Skirmish maps are like scenarios, except the player can choose their civ during match setup. To create a skirmish map: place some skirmish entities for each player in Atlas (see templates/skirmish/* for examples), uncheck the player's civ in Atlas' player panel if desired, and save in the maps/skirmishes directory. The map will appear in match setup under the "Skirmish" match type.
Implements custom, VFS-based map load/save dialogs for Atlas (replaces
broken native file dialogs), fixes #631, #889.
Fixes map loading/saving to handle arbitrary subdirectories for better
organization.
Adds default settings to Atlas player panel, fixes #1872. Each setting
now has a checkbox to choose whether it should be saved with the map
(avoids writing lots of useless default data for each map).
Adds map preview setting to Atlas, refs #1745.
Cleans up and simplifies some duplicate code.
Fixes optional serialization performance test.
This was SVN commit r13938.
2013-10-03 19:29:16 -07:00
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
std::wstring mapFile;
|
2020-11-13 05:18:22 -08:00
|
|
|
|
JS::RootedValue settings(rq.cx);
|
2021-05-13 10:23:52 -07:00
|
|
|
|
Script::GetProperty(rq, attribs, "map", mapFile);
|
|
|
|
|
|
Script::GetProperty(rq, attribs, "settings", &settings);
|
Implements skirmish maps, based on patch by sanderd17, fixes #1198. Skirmish maps are like scenarios, except the player can choose their civ during match setup. To create a skirmish map: place some skirmish entities for each player in Atlas (see templates/skirmish/* for examples), uncheck the player's civ in Atlas' player panel if desired, and save in the maps/skirmishes directory. The map will appear in match setup under the "Skirmish" match type.
Implements custom, VFS-based map load/save dialogs for Atlas (replaces
broken native file dialogs), fixes #631, #889.
Fixes map loading/saving to handle arbitrary subdirectories for better
organization.
Adds default settings to Atlas player panel, fixes #1872. Each setting
now has a checkbox to choose whether it should be saved with the map
(avoids writing lots of useless default data for each map).
Adds map preview setting to Atlas, refs #1745.
Cleans up and simplifies some duplicate code.
Fixes optional serialization performance test.
This was SVN commit r13938.
2013-10-03 19:29:16 -07:00
|
|
|
|
|
2020-11-18 06:39:04 -08:00
|
|
|
|
m_World->RegisterInit(mapFile, *scriptInterface.GetContext(), settings, m_PlayerID);
|
Implements skirmish maps, based on patch by sanderd17, fixes #1198. Skirmish maps are like scenarios, except the player can choose their civ during match setup. To create a skirmish map: place some skirmish entities for each player in Atlas (see templates/skirmish/* for examples), uncheck the player's civ in Atlas' player panel if desired, and save in the maps/skirmishes directory. The map will appear in match setup under the "Skirmish" match type.
Implements custom, VFS-based map load/save dialogs for Atlas (replaces
broken native file dialogs), fixes #631, #889.
Fixes map loading/saving to handle arbitrary subdirectories for better
organization.
Adds default settings to Atlas player panel, fixes #1872. Each setting
now has a checkbox to choose whether it should be saved with the map
(avoids writing lots of useless default data for each map).
Adds map preview setting to Atlas, refs #1745.
Cleans up and simplifies some duplicate code.
Fixes optional serialization performance test.
This was SVN commit r13938.
2013-10-03 19:29:16 -07:00
|
|
|
|
}
|
2014-07-04 02:03:21 -07:00
|
|
|
|
if (m_GameView)
|
2023-06-26 11:35:34 -07:00
|
|
|
|
LDR_Register([&waterManager = g_Renderer.GetSceneRenderer().GetWaterManager()](const double)
|
|
|
|
|
|
{
|
|
|
|
|
|
return waterManager.LoadWaterTextures();
|
|
|
|
|
|
}, L"LoadWaterTextures", 80);
|
2010-10-29 21:02:42 -07:00
|
|
|
|
|
2011-12-22 06:04:32 -08:00
|
|
|
|
if (m_IsSavedGame)
|
2023-06-26 11:35:34 -07:00
|
|
|
|
LDR_Register([this, savedState](const double)
|
|
|
|
|
|
{
|
|
|
|
|
|
return LoadInitialState(savedState);
|
|
|
|
|
|
}, L"Loading game", 1000);
|
2011-10-29 17:07:28 -07:00
|
|
|
|
|
2015-09-13 12:03:33 -07:00
|
|
|
|
if (m_IsVisualReplay)
|
2023-06-26 11:35:34 -07:00
|
|
|
|
LDR_Register([this](const double)
|
|
|
|
|
|
{
|
|
|
|
|
|
return LoadVisualReplayData();
|
|
|
|
|
|
}, L"Loading visual replay data", 1000);
|
2015-06-06 01:45:49 -07:00
|
|
|
|
|
2005-03-21 18:17:55 -08:00
|
|
|
|
LDR_EndRegistering();
|
|
|
|
|
|
}
|
2010-06-30 14:41:04 -07:00
|
|
|
|
|
2023-06-26 11:35:34 -07:00
|
|
|
|
int CGame::LoadInitialState(const std::string& savedState)
|
2011-10-29 17:07:28 -07:00
|
|
|
|
{
|
2011-12-22 06:04:32 -08:00
|
|
|
|
ENSURE(m_IsSavedGame);
|
2011-10-29 17:07:28 -07:00
|
|
|
|
|
2023-06-26 11:35:34 -07:00
|
|
|
|
std::stringstream stream(savedState);
|
2011-10-29 17:07:28 -07:00
|
|
|
|
|
|
|
|
|
|
bool ok = m_Simulation2->DeserializeState(stream);
|
2011-12-22 06:04:32 -08:00
|
|
|
|
if (!ok)
|
|
|
|
|
|
{
|
|
|
|
|
|
CancelLoad(L"Failed to load saved game state. It might have been\nsaved with an incompatible version of the game.");
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
2011-10-29 17:07:28 -07:00
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-01-31 17:34:17 -08:00
|
|
|
|
/**
|
|
|
|
|
|
* Game initialization has been completed. Set game started flag and start the session.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @return PSRETURN 0
|
|
|
|
|
|
**/
|
2005-03-21 18:17:55 -08:00
|
|
|
|
PSRETURN CGame::ReallyStartGame()
|
|
|
|
|
|
{
|
2013-10-16 10:58:12 -07:00
|
|
|
|
// Call the script function InitGame only for new games, not saved games
|
2011-12-22 06:04:32 -08:00
|
|
|
|
if (!m_IsSavedGame)
|
|
|
|
|
|
{
|
2016-11-23 05:02:58 -08:00
|
|
|
|
// Perform some simulation initializations (replace skirmish entities, explore territories, etc.)
|
2015-01-08 14:36:13 -08:00
|
|
|
|
// that needs to be done before setting up the AI and shouldn't be done in Atlas
|
2013-12-04 08:52:44 -08:00
|
|
|
|
if (!g_AtlasGameLoop->running)
|
2014-11-25 14:47:02 -08:00
|
|
|
|
m_Simulation2->PreInitGame();
|
2015-01-08 14:36:13 -08:00
|
|
|
|
|
2018-02-09 11:50:01 -08:00
|
|
|
|
m_Simulation2->InitGame();
|
2011-12-22 06:04:32 -08:00
|
|
|
|
}
|
2011-01-12 04:29:00 -08:00
|
|
|
|
|
2013-02-21 23:32:22 -08:00
|
|
|
|
// We need to do an initial Interpolate call to set up all the models etc,
|
|
|
|
|
|
// because Update might never interpolate (e.g. if the game starts paused)
|
|
|
|
|
|
// and we could end up rendering before having set up any models (so they'd
|
|
|
|
|
|
// all be invisible)
|
|
|
|
|
|
Interpolate(0, 0);
|
|
|
|
|
|
|
2022-01-04 10:13:45 -08:00
|
|
|
|
m_GameStarted = true;
|
2016-11-23 06:09:58 -08:00
|
|
|
|
|
2022-01-04 10:13:45 -08:00
|
|
|
|
// Preload resources to avoid blinking on a first game frame.
|
2013-02-21 23:32:22 -08:00
|
|
|
|
if (CRenderer::IsInitialised())
|
2022-01-04 10:13:45 -08:00
|
|
|
|
g_Renderer.PreloadResourcesBeforeNextFrame();
|
2013-02-21 23:32:22 -08:00
|
|
|
|
|
2015-05-02 19:06:17 -07:00
|
|
|
|
if (g_NetClient)
|
|
|
|
|
|
g_NetClient->LoadFinished();
|
|
|
|
|
|
|
2006-06-09 16:07:11 -07:00
|
|
|
|
// Call the reallyStartGame GUI function, but only if it exists
|
2019-09-04 08:45:48 -07:00
|
|
|
|
if (g_GUI && g_GUI->GetPageCount())
|
2005-06-27 16:04:34 -07:00
|
|
|
|
{
|
2021-05-22 12:28:40 -07:00
|
|
|
|
std::shared_ptr<ScriptInterface> scriptInterface = g_GUI->GetActiveGUI()->GetScriptInterface();
|
Improve JS Exception handling.
- Check for pending exceptions after function calls and script
executions.
- Call LOGERROR instead of JS_ReportError when there is a conversion
error in FromJSVal, since that can only be called from C++ (where JS
errors don't really make sense). Instead, C++ callers of FromJSVal
should handle the failure and, themselves, either report an error or
simply do something else.
- Wrap JS_ReportError since that makes updating it later easier.
This isn't a systematical fix since ToJSVal also ought return a boolean
for failures, and we probably should trigger errors instead of warnings
on 'implicit' conversions, rather a preparation diff.
Part of the SM52 migration, stage: SM45 compatible (actually SM52
incompatible, too).
Based on a patch by: Itms
Comments by: Vladislavbelov, Stan`
Refs #742, #4893
Differential Revision: https://code.wildfiregames.com/D3093
This was SVN commit r24187.
2020-11-15 10:29:17 -08:00
|
|
|
|
ScriptRequest rq(scriptInterface);
|
2020-11-13 05:18:22 -08:00
|
|
|
|
|
2020-11-14 00:46:32 -08:00
|
|
|
|
JS::RootedValue global(rq.cx, rq.globalValue());
|
2021-05-13 10:23:52 -07:00
|
|
|
|
if (Script::HasProperty(rq, global, "reallyStartGame"))
|
2021-05-01 07:04:53 -07:00
|
|
|
|
ScriptFunction::CallVoid(rq, global, "reallyStartGame");
|
2005-06-27 16:04:34 -07:00
|
|
|
|
}
|
2005-03-21 18:17:55 -08:00
|
|
|
|
|
2015-02-13 17:45:13 -08:00
|
|
|
|
debug_printf("GAME STARTED, ALL INIT COMPLETE\n");
|
2005-03-21 18:17:55 -08:00
|
|
|
|
|
2005-03-30 08:14:19 -08:00
|
|
|
|
// The call tree we've built for pregame probably isn't useful in-game.
|
2010-06-30 14:41:04 -07:00
|
|
|
|
if (CProfileManager::IsInitialised())
|
|
|
|
|
|
g_Profiler.StructuralReset();
|
2005-03-30 08:14:19 -08:00
|
|
|
|
|
2005-03-21 18:17:55 -08:00
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-06-30 14:41:04 -07:00
|
|
|
|
int CGame::GetPlayerID()
|
|
|
|
|
|
{
|
|
|
|
|
|
return m_PlayerID;
|
|
|
|
|
|
}
|
2004-08-16 08:19:17 -07:00
|
|
|
|
|
2015-06-06 01:45:49 -07:00
|
|
|
|
void CGame::SetPlayerID(player_id_t playerID)
|
2010-06-30 14:41:04 -07:00
|
|
|
|
{
|
|
|
|
|
|
m_PlayerID = playerID;
|
2016-05-19 12:42:07 -07:00
|
|
|
|
m_ViewedPlayerID = playerID;
|
|
|
|
|
|
|
2010-06-30 14:41:04 -07:00
|
|
|
|
if (m_TurnManager)
|
|
|
|
|
|
m_TurnManager->SetPlayerID(m_PlayerID);
|
2016-05-19 12:42:07 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int CGame::GetViewedPlayerID()
|
|
|
|
|
|
{
|
|
|
|
|
|
return m_ViewedPlayerID;
|
|
|
|
|
|
}
|
2016-03-06 05:56:07 -08:00
|
|
|
|
|
2016-05-19 12:42:07 -07:00
|
|
|
|
void CGame::SetViewedPlayerID(player_id_t playerID)
|
|
|
|
|
|
{
|
|
|
|
|
|
m_ViewedPlayerID = playerID;
|
2004-07-27 14:00:53 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-01-24 06:46:52 -08:00
|
|
|
|
void CGame::StartGame(JS::MutableHandleValue attribs, const std::string& savedState)
|
2010-06-30 14:41:04 -07:00
|
|
|
|
{
|
2015-06-12 19:42:21 -07:00
|
|
|
|
if (m_ReplayLogger)
|
2015-06-06 01:45:49 -07:00
|
|
|
|
m_ReplayLogger->StartGame(attribs);
|
|
|
|
|
|
|
2011-10-29 17:07:28 -07:00
|
|
|
|
RegisterInit(attribs, savedState);
|
2010-06-30 14:41:04 -07:00
|
|
|
|
}
|
2007-01-31 17:34:17 -08:00
|
|
|
|
|
|
|
|
|
|
// TODO: doInterpolate is optional because Atlas interpolates explicitly,
|
|
|
|
|
|
// so that it has more control over the update rate. The game might want to
|
|
|
|
|
|
// do the same, and then doInterpolate should be redundant and removed.
|
|
|
|
|
|
|
2016-01-25 01:57:33 -08:00
|
|
|
|
void CGame::Update(const double deltaRealTime, bool doInterpolate)
|
2004-07-27 14:00:53 -07:00
|
|
|
|
{
|
2016-01-25 01:57:33 -08:00
|
|
|
|
if (m_Paused || !m_TurnManager)
|
|
|
|
|
|
return;
|
2010-06-30 14:41:04 -07:00
|
|
|
|
|
2012-06-06 12:37:03 -07:00
|
|
|
|
const double deltaSimTime = deltaRealTime * m_SimRate;
|
2016-11-23 06:09:58 -08:00
|
|
|
|
|
2012-06-06 12:37:03 -07:00
|
|
|
|
if (deltaSimTime)
|
2010-05-01 09:20:58 -07:00
|
|
|
|
{
|
2010-12-06 11:58:06 -08:00
|
|
|
|
// At the normal sim rate, we currently want to render at least one
|
|
|
|
|
|
// frame per simulation turn, so let maxTurns be 1. But for fast-forward
|
|
|
|
|
|
// sim rates we want to allow more, so it's not bounded by framerate,
|
|
|
|
|
|
// so just use the sim rate itself as the number of turns per frame.
|
|
|
|
|
|
size_t maxTurns = (size_t)m_SimRate;
|
|
|
|
|
|
|
2012-06-06 12:37:03 -07:00
|
|
|
|
if (m_TurnManager->Update(deltaSimTime, maxTurns))
|
2011-02-02 17:12:24 -08:00
|
|
|
|
{
|
2011-11-09 05:09:01 -08:00
|
|
|
|
{
|
|
|
|
|
|
PROFILE3("gui sim update");
|
2020-01-15 08:00:37 -08:00
|
|
|
|
g_GUI->SendEventToAll(EventNameSimulationUpdate);
|
2011-11-09 05:09:01 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2011-02-02 17:12:24 -08:00
|
|
|
|
GetView()->GetLOSTexture().MakeDirty();
|
|
|
|
|
|
}
|
2015-07-30 14:55:34 -07:00
|
|
|
|
|
2012-08-06 12:10:47 -07:00
|
|
|
|
if (CRenderer::IsInitialised())
|
|
|
|
|
|
g_Renderer.GetTimeManager().Update(deltaSimTime);
|
2010-05-01 09:20:58 -07:00
|
|
|
|
}
|
2010-02-07 12:06:16 -08:00
|
|
|
|
|
2007-01-24 12:17:28 -08:00
|
|
|
|
if (doInterpolate)
|
2012-06-06 12:37:03 -07:00
|
|
|
|
m_TurnManager->Interpolate(deltaSimTime, deltaRealTime);
|
2005-12-28 20:52:40 -08:00
|
|
|
|
}
|
2006-03-21 12:55:45 -08:00
|
|
|
|
|
2012-06-06 12:37:03 -07:00
|
|
|
|
void CGame::Interpolate(float simFrameLength, float realFrameLength)
|
2010-05-19 17:59:01 -07:00
|
|
|
|
{
|
2010-06-30 14:41:04 -07:00
|
|
|
|
if (!m_TurnManager)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
2012-06-06 12:37:03 -07:00
|
|
|
|
m_TurnManager->Interpolate(simFrameLength, realFrameLength);
|
2010-05-19 17:59:01 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2011-03-26 13:17:21 -07:00
|
|
|
|
|
2010-06-30 14:41:04 -07:00
|
|
|
|
static CColor BrokenColor(0.3f, 0.3f, 0.3f, 1.0f);
|
2011-03-26 13:17:21 -07:00
|
|
|
|
|
2015-03-15 16:59:48 -07:00
|
|
|
|
void CGame::CachePlayerColors()
|
2005-01-28 16:11:50 -08:00
|
|
|
|
{
|
2015-03-15 16:59:48 -07:00
|
|
|
|
m_PlayerColors.clear();
|
2011-03-26 13:17:21 -07:00
|
|
|
|
|
2010-06-30 14:41:04 -07:00
|
|
|
|
CmpPtr<ICmpPlayerManager> cmpPlayerManager(*m_Simulation2, SYSTEM_ENTITY);
|
2012-02-07 18:46:15 -08:00
|
|
|
|
if (!cmpPlayerManager)
|
2011-03-26 13:17:21 -07:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
int numPlayers = cmpPlayerManager->GetNumPlayers();
|
2015-03-15 16:59:48 -07:00
|
|
|
|
m_PlayerColors.resize(numPlayers);
|
2011-03-26 13:17:21 -07:00
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < numPlayers; ++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
CmpPtr<ICmpPlayer> cmpPlayer(*m_Simulation2, cmpPlayerManager->GetPlayerByID(i));
|
2012-02-07 18:46:15 -08:00
|
|
|
|
if (!cmpPlayer)
|
2015-03-15 16:59:48 -07:00
|
|
|
|
m_PlayerColors[i] = BrokenColor;
|
2011-03-26 13:17:21 -07:00
|
|
|
|
else
|
2018-02-03 06:17:31 -08:00
|
|
|
|
m_PlayerColors[i] = cmpPlayer->GetDisplayedColor();
|
2011-03-26 13:17:21 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-14 15:56:38 -07:00
|
|
|
|
const CColor& CGame::GetPlayerColor(player_id_t player) const
|
2011-03-26 13:17:21 -07:00
|
|
|
|
{
|
2015-03-15 16:59:48 -07:00
|
|
|
|
if (player < 0 || player >= (int)m_PlayerColors.size())
|
2010-06-30 14:41:04 -07:00
|
|
|
|
return BrokenColor;
|
2011-03-26 13:17:21 -07:00
|
|
|
|
|
2015-03-15 16:59:48 -07:00
|
|
|
|
return m_PlayerColors[player];
|
2005-01-28 16:11:50 -08:00
|
|
|
|
}
|
2017-05-23 12:26:33 -07:00
|
|
|
|
|
|
|
|
|
|
bool CGame::IsGameFinished() const
|
|
|
|
|
|
{
|
|
|
|
|
|
for (const std::pair<entity_id_t, IComponent*>& p : m_Simulation2->GetEntitiesWithInterface(IID_Player))
|
|
|
|
|
|
{
|
|
|
|
|
|
CmpPtr<ICmpPlayer> cmpPlayer(*m_Simulation2, p.first);
|
|
|
|
|
|
if (cmpPlayer && cmpPlayer->GetState() == "won")
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|