enemy_system.cpp 10.1 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();

33
34
35
36
37
		do_movement(beat);
		do_shooting(beat);
		do_bullet_hit_detection();
	}

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

Tim Scheiber's avatar
Tim Scheiber committed
45
46
	void Enemy_system::do_fixed_path_movement(Beat_state& beat)
	{
47
		for(auto&& [movement, fixed_path] : _entity_manager.list<Movement_comp, Fixed_path_comp>()) {
48
49
50
51
52
			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;
53
54
55
						movement.aim.x      = std::sin(rad_direction);
						movement.aim.y      = -std::cos(rad_direction);
						movement.move       = true;
56
57
58
59
					}
					fixed_path.wait_beats = fixed_path.pause_between_steps;
				} else {
					fixed_path.wait_beats--;
60
61
62
				}
			}
		}
63
64
	}

Tim Scheiber's avatar
Tim Scheiber committed
65
66
	void Enemy_system::do_cont_path_movement(Beat_state& beat)
	{
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
		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
82
83
	void Enemy_system::do_hunting_movement(Beat_state& beat)
	{
84
85
		for(auto&& [movement, follow_target, transform] :
		    _entity_manager.list<Movement_comp, Follow_target_comp, components::Transform_comp>()) {
86
87
88
			if(beat.beat) {
				if(follow_target.wait_beats == 0 && !movement.move) {
					auto target_facet = _entity_manager.get(follow_target.target);
89

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

93
94
95
96
97
98
99
100
101
102
103
						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--;
104
105
106
				}
			}
		}
107
	}
108

Tim Scheiber's avatar
Tim Scheiber committed
109
110
	void Enemy_system::do_shooting(Beat_state& beat)
	{
Tim Scheiber's avatar
Tim Scheiber committed
111
112
		for(auto&& [entity, shooting, transform] :
		    _entity_manager.list<Entity_facet, Shooting_comp, components::Transform_comp>()) {
113
			auto      my_position = transform.position;
Tim Scheiber's avatar
Tim Scheiber committed
114
			glm::vec3 closest_target_pos(0.f);
115
116
117
118
119
			float     closest_dist = 999999999.f;

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

120
121
				auto target_position = target_transform.position;
				auto dist            = glm::length(glm::vec2(target_position.x, target_position.z)
122
123
                                        - glm::vec2(my_position.x, my_position.z));
				if(dist < closest_dist) {
124
					closest_dist       = dist;
125
					closest_target_pos = target_position;
Tim Scheiber's avatar
Tim Scheiber committed
126
127
128
				}
			}

Tim Scheiber's avatar
Tim Scheiber committed
129
			glm::vec2 t_direction{closest_target_pos.x - my_position.x, closest_target_pos.z - my_position.z};
130
131
132
			shooting.target_direction =
			        std::acos(-t_direction.y / glm::length(t_direction)) * 180.f / mirrage::util::PI;
			if(t_direction.x < 0) {
133
134
135
				shooting.target_direction *= -1;
			}

136
			if(closest_dist <= shooting.attack_radius) {
137
138
139
140
141
142
143
144
145
146
147
				shooting.idle = false;
			} else {
				shooting.idle = true;
			}

			auto orientation = shooting.target_direction;
			if(shooting.idle) {
				orientation = shooting.default_orientation + shooting.rotation;
			}
			auto rad_orientation = orientation * mirrage::util::PI / 180.f;

148
			if(beat.beat) {
149
150
151
				if(shooting.idle && shooting.rotate) {
					shooting.do_rotation();
					shooting.rotate = false;
Tim Scheiber's avatar
Tim Scheiber committed
152
				}
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
				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));

					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;
							        });
174
175
176
177
178
							        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);
									});
179
180
181
182
183
						        })
						        .create();
					}

					shooting.wait_beats = shooting.pause_between_shots;
184
					shooting.rotate     = true;
185
186
				} else {
					shooting.wait_beats--;
187
188
				}
			}
189
190
191

			transform.orientation = glm::rotation(
			        {0, 0, 1}, glm::vec3(std::sin(rad_orientation), 0, -std::cos(rad_orientation)));
Tim Scheiber's avatar
Tim Scheiber committed
192
			entity.process([&](Movement_comp& movement) { movement.want_orientation = false; });
193
		}
194
	}
195
196
197

	void Enemy_system::do_bullet_hit_detection()
	{
Tim Scheiber's avatar
Tim Scheiber committed
198
199
200
201
202
203
204
205
206
207
208
209
		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>()) {
210
				glm::vec2 bullet_pos{bullet_transform.position.x, bullet_transform.position.z};
Tim Scheiber's avatar
Tim Scheiber committed
211
				glm::vec2 last_bullet_pos = bullet_movement.last_position;
212
				glm::vec2 player_pos{player_transform.position.x, player_transform.position.z};
Tim Scheiber's avatar
Tim Scheiber committed
213
				glm::vec2 last_player_pos = player_movement.last_position;
214
				if(!bullet_movement.moved) {
Tim Scheiber's avatar
Tim Scheiber committed
215
216
					last_bullet_pos = bullet_pos;
				}
217
				if(!player_movement.moved) {
Tim Scheiber's avatar
Tim Scheiber committed
218
219
					last_player_pos = player_pos;
				}
220
221
				if(intersect(Stadium{last_bullet_pos, bullet_pos, bullet_body.radius},
				             Stadium{last_player_pos, player_pos, player_body.radius})) {
222
223
224
225
226
227
					_entity_manager.erase(bullet_handle);
					_bus.send<Damaged_msg>(player_handle);
				}
			}
		}
	}
Tim Scheiber's avatar
Tim Scheiber committed
228

229
	bool Enemy_system::intersect(const Stadium& stad1, const Stadium& stad2)
Tim Scheiber's avatar
Tim Scheiber committed
230
231
232
233
	{
		glm::vec2 direction1 = stad1.point2 - stad1.point1;
		glm::vec2 direction2 = stad2.point2 - stad2.point1;

234
235
236
237
238
239
240
241
242
243
		// 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
244
245
246
247
				return true;
			}
		}

248
		// check the distance from each end point to the other line segment
Tim Scheiber's avatar
Tim Scheiber committed
249
250
251
		float comb_radius = stad1.radius + stad2.radius;

		float length2 = glm::length2(direction1);
252
253
254
		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
255
256
257
258
259
260
261
262
263
264
265
266
267
268
				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) {
269
270
			if(glm::distance(stad1.point1, stad2.point1) < comb_radius
			   || glm::distance(stad1.point2, stad2.point1) < comb_radius) {
Tim Scheiber's avatar
Tim Scheiber committed
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
				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;
	}

286
287
	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