enemy_system.cpp 9.37 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)
	{
111
112
113
		for(auto&& [shooting, transform] :
		    _entity_manager.list<Shooting_comp, components::Transform_comp>()) {
			auto      my_position = transform.position;
114
115
116
117
118
119
			glm::vec3 closest_target_pos;
			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
174
175
176
177
178
				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;
							        });
						        })
						        .create();
					}

					shooting.wait_beats = shooting.pause_between_shots;
179
					shooting.rotate     = true;
180
181
				} else {
					shooting.wait_beats--;
182
183
				}
			}
184
185
186

			transform.orientation = glm::rotation(
			        {0, 0, 1}, glm::vec3(std::sin(rad_orientation), 0, -std::cos(rad_orientation)));
187
		}
188
	}
189
190
191

	void Enemy_system::do_bullet_hit_detection()
	{
Tim Scheiber's avatar
Tim Scheiber committed
192
193
194
195
196
197
198
199
200
201
202
203
		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>()) {
204
				glm::vec2 bullet_pos{bullet_transform.position.x, bullet_transform.position.z};
Tim Scheiber's avatar
Tim Scheiber committed
205
				glm::vec2 last_bullet_pos = bullet_movement.last_position;
206
				glm::vec2 player_pos{player_transform.position.x, player_transform.position.z};
Tim Scheiber's avatar
Tim Scheiber committed
207
208
209
210
211
212
213
214
				glm::vec2 last_player_pos = player_movement.last_position;
				if (!bullet_movement.moved) {
					last_bullet_pos = bullet_pos;
				}
				if (!player_movement.moved) {
					last_player_pos = player_pos;
				}
				if(intersect(Stadium{last_bullet_pos, bullet_pos, bullet_body.radius}, Stadium{last_player_pos, player_pos, player_body.radius})) {
215
216
217
218
219
220
					_entity_manager.erase(bullet_handle);
					_bus.send<Damaged_msg>(player_handle);
				}
			}
		}
	}
Tim Scheiber's avatar
Tim Scheiber committed
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273

	bool Enemy_system::intersect(Stadium& stad1, Stadium& stad2)
	{
		glm::vec2 direction1 = stad1.point2 - stad1.point1;
		glm::vec2 direction2 = stad2.point2 - stad2.point1;

		glm::vec2 eliminator{-direction1.y, direction1.x};

		float temp  = glm::dot(direction2, eliminator);
		if(temp != 0) {
			float lambda = glm::dot((stad1.point1 - stad2.point1), eliminator) / temp;
			if (lambda >= 0 && lambda <= 1) {
				return true;
			}
		}

		float comb_radius = stad1.radius + stad2.radius;

		float length2 = glm::length2(direction1);
		if (length2 <= 0.001f) {
			if (glm::distance(stad2.point1, stad1.point1) < comb_radius || glm::distance(stad2.point2, stad1.point1) < comb_radius) {
				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) {
			if(glm::distance(stad1.point1, stad2.point1) < comb_radius || glm::distance(stad1.point2, stad2.point1) < comb_radius) {
				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;
	}

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