OpenClonk
C4Game.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 1998-2000, Matthes Bender
5  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
6  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
7  *
8  * Distributed under the terms of the ISC license; see accompanying file
9  * "COPYING" for details.
10  *
11  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12  * See accompanying file "TRADEMARK" for details.
13  *
14  * To redistribute this file separately, substitute the full license texts
15  * for the above references.
16  */
17 
18 /* Main class to run the game */
19 
20 #include "C4Include.h"
22 #include "game/C4Game.h"
23 
24 #include "C4Version.h"
25 #include "control/C4GameControl.h"
27 #include "control/C4GameSave.h"
29 #include "control/C4PlayerInfo.h"
30 #include "control/C4Record.h"
31 #include "control/C4RoundResults.h"
32 #include "editor/C4Console.h"
33 #include "game/C4Application.h"
34 #include "game/C4FullScreen.h"
35 #include "game/C4GraphicsSystem.h"
36 #include "game/C4Viewport.h"
38 #include "gui/C4ChatDlg.h"
39 #include "gui/C4GameLobby.h"
40 #include "gui/C4GameMessage.h"
41 #include "gui/C4GameOverDlg.h"
42 #include "gui/C4LoaderScreen.h"
43 #include "gui/C4MessageInput.h"
44 #include "gui/C4MouseControl.h"
45 #include "gui/C4ScriptGuiWindow.h"
46 #include "gui/C4Startup.h"
47 #include "landscape/C4Landscape.h"
48 #include "landscape/C4MapScript.h"
49 #include "landscape/C4MassMover.h"
50 #include "landscape/C4Material.h"
51 #include "landscape/C4PXS.h"
52 #include "landscape/C4Particles.h"
53 #include "landscape/C4Sky.h"
54 #include "landscape/C4SolidMask.h"
55 #include "landscape/C4Texture.h"
56 #include "landscape/C4Weather.h"
57 #include "landscape/fow/C4FoW.h"
58 #include "lib/C4Random.h"
59 #include "lib/C4Stat.h"
60 #include "lib/StdMesh.h"
61 #include "network/C4League.h"
65 #include "object/C4Command.h"
66 #include "object/C4Def.h"
67 #include "object/C4DefList.h"
68 #include "object/C4GameObjects.h"
69 #include "object/C4Object.h"
70 #include "object/C4ObjectCom.h"
71 #include "object/C4ObjectInfo.h"
72 #include "object/C4ObjectMenu.h"
73 #include "platform/C4FileMonitor.h"
74 #include "player/C4Player.h"
75 #include "player/C4PlayerList.h"
76 #include "player/C4RankSystem.h"
77 #include "script/C4AulDebug.h"
78 #include "script/C4AulExec.h"
79 #include "script/C4Effect.h"
80 
81 #include <unordered_map>
82 
84 {
85 public:
87  ~C4GameSec1Timer() override { Application.Remove(this); }
88  void OnSec1Timer() override;
89 };
90 
91 static C4GameParameters GameParameters;
92 static C4ScenarioParameterDefs GameScenarioParameterDefs;
93 static C4ScenarioParameters GameStartupScenarioParameters;
94 static C4RoundResults GameRoundResults;
95 static C4Value GameGlobalSoundModifier;
96 
98  ScenarioParameterDefs(GameScenarioParameterDefs),
99  Parameters(GameParameters),
100  StartupScenarioParameters(GameStartupScenarioParameters),
101  Clients(Parameters.Clients),
102  Teams(Parameters.Teams),
103  PlayerInfos(Parameters.PlayerInfos),
104  RestorePlayerInfos(Parameters.RestorePlayerInfos),
105  RoundResults(GameRoundResults),
106  Input(Control.Input),
107  KeyboardInput(C4KeyboardInput_Init()),
108  pSec1Timer(new C4GameSec1Timer()),
109  GlobalSoundModifier(GameGlobalSoundModifier)
110 {
111  Default();
112 }
113 
115 {
116  // make sure no startup gfx remain loaded
118 }
119 
121 {
122  int32_t iDefs=0;
123  Log(LoadResStr("IDS_PRC_INITDEFS"));
124  int iDefResCount = 0;
125  C4GameRes *pDef;
126  for (pDef = Parameters.GameRes.iterRes(nullptr, NRT_Definitions); pDef; pDef = Parameters.GameRes.iterRes(pDef, NRT_Definitions))
127  ++iDefResCount;
128  int i = 0;
129  // Load specified defs
130  for (pDef = Parameters.GameRes.iterRes(nullptr, NRT_Definitions); pDef; pDef = Parameters.GameRes.iterRes(pDef, NRT_Definitions))
131  {
132  int iMinProgress = 25 + (25 * i) / iDefResCount;
133  int iMaxProgress = 25 + (25 * (i + 1)) / iDefResCount;
134  ++i;
135  iDefs+=::Definitions.Load(pDef->getFile(),C4D_Load_RX,Config.General.LanguageEx,&Application.SoundSystem,true,iMinProgress,iMaxProgress);
136 
137  // Def load failure
138  if (::Definitions.LoadFailure) return false;
139  }
140 
141  // Load for scenario file - ignore sys group here, because it has been loaded already
143 
144  // Absolutely no defs: we don't like that
145  if (!iDefs) { LogFatal(LoadResStr("IDS_PRC_NODEFS")); return false; }
146 
147  // Check def engine version (should be done immediately on def load)
148  iDefs=::Definitions.CheckEngineVersion(C4XVER1,C4XVER2);
149  if (iDefs>0) { LogF(LoadResStr("IDS_PRC_DEFSINVC4X"),iDefs); }
150 
151  // Check for unmet requirements
153 
154  // build quick access table
156 
157  // handle skeleton appends and includes
159 
160  // Done
161  return true;
162 }
163 
164 
166 {
167 
168  // Scenario from record stream
169  if (RecordStream.getSize())
170  {
171  StdStrBuf RecordFile;
172  if (!C4Playback::StreamToRecord(RecordStream.getData(), &RecordFile))
173  { LogFatal("[!] Could not process record stream data!"); return false; }
174  SCopy(RecordFile.getData(), ScenarioFilename, _MAX_PATH);
175  }
176 
177  // Scenario filename check & log
178  if (!ScenarioFilename[0]) { LogFatal(LoadResStr("IDS_PRC_NOC4S")); return false; }
179  LogF(LoadResStr("IDS_PRC_LOADC4S"),ScenarioFilename);
180 
181  // get parent folder, if it's ocf
183 
184  // open scenario
185  if (pParentGroup)
186  {
187  // open from parent group
189  { LogF("%s: %s", LoadResStr("IDS_PRC_FILENOTFOUND"), (const char *)ScenarioFilename); return false; }
190  }
191  else
192  {
193  // open directly
195  { LogF("%s: %s", LoadResStr("IDS_PRC_FILENOTFOUND"), (const char *)ScenarioFilename); return false; }
196  }
197 
198  // Remember full (absolute) path
200 
201  // add scenario to group
203 
204  // Read scenario core
205  if (!C4S.Load(ScenarioFile))
206  { LogFatal(LoadResStr("IDS_PRC_FILEINVALID")); return false; }
207 
208  // Check minimum engine version
209  if (CompareVersion(C4S.Head.C4XVer[0],C4S.Head.C4XVer[1]) > 0)
210  {
211  LogFatal(FormatString(LoadResStr("IDS_PRC_NOREQC4X"), C4S.Head.C4XVer[0],C4S.Head.C4XVer[1]).getData());
212  return false;
213  }
214 
215  // Add scenario origin to group set
218 
219  // Scenario definition preset
220  StdStrBuf sDefinitionFilenames;
221  if (!C4S.Definitions.AllowUserChange && C4S.Definitions.GetModules(&sDefinitionFilenames))
222  {
223  SCopy(sDefinitionFilenames.getData(), DefinitionFilenames, (sizeof DefinitionFilenames)-1);
224  if (DefinitionFilenames[0]) Log(LoadResStr("IDS_PRC_SCEOWNDEFS"));
225  else Log(LoadResStr("IDS_PRC_LOCALONLY"));
226  }
227 
228  // Check mission access
229 #ifndef USE_CONSOLE
230 #ifndef _DEBUG
231  if (C4S.Head.MissionAccess[0])
232  if (!Application.isEditor)
234  { LogFatal(LoadResStr("IDS_PRC_NOMISSIONACCESS")); return false; }
235 #endif
236 #endif
237 
238  // Title
242 
243  // String tables
245 
246  // Custom scenario parameter definitions. Load even as network client to get localized option names
248 
249  // Load parameters (not as network client, because then team info has already been sent by host)
250  if (!Network.isEnabled() || Network.isHost())
252  return false;
253 
254  SetInitProgress(4);
255 
256  // If scenario is a directory: Watch for changes
258  Game.pFileMonitor->AddDirectory(ScenarioFile.GetFullName().getData());
259 
260  return true;
261 }
262 
264 {
265  // close scenario
268  pParentGroup = nullptr;
269  // remove if temporary
270  if (TempScenarioFile)
271  {
274  }
275  // clear scenario section
276  // this removes any temp files, which may yet need to be used by any future features
277  // so better don't do this too early (like, in C4Game::Clear)
279 }
280 
281 
283 {
284  // init extra root group
285  // this loads font definitions in this group as well
286  // the function may return false, if no extra group is present - that is OK
287  Extra.InitGroup();
288 
289  RandomSeed = time(nullptr);
290  // Randomize
292  // Timer flags
293  GameGo=false;
294  // init message input (default commands)
295  MessageInput.Init();
296  Game.SetInitProgress(31.0f);
297  // init keyboard input (default keys, plus overloads)
298  if (!InitKeyboard())
299  { LogFatal(LoadResStr("IDS_ERR_NOKEYBOARD")); return false; }
300  // Load string table
301  UpdateLanguage();
302  // Player keyboard input: Key definitions and default sets
303  if (!InitPlayerControlSettings()) return false;
304  Game.SetInitProgress(32.0f);
305  // Rank system
306  ::DefaultRanks.Init(Config.GetSubkeyPath("ClonkRanks"), LoadResStr("IDS_GAME_DEFRANKS"), 1000);
307  Game.SetInitProgress(33.0f);
308 
309  // Graphics system (required for GUI)
310  if (!GraphicsSystem.Init())
311  { LogFatal(LoadResStr("IDS_ERR_NOGFXSYS")); return false; }
312 
313  // load GUI
314 #ifndef USE_CONSOLE
315  C4Rect r;
316  if (Application.isEditor)
317  Console.GetSize(&r);
318  else
319  FullScreen.GetSize(&r);
320  pGUI->Init(0, 0, r.Wdt, r.Hgt);
321 #endif
322 
323  fPreinited = true;
324 
325  return true;
326 }
327 
329 {
330  C4ValueNumbers numbers;
331  IsRunning = false;
332 
334  SetInitProgress(0);
335 
336  // reinit keyboard to reflect any config changes that might have been done
337  // this is a good time to do it, because no GUI dialogs are opened
338  if (!InitKeyboard()) LogFatal(LoadResStr("IDS_ERR_NOKEYBOARD"));
339 
340  // start log pos (used by startup)
342  fQuitWithError = false;
343  C4GameLobby::UserAbort = false;
344 
345  // Store a start time that identifies this game on this host
346  StartTime = time(nullptr);
347 
348  // Get PlayerFilenames from Config, if ParseCommandLine did not fill some in
349  // Must be done here, because InitGame calls PlayerInfos.InitLocal
350  if (!*PlayerFilenames)
351  {
353  }
354 
355  // Join a game?
357  {
358 
360  {
361  // init extra; needed for loader screen
362  Log(LoadResStr("IDS_PRC_INITEXTRA"));
363  if (!Extra.Init())
364  { LogFatal(LoadResStr("IDS_PRC_ERREXTRA")); return false; }
365 
366  // init loader
368  { LogFatal(LoadResStr("IDS_PRC_ERRLOADER")); return false; }
369  }
370 
371  SetInitProgress(5);
372 
373  // Initialize network
374  if (pJoinReference)
375  {
376  // By reference
377  bool fSuccess = InitNetworkFromReference(*pJoinReference);
378  pJoinReference.reset();
379  if (!fSuccess)
380  return false;
381  }
383  {
384  // By reference serialized to temp file
386  return false;
387  }
388  else
389  {
390  // By address
392  return false;
393  }
394 
395  // check wether console mode is allowed
397  { LogFatal(LoadResStr("IDS_TEXT_JOININCONSOLEMODENOTALLOW")); return false; }
398 
399  // do lobby (if desired)
400  if (Network.isLobbyActive())
401  if (!Network.DoLobby())
402  return false;
403 
404  // get scenario
405  char szScenario[_MAX_PATH+1];
406  SetInitProgress(6);
407  if (!Network.RetrieveScenario(szScenario)) return false;
408 
409  // open new scenario
410  SCopy(szScenario, ScenarioFilename, _MAX_PATH);
411  if (!OpenScenario()) return false;
413 
414  // get everything else
415  if (!Parameters.GameRes.RetrieveFiles()) return false;
416 
417  // Check network game data scenario type (safety)
418  if (!C4S.Head.NetworkGame)
419  { LogFatal(LoadResStr("IDS_NET_NONETGAME")); return false; }
420 
421  SetInitProgress(7);
422 
423  }
424 
425  // Local game or host?
426  else
427  {
428 
429  // Open scenario
430  if (!OpenScenario())
431  { LogFatal(LoadResStr("IDS_PRC_FAIL")); return false; }
432 
433  // init extra; needed for loader screen
434  Log(LoadResStr("IDS_PRC_INITEXTRA"));
435  if (!Extra.Init())
436  { LogFatal(LoadResStr("IDS_PRC_ERREXTRA")); return false; }
437 
438  // init loader
440  { LogFatal(LoadResStr("IDS_PRC_ERRLOADER")); return false; }
441 
442  // Init network
443  if (!InitNetworkHost()) return false;
444  SetInitProgress(7);
445 
446  }
447 
448  // now free all startup gfx to make room for game gfx
450 
451  // Init debugmode
454  DebugMode = true;
455  if (!Parameters.AllowDebug)
456  DebugMode = false;
457 
458  // Init game
459  if (!InitGame(ScenarioFile, IM_Normal, true, &numbers)) return false;
460 
461  // Network final init
462  if (Network.isEnabled())
463  {
464  if (!Network.FinalInit()) return false;
465  }
466  // non-net may have to synchronize now to keep in sync with replays
467  // also needs to synchronize to update transfer zones
468  else
469  {
470  // - would kill DebugRec-sync for runtime debugrec starts
471  C4DebugRecOff DBGRECOFF(!!C4S.Head.SaveGame);
472  SyncClearance();
473  Synchronize(false);
474  }
475 
476  // Init players
477  if (!InitPlayers(&numbers)) return false;
478  SetInitProgress(98);
479 
480  // Final init
481  if (!InitGameFinal(IM_Normal)) return false;
482  SetInitProgress(99);
483 
484  // Sound modifier from savegames
486 
487  // Message board and upper board
488  if (!Application.isEditor)
489  {
491  }
492 
493  // Default fullscreen menu, in case any old surfaces are left (extra safety)
495 
496  // start statistics (always for now. Make this a config?)
497  pNetworkStatistics = std::make_unique<C4Network2Stats>();
498 
499  // clear loader screen
501  {
504  }
505 
506  // game running now!
507  IsRunning = true;
508 
509  // Start message
510  Log(LoadResStr(C4S.Head.NetworkGame ? "IDS_PRC_JOIN" : C4S.Head.SaveGame ? "IDS_PRC_RESUME" : "IDS_PRC_START"));
511 
512  // set non-exclusive GUI
513  pGUI->SetExclusive(false);
514 
515  // after GUI is made non-exclusive, recheck the scoreboard
516  Scoreboard.DoDlgShow(0, false);
517  SetInitProgress(100);
518 
519  // and redraw background
521 
522  // Notify editor
523  Console.InitGame();
524 
525  return true;
526 }
527 
528 void C4Game::SetScenarioFilename(const char * c4sfile)
529 {
531  if (SEqualNoCase(GetFilename(c4sfile),"scenario.txt"))
532  {
534  }
535 }
536 
538 {
539  pFileMonitor.reset();
540  // fade out music
542  // game no longer running
543  IsRunning = false;
544  PointersDenumerated = false;
545 
547  // C4ST_RESET
548 
549  // Evaluation
550  if (GameOver)
551  {
552  if (!Evaluated) Evaluate();
553  }
554 
555  // stop statistics
556  pNetworkStatistics.reset();
558 
559  // next mission (shoud have been transferred to C4Application now if next mission was desired)
561 
562  // Clear control
563  Network.Clear();
564  Control.Clear();
570 
571  // Clear game info
572  Scoreboard.Clear();
573  Players.Clear();
574  Parameters.Clear();
576  C4S.Clear();
579  Info.Clear();
580  Title.Clear();
581  Names.Clear();
582  GameText.Clear();
585 
586 #ifdef WITH_QT_EDITOR
587  // clear console pointers held into script engine
589  ::Console.ClearGamePointers();
590 #endif
591  // notify editor
592  Console.CloseGame();
593 
594  // Clear the particles before cleaning up the objects.
595  Particles.Clear();
596  DeleteObjects(true);
597 
598  // exit gui
599  pGUI->Clear();
600  ScriptGuiRoot.reset();
601 
602  // Clear landscape
603  Weather.Clear();
604  Landscape.Clear();
605  PXS.Clear();
607  TextureMap.Clear(); // texture map *MUST* be cleared after the materials, because of the patterns!
608  PathFinder.Clear();
610 
611  ::Messages.Clear();
613  SetGlobalSoundModifier(nullptr); // must be called before script engine clear
614  Application.SoundSystem.Modifiers.Clear(); // free some prop list pointers
615 
616  // Clear script engine
617  ::MapScript.Clear();
620  C4PropListNumbered::ClearShelve(); // may be nonempty if there was a fatal error during section load
622  pScenarioObjectsScript = nullptr;
623  // delete any remaining prop lists from circular chains
626 
627  // Clear translation tables
630 
631  // Cleanup remaining open scenario files
632  CloseScenario();
633  GroupSet.Clear();
635  Application.SoundSystem.Clear(); // will be re-inited by application pre-init if running from startup system
636 
637  // Clear remaining graphics
638 #ifndef USE_CONSOLE
640 #endif
643 
644  // global fullscreen class is not cleared, because it holds the carrier window
645  // but the menu must be cleared (maybe move Fullscreen.Menu somewhere else?)
647 
648  // Message
649  // avoid double message by not printing it if no restbl is loaded
650  // this would log an "[Undefined]" only, anyway
651  // (could abort the whole clear-procedure here, btw?)
652  if (::Languages.HasStringTable()) Log(LoadResStr("IDS_CNS_GAMECLOSED"));
653 
654  // clear game starting parameters
656 
657  // join reference
658  pJoinReference.reset();
659 
660  // okay, game cleared now. Remember log section
661  QuitLogPos = GetLogPos();
662 
663  fPreinited = false;
665 
666  // FIXME: remove this
667  Default();
668 }
669 
671 {
672  bool fDoGameOver = false;
673 
674  // Only every 35 ticks
675  if (::Game.iTick35) return false;
676 
677  // do not GameOver in replay
678  if (Control.isReplay()) return false;
679 
680  // All players eliminated: game over
682  fDoGameOver=true;
683 
684  // Message
685  if (fDoGameOver) DoGameOver();
686 
687  return GameOver;
688 }
689 
690 C4ST_NEW(ControlRcvStat, "C4Game::Execute ReceiveControl")
691 C4ST_NEW(ControlStat, "C4Game::Execute ExecuteControl")
692 C4ST_NEW(ExecObjectsStat, "C4Game::Execute ExecObjects")
693 C4ST_NEW(GEStats, "C4Game::Execute pGlobalEffects->Execute")
694 C4ST_NEW(PXSStat, "C4Game::Execute PXS.Execute")
695 C4ST_NEW(DynPartStat, "C4Game::Execute Particles.Execute")
696 C4ST_NEW(MassMoverStat, "C4Game::Execute MassMover.Execute")
697 C4ST_NEW(WeatherStat, "C4Game::Execute Weather.Execute")
698 C4ST_NEW(PlayersStat, "C4Game::Execute Players.Execute")
699 C4ST_NEW(LandscapeStat, "C4Game::Execute Landscape.Execute")
700 C4ST_NEW(MusicSystemStat, "C4Game::Execute MusicSystem.Execute")
701 C4ST_NEW(MessagesStat, "C4Game::Execute Messages.Execute")
702 
703 #define EXEC_S(Expressions, Stat) \
704  { C4ST_START(Stat) Expressions C4ST_STOP(Stat) }
705 
706 #define EXEC_S_DR(Expressions, Stat, DebugRecName) { if (Config.General.DebugRec) AddDbgRec(RCT_Block, DebugRecName, 6); EXEC_S(Expressions, Stat) }
707 #define EXEC_DR(Expressions, DebugRecName) { if (Config.General.DebugRec) AddDbgRec(RCT_Block, DebugRecName, 6); Expressions }
708 
709 bool C4Game::Execute() // Returns true if the game is over
710 {
711 
712  // Let's go
713  GameGo = true;
714 
715  // Network
716  Network.Execute();
717 
718  // Prepare control
719  bool fControl;
720  EXEC_S( fControl = Control.Prepare(); , ControlStat )
721  if (!fControl) return false; // not ready yet: wait
722 
723  // Halt
724  if (HaltCount) return false;
725 
726  if (Config.General.DebugRec)
728 
729  // Execute the control
730  Control.Execute();
731  if (!IsRunning) return false;
732 
733  // Ticks
734  EXEC_DR( Ticks(); , "Ticks")
735 
736  if (Config.General.DebugRec)
737  // debugrec
738  AddDbgRec(RCT_DbgFrame, &FrameCounter, sizeof(int32_t));
739 
740  // allow the particle system to execute the next frame BEFORE the other game stuff is calculated since it will run in parallel to the main thread
742 
743  // Game
744 
745  EXEC_S( ExecObjects(); , ExecObjectsStat )
748  , GEStats , "GEEx\0");
749  EXEC_S_DR( PXS.Execute(); , PXSStat , "PXSEx")
750  EXEC_S_DR( MassMover.Execute(); , MassMoverStat , "MMvEx")
751  EXEC_S_DR( Weather.Execute(); , WeatherStat , "WtrEx")
752  EXEC_S_DR( Landscape.Execute(); , LandscapeStat , "LdsEx")
753  EXEC_S_DR( Players.Execute(); , PlayersStat , "PlrEx")
754  EXEC_S_DR( ::Messages.Execute(); , MessagesStat , "MsgEx")
755 
756  EXEC_DR( MouseControl.Execute(); , "Input")
757 
758  EXEC_DR( GameOverCheck(); , "Misc\0")
759 
761 
762  // Evaluation; Game over dlg
763  if (GameOver)
764  {
765  if (!Evaluated) Evaluate();
767  }
768 
769  // show stat each 1000 ticks
770  if (!(FrameCounter % 1000))
771  {
774  }
775 
776  if (Config.General.DebugRec)
777  {
778  AddDbgRec(RCT_Block, "eGame", 6);
780  }
781 
782  return true;
783 }
784 
786 {
787  // It can happen that this is called before graphics are loaded due to
788  // an early OnResolutionChanged() call. Ignore it, the message board,
789  // upper board and viewports will be initialized within the regular
790  // startup sequence then.
791  if(!GraphicsResource.IsInitialized()) return;
792 
793  // fullscreen message board
794  C4Facet cgo;
796  GraphicsSystem.MessageBoard->Init(cgo, !fRunning);
797  if (fRunning)
798  {
799  // running game: Message board upper board and viewports
800  C4Facet cgo2;
804  }
805 }
806 
808 {
809 
810  // Clear old data
811  TextureMap.Clear();
813 
814  // Check for scenario local materials
815  bool fHaveScenMaterials = Game.ScenarioFile.FindEntry(C4CFN_Material);
816 
817  // Load all materials
818  C4GameRes *pMatRes = nullptr;
819  bool fFirst = true, fOverloadMaterials = true, fOverloadTextures = true;
820  long tex_count = 0, mat_count = 0;
821  while (fOverloadMaterials || fOverloadTextures)
822  {
823 
824  // Are there any scenario local materials that need to be looked at firs?
825  C4Group Mats;
826  if (fHaveScenMaterials)
827  {
829  return false;
830  // Once only
831  fHaveScenMaterials = false;
832  }
833  else
834  {
835  // Find next external material source
836  pMatRes = Game.Parameters.GameRes.iterRes(pMatRes, NRT_Material);
837  if (!pMatRes) break;
838  if (!Reloc.Open(Mats, pMatRes->getFile()))
839  return false;
840  }
841 
842  // Texture loader will access out of order. Pre-cache the small text-files to prevent rewind.
845 
846  // First material file? Load texture map.
847  bool fNewOverloadMaterials = false, fNewOverloadTextures = false;
848  if (fFirst)
849  {
850  long tme_count = TextureMap.LoadMap(Mats, C4CFN_TexMap, &fNewOverloadMaterials, &fNewOverloadTextures);
851  LogF(LoadResStr("IDS_PRC_TEXMAPENTRIES"),tme_count);
852  // Only once
853  fFirst = false;
854  }
855  else
856  {
857  // Check overload-flags only
858  if (!C4TextureMap::LoadFlags(Mats, C4CFN_TexMap, &fNewOverloadMaterials, &fNewOverloadTextures))
859  fOverloadMaterials = fOverloadTextures = false;
860  }
861 
862  // Load textures
863  if (fOverloadTextures)
864  {
865  int iTexs = TextureMap.LoadTextures(Mats);
866  // Automatically continue search if no texture was found
867  if (!iTexs) fNewOverloadTextures = true;
868  tex_count += iTexs;
869  }
870 
871  // Load materials
872  if (fOverloadMaterials)
873  {
874  int iMats = ::MaterialMap.Load(Mats);
875  // Automatically continue search if no material was found
876  if (!iMats) fNewOverloadMaterials = true;
877  mat_count += iMats;
878  }
879 
880  // Set flags
881  fOverloadTextures = fNewOverloadTextures;
882  fOverloadMaterials = fNewOverloadMaterials;
883  }
884 
885  // Logs
886  LogF(LoadResStr("IDS_PRC_TEXTURES"),tex_count);
887  LogF(LoadResStr("IDS_PRC_MATERIALS"),mat_count);
888 
889  // Load material enumeration
891  { LogFatal(LoadResStr("IDS_PRC_NOMATENUM")); return false; }
892 
893  // Initialize texture map
894  TextureMap.Init();
895 
896  // Cross map mats (after texture init, because Material-Texture-combinations are used)
897  if (!::MaterialMap.CrossMapMaterials(C4S.Landscape.Material.c_str())) return false;
898 
899  // get material script funcs
901 
902  return true;
903 }
904 
906 {
907  // May not call Objects.ClearPointers() because that would
908  // remove pObj from primary list and pObj is to be kept
909  // until CheckObjectRemoval().
910  for (C4Object *cObj : Objects)
911  {
912  cObj->ClearPointers(pObj);
913  }
914  // check in inactive objects as well
915  for (C4Object *cObj : Objects.InactiveObjects)
916  {
917  cObj->ClearPointers(pObj);
918  }
919 }
920 
922 {
923  ::AulExec.ClearPointers(pObj);
926  ClearObjectPtrs(pObj);
928  ::Players.ClearPointers(pObj);
931  ::Console.ClearPointers(pObj);
933  ScriptGuiRoot->ClearPointers(pObj);
940 }
941 
943 {
944  // pause toggling disabled during round evaluation
945  if (C4GameOverDlg::IsShown()) return false;
946  // otherwise, toggle
947  if (IsPaused()) return Unpause(); else return Pause();
948 }
949 
951 {
952  // already paused?
953  if (IsPaused()) return false;
954  // pause by net?
955  if (::Network.isEnabled())
956  {
957  // league? Vote...
958  if (Parameters.isLeague() && !Game.Evaluated)
959  {
960  ::Network.Vote(VT_Pause, true, true);
961  return false;
962  }
963  // host only
964  if (!::Network.isHost()) return true;
965  ::Network.Pause();
966  }
967  else
968  {
969  // pause game directly
970  Game.HaltCount = true;
971  }
973  return true;
974 }
975 
977 {
978  // already paused?
979  if (!IsPaused()) return false;
980  // pause by net?
981  if (::Network.isEnabled())
982  {
983  // league? Vote...
984  if (Parameters.isLeague() && !Game.Evaluated)
985  {
986  ::Network.Vote(VT_Pause, true, false);
987  return false;
988  }
989  // host only
990  if (!::Network.isHost()) return true;
991  ::Network.Start();
992  }
993  else
994  {
995  // unpause game directly
996  Game.HaltCount = false;
997  }
999  return true;
1000 }
1001 
1003 {
1004  // pause state defined either by network or by game halt count
1005  if (::Network.isEnabled())
1006  return !::Network.isRunning();
1007  return !!HaltCount;
1008 }
1009 
1010 
1012  int32_t iOwner, C4ObjectInfo *pInfo,
1013  int32_t iX, int32_t iY, int32_t iR,
1014  C4Real xdir, C4Real ydir, C4Real rdir,
1015  int32_t iCon, int32_t iController, bool grow_from_center)
1016 {
1017  // Safety
1018  if (!pDef) return nullptr;
1019  if (Config.General.DebugRec)
1020  {
1021  C4RCCreateObj rc;
1022  memset(&rc, '\0', sizeof(rc));
1023  strncpy(rc.id, pDef->GetName(), 32+1);
1025  rc.x=iX; rc.y=iY; rc.ownr=iOwner;
1026  AddDbgRec(RCT_CrObj, &rc, sizeof(rc));
1027  }
1028  // Create object
1029  C4Object *pObj;
1030  if (!(pObj=new C4Object)) return nullptr;
1031  // Initialize object
1032  pObj->Init( pDef,pCreator,iOwner,pInfo,iX,iY,iR,xdir,ydir,rdir, iController );
1033  // Add to object list
1034  if (!Objects.Add(pObj)) { delete pObj; return nullptr; }
1035  // ---- From now on, object is ready to be used in scripts!
1036  // Construction callback
1037  C4AulParSet pars(pCreator);
1038  pObj->Call(PSF_Construction, &pars);
1039  // AssignRemoval called? (Con 0)
1040  if (!pObj->Status) { return nullptr; }
1041  // Do initial con (grow)
1042  pObj->DoCon(iCon, grow_from_center);
1043  // AssignRemoval called? (Con 0)
1044  if (!pObj->Status) { return nullptr; }
1045  // Success
1046  return pObj;
1047 }
1048 
1049 void C4Game::DeleteObjects(bool fDeleteInactive)
1050 {
1051  // del any objects
1052  ::Objects.DeleteObjects(fDeleteInactive);
1053  // reset resort flag
1054  fResortAnyObject = false;
1055 }
1056 
1057 C4Object* C4Game::CreateObject(C4ID id, C4Object *pCreator, int32_t iOwner,
1058  int32_t x, int32_t y, int32_t r, bool grow_from_center,
1059  C4Real xdir, C4Real ydir, C4Real rdir, int32_t iController)
1060 {
1061  C4Def *pDef;
1062  // Get pDef
1063  if (!(pDef=C4Id2Def(id))) return nullptr;
1064  // Create object
1065  return NewObject(pDef,pCreator,
1066  iOwner,nullptr,
1067  x,y,r,
1068  xdir,ydir,rdir,
1069  FullCon, iController, grow_from_center);
1070 }
1071 
1072 C4Object* C4Game::CreateObject(C4PropList * PropList, C4Object *pCreator, int32_t iOwner,
1073  int32_t x, int32_t y, int32_t r, bool grow_from_center,
1074  C4Real xdir, C4Real ydir, C4Real rdir, int32_t iController)
1075 {
1076  // check Definition
1077  if (!PropList || !PropList->GetDef()) return nullptr;
1078  // Create object
1079  return NewObject(PropList,pCreator,
1080  iOwner,nullptr,
1081  x,y,r,
1082  xdir,ydir,rdir,
1083  FullCon, iController, grow_from_center);
1084 }
1085 
1087  int32_t tx, int32_t ty)
1088 {
1089  C4Def *def;
1090  // Valid check
1091  if (!cinf) return nullptr;
1092  // Get def
1093  if (!(def=C4Id2Def(cinf->id))) return nullptr;
1094  // Create object
1095  return NewObject( def,nullptr,
1096  iOwner,cinf,
1097  tx,ty,0,
1098  Fix0,Fix0,Fix0,
1099  FullCon, NO_OWNER, false);
1100 }
1101 
1103  C4Object *pCreator,
1104  int32_t iOwner,
1105  int32_t iX, int32_t iBY,
1106  int32_t iCon,
1107  bool fTerrain)
1108 {
1109  C4Def *pDef;
1110  C4Object *pObj;
1111 
1112  // Get def
1113  if (!PropList) return nullptr;
1114  if (!(pDef=PropList->GetDef())) return nullptr;
1115 
1116  int32_t dx,dy,dwdt,dhgt;
1117  dwdt=pDef->Shape.Wdt; dhgt=pDef->Shape.Hgt;
1118  dx=iX-dwdt/2; dy=iBY-dhgt;
1119 
1120  // Terrain
1121  if (fTerrain)
1122  {
1123  // Clear site background (ignored for ultra-large structures)
1124  if (dwdt*dhgt<12000)
1125  Landscape.DigFreeRect(dx,dy,dwdt,dhgt);
1126  // Raise Terrain
1127  Landscape.RaiseTerrain(dx,dy+dhgt,dwdt);
1128  }
1129 
1130  // Create object
1131  if (!(pObj=NewObject(PropList,
1132  pCreator,
1133  iOwner,nullptr,
1134  iX,iBY,0,
1135  Fix0,Fix0,Fix0,
1136  iCon, pCreator ? pCreator->Controller : NO_OWNER, false))) return nullptr;
1137 
1138  return pObj;
1139 }
1140 
1141 // Finds an object (OCF_Exclusive) that blocks a potential construction site in the given rectangle
1142 C4Object* C4Game::FindConstuctionSiteBlock(int32_t tx, int32_t ty, int32_t wdt, int32_t hgt)
1143 {
1144  C4Rect rect1,rect2;
1145  rect1.x=tx; rect1.y=ty; rect1.Wdt=wdt; rect1.Hgt=hgt;
1146  C4LArea Area(&::Objects.Sectors, tx, ty, wdt, hgt); C4LSector *pSector;
1147  for (C4ObjectList *pObjs = Area.FirstObjectShapes(&pSector); pSector; pObjs = Area.NextObjectShapes(pObjs, &pSector))
1148  for (C4Object *cObj : *pObjs)
1149  if (cObj->Status && !cObj->Contained)
1150  if (cObj->OCF & OCF_Exclusive)
1151  {
1152  rect2=cObj->Shape; rect2.x+=cObj->GetX(); rect2.y+=cObj->GetY();
1153  if (rect1.Overlap(rect2)) return cObj;
1154  }
1155  return nullptr;
1156 }
1157 
1159  int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt,
1160  DWORD ocf,
1161  C4Object *pFindNext)
1162 {
1163 
1164  C4Object *pClosest=nullptr;
1165  int32_t iClosest = 0,iDistance,iFartherThan=-1;
1166  C4Object *pFindNextCpy=pFindNext;
1167 
1168  // check the easy case first: no instances at all?
1169  if (pDef && !pDef->Count) return nullptr;
1170 
1171  // Finding next closest: find closest but further away than last closest
1172  if (pFindNext && (iWdt==-1) && (iHgt==-1))
1173  {
1174  iFartherThan = (pFindNext->GetX()-iX)*(pFindNext->GetX()-iX)+(pFindNext->GetY()-iY)*(pFindNext->GetY()-iY);
1175  pFindNext = nullptr;
1176  }
1177 
1178  // Scan all objects
1179  for (C4Object *cObj : Objects)
1180  {
1181  // Not skipping to find next
1182  if (!pFindNext)
1183  // Status
1184  if (cObj->Status)
1185  // ID
1186  if (!pDef || (cObj->Def == pDef))
1187  // OCF (match any specified)
1188  if (cObj->OCF & ocf)
1189  // Area
1190  {
1191  // Point
1192  if ((iWdt==0) && (iHgt==0))
1193  {
1194  if (Inside<int32_t>(iX-(cObj->GetX()+cObj->Shape.x),0,cObj->Shape.Wdt-1))
1195  if (Inside<int32_t>(iY-(cObj->GetY()+cObj->Shape.y),0,cObj->Shape.Hgt-1))
1196  return cObj;
1197  continue;
1198  }
1199  // Closest
1200  if ((iWdt==-1) && (iHgt==-1))
1201  {
1202  iDistance = (cObj->GetX()-iX)*(cObj->GetX()-iX)+(cObj->GetY()-iY)*(cObj->GetY()-iY);
1203  // same distance?
1204  if ((iDistance == iFartherThan) && !pFindNextCpy)
1205  return cObj;
1206  // nearer than/first closest?
1207  if (!pClosest || (iDistance < iClosest))
1208  if (iDistance > iFartherThan)
1209  { pClosest=cObj; iClosest=iDistance; }
1210  }
1211  // Range
1212  else if (Inside<int32_t>(cObj->GetX()-iX,0,iWdt-1) && Inside<int32_t>(cObj->GetY()-iY,0,iHgt-1))
1213  return cObj;
1214  }
1215 
1216  // Find next mark reached
1217  if (cObj == pFindNextCpy) pFindNext = pFindNextCpy = nullptr;
1218 
1219  }
1220 
1221  return pClosest;
1222 
1223 }
1224 
1225 C4Object *C4Game::FindVisObject(float tx, float ty, int32_t iPlr, const C4Facet &fctViewportGame, const C4Facet &fctViewportGUI,
1226  float game_x, float game_y, DWORD category, float gui_x, float gui_y)
1227 {
1228  // FIXME: Use C4FindObject here for optimization
1229  // -- can't really do that, since sectors ignore parallaxity, etc.
1230  // determine layer to search in
1231  C4Object *layer_object = nullptr;
1232  C4Player *plr = ::Players.Get(iPlr);
1233  if (plr && plr->Cursor) layer_object = plr->Cursor->Layer;
1234  // scan all object lists separately
1236  while (pLst)
1237  {
1238  // Scan all objects in list
1239  for (C4Object *cObj : *pLst)
1240  {
1241  // Status
1242  if (cObj->Status == C4OS_NORMAL)
1243  // exclude fore-objects from main list
1244  if ((pLst != &Objects) || (~cObj->Category & C4D_Foreground))
1245  // exclude MouseIgnore-objects
1246  if (~cObj->Category & C4D_MouseIgnore)
1247  // Category (match any specified)
1248  if (cObj->Category & category)
1249  // Container
1250  if (!cObj->Contained)
1251  // Visibility
1252  if (cObj->IsVisible(iPlr, false))
1253  // Layer check: Layered objects are invisible to players whose cursor is in another layer
1254  // except for GUI: GUI always visible
1255  {
1256  if (cObj->Layer != layer_object)
1257  if (pLst != &::Objects.ForeObjects)
1258  continue;
1259  // Area
1260  // get object position
1261  float iObjX, iObjY, check_x, check_y;
1262  if (pLst == &::Objects.ForeObjects)
1263  {
1264  // object position for HUD object
1265  check_x = gui_x; check_y = gui_y;
1266  cObj->GetViewPos(iObjX, iObjY, -fctViewportGUI.X, -fctViewportGUI.Y, fctViewportGUI);
1267  }
1268  else
1269  {
1270  // object position for game object
1271  check_x = game_x; check_y = game_y;
1272  cObj->GetViewPos(iObjX, iObjY, tx, ty, fctViewportGame);
1273  }
1274  // Point search
1275  if (Inside<float>(check_x-(iObjX+cObj->Shape.x),0,float(cObj->Shape.Wdt)-1))
1276  if (Inside<float>(check_y-(iObjY+cObj->Shape.y-cObj->addtop()),0,float(cObj->Shape.Hgt+cObj->addtop()-1)))
1277  return cObj;
1278  }
1279  }
1280  // next list
1281  if (pLst == &::Objects.ForeObjects) pLst = &Objects;
1282  else pLst = nullptr;
1283  }
1284 
1285  // none found
1286  return nullptr;
1287 }
1288 
1290 {
1291  C4Def *pDef;
1292  // check the easy cases first
1293  if (id != C4ID::None)
1294  {
1295  if (!(pDef=C4Id2Def(id))) return 0; // no valid def
1296  return pDef->Count;
1297  }
1298  int32_t iResult = 0;
1299  for (C4Object *cObj : Objects)
1300  // Status
1301  if (cObj->Status)
1302  ++iResult;
1303  return iResult;
1304 }
1305 
1306 // Deletes removal-assigned data from list.
1307 // Pointer clearance is done by AssignRemoval.
1308 
1309 void C4Game::ObjectRemovalCheck() // Every ::Game.iTick255 by ExecObjects
1310 {
1311  for (C4Object *cObj : Objects)
1312  {
1313  if (!cObj->Status && (cObj->RemovalDelay==0))
1314  {
1315  Objects.Remove(cObj);
1316  delete cObj;
1317  }
1318  }
1319 }
1320 
1321 void C4Game::ExecObjects() // Every Tick1 by Execute
1322 {
1323  if (Config.General.DebugRec)
1324  AddDbgRec(RCT_Block, "ObjEx", 6);
1325 
1326  // Execute objects - reverse order to ensure
1327  for (C4Object *cObj : Objects.reverse())
1328  {
1329  if (cObj)
1330  {
1331  if (cObj->Status)
1332  // Execute object
1333  cObj->Execute();
1334  else
1335  // Status reset: process removal delay
1336  if (cObj->RemovalDelay>0) cObj->RemovalDelay--;
1337  }
1338  }
1339 
1340  if (Config.General.DebugRec)
1341  AddDbgRec(RCT_Block, "ObjCC", 6);
1342 
1343  // Cross check objects
1344  Objects.CrossCheck();
1345 
1346  if (Config.General.DebugRec)
1347  AddDbgRec(RCT_Block, "ObjRs", 6);
1348 
1349  // Resort
1350  if (fResortAnyObject)
1351  {
1352  fResortAnyObject = false;
1354  }
1355 
1356  if (Config.General.DebugRec)
1357  AddDbgRec(RCT_Block, "ObjRm", 6);
1358 
1359  // Removal
1360  if (!::Game.iTick255) ObjectRemovalCheck();
1361 }
1362 
1363 C4ID DefFileGetID(const char *szFilename)
1364 {
1365  C4Group hDef;
1366  C4Def DefCore;
1367  if (!hDef.Open(szFilename)) return C4ID::None;
1368  if (!DefCore.LoadDefCore(hDef)) { hDef.Close(); return C4ID::None; }
1369  hDef.Close();
1370  return DefCore.id;
1371 }
1372 
1373 bool C4Game::DropFile(const char *szFilename, float iX, float iY)
1374 {
1375  C4ID c_id; C4Def *cdef;
1376  // Drop def to create object
1377  if (SEqualNoCase(GetExtension(szFilename),"ocd"))
1378  {
1379  // Get id from file
1380  if ((c_id=DefFileGetID(szFilename)))
1381  // Get loaded def or try to load def from file
1382  if ( (cdef=C4Id2Def(c_id))
1383  || (::Definitions.Load(szFilename,C4D_Load_RX,Config.General.LanguageEx,&Application.SoundSystem) && (cdef=C4Id2Def(c_id))) )
1384  {
1385  return DropDef(c_id, iX, iY);
1386  }
1387  // Failure
1388  Console.Out(FormatString(LoadResStr("IDS_CNS_DROPNODEF"),GetFilename(szFilename)).getData());
1389  return false;
1390  }
1391  return false;
1392 }
1393 
1394 bool C4Game::DropDef(C4ID id, float X, float Y)
1395 {
1396  // Get def
1397  C4Def *pDef;
1398  if ((pDef=C4Id2Def(id)))
1399  {
1401  return true;
1402  }
1403  else
1404  {
1405  // Failure
1406  Console.Out(FormatString(LoadResStr("IDS_CNS_DROPNODEF"),id.ToString()).getData());
1407  }
1408  return false;
1409 }
1410 
1411 void C4Game::CastObjects(C4ID id, C4Object *pCreator, int32_t num, int32_t level, int32_t tx, int32_t ty, int32_t iOwner, int32_t iController, C4ValueArray *out_objects)
1412 {
1413  int32_t cnt, out_obj_size=0;
1414  if (out_objects)
1415  {
1416  out_obj_size = out_objects->GetSize();
1417  out_objects->SetSize(out_obj_size + num);
1418  }
1419  for (cnt=0; cnt<num; cnt++)
1420  {
1421  // Must do these calculation steps separately, because the order of
1422  // invokations of Random() is not defined if they're used as parameters
1423  int32_t angle = Random(360);
1424  C4Real xdir = C4REAL10(Random(2*level+1)-level);
1425  C4Real ydir = C4REAL10(Random(2*level+1)-level);
1426  C4Real rdir = itofix(Random(3)+1);
1427  C4Object *obj = CreateObject(id,pCreator,iOwner,
1428  tx,ty,angle,
1429  false,
1430  xdir,
1431  ydir,
1432  rdir, iController);
1433  if (obj && obj->Status && out_objects) (*out_objects)[out_obj_size+cnt] = C4VObj(obj);
1434  }
1435 }
1436 
1438 {
1439  // updates the game clock
1440  if (Game.TimeGo) { Game.Time++; Game.TimeGo = false; }
1441  Game.FPS=Game.cFPS; Game.cFPS=0;
1442 }
1443 
1445 {
1446  PointersDenumerated = false;
1447  IsRunning = false;
1448  FrameCounter=0;
1450  ScenarioFilename[0]=0;
1451  PlayerFilenames[0]=0;
1452  DefinitionFilenames[0]=0;
1453  DirectJoinAddress[0]=0;
1454  pJoinReference=nullptr;
1456  StartupTeamCount = 0;
1457  ScenarioTitle.Ref("");
1458  HaltCount=0;
1460  Evaluated=false;
1461  EvaluateOnAbort=false;
1462  TimeGo=false;
1463  Time=0;
1464  StartTime=0;
1466  FPS=cFPS=0;
1467  fScriptCreatedObjects=false;
1468  fLobby=fObserve=false;
1469  iLobbyTimeout=0;
1471  FullSpeed=false;
1472  FrameSkip=1; DoSkipFrame=false;
1475  Objects.Default();
1476  Players.Default();
1477  Weather.Default();
1478  Landscape.Default();
1480  MassMover.Default();
1481  PXS.Default();
1483  C4S.Default();
1484  ::Messages.Default();
1487  PathFinder.Default();
1489  GroupSet.Default();
1490  pParentGroup=nullptr;
1493  fResortAnyObject=false;
1494  pNetworkStatistics.reset();
1496  DebugPort = 0;
1497  DebugPassword.Clear();
1498  DebugHost.Clear();
1499  DebugWait = false;
1500  assert(!ScriptGuiRoot);
1501  ScriptGuiRoot.reset();
1502 }
1503 
1505 {
1506 
1507  // League game?
1508  bool fLeague = Network.isEnabled() && Network.isHost() && Parameters.isLeague();
1509 
1510  // Stop record
1511  StdStrBuf RecordName; BYTE RecordSHA[SHA_DIGEST_LENGTH];
1512  if (Control.isRecord())
1513  Control.StopRecord(&RecordName, fLeague ? RecordSHA : nullptr);
1514 
1515  // Send league result
1516  if (fLeague)
1517  Network.LeagueGameEvaluate(RecordName.getData(), RecordSHA);
1518 
1519  // Players
1520  // saving local players only, because remote players will probably not rejoin after evaluation anyway)
1521  Players.Evaluate();
1522  Players.Save(true);
1523 
1524  // Round results
1526 
1527  // Set game flag
1528  Log(LoadResStr("IDS_PRC_EVALUATED"));
1529  Evaluated=true;
1530 
1531 }
1532 
1533 void C4Game::DrawCrewOverheadText(C4TargetFacet &cgo, int32_t iPlayer)
1534 {
1535 
1536  // All drawing in this function must not be affected by zoom; but remember zoom and reset later.
1537  ZoomData r;
1538  pDraw->GetZoom(&r);
1539  const float zoom = r.Zoom;
1540  r.Zoom = 1.0;
1541  pDraw->SetZoom(r);
1542  // Offset for all text/objects
1543  const float fixedOffsetX = -cgo.X * cgo.Zoom + cgo.X;
1544  const float fixedOffsetY = (-cgo.Y - 10.0f) * cgo.Zoom + cgo.Y;
1545  // Draw cursor mark arrow & cursor object name
1547  for (C4Player *pPlr = Players.First; pPlr; pPlr = pPlr->Next)
1548  {
1549  // Draw a small selector & name above the cursor? F.e. after switching crew.
1550  const bool drawCursorInfo = (pPlr->Number == iPlayer || iPlayer == NO_OWNER) // only for the viewport's player..
1551  && (pPlr->CursorFlash && pPlr->Cursor); // ..and if the player wants to show their cursor.
1552  // Otherwise, for allied players we might want to draw player & crew names.
1553  // Note that these two conditions are generally mutually-exclusive.
1554  const bool drawPlayerAndCursorNames = (pPlr->Number != iPlayer) // Never for own player..
1555  && (Config.Graphics.ShowCrewNames || Config.Graphics.ShowCrewCNames) // ..and if the settings allow it..
1556  && !Hostile(iPlayer, pPlr->Number) && !pPlr->IsInvisible(); // ..and of course only if applicable.
1557 
1558  if (!drawPlayerAndCursorNames && !drawCursorInfo) continue;
1559 
1560  // Lambda to calculate correct drawing position of object, (re-)adjusted by zoom.
1561  float drawX, drawY, drawZoom;
1562  auto calculateObjectTextPosition = [&](C4Object *obj)
1563  {
1564  obj->GetDrawPosition(cgo, fixtof(obj->fix_x), fixtof(obj->fix_y), 1.0, drawX, drawY, drawZoom);
1565  drawX = drawX * cgo.Zoom + fixedOffsetX;
1566  drawY = drawY * cgo.Zoom - static_cast<float>(obj->Def->Shape.Hgt) / 2.0 + fixedOffsetY;
1567  };
1568 
1569  // Actual text output!
1570  if (drawPlayerAndCursorNames)
1571  {
1572  // We need to show crew names for that player, we do so for every crew-member.
1573  for (C4Object * const & crew : pPlr->Crew)
1574  {
1575  if (!crew->Status || !crew->Def) continue;
1576  if (crew->Contained) continue;
1577  if ((crew->OCF & OCF_CrewMember) == 0) continue;
1578  if (!crew->IsVisible(iPlayer, false)) continue;
1579 
1580  calculateObjectTextPosition(crew);
1581  drawY -= 5.0f; // aesthetical offset
1582 
1583  // compose string
1584  char szText[C4GM_MaxText + 1];
1587  sprintf(szText, "%s (%s)", crew->GetName(), pPlr->GetName());
1588  else
1589  SCopy(pPlr->GetName(), szText);
1590  else
1591  SCopy(crew->GetName(), szText);
1592  // Word wrap to cgo width
1593  int32_t iCharWdt, dummy;
1594  ::GraphicsResource.FontRegular.GetTextExtent("m", iCharWdt, dummy, false);
1595  int32_t iMaxLine = std::max<int32_t>(cgo.Wdt / std::max<int32_t>(1, iCharWdt), 20);
1596  SWordWrap(szText, ' ', '|', iMaxLine);
1597  // Center text vertically, too
1598  int textWidth, textHeight;
1599  ::GraphicsResource.FontRegular.GetTextExtent(szText, textWidth, textHeight, true);
1600  // Draw
1601  pDraw->TextOut(szText, ::GraphicsResource.FontRegular, 1.0, cgo.Surface, drawX, drawY - static_cast<float>(textHeight) / 2.0,
1602  pPlr->ColorDw | 0x7f000000, ACenter);
1603  }
1604  }
1605  else if (drawCursorInfo)
1606  {
1607  C4Object * const cursor = pPlr->Cursor;
1608  calculateObjectTextPosition(cursor);
1609  // Draw a down-arrow above the Clonk's head
1610  drawY += -fctCursor.Hgt;
1611  fctCursor.Draw(cgo.Surface, drawX - static_cast<float>(fctCursor.Wdt) / 2.0, drawY, 4);
1612  // And possibly draw some info text, too
1613  if (cursor->Info)
1614  {
1615  int32_t texthgt = ::GraphicsResource.FontRegular.GetLineHeight();
1616  StdStrBuf str;
1617  if (cursor->Info->Rank > 0)
1618  {
1619  str.Format("%s|%s", cursor->Info->sRankName.getData(), cursor->GetName());
1620  texthgt += texthgt;
1621  }
1622  else str = cursor->GetName();
1623 
1625  drawX,
1626  drawY - static_cast<float>(texthgt) / 2.0,
1627  0xffff0000, ACenter);
1628 
1629  }
1630  }
1631  }
1632  // Reset zoom
1633  r.Zoom = zoom;
1634  pDraw->SetZoom(r);
1635 }
1636 
1638 {
1639  // Frames
1641  // Ticks
1642  if (++iTick2==2) iTick2=0;
1643  if (++iTick3==3) iTick3=0;
1644  if (++iTick5==5) iTick5=0;
1645  if (++iTick10==10) iTick10=0;
1646  if (++iTick35==35) iTick35=0;
1647  if (++iTick255==255) iTick255=0;
1648  if (++iTick1000==1000) iTick1000=0;
1649  // FPS / time
1650  cFPS++; TimeGo = true;
1651  // Frame skip
1652  if (FrameCounter % FrameSkip) DoSkipFrame = true;
1653  // Control
1654  Control.Ticks();
1655  // Full speed
1656  if (GameGo) Application.NextTick(); // short-circuit the timer
1657  // statistics
1658  if (pNetworkStatistics) pNetworkStatistics->ExecuteFrame();
1659 }
1660 
1661 void C4Game::CompileFunc(StdCompiler *pComp, CompileSettings comp, C4ValueNumbers * numbers)
1662 {
1663  if (comp.init_mode == IM_Normal && comp.fExact)
1664  {
1665  pComp->Name("Game");
1666  pComp->Value(mkNamingAdapt(Time, "Time", 0));
1667  pComp->Value(mkNamingAdapt(FrameCounter, "Frame", 0));
1668  if (comp.fSync)
1669  {
1670  pComp->Value(mkNamingAdapt(Control.ControlTick, "ControlTick", 0));
1671  pComp->Value(mkNamingAdapt(Control.SyncRate, "SyncRate", C4SyncCheckRate));
1672  }
1673  pComp->Value(mkNamingAdapt(iTick2, "Tick2", 0));
1674  pComp->Value(mkNamingAdapt(iTick3, "Tick3", 0));
1675  pComp->Value(mkNamingAdapt(iTick5, "Tick5", 0));
1676  pComp->Value(mkNamingAdapt(iTick10, "Tick10", 0));
1677  pComp->Value(mkNamingAdapt(iTick35, "Tick35", 0));
1678  pComp->Value(mkNamingAdapt(iTick255, "Tick255", 0));
1679  pComp->Value(mkNamingAdapt(iTick1000, "Tick1000", 0));
1680  pComp->Value(mkNamingAdapt(InitialPlayersJoined, "InitialPlayersJoined", false));
1681  pComp->Value(mkNamingAdapt(StartupPlayerCount, "StartupPlayerCount", 0));
1682  pComp->Value(mkNamingAdapt(StartupTeamCount, "StartupTeamCount", 0));
1683  pComp->Value(mkNamingAdapt(C4PropListNumbered::EnumerationIndex,"ObjectEnumerationIndex",0));
1684  pComp->Value(mkNamingAdapt(mkStringAdaptMA(CurrentScenarioSection), "CurrentScenarioSection", ""));
1685  pComp->Value(mkNamingAdapt(fResortAnyObject, "ResortAnyObj", false));
1686  pComp->Value(mkNamingAdapt(mkParAdapt(GlobalSoundModifier, numbers), "GlobalSoundModifier", C4Value()));
1687  pComp->Value(mkNamingAdapt(NextMission, "NextMission", StdCopyStrBuf()));
1688  pComp->Value(mkNamingAdapt(NextMissionText, "NextMissionText", StdCopyStrBuf()));
1689  pComp->Value(mkNamingAdapt(NextMissionDesc, "NextMissionDesc", StdCopyStrBuf()));
1690  pComp->NameEnd();
1691 
1692  // Music settings
1693  pComp->Value(mkNamingAdapt(::Application.MusicSystem, "Music"));
1694 
1695  // scoreboard compiles into main level [Scoreboard]
1696  pComp->Value(mkNamingAdapt(Scoreboard, "Scoreboard"));
1697  // Keyboard status of global keys synchronized for exact (runtime join) only; not for savegames,
1698  // as keys might be released between a savegame save and its resume
1699  }
1700 
1701  if (comp.fExact)
1702  {
1703  pComp->Value(mkNamingAdapt(Weather, "Weather"));
1704  pComp->Value(mkNamingAdapt(Landscape, "Landscape"));
1705  pComp->Value(mkNamingAdapt(Landscape.GetSky(), "Sky"));
1706 
1707  // save custom GUIs only if a real savegame and not for editor-scenario-saves or section changes
1708  if (comp.init_mode == IM_Normal)
1709  {
1710  pComp->Name("GUI");
1711  if (pComp->isDeserializer())
1712  {
1713  C4Value val;
1714  pComp->Value(mkNamingAdapt(mkParAdapt(val, numbers), "ScriptGUIs", C4VNull));
1715  // if loading, assume
1716  assert(ScriptGuiRoot->GetID() == 0); // ID of 0 means "had no subwindows ever" aka "is fresh" for root
1717  // we will need to denumerate and create the actual GUI post-loading
1718  // for now, just remember our enumerated ID
1719  if (val.GetType() == C4V_Enum)
1720  {
1721  int enumID = val._getInt();
1722  ScriptGuiRoot->SetEnumeratedID(enumID);
1723  }
1724  }
1725  else
1726  {
1727  C4Value *val = new C4Value(ScriptGuiRoot->ToC4Value());
1728  pComp->Value(mkNamingAdapt(mkParAdapt(*val, numbers), "ScriptGUIs", C4VNull));
1729  }
1730  pComp->NameEnd();
1731  }
1732  }
1733 
1734  if (comp.fPlayers)
1735  {
1736  assert(pComp->isSerializer());
1737  // player parsing: Parse all players
1738  // This doesn't create any players, but just parses existing by their ID
1739  // Primary player ininitialization (also setting ID) is done by player info list
1740  // Won't work this way for binary mode!
1741  for (C4Player *pPlr=Players.First; pPlr; pPlr=pPlr->Next)
1742  pComp->Value(mkNamingAdapt(mkParAdapt(*pPlr, numbers), FormatString("Player%d", pPlr->ID).getData()));
1743  }
1744 
1745  // Section load: Clear existing prop list numbering to make room for the new objects
1746  // Numbers will be re-acquired in C4GameObjects::PostLoad
1748 
1749  pComp->Value(mkParAdapt(Objects, !comp.fExact, numbers));
1750 
1751  pComp->Value(mkNamingAdapt(mkParAdapt(ScriptEngine, comp.init_mode == IM_Section, numbers), "Script"));
1752 }
1753 
1754 bool C4Game::CompileRuntimeData(C4Group &hGroup, InitMode init_mode, bool exact, bool sync, C4ValueNumbers * numbers)
1755 {
1756  ::Objects.Clear(init_mode != IM_Section);
1757  GameText.Load(hGroup,C4CFN_Game);
1758  CompileSettings Settings(init_mode, false, exact, sync);
1759  // C4Game is not defaulted on compilation.
1760  // Loading of runtime data overrides only certain values.
1761  // Doesn't compile players; those will be done later
1762  if (GameText.GetData())
1763  {
1764  if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(
1765  mkParAdapt(*this, Settings, numbers),
1767  return false;
1768  // Objects
1769  int32_t iObjects = Objects.ObjectCount();
1770  if (iObjects) { LogF(LoadResStr("IDS_PRC_OBJECTSLOADED"),iObjects); }
1771  }
1772  // Success
1773  return true;
1774 }
1775 
1776 bool C4Game::SaveData(C4Group &hGroup, bool fSaveSection, bool fSaveExact, bool fSaveSync, C4ValueNumbers * numbers)
1777 {
1778  if (fSaveExact)
1779  {
1780  StdStrBuf Buf;
1781  // Decompile (without players for scenario sections)
1782  DecompileToBuf_Log<StdCompilerINIWrite>(mkParAdapt(*this, CompileSettings(fSaveSection ? IM_Section : IM_Normal, !fSaveSection && fSaveExact, fSaveExact, fSaveSync), numbers), &Buf, "Game");
1783 
1784  // Empty? All default save a Game.txt anyway because it is used to signal the engine to not load Objects.c
1785  if (!Buf.getLength()) Buf.Copy(" ");
1786 
1787  // Save
1788  return hGroup.Add(C4CFN_Game,Buf,false,true);
1789  }
1790  else
1791  {
1792  // Clear any exact game data in case scenario is saved from savegame resume
1793  hGroup.Delete(C4CFN_Game);
1794 
1795  // Save objects to file using system scripts
1796  int32_t objects_file_handle = ::ScriptEngine.CreateUserFile();
1797  C4AulParSet pars(objects_file_handle);
1798  C4Value result_c4v(::ScriptEngine.GetPropList()->Call(PSF_SaveScenarioObjects, &pars));
1799  bool result = !!result_c4v;
1800  if (result_c4v.GetType() == C4V_Nil)
1801  {
1802  // Function returned nil: This usually means there was a script error during object writing.
1803  // It could also mean the scripter overloaded global func SaveScenarioObjects and returned nil.
1804  // In either case, objects will not match landscape any more, so better fail and don't save at all.
1805  LogF("ERROR: No valid result from global func " PSF_SaveScenarioObjects ". Saving objects failed.");
1806  }
1807  else
1808  {
1809  // Function completed successfully (returning true or false)
1810  C4AulUserFile *file = ::ScriptEngine.GetUserFile(objects_file_handle);
1811  if (!result || !file || !file->GetFileLength())
1812  {
1813  // Nothing written? Then we don't have objects.
1815  // That's OK; not an error.
1816  result = true;
1817  }
1818  else
1819  {
1820  // Write objects script to file!
1821  StdStrBuf data = file->GrabFileContents();
1822  result = hGroup.Add(C4CFN_ScenarioObjectsScript, data, false, true);
1823  }
1824  }
1825  ::ScriptEngine.CloseUserFile(objects_file_handle);
1826  return result;
1827  }
1828 }
1829 
1831 {
1832 
1833  // Game not running
1834  if (!FrameCounter)
1835  {
1836  char* bpBytes;
1837  size_t iSize;
1838  StdStrBuf realFilename;
1839 
1840  if(ScenarioFile.FindEntry(FormatString("%s.*",C4CFN_ScenarioTitle).getData(),&realFilename,&iSize))
1841  if (ScenarioFile.LoadEntry(realFilename.getData(),&bpBytes,&iSize))
1842  hGroup.Add(realFilename.getData(),bpBytes,iSize,false,true);
1843  }
1844 
1845  // Fullscreen screenshot
1846  else if (!Application.isEditor && Application.Active)
1847  {
1848  C4Surface * sfcPic; int32_t iSfcWdt=200,iSfcHgt=150;
1849  if (!(sfcPic = new C4Surface(iSfcWdt,iSfcHgt,0))) return false;
1850 
1851  // Fullscreen
1854  sfcPic,0,0,iSfcWdt,iSfcHgt);
1855 
1856  bool fOkay=true;
1857  fOkay = sfcPic->SavePNG(Config.AtTempPath(C4CFN_TempTitle), false, false, false);
1858  StdStrBuf destFilename = FormatString("%s.png",C4CFN_ScenarioTitle);
1859  delete sfcPic; if (!fOkay) return false;
1860  if (!hGroup.Move(Config.AtTempPath(C4CFN_TempTitle),destFilename.getData())) return false;
1861  }
1862 
1863  return true;
1864 }
1865 
1866 bool C4Game::DoKeyboardInput(C4KeyCode vk_code, C4KeyEventType eEventType, bool fAlt, bool fCtrl, bool fShift, bool fRepeated, class C4GUI::Dialog *pForDialog, bool fPlrCtrlOnly, int32_t iStrength)
1867 {
1868  // compose key
1869  C4KeyCodeEx Key(vk_code, C4KeyShiftState(fAlt*KEYS_Alt + fCtrl*KEYS_Control + fShift*KEYS_Shift), fRepeated);
1870  return DoKeyboardInput(Key, eEventType, pForDialog, fPlrCtrlOnly, iStrength);
1871 }
1872 
1873 
1874 bool C4Game::DoKeyboardInput(C4KeyCodeEx Key, C4KeyEventType eEventType, class C4GUI::Dialog *pForDialog, bool fPlrCtrlOnly, int32_t iStrength)
1875 {
1876  Key.FixShiftKeys();
1877  // compose keyboard scope
1878  DWORD InScope = 0;
1879  if (fPlrCtrlOnly)
1880  InScope = KEYSCOPE_Control;
1881  else
1882  {
1883  if (IsRunning) InScope = KEYSCOPE_Generic;
1884  // if GUI has keyfocus, this overrides regular controls
1885  if (pGUI->HasKeyboardFocus() || pForDialog)
1886  {
1887  InScope |= KEYSCOPE_Gui;
1888  // control to console mode dialog: Make current keyboard target the active dlg,
1889  // so it can process input
1890  if (pForDialog) pGUI->ActivateDialog(pForDialog);
1891  // any keystroke in GUI resets tooltip times
1892  pGUI->KeyAny();
1893  }
1894  else
1895  {
1896  if (!Application.isEditor)
1897  {
1898  if (FullScreen.pMenu && FullScreen.pMenu->IsActive()) // fullscreen menu
1899  InScope |= KEYSCOPE_FullSMenu;
1900  else if (Game.C4S.Head.Replay && C4S.Head.Film) // film view only
1901  InScope |= KEYSCOPE_FilmView;
1902  else if (::Viewports.GetViewport(NO_OWNER)) // NO_OWNER-viewport-controls
1903  InScope |= KEYSCOPE_FreeView;
1904  else
1905  {
1906  // regular player viewport controls
1907  InScope |= KEYSCOPE_FullSView;
1908  // player controls disabled during round over dlg
1909  if (!C4GameOverDlg::IsShown()) InScope |= KEYSCOPE_Control;
1910  }
1911  }
1912  else
1913  // regular player viewport controls
1914  InScope |= KEYSCOPE_Control;
1915  }
1916  // fullscreen/console (in running game)
1917  if (IsRunning)
1918  {
1919  if (FullScreen.Active) InScope |= KEYSCOPE_Fullscreen;
1920  if (Console.Active) InScope |= KEYSCOPE_Console;
1921  }
1922  }
1923  // okay; do input
1924  if (KeyboardInput.DoInput(Key, eEventType, InScope, iStrength))
1925  return true;
1926 
1927  // unprocessed key
1928  return false;
1929 }
1930 
1932 {
1933  // Network hosts only
1934  if (Network.isEnabled() && !Network.isHost())
1935  { Log(LoadResStr("IDS_GAME_NOCLIENTSAVE")); return false; }
1936 
1937  return true;
1938 }
1939 
1940 bool C4Game::QuickSave(const char *strFilename, const char *strTitle, bool fForceSave)
1941 {
1942  // Check
1943  if (!fForceSave) if (!CanQuickSave()) return false;
1944 
1945  // Create savegame folder
1946  if (!Config.General.CreateSaveFolder(Config.AtUserDataPath(C4CFN_Savegames), LoadResStr("IDS_GAME_SAVEGAMESTITLE")))
1947  { Log(LoadResStr("IDS_GAME_FAILSAVEGAME")); return false; }
1948 
1949  // Create savegame subfolder(s)
1950  char strSaveFolder[_MAX_PATH + 1];
1951  for (uint32_t i = 0; i < SCharCount(DirectorySeparator, strFilename); i++)
1952  {
1953  SCopy(Config.AtUserDataPath(C4CFN_Savegames), strSaveFolder); AppendBackslash(strSaveFolder);
1954  SCopyUntil(strFilename, strSaveFolder + SLen(strSaveFolder), DirectorySeparator, _MAX_PATH, i);
1955  if (!Config.General.CreateSaveFolder(strSaveFolder, strTitle))
1956  { Log(LoadResStr("IDS_GAME_FAILSAVEGAME")); return false; }
1957  }
1958 
1959  // Compose savegame filename
1960  StdStrBuf strSavePath;
1961  strSavePath.Format("%s%c%s", Config.AtUserDataPath(C4CFN_Savegames), DirectorySeparator, strFilename);
1962 
1963  // Must not be the scenario file that is currently open
1964  if (ItemIdentical(ScenarioFilename, strSavePath.getData()))
1965  {
1966  StartSoundEffect("UI::Error");
1967  ::GraphicsSystem.FlashMessage(LoadResStr("IDS_GAME_NOSAVEONCURR"));
1968  Log(LoadResStr("IDS_GAME_FAILSAVEGAME"));
1969  return false;
1970  }
1971 
1972  // Wait message
1973  Log(LoadResStr("IDS_HOLD_SAVINGGAME"));
1974  GraphicsSystem.MessageBoard->EnsureLastMessage();
1975 
1976  // Save to target scenario file
1977  C4GameSave *pGameSave;
1978  pGameSave = new C4GameSaveSavegame();
1979  if (!pGameSave->Save(strSavePath.getData()))
1980  { Log(LoadResStr("IDS_GAME_FAILSAVEGAME")); delete pGameSave; return false; }
1981  delete pGameSave;
1982 
1983  // Success
1984  Log(LoadResStr("IDS_CNS_GAMESAVED"));
1985  return true;
1986 }
1987 
1988 bool LandscapeFree(int32_t x, int32_t y)
1989 {
1990  if (!Inside<int32_t>(x,0,::Landscape.GetWidth()-1) || !Inside<int32_t>(y,0,::Landscape.GetHeight()-1)) return false;
1991  return !DensitySolid(GBackDensity(x,y));
1992 }
1993 
1994 static void FileMonitorCallback(const char * file, const char * extrafile)
1995 {
1996  Game.ReloadFile(file);
1997 }
1998 
1999 bool C4Game::ReloadFile(const char *szFile)
2000 {
2001  // not in network
2002  if (::Network.isEnabled()) return false;
2003  const char *szRelativePath = Config.AtRelativePath(szFile);
2004  // a definition? or part of a definition?
2005  C4Def *pDef;
2006  if ((pDef = ::Definitions.GetByPath(szRelativePath)))
2007  return ReloadDef(pDef->id);
2008  // script?
2009  if (ScriptEngine.ReloadScript(szRelativePath, Config.General.LanguageEx))
2010  {
2012  }
2013  return true;
2014 }
2015 
2017 {
2018  bool fSucc;
2019  // not in network
2020  if (::Network.isEnabled()) return false;
2021  // syncronize (close menus with dead surfaces, etc.)
2022  // no need to sync back player files, though
2023  Synchronize(false);
2024  // SolidMasks might be updated
2026  // reload def
2027  C4Def *pDef = ::Definitions.ID2Def(id);
2028  if (!pDef) return false;
2029  // Open Graphics.ocg -- we might need to fetch some shader (slices)
2030  // from there when reloading mesh materials.
2031  if (!::GraphicsResource.RegisterGlobalGraphics()) return false;
2032  if (!::GraphicsResource.RegisterMainGroups()) return false;
2033  // Message
2034  LogF("Reloading %s from %s",pDef->id.ToString(),GetFilename(pDef->Filename));
2035  // Reload def
2037  {
2038  // update script engine - this will also do include callbacks and Freeze() pDef
2040  // Success, update all concerned object faces
2041  // may have been done by graphics-update already - but not for objects using graphics of another def
2042  // better update everything :)
2043  for (C4Object *obj : Objects)
2044  {
2045  if (obj->id == id)
2046  obj->UpdateFace(true);
2047  }
2048  fSucc = true;
2049  }
2050  else
2051  {
2052  // Failure, remove all objects of this type
2053  for (C4Object *obj : Objects)
2054  if (obj->id == id)
2055  obj->AssignRemoval();
2056  // safety: If a removed def is being profiled, profiling must stop
2058  // Kill def
2059  ::Definitions.Remove(pDef);
2060  // Log
2061  Log("Reloading failure. All objects of this type removed.");
2062  fSucc = false;
2063  }
2064  // close Graphics.ocg again
2066  // update game messages
2067  ::Messages.UpdateDef(id);
2068  // re-put removed SolidMasks
2070  // done
2071  return fSucc;
2072 }
2073 
2074 bool C4Game::ReloadParticle(const char *szName)
2075 {
2076  // not in network
2077  if (::Network.isEnabled()) return false;
2078  // safety
2079  if (!szName) return false;
2080  // get particle def
2081  C4ParticleDef *pDef = Particles.definitions.GetDef(szName);
2082  if (!pDef) return false;
2083  // verbose
2084  LogF("Reloading particle %s from %s",pDef->Name.getData(),GetFilename(pDef->Filename.getData()));
2085  // reload it
2086  if (!pDef->Reload())
2087  {
2088  // safer: remove all particles
2090  // clear def
2091  delete pDef;
2092  // log
2093  LogF("Reloading failure. All particles removed.");
2094  // failure
2095  return false;
2096  }
2097  // success
2098  return true;
2099 }
2100 
2101 bool C4Game::InitGame(C4Group &hGroup, InitMode init_mode, bool fLoadSky, C4ValueNumbers * numbers)
2102 {
2103  // Activate debugger if requested
2104  // needs to happen before any scripts are compiled to bytecode so AB_DEBUG chunks will be inserted
2105  if (DebugPort)
2106  {
2107  if (Parameters.isLeague())
2108  Log("Debugger disabled. Not allowed in league.");
2109  else
2111  return false;
2112  }
2113 
2114  if (init_mode == IM_Normal)
2115  {
2116 
2117  // file monitor
2119  pFileMonitor = std::make_unique<C4FileMonitor>(FileMonitorCallback);
2120 
2121  // system scripts
2122  if (!InitScriptEngine())
2123  { LogFatal(LoadResStr("IDS_PRC_FAIL")); return false; }
2124  SetInitProgress(8);
2125 
2126  // Scenario components
2127  if (!LoadScenarioComponents())
2128  { LogFatal(LoadResStr("IDS_PRC_FAIL")); return false; }
2129  SetInitProgress(9);
2130 
2131  // join local players for regular games
2132  // should be done before record/replay is initialized, so the players are stored in PlayerInfos.txt
2133  // for local savegame resumes, players are joined into PlayerInfos and later associated in InitPlayers
2134  if (!::Network.isEnabled())
2135  if (!PlayerInfos.InitLocal())
2136  { LogFatal(LoadResStr("IDS_PRC_FAIL")); return false; }
2137 
2138  // for replays, make sure teams are assigned correctly
2139  if (C4S.Head.Replay)
2140  {
2141  PlayerInfos.RecheckAutoGeneratedTeams(); // checks that all teams used in playerinfos exist
2142  Teams.RecheckPlayers(); // syncs player list of teams with teams set in PlayerInfos
2143  }
2144 
2145  // set up control (inits Record/Replay)
2146  if (!InitControl()) return false;
2147 
2148  // Graphics and fonts (may reinit main font, too)
2149  // redundant call in NETWORK2; but it may do scenario local overloads
2150  Log(LoadResStr("IDS_PRC_GFXRES"));
2151  if (!GraphicsResource.Init())
2152  { LogFatal(LoadResStr("IDS_PRC_FAIL")); return false; }
2153  SetInitProgress(25);
2154 
2155  // Definitions
2156  if (!InitDefs()) return false;
2157  SetInitProgress(55);
2158 
2159  // Scenario scripts (and local system.ocg)
2161  // Map scripts
2163  // Scenario objects
2165  // After defs to get overloading priority
2167  { LogFatal(LoadResStr("IDS_PRC_FAIL")); return false; }
2168  SetInitProgress(57);
2169 
2170  // Final init for loaded player commands. Before linking scripts, so CON_* constants are registered
2172 
2173  // Register constants for scenario options
2175 
2176  // Now that all controls and assignments are known, resolve user overloads on control assignments
2177  if (!InitPlayerControlUserSettings()) return false;
2178  // Sort assignments by priority. Should be done last, because the user should not see this order in the control config dialog
2180  // (Theoretically, PlayerControlDefaultAssignmentSets could be cleared now. However, the amount of memory used is negligible)
2181 
2182  // Link scripts
2183  if (!LinkScriptEngine()) return false;
2184  SetInitProgress(58);
2185 
2186  // Materials
2187  if (!InitMaterialTexture())
2188  { LogFatal(LoadResStr("IDS_PRC_MATERROR")); return false; }
2189  SetInitProgress(60);
2190 
2191  // prepare script menus
2192  assert(!ScriptGuiRoot);
2193  ScriptGuiRoot = std::make_unique<C4ScriptGuiWindow>();
2194  }
2195  else if (fLoadSky)
2196  {
2197  // Sky needs graphics loaded, for shaders
2198  if (!GraphicsResource.Init())
2199  { LogFatal(LoadResStr("IDS_PRC_FAIL")); return false; }
2200  }
2201 
2202  // Load section sounds
2203  Application.SoundSystem.LoadEffects(hGroup, nullptr, true);
2204 
2205  // determine startup player and team count, which may be used for initial map generation
2206  if (!FrameCounter)
2207  {
2210  }
2211 
2212  // The Landscape is the last long chunk of loading time, so it's a good place to start the music fadeout
2213  if (init_mode == IM_Normal) Application.MusicSystem.FadeOut(2000);
2214  // Landscape
2215  Log(LoadResStr("IDS_PRC_LANDSCAPE"));
2216  bool fLandscapeLoaded = false;
2217  if (!Landscape.Init(hGroup, init_mode != IM_Normal, fLoadSky, fLandscapeLoaded, !!C4S.Head.SaveGame))
2218  { LogFatal(LoadResStr("IDS_ERR_GBACK")); return false; }
2219  SetInitProgress(88);
2220  // the savegame flag is set if runtime data is present, in which case this is to be used
2221  // except for scenario sections
2222  if (fLandscapeLoaded && (!C4S.Head.SaveGame || init_mode == IM_Section))
2224  // clear old landscape data
2225  if (init_mode != IM_Normal && fLandscapeLoaded) { PXS.Clear(); MassMover.Clear(); }
2226  SetInitProgress(89);
2227  // Init main object list
2229 
2230  // Pathfinder
2231  if (init_mode == IM_Normal) PathFinder.Init( &LandscapeFree, &TransferZones );
2232  SetInitProgress(90);
2233 
2234  // PXS
2235  if (hGroup.FindEntry(C4CFN_PXS))
2236  if (!PXS.Load(hGroup))
2237  { LogFatal(LoadResStr("IDS_ERR_PXS")); return false; }
2238  SetInitProgress(91);
2239 
2240  // MassMover
2241  if (hGroup.FindEntry(C4CFN_MassMover))
2242  if (!MassMover.Load(hGroup))
2243  { LogFatal(LoadResStr("IDS_ERR_MOVER")); return false; }
2244  SetInitProgress(92);
2245 
2246  // definition value overloads
2247  // TODO: Remove this function? We could move value to script and allow it through regular overloads
2248  if (init_mode == IM_Normal) InitValueOverloads();
2249 
2250  // runtime data
2251  if (!CompileRuntimeData(hGroup, init_mode, C4S.Head.SaveGame, C4S.Head.NetworkGame, numbers))
2252  { LogFatal(LoadResStr("IDS_PRC_FAIL")); return false; }
2253 
2254  SetInitProgress(93);
2255 
2256  // Load round results
2257  if (init_mode == IM_Normal)
2258  {
2259  if (hGroup.FindEntry(C4CFN_RoundResults))
2260  {
2261  if (!RoundResults.Load(hGroup, C4CFN_RoundResults))
2262  { LogFatal(LoadResStr("IDS_ERR_ERRORLOADINGROUNDRESULTS")); return false; }
2263  }
2264  else
2265  {
2266  RoundResults.Init();
2267  }
2268  // Per-scenario flag, ignored in sections.
2270  }
2271 
2272  // Denumerate game data pointers
2273  if (init_mode == IM_Normal) ScriptEngine.Denumerate(numbers);
2274  if (init_mode == IM_Normal) GlobalSoundModifier.Denumerate(numbers);
2275  numbers->Denumerate();
2276  if (init_mode == IM_Normal) ScriptGuiRoot->Denumerate(numbers);
2277  // Object.PostLoad must happen after number->Denumerate(), becuase UpdateFace() will access Action proplist,
2278  // which might have a non-denumerated prototype otherwise
2279  Objects.PostLoad(init_mode == IM_Section, numbers);
2280 
2281  // Check object enumeration
2282  if (!CheckObjectEnumeration()) return false;
2283 
2284  // Okay; everything in denumerated state from now on
2285  PointersDenumerated = true;
2286 
2287  // scenario objects script
2290 
2291  // Environment
2292  if (!C4S.Head.NoInitialize && fLandscapeLoaded)
2293  {
2294  Log(LoadResStr("IDS_PRC_ENVIRONMENT"));
2295  InitVegetation();
2296  InitInEarth();
2297  InitAnimals();
2298  InitEnvironment();
2299  InitRules();
2300  InitGoals();
2302  }
2303  SetInitProgress(94);
2304 
2305  // Weather
2306  if (fLandscapeLoaded) Weather.Init(!C4S.Head.SaveGame);
2307  SetInitProgress(96);
2308 
2309  // close any gfx groups, because they are no longer needed (after sky is initialized)
2311 
2312  if (init_mode == IM_Normal) // reload doesn't affect the music (takes too long)
2313  {
2314  // Music
2317  if (::Config.Sound.RXMusic)
2318  {
2319  // Play something that is not Frontend.mid
2321  }
2322  else
2324  SetInitProgress(97);
2325  }
2326 
2327  return true;
2328 }
2329 
2331 {
2332  // Validate object owners & assign loaded info objects
2334  Objects.AssignInfo();
2335  Objects.AssignLightRange(); // update FoW-repellers
2336 
2337  // Ambience init (before scenario construction, so the scenario can easily modify ambience in Initialize)
2339 
2340  // Script constructor call
2341  int32_t iObjCount = Objects.ObjectCount();
2343  if (Objects.ObjectCount()!=iObjCount) fScriptCreatedObjects=true;
2344 
2345  // Player final init
2346  C4Player *pPlr;
2347  for (pPlr = Players.First; pPlr; pPlr = pPlr->Next)
2348  {
2349  if (init_mode == IM_ReInit) pPlr->ScenarioInit();
2350  pPlr->FinalInit(!C4S.Head.SaveGame);
2351  }
2352 
2353  // Create viewports
2354  if (init_mode == IM_Normal)
2355  {
2356  for (pPlr = Players.First; pPlr; pPlr = pPlr->Next)
2357  if (pPlr->LocalControl)
2359  // Check fullscreen viewports
2361  // update halt state
2363 
2364  // Host: players without connected clients: remove via control queue
2365  if (Network.isEnabled() && Network.isHost())
2366  for (int32_t cnt = 0; cnt < Players.GetCount(); cnt++)
2367  if (Players.GetByIndex(cnt)->AtClient < 0)
2368  Players.Remove(Players.GetByIndex(cnt), true, false);
2369 
2370  // It should be safe now to reload stuff
2371  if (pFileMonitor) pFileMonitor->StartMonitoring();
2372  }
2373  return true;
2374 }
2375 
2377 {
2378  // engine functions
2383 
2384  // system functions: check if system group is open
2386  { LogFatal(LoadResStr("IDS_ERR_INVALIDSYSGRP")); return false; }
2388 
2389  // get scripts
2390  char fn[_MAX_FNAME+1] = { 0 };
2391  File.ResetSearch();
2392  while (File.FindNextEntry(C4CFN_ScriptFiles, fn, nullptr, !!fn[0]))
2393  {
2394  // host will be destroyed by script engine, so drop the references
2395  C4ScriptHost *scr = new C4ExtraScriptHost();
2396  scr->Reg2List(&ScriptEngine);
2398  }
2399 
2400  // if it's a physical group: watch out for changes
2401  if (!File.IsPacked() && Game.pFileMonitor)
2402  Game.pFileMonitor->AddDirectory(File.GetFullName().getData());
2403 
2404  // Prepare host for Objects.c script
2407  C4Value scen_obj_script_val;
2408  scen_obj_script_val.SetPropList(pScenarioObjectsScript->GetPropList());
2409  ::ScriptEngine.RegisterGlobalConstant("ScenarioObjects", scen_obj_script_val);
2410 
2411  // load standard clonk names
2412  Names.Load(File, C4CFN_Names);
2413 
2414  return true;
2415 }
2416 
2418 {
2419  // Link script engine (resolve includes/appends, generate code)
2421 
2422  // display errors
2423  LogF("C4AulScriptEngine linked - %d line%s, %d warning%s, %d error%s",
2424  ScriptEngine.lineCnt, (ScriptEngine.lineCnt != 1 ? "s" : ""),
2425  ScriptEngine.warnCnt, (ScriptEngine.warnCnt != 1 ? "s" : ""),
2426  ScriptEngine.errCnt, (ScriptEngine.errCnt != 1 ? "s" : ""));
2427 
2428  // update material pointers
2430 
2431  if (C4AulDebug *pDebug = C4AulDebug::GetDebugger())
2432  if (!pDebug->Listen(DebugPort, !!DebugWait))
2433  return false;
2434 
2435  return true;
2436 }
2437 
2439 {
2441 
2442  // update effect pointers
2444 
2445  // update material pointers
2447 
2448  return true;
2449 }
2450 
2452 {
2453  int32_t iPlrCnt = 0;
2454 
2456  {
2457  // Load players to restore from scenario
2458  C4PlayerInfoList LocalRestorePlayerInfos;
2459  LocalRestorePlayerInfos.Load(ScenarioFile, C4CFN_SavePlayerInfos, &ScenarioLangStringTable);
2460  // -- runtime join player restore
2461  // all restore functions will be executed on RestorePlayerInfos, because the main playerinfos may be more up-to-date
2462  // extract all players to temp store and update filenames to point there
2463  if (!LocalRestorePlayerInfos.RecreatePlayerFiles())
2464  { LogFatal(LoadResStr("IDS_ERR_NOPLRFILERECR")); return false; }
2465  // recreate the files
2466  if (!LocalRestorePlayerInfos.RecreatePlayers(numbers))
2467  { LogFatal(LoadResStr("IDS_ERR_NOPLRNETRECR")); return false; }
2468  }
2470  {
2471  // -- savegame player restore
2472  // for savegames or regular scenarios with restore infos, the player info list should have been loaded from the savegame
2473  // or got restored from game text in OpenScenario()
2474  // merge restore player info into main player info list now
2475  // -for single-host games, this will move all infos
2476  // -for network games, it will merge according to savegame association done in the lobby
2477  // for all savegames, script players get restored by adding one new script player for earch savegame script player to the host
2479  { LogFatal(LoadResStr("IDS_ERR_NOPLRSAVEINFORECR")); return false; }
2481  // try to associate local filenames (non-net+replay) or resources (net) with all player infos
2483  { LogFatal(LoadResStr("IDS_ERR_NOPLRFILERECR")); return false; }
2484  // recreate players by joining all players whose joined-flag is already set
2485  if (!PlayerInfos.RecreatePlayers(numbers))
2486  { LogFatal(LoadResStr("IDS_ERR_NOPLRSAVERECR")); return false; }
2487  }
2488 
2489  // any regular non-net non-replay game: Do the normal control queue join
2490  // this includes additional player joins in savegames
2491  if (!Network.isEnabled() && !Control.NoInput())
2493  {
2494  // error joining local players - either join was done earlier somehow,
2495  // or the player count check will soon end this round
2496  }
2497 
2498  // non-replay player joins will be done by player info list when go tick is reached
2499  // this is handled by C4Network2Players and needs no further treatment here
2500  // set iPlrCnt for player count check in host/single games
2502 
2503  // Check valid participating player numbers (host/single only)
2504  if (!Network.isEnabled() || (Network.isHost() && !fLobby))
2505  {
2506 #ifndef USE_CONSOLE
2507  // No players in fullscreen
2508  if (iPlrCnt==0)
2509  if (!Application.isEditor && !Control.NoInput())
2510  {
2511  LogFatal(LoadResStr("IDS_CNS_NOFULLSCREENPLRS")); return false;
2512  }
2513 #endif
2514  // Too many players
2515  if (iPlrCnt>Game.Parameters.MaxPlayers)
2516  {
2517  if (!Application.isEditor)
2518  {
2519  LogFatal(FormatString(LoadResStr("IDS_PRC_TOOMANYPLRS"),Game.Parameters.MaxPlayers).getData());
2520  return false;
2521  }
2522  else
2523  {
2525  }
2526  }
2527  }
2528  // Console and no real players: halt
2529  if (Console.Active)
2530  if (!fLobby)
2532  ++HaltCount;
2533  return true;
2534 }
2535 
2537 {
2538  // update random seed
2539  if (C4S.Head.NetworkGame || C4S.Head.Replay)
2540  {
2542  }
2543  // Randomize
2545 
2546  // Replay?
2547  if (C4S.Head.Replay)
2548  {
2549  // no joins
2550  PlayerFilenames[0]=0;
2551  // start playback
2553  return false;
2554  // no record!
2555  Record = false;
2556  }
2557  else if (Network.isEnabled())
2558  {
2559  // initialize
2561  return false;
2562  // league? always record
2563  if (Parameters.isLeague())
2564  Record = true;
2565  }
2566  // Otherwise: local game
2567  else
2568  {
2569  // init
2571  return false;
2572  }
2573 
2574  // record?
2575  if (Record)
2576  if (!Control.StartRecord(true, Parameters.doStreaming()))
2577  {
2578  // Special: If this happens for a league host, the game must not start.
2580  {
2581  LogFatal(LoadResStr("IDS_ERR_NORECORD"));
2582  return false;
2583  }
2584  else
2585  {
2586  Log(LoadResStr("IDS_ERR_NORECORD"));
2587  }
2588  }
2589 
2590  return true;
2591 }
2592 
2594  C4ID *idlist, int32_t maxidlist)
2595 {
2596  int32_t cnt,cnt2,ccount,cpos;
2597  for (cpos=0,cnt=0; rlist.GetID(cnt); cnt++)
2598  if (C4Id2Def(rlist.GetID(cnt,&ccount)))
2599  for (cnt2=0; cnt2<ccount; cnt2++)
2600  if (cpos<maxidlist)
2601  { idlist[cpos]=rlist.GetID(cnt); cpos++; }
2602  return cpos;
2603 }
2604 
2606 {
2607  int32_t cnt,tx,ty;
2608  for (cnt=0; cnt<35; cnt++) // cheap trys
2609  {
2611  if (GBackMat(tx,ty)==MEarth)
2612  if (CreateObject(id,nullptr,NO_OWNER,tx,ty,Random(360)))
2613  return true;
2614  }
2615  return false;
2616 }
2617 
2618 static bool PlaceVegetation_GetRandomPoint(int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt, C4PropList * shape_proplist, C4PropList * out_pos_proplist, int32_t *piTx, int32_t *piTy)
2619 {
2620  // Helper for C4Game::PlaceVegetation: return random position in rectangle. Use shape_proplist if provided.
2621  if (shape_proplist && out_pos_proplist)
2622  {
2623  C4AulParSet pars(C4VPropList(out_pos_proplist));
2624  if (!shape_proplist->Call(P_GetRandomPoint, &pars)) return false;
2625  *piTx = out_pos_proplist->GetPropertyInt(P_x);
2626  *piTy = out_pos_proplist->GetPropertyInt(P_y);
2627  }
2628  else
2629  {
2630  *piTx = iX + Random(iWdt);
2631  *piTy = iY + Random(iHgt);
2632  }
2633  return true;
2634 }
2635 
2636 static bool PlaceVegetation_IsPosInBounds(int32_t iTx, int32_t iTy, int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt, C4PropList * shape_proplist)
2637 {
2638  if (shape_proplist)
2639  {
2640  // check using shape proplist
2641  C4AulParSet pars(C4VInt(iTx), C4VInt(iTy));
2642  if (!shape_proplist->Call(P_IsPointContained, &pars)) return false;
2643  }
2644  else
2645  {
2646  // check using bounds rect
2647  if (iTy < iY) return false;
2648  }
2649  return true;
2650 }
2651 
2652 C4Object* C4Game::PlaceVegetation(C4PropList * PropList, int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt, int32_t iGrowth, C4PropList * shape_proplist, C4PropList * out_pos_proplist)
2653 {
2654  int32_t cnt,iTx,iTy,iMaterial;
2655 
2656  // Get definition
2657  C4Def* pDef;
2658  if (!PropList || !(pDef = PropList->GetDef())) return nullptr;
2659 
2660  // No growth specified: full growth
2661  if (iGrowth<=0)
2662  {
2663  iGrowth=FullCon;
2664  }
2665 
2666  // Place by placement type
2667  switch (PropList->GetPropertyInt(P_Placement))
2668  {
2669 
2670  // Surface soil
2671  case C4D_Place_Surface:
2672  for (cnt=0; cnt<20; cnt++)
2673  {
2674  // Random hit within target area
2675  if (!PlaceVegetation_GetRandomPoint(iX, iY, iWdt, iHgt, shape_proplist, out_pos_proplist, &iTx, &iTy)) break;
2676  // Above tunnel
2677  while ((iTy>0) && Landscape.GetBackPix(iTx,iTy) == 0) iTy--;
2678  // Above semi solid
2679  if (!AboveSemiSolid(iTx,iTy) || !Inside<int32_t>(iTy,50,::Landscape.GetHeight()-50))
2680  continue;
2681  // Still inside bounds?
2682  if (!PlaceVegetation_IsPosInBounds(iTx, iTy, iX, iY, iWdt, iHgt, shape_proplist)) continue;
2683  // Free above
2684  if (GBackSemiSolid(iTx,iTy-pDef->Shape.Hgt) || GBackSemiSolid(iTx,iTy-pDef->Shape.Hgt/2))
2685  continue;
2686  // Free upleft and upright
2687  if (GBackSemiSolid(iTx-pDef->Shape.Wdt/2,iTy-pDef->Shape.Hgt*2/3) || GBackSemiSolid(iTx+pDef->Shape.Wdt/2,iTy-pDef->Shape.Hgt*2/3))
2688  continue;
2689  // Soil check
2690  iTy+=3; // two pix into ground
2691  iMaterial = GBackMat(iTx,iTy);
2692  if (iMaterial!=MNone) if (::MaterialMap.Map[iMaterial].Soil)
2693  {
2694  iTy+=5;
2695  return CreateObjectConstruction(PropList,nullptr,NO_OWNER,iTx,iTy,iGrowth);
2696  }
2697  }
2698  break;
2699 
2700  // Underwater
2701  case C4D_Place_Liquid:
2702  // Random range
2703  if (!PlaceVegetation_GetRandomPoint(iX, iY, iWdt, iHgt, shape_proplist, out_pos_proplist, &iTx, &iTy)) return nullptr;
2704  // Find liquid
2705  if (!FindSurfaceLiquid(iTx,iTy,pDef->Shape.Wdt,pDef->Shape.Hgt))
2706  if (!FindLiquid(iTx,iTy,pDef->Shape.Wdt,pDef->Shape.Hgt))
2707  return nullptr;
2708  // Liquid bottom
2709  if (!SemiAboveSolid(iTx,iTy)) return nullptr;
2710  iTy+=3;
2711  // Still inside bounds?
2712  if (!PlaceVegetation_IsPosInBounds(iTx, iTy, iX, iY, iWdt, iHgt, shape_proplist)) return nullptr;
2713  // Create object
2714  return CreateObjectConstruction(PropList,nullptr,NO_OWNER,iTx,iTy,iGrowth);
2715  break;
2716 
2717  // Underground/Tunnel
2718  case C4D_Place_Subsurface:
2719  for (cnt=0; cnt<5; cnt++)
2720  {
2721  // Random range
2722  if (!PlaceVegetation_GetRandomPoint(iX, iY, iWdt, iHgt, shape_proplist, out_pos_proplist, &iTx, &iTy)) break;
2723  // Find tunnel
2724  if (!FindTunnel(iTx,iTy,pDef->Shape.Wdt,pDef->Shape.Hgt))
2725  continue;
2726  // Tunnel bottom
2727  if (!AboveSemiSolid(iTx,iTy)) continue;
2728  // Still inside bounds?
2729  if (!PlaceVegetation_IsPosInBounds(iTx, iTy, iX, iY, iWdt, iHgt, shape_proplist)) continue;
2730  // Soil check
2731  iTy+=3; // two pix into ground
2732  iMaterial = GBackMat(iTx,iTy);
2733  if (iMaterial!=MNone) if (::MaterialMap.Map[iMaterial].Soil)
2734  {
2735  // Create object
2736  iTy+=5;
2737  return CreateObjectConstruction(PropList,nullptr,NO_OWNER,iTx,iTy,iGrowth);
2738  }
2739  }
2740 
2741  // Under- or aboveground
2742  case C4D_Place_BothSurface:
2743  for (cnt=0; cnt<20; cnt++)
2744  {
2745  // Random hit within target area
2746  if (!PlaceVegetation_GetRandomPoint(iX, iY, iWdt, iHgt, shape_proplist, out_pos_proplist, &iTx, &iTy)) break;
2747  // Above semi solid
2748  if (!AboveSemiSolid(iTx,iTy) || !Inside<int32_t>(iTy,50,::Landscape.GetHeight()-50))
2749  continue;
2750  // Free above
2751  if (GBackSemiSolid(iTx,iTy-pDef->Shape.Hgt) || GBackSemiSolid(iTx,iTy-pDef->Shape.Hgt/2))
2752  continue;
2753  // Still inside bounds?
2754  if (!PlaceVegetation_IsPosInBounds(iTx, iTy, iX, iY, iWdt, iHgt, shape_proplist))
2755  continue;
2756  // Free upleft and upright
2757  if (GBackSemiSolid(iTx-pDef->Shape.Wdt/2,iTy-pDef->Shape.Hgt*2/3) || GBackSemiSolid(iTx+pDef->Shape.Wdt/2,iTy-pDef->Shape.Hgt*2/3))
2758  continue;
2759  // Soil check
2760  iTy+=3; // two pix into ground
2761  iMaterial = GBackMat(iTx,iTy);
2762  if (iMaterial!=MNone) if (::MaterialMap.Map[iMaterial].Soil)
2763  {
2764  iTy+=5;
2765  return CreateObjectConstruction(PropList,nullptr,NO_OWNER,iTx,iTy,iGrowth);
2766  }
2767  }
2768 
2769  }
2770 
2771  // Undefined placement type
2772  return nullptr;
2773 }
2774 
2776 {
2777  C4Def * pDef;
2778  if (!PropList || !(pDef = PropList->GetDef())) return nullptr;
2779  int32_t iX,iY;
2780  // Placement
2781  switch (PropList->GetPropertyInt(P_Placement))
2782  {
2783  // Running free
2784  case C4D_Place_Surface:
2786  if (!FindSolidGround(iX,iY,pDef->Shape.Wdt)) return nullptr;
2787  break;
2788  // In liquid
2789  case C4D_Place_Liquid:
2791  if (!FindSurfaceLiquid(iX,iY,pDef->Shape.Wdt,pDef->Shape.Hgt))
2792  if (!FindLiquid(iX,iY,pDef->Shape.Wdt,pDef->Shape.Hgt))
2793  return nullptr;
2794  iY+=pDef->Shape.Hgt/2;
2795  break;
2796  // Floating in air
2797  case C4D_Place_Air:
2798  iX=Random(::Landscape.GetWidth());
2799  for (iY=0; (iY<::Landscape.GetHeight()) && !GBackSemiSolid(iX,iY); iY++) {}
2800  if (iY<=0) return nullptr;
2801  iY=Random(iY);
2802  break;
2803  default:
2804  return nullptr;
2805  }
2806  // Create object
2807  return CreateObject(PropList,nullptr,NO_OWNER,iX,iY);
2808 }
2809 
2811 {
2812  const int32_t maxvid=100;
2813  int32_t cnt,vidnum;
2814  C4ID vidlist[maxvid];
2815  // Amount
2817  // List all valid IDs from C4S
2818  vidnum=ListExpandValids(C4S.Landscape.InEarth,vidlist,maxvid);
2819  // Place
2820  if (vidnum>0)
2821  for (cnt=0; cnt<amt; cnt++)
2822  PlaceInEarth(vidlist[Random(vidnum)]);
2823 
2824 }
2825 
2827 {
2828  const int32_t maxvid=100;
2829  int32_t cnt,vidnum;
2830  C4ID vidlist[maxvid];
2831  // Amount
2832  int32_t amt=(::Landscape.GetWidth()/50)*C4S.Landscape.VegLevel.Evaluate()/100;
2833  // Get percentage vidlist from C4S
2834  vidnum=ListExpandValids(C4S.Landscape.Vegetation,vidlist,maxvid);
2835  // Place vegetation
2836  if (vidnum>0)
2837  for (cnt=0; cnt<amt; cnt++)
2838  PlaceVegetation(C4Id2Def(vidlist[Random(vidnum)]),0,0,::Landscape.GetWidth(),::Landscape.GetHeight(),-1,nullptr,nullptr);
2839 }
2840 
2842 {
2843  int32_t cnt,cnt2;
2844  C4ID idAnimal; int32_t iCount;
2845  // Place animals
2846  for (cnt=0; (idAnimal=C4S.Animals.FreeLife.GetID(cnt,&iCount)); cnt++)
2847  for (cnt2=0; cnt2<iCount; cnt2++)
2848  PlaceAnimal(C4Id2Def(idAnimal));
2849  // Place nests
2850  for (cnt=0; (idAnimal=C4S.Animals.EarthNest.GetID(cnt,&iCount)); cnt++)
2851  for (cnt2=0; cnt2<iCount; cnt2++)
2852  PlaceInEarth(idAnimal);
2853 }
2854 
2855 
2857 {
2858  // Info
2860  // Overload clonk names from scenario file
2863  // scenario sections
2864  char fn[_MAX_FNAME+1] = { 0 };
2865  ScenarioFile.ResetSearch(); *fn=0;
2866  while (ScenarioFile.FindNextEntry(C4CFN_ScenarioSections, fn, nullptr, !!*fn))
2867  {
2868  // get section name
2869  char SctName[_MAX_FNAME+1];
2870  int32_t iWildcardPos = SCharPos('*', C4CFN_ScenarioSections);
2871  SCopy(fn + iWildcardPos, SctName, _MAX_FNAME);
2872  RemoveExtension(SctName);
2873  if (std::strlen(SctName)>C4MaxName || !*SctName)
2874  {
2875  DebugLog("invalid section name");
2876  LogFatal(FormatString(LoadResStr("IDS_ERR_SCENSECTION"), fn).getData()); return false;
2877  }
2878  // load this section into temp store
2879  C4ScenarioSection *pSection = new C4ScenarioSection(SctName);
2880  if (!pSection->ScenarioLoad(fn, false))
2881  { LogFatal(FormatString(LoadResStr("IDS_ERR_SCENSECTION"), fn).getData()); return false; }
2882  }
2883  // Success
2884  return true;
2885 }
2886 
2888 {
2889  // called for scenario local and definition local System.ocg groups
2890  C4Group SysGroup;
2891  char fn[_MAX_FNAME+1] = { 0 };
2892  if (SysGroup.OpenAsChild(&parent_group, C4CFN_System))
2893  {
2894  C4LangStringTable *pSysGroupString = new C4LangStringTable();
2896  // load custom scenario control definitions
2897  if (SysGroup.FindEntry(C4CFN_PlayerControls))
2898  {
2899  Log(LoadResStr("IDS_PRC_LOADSCEPLRCTRL"));
2900  C4PlayerControlFile PlayerControlFile;
2901  if (!PlayerControlFile.Load(SysGroup, C4CFN_PlayerControls, pSysGroupString))
2902  {
2903  // non-fatal error here
2904  Log(LoadResStr("IDS_PRC_LOADSCEPLRCTRLFAIL"));
2905  }
2906  else
2907  {
2908  // local definitions loaded successfully - merge into global definitions
2909  PlayerControlDefs.MergeFrom(PlayerControlFile.GetControlDefs());
2912  }
2913  }
2914  // load all scripts in there
2915  SysGroup.ResetSearch();
2916  while (SysGroup.FindNextEntry(C4CFN_ScriptFiles, fn, nullptr, !!fn[0]))
2917  {
2918  // host will be destroyed by script engine, so drop the references
2919  C4ScriptHost *scr = new C4ExtraScriptHost();
2920  scr->Reg2List(&ScriptEngine);
2921  scr->Load(SysGroup, fn, Config.General.LanguageEx, pSysGroupString);
2922  }
2923  // if it's a physical group: watch out for changes
2924  if (!SysGroup.IsPacked() && Game.pFileMonitor)
2925  Game.pFileMonitor->AddDirectory(SysGroup.GetFullName().getData());
2926  SysGroup.Close();
2927  // release string table if no longer used
2928  pSysGroupString->DelRef();
2929  }
2930  return true;
2931 }
2932 
2934 {
2935  C4CustomKey::CodeList Keys;
2936 
2937  // clear previous
2938  KeyboardInput.Clear();
2939 
2940  // globals
2945 
2946  // main ingame
2951 
2952  // debug mode & debug displays
2957 
2958  // playback speed - improve...
2961 
2962  // fullscreen menu
2963  Keys.clear(); Keys.emplace_back(K_LEFT);
2966  Keys.clear(); Keys.emplace_back(K_RIGHT);
2969  Keys.clear(); Keys.emplace_back(K_UP);
2972  Keys.clear(); Keys.emplace_back(K_DOWN);
2975  Keys.clear(); Keys.emplace_back(K_SPACE); Keys.emplace_back(K_RETURN);
2977  KeyboardInput.RegisterKey(new C4CustomKey(Keys, "FullscreenMenuOK", KEYSCOPE_FullSMenu, new C4KeyCBEx<C4FullScreen, BYTE> (FullScreen, COM_MenuEnter, &C4FullScreen::MenuKeyControl))); // name used by PlrControlKeyName
2978  Keys.clear(); Keys.emplace_back(K_ESCAPE);
2980  KeyboardInput.RegisterKey(new C4CustomKey(Keys, "FullscreenMenuCancel", KEYSCOPE_FullSMenu, new C4KeyCBEx<C4FullScreen, BYTE> (FullScreen, COM_MenuClose, &C4FullScreen::MenuKeyControl))); // name used by PlrControlKeyName
2981  Keys.clear(); Keys.emplace_back(K_SPACE);
2983  KeyboardInput.RegisterKey(new C4CustomKey(Keys, "FullscreenMenuOpen", KEYSCOPE_FreeView, new C4KeyCB <C4FullScreen> (FullScreen, &C4FullScreen::ActivateMenuMain))); // name used by C4MainMenu!
2985 
2986  // chat
2987  Keys.clear();
2988  Keys.emplace_back(K_RETURN);
2989  Keys.emplace_back(K_F2); // alternate chat key, if RETURN is blocked by player control
2992 
3000 
3001  // console keys
3010 
3011  // no default keys assigned
3019 
3020  // load any custom keysboard overloads
3022 
3023  // done, success
3024  return true;
3025 }
3026 
3028 {
3029  // Reload System.ocg string table
3031 }
3032 
3034 {
3035  // Load controls and default control sets
3036  C4PlayerControlFile PlayerControlFile;
3037  if (!PlayerControlFile.Load(Application.SystemGroup, C4CFN_PlayerControls, &MainSysLangStringTable)) { LogFatal("[!]Error loading player controls"); return false; }
3038  PlayerControlDefs = PlayerControlFile.GetControlDefs();
3042  // Merge w/ config settings into user control sets
3043  // User settings will be cleared and re-merged again later after scenario/definition control overloads, but initialization
3044  // is needed already for config dialogs
3045  if (!InitPlayerControlUserSettings()) return false;
3046  return true;
3047 }
3048 
3050 {
3051  // Merge config control settings with user settings
3056  return true;
3057 }
3058 
3059 C4Player *C4Game::JoinPlayer(const char *szFilename, int32_t iAtClient, const char *szAtClientName, C4PlayerInfo *pInfo)
3060 {
3061  assert(pInfo);
3062  C4Player *pPlr;
3063  // Join
3064  if (!( pPlr = Players.Join(szFilename,true,iAtClient,szAtClientName, pInfo, nullptr) )) return nullptr;
3065  // Player final init
3066  pPlr->FinalInit(true);
3067  // Create player viewport
3068  if (pPlr->LocalControl) ::Viewports.CreateViewport(pPlr->Number);
3069  // Check fullscreen viewports
3071  // Update menus
3072  Console.UpdateMenus();
3073  // Append player name to list of session player names (no duplicates)
3074  if (!SIsModule(PlayerNames.getData(), pPlr->GetName()))
3075  {
3076  if (PlayerNames) PlayerNames += ";";
3077  PlayerNames += pPlr->GetName();
3078  }
3079  // Success
3080  return pPlr;
3081 }
3082 
3084 {
3085  // Do the InitializePlayers callback once all player joins have finished with at least one human player
3087  {
3088  InitialPlayersJoined = true;
3090  }
3091 }
3092 
3093 void C4Game::FixRandom(uint64_t iSeed)
3094 {
3095  FixedRandom(iSeed);
3096 }
3097 
3099 {
3100  // Duplication safety
3101  if (GameOver) return false;
3102  // Flag, log, call
3103  GameOver=true;
3104  Log(LoadResStr("IDS_PRC_GAMEOVER"));
3106  // Flag all surviving players as winners
3107  for (C4Player *pPlayer = Players.First; pPlayer; pPlayer = pPlayer->Next)
3108  if (!pPlayer->Eliminated)
3109  pPlayer->EvaluateLeague(false, true);
3110  // Immediately save config so mission access gained by this round is stored if the game crashes during shutdown
3111  // or in a following round
3112  Config.Save();
3113  return true;
3114 }
3115 
3117 {
3118  // safety
3119  if (GameOverDlgShown) return;
3120  // flag, show
3121  GameOverDlgShown = true;
3122 #ifndef USE_CONSOLE
3123  if (!Application.isEditor)
3124  {
3125  C4GameOverDlg *pDlg = new C4GameOverDlg();
3126  pDlg->SetDelOnClose();
3127  if (!pDlg->Show(pGUI, true)) { delete pDlg; Application.QuitGame(); }
3128  }
3129 #endif
3130 }
3131 
3133 {
3135 }
3136 
3137 void C4Game::Synchronize(bool fSavePlayerFiles)
3138 {
3139  // Log
3140  LogSilentF("Network: Synchronization (Frame %i) [PlrSave: %d]",FrameCounter, fSavePlayerFiles);
3141  // callback to control (to start record)
3143  // Fix random
3145  // Synchronize members
3149  Objects.Synchronize();
3150  // synchronize local player files if desired
3151  // this will reset any InActionTimes!
3152  // (not in replay mode)
3153  if (fSavePlayerFiles && !Control.isReplay()) Players.SynchronizeLocalFiles();
3154  // callback to network
3156  // TransferZone synchronization: Must do this after dynamic creation to avoid synchronization loss
3157  // if OnSynchronized-callbacks do sync-relevant changes
3159 }
3160 
3161 bool C4Game::InitNetworkFromAddress(const char *szAddress)
3162 {
3163  StdCopyStrBuf strRefQueryFailed(LoadResStr("IDS_NET_REFQUERY_FAILED"));
3164  // Query reference
3165  C4Network2RefClient RefClient;
3166  if (!RefClient.Init() ||
3167  !RefClient.SetServer(szAddress) ||
3168  !RefClient.QueryReferences())
3169  { LogFatal(FormatString(strRefQueryFailed.getData(), RefClient.GetError()).getData()); return false; }
3170  // We have to wait for the answer
3171  StdStrBuf Message = FormatString(LoadResStr("IDS_NET_REFQUERY_QUERYMSG"), szAddress);
3172  Log(Message.getData());
3173  // Set up wait dialog
3174  C4GUI::MessageDialog *pDlg = nullptr;
3175  if (!Application.isEditor)
3176  {
3177  // create & show
3178  pDlg = new C4GUI::MessageDialog(Message.getData(), LoadResStr("IDS_NET_REFQUERY_QUERYTITLE"),
3180  if (!pDlg || !pDlg->Show(::pGUI, true)) return false;
3181  }
3182  // Wait for response
3183  while (RefClient.isBusy())
3184  {
3185  // Execute GUI
3186  if (!Application.ScheduleProcs(100) ||
3187  (pDlg && pDlg->IsAborted()))
3188  {
3189  delete pDlg;
3190  return false;
3191  }
3192  // Check if reference is received
3193  if (!RefClient.Execute(0))
3194  break;
3195  }
3196  // Close dialog
3197  delete pDlg;
3198  // Error?
3199  if (!RefClient.isSuccess())
3200  { LogFatal(FormatString(strRefQueryFailed.getData(), RefClient.GetError()).getData()); return false; }
3201  // Get references
3202  C4Network2Reference **ppRefs = nullptr; int32_t iRefCount;
3203  if (!RefClient.GetReferences(ppRefs, iRefCount) || iRefCount <= 0)
3204  { LogFatal(FormatString(strRefQueryFailed.getData(), LoadResStr("IDS_NET_REFQUERY_NOREF")).getData()); return false; }
3205  // Connect to first reference
3206  bool fSuccess = InitNetworkFromReference(*ppRefs[0]);
3207  // Remove references
3208  for (int i = 0; i < iRefCount; i++)
3209  delete ppRefs[i];
3210  delete[] ppRefs;
3211  return fSuccess;
3212 }
3213 
3214 bool C4Game::InitNetworkFromReferenceFile(const char *temp_filename)
3215 {
3216  // We need winsock for address parsing
3217  WinSockHolder ws;
3218  // Load reference from temp file + delete the temp file
3219  bool success = false;
3220  C4Network2Reference ref;
3221  StdStrBuf join_data;
3222  if (join_data.LoadFromFile(temp_filename))
3223  {
3224  CompileFromBuf<StdCompilerINIRead>(mkNamingAdapt(ref, "Reference"), join_data);
3225  success = true;
3226  }
3227  EraseFile(temp_filename);
3228  if (!success) return false;
3229  return InitNetworkFromReference(ref);
3230 }
3231 
3233 {
3234  // Find host data
3235  C4Client *pHostData = Reference.Parameters.Clients.getClientByID(C4ClientIDHost);
3236  if (!pHostData) { LogFatal(LoadResStr("IDS_NET_INVALIDREF")); return false; }
3237  // Save scenario title
3238  ScenarioTitle = Reference.getTitle();
3239  // Log
3240  LogF(LoadResStr("IDS_NET_JOINGAMEBY"), pHostData->getName());
3241  // Init clients
3242  if (!Clients.Init())
3243  return false;
3244  // Connect
3245  if (Network.InitClient(Reference, false) != C4Network2::IR_Success)
3246  {
3247  LogFatal(FormatString(LoadResStr("IDS_NET_NOHOSTCON"), pHostData->getName()).getData());
3248  return false;
3249  }
3250  // init control
3251  if (!Control.InitNetwork(Clients.getLocal())) return false;
3252  // init local player info list
3253  Network.Players.Init();
3254  return true;
3255 }
3256 
3258 {
3259  // Network not active?
3260  if (!NetworkActive)
3261  {
3262  // Clear client list
3263  if (!C4S.Head.Replay)
3264  Clients.Init();
3265  return true;
3266  }
3267  // network not active?
3268  if (C4S.Head.NetworkGame)
3269  { LogFatal(LoadResStr("IDS_NET_NODIRECTSTART")); return Clients.Init(); }
3270  // replay?
3271  if (C4S.Head.Replay)
3272  { LogFatal(LoadResStr("IDS_PRC_NONETREPLAY")); return true; }
3273  // clear client list
3274  if (!Clients.Init())
3275  return false;
3276  // init network as host
3277  if (!Network.InitHost(fLobby)) return false;
3278  // init control
3279  if (!Control.InitNetwork(Clients.getLocal())) return false;
3280  // init local player info list
3281  Network.Players.Init();
3282  // allow connect
3283  Network.AllowJoin(true);
3284  // do lobby (if desired)
3285  if (fLobby)
3286  {
3287  if (!Network.DoLobby()) return false;
3288  }
3289  else
3290  {
3291  // otherwise: start manually
3292  if (!Network.Start()) return false;
3293  }
3294  // ok
3295  return true;
3296 }
3297 
3299 {
3300 
3301  struct Check
3302  {
3303  int32_t maxNumber{0};
3304  Check() = default;
3305  // Check valid & maximum number & duplicate numbers
3306  bool that(C4Object* cObj)
3307  {
3308  // Invalid number
3309  if (cObj->Number<1)
3310  {
3311  LogFatal(FormatString("Invalid object enumeration number (%d) of object %s (x=%d)", cObj->Number, cObj->id.ToString(), cObj->GetX()).getData()); return false;
3312  }
3313  // Max
3314  if (cObj->Number>maxNumber) maxNumber=cObj->Number;
3315  // Duplicate
3316  for (C4Object *cObj2 : Objects)
3317  if (cObj2!=cObj)
3318  if (cObj->Number==cObj2->Number)
3319  { LogFatal(FormatString("Duplicate object enumeration number %d (%s and %s)",cObj2->Number,cObj->GetName(),cObj2->GetName()).getData()); return false; }
3320  for (C4Object *cObj2 : Objects.InactiveObjects)
3321  if (cObj2!=cObj)
3322  if (cObj->Number==cObj2->Number)
3323  { LogFatal(FormatString("Duplicate object enumeration number %d (%s and %s(i))",cObj2->Number,cObj->GetName(),cObj2->GetName()).getData()); return false; }
3324  return true;
3325  }
3326  };
3327 
3328  Check check;
3329  for (C4Object *cObj : Objects)
3330  {
3331  if (!check.that(cObj))
3332  return false;
3333  }
3334 
3335  for (C4Object *cObj : Objects.InactiveObjects)
3336  {
3337  if (!check.that(cObj))
3338  return false;
3339  }
3340 
3341  // Adjust enumeration index
3342  C4PropListNumbered::SetEnumerationIndex(check.maxNumber);
3343  // Done
3344  return true;
3345 }
3346 
3348 {
3349  C4ID idOvrl; C4Def *pDef;
3350  // set new values
3351  for (int32_t cnt=0; (idOvrl=C4S.Game.Realism.ValueOverloads.GetID(cnt)); cnt++)
3352  if ((pDef=::Definitions.ID2Def(idOvrl)))
3354 }
3355 
3357 {
3358  // Place environment objects
3359  int32_t cnt,cnt2;
3360  C4ID idType; int32_t iCount;
3361  for (cnt=0; (idType=C4S.Environment.Objects.GetID(cnt,&iCount)); cnt++)
3362  for (cnt2=0; cnt2<iCount; cnt2++)
3363  CreateObject(idType,nullptr);
3364 }
3365 
3367 {
3368  // Place rule objects
3369  int32_t cnt,cnt2;
3370  C4ID idType; int32_t iCount;
3371  for (cnt=0; (idType=Parameters.Rules.GetID(cnt,&iCount)); cnt++)
3372  for (cnt2=0; cnt2<std::max<int32_t>(iCount,1); cnt2++)
3373  CreateObject(idType,nullptr);
3374 }
3375 
3377 {
3378  // Place goal objects
3379  int32_t cnt,cnt2;
3380  C4ID idType; int32_t iCount;
3381  for (cnt=0; (idType=Parameters.Goals.GetID(cnt,&iCount)); cnt++)
3382  for (cnt2=0; cnt2<iCount; cnt2++)
3383  CreateObject(idType,nullptr);
3384 }
3385 
3386 void C4Game::SetInitProgress(float fToProgress)
3387 {
3388  // set new progress
3389  InitProgress=int32_t(fToProgress);
3390  // if progress is more than one percent, display it
3392  {
3394  GraphicsSystem.MessageBoard->LogNotify();
3395  }
3396  // Cheap hack to get the Console window updated while loading
3397  // (unless game is running, i.e. section change - section change would be quick and timer execution can mess with things unpredictably)
3398  if (!IsRunning)
3399  {
3401 #ifdef WITH_QT_EDITOR
3402  Application.ProcessQtEvents();
3403 #endif
3404  }
3405 }
3406 
3407 void C4Game::OnResolutionChanged(unsigned int iXRes, unsigned int iYRes)
3408 {
3409  // update anything that's dependant on screen resolution
3410  pGUI->SetBounds(C4Rect(0,0,iXRes,iYRes));
3411  if (FullScreen.Active)
3413  // note that this may fail if the gfx groups are closed already (runtime resolution change)
3414  // doesn't matter; old gfx are kept in this case
3417 }
3418 
3420 {
3421  // Layout changed: Re-resolve keys
3424 }
3425 
3426 bool C4Game::CreateSectionFromTempFile(const char *section_name, const char *temp_filename)
3427 {
3428  // Remove existing (temp) section of same name
3429  C4ScenarioSection *existing_section = pScenarioSections, *prev = nullptr;
3430  while (existing_section) if (SEqualNoCase(existing_section->name.getData(), section_name)) break; else existing_section = (prev = existing_section)->pNext;
3431  bool deleted_current_section = false;
3432  if (existing_section)
3433  {
3434  deleted_current_section = (existing_section == pCurrentScenarioSection);
3435  if (deleted_current_section)
3436  {
3437  pCurrentScenarioSection = nullptr;
3438  pScenarioObjectsScript = nullptr;
3439  }
3440  if (existing_section->pObjectScripts)
3441  {
3442  delete existing_section->pObjectScripts;
3443  }
3444  if (prev) prev->pNext = existing_section->pNext; else pScenarioSections = existing_section->pNext;
3445  existing_section->pNext = nullptr;
3446  delete existing_section;
3447  }
3448  // Create new (temp) section
3449  C4ScenarioSection *new_section = new C4ScenarioSection(section_name);
3450  if (!new_section->ScenarioLoad(temp_filename, true))
3451  {
3452  pScenarioSections = new_section->pNext;
3453  new_section->pNext = nullptr;
3454  delete new_section;
3455  return false;
3456  }
3457  // Re-Link current section into newly created section
3458  if (deleted_current_section)
3459  {
3460  pCurrentScenarioSection = new_section;
3461  pScenarioObjectsScript = new_section->pObjectScripts;
3462  }
3463  // Link new Objects.c (or re-link because old Objects.c was removed)
3465  return !!new_section;
3466 }
3467 
3468 bool C4Game::LoadScenarioSection(const char *szSection, DWORD dwFlags)
3469 {
3470  // note on scenario section saving:
3471  // if a scenario section overwrites a value that had used the default values in the main scenario section,
3472  // returning to the main section with an unsaved landscape (and thus an unsaved scenario core),
3473  // would leave those values in the altered state of the previous section
3474  // scenario designers should regard this and always define any values, that are defined in subsections as well
3475  C4Group hGroup, *pGrp;
3476  // if current section was the loaded section (maybe main, but need not for resumed savegames)
3478  {
3482  }
3483  // find section to load
3484  C4ScenarioSection *pLoadSect = pScenarioSections;
3485  while (pLoadSect) if (SEqualNoCase(pLoadSect->name.getData(), szSection)) break; else pLoadSect = pLoadSect->pNext;
3486  if (!pLoadSect)
3487  {
3488  DebugLogF("LoadScenarioSection: scenario section %s not found!", szSection);
3489  return false;
3490  }
3491  // save current section state
3492  if (pLoadSect != pCurrentScenarioSection && dwFlags & (C4S_SAVE_LANDSCAPE | C4S_SAVE_OBJECTS))
3493  {
3494  // ensure that the section file does point to temp store
3496  {
3497  DebugLogF("LoadScenarioSection(%s): could not extract section files of current section %s", szSection, pCurrentScenarioSection->name.getData());
3498  return false;
3499  }
3500  // open current group
3501  if (!(pGrp = pCurrentScenarioSection->GetGroupfile(hGroup)))
3502  {
3503  DebugLog("LoadScenarioSection: error opening current group file");
3504  return false;
3505  }
3506  // store landscape, if desired (w/o material enumeration - that's assumed to stay consistent during the game)
3507  if (dwFlags & C4S_SAVE_LANDSCAPE)
3508  {
3509  // storing the landscape implies storing the scenario core
3510  // otherwise, the ExactLandscape-flag would be lost
3511  // maybe imply exact landscapes by the existance of Landscape.png-files?
3512  C4Scenario rC4S = C4S;
3513  rC4S.SetExactLandscape();
3514  if (!rC4S.Save(*pGrp, true))
3515  {
3516  DebugLog("LoadScenarioSection: Error saving C4S");
3517  return false;
3518  }
3519  // landscape
3520  {
3521  C4DebugRecOff DBGRECOFF;
3522  if (!Landscape.Save(*pGrp))
3523  {
3524  DebugLog("LoadScenarioSection: Error saving Landscape");
3525  return false;
3526  }
3527  }
3528  // PXS
3529  if (!PXS.Save(*pGrp))
3530  {
3531  DebugLog("LoadScenarioSection: Error saving PXS");
3532  return false;
3533  }
3534  // MassMover (create copy, may not modify running data)
3535  C4MassMoverSet MassMoverSet;
3536  MassMoverSet.Copy(MassMover);
3537  if (!MassMoverSet.Save(*pGrp))
3538  {
3539  DebugLog("LoadScenarioSection: Error saving MassMover");
3540  return false;
3541  }
3542  }
3543  // store objects
3544  if (dwFlags & C4S_SAVE_OBJECTS)
3545  {
3546  C4ValueNumbers numbers;
3547  // objects: do not save info objects or inactive objects
3548  if (!SaveData(*pGrp,true,false, false, &numbers))
3549  {
3550  DebugLog("LoadScenarioSection: Error saving objects");
3551  return false;
3552  }
3553  }
3554  // close current group
3555  if (hGroup.IsOpen()) hGroup.Close();
3556  // mark modified
3558  }
3559  // open section group
3560  if (!(pGrp=pLoadSect->GetGroupfile(hGroup)))
3561  {
3562  DebugLog("LoadScenarioSection: error opening group file");
3563  return false;
3564  }
3565  // remove all objects
3566  // do correct removal calls, because this will stop fire sounds, etc.
3567  for (C4Object *obj : Objects)
3568  obj->AssignRemoval();
3569  for (C4Object *obj : Objects)
3570  if (obj->Status)
3571  {
3572  DebugLogF("LoadScenarioSection: WARNING: Object %d created in destruction process!", (int) obj->Number);
3573  ClearPointers(obj);
3574  }
3575  // Final removal in case objects got recreated
3576  // Also kill inactive objects if scenario is reinitialized
3577  DeleteObjects(!!(dwFlags & C4S_REINIT_SCENARIO));
3578  // remove global effects
3579  if (::ScriptEngine.pGlobalEffects && !(dwFlags & C4S_KEEP_EFFECTS))
3580  {
3582  // scenario section call might have been done from a global effect
3583  // rely on dead effect removal for actually removing the effects; do not clear the array here!
3584  }
3585  if (::GameScript.pScenarioEffects && !(dwFlags & C4S_KEEP_EFFECTS))
3587  // del particles as well
3589  // clear transfer zones
3590  TransferZones.Clear();
3591  // backup old sky
3592  std::string old_sky;
3593  old_sky = C4S.Landscape.SkyDef;
3594  // do not warn on ignored values in main section
3595  // they are caused because not all parts of scenario core are compiled on section change
3596  bool is_main_section = SEqualNoCase(pLoadSect->name.getData(), C4ScenSect_Main);
3597  // overload scenario values (fails if no scenario core is present; that's OK)
3598  C4S.Load(*pGrp, true, is_main_section);
3599  // determine whether a new sky has to be loaded
3600  bool fLoadNewSky = !SEqualNoCase(old_sky.c_str(), C4S.Landscape.SkyDef.c_str()) || pGrp->FindEntry(C4CFN_Sky ".*");
3601  // set new Objects.c source
3603  // remove reference to FoW from viewports, so that we can safely
3604  // reload the landscape and its FoW.
3606  // landscape initialization resets the RNG
3607  // set a new seed here to get new dynamic landscapes
3608  // TODO: add an option to disable this?
3609  RandomSeed = Random(2147483647);
3611  // re-init game in new section
3612  C4ValueNumbers numbers;
3613  if (!InitGame(*pGrp, (dwFlags & C4S_REINIT_SCENARIO) ? IM_ReInit : IM_Section, fLoadNewSky, &numbers))
3614  {
3615  DebugLog("LoadScenarioSection: Error reiniting game");
3617  return false;
3618  }
3619  // restore shelved proplists in case loading failed
3621  // set new current section
3622  pCurrentScenarioSection = pLoadSect;
3624  // Final init on game re-init (doing mostly player initialization)
3625  if (dwFlags & C4S_REINIT_SCENARIO)
3626  {
3628  // Extra InitializePlayers callback on the already-joined players to start intros, etc.
3629  // (unless the call is still pending - can happen if section is loaded during player join)
3632  }
3633  // resize viewports, and enable lighting again
3636  // done, success
3637  return true;
3638 }
3639 
3641 {
3642  // debug mode not allowed
3643  if (!Parameters.AllowDebug && !DebugMode) { GraphicsSystem.FlashMessage(LoadResStr("IDS_MSG_DEBUGMODENOTALLOWED")); return false; }
3644  DebugMode = !DebugMode;
3646  GraphicsSystem.FlashMessageOnOff(LoadResStr("IDS_CTL_DEBUGMODE"), DebugMode);
3647  return true;
3648 }
3649 
3650 bool C4Game::ActivateMenu(const char *szCommand)
3651 {
3652  // no new menu during round evaluation
3653  if (C4GameOverDlg::IsShown()) return false;
3654  // forward to primary player
3656  if (!pPlr) return false;
3657  pPlr->Menu.ActivateCommand(pPlr->Number, szCommand);
3658  return true;
3659 }
3660 
3662 {
3664  return true;
3665 }
3666 
3667 void C4Game::Abort(bool fApproved)
3668 {
3669  // league needs approval
3670  if (Network.isEnabled() && Parameters.isLeague() && !fApproved)
3671  {
3672  if (Control.isCtrlHost() && !Game.GameOver)
3673  {
3675  return;
3676  }
3678  {
3679  Network.Vote(VT_Kick, true, Control.ClientID());
3680  return;
3681  }
3682  }
3683  if (EvaluateOnAbort)
3684  {
3685  // Scenario forces evaluation. This is intended for scenarios that
3686  // always save progress in the players, such as Tower of Despair.
3687  Evaluate();
3688  }
3689  // hard-abort: eval league and quit
3690  // manually evaluate league
3691  Players.RemoveLocal(true, true);
3692  Players.RemoveAtRemoteClient(true, true);
3693  // normal quit
3695 }
3696 
3697 static const std::unordered_map<std::string, C4GUI::Icons> str_to_icon =
3698 {
3699  { "Locked", C4GUI::Ico_Ex_LockedFrontal },
3700  { "League", C4GUI::Ico_Ex_League },
3701  { "GameRunning", C4GUI::Ico_GameRunning },
3702  { "Lobby", C4GUI::Ico_Lobby },
3703  { "RuntimeJoin", C4GUI::Ico_RuntimeJoin },
3704 
3705  { "A", C4GUI::Ico_Controller_A },
3706  { "B", C4GUI::Ico_Controller_B },
3707  { "X", C4GUI::Ico_Controller_X },
3708  { "Y", C4GUI::Ico_Controller_Y },
3709  { "Back", C4GUI::Ico_Controller_Back },
3710  { "Start", C4GUI::Ico_Controller_Start },
3711  { "Dpad", C4GUI::Ico_Controller_Dpad },
3712  { "DpadLeft", C4GUI::Ico_Controller_DpadLeft },
3713  { "DpadRight", C4GUI::Ico_Controller_DpadRight },
3714  { "DpadDown", C4GUI::Ico_Controller_DpadDown },
3715  { "DpadUp", C4GUI::Ico_Controller_DpadUp },
3716  { "LeftShoulder", C4GUI::Ico_Controller_LeftShoulder },
3717  { "RightShoulder", C4GUI::Ico_Controller_RightShoulder },
3718  { "LeftTrigger", C4GUI::Ico_Controller_LeftTrigger },
3719  { "RightTrigger", C4GUI::Ico_Controller_RightTrigger },
3720  { "LeftStick", C4GUI::Ico_Controller_LeftStick },
3721  { "RightStick", C4GUI::Ico_Controller_RightStick },
3722 };
3723 
3724 bool GetTextSpecFacet(const char* szSpec, C4Facet& fct)
3725 {
3726  // safety
3727  assert(szSpec && *szSpec);
3728  if (!szSpec) return false;
3729  // Special icon?
3730  if (SEqual2(szSpec, "@Ico:"))
3731  {
3732  szSpec += 5;
3733  auto it = str_to_icon.find(szSpec);
3734  if (it != str_to_icon.end())
3735  {
3736  fct = C4GUI::Icon::GetIconFacet(it->second);
3737  return true;
3738  }
3739  }
3740 
3741  return false;
3742 }
3743 
3744 bool C4Game::DrawTextSpecImage(C4Facet &fctTarget, const char *szSpec, C4DrawTransform* pTransform, uint32_t dwClr)
3745 {
3746  // safety
3747  assert(szSpec && *szSpec);
3748  if (!szSpec) return false;
3749 
3750  C4Facet fctSource;
3751  if(GetTextSpecFacet(szSpec, fctSource))
3752  {
3753  fctSource.DrawXT(fctTarget.Surface, fctTarget.X, fctTarget.Y, fctTarget.Wdt, fctTarget.Hgt, 0, 0, pTransform);
3754  return true;
3755  }
3756  else
3757  {
3758  C4Def *pDef = C4Id2Def(C4ID(szSpec));
3759  if (!pDef) return false;
3760 
3761  pDef->Draw(fctTarget, false, dwClr, nullptr, 0, 0, pTransform);
3762  return true;
3763  }
3764 }
3765 
3766 float C4Game::GetTextSpecImageAspect(const char* szSpec)
3767 {
3768  // safety
3769  assert(szSpec && *szSpec);
3770  if (!szSpec) return -1.0f;
3771 
3772  C4Facet fctSource;
3773  if(GetTextSpecFacet(szSpec, fctSource))
3774  {
3775  return static_cast<float>(fctSource.Wdt) / static_cast<float>(fctSource.Hgt);
3776  }
3777  else
3778  {
3779  C4Def *pDef = C4Id2Def(C4ID(szSpec));
3780  if (!pDef) return -1.0f;
3781 
3782  C4DefGraphics* pGfx = &pDef->Graphics;
3783  if(pGfx->Type == C4DefGraphics::TYPE_Bitmap)
3784  {
3785  return static_cast<float>(pDef->PictureRect.Wdt) / static_cast<float>(pDef->PictureRect.Hgt);
3786  }
3787  else if (pGfx->Type == C4DefGraphics::TYPE_Mesh)
3788  {
3789  const StdMesh& mesh = *pGfx->Mesh;
3790  const StdMeshBox& box = mesh.GetBoundingBox();
3791  return (box.x2 - box.x1) / (box.y2 - box.y1);
3792  }
3793 
3794  return -1.0f;
3795  }
3796 }
3797 
3799 {
3800  // safety
3801  assert(pSpec);
3802  if (!pSpec) return false;
3803 
3804  // get source definition
3805  C4PropList *source_def_proplist = pSpec->GetPropertyPropList(P_Source);
3806  if (!source_def_proplist) return false;
3807  C4Def *source_def = source_def_proplist->GetDef();
3808  if (!source_def) return false;
3809 
3810  // get custom color
3811  uint32_t color = (uint32_t)pSpec->GetPropertyInt(P_Color);
3812 
3813  C4String *source_name = pSpec->GetPropertyStr(P_Name);
3814  if (!source_name)
3815  {
3816  // Base graphics
3817  source_def->Draw(fctTarget, false, color);
3818  }
3819  else
3820  {
3821  // Alternative named graphics
3822  C4DefGraphics *source_graphics = source_def->Graphics.Get(source_name->GetCStr());
3823  if (!source_graphics) return false;
3824  source_graphics->Draw(fctTarget, color, nullptr, 0,0, nullptr);
3825  }
3826  return true;
3827 }
3828 
3830 {
3831  // As these functions work stepwise, there's the old maximum speed of 50.
3832  // Use /fast to set to even higher speeds.
3833  FrameSkip = Clamp<int32_t>(FrameSkip + 1, 1, 50);
3834  FullSpeed = true;
3835  GraphicsSystem.FlashMessage(FormatString(LoadResStr("IDS_MSG_SPEED"), FrameSkip).getData());
3836  return true;
3837 }
3838 
3840 {
3841  FrameSkip = Clamp<int32_t>(FrameSkip - 1, 1, 50);
3842  if (FrameSkip == 1)
3843  FullSpeed = false;
3844  GraphicsSystem.FlashMessage(FormatString(LoadResStr("IDS_MSG_SPEED"), FrameSkip).getData());
3845  return true;
3846 }
3847 
3849 {
3850  return C4ChatDlg::ToggleChat();
3851 }
3852 
3853 C4Value C4Game::GRBroadcast(const char *szFunction, C4AulParSet *pPars, bool fPassError, bool fRejectTest)
3854 {
3855  std::string func{ szFunction };
3856  if (func[0] != '~')
3857  func.insert(0, 1, '~');
3858 
3859  // call objects first - scenario script might overwrite hostility, etc...
3860  C4Value vResult = ::Objects.GRBroadcast(func.c_str(), pPars, fPassError, fRejectTest);
3861  // rejection tests abort on first nonzero result
3862  if (fRejectTest) if (!!vResult) return vResult;
3863  // scenario script call
3864  return ::GameScript.Call(func.c_str(), pPars, fPassError);
3865 }
3866 
3868 {
3869  // Skip this if graphics haven't been initialized yet (happens when
3870  // we bail during initialization)
3871  if (!pDraw) return;
3872  // Default gamma
3873  pDraw->ResetGamma();
3874  pDraw->SetGamma(float(Config.Graphics.Gamma) / 100.0,
3875  float(Config.Graphics.Gamma) / 100.0,
3876  float(Config.Graphics.Gamma) / 100.0,
3878 }
3879 
3881 {
3882  // set in prop list (for savegames) and in sound system::
3883  C4SoundModifier *mod;
3884  if (new_modifier)
3885  {
3886  GlobalSoundModifier.SetPropList(new_modifier);
3887  mod = ::Application.SoundSystem.Modifiers.Get(new_modifier, true);
3888  }
3889  else
3890  {
3892  mod = nullptr;
3893  }
3895 }
3896 
3897 C4String *C4Game::GetTranslatedString(const C4Value &input_string, C4Value *selected_language, bool fail_silently) const
3898 {
3899  // Resolve a localized string
3900  // If a string is passed, just return it
3901  // If a proplist like { DE="Hallo, Welt!", US="Hello, world!" } is passed, return the string matching the selected language
3902  // Nothing?
3903  if (input_string.GetType() == C4V_Nil)
3904  {
3905  return nullptr;
3906  }
3907  // Non-localized string?
3908  if (input_string.GetType() == C4V_String)
3909  {
3910  return input_string._getStr();
3911  }
3912  // Invalid type for this function?
3913  C4PropList *p = input_string._getPropList();
3914  if (!p || p->GetPropertyStr(P_Function) != &::Strings.P[P_Translate])
3915  {
3916  if (fail_silently)
3917  {
3918  return nullptr;
3919  }
3920  else
3921  {
3922  throw C4AulExecError(FormatString("Invalid value for translation: %s", input_string.GetDataString().getData()).getData());
3923  }
3924  }
3925  // This is a proplist. Resolve the language as the key.
3926  char lang_code[3] = "";
3927  for (int32_t lang_index = 0; SCopySegment(Config.General.LanguageEx, lang_index, lang_code, ',', 2); ++lang_index)
3928  {
3929  C4String *lang_string = ::Strings.FindString(lang_code);
3930  if (lang_string) // If the string is not found, it cannot be the key in a prop list
3931  {
3932  C4Value localized_string_val;
3933  if (p->GetPropertyByS(lang_string, &localized_string_val))
3934  {
3935  C4String *localized_string = localized_string_val.getStr();
3936  if (localized_string)
3937  {
3938  // Found it!
3939  if (selected_language)
3940  {
3941  selected_language->SetString(lang_string);
3942  }
3943  return localized_string;
3944  }
3945  }
3946  }
3947  }
3948  // No language matched. Just use any property and assume it's a language key.
3949  for (C4String *lang_string : p->GetSortedLocalProperties(false))
3950  {
3951  C4Value localized_string_val;
3952  if (p->GetPropertyByS(lang_string, &localized_string_val))
3953  {
3954  C4String *localized_string = localized_string_val.getStr();
3955  if (localized_string)
3956  {
3957  // Found it!
3958  if (selected_language)
3959  {
3960  selected_language->SetString(lang_string);
3961  }
3962  return localized_string;
3963  }
3964  }
3965  }
3966  // No string properties. There's no localized information to be found.
3967  return nullptr;
3968 }
3969 
3971 {
3972  C4PropListScript *value_proplist = new C4PropListScript();
3973  value_proplist->SetProperty(P_Function, C4VString(&::Strings.P[P_Translate]));
3974  return value_proplist;
3975 }
void Copy(C4MassMoverSet &rSet)
#define C4CFN_MassMover
Definition: C4Components.h:76
char * GetFilename(char *szPath)
Definition: StdFile.cpp:45
C4SoundSystem SoundSystem
Definition: C4Application.h:42
bool DoGameOver()
Definition: C4Game.cpp:3098
C4IDList ValueOverloads
Definition: C4Scenario.h:109
C4EditCursor EditCursor
Definition: C4Console.h:90
bool Load(C4Group &hGroup, class C4LangStringTable *pLang)
const int C4MaxGammaUserRamps
Definition: C4Constants.h:34
C4Def * ID2Def(C4ID id)
void SetInitProgress(float fToProgress)
Definition: C4Game.cpp:3386
float y2
Definition: StdMesh.h:147
void InitValueOverloads()
Definition: C4Game.cpp:3347
bool FindEntry(const char *szWildCard, StdStrBuf *sFileName=nullptr, size_t *iSize=nullptr)
Definition: C4Group.cpp:1774
C4Group * pParentGroup
Definition: C4Game.h:88
void RecheckPlayers()
Definition: C4Teams.cpp:663
C4String P[P_LAST]
void ClearPointers(C4Object *pObj)
const char * GetData() const
StdCopyStrBuf Origin
Definition: C4Scenario.h:80
bool Load(C4Group &hGroup, const char *szFilename, C4LangStringTable *pLang)
void ClearAll(int32_t iClearFlag)
Definition: C4Effect.cpp:369
bool HasKeyboardFocus()
Definition: C4Gui.h:2668
bool IsRunning
Definition: C4Game.h:140
void Denumerate()
Definition: C4Value.cpp:281
bool InitMaterialTexture()
Definition: C4Game.cpp:807
bool InitGame(C4Group &hGroup, InitMode init_mode, bool fLoadSky, C4ValueNumbers *)
Definition: C4Game.cpp:2101
void RegisterGlobalConstant(const char *szName, const C4Value &rValue)
Definition: C4Aul.cpp:123
bool CreateSectionFromTempFile(const char *section_name, const char *temp_filename)
Definition: C4Game.cpp:3426
C4PlayerInfoList & RestorePlayerInfos
Definition: C4Game.h:72
bool Start()
Definition: C4Network2.cpp:505
int32_t GetActiveScriptPlayerCount(bool fCountSavegameResumes, bool fCountInvisible) const
void Any(T &keys)
bool NetworkRuntimeJoin
Definition: C4Scenario.h:79
#define SHA_DIGEST_LENGTH
Definition: SHA1.h:42
int32_t iLobbyTimeout
Definition: C4Game.h:120
C4ID id
Definition: C4Def.h:101
void ClearPointers(C4Object *pObj)
char ScenarioFilename[_MAX_PATH+1]
Definition: C4Game.h:102
C4Object * CreateObjectConstruction(C4PropList *type, C4Object *pCreator, int32_t owner, int32_t ctx=0, int32_t bty=0, int32_t con=1, bool terrain=false)
Definition: C4Game.cpp:1102
void Default()
Definition: C4GroupSet.cpp:57
bool ToggleTool()
Definition: C4ToolsDlg.h:71
const BYTE COM_MenuLeft
Definition: C4Constants.h:128
StdStrBuf TempScenarioFile
Definition: C4Game.h:127
int32_t GetX() const
Definition: C4Object.h:287
int32_t RandomSeed
Definition: C4Game.h:135
C4Config Config
Definition: C4Config.cpp:833
int32_t iTick255
Definition: C4Game.h:130
C4String * getStr() const
Definition: C4Value.h:117
std::string Material
Definition: C4Scenario.h:183
bool Init(C4Group &hGroup, bool fOverloadCurrent, bool fLoadSky, bool &rfLoaded, bool fSavegame)
bool GetSize(C4Rect *pRect)
Definition: C4AppT.cpp:108
bool ToggleChat()
Definition: C4Game.cpp:3848
C4Object * NewObject(C4PropList *ndef, C4Object *pCreator, int32_t owner, C4ObjectInfo *info, int32_t tx, int32_t ty, int32_t tr, C4Real xdir, C4Real ydir, C4Real rdir, int32_t con, int32_t iController, bool grow_from_center)
Definition: C4Game.cpp:1011
float Y
Definition: C4Facet.h:118
InitMode
Definition: C4Game.h:36
C4IDList Objects
Definition: C4Scenario.h:221
bool IsPacked() const
Definition: C4Group.cpp:2009
void Synchronize()
void Up(T &keys)
void SCopy(const char *szSource, char *sTarget, size_t iMaxL)
Definition: Standard.cpp:152
C4Player * GetLocalByIndex(int iIndex) const
C4SDefinitions Definitions
Definition: C4Scenario.h:233
std::unique_ptr< C4Network2Reference > pJoinReference
Definition: C4Game.h:108
StdStrBuf RecordDumpFile
Definition: C4Game.h:125
StdStrBuf GrabFileContents()
Definition: C4Aul.h:99
const char * ToString() const
Definition: C4Id.h:59
void StopRecord(StdStrBuf *pRecordName=nullptr, BYTE *pRecordSHA1=nullptr)
int EntryCount(const char *szWildCard=nullptr)
Definition: C4Group.cpp:1850
void InitEnvironment()
Definition: C4Game.cpp:3356
C4ID id
Definition: C4Object.h:108
bool isReplay() const
Definition: C4GameControl.h:98
C4AulUserFile * GetUserFile(int32_t handle)
Definition: C4Aul.cpp:252
C4ObjectPtr Cursor
Definition: C4Player.h:130
float Zoom
Definition: C4Facet.h:165
uint32_t Random()
Definition: C4Random.cpp:43
int32_t ControlTick
Definition: C4GameControl.h:89
C4Value & GlobalSoundModifier
Definition: C4Game.h:99
StdCopyStrBuf NextMission
Definition: C4Game.h:147
C4Console Console
Definition: C4Globals.cpp:45
bool RegisterGroup(C4Group &rGroup, bool fOwnGrp, int32_t Priority, int32_t Contents, bool fCheckContent=true)
Definition: C4GroupSet.cpp:88
bool Add(C4Object *nObj)
bool SynchronizeLocalFiles()
float GetTextSpecImageAspect(const char *szSpec)
Definition: C4Game.cpp:3766
int32_t iTick5
Definition: C4Game.h:130
C4PropListStatic * GetPropList()
Definition: C4Aul.h:151
bool Save(C4Group &hGroup, bool fSaveSection=false)
Definition: C4Scenario.cpp:114
bool ActivateMenuMain()
bool SemiAboveSolid(int32_t &rx, int32_t &ry)
C4MainMenu * pMenu
Definition: C4FullScreen.h:30
bool Load(C4Group &hGroup, const char *szFilename, const char *szLanguage=nullptr)
C4MainMenu Menu
Definition: C4Player.h:103
void SetExactLandscape()
Definition: C4Scenario.cpp:415
bool PopMaterial()
Definition: C4Console.cpp:703
void SyncClearance()
Definition: C4Game.cpp:3132
bool ItemIdentical(const char *szFilename1, const char *szFilename2)
Definition: StdFile.cpp:855
C4Game Game
Definition: C4Globals.cpp:52
char MissionAccess[CFG_MaxString+1]
Definition: C4Config.h:45
C4GameRes * iterRes(C4GameRes *pLast, C4Network2ResType eType=NRT_Null)
int32_t Number
Definition: C4Player.h:86
bool isCtrlHost() const
Definition: C4GameControl.h:99
~C4Game()
Definition: C4Game.cpp:114
C4GameScriptHost GameScript
C4SEnvironment Environment
Definition: C4Scenario.h:239
int32_t iTick35
Definition: C4Game.h:130
C4LangStringTable MainSysLangStringTable
Definition: C4Game.h:80
C4Rect PictureRect
Definition: C4Def.h:107
void Down(T &keys)
bool SaveScreenshotKey(bool fSaveAll)
std::string Loader
Definition: C4Scenario.h:65
C4AulScriptEngine ScriptEngine
Definition: C4Globals.cpp:43
C4Scenario C4S
Definition: C4Game.h:74
bool LoadEnumeration(C4Group &hGroup)
Definition: C4Material.cpp:587
std::string MissionAccess
Definition: C4Scenario.h:76
bool FinalInit()
Definition: C4Network2.cpp:537
static void RemoveSolidMasks()
void Clear()
Definition: StdBuf.h:466
C4GroupSet GroupSet
Definition: C4Game.h:87
C4ConfigGeneral General
Definition: C4Config.h:251
#define sprintf
Definition: Standard.h:164
C4ParticleSystem Particles
C4String * GetPropertyStr(C4PropertyName k) const
Definition: C4PropList.cpp:775
#define C4CFN_Material
Definition: C4Components.h:25
static void ResetEnumerationIndex()
Definition: C4PropList.cpp:99
int32_t ListExpandValids(C4IDList &rlist, C4ID *idlist, int32_t maxidlist)
Definition: C4Game.cpp:2593
StdCopyStrBuf ScenarioTitle
Definition: C4Game.h:103
#define C4ST_RESETPART
Definition: C4Stat.h:157
void Clear()
Definition: C4Scenario.cpp:410
#define C4CFN_Names
Definition: C4Components.h:145
void RecalculateViewports()
Definition: C4Viewport.cpp:989
void BuildTable()
Definition: C4DefList.cpp:514
const int32_t C4SyncCheckRate
Definition: C4GameControl.h:53
static constexpr const char * DirectJoinFilePrefix
Definition: C4Game.h:296
#define C4CFN_Script
Definition: C4Components.h:66
bool SCopySegment(const char *szString, int iSegment, char *sTarget, char cSeparator, int iMaxL, bool fSkipWhitespace)
Definition: Standard.cpp:273
void InitGoals()
Definition: C4Game.cpp:3376
bool Load(C4Group &hGroup, C4Scenario *pDefault, const char *szGameText, C4LangStringTable *pLang, const char *DefinitionFilenames, C4ScenarioParameters *pStartupScenarioParameters)
void Clear()
Definition: C4Texture.cpp:168
bool Replay
Definition: C4Scenario.h:72
C4ComponentHost GameText
Definition: C4Game.h:79
int32_t LoadEffects(C4Group &hGroup, const char *namespace_prefix, bool group_is_root)
C4Def * GetByPath(const char *szPath)
Definition: C4DefList.cpp:323
const char * GetSubkeyPath(const char *strSubkey)
Definition: C4Config.cpp:713
bool SavePNG(C4Group &hGroup, const char *szFilename, bool fSaveAlpha=true, bool fSaveOverlayOnly=false)
C4Value GRBroadcast(const char *szFunction, C4AulParSet *pPars, bool fPassError, bool fRejectTest)
void Cancel(T &keys)
bool DebugLog(const char *strMessage)
Definition: C4Log.cpp:280
C4ToolsDlg ToolsDlg
Definition: C4Console.h:88
C4Surface * pSurface
Definition: C4Window.h:275
bool RemoveAtRemoteClient(bool fDisonnected, bool fNoCalls)
BYTE GetBackPix(int32_t x, int32_t y) const
static void ClearNumberedPropLists()
Definition: C4PropList.cpp:137
void OnPlayerJoinFinished()
Definition: C4Game.cpp:3083
#define C4CFN_RoundResults
Definition: C4Components.h:129
C4SLandscape Landscape
Definition: C4Scenario.h:236
void Execute()
#define C4S_REINIT_SCENARIO
Definition: C4Scenario.h:51
void MergeFrom(const C4PlayerControlAssignmentSets &Src, C4PlayerControlAssignmentSet::MergeMode merge_mode)
bool RecreatePlayers(C4ValueNumbers *)
C4Value Call(const char *szFunction, C4AulParSet *pPars=nullptr, bool fPassError=false)
bool GameOverDlgShown
Definition: C4Game.h:117
C4Material * Map
Definition: C4Material.h:169
#define C4OS_NORMAL
Definition: C4Object.h:35
bool isEnabled() const
Definition: C4Network2.h:203
int CompareVersion(int iVer1, int iVer2, int iRVer1=C4XVER1, int iRVer2=C4XVER2)
Definition: C4GameVersion.h:51
float x2
Definition: StdMesh.h:147
bool TogglePause()
Definition: C4Console.cpp:617
C4KeyShiftState
bool GetModules(StdStrBuf *psOutModules) const
Definition: C4Scenario.cpp:422
C4Value C4VInt(int32_t i)
Definition: C4Value.h:242
bool PostInitMap()
bool ToggleAllowJoin()
Definition: C4Network2.cpp:758
void DoDlgShow(int32_t iChange, bool fUserToggle)
int32_t GetPropertyInt(C4PropertyName k, int32_t default_val=0) const
Definition: C4PropList.cpp:886
#define C4FxCall_RemoveClear
Definition: C4Effect.h:45
StdStrBuf GetDataString(int depth=10, const class C4PropListStatic *ignore_reference_parent=nullptr) const
Definition: C4Value.cpp:131
bool EvaluateOnAbort
Definition: C4Game.h:115
void Default()
Definition: C4PXS.cpp:158
bool TextOut(const char *szText, CStdFont &rFont, float fZoom, C4Surface *sfcDest, float iTx, float iTy, DWORD dwFCol=0xffffffff, BYTE byForm=ALeft, bool fDoMarkup=true)
Definition: C4Draw.cpp:567
void SetProperty(C4PropertyName k, const C4Value &to)
Definition: C4PropList.h:120
bool SEqualNoCase(const char *szStr1, const char *szStr2, int iLen)
Definition: Standard.cpp:207
static void Unload()
Definition: C4Startup.cpp:301
void RecheckAutoGeneratedTeams()
C4FullScreen FullScreen
Definition: C4Globals.cpp:46
void DeleteObjects(bool fDeleteInactive)
int32_t ObjectCount(C4ID id)
Definition: C4Game.cpp:1289
StdStrBuf DebugHost
Definition: C4Game.h:149
StdStrBuf sRankName
Definition: C4InfoCore.h:40
#define C4S_SAVE_LANDSCAPE
Definition: C4Scenario.h:48
void ClearPointers(C4Object *pObj)
static void SetEnumerationIndex(int32_t iMaxObjectNumber)
Definition: C4PropList.cpp:93
#define PSF_InitializeAmbience
Definition: C4GameScript.h:36
const uint32_t OCF_CrewMember
Definition: C4Constants.h:97
bool InitNetwork(C4Client *pLocal)
int32_t ShowCrewNames
Definition: C4Config.h:107
bool CreateSaveFolder(const char *strDirectory, const char *strLanguageTitle)
Definition: C4Config.cpp:572
void Vote(C4ControlVoteType eType, bool fApprove=true, int32_t iData=0)
void RaiseTerrain(int32_t tx, int32_t ty, int32_t wdt)
C4Client * getLocal() const
Definition: C4Client.h:161
C4RankSystem DefaultRanks
bool Execute()
Definition: C4Game.cpp:709
int32_t DigFreeRect(int32_t tx, int32_t ty, int32_t wdt, int32_t hgt, C4Object *by_object=nullptr, bool no_dig2objects=false, bool no_instability_check=false)
std::string Title
Definition: C4Scenario.h:64
Definition: C4Rect.h:27
bool InitLocal(C4Client *pLocal)
int32_t GetScreenWdt()
Definition: C4Gui.h:2821
const C4PlayerControlAssignmentSets & GetAssignmentSets() const
void Default() override
void OnSec1Timer() override
Definition: C4Game.cpp:1437
bool LoadEntry(const char *szEntryName, char **lpbpBuf, size_t *ipSize=nullptr, int iAppendZeros=0)
Definition: C4Group.cpp:1893
uint8_t BYTE
C4Player * First
Definition: C4PlayerList.h:31
static bool LoadFlags(C4Group &hGroup, const char *szEntryName, bool *pOverloadMaterials, bool *pOverloadTextures)
Definition: C4Texture.cpp:187
bool InitReplay(C4Group &rGroup)
void Set0()
Definition: C4Value.h:336
bool GetTextExtent(const char *szText, int32_t &rsx, int32_t &rsy, bool fCheckMarkup=true)
C4PropList * AllocateTranslatedString()
Definition: C4Game.cpp:3970
int32_t LoadMap(C4Group &hGroup, const char *szEntryName, bool *pOverloadMaterials, bool *pOverloadTextures)
Definition: C4Texture.cpp:211
bool RemoveLocal(bool fDisonnected, bool fNoCalls)
void Init(PointFreeFn fnPointFree, C4TransferZones *pTransferZones=nullptr)
void Format(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
Definition: StdBuf.cpp:174
C4IDList Vegetation
Definition: C4Scenario.h:167
virtual bool Name(const char *szName)
Definition: StdCompiler.h:77
C4TextureMap TextureMap
Definition: C4Texture.cpp:576
C4MessageInput MessageInput
int32_t LastInitProgress
Definition: C4Game.h:134
class C4ScenarioObjectsScriptHost * pScenarioObjectsScript
Definition: C4Game.h:90
bool Load(C4Group &hGroup, const char *szFilename=C4CFN_RoundResults)
C4AulExec AulExec
Definition: C4AulExec.cpp:29
bool SIsModule(const char *szList, const char *szString, int *ipIndex, bool fCaseSensitive)
Definition: Standard.cpp:541
C4Viewport * GetViewport(int32_t iPlayer, C4Viewport *pPrev=nullptr)
bool FindTunnel(int32_t &rx, int32_t &ry, int32_t width, int32_t height)
C4Scoreboard Scoreboard
Definition: C4Game.h:94
bool Reload(C4Def *pDef, DWORD dwLoadWhat, const char *szLanguage, C4SoundSystem *pSoundSystem=nullptr)
Definition: C4DefList.cpp:420
void Execute()
Definition: C4Network2.cpp:648
void FlashMessageOnOff(const char *strWhat, bool fOn)
bool LoadScenarioSection(const char *szSection, DWORD dwFlags)
Definition: C4Game.cpp:3468
C4Player * GetByIndex(int iIndex) const
bool Delete(const char *szFiles, bool fRecursive=false)
Definition: C4Group.cpp:1334
bool LandscapeFree(int32_t x, int32_t y)
Definition: C4Game.cpp:1988
#define _MAX_PATH
C4ClientList Clients
const int C4UpperBoardHeight
Definition: C4Constants.h:59
bool ViewportZoomIn()
bool DoSkipFrame
Definition: C4Game.h:138
class C4ScenarioParameterDefs & ScenarioParameterDefs
Definition: C4Game.h:75
#define C4GSCnt_Scenario
Definition: C4GroupSet.h:46
C4SoundModifier * Get(class C4PropList *props, bool create_if_not_found)
StdStrBuf PlayerNames
Definition: C4Game.h:81
const int32_t C4D_Place_BothSurface
Definition: C4Def.h:74
C4MouseControl MouseControl
Definition: C4Globals.cpp:47
void Default()
Definition: C4Scenario.cpp:78
size_t SLen(const char *sptr)
Definition: Standard.h:74
bool SaveData(C4Group &hGroup, bool fSaveSection, bool fSaveExact, bool fSaveSync, C4ValueNumbers *)
Definition: C4Game.cpp:1776
void AppendBackslash(char *szFilename)
Definition: StdFile.cpp:257
C4ObjectList * NextObjectShapes(C4ObjectList *pPrev, C4LSector **ppSct)
Definition: C4Sector.cpp:317
const StdMeshBox & GetBoundingBox() const
Definition: StdMesh.h:206
void UpdateMenus()
Definition: C4Console.cpp:504
void Set(C4Surface &rSfc)
Definition: C4Facet.cpp:459
void ScenarioInit()
C4ComponentHost Names
Definition: C4Game.h:78
#define PSF_OnGameOver
Definition: C4GameScript.h:138
bool InitPlayers(C4ValueNumbers *)
Definition: C4Game.cpp:2451
void CastObjects(C4ID id, C4Object *pCreator, int32_t num, int32_t level, int32_t tx, int32_t ty, int32_t iOwner=NO_OWNER, int32_t iController=NO_OWNER, C4ValueArray *out_objects=nullptr)
Definition: C4Game.cpp:1411
C4GraphicsResource GraphicsResource
const ReverseView reverse() const
Definition: C4ObjectList.h:104
StdStrBuf DebugPassword
Definition: C4Game.h:149
C4ObjectPtr Layer
Definition: C4Object.h:136
C4GameParameters & Parameters
Definition: C4Game.h:67
void Add(StdSchedulerProc *pProc)
bool ActivateCommand(int32_t iPlayer, const char *szCommand)
Definition: C4MainMenu.cpp:865
void SetDelOnClose(bool fToVal=true)
Definition: C4Gui.h:2190
C4Value C4VObj(C4Object *pObj)
Definition: C4Value.cpp:88
C4UpperBoard UpperBoard
unsigned int SCharCount(char cTarget, const char *szInStr, const char *cpUntil)
Definition: Standard.cpp:320
bool OpenSystemGroup()
Definition: C4Application.h:55
C4Player * JoinPlayer(const char *szFilename, int32_t iAtClient, const char *szAtClientName, C4PlayerInfo *pInfo)
Definition: C4Game.cpp:3059
const BYTE COM_MenuEnter
Definition: C4Constants.h:124
bool InitNetworkFromReferenceFile(const char *temp_filename)
Definition: C4Game.cpp:3214
bool Save(C4Group &hGroup)
Definition: C4PXS.cpp:325
bool OpenScenario()
Definition: C4Game.cpp:165