OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
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  TimeGo=false;
1462  Time=0;
1463  StartTime=0;
1465  FPS=cFPS=0;
1466  fScriptCreatedObjects=false;
1467  fLobby=fObserve=false;
1468  iLobbyTimeout=0;
1470  FullSpeed=false;
1471  FrameSkip=1; DoSkipFrame=false;
1474  Objects.Default();
1475  Players.Default();
1476  Weather.Default();
1477  Landscape.Default();
1479  MassMover.Default();
1480  PXS.Default();
1482  C4S.Default();
1483  ::Messages.Default();
1486  PathFinder.Default();
1488  GroupSet.Default();
1489  pParentGroup=nullptr;
1492  fResortAnyObject=false;
1493  pNetworkStatistics.reset();
1495  DebugPort = 0;
1496  DebugPassword.Clear();
1497  DebugHost.Clear();
1498  DebugWait = false;
1499  assert(!ScriptGuiRoot);
1500  ScriptGuiRoot.reset();
1501 }
1502 
1504 {
1505 
1506  // League game?
1507  bool fLeague = Network.isEnabled() && Network.isHost() && Parameters.isLeague();
1508 
1509  // Stop record
1510  StdStrBuf RecordName; BYTE RecordSHA[SHA_DIGEST_LENGTH];
1511  if (Control.isRecord())
1512  Control.StopRecord(&RecordName, fLeague ? RecordSHA : nullptr);
1513 
1514  // Send league result
1515  if (fLeague)
1516  Network.LeagueGameEvaluate(RecordName.getData(), RecordSHA);
1517 
1518  // Players
1519  // saving local players only, because remote players will probably not rejoin after evaluation anyway)
1520  Players.Evaluate();
1521  Players.Save(true);
1522 
1523  // Round results
1525 
1526  // Set game flag
1527  Log(LoadResStr("IDS_PRC_EVALUATED"));
1528  Evaluated=true;
1529 
1530 }
1531 
1532 void C4Game::DrawCrewOverheadText(C4TargetFacet &cgo, int32_t iPlayer)
1533 {
1534 
1535  // All drawing in this function must not be affected by zoom; but remember zoom and reset later.
1536  ZoomData r;
1537  pDraw->GetZoom(&r);
1538  const float zoom = r.Zoom;
1539  r.Zoom = 1.0;
1540  pDraw->SetZoom(r);
1541  // Offset for all text/objects
1542  const float fixedOffsetX = -cgo.X * cgo.Zoom + cgo.X;
1543  const float fixedOffsetY = (-cgo.Y - 10.0f) * cgo.Zoom + cgo.Y;
1544  // Draw cursor mark arrow & cursor object name
1546  for (C4Player *pPlr = Players.First; pPlr; pPlr = pPlr->Next)
1547  {
1548  // Draw a small selector & name above the cursor? F.e. after switching crew.
1549  const bool drawCursorInfo = (pPlr->Number == iPlayer || iPlayer == NO_OWNER) // only for the viewport's player..
1550  && (pPlr->CursorFlash && pPlr->Cursor); // ..and if the player wants to show their cursor.
1551  // Otherwise, for allied players we might want to draw player & crew names.
1552  // Note that these two conditions are generally mutually-exclusive.
1553  const bool drawPlayerAndCursorNames = (pPlr->Number != iPlayer) // Never for own player..
1554  && (Config.Graphics.ShowCrewNames || Config.Graphics.ShowCrewCNames) // ..and if the settings allow it..
1555  && !Hostile(iPlayer, pPlr->Number) && !pPlr->IsInvisible(); // ..and of course only if applicable.
1556 
1557  if (!drawPlayerAndCursorNames && !drawCursorInfo) continue;
1558 
1559  // Lambda to calculate correct drawing position of object, (re-)adjusted by zoom.
1560  float drawX, drawY, drawZoom;
1561  auto calculateObjectTextPosition = [&](C4Object *obj)
1562  {
1563  obj->GetDrawPosition(cgo, fixtof(obj->fix_x), fixtof(obj->fix_y), 1.0, drawX, drawY, drawZoom);
1564  drawX = drawX * cgo.Zoom + fixedOffsetX;
1565  drawY = drawY * cgo.Zoom - static_cast<float>(obj->Def->Shape.Hgt) / 2.0 + fixedOffsetY;
1566  };
1567 
1568  // Actual text output!
1569  if (drawPlayerAndCursorNames)
1570  {
1571  // We need to show crew names for that player, we do so for every crew-member.
1572  for (C4Object * const & crew : pPlr->Crew)
1573  {
1574  if (!crew->Status || !crew->Def) continue;
1575  if (crew->Contained) continue;
1576  if ((crew->OCF & OCF_CrewMember) == 0) continue;
1577  if (!crew->IsVisible(iPlayer, false)) continue;
1578 
1579  calculateObjectTextPosition(crew);
1580  drawY -= 5.0f; // aesthetical offset
1581 
1582  // compose string
1583  char szText[C4GM_MaxText + 1];
1586  sprintf(szText, "%s (%s)", crew->GetName(), pPlr->GetName());
1587  else
1588  SCopy(pPlr->GetName(), szText);
1589  else
1590  SCopy(crew->GetName(), szText);
1591  // Word wrap to cgo width
1592  int32_t iCharWdt, dummy;
1593  ::GraphicsResource.FontRegular.GetTextExtent("m", iCharWdt, dummy, false);
1594  int32_t iMaxLine = std::max<int32_t>(cgo.Wdt / std::max<int32_t>(1, iCharWdt), 20);
1595  SWordWrap(szText, ' ', '|', iMaxLine);
1596  // Center text vertically, too
1597  int textWidth, textHeight;
1598  ::GraphicsResource.FontRegular.GetTextExtent(szText, textWidth, textHeight, true);
1599  // Draw
1600  pDraw->TextOut(szText, ::GraphicsResource.FontRegular, 1.0, cgo.Surface, drawX, drawY - static_cast<float>(textHeight) / 2.0,
1601  pPlr->ColorDw | 0x7f000000, ACenter);
1602  }
1603  }
1604  else if (drawCursorInfo)
1605  {
1606  C4Object * const cursor = pPlr->Cursor;
1607  calculateObjectTextPosition(cursor);
1608  // Draw a down-arrow above the Clonk's head
1609  drawY += -fctCursor.Hgt;
1610  fctCursor.Draw(cgo.Surface, drawX - static_cast<float>(fctCursor.Wdt) / 2.0, drawY, 4);
1611  // And possibly draw some info text, too
1612  if (cursor->Info)
1613  {
1614  int32_t texthgt = ::GraphicsResource.FontRegular.GetLineHeight();
1615  StdStrBuf str;
1616  if (cursor->Info->Rank > 0)
1617  {
1618  str.Format("%s|%s", cursor->Info->sRankName.getData(), cursor->GetName());
1619  texthgt += texthgt;
1620  }
1621  else str = cursor->GetName();
1622 
1624  drawX,
1625  drawY - static_cast<float>(texthgt) / 2.0,
1626  0xffff0000, ACenter);
1627 
1628  }
1629  }
1630  }
1631  // Reset zoom
1632  r.Zoom = zoom;
1633  pDraw->SetZoom(r);
1634 }
1635 
1637 {
1638  // Frames
1640  // Ticks
1641  if (++iTick2==2) iTick2=0;
1642  if (++iTick3==3) iTick3=0;
1643  if (++iTick5==5) iTick5=0;
1644  if (++iTick10==10) iTick10=0;
1645  if (++iTick35==35) iTick35=0;
1646  if (++iTick255==255) iTick255=0;
1647  if (++iTick1000==1000) iTick1000=0;
1648  // FPS / time
1649  cFPS++; TimeGo = true;
1650  // Frame skip
1651  if (FrameCounter % FrameSkip) DoSkipFrame = true;
1652  // Control
1653  Control.Ticks();
1654  // Full speed
1655  if (GameGo) Application.NextTick(); // short-circuit the timer
1656  // statistics
1657  if (pNetworkStatistics) pNetworkStatistics->ExecuteFrame();
1658 }
1659 
1660 void C4Game::CompileFunc(StdCompiler *pComp, CompileSettings comp, C4ValueNumbers * numbers)
1661 {
1662  if (comp.init_mode == IM_Normal && comp.fExact)
1663  {
1664  pComp->Name("Game");
1665  pComp->Value(mkNamingAdapt(Time, "Time", 0));
1666  pComp->Value(mkNamingAdapt(FrameCounter, "Frame", 0));
1667  if (comp.fSync)
1668  {
1669  pComp->Value(mkNamingAdapt(Control.ControlTick, "ControlTick", 0));
1670  pComp->Value(mkNamingAdapt(Control.SyncRate, "SyncRate", C4SyncCheckRate));
1671  }
1672  pComp->Value(mkNamingAdapt(iTick2, "Tick2", 0));
1673  pComp->Value(mkNamingAdapt(iTick3, "Tick3", 0));
1674  pComp->Value(mkNamingAdapt(iTick5, "Tick5", 0));
1675  pComp->Value(mkNamingAdapt(iTick10, "Tick10", 0));
1676  pComp->Value(mkNamingAdapt(iTick35, "Tick35", 0));
1677  pComp->Value(mkNamingAdapt(iTick255, "Tick255", 0));
1678  pComp->Value(mkNamingAdapt(iTick1000, "Tick1000", 0));
1679  pComp->Value(mkNamingAdapt(InitialPlayersJoined, "InitialPlayersJoined", false));
1680  pComp->Value(mkNamingAdapt(StartupPlayerCount, "StartupPlayerCount", 0));
1681  pComp->Value(mkNamingAdapt(StartupTeamCount, "StartupTeamCount", 0));
1682  pComp->Value(mkNamingAdapt(C4PropListNumbered::EnumerationIndex,"ObjectEnumerationIndex",0));
1683  pComp->Value(mkNamingAdapt(mkStringAdaptMA(CurrentScenarioSection), "CurrentScenarioSection", ""));
1684  pComp->Value(mkNamingAdapt(fResortAnyObject, "ResortAnyObj", false));
1685  pComp->Value(mkNamingAdapt(mkParAdapt(GlobalSoundModifier, numbers), "GlobalSoundModifier", C4Value()));
1686  pComp->Value(mkNamingAdapt(NextMission, "NextMission", StdCopyStrBuf()));
1687  pComp->Value(mkNamingAdapt(NextMissionText, "NextMissionText", StdCopyStrBuf()));
1688  pComp->Value(mkNamingAdapt(NextMissionDesc, "NextMissionDesc", StdCopyStrBuf()));
1689  pComp->NameEnd();
1690 
1691  // Music settings
1692  pComp->Value(mkNamingAdapt(::Application.MusicSystem, "Music"));
1693 
1694  // scoreboard compiles into main level [Scoreboard]
1695  pComp->Value(mkNamingAdapt(Scoreboard, "Scoreboard"));
1696  // Keyboard status of global keys synchronized for exact (runtime join) only; not for savegames,
1697  // as keys might be released between a savegame save and its resume
1698  }
1699 
1700  if (comp.fExact)
1701  {
1702  pComp->Value(mkNamingAdapt(Weather, "Weather"));
1703  pComp->Value(mkNamingAdapt(Landscape, "Landscape"));
1704  pComp->Value(mkNamingAdapt(Landscape.GetSky(), "Sky"));
1705 
1706  // save custom GUIs only if a real savegame and not for editor-scenario-saves or section changes
1707  if (comp.init_mode == IM_Normal)
1708  {
1709  pComp->Name("GUI");
1710  if (pComp->isDeserializer())
1711  {
1712  C4Value val;
1713  pComp->Value(mkNamingAdapt(mkParAdapt(val, numbers), "ScriptGUIs", C4VNull));
1714  // if loading, assume
1715  assert(ScriptGuiRoot->GetID() == 0); // ID of 0 means "had no subwindows ever" aka "is fresh" for root
1716  // we will need to denumerate and create the actual GUI post-loading
1717  // for now, just remember our enumerated ID
1718  if (val.GetType() == C4V_Enum)
1719  {
1720  int enumID = val._getInt();
1721  ScriptGuiRoot->SetEnumeratedID(enumID);
1722  }
1723  }
1724  else
1725  {
1726  C4Value *val = new C4Value(ScriptGuiRoot->ToC4Value());
1727  pComp->Value(mkNamingAdapt(mkParAdapt(*val, numbers), "ScriptGUIs", C4VNull));
1728  }
1729  pComp->NameEnd();
1730  }
1731  }
1732 
1733  if (comp.fPlayers)
1734  {
1735  assert(pComp->isSerializer());
1736  // player parsing: Parse all players
1737  // This doesn't create any players, but just parses existing by their ID
1738  // Primary player ininitialization (also setting ID) is done by player info list
1739  // Won't work this way for binary mode!
1740  for (C4Player *pPlr=Players.First; pPlr; pPlr=pPlr->Next)
1741  pComp->Value(mkNamingAdapt(mkParAdapt(*pPlr, numbers), FormatString("Player%d", pPlr->ID).getData()));
1742  }
1743 
1744  // Section load: Clear existing prop list numbering to make room for the new objects
1745  // Numbers will be re-acquired in C4GameObjects::PostLoad
1747 
1748  pComp->Value(mkParAdapt(Objects, !comp.fExact, numbers));
1749 
1750  pComp->Value(mkNamingAdapt(mkParAdapt(ScriptEngine, comp.init_mode == IM_Section, numbers), "Script"));
1751 }
1752 
1753 bool C4Game::CompileRuntimeData(C4Group &hGroup, InitMode init_mode, bool exact, bool sync, C4ValueNumbers * numbers)
1754 {
1755  ::Objects.Clear(init_mode != IM_Section);
1756  GameText.Load(hGroup,C4CFN_Game);
1757  CompileSettings Settings(init_mode, false, exact, sync);
1758  // C4Game is not defaulted on compilation.
1759  // Loading of runtime data overrides only certain values.
1760  // Doesn't compile players; those will be done later
1761  if (GameText.GetData())
1762  {
1763  if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(
1764  mkParAdapt(*this, Settings, numbers),
1766  return false;
1767  // Objects
1768  int32_t iObjects = Objects.ObjectCount();
1769  if (iObjects) { LogF(LoadResStr("IDS_PRC_OBJECTSLOADED"),iObjects); }
1770  }
1771  // Success
1772  return true;
1773 }
1774 
1775 bool C4Game::SaveData(C4Group &hGroup, bool fSaveSection, bool fSaveExact, bool fSaveSync, C4ValueNumbers * numbers)
1776 {
1777  if (fSaveExact)
1778  {
1779  StdStrBuf Buf;
1780  // Decompile (without players for scenario sections)
1781  DecompileToBuf_Log<StdCompilerINIWrite>(mkParAdapt(*this, CompileSettings(fSaveSection ? IM_Section : IM_Normal, !fSaveSection && fSaveExact, fSaveExact, fSaveSync), numbers), &Buf, "Game");
1782 
1783  // Empty? All default save a Game.txt anyway because it is used to signal the engine to not load Objects.c
1784  if (!Buf.getLength()) Buf.Copy(" ");
1785 
1786  // Save
1787  return hGroup.Add(C4CFN_Game,Buf,false,true);
1788  }
1789  else
1790  {
1791  // Clear any exact game data in case scenario is saved from savegame resume
1792  hGroup.Delete(C4CFN_Game);
1793 
1794  // Save objects to file using system scripts
1795  int32_t objects_file_handle = ::ScriptEngine.CreateUserFile();
1796  C4AulParSet pars(objects_file_handle);
1797  C4Value result_c4v(::ScriptEngine.GetPropList()->Call(PSF_SaveScenarioObjects, &pars));
1798  bool result = !!result_c4v;
1799  if (result_c4v.GetType() == C4V_Nil)
1800  {
1801  // Function returned nil: This usually means there was a script error during object writing.
1802  // It could also mean the scripter overloaded global func SaveScenarioObjects and returned nil.
1803  // In either case, objects will not match landscape any more, so better fail and don't save at all.
1804  LogF("ERROR: No valid result from global func " PSF_SaveScenarioObjects ". Saving objects failed.");
1805  }
1806  else
1807  {
1808  // Function completed successfully (returning true or false)
1809  C4AulUserFile *file = ::ScriptEngine.GetUserFile(objects_file_handle);
1810  if (!result || !file || !file->GetFileLength())
1811  {
1812  // Nothing written? Then we don't have objects.
1814  // That's OK; not an error.
1815  result = true;
1816  }
1817  else
1818  {
1819  // Write objects script to file!
1820  StdStrBuf data = file->GrabFileContents();
1821  result = hGroup.Add(C4CFN_ScenarioObjectsScript, data, false, true);
1822  }
1823  }
1824  ::ScriptEngine.CloseUserFile(objects_file_handle);
1825  return result;
1826  }
1827 }
1828 
1830 {
1831 
1832  // Game not running
1833  if (!FrameCounter)
1834  {
1835  char* bpBytes;
1836  size_t iSize;
1837  StdStrBuf realFilename;
1838 
1839  if(ScenarioFile.FindEntry(FormatString("%s.*",C4CFN_ScenarioTitle).getData(),&realFilename,&iSize))
1840  if (ScenarioFile.LoadEntry(realFilename.getData(),&bpBytes,&iSize))
1841  hGroup.Add(realFilename.getData(),bpBytes,iSize,false,true);
1842  }
1843 
1844  // Fullscreen screenshot
1845  else if (!Application.isEditor && Application.Active)
1846  {
1847  C4Surface * sfcPic; int32_t iSfcWdt=200,iSfcHgt=150;
1848  if (!(sfcPic = new C4Surface(iSfcWdt,iSfcHgt,0))) return false;
1849 
1850  // Fullscreen
1853  sfcPic,0,0,iSfcWdt,iSfcHgt);
1854 
1855  bool fOkay=true;
1856  fOkay = sfcPic->SavePNG(Config.AtTempPath(C4CFN_TempTitle), false, false, false);
1857  StdStrBuf destFilename = FormatString("%s.png",C4CFN_ScenarioTitle);
1858  delete sfcPic; if (!fOkay) return false;
1859  if (!hGroup.Move(Config.AtTempPath(C4CFN_TempTitle),destFilename.getData())) return false;
1860  }
1861 
1862  return true;
1863 }
1864 
1865 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)
1866 {
1867  // compose key
1868  C4KeyCodeEx Key(vk_code, C4KeyShiftState(fAlt*KEYS_Alt + fCtrl*KEYS_Control + fShift*KEYS_Shift), fRepeated);
1869  return DoKeyboardInput(Key, eEventType, pForDialog, fPlrCtrlOnly, iStrength);
1870 }
1871 
1872 
1873 bool C4Game::DoKeyboardInput(C4KeyCodeEx Key, C4KeyEventType eEventType, class C4GUI::Dialog *pForDialog, bool fPlrCtrlOnly, int32_t iStrength)
1874 {
1875  Key.FixShiftKeys();
1876  // compose keyboard scope
1877  DWORD InScope = 0;
1878  if (fPlrCtrlOnly)
1879  InScope = KEYSCOPE_Control;
1880  else
1881  {
1882  if (IsRunning) InScope = KEYSCOPE_Generic;
1883  // if GUI has keyfocus, this overrides regular controls
1884  if (pGUI->HasKeyboardFocus() || pForDialog)
1885  {
1886  InScope |= KEYSCOPE_Gui;
1887  // control to console mode dialog: Make current keyboard target the active dlg,
1888  // so it can process input
1889  if (pForDialog) pGUI->ActivateDialog(pForDialog);
1890  // any keystroke in GUI resets tooltip times
1891  pGUI->KeyAny();
1892  }
1893  else
1894  {
1895  if (!Application.isEditor)
1896  {
1897  if (FullScreen.pMenu && FullScreen.pMenu->IsActive()) // fullscreen menu
1898  InScope |= KEYSCOPE_FullSMenu;
1899  else if (Game.C4S.Head.Replay && C4S.Head.Film) // film view only
1900  InScope |= KEYSCOPE_FilmView;
1901  else if (::Viewports.GetViewport(NO_OWNER)) // NO_OWNER-viewport-controls
1902  InScope |= KEYSCOPE_FreeView;
1903  else
1904  {
1905  // regular player viewport controls
1906  InScope |= KEYSCOPE_FullSView;
1907  // player controls disabled during round over dlg
1908  if (!C4GameOverDlg::IsShown()) InScope |= KEYSCOPE_Control;
1909  }
1910  }
1911  else
1912  // regular player viewport controls
1913  InScope |= KEYSCOPE_Control;
1914  }
1915  // fullscreen/console (in running game)
1916  if (IsRunning)
1917  {
1918  if (FullScreen.Active) InScope |= KEYSCOPE_Fullscreen;
1919  if (Console.Active) InScope |= KEYSCOPE_Console;
1920  }
1921  }
1922  // okay; do input
1923  if (KeyboardInput.DoInput(Key, eEventType, InScope, iStrength))
1924  return true;
1925 
1926  // unprocessed key
1927  return false;
1928 }
1929 
1931 {
1932  // Network hosts only
1933  if (Network.isEnabled() && !Network.isHost())
1934  { Log(LoadResStr("IDS_GAME_NOCLIENTSAVE")); return false; }
1935 
1936  return true;
1937 }
1938 
1939 bool C4Game::QuickSave(const char *strFilename, const char *strTitle, bool fForceSave)
1940 {
1941  // Check
1942  if (!fForceSave) if (!CanQuickSave()) return false;
1943 
1944  // Create savegame folder
1945  if (!Config.General.CreateSaveFolder(Config.AtUserDataPath(C4CFN_Savegames), LoadResStr("IDS_GAME_SAVEGAMESTITLE")))
1946  { Log(LoadResStr("IDS_GAME_FAILSAVEGAME")); return false; }
1947 
1948  // Create savegame subfolder(s)
1949  char strSaveFolder[_MAX_PATH + 1];
1950  for (uint32_t i = 0; i < SCharCount(DirectorySeparator, strFilename); i++)
1951  {
1952  SCopy(Config.AtUserDataPath(C4CFN_Savegames), strSaveFolder); AppendBackslash(strSaveFolder);
1953  SCopyUntil(strFilename, strSaveFolder + SLen(strSaveFolder), DirectorySeparator, _MAX_PATH, i);
1954  if (!Config.General.CreateSaveFolder(strSaveFolder, strTitle))
1955  { Log(LoadResStr("IDS_GAME_FAILSAVEGAME")); return false; }
1956  }
1957 
1958  // Compose savegame filename
1959  StdStrBuf strSavePath;
1960  strSavePath.Format("%s%c%s", Config.AtUserDataPath(C4CFN_Savegames), DirectorySeparator, strFilename);
1961 
1962  // Must not be the scenario file that is currently open
1963  if (ItemIdentical(ScenarioFilename, strSavePath.getData()))
1964  {
1965  StartSoundEffect("UI::Error");
1966  ::GraphicsSystem.FlashMessage(LoadResStr("IDS_GAME_NOSAVEONCURR"));
1967  Log(LoadResStr("IDS_GAME_FAILSAVEGAME"));
1968  return false;
1969  }
1970 
1971  // Wait message
1972  Log(LoadResStr("IDS_HOLD_SAVINGGAME"));
1973  GraphicsSystem.MessageBoard->EnsureLastMessage();
1974 
1975  // Save to target scenario file
1976  C4GameSave *pGameSave;
1977  pGameSave = new C4GameSaveSavegame();
1978  if (!pGameSave->Save(strSavePath.getData()))
1979  { Log(LoadResStr("IDS_GAME_FAILSAVEGAME")); delete pGameSave; return false; }
1980  delete pGameSave;
1981 
1982  // Success
1983  Log(LoadResStr("IDS_CNS_GAMESAVED"));
1984  return true;
1985 }
1986 
1987 bool LandscapeFree(int32_t x, int32_t y)
1988 {
1989  if (!Inside<int32_t>(x,0,::Landscape.GetWidth()-1) || !Inside<int32_t>(y,0,::Landscape.GetHeight()-1)) return false;
1990  return !DensitySolid(GBackDensity(x,y));
1991 }
1992 
1993 static void FileMonitorCallback(const char * file, const char * extrafile)
1994 {
1995  Game.ReloadFile(file);
1996 }
1997 
1998 bool C4Game::ReloadFile(const char *szFile)
1999 {
2000  // not in network
2001  if (::Network.isEnabled()) return false;
2002  const char *szRelativePath = Config.AtRelativePath(szFile);
2003  // a definition? or part of a definition?
2004  C4Def *pDef;
2005  if ((pDef = ::Definitions.GetByPath(szRelativePath)))
2006  return ReloadDef(pDef->id);
2007  // script?
2008  if (ScriptEngine.ReloadScript(szRelativePath, Config.General.LanguageEx))
2009  {
2011  }
2012  return true;
2013 }
2014 
2016 {
2017  bool fSucc;
2018  // not in network
2019  if (::Network.isEnabled()) return false;
2020  // syncronize (close menus with dead surfaces, etc.)
2021  // no need to sync back player files, though
2022  Synchronize(false);
2023  // SolidMasks might be updated
2025  // reload def
2026  C4Def *pDef = ::Definitions.ID2Def(id);
2027  if (!pDef) return false;
2028  // Open Graphics.ocg -- we might need to fetch some shader (slices)
2029  // from there when reloading mesh materials.
2030  if (!::GraphicsResource.RegisterGlobalGraphics()) return false;
2031  if (!::GraphicsResource.RegisterMainGroups()) return false;
2032  // Message
2033  LogF("Reloading %s from %s",pDef->id.ToString(),GetFilename(pDef->Filename));
2034  // Reload def
2036  {
2037  // update script engine - this will also do include callbacks and Freeze() pDef
2039  // Success, update all concerned object faces
2040  // may have been done by graphics-update already - but not for objects using graphics of another def
2041  // better update everything :)
2042  for (C4Object *obj : Objects)
2043  {
2044  if (obj->id == id)
2045  obj->UpdateFace(true);
2046  }
2047  fSucc = true;
2048  }
2049  else
2050  {
2051  // Failure, remove all objects of this type
2052  for (C4Object *obj : Objects)
2053  if (obj->id == id)
2054  obj->AssignRemoval();
2055  // safety: If a removed def is being profiled, profiling must stop
2057  // Kill def
2058  ::Definitions.Remove(pDef);
2059  // Log
2060  Log("Reloading failure. All objects of this type removed.");
2061  fSucc = false;
2062  }
2063  // close Graphics.ocg again
2065  // update game messages
2066  ::Messages.UpdateDef(id);
2067  // re-put removed SolidMasks
2069  // done
2070  return fSucc;
2071 }
2072 
2073 bool C4Game::ReloadParticle(const char *szName)
2074 {
2075  // not in network
2076  if (::Network.isEnabled()) return false;
2077  // safety
2078  if (!szName) return false;
2079  // get particle def
2080  C4ParticleDef *pDef = Particles.definitions.GetDef(szName);
2081  if (!pDef) return false;
2082  // verbose
2083  LogF("Reloading particle %s from %s",pDef->Name.getData(),GetFilename(pDef->Filename.getData()));
2084  // reload it
2085  if (!pDef->Reload())
2086  {
2087  // safer: remove all particles
2089  // clear def
2090  delete pDef;
2091  // log
2092  LogF("Reloading failure. All particles removed.");
2093  // failure
2094  return false;
2095  }
2096  // success
2097  return true;
2098 }
2099 
2100 bool C4Game::InitGame(C4Group &hGroup, InitMode init_mode, bool fLoadSky, C4ValueNumbers * numbers)
2101 {
2102  // Activate debugger if requested
2103  // needs to happen before any scripts are compiled to bytecode so AB_DEBUG chunks will be inserted
2104  if (DebugPort)
2105  {
2106  if (Parameters.isLeague())
2107  Log("Debugger disabled. Not allowed in league.");
2108  else
2110  return false;
2111  }
2112 
2113  if (init_mode == IM_Normal)
2114  {
2115 
2116  // file monitor
2118  pFileMonitor = std::make_unique<C4FileMonitor>(FileMonitorCallback);
2119 
2120  // system scripts
2121  if (!InitScriptEngine())
2122  { LogFatal(LoadResStr("IDS_PRC_FAIL")); return false; }
2123  SetInitProgress(8);
2124 
2125  // Scenario components
2126  if (!LoadScenarioComponents())
2127  { LogFatal(LoadResStr("IDS_PRC_FAIL")); return false; }
2128  SetInitProgress(9);
2129 
2130  // join local players for regular games
2131  // should be done before record/replay is initialized, so the players are stored in PlayerInfos.txt
2132  // for local savegame resumes, players are joined into PlayerInfos and later associated in InitPlayers
2133  if (!::Network.isEnabled())
2134  if (!PlayerInfos.InitLocal())
2135  { LogFatal(LoadResStr("IDS_PRC_FAIL")); return false; }
2136 
2137  // for replays, make sure teams are assigned correctly
2138  if (C4S.Head.Replay)
2139  {
2140  PlayerInfos.RecheckAutoGeneratedTeams(); // checks that all teams used in playerinfos exist
2141  Teams.RecheckPlayers(); // syncs player list of teams with teams set in PlayerInfos
2142  }
2143 
2144  // set up control (inits Record/Replay)
2145  if (!InitControl()) return false;
2146 
2147  // Graphics and fonts (may reinit main font, too)
2148  // redundant call in NETWORK2; but it may do scenario local overloads
2149  Log(LoadResStr("IDS_PRC_GFXRES"));
2150  if (!GraphicsResource.Init())
2151  { LogFatal(LoadResStr("IDS_PRC_FAIL")); return false; }
2152  SetInitProgress(25);
2153 
2154  // Definitions
2155  if (!InitDefs()) return false;
2156  SetInitProgress(55);
2157 
2158  // Scenario scripts (and local system.ocg)
2160  // Map scripts
2162  // Scenario objects
2164  // After defs to get overloading priority
2166  { LogFatal(LoadResStr("IDS_PRC_FAIL")); return false; }
2167  SetInitProgress(57);
2168 
2169  // Final init for loaded player commands. Before linking scripts, so CON_* constants are registered
2171 
2172  // Register constants for scenario options
2174 
2175  // Now that all controls and assignments are known, resolve user overloads on control assignments
2176  if (!InitPlayerControlUserSettings()) return false;
2177  // Sort assignments by priority. Should be done last, because the user should not see this order in the control config dialog
2179  // (Theoretically, PlayerControlDefaultAssignmentSets could be cleared now. However, the amount of memory used is negligible)
2180 
2181  // Link scripts
2182  if (!LinkScriptEngine()) return false;
2183  SetInitProgress(58);
2184 
2185  // Materials
2186  if (!InitMaterialTexture())
2187  { LogFatal(LoadResStr("IDS_PRC_MATERROR")); return false; }
2188  SetInitProgress(60);
2189 
2190  // prepare script menus
2191  assert(!ScriptGuiRoot);
2192  ScriptGuiRoot = std::make_unique<C4ScriptGuiWindow>();
2193  }
2194  else if (fLoadSky)
2195  {
2196  // Sky needs graphics loaded, for shaders
2197  if (!GraphicsResource.Init())
2198  { LogFatal(LoadResStr("IDS_PRC_FAIL")); return false; }
2199  }
2200 
2201  // Load section sounds
2202  Application.SoundSystem.LoadEffects(hGroup, nullptr, true);
2203 
2204  // determine startup player and team count, which may be used for initial map generation
2205  if (!FrameCounter)
2206  {
2209  }
2210 
2211  // The Landscape is the last long chunk of loading time, so it's a good place to start the music fadeout
2212  if (init_mode == IM_Normal) Application.MusicSystem.FadeOut(2000);
2213  // Landscape
2214  Log(LoadResStr("IDS_PRC_LANDSCAPE"));
2215  bool fLandscapeLoaded = false;
2216  if (!Landscape.Init(hGroup, init_mode != IM_Normal, fLoadSky, fLandscapeLoaded, !!C4S.Head.SaveGame))
2217  { LogFatal(LoadResStr("IDS_ERR_GBACK")); return false; }
2218  SetInitProgress(88);
2219  // the savegame flag is set if runtime data is present, in which case this is to be used
2220  // except for scenario sections
2221  if (fLandscapeLoaded && (!C4S.Head.SaveGame || init_mode == IM_Section))
2223  // clear old landscape data
2224  if (init_mode != IM_Normal && fLandscapeLoaded) { PXS.Clear(); MassMover.Clear(); }
2225  SetInitProgress(89);
2226  // Init main object list
2228 
2229  // Pathfinder
2230  if (init_mode == IM_Normal) PathFinder.Init( &LandscapeFree, &TransferZones );
2231  SetInitProgress(90);
2232 
2233  // PXS
2234  if (hGroup.FindEntry(C4CFN_PXS))
2235  if (!PXS.Load(hGroup))
2236  { LogFatal(LoadResStr("IDS_ERR_PXS")); return false; }
2237  SetInitProgress(91);
2238 
2239  // MassMover
2240  if (hGroup.FindEntry(C4CFN_MassMover))
2241  if (!MassMover.Load(hGroup))
2242  { LogFatal(LoadResStr("IDS_ERR_MOVER")); return false; }
2243  SetInitProgress(92);
2244 
2245  // definition value overloads
2246  // TODO: Remove this function? We could move value to script and allow it through regular overloads
2247  if (init_mode == IM_Normal) InitValueOverloads();
2248 
2249  // runtime data
2250  if (!CompileRuntimeData(hGroup, init_mode, C4S.Head.SaveGame, C4S.Head.NetworkGame, numbers))
2251  { LogFatal(LoadResStr("IDS_PRC_FAIL")); return false; }
2252 
2253  SetInitProgress(93);
2254 
2255  // Load round results
2256  if (init_mode == IM_Normal)
2257  {
2258  if (hGroup.FindEntry(C4CFN_RoundResults))
2259  {
2260  if (!RoundResults.Load(hGroup, C4CFN_RoundResults))
2261  { LogFatal(LoadResStr("IDS_ERR_ERRORLOADINGROUNDRESULTS")); return false; }
2262  }
2263  else
2264  {
2265  RoundResults.Init();
2266  }
2267  }
2268 
2269  // Denumerate game data pointers
2270  if (init_mode == IM_Normal) ScriptEngine.Denumerate(numbers);
2271  if (init_mode == IM_Normal) GlobalSoundModifier.Denumerate(numbers);
2272  numbers->Denumerate();
2273  if (init_mode == IM_Normal) ScriptGuiRoot->Denumerate(numbers);
2274  // Object.PostLoad must happen after number->Denumerate(), becuase UpdateFace() will access Action proplist,
2275  // which might have a non-denumerated prototype otherwise
2276  Objects.PostLoad(init_mode == IM_Section, numbers);
2277 
2278  // Check object enumeration
2279  if (!CheckObjectEnumeration()) return false;
2280 
2281  // Okay; everything in denumerated state from now on
2282  PointersDenumerated = true;
2283 
2284  // scenario objects script
2287 
2288  // Environment
2289  if (!C4S.Head.NoInitialize && fLandscapeLoaded)
2290  {
2291  Log(LoadResStr("IDS_PRC_ENVIRONMENT"));
2292  InitVegetation();
2293  InitInEarth();
2294  InitAnimals();
2295  InitEnvironment();
2296  InitRules();
2297  InitGoals();
2299  }
2300  SetInitProgress(94);
2301 
2302  // Weather
2303  if (fLandscapeLoaded) Weather.Init(!C4S.Head.SaveGame);
2304  SetInitProgress(96);
2305 
2306  // close any gfx groups, because they are no longer needed (after sky is initialized)
2308 
2309  if (init_mode == IM_Normal) // reload doesn't affect the music (takes too long)
2310  {
2311  // Music
2314  if (::Config.Sound.RXMusic)
2315  {
2316  // Play something that is not Frontend.mid
2318  }
2319  else
2321  SetInitProgress(97);
2322  }
2323 
2324  return true;
2325 }
2326 
2328 {
2329  // Validate object owners & assign loaded info objects
2331  Objects.AssignInfo();
2332  Objects.AssignLightRange(); // update FoW-repellers
2333 
2334  // Ambience init (before scenario construction, so the scenario can easily modify ambience in Initialize)
2336 
2337  // Script constructor call
2338  int32_t iObjCount = Objects.ObjectCount();
2340  if (Objects.ObjectCount()!=iObjCount) fScriptCreatedObjects=true;
2341 
2342  // Player final init
2343  C4Player *pPlr;
2344  for (pPlr = Players.First; pPlr; pPlr = pPlr->Next)
2345  {
2346  if (init_mode == IM_ReInit) pPlr->ScenarioInit();
2347  pPlr->FinalInit(!C4S.Head.SaveGame);
2348  }
2349 
2350  // Create viewports
2351  if (init_mode == IM_Normal)
2352  {
2353  for (pPlr = Players.First; pPlr; pPlr = pPlr->Next)
2354  if (pPlr->LocalControl)
2356  // Check fullscreen viewports
2358  // update halt state
2360 
2361  // Host: players without connected clients: remove via control queue
2362  if (Network.isEnabled() && Network.isHost())
2363  for (int32_t cnt = 0; cnt < Players.GetCount(); cnt++)
2364  if (Players.GetByIndex(cnt)->AtClient < 0)
2365  Players.Remove(Players.GetByIndex(cnt), true, false);
2366 
2367  // It should be safe now to reload stuff
2368  if (pFileMonitor) pFileMonitor->StartMonitoring();
2369  }
2370  return true;
2371 }
2372 
2374 {
2375  // engine functions
2380 
2381  // system functions: check if system group is open
2383  { LogFatal(LoadResStr("IDS_ERR_INVALIDSYSGRP")); return false; }
2385 
2386  // get scripts
2387  char fn[_MAX_FNAME+1] = { 0 };
2388  File.ResetSearch();
2389  while (File.FindNextEntry(C4CFN_ScriptFiles, fn, nullptr, !!fn[0]))
2390  {
2391  // host will be destroyed by script engine, so drop the references
2392  C4ScriptHost *scr = new C4ExtraScriptHost();
2393  scr->Reg2List(&ScriptEngine);
2395  }
2396 
2397  // if it's a physical group: watch out for changes
2398  if (!File.IsPacked() && Game.pFileMonitor)
2399  Game.pFileMonitor->AddDirectory(File.GetFullName().getData());
2400 
2401  // Prepare host for Objects.c script
2404  C4Value scen_obj_script_val;
2405  scen_obj_script_val.SetPropList(pScenarioObjectsScript->GetPropList());
2406  ::ScriptEngine.RegisterGlobalConstant("ScenarioObjects", scen_obj_script_val);
2407 
2408  // load standard clonk names
2409  Names.Load(File, C4CFN_Names);
2410 
2411  return true;
2412 }
2413 
2415 {
2416  // Link script engine (resolve includes/appends, generate code)
2418 
2419  // display errors
2420  LogF("C4AulScriptEngine linked - %d line%s, %d warning%s, %d error%s",
2421  ScriptEngine.lineCnt, (ScriptEngine.lineCnt != 1 ? "s" : ""),
2422  ScriptEngine.warnCnt, (ScriptEngine.warnCnt != 1 ? "s" : ""),
2423  ScriptEngine.errCnt, (ScriptEngine.errCnt != 1 ? "s" : ""));
2424 
2425  // update material pointers
2427 
2428  if (C4AulDebug *pDebug = C4AulDebug::GetDebugger())
2429  if (!pDebug->Listen(DebugPort, !!DebugWait))
2430  return false;
2431 
2432  return true;
2433 }
2434 
2436 {
2438 
2439  // update effect pointers
2441 
2442  // update material pointers
2444 
2445  return true;
2446 }
2447 
2449 {
2450  int32_t iPlrCnt = 0;
2451 
2453  {
2454  // Load players to restore from scenario
2455  C4PlayerInfoList LocalRestorePlayerInfos;
2456  LocalRestorePlayerInfos.Load(ScenarioFile, C4CFN_SavePlayerInfos, &ScenarioLangStringTable);
2457  // -- runtime join player restore
2458  // all restore functions will be executed on RestorePlayerInfos, because the main playerinfos may be more up-to-date
2459  // extract all players to temp store and update filenames to point there
2460  if (!LocalRestorePlayerInfos.RecreatePlayerFiles())
2461  { LogFatal(LoadResStr("IDS_ERR_NOPLRFILERECR")); return false; }
2462  // recreate the files
2463  if (!LocalRestorePlayerInfos.RecreatePlayers(numbers))
2464  { LogFatal(LoadResStr("IDS_ERR_NOPLRNETRECR")); return false; }
2465  }
2467  {
2468  // -- savegame player restore
2469  // for savegames or regular scenarios with restore infos, the player info list should have been loaded from the savegame
2470  // or got restored from game text in OpenScenario()
2471  // merge restore player info into main player info list now
2472  // -for single-host games, this will move all infos
2473  // -for network games, it will merge according to savegame association done in the lobby
2474  // for all savegames, script players get restored by adding one new script player for earch savegame script player to the host
2476  { LogFatal(LoadResStr("IDS_ERR_NOPLRSAVEINFORECR")); return false; }
2478  // try to associate local filenames (non-net+replay) or resources (net) with all player infos
2480  { LogFatal(LoadResStr("IDS_ERR_NOPLRFILERECR")); return false; }
2481  // recreate players by joining all players whose joined-flag is already set
2482  if (!PlayerInfos.RecreatePlayers(numbers))
2483  { LogFatal(LoadResStr("IDS_ERR_NOPLRSAVERECR")); return false; }
2484  }
2485 
2486  // any regular non-net non-replay game: Do the normal control queue join
2487  // this includes additional player joins in savegames
2488  if (!Network.isEnabled() && !Control.NoInput())
2490  {
2491  // error joining local players - either join was done earlier somehow,
2492  // or the player count check will soon end this round
2493  }
2494 
2495  // non-replay player joins will be done by player info list when go tick is reached
2496  // this is handled by C4Network2Players and needs no further treatment here
2497  // set iPlrCnt for player count check in host/single games
2499 
2500  // Check valid participating player numbers (host/single only)
2501  if (!Network.isEnabled() || (Network.isHost() && !fLobby))
2502  {
2503 #ifndef USE_CONSOLE
2504  // No players in fullscreen
2505  if (iPlrCnt==0)
2506  if (!Application.isEditor && !Control.NoInput())
2507  {
2508  LogFatal(LoadResStr("IDS_CNS_NOFULLSCREENPLRS")); return false;
2509  }
2510 #endif
2511  // Too many players
2512  if (iPlrCnt>Game.Parameters.MaxPlayers)
2513  {
2514  if (!Application.isEditor)
2515  {
2516  LogFatal(FormatString(LoadResStr("IDS_PRC_TOOMANYPLRS"),Game.Parameters.MaxPlayers).getData());
2517  return false;
2518  }
2519  else
2520  {
2522  }
2523  }
2524  }
2525  // Console and no real players: halt
2526  if (Console.Active)
2527  if (!fLobby)
2529  ++HaltCount;
2530  return true;
2531 }
2532 
2534 {
2535  // update random seed
2536  if (C4S.Head.NetworkGame || C4S.Head.Replay)
2537  {
2539  }
2540  // Randomize
2542 
2543  // Replay?
2544  if (C4S.Head.Replay)
2545  {
2546  // no joins
2547  PlayerFilenames[0]=0;
2548  // start playback
2550  return false;
2551  // no record!
2552  Record = false;
2553  }
2554  else if (Network.isEnabled())
2555  {
2556  // initialize
2558  return false;
2559  // league? always record
2560  if (Parameters.isLeague())
2561  Record = true;
2562  }
2563  // Otherwise: local game
2564  else
2565  {
2566  // init
2568  return false;
2569  }
2570 
2571  // record?
2572  if (Record)
2573  if (!Control.StartRecord(true, Parameters.doStreaming()))
2574  {
2575  // Special: If this happens for a league host, the game must not start.
2577  {
2578  LogFatal(LoadResStr("IDS_ERR_NORECORD"));
2579  return false;
2580  }
2581  else
2582  {
2583  Log(LoadResStr("IDS_ERR_NORECORD"));
2584  }
2585  }
2586 
2587  return true;
2588 }
2589 
2591  C4ID *idlist, int32_t maxidlist)
2592 {
2593  int32_t cnt,cnt2,ccount,cpos;
2594  for (cpos=0,cnt=0; rlist.GetID(cnt); cnt++)
2595  if (C4Id2Def(rlist.GetID(cnt,&ccount)))
2596  for (cnt2=0; cnt2<ccount; cnt2++)
2597  if (cpos<maxidlist)
2598  { idlist[cpos]=rlist.GetID(cnt); cpos++; }
2599  return cpos;
2600 }
2601 
2603 {
2604  int32_t cnt,tx,ty;
2605  for (cnt=0; cnt<35; cnt++) // cheap trys
2606  {
2608  if (GBackMat(tx,ty)==MEarth)
2609  if (CreateObject(id,nullptr,NO_OWNER,tx,ty,Random(360)))
2610  return true;
2611  }
2612  return false;
2613 }
2614 
2615 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)
2616 {
2617  // Helper for C4Game::PlaceVegetation: return random position in rectangle. Use shape_proplist if provided.
2618  if (shape_proplist && out_pos_proplist)
2619  {
2620  C4AulParSet pars(C4VPropList(out_pos_proplist));
2621  if (!shape_proplist->Call(P_GetRandomPoint, &pars)) return false;
2622  *piTx = out_pos_proplist->GetPropertyInt(P_x);
2623  *piTy = out_pos_proplist->GetPropertyInt(P_y);
2624  }
2625  else
2626  {
2627  *piTx = iX + Random(iWdt);
2628  *piTy = iY + Random(iHgt);
2629  }
2630  return true;
2631 }
2632 
2633 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)
2634 {
2635  if (shape_proplist)
2636  {
2637  // check using shape proplist
2638  C4AulParSet pars(C4VInt(iTx), C4VInt(iTy));
2639  if (!shape_proplist->Call(P_IsPointContained, &pars)) return false;
2640  }
2641  else
2642  {
2643  // check using bounds rect
2644  if (iTy < iY) return false;
2645  }
2646  return true;
2647 }
2648 
2649 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)
2650 {
2651  int32_t cnt,iTx,iTy,iMaterial;
2652 
2653  // Get definition
2654  C4Def* pDef;
2655  if (!PropList || !(pDef = PropList->GetDef())) return nullptr;
2656 
2657  // No growth specified: full growth
2658  if (iGrowth<=0)
2659  {
2660  iGrowth=FullCon;
2661  }
2662 
2663  // Place by placement type
2664  switch (PropList->GetPropertyInt(P_Placement))
2665  {
2666 
2667  // Surface soil
2668  case C4D_Place_Surface:
2669  for (cnt=0; cnt<20; cnt++)
2670  {
2671  // Random hit within target area
2672  if (!PlaceVegetation_GetRandomPoint(iX, iY, iWdt, iHgt, shape_proplist, out_pos_proplist, &iTx, &iTy)) break;
2673  // Above tunnel
2674  while ((iTy>0) && Landscape.GetBackPix(iTx,iTy) == 0) iTy--;
2675  // Above semi solid
2676  if (!AboveSemiSolid(iTx,iTy) || !Inside<int32_t>(iTy,50,::Landscape.GetHeight()-50))
2677  continue;
2678  // Still inside bounds?
2679  if (!PlaceVegetation_IsPosInBounds(iTx, iTy, iX, iY, iWdt, iHgt, shape_proplist)) continue;
2680  // Free above
2681  if (GBackSemiSolid(iTx,iTy-pDef->Shape.Hgt) || GBackSemiSolid(iTx,iTy-pDef->Shape.Hgt/2))
2682  continue;
2683  // Free upleft and upright
2684  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))
2685  continue;
2686  // Soil check
2687  iTy+=3; // two pix into ground
2688  iMaterial = GBackMat(iTx,iTy);
2689  if (iMaterial!=MNone) if (::MaterialMap.Map[iMaterial].Soil)
2690  {
2691  iTy+=5;
2692  return CreateObjectConstruction(PropList,nullptr,NO_OWNER,iTx,iTy,iGrowth);
2693  }
2694  }
2695  break;
2696 
2697  // Underwater
2698  case C4D_Place_Liquid:
2699  // Random range
2700  if (!PlaceVegetation_GetRandomPoint(iX, iY, iWdt, iHgt, shape_proplist, out_pos_proplist, &iTx, &iTy)) return nullptr;
2701  // Find liquid
2702  if (!FindSurfaceLiquid(iTx,iTy,pDef->Shape.Wdt,pDef->Shape.Hgt))
2703  if (!FindLiquid(iTx,iTy,pDef->Shape.Wdt,pDef->Shape.Hgt))
2704  return nullptr;
2705  // Liquid bottom
2706  if (!SemiAboveSolid(iTx,iTy)) return nullptr;
2707  iTy+=3;
2708  // Still inside bounds?
2709  if (!PlaceVegetation_IsPosInBounds(iTx, iTy, iX, iY, iWdt, iHgt, shape_proplist)) return nullptr;
2710  // Create object
2711  return CreateObjectConstruction(PropList,nullptr,NO_OWNER,iTx,iTy,iGrowth);
2712  break;
2713 
2714  // Underground/Tunnel
2715  case C4D_Place_Subsurface:
2716  for (cnt=0; cnt<5; cnt++)
2717  {
2718  // Random range
2719  if (!PlaceVegetation_GetRandomPoint(iX, iY, iWdt, iHgt, shape_proplist, out_pos_proplist, &iTx, &iTy)) break;
2720  // Find tunnel
2721  if (!FindTunnel(iTx,iTy,pDef->Shape.Wdt,pDef->Shape.Hgt))
2722  continue;
2723  // Tunnel bottom
2724  if (!AboveSemiSolid(iTx,iTy)) continue;
2725  // Still inside bounds?
2726  if (!PlaceVegetation_IsPosInBounds(iTx, iTy, iX, iY, iWdt, iHgt, shape_proplist)) continue;
2727  // Soil check
2728  iTy+=3; // two pix into ground
2729  iMaterial = GBackMat(iTx,iTy);
2730  if (iMaterial!=MNone) if (::MaterialMap.Map[iMaterial].Soil)
2731  {
2732  // Create object
2733  iTy+=5;
2734  return CreateObjectConstruction(PropList,nullptr,NO_OWNER,iTx,iTy,iGrowth);
2735  }
2736  }
2737 
2738  // Under- or aboveground
2739  case C4D_Place_BothSurface:
2740  for (cnt=0; cnt<20; cnt++)
2741  {
2742  // Random hit within target area
2743  if (!PlaceVegetation_GetRandomPoint(iX, iY, iWdt, iHgt, shape_proplist, out_pos_proplist, &iTx, &iTy)) break;
2744  // Above semi solid
2745  if (!AboveSemiSolid(iTx,iTy) || !Inside<int32_t>(iTy,50,::Landscape.GetHeight()-50))
2746  continue;
2747  // Free above
2748  if (GBackSemiSolid(iTx,iTy-pDef->Shape.Hgt) || GBackSemiSolid(iTx,iTy-pDef->Shape.Hgt/2))
2749  continue;
2750  // Still inside bounds?
2751  if (!PlaceVegetation_IsPosInBounds(iTx, iTy, iX, iY, iWdt, iHgt, shape_proplist))
2752  continue;
2753  // Free upleft and upright
2754  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))
2755  continue;
2756  // Soil check
2757  iTy+=3; // two pix into ground
2758  iMaterial = GBackMat(iTx,iTy);
2759  if (iMaterial!=MNone) if (::MaterialMap.Map[iMaterial].Soil)
2760  {
2761  iTy+=5;
2762  return CreateObjectConstruction(PropList,nullptr,NO_OWNER,iTx,iTy,iGrowth);
2763  }
2764  }
2765 
2766  }
2767 
2768  // Undefined placement type
2769  return nullptr;
2770 }
2771 
2773 {
2774  C4Def * pDef;
2775  if (!PropList || !(pDef = PropList->GetDef())) return nullptr;
2776  int32_t iX,iY;
2777  // Placement
2778  switch (PropList->GetPropertyInt(P_Placement))
2779  {
2780  // Running free
2781  case C4D_Place_Surface:
2783  if (!FindSolidGround(iX,iY,pDef->Shape.Wdt)) return nullptr;
2784  break;
2785  // In liquid
2786  case C4D_Place_Liquid:
2788  if (!FindSurfaceLiquid(iX,iY,pDef->Shape.Wdt,pDef->Shape.Hgt))
2789  if (!FindLiquid(iX,iY,pDef->Shape.Wdt,pDef->Shape.Hgt))
2790  return nullptr;
2791  iY+=pDef->Shape.Hgt/2;
2792  break;
2793  // Floating in air
2794  case C4D_Place_Air:
2795  iX=Random(::Landscape.GetWidth());
2796  for (iY=0; (iY<::Landscape.GetHeight()) && !GBackSemiSolid(iX,iY); iY++) {}
2797  if (iY<=0) return nullptr;
2798  iY=Random(iY);
2799  break;
2800  default:
2801  return nullptr;
2802  }
2803  // Create object
2804  return CreateObject(PropList,nullptr,NO_OWNER,iX,iY);
2805 }
2806 
2808 {
2809  const int32_t maxvid=100;
2810  int32_t cnt,vidnum;
2811  C4ID vidlist[maxvid];
2812  // Amount
2814  // List all valid IDs from C4S
2815  vidnum=ListExpandValids(C4S.Landscape.InEarth,vidlist,maxvid);
2816  // Place
2817  if (vidnum>0)
2818  for (cnt=0; cnt<amt; cnt++)
2819  PlaceInEarth(vidlist[Random(vidnum)]);
2820 
2821 }
2822 
2824 {
2825  const int32_t maxvid=100;
2826  int32_t cnt,vidnum;
2827  C4ID vidlist[maxvid];
2828  // Amount
2829  int32_t amt=(::Landscape.GetWidth()/50)*C4S.Landscape.VegLevel.Evaluate()/100;
2830  // Get percentage vidlist from C4S
2831  vidnum=ListExpandValids(C4S.Landscape.Vegetation,vidlist,maxvid);
2832  // Place vegetation
2833  if (vidnum>0)
2834  for (cnt=0; cnt<amt; cnt++)
2835  PlaceVegetation(C4Id2Def(vidlist[Random(vidnum)]),0,0,::Landscape.GetWidth(),::Landscape.GetHeight(),-1,nullptr,nullptr);
2836 }
2837 
2839 {
2840  int32_t cnt,cnt2;
2841  C4ID idAnimal; int32_t iCount;
2842  // Place animals
2843  for (cnt=0; (idAnimal=C4S.Animals.FreeLife.GetID(cnt,&iCount)); cnt++)
2844  for (cnt2=0; cnt2<iCount; cnt2++)
2845  PlaceAnimal(C4Id2Def(idAnimal));
2846  // Place nests
2847  for (cnt=0; (idAnimal=C4S.Animals.EarthNest.GetID(cnt,&iCount)); cnt++)
2848  for (cnt2=0; cnt2<iCount; cnt2++)
2849  PlaceInEarth(idAnimal);
2850 }
2851 
2852 
2854 {
2855  // Info
2857  // Overload clonk names from scenario file
2860  // scenario sections
2861  char fn[_MAX_FNAME+1] = { 0 };
2862  ScenarioFile.ResetSearch(); *fn=0;
2863  while (ScenarioFile.FindNextEntry(C4CFN_ScenarioSections, fn, nullptr, !!*fn))
2864  {
2865  // get section name
2866  char SctName[_MAX_FNAME+1];
2867  int32_t iWildcardPos = SCharPos('*', C4CFN_ScenarioSections);
2868  SCopy(fn + iWildcardPos, SctName, _MAX_FNAME);
2869  RemoveExtension(SctName);
2870  if (std::strlen(SctName)>C4MaxName || !*SctName)
2871  {
2872  DebugLog("invalid section name");
2873  LogFatal(FormatString(LoadResStr("IDS_ERR_SCENSECTION"), fn).getData()); return false;
2874  }
2875  // load this section into temp store
2876  C4ScenarioSection *pSection = new C4ScenarioSection(SctName);
2877  if (!pSection->ScenarioLoad(fn, false))
2878  { LogFatal(FormatString(LoadResStr("IDS_ERR_SCENSECTION"), fn).getData()); return false; }
2879  }
2880  // Success
2881  return true;
2882 }
2883 
2885 {
2886  // called for scenario local and definition local System.ocg groups
2887  C4Group SysGroup;
2888  char fn[_MAX_FNAME+1] = { 0 };
2889  if (SysGroup.OpenAsChild(&parent_group, C4CFN_System))
2890  {
2891  C4LangStringTable *pSysGroupString = new C4LangStringTable();
2893  // load custom scenario control definitions
2894  if (SysGroup.FindEntry(C4CFN_PlayerControls))
2895  {
2896  Log(LoadResStr("IDS_PRC_LOADSCEPLRCTRL"));
2897  C4PlayerControlFile PlayerControlFile;
2898  if (!PlayerControlFile.Load(SysGroup, C4CFN_PlayerControls, pSysGroupString))
2899  {
2900  // non-fatal error here
2901  Log(LoadResStr("IDS_PRC_LOADSCEPLRCTRLFAIL"));
2902  }
2903  else
2904  {
2905  // local definitions loaded successfully - merge into global definitions
2906  PlayerControlDefs.MergeFrom(PlayerControlFile.GetControlDefs());
2909  }
2910  }
2911  // load all scripts in there
2912  SysGroup.ResetSearch();
2913  while (SysGroup.FindNextEntry(C4CFN_ScriptFiles, fn, nullptr, !!fn[0]))
2914  {
2915  // host will be destroyed by script engine, so drop the references
2916  C4ScriptHost *scr = new C4ExtraScriptHost();
2917  scr->Reg2List(&ScriptEngine);
2918  scr->Load(SysGroup, fn, Config.General.LanguageEx, pSysGroupString);
2919  }
2920  // if it's a physical group: watch out for changes
2921  if (!SysGroup.IsPacked() && Game.pFileMonitor)
2922  Game.pFileMonitor->AddDirectory(SysGroup.GetFullName().getData());
2923  SysGroup.Close();
2924  // release string table if no longer used
2925  pSysGroupString->DelRef();
2926  }
2927  return true;
2928 }
2929 
2931 {
2932  C4CustomKey::CodeList Keys;
2933 
2934  // clear previous
2935  KeyboardInput.Clear();
2936 
2937  // globals
2942 
2943  // main ingame
2948 
2949  // debug mode & debug displays
2954 
2955  // playback speed - improve...
2958 
2959  // fullscreen menu
2960  Keys.clear(); Keys.emplace_back(K_LEFT);
2963  Keys.clear(); Keys.emplace_back(K_RIGHT);
2966  Keys.clear(); Keys.emplace_back(K_UP);
2969  Keys.clear(); Keys.emplace_back(K_DOWN);
2972  Keys.clear(); Keys.emplace_back(K_SPACE); Keys.emplace_back(K_RETURN);
2974  KeyboardInput.RegisterKey(new C4CustomKey(Keys, "FullscreenMenuOK", KEYSCOPE_FullSMenu, new C4KeyCBEx<C4FullScreen, BYTE> (FullScreen, COM_MenuEnter, &C4FullScreen::MenuKeyControl))); // name used by PlrControlKeyName
2975  Keys.clear(); Keys.emplace_back(K_ESCAPE);
2977  KeyboardInput.RegisterKey(new C4CustomKey(Keys, "FullscreenMenuCancel", KEYSCOPE_FullSMenu, new C4KeyCBEx<C4FullScreen, BYTE> (FullScreen, COM_MenuClose, &C4FullScreen::MenuKeyControl))); // name used by PlrControlKeyName
2978  Keys.clear(); Keys.emplace_back(K_SPACE);
2980  KeyboardInput.RegisterKey(new C4CustomKey(Keys, "FullscreenMenuOpen", KEYSCOPE_FreeView, new C4KeyCB <C4FullScreen> (FullScreen, &C4FullScreen::ActivateMenuMain))); // name used by C4MainMenu!
2982 
2983  // chat
2984  Keys.clear();
2985  Keys.emplace_back(K_RETURN);
2986  Keys.emplace_back(K_F2); // alternate chat key, if RETURN is blocked by player control
2989 
2997 
2998  // console keys
3007 
3008  // no default keys assigned
3016 
3017  // load any custom keysboard overloads
3019 
3020  // done, success
3021  return true;
3022 }
3023 
3025 {
3026  // Reload System.ocg string table
3028 }
3029 
3031 {
3032  // Load controls and default control sets
3033  C4PlayerControlFile PlayerControlFile;
3034  if (!PlayerControlFile.Load(Application.SystemGroup, C4CFN_PlayerControls, &MainSysLangStringTable)) { LogFatal("[!]Error loading player controls"); return false; }
3035  PlayerControlDefs = PlayerControlFile.GetControlDefs();
3039  // Merge w/ config settings into user control sets
3040  // User settings will be cleared and re-merged again later after scenario/definition control overloads, but initialization
3041  // is needed already for config dialogs
3042  if (!InitPlayerControlUserSettings()) return false;
3043  return true;
3044 }
3045 
3047 {
3048  // Merge config control settings with user settings
3053  return true;
3054 }
3055 
3056 C4Player *C4Game::JoinPlayer(const char *szFilename, int32_t iAtClient, const char *szAtClientName, C4PlayerInfo *pInfo)
3057 {
3058  assert(pInfo);
3059  C4Player *pPlr;
3060  // Join
3061  if (!( pPlr = Players.Join(szFilename,true,iAtClient,szAtClientName, pInfo, nullptr) )) return nullptr;
3062  // Player final init
3063  pPlr->FinalInit(true);
3064  // Create player viewport
3065  if (pPlr->LocalControl) ::Viewports.CreateViewport(pPlr->Number);
3066  // Check fullscreen viewports
3068  // Update menus
3069  Console.UpdateMenus();
3070  // Append player name to list of session player names (no duplicates)
3071  if (!SIsModule(PlayerNames.getData(), pPlr->GetName()))
3072  {
3073  if (PlayerNames) PlayerNames += ";";
3074  PlayerNames += pPlr->GetName();
3075  }
3076  // Success
3077  return pPlr;
3078 }
3079 
3081 {
3082  // Do the InitializePlayers callback once all player joins have finished with at least one human player
3084  {
3085  InitialPlayersJoined = true;
3087  }
3088 }
3089 
3090 void C4Game::FixRandom(uint64_t iSeed)
3091 {
3092  FixedRandom(iSeed);
3093 }
3094 
3096 {
3097  // Duplication safety
3098  if (GameOver) return false;
3099  // Flag, log, call
3100  GameOver=true;
3101  Log(LoadResStr("IDS_PRC_GAMEOVER"));
3103  // Flag all surviving players as winners
3104  for (C4Player *pPlayer = Players.First; pPlayer; pPlayer = pPlayer->Next)
3105  if (!pPlayer->Eliminated)
3106  pPlayer->EvaluateLeague(false, true);
3107  // Immediately save config so mission access gained by this round is stored if the game crashes during shutdown
3108  // or in a following round
3109  Config.Save();
3110  return true;
3111 }
3112 
3114 {
3115  // safety
3116  if (GameOverDlgShown) return;
3117  // flag, show
3118  GameOverDlgShown = true;
3119 #ifndef USE_CONSOLE
3120  if (!Application.isEditor)
3121  {
3122  C4GameOverDlg *pDlg = new C4GameOverDlg();
3123  pDlg->SetDelOnClose();
3124  if (!pDlg->Show(pGUI, true)) { delete pDlg; Application.QuitGame(); }
3125  }
3126 #endif
3127 }
3128 
3130 {
3132 }
3133 
3134 void C4Game::Synchronize(bool fSavePlayerFiles)
3135 {
3136  // Log
3137  LogSilentF("Network: Synchronization (Frame %i) [PlrSave: %d]",FrameCounter, fSavePlayerFiles);
3138  // callback to control (to start record)
3140  // Fix random
3142  // Synchronize members
3146  Objects.Synchronize();
3147  // synchronize local player files if desired
3148  // this will reset any InActionTimes!
3149  // (not in replay mode)
3150  if (fSavePlayerFiles && !Control.isReplay()) Players.SynchronizeLocalFiles();
3151  // callback to network
3153  // TransferZone synchronization: Must do this after dynamic creation to avoid synchronization loss
3154  // if OnSynchronized-callbacks do sync-relevant changes
3156 }
3157 
3158 bool C4Game::InitNetworkFromAddress(const char *szAddress)
3159 {
3160  StdCopyStrBuf strRefQueryFailed(LoadResStr("IDS_NET_REFQUERY_FAILED"));
3161  // Query reference
3162  C4Network2RefClient RefClient;
3163  if (!RefClient.Init() ||
3164  !RefClient.SetServer(szAddress) ||
3165  !RefClient.QueryReferences())
3166  { LogFatal(FormatString(strRefQueryFailed.getData(), RefClient.GetError()).getData()); return false; }
3167  // We have to wait for the answer
3168  StdStrBuf Message = FormatString(LoadResStr("IDS_NET_REFQUERY_QUERYMSG"), szAddress);
3169  Log(Message.getData());
3170  // Set up wait dialog
3171  C4GUI::MessageDialog *pDlg = nullptr;
3172  if (!Application.isEditor)
3173  {
3174  // create & show
3175  pDlg = new C4GUI::MessageDialog(Message.getData(), LoadResStr("IDS_NET_REFQUERY_QUERYTITLE"),
3177  if (!pDlg || !pDlg->Show(::pGUI, true)) return false;
3178  }
3179  // Wait for response
3180  while (RefClient.isBusy())
3181  {
3182  // Execute GUI
3183  if (!Application.ScheduleProcs(100) ||
3184  (pDlg && pDlg->IsAborted()))
3185  {
3186  delete pDlg;
3187  return false;
3188  }
3189  // Check if reference is received
3190  if (!RefClient.Execute(0))
3191  break;
3192  }
3193  // Close dialog
3194  delete pDlg;
3195  // Error?
3196  if (!RefClient.isSuccess())
3197  { LogFatal(FormatString(strRefQueryFailed.getData(), RefClient.GetError()).getData()); return false; }
3198  // Get references
3199  C4Network2Reference **ppRefs = nullptr; int32_t iRefCount;
3200  if (!RefClient.GetReferences(ppRefs, iRefCount) || iRefCount <= 0)
3201  { LogFatal(FormatString(strRefQueryFailed.getData(), LoadResStr("IDS_NET_REFQUERY_NOREF")).getData()); return false; }
3202  // Connect to first reference
3203  bool fSuccess = InitNetworkFromReference(*ppRefs[0]);
3204  // Remove references
3205  for (int i = 0; i < iRefCount; i++)
3206  delete ppRefs[i];
3207  delete[] ppRefs;
3208  return fSuccess;
3209 }
3210 
3211 bool C4Game::InitNetworkFromReferenceFile(const char *temp_filename)
3212 {
3213  // We need winsock for address parsing
3214  WinSockHolder ws;
3215  // Load reference from temp file + delete the temp file
3216  bool success = false;
3217  C4Network2Reference ref;
3218  StdBuf join_data;
3219  if (join_data.LoadFromFile(temp_filename))
3220  {
3221  CompileFromBuf<StdCompilerBinRead>(ref, join_data);
3222  success = true;
3223  }
3224  EraseFile(temp_filename);
3225  if (!success) return false;
3226  return InitNetworkFromReference(ref);
3227 }
3228 
3230 {
3231  // Find host data
3232  C4Client *pHostData = Reference.Parameters.Clients.getClientByID(C4ClientIDHost);
3233  if (!pHostData) { LogFatal(LoadResStr("IDS_NET_INVALIDREF")); return false; }
3234  // Save scenario title
3235  ScenarioTitle = Reference.getTitle();
3236  // Log
3237  LogF(LoadResStr("IDS_NET_JOINGAMEBY"), pHostData->getName());
3238  // Init clients
3239  if (!Clients.Init())
3240  return false;
3241  // Connect
3242  if (Network.InitClient(Reference, false) != C4Network2::IR_Success)
3243  {
3244  LogFatal(FormatString(LoadResStr("IDS_NET_NOHOSTCON"), pHostData->getName()).getData());
3245  return false;
3246  }
3247  // init control
3248  if (!Control.InitNetwork(Clients.getLocal())) return false;
3249  // init local player info list
3250  Network.Players.Init();
3251  return true;
3252 }
3253 
3255 {
3256  // Network not active?
3257  if (!NetworkActive)
3258  {
3259  // Clear client list
3260  if (!C4S.Head.Replay)
3261  Clients.Init();
3262  return true;
3263  }
3264  // network not active?
3265  if (C4S.Head.NetworkGame)
3266  { LogFatal(LoadResStr("IDS_NET_NODIRECTSTART")); return Clients.Init(); }
3267  // replay?
3268  if (C4S.Head.Replay)
3269  { LogFatal(LoadResStr("IDS_PRC_NONETREPLAY")); return true; }
3270  // clear client list
3271  if (!Clients.Init())
3272  return false;
3273  // init network as host
3274  if (!Network.InitHost(fLobby)) return false;
3275  // init control
3276  if (!Control.InitNetwork(Clients.getLocal())) return false;
3277  // init local player info list
3278  Network.Players.Init();
3279  // allow connect
3280  Network.AllowJoin(true);
3281  // do lobby (if desired)
3282  if (fLobby)
3283  {
3284  if (!Network.DoLobby()) return false;
3285  }
3286  else
3287  {
3288  // otherwise: start manually
3289  if (!Network.Start()) return false;
3290  }
3291  // ok
3292  return true;
3293 }
3294 
3296 {
3297 
3298  struct Check
3299  {
3300  int32_t maxNumber{0};
3301  Check() = default;
3302  // Check valid & maximum number & duplicate numbers
3303  bool that(C4Object* cObj)
3304  {
3305  // Invalid number
3306  if (cObj->Number<1)
3307  {
3308  LogFatal(FormatString("Invalid object enumeration number (%d) of object %s (x=%d)", cObj->Number, cObj->id.ToString(), cObj->GetX()).getData()); return false;
3309  }
3310  // Max
3311  if (cObj->Number>maxNumber) maxNumber=cObj->Number;
3312  // Duplicate
3313  for (C4Object *cObj2 : Objects)
3314  if (cObj2!=cObj)
3315  if (cObj->Number==cObj2->Number)
3316  { LogFatal(FormatString("Duplicate object enumeration number %d (%s and %s)",cObj2->Number,cObj->GetName(),cObj2->GetName()).getData()); return false; }
3317  for (C4Object *cObj2 : Objects.InactiveObjects)
3318  if (cObj2!=cObj)
3319  if (cObj->Number==cObj2->Number)
3320  { LogFatal(FormatString("Duplicate object enumeration number %d (%s and %s(i))",cObj2->Number,cObj->GetName(),cObj2->GetName()).getData()); return false; }
3321  return true;
3322  }
3323  };
3324 
3325  Check check;
3326  for (C4Object *cObj : Objects)
3327  {
3328  if (!check.that(cObj))
3329  return false;
3330  }
3331 
3332  for (C4Object *cObj : Objects.InactiveObjects)
3333  {
3334  if (!check.that(cObj))
3335  return false;
3336  }
3337 
3338  // Adjust enumeration index
3339  C4PropListNumbered::SetEnumerationIndex(check.maxNumber);
3340  // Done
3341  return true;
3342 }
3343 
3345 {
3346  C4ID idOvrl; C4Def *pDef;
3347  // set new values
3348  for (int32_t cnt=0; (idOvrl=C4S.Game.Realism.ValueOverloads.GetID(cnt)); cnt++)
3349  if ((pDef=::Definitions.ID2Def(idOvrl)))
3351 }
3352 
3354 {
3355  // Place environment objects
3356  int32_t cnt,cnt2;
3357  C4ID idType; int32_t iCount;
3358  for (cnt=0; (idType=C4S.Environment.Objects.GetID(cnt,&iCount)); cnt++)
3359  for (cnt2=0; cnt2<iCount; cnt2++)
3360  CreateObject(idType,nullptr);
3361 }
3362 
3364 {
3365  // Place rule objects
3366  int32_t cnt,cnt2;
3367  C4ID idType; int32_t iCount;
3368  for (cnt=0; (idType=Parameters.Rules.GetID(cnt,&iCount)); cnt++)
3369  for (cnt2=0; cnt2<std::max<int32_t>(iCount,1); cnt2++)
3370  CreateObject(idType,nullptr);
3371 }
3372 
3374 {
3375  // Place goal objects
3376  int32_t cnt,cnt2;
3377  C4ID idType; int32_t iCount;
3378  for (cnt=0; (idType=Parameters.Goals.GetID(cnt,&iCount)); cnt++)
3379  for (cnt2=0; cnt2<iCount; cnt2++)
3380  CreateObject(idType,nullptr);
3381 }
3382 
3383 void C4Game::SetInitProgress(float fToProgress)
3384 {
3385  // set new progress
3386  InitProgress=int32_t(fToProgress);
3387  // if progress is more than one percent, display it
3389  {
3391  GraphicsSystem.MessageBoard->LogNotify();
3392  }
3393  // Cheap hack to get the Console window updated while loading
3394  // (unless game is running, i.e. section change - section change would be quick and timer execution can mess with things unpredictably)
3395  if (!IsRunning)
3396  {
3398 #ifdef WITH_QT_EDITOR
3399  Application.ProcessQtEvents();
3400 #endif
3401  }
3402 }
3403 
3404 void C4Game::OnResolutionChanged(unsigned int iXRes, unsigned int iYRes)
3405 {
3406  // update anything that's dependant on screen resolution
3407  pGUI->SetBounds(C4Rect(0,0,iXRes,iYRes));
3408  if (FullScreen.Active)
3410  // note that this may fail if the gfx groups are closed already (runtime resolution change)
3411  // doesn't matter; old gfx are kept in this case
3414 }
3415 
3417 {
3418  // Layout changed: Re-resolve keys
3421 }
3422 
3423 bool C4Game::CreateSectionFromTempFile(const char *section_name, const char *temp_filename)
3424 {
3425  // Remove existing (temp) section of same name
3426  C4ScenarioSection *existing_section = pScenarioSections, *prev = nullptr;
3427  while (existing_section) if (SEqualNoCase(existing_section->name.getData(), section_name)) break; else existing_section = (prev = existing_section)->pNext;
3428  bool deleted_current_section = false;
3429  if (existing_section)
3430  {
3431  deleted_current_section = (existing_section == pCurrentScenarioSection);
3432  if (deleted_current_section)
3433  {
3434  pCurrentScenarioSection = nullptr;
3435  pScenarioObjectsScript = nullptr;
3436  }
3437  if (existing_section->pObjectScripts)
3438  {
3439  delete existing_section->pObjectScripts;
3440  }
3441  if (prev) prev->pNext = existing_section->pNext; else pScenarioSections = existing_section->pNext;
3442  existing_section->pNext = nullptr;
3443  delete existing_section;
3444  }
3445  // Create new (temp) section
3446  C4ScenarioSection *new_section = new C4ScenarioSection(section_name);
3447  if (!new_section->ScenarioLoad(temp_filename, true))
3448  {
3449  pScenarioSections = new_section->pNext;
3450  new_section->pNext = nullptr;
3451  delete new_section;
3452  return false;
3453  }
3454  // Re-Link current section into newly created section
3455  if (deleted_current_section)
3456  {
3457  pCurrentScenarioSection = new_section;
3458  pScenarioObjectsScript = new_section->pObjectScripts;
3459  }
3460  // Link new Objects.c (or re-link because old Objects.c was removed)
3462  return !!new_section;
3463 }
3464 
3465 bool C4Game::LoadScenarioSection(const char *szSection, DWORD dwFlags)
3466 {
3467  // note on scenario section saving:
3468  // if a scenario section overwrites a value that had used the default values in the main scenario section,
3469  // returning to the main section with an unsaved landscape (and thus an unsaved scenario core),
3470  // would leave those values in the altered state of the previous section
3471  // scenario designers should regard this and always define any values, that are defined in subsections as well
3472  C4Group hGroup, *pGrp;
3473  // if current section was the loaded section (maybe main, but need not for resumed savegames)
3475  {
3479  }
3480  // find section to load
3481  C4ScenarioSection *pLoadSect = pScenarioSections;
3482  while (pLoadSect) if (SEqualNoCase(pLoadSect->name.getData(), szSection)) break; else pLoadSect = pLoadSect->pNext;
3483  if (!pLoadSect)
3484  {
3485  DebugLogF("LoadScenarioSection: scenario section %s not found!", szSection);
3486  return false;
3487  }
3488  // save current section state
3489  if (pLoadSect != pCurrentScenarioSection && dwFlags & (C4S_SAVE_LANDSCAPE | C4S_SAVE_OBJECTS))
3490  {
3491  // ensure that the section file does point to temp store
3493  {
3494  DebugLogF("LoadScenarioSection(%s): could not extract section files of current section %s", szSection, pCurrentScenarioSection->name.getData());
3495  return false;
3496  }
3497  // open current group
3498  if (!(pGrp = pCurrentScenarioSection->GetGroupfile(hGroup)))
3499  {
3500  DebugLog("LoadScenarioSection: error opening current group file");
3501  return false;
3502  }
3503  // store landscape, if desired (w/o material enumeration - that's assumed to stay consistent during the game)
3504  if (dwFlags & C4S_SAVE_LANDSCAPE)
3505  {
3506  // storing the landscape implies storing the scenario core
3507  // otherwise, the ExactLandscape-flag would be lost
3508  // maybe imply exact landscapes by the existance of Landscape.png-files?
3509  C4Scenario rC4S = C4S;
3510  rC4S.SetExactLandscape();
3511  if (!rC4S.Save(*pGrp, true))
3512  {
3513  DebugLog("LoadScenarioSection: Error saving C4S");
3514  return false;
3515  }
3516  // landscape
3517  {
3518  C4DebugRecOff DBGRECOFF;
3519  if (!Landscape.Save(*pGrp))
3520  {
3521  DebugLog("LoadScenarioSection: Error saving Landscape");
3522  return false;
3523  }
3524  }
3525  // PXS
3526  if (!PXS.Save(*pGrp))
3527  {
3528  DebugLog("LoadScenarioSection: Error saving PXS");
3529  return false;
3530  }
3531  // MassMover (create copy, may not modify running data)
3532  C4MassMoverSet MassMoverSet;
3533  MassMoverSet.Copy(MassMover);
3534  if (!MassMoverSet.Save(*pGrp))
3535  {
3536  DebugLog("LoadScenarioSection: Error saving MassMover");
3537  return false;
3538  }
3539  }
3540  // store objects
3541  if (dwFlags & C4S_SAVE_OBJECTS)
3542  {
3543  C4ValueNumbers numbers;
3544  // objects: do not save info objects or inactive objects
3545  if (!SaveData(*pGrp,true,false, false, &numbers))
3546  {
3547  DebugLog("LoadScenarioSection: Error saving objects");
3548  return false;
3549  }
3550  }
3551  // close current group
3552  if (hGroup.IsOpen()) hGroup.Close();
3553  // mark modified
3555  }
3556  // open section group
3557  if (!(pGrp=pLoadSect->GetGroupfile(hGroup)))
3558  {
3559  DebugLog("LoadScenarioSection: error opening group file");
3560  return false;
3561  }
3562  // remove all objects
3563  // do correct removal calls, because this will stop fire sounds, etc.
3564  for (C4Object *obj : Objects)
3565  obj->AssignRemoval();
3566  for (C4Object *obj : Objects)
3567  if (obj->Status)
3568  {
3569  DebugLogF("LoadScenarioSection: WARNING: Object %d created in destruction process!", (int) obj->Number);
3570  ClearPointers(obj);
3571  }
3572  // Final removal in case objects got recreated
3573  // Also kill inactive objects if scenario is reinitialized
3574  DeleteObjects(!!(dwFlags & C4S_REINIT_SCENARIO));
3575  // remove global effects
3576  if (::ScriptEngine.pGlobalEffects && !(dwFlags & C4S_KEEP_EFFECTS))
3577  {
3579  // scenario section call might have been done from a global effect
3580  // rely on dead effect removal for actually removing the effects; do not clear the array here!
3581  }
3582  if (::GameScript.pScenarioEffects && !(dwFlags & C4S_KEEP_EFFECTS))
3584  // del particles as well
3586  // clear transfer zones
3587  TransferZones.Clear();
3588  // backup old sky
3589  std::string old_sky;
3590  old_sky = C4S.Landscape.SkyDef;
3591  // do not warn on ignored values in main section
3592  // they are caused because not all parts of scenario core are compiled on section change
3593  bool is_main_section = SEqualNoCase(pLoadSect->name.getData(), C4ScenSect_Main);
3594  // overload scenario values (fails if no scenario core is present; that's OK)
3595  C4S.Load(*pGrp, true, is_main_section);
3596  // determine whether a new sky has to be loaded
3597  bool fLoadNewSky = !SEqualNoCase(old_sky.c_str(), C4S.Landscape.SkyDef.c_str()) || pGrp->FindEntry(C4CFN_Sky ".*");
3598  // set new Objects.c source
3600  // remove reference to FoW from viewports, so that we can safely
3601  // reload the landscape and its FoW.
3603  // landscape initialization resets the RNG
3604  // set a new seed here to get new dynamic landscapes
3605  // TODO: add an option to disable this?
3606  RandomSeed = Random(2147483647);
3608  // re-init game in new section
3609  C4ValueNumbers numbers;
3610  if (!InitGame(*pGrp, (dwFlags & C4S_REINIT_SCENARIO) ? IM_ReInit : IM_Section, fLoadNewSky, &numbers))
3611  {
3612  DebugLog("LoadScenarioSection: Error reiniting game");
3614  return false;
3615  }
3616  // restore shelved proplists in case loading failed
3618  // set new current section
3619  pCurrentScenarioSection = pLoadSect;
3621  // Final init on game re-init (doing mostly player initialization)
3622  if (dwFlags & C4S_REINIT_SCENARIO)
3623  {
3625  // Extra InitializePlayers callback on the already-joined players to start intros, etc.
3626  // (unless the call is still pending - can happen if section is loaded during player join)
3629  }
3630  // resize viewports, and enable lighting again
3633  // done, success
3634  return true;
3635 }
3636 
3638 {
3639  // debug mode not allowed
3640  if (!Parameters.AllowDebug && !DebugMode) { GraphicsSystem.FlashMessage(LoadResStr("IDS_MSG_DEBUGMODENOTALLOWED")); return false; }
3641  DebugMode = !DebugMode;
3643  GraphicsSystem.FlashMessageOnOff(LoadResStr("IDS_CTL_DEBUGMODE"), DebugMode);
3644  return true;
3645 }
3646 
3647 bool C4Game::ActivateMenu(const char *szCommand)
3648 {
3649  // no new menu during round evaluation
3650  if (C4GameOverDlg::IsShown()) return false;
3651  // forward to primary player
3653  if (!pPlr) return false;
3654  pPlr->Menu.ActivateCommand(pPlr->Number, szCommand);
3655  return true;
3656 }
3657 
3659 {
3661  return true;
3662 }
3663 
3664 void C4Game::Abort(bool fApproved)
3665 {
3666  // league needs approval
3667  if (Network.isEnabled() && Parameters.isLeague() && !fApproved)
3668  {
3669  if (Control.isCtrlHost() && !Game.GameOver)
3670  {
3672  return;
3673  }
3675  {
3676  Network.Vote(VT_Kick, true, Control.ClientID());
3677  return;
3678  }
3679  }
3680  // hard-abort: eval league and quit
3681  // manually evaluate league
3682  Players.RemoveLocal(true, true);
3683  Players.RemoveAtRemoteClient(true, true);
3684  // normal quit
3686 }
3687 
3688 static const std::unordered_map<std::string, C4GUI::Icons> str_to_icon =
3689 {
3690  { "Locked", C4GUI::Ico_Ex_LockedFrontal },
3691  { "League", C4GUI::Ico_Ex_League },
3692  { "GameRunning", C4GUI::Ico_GameRunning },
3693  { "Lobby", C4GUI::Ico_Lobby },
3694  { "RuntimeJoin", C4GUI::Ico_RuntimeJoin },
3695 
3696  { "A", C4GUI::Ico_Controller_A },
3697  { "B", C4GUI::Ico_Controller_B },
3698  { "X", C4GUI::Ico_Controller_X },
3699  { "Y", C4GUI::Ico_Controller_Y },
3700  { "Back", C4GUI::Ico_Controller_Back },
3701  { "Start", C4GUI::Ico_Controller_Start },
3702  { "Dpad", C4GUI::Ico_Controller_Dpad },
3703  { "DpadLeft", C4GUI::Ico_Controller_DpadLeft },
3704  { "DpadRight", C4GUI::Ico_Controller_DpadRight },
3705  { "DpadDown", C4GUI::Ico_Controller_DpadDown },
3706  { "DpadUp", C4GUI::Ico_Controller_DpadUp },
3707  { "LeftShoulder", C4GUI::Ico_Controller_LeftShoulder },
3708  { "RightShoulder", C4GUI::Ico_Controller_RightShoulder },
3709  { "LeftTrigger", C4GUI::Ico_Controller_LeftTrigger },
3710  { "RightTrigger", C4GUI::Ico_Controller_RightTrigger },
3711  { "LeftStick", C4GUI::Ico_Controller_LeftStick },
3712  { "RightStick", C4GUI::Ico_Controller_RightStick },
3713 };
3714 
3715 bool GetTextSpecFacet(const char* szSpec, C4Facet& fct)
3716 {
3717  // safety
3718  assert(szSpec && *szSpec);
3719  if (!szSpec) return false;
3720  // Special icon?
3721  if (SEqual2(szSpec, "@Ico:"))
3722  {
3723  szSpec += 5;
3724  auto it = str_to_icon.find(szSpec);
3725  if (it != str_to_icon.end())
3726  {
3727  fct = C4GUI::Icon::GetIconFacet(it->second);
3728  return true;
3729  }
3730  }
3731 
3732  return false;
3733 }
3734 
3735 bool C4Game::DrawTextSpecImage(C4Facet &fctTarget, const char *szSpec, C4DrawTransform* pTransform, uint32_t dwClr)
3736 {
3737  // safety
3738  assert(szSpec && *szSpec);
3739  if (!szSpec) return false;
3740 
3741  C4Facet fctSource;
3742  if(GetTextSpecFacet(szSpec, fctSource))
3743  {
3744  fctSource.DrawXT(fctTarget.Surface, fctTarget.X, fctTarget.Y, fctTarget.Wdt, fctTarget.Hgt, 0, 0, pTransform);
3745  return true;
3746  }
3747  else
3748  {
3749  C4Def *pDef = C4Id2Def(C4ID(szSpec));
3750  if (!pDef) return false;
3751 
3752  pDef->Draw(fctTarget, false, dwClr, nullptr, 0, 0, pTransform);
3753  return true;
3754  }
3755 }
3756 
3757 float C4Game::GetTextSpecImageAspect(const char* szSpec)
3758 {
3759  // safety
3760  assert(szSpec && *szSpec);
3761  if (!szSpec) return -1.0f;
3762 
3763  C4Facet fctSource;
3764  if(GetTextSpecFacet(szSpec, fctSource))
3765  {
3766  return static_cast<float>(fctSource.Wdt) / static_cast<float>(fctSource.Hgt);
3767  }
3768  else
3769  {
3770  C4Def *pDef = C4Id2Def(C4ID(szSpec));
3771  if (!pDef) return -1.0f;
3772 
3773  C4DefGraphics* pGfx = &pDef->Graphics;
3774  if(pGfx->Type == C4DefGraphics::TYPE_Bitmap)
3775  {
3776  return static_cast<float>(pDef->PictureRect.Wdt) / static_cast<float>(pDef->PictureRect.Hgt);
3777  }
3778  else if (pGfx->Type == C4DefGraphics::TYPE_Mesh)
3779  {
3780  const StdMesh& mesh = *pGfx->Mesh;
3781  const StdMeshBox& box = mesh.GetBoundingBox();
3782  return (box.x2 - box.x1) / (box.y2 - box.y1);
3783  }
3784 
3785  return -1.0f;
3786  }
3787 }
3788 
3790 {
3791  // safety
3792  assert(pSpec);
3793  if (!pSpec) return false;
3794 
3795  // get source definition
3796  C4PropList *source_def_proplist = pSpec->GetPropertyPropList(P_Source);
3797  if (!source_def_proplist) return false;
3798  C4Def *source_def = source_def_proplist->GetDef();
3799  if (!source_def) return false;
3800 
3801  // get custom color
3802  uint32_t color = (uint32_t)pSpec->GetPropertyInt(P_Color);
3803 
3804  C4String *source_name = pSpec->GetPropertyStr(P_Name);
3805  if (!source_name)
3806  {
3807  // Base graphics
3808  source_def->Draw(fctTarget, false, color);
3809  }
3810  else
3811  {
3812  // Alternative named graphics
3813  C4DefGraphics *source_graphics = source_def->Graphics.Get(source_name->GetCStr());
3814  if (!source_graphics) return false;
3815  source_graphics->Draw(fctTarget, color, nullptr, 0,0, nullptr);
3816  }
3817  return true;
3818 }
3819 
3821 {
3822  // As these functions work stepwise, there's the old maximum speed of 50.
3823  // Use /fast to set to even higher speeds.
3824  FrameSkip = Clamp<int32_t>(FrameSkip + 1, 1, 50);
3825  FullSpeed = true;
3826  GraphicsSystem.FlashMessage(FormatString(LoadResStr("IDS_MSG_SPEED"), FrameSkip).getData());
3827  return true;
3828 }
3829 
3831 {
3832  FrameSkip = Clamp<int32_t>(FrameSkip - 1, 1, 50);
3833  if (FrameSkip == 1)
3834  FullSpeed = false;
3835  GraphicsSystem.FlashMessage(FormatString(LoadResStr("IDS_MSG_SPEED"), FrameSkip).getData());
3836  return true;
3837 }
3838 
3840 {
3841  return C4ChatDlg::ToggleChat();
3842 }
3843 
3844 C4Value C4Game::GRBroadcast(const char *szFunction, C4AulParSet *pPars, bool fPassError, bool fRejectTest)
3845 {
3846  std::string func{ szFunction };
3847  if (func[0] != '~')
3848  func.insert(0, 1, '~');
3849 
3850  // call objects first - scenario script might overwrite hostility, etc...
3851  C4Value vResult = ::Objects.GRBroadcast(func.c_str(), pPars, fPassError, fRejectTest);
3852  // rejection tests abort on first nonzero result
3853  if (fRejectTest) if (!!vResult) return vResult;
3854  // scenario script call
3855  return ::GameScript.Call(func.c_str(), pPars, fPassError);
3856 }
3857 
3859 {
3860  // Skip this if graphics haven't been initialized yet (happens when
3861  // we bail during initialization)
3862  if (!pDraw) return;
3863  // Default gamma
3864  pDraw->ResetGamma();
3865  pDraw->SetGamma(float(Config.Graphics.Gamma) / 100.0,
3866  float(Config.Graphics.Gamma) / 100.0,
3867  float(Config.Graphics.Gamma) / 100.0,
3869 }
3870 
3872 {
3873  // set in prop list (for savegames) and in sound system::
3874  C4SoundModifier *mod;
3875  if (new_modifier)
3876  {
3877  GlobalSoundModifier.SetPropList(new_modifier);
3878  mod = ::Application.SoundSystem.Modifiers.Get(new_modifier, true);
3879  }
3880  else
3881  {
3883  mod = nullptr;
3884  }
3886 }
3887 
3888 C4String *C4Game::GetTranslatedString(const C4Value &input_string, C4Value *selected_language, bool fail_silently) const
3889 {
3890  // Resolve a localized string
3891  // If a string is passed, just return it
3892  // If a proplist like { DE="Hallo, Welt!", US="Hello, world!" } is passed, return the string matching the selected language
3893  // Nothing?
3894  if (input_string.GetType() == C4V_Nil)
3895  {
3896  return nullptr;
3897  }
3898  // Non-localized string?
3899  if (input_string.GetType() == C4V_String)
3900  {
3901  return input_string._getStr();
3902  }
3903  // Invalid type for this function?
3904  C4PropList *p = input_string._getPropList();
3905  if (!p || p->GetPropertyStr(P_Function) != &::Strings.P[P_Translate])
3906  {
3907  if (fail_silently)
3908  {
3909  return nullptr;
3910  }
3911  else
3912  {
3913  throw C4AulExecError(FormatString("Invalid value for translation: %s", input_string.GetDataString().getData()).getData());
3914  }
3915  }
3916  // This is a proplist. Resolve the language as the key.
3917  char lang_code[3] = "";
3918  for (int32_t lang_index = 0; SCopySegment(Config.General.LanguageEx, lang_index, lang_code, ',', 2); ++lang_index)
3919  {
3920  C4String *lang_string = ::Strings.FindString(lang_code);
3921  if (lang_string) // If the string is not found, it cannot be the key in a prop list
3922  {
3923  C4Value localized_string_val;
3924  if (p->GetPropertyByS(lang_string, &localized_string_val))
3925  {
3926  C4String *localized_string = localized_string_val.getStr();
3927  if (localized_string)
3928  {
3929  // Found it!
3930  if (selected_language)
3931  {
3932  selected_language->SetString(lang_string);
3933  }
3934  return localized_string;
3935  }
3936  }
3937  }
3938  }
3939  // No language matched. Just use any property and assume it's a language key.
3940  for (C4String *lang_string : p->GetSortedLocalProperties(false))
3941  {
3942  C4Value localized_string_val;
3943  if (p->GetPropertyByS(lang_string, &localized_string_val))
3944  {
3945  C4String *localized_string = localized_string_val.getStr();
3946  if (localized_string)
3947  {
3948  // Found it!
3949  if (selected_language)
3950  {
3951  selected_language->SetString(lang_string);
3952  }
3953  return localized_string;
3954  }
3955  }
3956  }
3957  // No string properties. There's no localized information to be found.
3958  return nullptr;
3959 }
3960 
3962 {
3963  C4PropListScript *value_proplist = new C4PropListScript();
3964  value_proplist->SetProperty(P_Function, C4VString(&::Strings.P[P_Translate]));
3965  return value_proplist;
3966 }
void Copy(C4MassMoverSet &rSet)
#define C4CFN_MassMover
Definition: C4Components.h:76
char * GetFilename(char *szPath)
Definition: StdFile.cpp:45
const char * getData() const
Definition: StdBuf.h:442
C4SoundSystem SoundSystem
Definition: C4Application.h:42
bool DoGameOver()
Definition: C4Game.cpp:3095
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)
C4Client * getLocal() const
Definition: C4Client.h:161
void SetInitProgress(float fToProgress)
Definition: C4Game.cpp:3383
float y2
Definition: StdMesh.h:147
void InitValueOverloads()
Definition: C4Game.cpp:3344
const C4PlayerControlAssignmentSets & GetAssignmentSets() const
bool FindEntry(const char *szWildCard, StdStrBuf *sFileName=nullptr, size_t *iSize=nullptr)
Definition: C4Group.cpp:1774
C4Group * pParentGroup
Definition: C4Game.h:88
int32_t GetY() const
Definition: C4Object.h:287
void RecheckPlayers()
Definition: C4Teams.cpp:663
C4String P[P_LAST]
void ClearPointers(C4Object *pObj)
int32_t GetIDCount(C4ID c_id, int32_t iZeroDefVal=0) const
Definition: stub-handle.cpp:67
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:139
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:2100
void RegisterGlobalConstant(const char *szName, const C4Value &rValue)
bool CreateSectionFromTempFile(const char *section_name, const char *temp_filename)
Definition: C4Game.cpp:3423
C4PlayerInfoList & RestorePlayerInfos
Definition: C4Game.h:72
bool Start()
Definition: C4Network2.cpp:505
void Any(T &keys)
bool NetworkRuntimeJoin
Definition: C4Scenario.h:79
#define SHA_DIGEST_LENGTH
int32_t iLobbyTimeout
Definition: C4Game.h:119
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:126
int32_t RandomSeed
Definition: C4Game.h:134
C4Config Config
Definition: C4Config.cpp:833
int32_t iTick255
Definition: C4Game.h:129
int GetLineHeight() const
Definition: C4FontLoader.h:125
std::string Material
Definition: C4Scenario.h:181
Definition: StdBuf.h:29
bool Init(C4Group &hGroup, bool fOverloadCurrent, bool fLoadSky, bool &rfLoaded, bool fSavegame)
bool GetSize(C4Rect *pRect)
Definition: C4AppT.cpp:105
bool ToggleChat()
Definition: C4Game.cpp:3839
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:219
void Synchronize()
void Up(T &keys)
void SCopy(const char *szSource, char *sTarget, size_t iMaxL)
Definition: Standard.cpp:152
C4SDefinitions Definitions
Definition: C4Scenario.h:231
std::unique_ptr< C4Network2Reference > pJoinReference
Definition: C4Game.h:108
StdStrBuf RecordDumpFile
Definition: C4Game.h:124
StdStrBuf GrabFileContents()
Definition: C4Aul.h:99
void StopRecord(StdStrBuf *pRecordName=nullptr, BYTE *pRecordSHA1=nullptr)
int EntryCount(const char *szWildCard=nullptr)
Definition: C4Group.cpp:1850
void InitEnvironment()
Definition: C4Game.cpp:3353
C4ID id
Definition: C4Object.h:108
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:146
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 isHost() const
Definition: C4Network2.h:209
bool Add(C4Object *nObj)
bool SynchronizeLocalFiles()
int32_t GetJoinPendingPlayerCount() const
float GetTextSpecImageAspect(const char *szSpec)
Definition: C4Game.cpp:3757
int32_t iTick5
Definition: C4Game.h:129
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)
bool isRunning() const
Definition: C4Network2.h:206
C4MainMenu Menu
Definition: C4Player.h:103
void SetExactLandscape()
Definition: C4Scenario.cpp:413
C4String * getStr() const
Definition: C4Value.h:117
bool PopMaterial()
Definition: C4Console.cpp:703
void SyncClearance()
Definition: C4Game.cpp:3129
bool isCtrlHost() const
Definition: C4GameControl.h:99
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
~C4Game()
Definition: C4Game.cpp:114
C4GameScriptHost GameScript
C4SEnvironment Environment
Definition: C4Scenario.h:237
int32_t iTick35
Definition: C4Game.h:129
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
bool isRecord() const
#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:2590
C4String * FindString(const char *strString) const
bool isLobbyActive() const
Definition: C4Network2.h:204
const char * GetData() const
StdCopyStrBuf ScenarioTitle
Definition: C4Game.h:103
#define C4ST_RESETPART
Definition: C4Stat.h:157
void Clear()
Definition: C4Scenario.cpp:408
#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:295
#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:3373
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)
bool HasStringTable() const
Definition: C4Language.h:70
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:274
bool RemoveAtRemoteClient(bool fDisonnected, bool fNoCalls)
static void ClearNumberedPropLists()
Definition: C4PropList.cpp:137
void OnPlayerJoinFinished()
Definition: C4Game.cpp:3080
#define C4CFN_RoundResults
Definition: C4Components.h:129
C4SLandscape Landscape
Definition: C4Scenario.h:234
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:116
C4Material * Map
Definition: C4Material.h:169
#define C4OS_NORMAL
Definition: C4Object.h:35
const char * GetCStr() const
Definition: C4StringTable.h:49
int CompareVersion(int iVer1, int iVer2, int iRVer1=C4XVER1, int iRVer2=C4XVER2)
Definition: C4GameVersion.h:51
bool LoadFromFile(const char *szFile)
Definition: StdBuf.cpp:32
float x2
Definition: StdMesh.h:147
bool TogglePause()
Definition: C4Console.cpp:617
C4KeyShiftState
C4Value C4VInt(int32_t i)
Definition: C4Value.h:242
bool PostInitMap()
bool ToggleAllowJoin()
Definition: C4Network2.cpp:758
void DoDlgShow(int32_t iChange, bool fUserToggle)
#define C4FxCall_RemoveClear
Definition: C4Effect.h:45
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:148
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
C4String * _getStr() const
Definition: C4Value.h:126
#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)
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)
virtual const char * GetError() const
Definition: C4NetIO.h:284
int32_t GetScreenWdt()
Definition: C4Gui.h:2821
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:3961
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:165
virtual bool Name(const char *szName)
Definition: StdCompiler.h:77
C4TextureMap TextureMap
Definition: C4Texture.cpp:576
C4MessageInput MessageInput
int32_t LastInitProgress
Definition: C4Game.h:133
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
bool GetModules(StdStrBuf *psOutModules) const
Definition: C4Scenario.cpp:420
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
StdStrBuf GetDataString(int depth=10, const class C4PropListStatic *ignore_reference_parent=nullptr) const
Definition: C4Value.cpp:131
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:3465
bool Delete(const char *szFiles, bool fRecursive=false)
Definition: C4Group.cpp:1334
bool LandscapeFree(int32_t x, int32_t y)
Definition: C4Game.cpp:1987
#define _MAX_PATH
int GetCount() const
C4ClientList Clients
const int C4UpperBoardHeight
Definition: C4Constants.h:59
bool ViewportZoomIn()
bool DoSkipFrame
Definition: C4Game.h:137
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:1775
void AppendBackslash(char *szFilename)
Definition: StdFile.cpp:257
C4ObjectList * NextObjectShapes(C4ObjectList *pPrev, C4LSector **ppSct)
Definition: C4Sector.cpp:317
void UpdateMenus()
Definition: C4Console.cpp:504
void Set(C4Surface &rSfc)
Definition: C4Facet.cpp:459
void ScenarioInit()
C4AulUserFile * GetUserFile(int32_t handle)
C4ComponentHost Names
Definition: C4Game.h:78
#define PSF_OnGameOver
Definition: C4GameScript.h:138
bool InitPlayers(C4ValueNumbers *)
Definition: C4Game.cpp:2448
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
StdStrBuf DebugPassword
Definition: C4Game.h:148
C4ObjectPtr