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.Tasks; using GamecraftModdingAPI.Utility; // TODO: exceptions namespace GamecraftModdingAPI.App { public class Game { 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 debugIds = new List(); private bool menuMode = true; private bool hasId = false; public Game(uint id) : this(new EGID(id, MyGamesScreenExclusiveGroups.MyGames)) { } 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?)"); } 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."); } public static Game CurrentGame() { return new Game(); } 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); } public static event EventHandler Simulate { add => buildSimEventEngine.SimulationMode += value; remove => buildSimEventEngine.SimulationMode -= value; } public static event EventHandler Edit { add => buildSimEventEngine.BuildMode += value; remove => buildSimEventEngine.BuildMode -= value; } public static event EventHandler Enter { add => gameEngine.EnterGame += value; remove => gameEngine.EnterGame -= value; } public static event EventHandler Exit { add => gameEngine.ExitGame += value; remove => gameEngine.ExitGame -= value; } public uint Id { get; private set; } public EGID EGID { get; private set; } public bool MenuItem { get => menuMode && hasId; } 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; } } } 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 } } } 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); } } } 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); } } } public bool IsSimulating { get { if (!VerifyMode()) return false; return !menuMode && gameEngine.IsTimeRunningMode(); } set { if (!VerifyMode()) return; if (!menuMode && gameEngine.IsTimeRunningMode() != value) gameEngine.ToggleTimeMode(); } } public bool IsTimeRunning { get => IsSimulating; set { IsSimulating = value; } } public bool IsTimeStopped { get { if (!VerifyMode()) return false; return !menuMode && gameEngine.IsTimeStoppedMode(); } set { if (!VerifyMode()) return; if (!menuMode && gameEngine.IsTimeStoppedMode() != value) gameEngine.ToggleTimeMode(); } } public void ToggleTimeMode() { if (!VerifyMode()) return; if (menuMode || !gameEngine.IsInGame) { throw new AppStateException("Game menu item cannot toggle it's time mode"); } gameEngine.ToggleTimeMode(); } 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); } public void ExitGame() { if (!VerifyMode()) return; if (menuMode) { throw new GameNotFoundException("Cannot exit game using menu ID"); } ISchedulable task = new Once(() => { gameEngine.ExitCurrentGame(); this.menuMode = true; }); Scheduler.Schedule(task); } public void AddDebugInfo(string id, Func 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); } 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); } ~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); } } }