enemy_system.cpp 11.5 KB
Newer Older
1 2
#include "enemy_system.hpp"

3
#include "beat_system.hpp"
4 5
#include "combat_system.hpp"

6
#include "movement_comp.hpp"
7
#include "player_comp.hpp"
Tim Scheiber's avatar
Tim Scheiber committed
8
#include "rigid_body_comp.hpp"
9

10
#include <mirrage/ecs/components/transform_comp.hpp>
11 12

namespace phase_shifter::gameplay {
Tim Scheiber's avatar
Tim Scheiber committed
13

14 15
	using namespace mirrage::ecs;

16 17 18 19
	Enemy_system::Enemy_system(mirrage::util::Message_bus& bus,
	                           Entity_manager&             entity_manager,
	                           const Beat_system&          beat_system)
	  : _bus(bus), _entity_manager(entity_manager), _beat_system(beat_system)
20 21
	{
		_entity_manager.register_component_type<Fixed_path_comp>();
22
		_entity_manager.register_component_type<Follow_target_comp>();
23
		_entity_manager.register_component_type<Continuous_path_comp>();
24
		_entity_manager.register_component_type<Shooting_comp>();
25
		_entity_manager.register_component_type<Target_comp>();
26
		_entity_manager.register_component_type<Bullet_comp>();
27 28 29 30
	}

	void Enemy_system::update(mirrage::util::Time dt)
	{
31 32
		auto beat = _beat_system.beat_state();

Tim Scheiber's avatar
Tim Scheiber committed
33
		despawn_distant_bullets();
34 35 36 37 38
		do_movement(beat);
		do_shooting(beat);
		do_bullet_hit_detection();
	}

Tim Scheiber's avatar
Tim Scheiber committed
39 40
	void Enemy_system::do_movement(Beat_state& beat)
	{
41 42 43 44 45
		do_fixed_path_movement(beat);
		do_cont_path_movement(beat);
		do_hunting_movement(beat);
	}

Tim Scheiber's avatar
Tim Scheiber committed
46 47
	void Enemy_system::do_fixed_path_movement(Beat_state& beat)
	{
48
		for(auto&& [movement, fixed_path] : _entity_manager.list<Movement_comp, Fixed_path_comp>()) {
49 50 51 52 53
			if(beat.beat) {
				if(fixed_path.wait_beats == 0 && !movement.move) {
					float direction = fixed_path.next_direction();
					if(direction < 360) {
						float rad_direction = direction * mirrage::util::PI / 180.f;
54 55 56
						movement.aim.x      = std::sin(rad_direction);
						movement.aim.y      = -std::cos(rad_direction);
						movement.move       = true;
57 58 59 60
					}
					fixed_path.wait_beats = fixed_path.pause_between_steps;
				} else {
					fixed_path.wait_beats--;
61 62 63
				}
			}
		}
64 65
	}

Tim Scheiber's avatar
Tim Scheiber committed
66 67
	void Enemy_system::do_cont_path_movement(Beat_state& beat)
	{
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
		for(auto&& [movement, cont_path] : _entity_manager.list<Movement_comp, Continuous_path_comp>()) {
			if(beat.beat) {
				if(cont_path.wait_beats == 0 && !movement.move) {
					float rad_direction  = cont_path.next_direction() * mirrage::util::PI / 180.f;
					movement.aim.x       = std::sin(rad_direction);
					movement.aim.y       = -std::cos(rad_direction);
					movement.move        = true;
					cont_path.wait_beats = cont_path.pause_between_steps;
				} else {
					cont_path.wait_beats--;
				}
			}
		}
	}

Tim Scheiber's avatar
Tim Scheiber committed
83 84
	void Enemy_system::do_hunting_movement(Beat_state& beat)
	{
85 86
		for(auto&& [movement, follow_target, transform] :
		    _entity_manager.list<Movement_comp, Follow_target_comp, components::Transform_comp>()) {
87 88 89
			if(beat.beat) {
				if(follow_target.wait_beats == 0 && !movement.move) {
					auto target_facet = _entity_manager.get(follow_target.target);
90

91
					if(target_facet.is_some()) {
92
						auto target_transform = target_facet.get_or_throw().get<components::Transform_comp>();
93

94 95 96 97 98 99 100 101 102 103 104
						if(target_transform.is_some()) {
							auto& target_position = target_transform.get_or_throw().position;
							auto& my_position     = transform.position;
							movement.aim          = glm::vec2(
                                    {target_position.x - my_position.x, target_position.z - my_position.z});
							movement.move = true;
						}
					}
					follow_target.wait_beats = follow_target.pause_between_steps;
				} else {
					follow_target.wait_beats--;
105 106 107
				}
			}
		}
108
	}
109

Tim Scheiber's avatar
Tim Scheiber committed
110 111
	void Enemy_system::do_shooting(Beat_state& beat)
	{
Tim Scheiber's avatar
Tim Scheiber committed
112 113
		for(auto&& [entity, shooting, transform] :
		    _entity_manager.list<Entity_facet, Shooting_comp, components::Transform_comp>()) {
Tim Scheiber's avatar
Tim Scheiber committed
114
			auto my_position = transform.position;
Tim Scheiber's avatar
Tim Scheiber committed
115
			bool shoot       = false;
Tim Scheiber's avatar
Tim Scheiber committed
116 117 118
			for(auto&& [player, player_transform] :
			    _entity_manager.list<Player_comp, components::Transform_comp>()) {
				if(glm::length(glm::vec2{player_transform.position.x, player_transform.position.z}
Tim Scheiber's avatar
Tim Scheiber committed
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
				               - glm::vec2{my_position.x, my_position.z})
				   <= trigger_distance_shooting) {
					shoot = true;
				}
			}
			glm::vec3 closest_target_pos(0.f);
			float     closest_dist = 999999999.f;

			for(auto&& [target, target_comp, target_transform] :
			    _entity_manager.list<Entity_facet, Target_comp, components::Transform_comp>()) {

				auto target_position = target_transform.position;
				auto dist            = glm::length(glm::vec2(target_position.x, target_position.z)
                                        - glm::vec2(my_position.x, my_position.z));
				if(dist < closest_dist) {
					closest_dist       = dist;
					closest_target_pos = target_position;
				}
			}
138

Tim Scheiber's avatar
Tim Scheiber committed
139 140 141 142 143 144
			glm::vec2 t_direction{closest_target_pos.x - my_position.x, closest_target_pos.z - my_position.z};
			shooting.target_direction =
			        std::acos(-t_direction.y / glm::length(t_direction)) * 180.f / mirrage::util::PI;
			if(t_direction.x < 0) {
				shooting.target_direction *= -1;
			}
145

Tim Scheiber's avatar
Tim Scheiber committed
146
			if(shooting.attack_radius >= 0 && closest_dist <= shooting.attack_radius) {
Tim Scheiber's avatar
Tim Scheiber committed
147 148 149 150
				shooting.idle = false;
			} else {
				shooting.idle = true;
			}
151

Tim Scheiber's avatar
Tim Scheiber committed
152 153 154 155 156
			auto orientation = shooting.target_direction;
			if(shooting.idle) {
				orientation = shooting.default_orientation + shooting.rotation;
			}
			auto rad_orientation = orientation * mirrage::util::PI / 180.f;
Tim Scheiber's avatar
Tim Scheiber committed
157

Tim Scheiber's avatar
Tim Scheiber committed
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
			if(beat.beat) {
				if(shooting.idle && shooting.rotate) {
					shooting.do_rotation();
					shooting.rotate = false;
				}
				if(shooting.wait_beats == 0) {
					auto bullet_pattern = shooting.next_pattern();
					auto spawn_position =
					        transform.position
					        + shooting.spawn_offset
					                  * glm::vec3(std::sin(rad_orientation), 0, -std::cos(rad_orientation))
					        + glm::vec3{0.f, 1.f, 0.f};

					if(shoot) {
						for(auto bullet : bullet_pattern.bullets) {
							auto bullet_direction     = orientation + bullet.direction;
							auto rad_bullet_direction = bullet_direction * mirrage::util::PI / 180.f;
							_entity_manager.entity_builder("bullet")
							        .position(spawn_position)
							        .rotation(glm::rotation({0, 0, 1},
							                                glm::vec3(std::sin(rad_bullet_direction),
							                                          0,
							                                          -std::cos(rad_bullet_direction))))
							        .post_create([=](auto entity) {
								        entity.process([=](Continuous_path_comp& cont_path) {
									        cont_path.direction = bullet_direction;
									        cont_path.curvature = bullet.curvature;
								        });
								        entity.process(
								                [=](Movement_comp& move, Continuous_path_comp& cont_path) {
									                float rad_direction =
									                        cont_path.direction * mirrage::util::PI / 180.f;
									                move.aim.x = std::sin(rad_direction);
									                move.aim.y = -std::cos(rad_direction);
								                });
							        })
							        .create();
Tim Scheiber's avatar
Tim Scheiber committed
195 196 197
						}
					}

Tim Scheiber's avatar
Tim Scheiber committed
198 199 200 201 202 203
					shooting.wait_beats = shooting.pause_between_shots;
					if(std::abs(shooting.rotation_per_step) > 0.0001f) {
						shooting.rotate = true;
					}
				} else {
					shooting.wait_beats--;
204 205
				}
			}
Tim Scheiber's avatar
Tim Scheiber committed
206 207 208 209

			transform.orientation = glm::rotation(
			        {0, 0, 1}, glm::vec3(std::sin(rad_orientation), 0, -std::cos(rad_orientation)));
			entity.process([&](Movement_comp& movement) { movement.want_orientation = false; });
210
		}
211
	}
212 213 214

	void Enemy_system::do_bullet_hit_detection()
	{
Tim Scheiber's avatar
Tim Scheiber committed
215 216 217 218 219 220 221 222 223 224 225 226
		for(auto&& [bullet_handle, bullet, bullet_transform, bullet_body, bullet_movement] :
		    _entity_manager.list<Entity_handle,
		                         Bullet_comp,
		                         components::Transform_comp,
		                         Rigid_body_comp,
		                         Movement_comp>()) {
			for(auto&& [player_handle, player, player_transform, player_body, player_movement] :
			    _entity_manager.list<Entity_handle,
			                         Player_comp,
			                         components::Transform_comp,
			                         Rigid_body_comp,
			                         Movement_comp>()) {
227
				glm::vec2 bullet_pos{bullet_transform.position.x, bullet_transform.position.z};
Tim Scheiber's avatar
Tim Scheiber committed
228
				glm::vec2 last_bullet_pos = bullet_movement.last_position;
229
				glm::vec2 player_pos{player_transform.position.x, player_transform.position.z};
Tim Scheiber's avatar
Tim Scheiber committed
230
				glm::vec2 last_player_pos = player_movement.last_position;
231
				if(!bullet_movement.moved) {
Tim Scheiber's avatar
Tim Scheiber committed
232 233
					last_bullet_pos = bullet_pos;
				}
234
				if(!player_movement.moved) {
Tim Scheiber's avatar
Tim Scheiber committed
235 236
					last_player_pos = player_pos;
				}
237 238
				if(intersect(Stadium{last_bullet_pos, bullet_pos, bullet_body.radius},
				             Stadium{last_player_pos, player_pos, player_body.radius})) {
239 240
					_entity_manager.erase(bullet_handle);
					_bus.send<Damaged_msg>(player_handle);
Tim Scheiber's avatar
Tim Scheiber committed
241
					_bus.send<Screen_shake_msg>(0.3f, 0.3f);
242 243 244 245
				}
			}
		}
	}
Tim Scheiber's avatar
Tim Scheiber committed
246

Tim Scheiber's avatar
Tim Scheiber committed
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
	void Enemy_system::despawn_distant_bullets()
	{
		for(auto&& [bullet_handle, bullet, bullet_transform] :
		    _entity_manager.list<Entity_handle, Bullet_comp, components::Transform_comp>()) {
			bool despawn_bullet = true;
			for (auto&& [player, player_transform] :
				_entity_manager.list<Player_comp, components::Transform_comp>()) {
				if(glm::length(glm::vec2{bullet_transform.position.x, bullet_transform.position.z}
				               - glm::vec2{player_transform.position.x, player_transform.position.z})
				   < trigger_distance_despawn_bullets) {
					despawn_bullet = false;
				}
			}
			if (despawn_bullet) {
				_entity_manager.erase(bullet_handle);
			}
		}
	}

266
	bool Enemy_system::intersect(const Stadium& stad1, const Stadium& stad2)
Tim Scheiber's avatar
Tim Scheiber committed
267 268 269 270
	{
		glm::vec2 direction1 = stad1.point2 - stad1.point1;
		glm::vec2 direction2 = stad2.point2 - stad2.point1;

271 272 273 274 275 276 277 278 279 280
		// check for intersection of the line segments
		float temp = -direction2.x * direction1.y + direction1.x * direction2.y;
		if (temp != 0) {
			float s = (-direction1.y * (stad1.point1.x - stad2.point1.x)
			     + direction1.x * (stad1.point1.y - stad2.point1.y))
			    / temp;
			float t = (-direction2.y * (stad1.point1.x - stad2.point1.x)
			     + direction2.x * (stad1.point1.y - stad2.point1.y))
			    / temp;
			if (s >= 0 && s <= 1 && t >= 0 && t <= 1) {
Tim Scheiber's avatar
Tim Scheiber committed
281 282 283 284
				return true;
			}
		}

285
		// check the distance from each end point to the other line segment
Tim Scheiber's avatar
Tim Scheiber committed
286 287 288
		float comb_radius = stad1.radius + stad2.radius;

		float length2 = glm::length2(direction1);
289 290 291
		if(length2 <= 0.001f) {
			if(glm::distance(stad2.point1, stad1.point1) < comb_radius
			   || glm::distance(stad2.point2, stad1.point1) < comb_radius) {
Tim Scheiber's avatar
Tim Scheiber committed
292 293 294 295 296 297 298 299 300 301 302 303 304 305
				return true;
			}
		}
		float lambda = std::clamp(dot(stad2.point1 - stad1.point1, direction1), 0.f, 1.f);
		if(glm::distance(stad2.point1, stad1.point1 + lambda * direction1) < comb_radius) {
			return true;
		}
		lambda = std::clamp(dot(stad2.point2 - stad1.point1, direction1), 0.f, 1.f);
		if(glm::distance(stad2.point2, stad1.point1 + lambda * direction1) < comb_radius) {
			return true;
		}

		length2 = glm::length2(direction2);
		if(length2 <= 0.001f) {
306 307
			if(glm::distance(stad1.point1, stad2.point1) < comb_radius
			   || glm::distance(stad1.point2, stad2.point1) < comb_radius) {
Tim Scheiber's avatar
Tim Scheiber committed
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
				return true;
			}
		}
		lambda = std::clamp(dot(stad1.point1 - stad2.point1, direction2), 0.f, 1.f);
		if(glm::distance(stad1.point1, stad2.point1 + lambda * direction2) < comb_radius) {
			return true;
		}
		lambda = std::clamp(dot(stad1.point2 - stad2.point1, direction2), 0.f, 1.f);
		if(glm::distance(stad1.point2, stad2.point1 + lambda * direction2) < comb_radius) {
			return true;
		}

		return false;
	}

323 324
	float Enemy_system::dot(const glm::vec2& a, const glm::vec2& b) { return a.x * b.x + a.y * b.y; }
} // namespace phase_shifter::gameplay