|
- using System;
- using System.Collections.Generic;
- using System.Runtime.CompilerServices;
-
- using RobocraftX.Common;
- using RobocraftX.GUI.MyGamesScreen;
- using RobocraftX.StateSync;
- using Svelto.ECS;
-
- using GamecraftModdingAPI;
- using GamecraftModdingAPI.Blocks;
- using GamecraftModdingAPI.Tasks;
- using GamecraftModdingAPI.Utility;
-
- namespace GamecraftModdingAPI.App
- {
- /// <summary>
- /// An in-game save.
- /// This can be a menu item for a local save or the currently loaded save.
- /// Support for Steam Workshop coming soon (hopefully).
- /// </summary>
- public class Game
- {
- // extensible engines
- protected static GameGameEngine gameEngine = new GameGameEngine();
- protected static GameMenuEngine menuEngine = new GameMenuEngine();
- protected static DebugInterfaceEngine debugOverlayEngine = new DebugInterfaceEngine();
- protected static GameBuildSimEventEngine buildSimEventEngine = new GameBuildSimEventEngine();
-
- private List<string> debugIds = new List<string>();
-
- private bool menuMode = true;
- private bool hasId = false;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="T:GamecraftModdingAPI.App.Game"/> class.
- /// </summary>
- /// <param name="id">Menu identifier.</param>
- public Game(uint id) : this(new EGID(id, MyGamesScreenExclusiveGroups.MyGames))
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="T:GamecraftModdingAPI.App.Game"/> class.
- /// </summary>
- /// <param name="id">Menu identifier.</param>
- public Game(EGID id)
- {
- this.Id = id.entityID;
- this.EGID = id;
- this.hasId = true;
- menuMode = true;
- if (!VerifyMode()) throw new AppStateException("Game cannot be created while not in a game nor in a menu (is the game in a loading screen?)");
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="T:GamecraftModdingAPI.App.Game"/> class without id.
- /// This is assumed to be the current game.
- /// </summary>
- public Game()
- {
- menuMode = false;
- if (!VerifyMode()) throw new AppStateException("Game cannot be created while not in a game nor in a menu (is the game in a loading screen?)");
- if (menuEngine.IsInMenu) throw new GameNotFoundException("Game not found.");
- }
-
- /// <summary>
- /// Returns the currently loaded game.
- /// If in a menu, manipulating the returned object may not work as intended.
- /// </summary>
- /// <returns>The current game.</returns>
- public static Game CurrentGame()
- {
- return new Game();
- }
-
- /// <summary>
- /// Creates a new game and adds it to the menu.
- /// If not in a menu, this will throw AppStateException.
- /// </summary>
- /// <returns>The new game.</returns>
- public static Game NewGame()
- {
- if (!menuEngine.IsInMenu) throw new AppStateException("New Game cannot be created while not in a menu.");
- uint nextId = menuEngine.HighestID() + 1;
- EGID egid = new EGID(nextId, MyGamesScreenExclusiveGroups.MyGames);
- menuEngine.CreateMyGame(egid);
- return new Game(egid);
- }
-
- /// <summary>
- /// An event that fires whenever a game is switched to simulation mode (time running mode).
- /// </summary>
- public static event EventHandler<GameEventArgs> Simulate
- {
- add => buildSimEventEngine.SimulationMode += value;
- remove => buildSimEventEngine.SimulationMode -= value;
- }
-
- /// <summary>
- /// An event that fires whenever a game is switched to edit mode (time stopped mode).
- /// This does not fire when a game is loaded.
- /// </summary>
- public static event EventHandler<GameEventArgs> Edit
- {
- add => buildSimEventEngine.BuildMode += value;
- remove => buildSimEventEngine.BuildMode -= value;
- }
-
- /// <summary>
- /// An event that fires right after a game is completely loaded.
- /// </summary>
- public static event EventHandler<GameEventArgs> Enter
- {
- add => gameEngine.EnterGame += value;
- remove => gameEngine.EnterGame -= value;
- }
-
- /// <summary>
- /// An event that fires right before a game returns to the main menu.
- /// At this point, Gamecraft is transitioning state so many things are invalid/unstable here.
- /// </summary>
- public static event EventHandler<GameEventArgs> Exit
- {
- add => gameEngine.ExitGame += value;
- remove => gameEngine.ExitGame -= value;
- }
-
- /// <summary>
- /// The game's unique menu identifier.
- /// </summary>
- /// <value>The identifier.</value>
- public uint Id
- {
- get;
- private set;
- }
-
- /// <summary>
- /// The game's unique menu EGID.
- /// </summary>
- /// <value>The egid.</value>
- public EGID EGID
- {
- get;
- private set;
- }
-
- /// <summary>
- /// Whether the game is a (valid) menu item.
- /// </summary>
- /// <value><c>true</c> if menu item; otherwise, <c>false</c>.</value>
- public bool MenuItem
- {
- get => menuMode && hasId;
- }
-
- /// <summary>
- /// The game's name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get
- {
- if (!VerifyMode()) return null;
- if (menuMode) return menuEngine.GetGameInfo(EGID).GameName;
- return GameMode.SaveGameDetails.Name;
- }
-
- set
- {
- if (!VerifyMode()) return;
- if (menuMode)
- {
- menuEngine.SetGameName(EGID, value);
- }
- else
- {
- GameMode.SaveGameDetails.Name = value;
- }
- }
- }
-
- /// <summary>
- /// The game's description.
- /// </summary>
- /// <value>The description.</value>
- public string Description
- {
- get
- {
- if (!VerifyMode()) return null;
- if (menuMode) return menuEngine.GetGameInfo(EGID).GameDescription;
- return "";
- }
-
- set
- {
- if (!VerifyMode()) return;
- if (menuMode)
- {
- menuEngine.SetGameDescription(EGID, value);
- }
- else
- {
- // No description exists in-game
- }
- }
- }
-
- /// <summary>
- /// The path to the game's save folder.
- /// </summary>
- /// <value>The path.</value>
- public string Path
- {
- get
- {
- if (!VerifyMode()) return null;
- if (menuMode) return menuEngine.GetGameInfo(EGID).SavedGamePath;
- return GameMode.SaveGameDetails.Folder;
- }
-
- set
- {
- if (!VerifyMode()) return;
- if (menuMode)
- {
- menuEngine.GetGameInfo(EGID).SavedGamePath.Set(value);
- }
- else
- {
- // this likely breaks things
- GameMode.SaveGameDetails = new SaveGameDetails(GameMode.SaveGameDetails.Name, value, GameMode.SaveGameDetails.WorkshopId);
- }
- }
- }
-
- /// <summary>
- /// The Steam Workshop Id of the game save.
- /// In most cases this is invalid and returns 0, so this can be ignored.
- /// </summary>
- /// <value>The workshop identifier.</value>
- public ulong WorkshopId
- {
- get
- {
- if (!VerifyMode()) return 0uL;
- if (menuMode) return 0uL; // MyGames don't have workshop IDs
- return GameMode.SaveGameDetails.WorkshopId;
- }
-
- set
- {
- VerifyMode();
- if (menuMode)
- {
- // MyGames don't have workshop IDs
- // menuEngine.GetGameInfo(EGID).GameName.Set(value);
- }
- else
- {
- // this likely breaks things
- GameMode.SaveGameDetails = new SaveGameDetails(GameMode.SaveGameDetails.Name, GameMode.SaveGameDetails.Folder, value);
- }
- }
- }
-
- /// <summary>
- /// Whether the game is in simulation mode.
- /// </summary>
- /// <value><c>true</c> if is simulating; otherwise, <c>false</c>.</value>
- public bool IsSimulating
- {
- get
- {
- if (!VerifyMode()) return false;
- return !menuMode && gameEngine.IsTimeRunningMode();
- }
-
- set
- {
- if (!VerifyMode()) return;
- if (!menuMode && gameEngine.IsTimeRunningMode() != value)
- gameEngine.ToggleTimeMode();
- }
- }
-
- /// <summary>
- /// Whether the game is in time-running mode.
- /// Alias of IsSimulating.
- /// </summary>
- /// <value><c>true</c> if is time running; otherwise, <c>false</c>.</value>
- public bool IsTimeRunning
- {
- get => IsSimulating;
-
- set
- {
- IsSimulating = value;
- }
- }
-
- /// <summary>
- /// Whether the game is in time-stopped mode.
- /// </summary>
- /// <value><c>true</c> if is time stopped; otherwise, <c>false</c>.</value>
- public bool IsTimeStopped
- {
- get
- {
- if (!VerifyMode()) return false;
- return !menuMode && gameEngine.IsTimeStoppedMode();
- }
-
- set
- {
- if (!VerifyMode()) return;
- if (!menuMode && gameEngine.IsTimeStoppedMode() != value)
- gameEngine.ToggleTimeMode();
- }
- }
-
- /// <summary>
- /// Toggles the time mode.
- /// </summary>
- public void ToggleTimeMode()
- {
- if (!VerifyMode()) return;
- if (menuMode || !gameEngine.IsInGame)
- {
- throw new AppStateException("Game menu item cannot toggle it's time mode");
- }
- gameEngine.ToggleTimeMode();
- }
-
- /// <summary>
- /// Load the game save.
- /// This happens asynchronously, so when this method returns the game not loaded yet.
- /// Use the Game.Enter event to perform operations after the game has completely loaded.
- /// </summary>
- public void EnterGame()
- {
- if (!VerifyMode()) return;
- if (!hasId)
- {
- throw new GameNotFoundException("Game has an invalid ID");
- }
- ISchedulable task = new Once(() => { menuEngine.EnterGame(EGID); this.menuMode = false; });
- Scheduler.Schedule(task);
- }
-
- /// <summary>
- /// Return to the menu.
- /// Part of this always happens asynchronously, so when this method returns the game has not exited yet.
- /// Use the Client.EnterMenu event to perform operations after the game has completely exited.
- /// </summary>
- /// <param name="async">If set to <c>true</c>, do this async.</param>
- public void ExitGame(bool async = false)
- {
- if (!VerifyMode()) return;
- if (menuMode)
- {
- throw new GameNotFoundException("Cannot exit game using menu ID");
- }
- gameEngine.ExitCurrentGame(async);
- this.menuMode = true;
- }
-
- /// <summary>
- /// Saves the game.
- /// Part of this happens asynchronously, so when this method returns the game has not been saved yet.
- /// </summary>
- public void SaveGame()
- {
- if (!VerifyMode()) return;
- if (menuMode)
- {
- throw new GameNotFoundException("Cannot save game using menu ID");
- }
- gameEngine.SaveCurrentGame();
- }
-
- /// <summary>
- /// Add information to the in-game debug display.
- /// When this object is garbage collected, this debug info is automatically removed.
- /// </summary>
- /// <param name="id">Debug info identifier.</param>
- /// <param name="contentGetter">Content getter.</param>
- public void AddDebugInfo(string id, Func<string> contentGetter)
- {
- if (!VerifyMode()) return;
- if (menuMode)
- {
- throw new GameNotFoundException("Game object references a menu item but AddDebugInfo only works on the currently-loaded game");
- }
- debugOverlayEngine.SetInfo(id, contentGetter);
- debugIds.Add(id);
- }
-
- /// <summary>
- /// Remove information from the in-game debug display.
- /// </summary>
- /// <returns><c>true</c>, if debug info was removed, <c>false</c> otherwise.</returns>
- /// <param name="id">Debug info identifier.</param>
- public bool RemoveDebugInfo(string id)
- {
- if (!VerifyMode()) return false;
- if (menuMode)
- {
- throw new GameNotFoundException("Game object references a menu item but RemoveDebugInfo only works on the currently-loaded game");
- }
- if (!debugIds.Contains(id)) return false;
- debugOverlayEngine.RemoveInfo(id);
- return debugIds.Remove(id);
- }
-
- /// <summary>
- /// Gets the blocks in the game.
- /// This returns null when in a loading state, and throws AppStateException when in menu.
- /// </summary>
- /// <returns>The blocks in game.</returns>
- /// <param name="filter">The block to search for. BlockIDs.Invalid will return all blocks.</param>
- public Block[] GetBlocksInGame(BlockIDs filter = BlockIDs.Invalid)
- {
- if (!VerifyMode()) return null;
- if (menuMode)
- {
- throw new AppStateException("Game object references a menu item but GetBlocksInGame only works on the currently-loaded game");
- }
- EGID[] blockEGIDs = gameEngine.GetAllBlocksInGame(filter);
- Block[] blocks = new Block[blockEGIDs.Length];
- for (int b = 0; b < blockEGIDs.Length; b++)
- {
- blocks[b] = new Block(blockEGIDs[b]);
- }
- return blocks;
- }
-
- ~Game()
- {
- foreach (string id in debugIds)
- {
- debugOverlayEngine.RemoveInfo(id);
- }
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private bool VerifyMode()
- {
- if (menuMode && (!menuEngine.IsInMenu || gameEngine.IsInGame))
- {
- // either game loading or API is broken
- return false;
- }
- if (!menuMode && (menuEngine.IsInMenu || !gameEngine.IsInGame))
- {
- // either game loading or API is broken
- return false;
- }
- return true;
- }
-
- internal static void Init()
- {
- GameEngineManager.AddGameEngine(gameEngine);
- GameEngineManager.AddGameEngine(debugOverlayEngine);
- MenuEngineManager.AddMenuEngine(menuEngine);
- }
-
- internal static void InitDeterministic(StateSyncRegistrationHelper stateSyncReg)
- {
- stateSyncReg.AddDeterministicEngine(buildSimEventEngine);
- }
- }
- }
|