-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathSSLGame.cs
More file actions
222 lines (190 loc) · 8.29 KB
/
SSLGame.cs
File metadata and controls
222 lines (190 loc) · 8.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
using Gum.DataTypes;
using Gum.Wireframe;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using MonoGameGum;
using SKSSL.ECS;
using SKSSL.Localization;
using SKSSL.Scenes;
using SKSSL.Utilities;
using static SKSSL.DustLogger;
// ReSharper disable NotAccessedField.Global
// ReSharper disable VirtualMemberCallInConstructor
// ReSharper disable NotAccessedField.Local
namespace SKSSL;
/// <summary>
/// Game Instances should inherit this class to have Gum and other systems automatically initialized.
/// <code>
/// override Initialize() {
/// base.Initialize(); // Naturally!
/// GameLoader.Register(...); // <- Registering Game Loaders
/// }
/// </code>
/// Registering Game factories, loaders and such, such as anything that inherits BaseRegistry or <see cref="Loc"/>,
/// is incredibly important as these are the loaders that will LOAD the game's content.
/// </summary>
public abstract class SSLGame : Game
{
/// Ultimate toggle to use ECS service. Enable this at project initialization.
/// To use, add the following to the game class inheriting SSLGame:
/// <code>
/// static MyGameClass() => UseECS = true;
/// </code>
internal static bool UseECS = false;
/// General context of the game dictated here.
public static SceneManager SceneManager = null!;
/// Static-instanced access for the Content Manager belonging to the active game instance.
public static ContentManager GameContent = null!;
/// <remarks>
/// In order to Spawn, Remove, or generally interact with entities in an ECS, a context is required. This context
/// varies between scenes.
/// //WARN: In the system's current limitations, it is difficult to interact with entities
/// across different scenes.
/// </remarks>
/// <returns>Scene Manager's Current World's Entity Context.</returns>
public static EntityContext? ECS()
{
if (!UseECS)
{
Log("Failed to get Entity Context because ECS is not enabled.", LOG.SYSTEM_ERROR);
return null;
}
if (SceneManager.CurrentWorld is not BaseWorld world)
{
Log("Failed to get Entity Context from current (null) world in Scene Manager!", LOG.SYSTEM_WARNING);
return null;
}
if (world.ECS is null)
{
Log("Failed to get Entity Context for a (null) ECS Controller!", LOG.SYSTEM_WARNING);
return null;
}
var entityContext = new EntityContext(world.ECS);
return entityContext;
}
internal readonly GraphicsDeviceManager _graphicsManager;
internal readonly SpriteBatch _spriteBatch;
private static GumService Gum => GumService.Default;
private readonly InteractiveGue _currentScreenGue = new();
/// Registries and services belonging to the game.
private readonly IServiceProvider GameServices;
/// <summary>
/// An array of Tuple paths assigned to an ID. These are loaded into the game's pather, and should
/// NEVER change. General examples include game texture and yaml prototypes folders.
/// </summary>
protected abstract (string id, string path)[] StaticPaths { get; }
/// <summary>
/// The Project Gum UI file that will dictate how UI is loaded.
/// <code>
/// Example: "Gum/SolKom.gumx"
/// </code>
/// </summary>
public static string GumFile = "CHANGE_ME";
/// <summary>
/// Constructor for SSLGame.
/// </summary>
/// <param name="title">Title of the game window.</param>
/// <param name="gumFile">Gum Interface File</param>
protected SSLGame(string title, string gumFile = "")
{
Title = title;
Content.RootDirectory = "Content";
Window.AllowUserResizing = true;
Window.ClientSizeChanged += HandleClientSizeChanged;
_graphicsManager = HandleGraphicsDesignManager(new GraphicsDeviceManager(this));
_spriteBatch = new SpriteBatch(GraphicsDevice);
_currentScreenGue.UpdateLayout(); // UI Behaviour when dragged
if (string.IsNullOrEmpty(gumFile))
Log($"Provided gum project file is empty! {title}, {nameof(SSLGame)}", 3);
else
GumFile = gumFile;
var services = new ServiceCollection();
LoadServices(services);
GameServices = services.BuildServiceProvider();
// Assign static-access content manager.
GameContent = Content;
// Initialize all static paths, which the developer must have defined!
// Includes load-order implementation. Higher values override lower values.
// TODO: Add a way to change load order priorities in game directories. Likely requires a file? Master file?
// A file per-game folder means version mismatches per file change that breaks every update.
// Ergo, a master file may be the best solution.
var gameDirectories = StaticGameLoader.GetAllGameDirectories();
GameContentDirectories = gameDirectories.OrderBy(d => d.LoadOrder).ToList();
// Display ECS status. This constructor is called after inheritors.
Log($"ECS status: {(UseECS ? "on" : "off")}");
if (UseECS)
{
// Initializing component registry before anything else.
Log("Initializing components.");
ComponentRegistry.Initialize();
}
// Load Static Game Content
Log("Initializing static paths.");
StaticGameLoader.Initialize(StaticPaths);
StaticGameLoader.Load(path => StaticGameLoader.GPath(path));
}
/// <summary>
/// Loads programmer-provided game services and registries.
/// </summary>
/// <param name="services"></param>
/// <code>services.AddSingleton<ExampleRegistry>();</code>
protected virtual void LoadServices(ServiceCollection services)
{
// Add game services to override method here.
}
/// Title of game window.
public string Title { get; set; }
/// <summary>
/// Accommodates for when the user readjusts the UI dimensions.
/// </summary>
private void HandleClientSizeChanged(object? _, EventArgs e)
{
GraphicalUiElement.CanvasWidth = _graphicsManager.GraphicsDevice.Viewport.Width;
GraphicalUiElement.CanvasHeight = _graphicsManager.GraphicsDevice.Viewport.Height;
}
private static GraphicsDeviceManager HandleGraphicsDesignManager(GraphicsDeviceManager graphicsDeviceManager)
{
var monitorWidth = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width;
var monitorHeight = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height;
graphicsDeviceManager.PreferredBackBufferWidth = monitorWidth; // Set preferred width
graphicsDeviceManager.PreferredBackBufferHeight = monitorHeight; // Set preferred height
graphicsDeviceManager.ApplyChanges();
return graphicsDeviceManager;
}
/// All content directories contained in the game folder. (E.g. game, mods ➡ etc.)
internal readonly IEnumerable<GameContentDirectory> GameContentDirectories;
/// <summary>
/// For custom <see cref="StaticGameLoader"/>s, you MUST initialize them before the base.Initialize() an inheritance
/// level above this class.
/// </summary>
protected override void Initialize()
{
// Initialize Gum UI Handling (Some projects may choose not to utilize Gum)
GumProjectSave? gumSave = null;
if (!string.IsNullOrEmpty(GumFile)) gumSave = Gum.Initialize(this, GumFile);
SceneManager = new SceneManager(_graphicsManager, _spriteBatch, gumSave);
// Continue
base.Initialize();
}
/// Quits the game.
public void Quit() => throw new NotImplementedException("Quit is not implemented, really. Let's crash, instead.");
/// Resets the game.
public void ResetGame() =>
throw new NotImplementedException("ResetGame is not implemented, really. Let's crash, instead.");
/// <inheritdoc />
protected override void Draw(GameTime gameTime)
{
SceneManager.Draw(gameTime);
Gum.Draw();
base.Draw(gameTime);
}
/// <inheritdoc />
protected override void Update(GameTime gameTime)
{
SceneManager.Update(gameTime);
Gum.Update(gameTime);
base.Update(gameTime);
}
}