Add C4SECT_ReinitScenario flag for LoadScenarioSection

This also resets the script engine and does player init callbacks. Useful to restart the scenario in the editor.
console-destruction
Sven Eberhardt 2016-09-06 21:47:29 -04:00
parent 3d5f2f012a
commit 2c20204021
5 changed files with 86 additions and 57 deletions

View File

@ -458,7 +458,7 @@ bool C4Game::Init()
DebugMode = false; DebugMode = false;
// Init game // Init game
if (!InitGame(ScenarioFile, false, true, &numbers)) return false; if (!InitGame(ScenarioFile, IM_Normal, true, &numbers)) return false;
// Network final init // Network final init
if (Network.isEnabled()) if (Network.isEnabled())
@ -480,7 +480,7 @@ bool C4Game::Init()
SetInitProgress(98); SetInitProgress(98);
// Final init // Final init
if (!InitGameFinal()) return false; if (!InitGameFinal(IM_Normal)) return false;
SetInitProgress(99); SetInitProgress(99);
// Sound modifier from savegames // Sound modifier from savegames
@ -1651,7 +1651,7 @@ void C4Game::Ticks()
void C4Game::CompileFunc(StdCompiler *pComp, CompileSettings comp, C4ValueNumbers * numbers) void C4Game::CompileFunc(StdCompiler *pComp, CompileSettings comp, C4ValueNumbers * numbers)
{ {
if (!comp.fScenarioSection && comp.fExact) if (comp.init_mode == IM_Normal && comp.fExact)
{ {
pComp->Name("Game"); pComp->Name("Game");
pComp->Value(mkNamingAdapt(Time, "Time", 0)); pComp->Value(mkNamingAdapt(Time, "Time", 0));
@ -1680,8 +1680,6 @@ void C4Game::CompileFunc(StdCompiler *pComp, CompileSettings comp, C4ValueNumber
pComp->Value(mkNamingAdapt(NextMissionDesc, "NextMissionDesc", StdCopyStrBuf())); pComp->Value(mkNamingAdapt(NextMissionDesc, "NextMissionDesc", StdCopyStrBuf()));
pComp->NameEnd(); pComp->NameEnd();
// Music settings // Music settings
pComp->Value(mkNamingAdapt(::Application.MusicSystem, "Music")); pComp->Value(mkNamingAdapt(::Application.MusicSystem, "Music"));
@ -1698,7 +1696,7 @@ void C4Game::CompileFunc(StdCompiler *pComp, CompileSettings comp, C4ValueNumber
pComp->Value(mkNamingAdapt(Landscape.GetSky(), "Sky")); pComp->Value(mkNamingAdapt(Landscape.GetSky(), "Sky"));
// save custom GUIs only if a real savegame and not for editor-scenario-saves or section changes // save custom GUIs only if a real savegame and not for editor-scenario-saves or section changes
if (!comp.fScenarioSection) if (comp.init_mode == IM_Normal)
{ {
pComp->Name("GUI"); pComp->Name("GUI");
if (pComp->isCompiler()) if (pComp->isCompiler())
@ -1737,18 +1735,18 @@ void C4Game::CompileFunc(StdCompiler *pComp, CompileSettings comp, C4ValueNumber
// Section load: Clear existing prop list numbering to make room for the new objects // Section load: Clear existing prop list numbering to make room for the new objects
// Numbers will be re-acquired in C4GameObjects::PostLoad // Numbers will be re-acquired in C4GameObjects::PostLoad
if (comp.fScenarioSection) C4PropListNumbered::ShelveNumberedPropLists(); if (comp.init_mode == IM_Section) C4PropListNumbered::ShelveNumberedPropLists();
pComp->Value(mkParAdapt(Objects, !comp.fExact, numbers)); pComp->Value(mkParAdapt(Objects, !comp.fExact, numbers));
pComp->Value(mkNamingAdapt(mkParAdapt(ScriptEngine, comp.fScenarioSection, numbers), "Script")); pComp->Value(mkNamingAdapt(mkParAdapt(ScriptEngine, comp.init_mode == IM_Section, numbers), "Script"));
} }
bool C4Game::CompileRuntimeData(C4Group &hGroup, bool fLoadSection, bool exact, bool sync, C4ValueNumbers * numbers) bool C4Game::CompileRuntimeData(C4Group &hGroup, InitMode init_mode, bool exact, bool sync, C4ValueNumbers * numbers)
{ {
::Objects.Clear(!fLoadSection); ::Objects.Clear(init_mode != IM_Section);
GameText.Load(hGroup,C4CFN_Game); GameText.Load(hGroup,C4CFN_Game);
CompileSettings Settings(fLoadSection, false, exact, sync); CompileSettings Settings(init_mode, false, exact, sync);
// C4Game is not defaulted on compilation. // C4Game is not defaulted on compilation.
// Loading of runtime data overrides only certain values. // Loading of runtime data overrides only certain values.
// Doesn't compile players; those will be done later // Doesn't compile players; those will be done later
@ -1772,7 +1770,7 @@ bool C4Game::SaveData(C4Group &hGroup, bool fSaveSection, bool fSaveExact, bool
{ {
StdStrBuf Buf; StdStrBuf Buf;
// Decompile (without players for scenario sections) // Decompile (without players for scenario sections)
DecompileToBuf_Log<StdCompilerINIWrite>(mkParAdapt(*this, CompileSettings(fSaveSection, !fSaveSection && fSaveExact, fSaveExact, fSaveSync), numbers), &Buf, "Game"); DecompileToBuf_Log<StdCompilerINIWrite>(mkParAdapt(*this, CompileSettings(fSaveSection ? IM_Section : IM_Normal, !fSaveSection && fSaveExact, fSaveExact, fSaveSync), numbers), &Buf, "Game");
// Empty? All default save a Game.txt anyway because it is used to signal the engine to not load Objects.c // Empty? All default save a Game.txt anyway because it is used to signal the engine to not load Objects.c
if (!Buf.getLength()) Buf.Copy(" "); if (!Buf.getLength()) Buf.Copy(" ");
@ -2091,7 +2089,7 @@ bool C4Game::ReloadParticle(const char *szName)
return true; return true;
} }
bool C4Game::InitGame(C4Group &hGroup, bool fLoadSection, bool fLoadSky, C4ValueNumbers * numbers) bool C4Game::InitGame(C4Group &hGroup, InitMode init_mode, bool fLoadSky, C4ValueNumbers * numbers)
{ {
// Activate debugger if requested // Activate debugger if requested
// needs to happen before any scripts are compiled to bytecode so AB_DEBUG chunks will be inserted // needs to happen before any scripts are compiled to bytecode so AB_DEBUG chunks will be inserted
@ -2104,7 +2102,7 @@ bool C4Game::InitGame(C4Group &hGroup, bool fLoadSection, bool fLoadSky, C4Value
return false; return false;
} }
if (!fLoadSection) if (init_mode == IM_Normal)
{ {
// file monitor // file monitor
@ -2197,25 +2195,25 @@ bool C4Game::InitGame(C4Group &hGroup, bool fLoadSection, bool fLoadSky, C4Value
} }
// The Landscape is the last long chunk of loading time, so it's a good place to start the music fadeout // The Landscape is the last long chunk of loading time, so it's a good place to start the music fadeout
if (!fLoadSection) Application.MusicSystem.FadeOut(2000); if (init_mode == IM_Normal) Application.MusicSystem.FadeOut(2000);
// Landscape // Landscape
Log(LoadResStr("IDS_PRC_LANDSCAPE")); Log(LoadResStr("IDS_PRC_LANDSCAPE"));
bool fLandscapeLoaded = false; bool fLandscapeLoaded = false;
if (!Landscape.Init(hGroup, fLoadSection, fLoadSky, fLandscapeLoaded, !!C4S.Head.SaveGame)) if (!Landscape.Init(hGroup, init_mode != IM_Normal, fLoadSky, fLandscapeLoaded, !!C4S.Head.SaveGame))
{ LogFatal(LoadResStr("IDS_ERR_GBACK")); return false; } { LogFatal(LoadResStr("IDS_ERR_GBACK")); return false; }
SetInitProgress(88); SetInitProgress(88);
// the savegame flag is set if runtime data is present, in which case this is to be used // the savegame flag is set if runtime data is present, in which case this is to be used
// except for scenario sections // except for scenario sections
if (fLandscapeLoaded && (!C4S.Head.SaveGame || fLoadSection)) if (fLandscapeLoaded && (!C4S.Head.SaveGame || init_mode == IM_Section))
Landscape.ScenarioInit(); Landscape.ScenarioInit();
// clear old landscape data // clear old landscape data
if (fLoadSection && fLandscapeLoaded) { PXS.Clear(); MassMover.Clear(); } if (init_mode != IM_Normal && fLandscapeLoaded) { PXS.Clear(); MassMover.Clear(); }
SetInitProgress(89); SetInitProgress(89);
// Init main object list // Init main object list
Objects.Init(Landscape.GetWidth(), Landscape.GetHeight()); Objects.Init(Landscape.GetWidth(), Landscape.GetHeight());
// Pathfinder // Pathfinder
if (!fLoadSection) PathFinder.Init( &LandscapeFree, &TransferZones ); if (init_mode == IM_Normal) PathFinder.Init( &LandscapeFree, &TransferZones );
SetInitProgress(90); SetInitProgress(90);
// PXS // PXS
@ -2231,16 +2229,17 @@ bool C4Game::InitGame(C4Group &hGroup, bool fLoadSection, bool fLoadSky, C4Value
SetInitProgress(92); SetInitProgress(92);
// definition value overloads // definition value overloads
if (!fLoadSection) InitValueOverloads(); // TODO: Remove this function? We could move value to script and allow it through regular overloads
if (init_mode == IM_Normal) InitValueOverloads();
// runtime data // runtime data
if (!CompileRuntimeData(hGroup, fLoadSection, C4S.Head.SaveGame, C4S.Head.NetworkGame, numbers)) if (!CompileRuntimeData(hGroup, init_mode, C4S.Head.SaveGame, C4S.Head.NetworkGame, numbers))
{ LogFatal(LoadResStr("IDS_PRC_FAIL")); return false; } { LogFatal(LoadResStr("IDS_PRC_FAIL")); return false; }
SetInitProgress(93); SetInitProgress(93);
// Load round results // Load round results
if (!fLoadSection) if (init_mode == IM_Normal)
{ {
if (hGroup.FindEntry(C4CFN_RoundResults)) if (hGroup.FindEntry(C4CFN_RoundResults))
{ {
@ -2254,13 +2253,13 @@ bool C4Game::InitGame(C4Group &hGroup, bool fLoadSection, bool fLoadSky, C4Value
} }
// Denumerate game data pointers // Denumerate game data pointers
if (!fLoadSection) ScriptEngine.Denumerate(numbers); if (init_mode == IM_Normal) ScriptEngine.Denumerate(numbers);
if (!fLoadSection) GlobalSoundModifier.Denumerate(numbers); if (init_mode == IM_Normal) GlobalSoundModifier.Denumerate(numbers);
numbers->Denumerate(); numbers->Denumerate();
if (!fLoadSection) ScriptGuiRoot->Denumerate(numbers); if (init_mode == IM_Normal) ScriptGuiRoot->Denumerate(numbers);
// Object.PostLoad must happen after number->Denumerate(), becuase UpdateFace() will access Action proplist, // Object.PostLoad must happen after number->Denumerate(), becuase UpdateFace() will access Action proplist,
// which might have a non-denumerated prototype otherwise // which might have a non-denumerated prototype otherwise
Objects.PostLoad(fLoadSection, numbers); Objects.PostLoad(init_mode == IM_Section, numbers);
// Check object enumeration // Check object enumeration
if (!CheckObjectEnumeration()) return false; if (!CheckObjectEnumeration()) return false;
@ -2293,7 +2292,7 @@ bool C4Game::InitGame(C4Group &hGroup, bool fLoadSection, bool fLoadSky, C4Value
// close any gfx groups, because they are no longer needed (after sky is initialized) // close any gfx groups, because they are no longer needed (after sky is initialized)
GraphicsResource.CloseFiles(); GraphicsResource.CloseFiles();
if (!fLoadSection) if (init_mode == IM_Normal) // reload doesn't affect the music (takes too long)
{ {
// Music // Music
::Application.MusicSystem.InitForScenario(ScenarioFile); ::Application.MusicSystem.InitForScenario(ScenarioFile);
@ -2311,9 +2310,8 @@ bool C4Game::InitGame(C4Group &hGroup, bool fLoadSection, bool fLoadSky, C4Value
return true; return true;
} }
bool C4Game::InitGameFinal() bool C4Game::InitGameFinal(InitMode init_mode)
{ {
// Validate object owners & assign loaded info objects // Validate object owners & assign loaded info objects
Objects.ValidateOwners(); Objects.ValidateOwners();
Objects.AssignInfo(); Objects.AssignInfo();
@ -2329,26 +2327,32 @@ bool C4Game::InitGameFinal()
// Player final init // Player final init
C4Player *pPlr; C4Player *pPlr;
for (pPlr=Players.First; pPlr; pPlr=pPlr->Next) for (pPlr = Players.First; pPlr; pPlr = pPlr->Next)
{
if (init_mode == IM_ReInit) pPlr->ScenarioInit();
pPlr->FinalInit(!C4S.Head.SaveGame); pPlr->FinalInit(!C4S.Head.SaveGame);
}
// Create viewports // Create viewports
for (pPlr=Players.First; pPlr; pPlr=pPlr->Next) if (init_mode == IM_Normal)
if (pPlr->LocalControl) {
::Viewports.CreateViewport(pPlr->Number); for (pPlr = Players.First; pPlr; pPlr = pPlr->Next)
// Check fullscreen viewports if (pPlr->LocalControl)
FullScreen.ViewportCheck(); ::Viewports.CreateViewport(pPlr->Number);
// update halt state // Check fullscreen viewports
Console.UpdateHaltCtrls(!!HaltCount); FullScreen.ViewportCheck();
// update halt state
Console.UpdateHaltCtrls(!!HaltCount);
// Host: players without connected clients: remove via control queue // Host: players without connected clients: remove via control queue
if (Network.isEnabled() && Network.isHost()) if (Network.isEnabled() && Network.isHost())
for (int32_t cnt = 0; cnt < Players.GetCount(); cnt++) for (int32_t cnt = 0; cnt < Players.GetCount(); cnt++)
if (Players.GetByIndex(cnt)->AtClient < 0) if (Players.GetByIndex(cnt)->AtClient < 0)
Players.Remove(Players.GetByIndex(cnt), true, false); Players.Remove(Players.GetByIndex(cnt), true, false);
// It should be safe now to reload stuff // It should be safe now to reload stuff
if (pFileMonitor) pFileMonitor->StartMonitoring(); if (pFileMonitor) pFileMonitor->StartMonitoring();
}
return true; return true;
} }
@ -3368,10 +3372,14 @@ void C4Game::SetInitProgress(float fToProgress)
GraphicsSystem.MessageBoard->LogNotify(); GraphicsSystem.MessageBoard->LogNotify();
} }
// Cheap hack to get the Console window updated while loading // Cheap hack to get the Console window updated while loading
Application.FlushMessages(); // (unless game is running, i.e. section change - section change would be quick and timer execution can mess with things unpredictably)
if (!IsRunning)
{
Application.FlushMessages();
#ifdef WITH_QT_EDITOR #ifdef WITH_QT_EDITOR
Application.ProcessQtEvents(); Application.ProcessQtEvents();
#endif #endif
}
} }
void C4Game::OnResolutionChanged(unsigned int iXRes, unsigned int iYRes) void C4Game::OnResolutionChanged(unsigned int iXRes, unsigned int iYRes)
@ -3490,7 +3498,7 @@ bool C4Game::LoadScenarioSection(const char *szSection, DWORD dwFlags)
DebugLog("LoadScenarioSection: error opening group file"); DebugLog("LoadScenarioSection: error opening group file");
return false; return false;
} }
// remove all objects (except inactive) // remove all objects
// do correct removal calls, because this will stop fire sounds, etc. // do correct removal calls, because this will stop fire sounds, etc.
for (C4Object *obj : Objects) for (C4Object *obj : Objects)
obj->AssignRemoval(); obj->AssignRemoval();
@ -3500,7 +3508,9 @@ bool C4Game::LoadScenarioSection(const char *szSection, DWORD dwFlags)
DebugLogF("LoadScenarioSection: WARNING: Object %d created in destruction process!", (int) obj->Number); DebugLogF("LoadScenarioSection: WARNING: Object %d created in destruction process!", (int) obj->Number);
ClearPointers(obj); ClearPointers(obj);
} }
DeleteObjects(false); // Final removal in case objects got recreated
// Also kill inactive objects if scenario is reinitialized
DeleteObjects(!!(dwFlags & C4S_REINIT_SCENARIO));
// remove global effects // remove global effects
if (::ScriptEngine.pGlobalEffects && !(dwFlags & C4S_KEEP_EFFECTS)) if (::ScriptEngine.pGlobalEffects && !(dwFlags & C4S_KEEP_EFFECTS))
{ {
@ -3536,7 +3546,7 @@ bool C4Game::LoadScenarioSection(const char *szSection, DWORD dwFlags)
FixRandom(RandomSeed); FixRandom(RandomSeed);
// re-init game in new section // re-init game in new section
C4ValueNumbers numbers; C4ValueNumbers numbers;
if (!InitGame(*pGrp, true, fLoadNewSky, &numbers)) if (!InitGame(*pGrp, (dwFlags & C4S_REINIT_SCENARIO) ? IM_ReInit : IM_Section, fLoadNewSky, &numbers))
{ {
DebugLog("LoadScenarioSection: Error reiniting game"); DebugLog("LoadScenarioSection: Error reiniting game");
::Viewports.EnableFoW(); ::Viewports.EnableFoW();
@ -3547,6 +3557,15 @@ bool C4Game::LoadScenarioSection(const char *szSection, DWORD dwFlags)
// set new current section // set new current section
pCurrentScenarioSection = pLoadSect; pCurrentScenarioSection = pLoadSect;
SCopy(pCurrentScenarioSection->szName, CurrentScenarioSection); SCopy(pCurrentScenarioSection->szName, CurrentScenarioSection);
// Final init on game re-init (doing mostly player initialization)
if (dwFlags & C4S_REINIT_SCENARIO)
{
InitGameFinal(IM_ReInit);
// Extra InitializePlayers on the already-joined players to start intros, etc.
// (unless the call is still pending - can happen if section is loaded during player join)
if (::Game.InitialPlayersJoined && ::Players.GetCount())
::Game.GRBroadcast(PSF_InitializePlayers);
}
// resize viewports, and enable lighting again // resize viewports, and enable lighting again
::Viewports.RecalculateViewports(); ::Viewports.RecalculateViewports();
::Viewports.EnableFoW(); ::Viewports.EnableFoW();

View File

@ -33,17 +33,25 @@ class C4ScriptGuiWindow;
class C4Game class C4Game
{ {
public:
// Initialization mode: Regular game start, section load or editor reload
enum InitMode
{
IM_Normal = 0,
IM_Section = 1,
IM_ReInit = 2
};
private: private:
// used as StdCompiler-parameter // used as StdCompiler-parameter
struct CompileSettings struct CompileSettings
{ {
bool fScenarioSection; InitMode init_mode;
bool fPlayers; bool fPlayers;
bool fExact; bool fExact;
bool fSync; bool fSync;
CompileSettings(bool fScenarioSection, bool fPlayers, bool fExact, bool fSync) CompileSettings(InitMode init_mode, bool fPlayers, bool fExact, bool fSync)
: fScenarioSection(fScenarioSection), fPlayers(fPlayers), fExact(fExact), fSync(fSync) { } : init_mode(init_mode), fPlayers(fPlayers), fExact(fExact), fSync(fSync) { }
}; };
// struct of keyboard set and indexed control key // struct of keyboard set and indexed control key
@ -242,8 +250,8 @@ public:
bool LoadAdditionalSystemGroup(class C4Group &parent_group); bool LoadAdditionalSystemGroup(class C4Group &parent_group);
bool SaveGameTitle(C4Group &hGroup); bool SaveGameTitle(C4Group &hGroup);
protected: protected:
bool InitGame(C4Group &hGroup, bool fLoadSection, bool fLoadSky, C4ValueNumbers *); bool InitGame(C4Group &hGroup, InitMode init_mode, bool fLoadSky, C4ValueNumbers *);
bool InitGameFinal(); bool InitGameFinal(InitMode init_mode);
bool InitNetworkFromAddress(const char *szAddress); bool InitNetworkFromAddress(const char *szAddress);
bool InitNetworkFromReferenceFile(const char *temp_filename); bool InitNetworkFromReferenceFile(const char *temp_filename);
bool InitNetworkFromReference(const C4Network2Reference &Reference); bool InitNetworkFromReference(const C4Network2Reference &Reference);
@ -262,7 +270,7 @@ public:
void CompileFunc(StdCompiler *pComp, CompileSettings comp, C4ValueNumbers *); void CompileFunc(StdCompiler *pComp, CompileSettings comp, C4ValueNumbers *);
bool SaveData(C4Group &hGroup, bool fSaveSection, bool fSaveExact, bool fSaveSync, C4ValueNumbers *); bool SaveData(C4Group &hGroup, bool fSaveSection, bool fSaveExact, bool fSaveSync, C4ValueNumbers *);
protected: protected:
bool CompileRuntimeData(C4Group &hGroup, bool fLoadSection, bool exact, bool sync, C4ValueNumbers *); bool CompileRuntimeData(C4Group &hGroup, InitMode init_mode, bool exact, bool sync, C4ValueNumbers *);
// Object function internals // Object function internals
C4Object *NewObject( C4PropList *ndef, C4Object *pCreator, C4Object *NewObject( C4PropList *ndef, C4Object *pCreator,

View File

@ -2950,6 +2950,7 @@ C4ScriptConstDef C4ScriptGameConstMap[]=
{ "C4SECT_SaveLandscape" ,C4V_Int, C4S_SAVE_LANDSCAPE }, { "C4SECT_SaveLandscape" ,C4V_Int, C4S_SAVE_LANDSCAPE },
{ "C4SECT_SaveObjects" ,C4V_Int, C4S_SAVE_OBJECTS }, { "C4SECT_SaveObjects" ,C4V_Int, C4S_SAVE_OBJECTS },
{ "C4SECT_KeepEffects" ,C4V_Int, C4S_KEEP_EFFECTS }, { "C4SECT_KeepEffects" ,C4V_Int, C4S_KEEP_EFFECTS },
{ "C4SECT_ReinitScenario" ,C4V_Int, C4S_REINIT_SCENARIO },
{ "TEAMID_New" ,C4V_Int, TEAMID_New }, { "TEAMID_New" ,C4V_Int, TEAMID_New },

View File

@ -48,6 +48,7 @@ public:
#define C4S_SAVE_LANDSCAPE 1 #define C4S_SAVE_LANDSCAPE 1
#define C4S_SAVE_OBJECTS 2 #define C4S_SAVE_OBJECTS 2
#define C4S_KEEP_EFFECTS 4 #define C4S_KEEP_EFFECTS 4
#define C4S_REINIT_SCENARIO 8
enum C4SFilmMode enum C4SFilmMode
{ {

View File

@ -149,9 +149,9 @@ void C4AulScriptEngine::CompileFunc(StdCompiler *pComp, bool fScenarioSection, C
pComp->Value(mkNamingAdapt(mkParAdapt(GlobalNamed, numbers), "StaticVariables", GlobalNamedDefault)); pComp->Value(mkNamingAdapt(mkParAdapt(GlobalNamed, numbers), "StaticVariables", GlobalNamedDefault));
pComp->Value(mkNamingAdapt(mkParAdapt(*GameScript.ScenPropList._getPropList(), numbers), "Scenario")); pComp->Value(mkNamingAdapt(mkParAdapt(*GameScript.ScenPropList._getPropList(), numbers), "Scenario"));
} }
if (fScenarioSection && pComp->isCompiler()) if (pComp->isCompiler() && pGlobalEffects)
{ {
// loading scenario section: Merge effects // loading scenario section or game re-init: Merge effects
// Must keep old effects here even if they're dead, because the LoadScenarioSection call typically came from execution of a global effect // Must keep old effects here even if they're dead, because the LoadScenarioSection call typically came from execution of a global effect
// and otherwise dead pointers would remain on the stack // and otherwise dead pointers would remain on the stack
GlobalEffectsMergeCompileFunc(pComp, pGlobalEffects, "Effects", this, numbers); GlobalEffectsMergeCompileFunc(pComp, pGlobalEffects, "Effects", this, numbers);