diff --git a/GamecraftModdingAPI/App/AppCallbacksTest.cs b/GamecraftModdingAPI/App/AppCallbacksTest.cs
index 0c2987b..041ac50 100644
--- a/GamecraftModdingAPI/App/AppCallbacksTest.cs
+++ b/GamecraftModdingAPI/App/AppCallbacksTest.cs
@@ -5,6 +5,10 @@ using GamecraftModdingAPI.Tests;
namespace GamecraftModdingAPI.App
{
#if TEST
+ ///
+ /// App callbacks tests.
+ /// Only available in TEST builds.
+ ///
[APITestClass]
public static class AppCallbacksTest
{
diff --git a/GamecraftModdingAPI/App/Client.cs b/GamecraftModdingAPI/App/Client.cs
index 2f1f005..e95d9f3 100644
--- a/GamecraftModdingAPI/App/Client.cs
+++ b/GamecraftModdingAPI/App/Client.cs
@@ -6,32 +6,56 @@ using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.App
{
+ ///
+ /// The Gamecraft application that is running this code right now.
+ ///
public class Client
{
+ // extensible engine
protected static AppEngine appEngine = new AppEngine();
+ ///
+ /// An event that fires whenever the main menu is loaded.
+ ///
public static event EventHandler EnterMenu
{
add => appEngine.EnterMenu += value;
remove => appEngine.EnterMenu -= value;
}
+ ///
+ /// An event that fire whenever the main menu is exited.
+ ///
public static event EventHandler ExitMenu
{
add => appEngine.ExitMenu += value;
remove => appEngine.ExitMenu -= value;
}
+ ///
+ /// Gamecraft build version string.
+ /// Usually this is in the form YYYY.mm.DD.HH.MM.SS
+ ///
+ /// The version.
public string Version
{
get => Application.version;
}
+ ///
+ /// Unity version string.
+ ///
+ /// The unity version.
public string UnityVersion
{
get => Application.unityVersion;
}
+ ///
+ /// Game saves currently visible in the menu.
+ /// These take a second to completely populate after the EnterMenu event fires.
+ ///
+ /// My games.
public Game[] MyGames
{
get
diff --git a/GamecraftModdingAPI/App/Game.cs b/GamecraftModdingAPI/App/Game.cs
index 0d31abf..3fd4900 100644
--- a/GamecraftModdingAPI/App/Game.cs
+++ b/GamecraftModdingAPI/App/Game.cs
@@ -9,12 +9,17 @@ using Svelto.ECS;
using GamecraftModdingAPI.Tasks;
using GamecraftModdingAPI.Utility;
-// TODO: exceptions
namespace GamecraftModdingAPI.App
{
+ ///
+ /// 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).
+ ///
public class Game
{
+ // extensible engines
protected static GameGameEngine gameEngine = new GameGameEngine();
protected static GameMenuEngine menuEngine = new GameMenuEngine();
protected static DebugInterfaceEngine debugOverlayEngine = new DebugInterfaceEngine();
@@ -25,10 +30,18 @@ namespace GamecraftModdingAPI.App
private bool menuMode = true;
private bool hasId = false;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Menu identifier.
public Game(uint id) : this(new EGID(id, MyGamesScreenExclusiveGroups.MyGames))
{
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Menu identifier.
public Game(EGID id)
{
this.Id = id.entityID;
@@ -38,6 +51,10 @@ namespace GamecraftModdingAPI.App
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?)");
}
+ ///
+ /// Initializes a new instance of the class without id.
+ /// This is assumed to be the current game.
+ ///
public Game()
{
menuMode = false;
@@ -45,11 +62,21 @@ namespace GamecraftModdingAPI.App
if (menuEngine.IsInMenu) throw new GameNotFoundException("Game not found.");
}
+ ///
+ /// Returns the currently loaded game.
+ /// If in a menu, manipulating the returned object may not work as intended.
+ ///
+ /// The current game.
public static Game CurrentGame()
{
return new Game();
}
+ ///
+ /// Creates a new game and adds it to the menu.
+ /// If not in a menu, this will throw AppStateException.
+ ///
+ /// The new game.
public static Game NewGame()
{
if (!menuEngine.IsInMenu) throw new AppStateException("New Game cannot be created while not in a menu.");
@@ -59,47 +86,77 @@ namespace GamecraftModdingAPI.App
return new Game(egid);
}
+ ///
+ /// An event that fires whenever a game is switched to simulation mode (time running mode).
+ ///
public static event EventHandler Simulate
{
add => buildSimEventEngine.SimulationMode += value;
remove => buildSimEventEngine.SimulationMode -= value;
}
+ ///
+ /// An event that fires whenever a game is switched to edit mode (time stopped mode).
+ /// This does not fire when a game is loaded.
+ ///
public static event EventHandler Edit
{
add => buildSimEventEngine.BuildMode += value;
remove => buildSimEventEngine.BuildMode -= value;
}
+ ///
+ /// An event that fires right after a game is completely loaded.
+ ///
public static event EventHandler Enter
{
add => gameEngine.EnterGame += value;
remove => gameEngine.EnterGame -= value;
}
+ ///
+ /// 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.
+ ///
public static event EventHandler Exit
{
add => gameEngine.ExitGame += value;
remove => gameEngine.ExitGame -= value;
}
+ ///
+ /// The game's unique menu identifier.
+ ///
+ /// The identifier.
public uint Id
{
get;
private set;
}
+ ///
+ /// The game's unique menu EGID.
+ ///
+ /// The egid.
public EGID EGID
{
get;
private set;
}
+ ///
+ /// Whether the game is a (valid) menu item.
+ ///
+ /// true if menu item; otherwise, false.
public bool MenuItem
{
get => menuMode && hasId;
}
+ ///
+ /// The game's name.
+ ///
+ /// The name.
public string Name
{
get
@@ -123,6 +180,10 @@ namespace GamecraftModdingAPI.App
}
}
+ ///
+ /// The game's description.
+ ///
+ /// The description.
public string Description
{
get
@@ -146,6 +207,10 @@ namespace GamecraftModdingAPI.App
}
}
+ ///
+ /// The path to the game's save folder.
+ ///
+ /// The path.
public string Path
{
get
@@ -170,6 +235,11 @@ namespace GamecraftModdingAPI.App
}
}
+ ///
+ /// The Steam Workshop Id of the game save.
+ /// In most cases this is invalid and returns 0, so this can be ignored.
+ ///
+ /// The workshop identifier.
public ulong WorkshopId
{
get
@@ -195,6 +265,10 @@ namespace GamecraftModdingAPI.App
}
}
+ ///
+ /// Whether the game is in simulation mode.
+ ///
+ /// true if is simulating; otherwise, false.
public bool IsSimulating
{
get
@@ -211,6 +285,11 @@ namespace GamecraftModdingAPI.App
}
}
+ ///
+ /// Whether the game is in time-running mode.
+ /// Alias of IsSimulating.
+ ///
+ /// true if is time running; otherwise, false.
public bool IsTimeRunning
{
get => IsSimulating;
@@ -221,6 +300,10 @@ namespace GamecraftModdingAPI.App
}
}
+ ///
+ /// Whether the game is in time-stopped mode.
+ ///
+ /// true if is time stopped; otherwise, false.
public bool IsTimeStopped
{
get
@@ -237,6 +320,9 @@ namespace GamecraftModdingAPI.App
}
}
+ ///
+ /// Toggles the time mode.
+ ///
public void ToggleTimeMode()
{
if (!VerifyMode()) return;
@@ -247,6 +333,11 @@ namespace GamecraftModdingAPI.App
gameEngine.ToggleTimeMode();
}
+ ///
+ /// 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.
+ ///
public void EnterGame()
{
if (!VerifyMode()) return;
@@ -258,17 +349,43 @@ namespace GamecraftModdingAPI.App
Scheduler.Schedule(task);
}
- public void ExitGame()
+ ///
+ /// 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.
+ ///
+ /// If set to true, do this async.
+ public void ExitGame(bool async = false)
{
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);
+ gameEngine.ExitCurrentGame(async);
+ this.menuMode = true;
}
+ ///
+ /// Saves the game.
+ /// Part of this happens asynchronously, so when this method returns the game has not been saved yet.
+ ///
+ public void SaveGame()
+ {
+ if (!VerifyMode()) return;
+ if (menuMode)
+ {
+ throw new GameNotFoundException("Cannot save game using menu ID");
+ }
+ gameEngine.SaveCurrentGame();
+ }
+
+ ///
+ /// Add information to the in-game debug display.
+ /// When this object is garbage collected, this debug info is automatically removed.
+ ///
+ /// Debug info identifier.
+ /// Content getter.
public void AddDebugInfo(string id, Func contentGetter)
{
if (!VerifyMode()) return;
@@ -280,6 +397,11 @@ namespace GamecraftModdingAPI.App
debugIds.Add(id);
}
+ ///
+ /// Remove information from the in-game debug display.
+ ///
+ /// true, if debug info was removed, false otherwise.
+ /// Debug info identifier.
public bool RemoveDebugInfo(string id)
{
if (!VerifyMode()) return false;
diff --git a/GamecraftModdingAPI/App/GameGameEngine.cs b/GamecraftModdingAPI/App/GameGameEngine.cs
index 9253af2..d46eb32 100644
--- a/GamecraftModdingAPI/App/GameGameEngine.cs
+++ b/GamecraftModdingAPI/App/GameGameEngine.cs
@@ -47,9 +47,18 @@ namespace GamecraftModdingAPI.App
private set;
} = false;
- public void ExitCurrentGame()
+ public void ExitCurrentGame(bool async = false)
{
- ExitCurrentGameAsync().RunOn(Lean.EveryFrameStepRunner_RUNS_IN_TIME_STOPPED_AND_RUNNING);
+ if (async)
+ {
+ ExitCurrentGameAsync().RunOn(Lean.EveryFrameStepRunner_RUNS_IN_TIME_STOPPED_AND_RUNNING);
+ }
+ else
+ {
+ entitiesDB.QueryEntity(CommonExclusiveGroups.GameSceneEGID).WantsToQuit = true;
+ entitiesDB.PublishEntityChange(CommonExclusiveGroups.GameSceneEGID);
+ }
+
}
public IEnumerator ExitCurrentGameAsync()
@@ -62,6 +71,14 @@ namespace GamecraftModdingAPI.App
entitiesDB.PublishEntityChange(CommonExclusiveGroups.GameSceneEGID);
}
+ public void SaveCurrentGame()
+ {
+ ref GameSceneEntityStruct gses = ref entitiesDB.QueryEntity(CommonExclusiveGroups.GameSceneEGID);
+ gses.LoadAfterSaving = false;
+ gses.SaveNow = true;
+ entitiesDB.PublishEntityChange(CommonExclusiveGroups.GameSceneEGID);
+ }
+
public bool IsTimeRunningMode()
{
return TimeRunningModeUtil.IsTimeRunningMode(entitiesDB);
diff --git a/GamecraftModdingAPI/Player.cs b/GamecraftModdingAPI/Player.cs
index efb381e..3d80e7b 100644
--- a/GamecraftModdingAPI/Player.cs
+++ b/GamecraftModdingAPI/Player.cs
@@ -45,6 +45,15 @@ namespace GamecraftModdingAPI
return playerEngine.ExistsById(player);
}
+ ///
+ /// The amount of Players in the current game.
+ ///
+ /// The count.
+ public static uint Count()
+ {
+ return playerEngine.GetAllPlayerCount();
+ }
+
///
/// Initializes a new instance of the class.
///
diff --git a/GamecraftModdingAPI/Players/PlayerEngine.cs b/GamecraftModdingAPI/Players/PlayerEngine.cs
index 2bb29a7..1bcce50 100644
--- a/GamecraftModdingAPI/Players/PlayerEngine.cs
+++ b/GamecraftModdingAPI/Players/PlayerEngine.cs
@@ -65,6 +65,26 @@ namespace GamecraftModdingAPI.Players
return uint.MaxValue;
}
+ public uint GetAllPlayerCount()
+ {
+ uint count = 0;
+ foreach (ExclusiveGroupStruct eg in PlayersExclusiveGroups.AllPlayers)
+ {
+ count += entitiesDB.Count(eg);
+ }
+ return count;
+ }
+
+ public uint GetLocalPlayerCount()
+ {
+ return entitiesDB.Count(PlayersExclusiveGroups.LocalPlayers);
+ }
+
+ public uint GetRemotePlayerCount()
+ {
+ return entitiesDB.Count(PlayersExclusiveGroups.RemotePlayers);
+ }
+
public bool ExistsById(uint playerId)
{
return entitiesDB.Exists(playerId, PlayersExclusiveGroups.LocalPlayers)
@@ -109,7 +129,7 @@ namespace GamecraftModdingAPI.Players
{
return ((Quaternion) rbes.rotation).eulerAngles;
}
- return default;
+ return default(float3);
}
public bool SetRotation(uint playerId, float3 value)
@@ -174,7 +194,7 @@ namespace GamecraftModdingAPI.Players
{
return rbes.physicsMass;
}
- return default;
+ return default(PhysicsMass);
}
public bool SetInverseMass(uint playerId, float inverseMass)
diff --git a/GamecraftModdingAPI/Tests/APITestAttributes.cs b/GamecraftModdingAPI/Tests/APITestAttributes.cs
index 8506a56..cf9da49 100644
--- a/GamecraftModdingAPI/Tests/APITestAttributes.cs
+++ b/GamecraftModdingAPI/Tests/APITestAttributes.cs
@@ -1,6 +1,10 @@
using System;
namespace GamecraftModdingAPI.Tests
{
+ ///
+ /// Test type.
+ /// When provided to APITestCaseAttribute, this dictates when the test case is called.
+ ///
public enum TestType
{
Menu,
@@ -9,6 +13,10 @@ namespace GamecraftModdingAPI.Tests
EditMode,
}
+ ///
+ /// API Test Class attribute.
+ /// Classes without this attribute will be ignored when searching for test cases.
+ ///
[AttributeUsage(AttributeTargets.Class)]
public class APITestClassAttribute : Attribute
{
@@ -20,6 +28,10 @@ namespace GamecraftModdingAPI.Tests
}
}
+ ///
+ /// API Test Case attribute.
+ /// Static methods with this attribute will be called when the API test system is running.
+ ///
[AttributeUsage(AttributeTargets.Method)]
public class APITestCaseAttribute : Attribute
{
@@ -31,12 +43,20 @@ namespace GamecraftModdingAPI.Tests
}
}
+ ///
+ /// API Test StartUp attribute.
+ /// Static methods with this attribute will be called before any test case is run by the API test system.
+ ///
[AttributeUsage(AttributeTargets.Method)]
public class APITestStartUpAttribute : Attribute
{
}
+ ///
+ /// API Test TearDown attribute.
+ /// Static methods with this attribute will be called after all API test system test cases have completed (failed or succeeded).
+ ///
[AttributeUsage(AttributeTargets.Method)]
public class APITestTearDownAttribute : Attribute
{
diff --git a/GamecraftModdingAPI/Tests/Assert.cs b/GamecraftModdingAPI/Tests/Assert.cs
index fb03013..d888f45 100644
--- a/GamecraftModdingAPI/Tests/Assert.cs
+++ b/GamecraftModdingAPI/Tests/Assert.cs
@@ -5,6 +5,9 @@ using System.Runtime.CompilerServices;
namespace GamecraftModdingAPI.Tests
{
+ ///
+ /// API test system assertion utilities.
+ ///
public static class Assert
{
private static StreamWriter logFile = null;
@@ -19,6 +22,11 @@ namespace GamecraftModdingAPI.Tests
private const string INFO = "DEBUG: ";
+ ///
+ /// Log a message to the test log.
+ ///
+ /// Message.
+ /// Message ending.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Log(string msg, string end = "\n")
{
@@ -27,6 +35,16 @@ namespace GamecraftModdingAPI.Tests
logFile.Flush();
}
+ ///
+ /// Asserts that the event receives a callback... eventually.
+ /// Add the eventhandler returned by this method to the relevant event.
+ /// This does not assert that the callback happens under that event's intended circumstances.
+ /// Add another event handler to assert specific circumstance requirements.
+ ///
+ /// The callback event handler.
+ /// Event name.
+ /// Event error message.
+ /// The event handler callback argument object.
public static EventHandler CallsBack(string eventName, string eventMsg = null)
{
if (eventMsg == null) eventMsg = $"expected callback to {eventName} but it never occurred...";
diff --git a/GamecraftModdingAPI/Tests/TestRoot.cs b/GamecraftModdingAPI/Tests/TestRoot.cs
index 167173f..99f77b7 100644
--- a/GamecraftModdingAPI/Tests/TestRoot.cs
+++ b/GamecraftModdingAPI/Tests/TestRoot.cs
@@ -15,6 +15,9 @@ using GamecraftModdingAPI.Utility;
namespace GamecraftModdingAPI.Tests
{
+ ///
+ /// API test system root class.
+ ///
public static class TestRoot
{
public static bool AutoShutdown = true;
@@ -229,6 +232,10 @@ namespace GamecraftModdingAPI.Tests
}
}
+ ///
+ /// Runs the tests.
+ ///
+ /// Assembly to search for tests. When set to null, this uses the GamecraftModdingAPI assembly.
public static void RunTests(Assembly asm = null)
{
if (asm == null) asm = Assembly.GetExecutingAssembly();