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