ST_engine  0.3-ALPHA
game_manager.cpp
1 /* This file is part of the "ST" project.
2  * You may use, distribute or modify this code under the terms
3  * of the GNU General Public License version 2.
4  * See LICENCE.txt in the root directory of the project.
5  *
6  * Author: Maxim Atanasov
7  * E-mail: maxim.atanasov@protonmail.com
8  */
9 
10 #include <game_manager/game_manager.hpp>
11 #include <algorithm>
12 #include <SDL_events.h>
13 #include <fstream>
14 
15 
16 static bool singleton_initialized = false;
17 
23 game_manager::game_manager(message_bus &gMessageBus) : gMessage_bus(gMessageBus) {
24 
25  if (singleton_initialized) {
26  throw std::runtime_error("The game manager cannot be initialized more than once!");
27  } else {
28  singleton_initialized = true;
29  }
30 
31  //subscribe to messages
32  gMessage_bus.subscribe(LOAD_LEVEL, &msg_sub);
33  gMessage_bus.subscribe(START_LEVEL, &msg_sub);
34  gMessage_bus.subscribe(UNLOAD_LEVEL, &msg_sub);
35  gMessage_bus.subscribe(RELOAD_LEVEL, &msg_sub);
36  gMessage_bus.subscribe(KEY_PRESSED, &msg_sub);
37  gMessage_bus.subscribe(KEY_HELD, &msg_sub);
38  gMessage_bus.subscribe(KEY_RELEASED, &msg_sub);
39  gMessage_bus.subscribe(MOUSE_X, &msg_sub);
40  gMessage_bus.subscribe(MOUSE_Y, &msg_sub);
41  gMessage_bus.subscribe(LEFT_TRIGGER, &msg_sub);
42  gMessage_bus.subscribe(RIGHT_TRIGGER, &msg_sub);
43  gMessage_bus.subscribe(LEFT_STICK_HORIZONTAL, &msg_sub);
44  gMessage_bus.subscribe(LEFT_STICK_VERTICAL, &msg_sub);
45  gMessage_bus.subscribe(RIGHT_STICK_HORIZONTAL, &msg_sub);
46  gMessage_bus.subscribe(RIGHT_STICK_VERTICAL, &msg_sub);
47  gMessage_bus.subscribe(VSYNC_STATE, &msg_sub);
48  gMessage_bus.subscribe(END_GAME, &msg_sub);
49  gMessage_bus.subscribe(MUSIC_VOLUME_LEVEL, &msg_sub);
50  gMessage_bus.subscribe(SOUNDS_VOLUME_LEVEL, &msg_sub);
51  gMessage_bus.subscribe(SHOW_MOUSE, &msg_sub);
52  gMessage_bus.subscribe(EXECUTE_SCRIPT, &msg_sub);
53  gMessage_bus.subscribe(FULLSCREEN_STATUS, &msg_sub);
54  gMessage_bus.subscribe(AUDIO_ENABLED, &msg_sub);
55  gMessage_bus.subscribe(VIRTUAL_SCREEN_COORDINATES, &msg_sub);
56  gMessage_bus.subscribe(REAL_SCREEN_COORDINATES, &msg_sub);
57 
58  //initialize initial states of keys
59  reset_keys();
60 
61  //Initialize the Lua environment
62  gScript_backend.initialize(&gMessage_bus, this);
63 
64  game_is_running_ = true;
65 }
66 
70 void game_manager::reset_keys() {
71  keys_pressed_data.reset();
72  keys_held_data.reset();
73  keys_released_data.reset();
74 }
75 
79 void game_manager::handle_messages() {
80  auto temp = msg_sub.get_next_message();
81  while (temp != nullptr) {
82  switch (temp->msg_name) {
83  case LOAD_LEVEL:
84  load_level(*static_cast<std::string *>(temp->get_data()));
85  break;
86  case RELOAD_LEVEL:
87  reload_level(*static_cast<std::string *>(temp->get_data()));
88  break;
89  case START_LEVEL:
90  start_level(*static_cast<std::string *>(temp->get_data()));
91  break;
92  case UNLOAD_LEVEL:
93  unload_level(*static_cast<std::string *>(temp->get_data()));
94  break;
95  case KEY_PRESSED: {
96  uint8_t key_index = temp->base_data0;
97  keys_pressed_data[key_index] = true;
98  keys_held_data[key_index] = false;
99  keys_released_data[key_index] = false;
100  break;
101  }
102  case KEY_HELD: {
103  uint8_t key_index = temp->base_data0;
104  keys_pressed_data[key_index] = false;
105  keys_held_data[key_index] = true;
106  keys_released_data[key_index] = false;
107  break;
108  }
109  case KEY_RELEASED: {
110  uint8_t key_index = temp->base_data0;
111  keys_pressed_data[key_index] = false;
112  keys_held_data[key_index] = false;
113  keys_released_data[key_index] = true;
114  break;
115  }
116  case MOUSE_X:
117  mouse_x = static_cast<int32_t>(temp->base_data0);
118  break;
119  case MOUSE_Y:
120  mouse_y = static_cast<int32_t>(temp->base_data0);
121  break;
122  case LEFT_TRIGGER:
123  left_trigger = static_cast<int16_t>(temp->base_data0);
124  break;
125  case RIGHT_TRIGGER:
126  right_trigger = static_cast<int16_t>(temp->base_data0);
127  break;
128  case LEFT_STICK_VERTICAL:
129  left_stick_vertical = static_cast<int16_t>(temp->base_data0);
130  break;
131  case LEFT_STICK_HORIZONTAL:
132  left_stick_horizontal = static_cast<int16_t>(temp->base_data0);
133  break;
134  case RIGHT_STICK_VERTICAL:
135  right_stick_vertical = static_cast<int16_t>(temp->base_data0);
136  break;
137  case RIGHT_STICK_HORIZONTAL:
138  right_stick_horizontal = static_cast<int16_t>(temp->base_data0);
139  break;
140  case MUSIC_VOLUME_LEVEL:
141  music_volume_level = static_cast<uint8_t>(temp->base_data0);
142  break;
143  case SOUNDS_VOLUME_LEVEL:
144  sounds_volume_level = static_cast<uint8_t>(temp->base_data0);
145  break;
146  case AUDIO_ENABLED:
147  audio_enabled = static_cast<bool>(temp->base_data0);
148  break;
149  case VSYNC_STATE:
150  vsync_flag = static_cast<bool>(temp->base_data0);
151  break;
152  case END_GAME:
153  game_is_running_ = false;
154  break;
155  case SHOW_MOUSE:
156  SDL_ShowCursor(static_cast<bool>(temp->base_data0));
157  break;
158  case EXECUTE_SCRIPT: {
159  auto script = static_cast<std::string *>(temp->get_data());
160  gScript_backend.run_script(*script);
161  break;
162  }
163  case FULLSCREEN_STATUS:
164  fullscreen_status = static_cast<bool>(temp->base_data0);
165  break;
166  case VIRTUAL_SCREEN_COORDINATES: {
167  auto data = temp->base_data0;
168  v_width = data & 0x0000ffffU;
169  v_height = (data >> 16U) & 0x0000ffffU;
170  break;
171  }
172  case REAL_SCREEN_COORDINATES: {
173  auto data = temp->base_data0;
174  w_width = data & 0x0000ffffU;
175  w_height = (data >> 16U) & 0x0000ffffU;
176  break;
177  }
178  }
179  delete temp;
180  temp = msg_sub.get_next_message();
181  }
182 }
183 
188 int8_t game_manager::load_level(const std::string &level_name) {
189 
190  //check if it is already loaded.
191  for (auto &i: levels) {
192  if (i.get_name() == level_name) {
193  return 0;
194  }
195  }
196 
197  //otherwise - create it
198  auto temp = ST::level(level_name, &gMessage_bus);
199  if (temp.load() != 0) {
200  gMessage_bus.send_msg(new message(LOG_ERROR, make_data<std::string>(
201  "Level with name " + level_name + " could not be loaded!")));
202  return -1;
203  }
204  levels.emplace_back(temp);
205 
206  //current level pointer must be reset, because apparently adding to the vector changes the pointer address
207  //(makes sense as it has to reallocate the whole thing)
208  for (auto &level: levels) {
209  if (level.get_name() == active_level) {
210  current_level_pointer = &level;
211  break;
212  }
213  }
214  return 0;
215 }
216 
221 void game_manager::unload_level(const std::string &level_name) {
222  for (uint64_t i = 0; i < levels.size(); ++i) {
223  if (levels[i].get_name() == level_name) {
224  levels[i].unload();
225  levels.erase(levels.begin() + i);
226  break;
227  }
228  }
229 
230  //current level pointer must be reset, because apparently adding/removing to/from the vector changes the pointer address
231  //(makes sense as it has to reallocate the whole thing)
232  for (auto &level: levels) {
233  if (level.get_name() == active_level) {
234  current_level_pointer = &level;
235  break;
236  }
237  }
238 }
239 
240 
245 void game_manager::reload_level(const std::string &level_name) {
246  for (auto &level: levels) {
247  if (level.get_name() == level_name) {
248  level.reload();
249  break;
250  }
251  }
252 }
253 
254 
259 void game_manager::start_level(const std::string &level_name) {
260 
261  //set the current level pointer
262  bool is_loaded = false;
263  for (auto &level: levels) {
264  if (level.get_name() == level_name) {
265  current_level_pointer = &level;
266  is_loaded = true;
267  break;
268  }
269  }
270  //if the level wasn't loaded in advance, load it now
271  if (!is_loaded) {
272  if (load_level(level_name) == 0) {
273  for (auto &level: levels) {
274  if (level.get_name() == level_name) {
275  current_level_pointer = &level;
276  break;
277  }
278  }
279  } else {
280  gMessage_bus.send_msg(new message(LOG_ERROR, make_data<std::string>("Error starting level " + level_name)));
281  return;
282  }
283  }
284 
285  gScript_backend.close();
286  gScript_backend.initialize(&gMessage_bus, this);
287  active_level = level_name;
288 
289  current_level_pointer->lights.clear();
290  current_level_pointer->entities.clear();
291  current_level_pointer->text_objects.clear();
292 
293  //construct level
294  current_level_pointer->camera.x = 0;
295  current_level_pointer->camera.y = 0;
296  current_level_pointer->camera.limitX2 = v_width;
297  current_level_pointer->camera.limitY2 = v_height;
298 
299  std::string temp = "game/levels/";
300  temp = temp + active_level;
301  temp = temp + "/level.lua";
302  gScript_backend.run_file(temp);
303 }
304 
309  gScript_backend.close();
310  singleton_initialized = false;
311 }
312 
313 
318  return left_trigger;
319 }
320 
325  return right_trigger;
326 }
327 
332  return left_stick_horizontal;
333 }
334 
339  return left_stick_vertical;
340 }
341 
346  return right_stick_vertical;
347 }
348 
353  return right_stick_horizontal;
354 }
355 
361  //TODO: Get rid of hardcoded values
362  //TODO: Move this functionality to lua
363  //TODO: Refactor
364  if (current_level_pointer->entities[id].velocity_x > 0) {
365  if (current_level_pointer->camera.x <
366  current_level_pointer->entities[id].x - v_width / 4 - current_level_pointer->entities[id].velocity_x) {
367  current_level_pointer->camera.x += current_level_pointer->entities[id].velocity_x + 5;
368  } else {
369  current_level_pointer->camera.x = current_level_pointer->entities[id].x - v_width / 4;
370  }
371  } else if (current_level_pointer->entities[id].velocity_x < 0) {
372  if (current_level_pointer->camera.x >
373  current_level_pointer->entities[id].x - v_width / 2 - current_level_pointer->entities[id].velocity_x) {
374  current_level_pointer->camera.x += current_level_pointer->entities[id].velocity_x - 5;
375  } else {
376  current_level_pointer->camera.x = current_level_pointer->entities[id].x - v_width / 2;
377  }
378  }
379  //TODO: Test different camera set-ups
380  //current_level_pointer->camera.x = current_level_pointer->entities[id].x - v_width / 4;
381 
382  current_level_pointer->camera.y = current_level_pointer->entities[id].y - v_height;
383 
384  uint8_t limit_x1_check = current_level_pointer->camera.x < current_level_pointer->camera.limitX1 + 1;
385  uint8_t limit_x2_check = current_level_pointer->camera.x > current_level_pointer->camera.limitX2 - 1;
386  uint8_t limit_y1_check = current_level_pointer->camera.y < current_level_pointer->camera.limitY1 + 1;
387  uint8_t limit_y2_check = current_level_pointer->camera.y > current_level_pointer->camera.limitY2 - 1;
388 
389  current_level_pointer->camera.x =
390  limit_x1_check * (current_level_pointer->camera.limitX1 + 1) +
391  !limit_x1_check * current_level_pointer->camera.x;
392  current_level_pointer->camera.x =
393  limit_x2_check * (current_level_pointer->camera.limitX2 - 1) +
394  !limit_x2_check * current_level_pointer->camera.x;
395  current_level_pointer->camera.y =
396  limit_y1_check * (current_level_pointer->camera.limitY1 + 1) +
397  !limit_y1_check * current_level_pointer->camera.y;
398  current_level_pointer->camera.y =
399  limit_y2_check * (current_level_pointer->camera.limitY2 - 1) +
400  !limit_y2_check * current_level_pointer->camera.y;
401 }
402 
407  handle_messages();
408  run_level_loop();
409 }
410 
415 std::string game_manager::get_active_level() const {
416  return active_level;
417 }
418 
422 void game_manager::run_level_loop() {
423  gScript_backend.run_global("loop");
424 }
425 
430  return current_level_pointer;
431 }
432 
439  return game_is_running_;
440 }
441 
446 int32_t game_manager::get_mouse_x() const {
447  return mouse_x;
448 }
449 
454 int32_t game_manager::get_mouse_y() const {
455  return mouse_y;
456 }
457 
463 bool game_manager::key_pressed(uint16_t arg) const {
464  for (ST::key key: current_level_pointer->actions_buttons[arg]) {
465  if (keys_pressed_data[static_cast<uint8_t>(key)]) {
466  return true;
467  }
468  }
469  return false;
470 }
471 
477 bool game_manager::key_held(uint16_t arg) const {
478  for (ST::key key: current_level_pointer->actions_buttons[arg]) {
479  if (keys_held_data[static_cast<uint8_t>(key)]) {
480  return true;
481  }
482  }
483  return false;
484 }
485 
491 bool game_manager::key_released(uint16_t arg) const {
492  for (ST::key key: current_level_pointer->actions_buttons[arg]) {
493  if (keys_released_data[static_cast<uint8_t>(key)]) {
494  return true;
495  }
496  }
497  return false;
498 }
499 
500 //TODO: Docs
501 //TODO: Test
502 void game_manager::save_state(const std::string &filepath) {
503  std::ofstream save_file;
504  save_file.open(filepath);
505 
506  save_file << "L" + current_level_pointer->get_name();
507  save_file << "CX" + std::to_string(current_level_pointer->camera.x);
508  save_file << "CY" + std::to_string(current_level_pointer->camera.y);
509 
510  for (uint8_t i = 0; i < PARALLAX_BG_LAYERS; i++) {
511  save_file << "B" + std::to_string(i) + std::to_string(current_level_pointer->background[i]);
512  }
513 
514  save_file << "BCR" + std::to_string(current_level_pointer->background_color.r) +
515  "G" + std::to_string(current_level_pointer->background_color.g) +
516  "B" + std::to_string(current_level_pointer->background_color.b) +
517  "A" + std::to_string(current_level_pointer->background_color.a);
518  save_file << "O" + std::to_string(current_level_pointer->overlay);
519  save_file << "OSN" + std::to_string(current_level_pointer->overlay_sprite_num);
520  save_file << "E";
521  for (const auto &entity: current_level_pointer->entities) {
522  save_file << "X" + std::to_string(entity.x);
523  save_file << "Y" + std::to_string(entity.y);
524  save_file << "VX" + std::to_string(entity.velocity_x);
525  save_file << "VY" + std::to_string(entity.velocity_y);
526  save_file << "A" + std::to_string(entity.animation);
527  save_file << "AN" + std::to_string(entity.animation_num);
528  save_file << "SN" + std::to_string(entity.sprite_num);
529  save_file << "TH" + std::to_string(entity.tex_h);
530  save_file << "TW" + std::to_string(entity.tex_w);
531  save_file << "TSX" + std::to_string(entity.tex_scale_x);
532  save_file << "TSY" + std::to_string(entity.tex_scale_y);
533  save_file << "T" + std::to_string(entity.texture);
534  save_file << "S" + std::to_string(entity.is_static());
535  save_file << "V" + std::to_string(entity.is_visible());
536  save_file << "P" + std::to_string(entity.is_affected_by_physics());
537  save_file << "A" + std::to_string(entity.is_active());
538  }
539  save_file << "E";
540  save_file << "L";
541  for (const auto &light: current_level_pointer->lights) {
542  save_file << "X" + std::to_string(light.origin_x);
543  save_file << "Y" + std::to_string(light.origin_y);
544  save_file << "S" + std::to_string(light.is_static);
545  save_file << "B" + std::to_string(light.brightness);
546  save_file << "I" + std::to_string(light.intensity);
547  save_file << "R" + std::to_string(light.radius);
548  }
549  save_file << "L";
550  save_file << "T";
551  for (const auto &text_object: current_level_pointer->text_objects) {
552  save_file << "X" + std::to_string(text_object.x);
553  save_file << "Y" + std::to_string(text_object.y);
554  save_file << "T" + text_object.text_string;
555  save_file << "V" + std::to_string(text_object.is_visible);
556  save_file << "F" + std::to_string(text_object.font);
557  save_file << "CR" + std::to_string(text_object.color.r) +
558  "G" + std::to_string(text_object.color.g) +
559  "B" + std::to_string(text_object.color.b) +
560  "A" + std::to_string(text_object.color.a);
561  }
562  save_file << "T";
563  save_file.close();
564 }
565 
570  return w_width;
571 }
572 
577  return w_height;
578 }
579 
584  return v_width;
585 }
586 
591  return v_height;
592 }
This object contains all the data for a level and provides functions for loading and unloading a leve...
Definition: level.hpp:29
std::string get_name() const
Definition: level.cpp:76
void reload()
Definition: level.cpp:50
bool key_released(uint16_t arg) const
uint16_t get_internal_width() const
void center_camera_on_entity(uint64_t id)
uint16_t get_window_height() const
int16_t get_left_stick_vertical() const
bool key_held(uint16_t arg) const
int16_t get_left_trigger() const
int32_t get_mouse_x() const
int16_t get_right_stick_vertical() const
int16_t get_left_stick_horizontal() const
ST::level * get_level() const
uint16_t get_window_width() const
bool game_is_running() const
uint16_t get_internal_height() const
int32_t get_mouse_y() const
bool key_pressed(uint16_t arg) const
int16_t get_right_trigger() const
game_manager(message_bus &msg_bus)
int16_t get_right_stick_horizontal() const
std::string get_active_level() const
int initialize(message_bus *msg_bus, game_manager *game_mngr)
Definition: lua_backend.cpp:41
void run_global(const std::string &arg)
int8_t run_file(const std::string &file)
int8_t run_script(const std::string &script)
The central messaging system of the engine. All subsystem make extensive use of it.
Definition: message_bus.hpp:29
void subscribe(uint8_t msg, subscriber *sub)
Definition: message_bus.cpp:71
void send_msg(message *msg)
Definition: message_bus.cpp:19
A message object passed around in the message bus. Holds anything created with make_data<>().
Definition: message.hpp:21
message * get_next_message()
Definition: subscriber.hpp:39