1 /// main YSGE module containing everything you need to make a game
2 module ysge.project;
3 
4 import std.file;
5 import std.path;
6 import std.string;
7 
8 public import bindbc.sdl;
9 public import ysge.util;
10 public import ysge.scene;
11 public import ysge.types;
12 public import ysge.uiBase;
13 public import ysge.texture;
14 public import ysge.animation;
15 public import ysge.gameObject;
16 public import ysge.objects.simpleBox;
17 
18 /// used when something goes wrong in the project
19 class ProjectException : Exception {
20 	this(string msg, string file = __FILE__, size_t line = __LINE__) {
21 		super(msg, file, line);
22 	}
23 }
24 
25 /// main project class used for the game as a whole
26 class Project {
27 	bool             running; /// while true, update functions are called
28 	SDL_Window*      window;
29 	SDL_Renderer*    renderer;
30 	TTF_Font*        font;
31 	Scene[]          scenes;
32 	Scene            currentScene;
33 	bool             usingLogicalRes; /// DON'T MODIFY!!!!
34 	Vec2!int         logicalRes; /// DON'T MODIFY!!!!
35 	Vec2!int         mousePos;
36 	ulong            frames; /// how many frames have passed since the game was started
37 
38 	this() {
39 		
40 	}
41 
42 	~this() {
43 		if (window) {
44 			SDL_DestroyWindow(window);
45 		}
46 		if (renderer) {
47 			SDL_DestroyRenderer(renderer);
48 		}
49 		SDL_Quit();
50 	}
51 
52 	/// called once at the start
53 	abstract void Init();
54 
55 	/// creates the window
56 	void InitWindow(string name, int w, int h, bool resizable) {
57 		SDLSupport support;
58 
59 		version (Windows) {
60 			support = loadSDL(cast(char*) (dirName(thisExePath()) ~ "/sdl2.dll"));
61 		}
62 		else {
63 			support = loadSDL();
64 		}
65 		
66 		if (support != sdlSupport) {
67 			throw new ProjectException("Failed to load SDL");
68 		}
69 
70 		int flags = 0;
71 
72 		if (resizable) {
73 			flags |= SDL_WINDOW_RESIZABLE;
74 		}
75 	
76 		window = SDL_CreateWindow(
77 			toStringz(name), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
78 			w, h, flags
79 		);
80 
81 		if (window is null) {
82 			throw new ProjectException("Failed to create window");
83 		}
84 
85 		renderer = SDL_CreateRenderer(
86 			window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC
87 		);
88 
89 		if (renderer is null) {
90 			throw new ProjectException("Failed to create renderer");
91 		}
92 	}
93 
94 	/// initialises the text library
95 	void InitLibs() {
96 		// SDL_TTF
97 		SDLTTFSupport supportTTF;
98 	
99 		version (Windows) {
100 			supportTTF = loadSDLTTF(
101 				cast(char*) (dirName(thisExePath()) ~ "/sdl2_ttf.dll")
102 			);
103 		}
104 		else {
105 			supportTTF = loadSDLTTF();
106 		}
107 	
108 		if (supportTTF < SDLTTFSupport.v2_0_12) {
109 			throw new ProjectException("Failed to load SDL_TTF library");
110 		}
111 
112 		if (TTF_Init() < 0) {
113 			throw new ProjectException("Failed to initialise SDL_TTF");
114 		}
115 
116 		// SDL_Image
117 		auto supportIMG = loadSDLImage();
118 
119 		if (supportIMG < SDLImageSupport.v2_0_0) {
120 			throw new ProjectException("Failed to load SDL_Image library");
121 		}
122 
123 		int imgFlags = IMG_INIT_PNG;
124 		if (IMG_Init(imgFlags) != imgFlags) {
125 			throw new ProjectException("Failed to initialise SDL_Image");
126 		}
127 	}
128 
129 	void LoadFontFile(string path, int pointSize) {
130 		font = TTF_OpenFont(toStringz(path), pointSize);
131 
132 		if (font is null) {
133 			throw new ProjectException("Failed to load font");
134 		}
135 	}
136 
137 	void LoadFontData(ubyte[] data) {
138 		auto rw = SDL_RWFromMem(data.ptr, cast(int) data.length);
139 		font = TTF_OpenFontRW(rw, 1, 16);
140 
141 		if (font is null) {
142 			throw new ProjectException("Failed to load font");
143 		}
144 	}
145 
146 	/// gets the resolution of the window
147 	Vec2!int GetResolution() {
148 		if (usingLogicalRes) {
149 			return logicalRes;
150 		}
151 		else {
152 			Vec2!int ret;
153 
154 			SDL_GetWindowSize(window, &ret.x, &ret.y);
155 
156 			return ret;
157 		}
158 	}
159 
160 	/// sets the logical resolution of the window
161 	void SetResolution(uint w, uint h) {
162 		SDL_RenderSetLogicalSize(renderer, w, h);
163 		usingLogicalRes = true;
164 		logicalRes      = Vec2!int(w, h);
165 	}
166 
167 	/// sets window size
168 	void SetWindowSize(uint w, uint h) {
169 		SDL_SetWindowSize(window, cast(int) w, cast(int) h);
170 	}
171 	
172 	/// gets the directory the game executable is in
173 	string GetGameDirectory() {
174 		return dirName(thisExePath());
175 	}
176 
177 	/// checks if a key is pressed
178 	bool KeyPressed(SDL_Scancode key) {
179 		auto keys = SDL_GetKeyboardState(null);
180 
181 		return keys[key]? true : false;
182 	}
183 
184 	/// adds a scene to the project scene array
185 	void AddScene(Scene scene) {
186 		scenes ~= scene;
187 	}
188 
189 	/// sets the current scene to a scene from the project scene array
190 	void SetScene(Scene scene) {
191 		currentScene = scene;
192 		currentScene.Setup();
193 		currentScene.Init(this);
194 	}
195 
196 	/// sets the current scene to a scene from the project scene array
197 	void SetScene(size_t index) {
198 		currentScene = scenes[index];
199 		currentScene.Setup();
200 		currentScene.Init(this);
201 	}
202 
203 	/// loads a texture from a file
204 	Texture LoadTextureFromFile(string fileName) {
205 		auto surface = IMG_Load(cast(char*) fileName.toStringz());
206 
207 		if (surface is null) {
208 			throw new ProjectException("Failed to load texture");
209 		}
210 
211 		auto texture = SDL_CreateTextureFromSurface(renderer, surface);
212 
213 		if (texture is null) {
214 			throw new ProjectException("Failed to load texture");
215 		}
216 
217 		return new Texture(texture);
218 	}
219 
220 	/// loads a texture from raw file data
221 	Texture LoadTextureFromData(ref ubyte[] data) {
222 		auto rw      = SDL_RWFromMem(data.ptr, cast(int) data.length);
223 		auto surface = IMG_Load_RW(rw, 1);
224 		
225 		if (surface is null) {
226 			throw new ProjectException("Failed to load texture");
227 		}
228 
229 		auto texture = SDL_CreateTextureFromSurface(renderer, surface);
230 
231 		if (texture is null) {
232 			throw new ProjectException("Failed to load texture");
233 		}
234 
235 		return new Texture(texture);
236 	}
237 
238 	/// runs the game
239 	void Run() {
240 		running = true;
241 		Init();
242 
243 		if (currentScene is null) {
244 			throw new ProjectException("Scene not set");
245 		}
246 
247 		if (font is null) {
248 			throw new ProjectException("Font not loaded");
249 		}
250 
251 		while (running) {
252 			++ frames;
253 
254 			currentScene.animations.Update(this, currentScene);
255 			currentScene.UpdateObjects(this);
256 			currentScene.Update(this);
257 			currentScene.UpdateCamera(this);
258 			currentScene.Render(this);
259 
260 			SDL_Event e;
261 			while (SDL_PollEvent(&e)) {
262 				switch (e.type) {
263 					case SDL_QUIT: {
264 						running = false;
265 						return;
266 					}
267 					case SDL_MOUSEMOTION: {
268 						mousePos = Vec2!int(e.motion.x, e.motion.y);
269 						break;
270 					}
271 					default: {
272 						if (currentScene.HandleUIEvent(this, e)) {
273 							continue;
274 						}
275 					
276 						currentScene.HandleEvent(this, e);
277 						break;
278 					}
279 				}
280 			}
281 		}
282 	}
283 }