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