ST_engine  0.3-ALPHA
physics_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 #include <physics_manager/physics_manager.hpp>
10 #include "main/timer.hpp"
11 
12 static bool singleton_initialized = false;
13 
19 physics_manager::physics_manager(message_bus &gMessageBus) : gMessage_bus(gMessageBus) {
20  if (singleton_initialized) {
21  throw std::runtime_error("The phsyics manager cannot be initialized more than once!");
22  } else {
23  singleton_initialized = true;
24  }
25  gMessage_bus.subscribe(SET_GRAVITY, &msg_sub);
26  gMessage_bus.subscribe(SET_FRICTION, &msg_sub);
27  gMessage_bus.subscribe(SET_FLOOR, &msg_sub);
28  gMessage_bus.subscribe(PAUSE_PHYSICS, &msg_sub);
29  gMessage_bus.subscribe(UNPAUSE_PHYSICS, &msg_sub);
30  gravity = 0;
31  friction = 4;
32  level_floor = 0;
33 }
34 
38 void physics_manager::process_horizontal(std::vector<ST::entity *> *entities, int8_t friction) {
39  for (uint64_t k = 0; k < entities->size(); ++k) {
40  auto &entity = entities->operator[](k);
41  //handle horizontal velocity
42  if (entity->velocity_x > 0) {
43  for (int j = 0; j < entity->velocity_x; ++j) {
44  //Branch-less check for whether x has been set.
45  entity->velocity_x = entity_set_x(entity->x + 1, k, entities) *
46  entity->velocity_x; // NOLINT(cppcoreguidelines-narrowing-conversions)
47  }
48  for (int j = 0; j < friction && entity->velocity_x > 0; ++j) {
49  entity->velocity_x = static_cast<int8_t>(entity->velocity_x - 1);
50  }
51  } else if (entity->velocity_x < 0) {
52  for (int j = 0; j > entity->velocity_x; --j) {
53  //Branch-less check for whether x has been set.
54  entity->velocity_x = entity_set_x(entity->x - 1, k, entities) *
55  entity->velocity_x; // NOLINT(cppcoreguidelines-narrowing-conversions)
56  }
57  for (int j = 0; j < friction && entity->velocity_x < 0; ++j) {
58  entity->velocity_x = static_cast<int8_t>(entity->velocity_x + 1);
59  }
60  }
61  }
62 }
63 
64 physics_manager::~physics_manager() {
65  singleton_initialized = false;
66 }
67 
71 void physics_manager::process_vertical(std::vector<ST::entity *> *entities, int8_t gravity, int32_t level_floor) {
72  for (uint64_t k = 0; k < entities->size(); ++k) {
73  auto &entity = entities->operator[](k);
74  //handle vertical velocity
75  const int8_t objectVelocity = entity->velocity_y + gravity;
76  for (int j = 0; j > objectVelocity && entity_set_y(entity->y - 1, k, entities) != 0; --j);
77  for (int j = 0; j < objectVelocity; ++j) {
78  if (entity->y + entity->get_col_y_offset() < level_floor) {
79  if (entity_set_y(entity->y + 1, k, entities) == 0) {
80  break;
81  }
82  }
83  }
84  //decrease velocity of objects (apply gravity)
85  int8_t realVelocity = objectVelocity - gravity;
86  entity->velocity_y =
87  (realVelocity < 0) * static_cast<int8_t>(realVelocity + 2) + (realVelocity >= 0) * entity->velocity_y;
88  }
89 }
90 
95 void physics_manager::handle_messages() {
96  message *temp = msg_sub.get_next_message();
97  while (temp != nullptr) {
98  switch (temp->msg_name) {
99  case SET_GRAVITY:
100  gravity = static_cast<int8_t>(temp->base_data0);
101  break;
102  case SET_FLOOR:
103  level_floor = static_cast<int32_t>(temp->base_data0);
104  break;
105  case SET_FRICTION:
106  friction = static_cast<int8_t>(temp->base_data0);
107  break;
108  case PAUSE_PHYSICS:
109  physics_paused = true;
110  break;
111  case UNPAUSE_PHYSICS:
112  physics_paused = false;
113  break;
114  }
115  delete temp;
116  temp = msg_sub.get_next_message();
117  }
118 }
119 
127 uint8_t physics_manager::entity_set_x(int32_t X, uint64_t ID, std::vector<ST::entity *> *entities) {
128  ST::entity *entity = entities->operator[](ID);
129  int32_t old_x = entity->x;
130  entity->x = X;
131  uint8_t collision = check_collision(ID, entities);
132  entity->x = collision * old_x + !collision * entity->x; //if there is a collision, don't modify x
133  return !collision;
134 }
135 
143 uint8_t physics_manager::entity_set_y(int32_t Y, uint64_t ID, std::vector<ST::entity *> *entities) {
144  ST::entity *entity = entities->operator[](ID);
145  int32_t old_y = entity->y;
146  entity->y = Y;
147  uint8_t collision = check_collision(ID, entities);
148  entity->y = collision * old_y + !collision * entity->y; //if there is a collision, don't modify y
149  return !collision;
150 }
151 
158 int physics_manager::check_collision(uint64_t ID, std::vector<ST::entity *> *entities) {
159  uint8_t result = 0;
160  for (size_t i = 0; i < entities->size() && result == 0; i++) {
161  ST::entity *temp = entities->operator[](i);
162  result = temp->is_affected_by_physics() && i != ID && temp->collides(*entities->operator[](ID));
163  }
164  return result;
165 }
166 
172  handle_messages();
173  if (!physics_paused) [[likely]] {
174  if (entities_ref.size() != level.physics_objects_count) {
175  entities_ref.clear();
176  entities_ref.reserve(level.physics_objects_count);
177  // Filter entities with physics enabled
178  //TODO: This is all very slow
179  for (uint64_t i = 0; i < level.entities.size(); i++) {
180  if (level.entities.operator[](i).is_affected_by_physics()) {
181  entities_ref.emplace_back(&level.entities.operator[](i));
182  }
183  }
184  }
185  process_horizontal(&entities_ref, friction);
186  process_vertical(&entities_ref, gravity, level_floor);
187  }
188 }
This class represents all static or dynamic objects in the game (excluding text, see ST::text)
Definition: entity.hpp:24
bool collides(const entity &) const
Definition: entity.hpp:210
This object contains all the data for a level and provides functions for loading and unloading a leve...
Definition: level.hpp:29
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
A message object passed around in the message bus. Holds anything created with make_data<>().
Definition: message.hpp:21
void update(ST::level &level)
physics_manager(message_bus &gMessageBus)
message * get_next_message()
Definition: subscriber.hpp:39