Commit 49857bb7 authored by Tim Scheiber's avatar Tim Scheiber

Turrets can now target the player when they are in range

parent c66978ba
Pipeline #3304 failed with stage
in 5 minutes and 8 seconds
......@@ -18,12 +18,15 @@
"off_beat_threshold": 0.0
},
"FixedPath": {
"pause_between_steps": 1
},
"Shooting": {
"default_orientation": 270,
"rotation_per_step": 5,
"max_rotation": 10,
"spawn_offset": 1
"rotation_per_step": 15,
"max_rotation": 30,
"spawn_offset": 1,
"attack_radius": 5,
"pause_between_shots": 1
},
"Killable": {
}
......
......@@ -22,7 +22,8 @@
"Dash": {
"attack_width": 0.8
},
"Rigid_body": {
"radius": 0.5
}
"Rigid_body": {
"radius": 0.5
},
"Target": {}
}
......@@ -12,8 +12,11 @@ namespace phase_shifter::gameplay {
float direction = 0; // angle of direction with 0° beeing North (-Z)
float curvature = 0; // angle of curvature in degrees
unsigned int pause_between_steps = 0; // number of beats the entity waits between steps
unsigned int wait_beats = 0; // number of beats the entity waits before taking the next step
};
sf2_structDef(Continuous_path_comp, direction, curvature);
sf2_structDef(Continuous_path_comp, direction, curvature, pause_between_steps);
} // namespace phase_shifter::gameplay
\ No newline at end of file
......@@ -7,13 +7,16 @@
namespace phase_shifter::gameplay {
Enemy_system::Enemy_system(mirrage::ecs::Entity_manager& entity_manager, const Beat_system& beat_system)
using namespace mirrage::ecs;
Enemy_system::Enemy_system(Entity_manager& entity_manager, const Beat_system& beat_system)
: _entity_manager(entity_manager), _beat_system(beat_system)
{
_entity_manager.register_component_type<Fixed_path_comp>();
_entity_manager.register_component_type<Follow_target_comp>();
_entity_manager.register_component_type<Continuous_path_comp>();
_entity_manager.register_component_type<Shooting_comp>();
_entity_manager.register_component_type<Target_comp>();
}
void Enemy_system::update(mirrage::util::Time dt)
......@@ -21,91 +24,134 @@ namespace phase_shifter::gameplay {
auto beat = _beat_system.beat_state();
for (auto&& [movement, fixed_path] : _entity_manager.list<Movement_comp, Fixed_path_comp>()) {
if (!movement.move) {
float rad_direction = fixed_path.next_direction() * mirrage::util::PI / 180.f;
if (rad_direction >= 0) {
movement.aim.x = std::sin(rad_direction);
movement.aim.y = -std::cos(rad_direction);
movement.move = true;
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;
movement.aim.x = std::sin(rad_direction);
movement.aim.y = -std::cos(rad_direction);
movement.move = true;
}
fixed_path.wait_beats = fixed_path.pause_between_steps;
} else {
fixed_path.wait_beats--;
}
}
}
for (auto&&[movement, follow_target, transform] : _entity_manager.list<Movement_comp, Follow_target_comp, mirrage::ecs::components::Transform_comp>()) {
auto target_facet = _entity_manager.get(follow_target.target);
for (auto&&[movement, follow_target, transform] : _entity_manager.list<Movement_comp, Follow_target_comp, components::Transform_comp>()) {
if(beat.beat) {
if(follow_target.wait_beats == 0 && !movement.move) {
auto target_facet = _entity_manager.get(follow_target.target);
if(target_facet.is_some()) {
auto target_transform = target_facet.get_or_throw().get<mirrage::ecs::components::Transform_comp>();
if(target_facet.is_some()) {
auto target_transform =
target_facet.get_or_throw().get<components::Transform_comp>();
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;
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--;
}
}
}
for(auto&& [movement, cont_path] : _entity_manager.list<Movement_comp, Continuous_path_comp>()) {
if(!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;
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--;
}
}
}
for (auto&& [shooting, transform] : _entity_manager.list<Shooting_comp, mirrage::ecs::components::Transform_comp>()) {
auto target_facet = _entity_manager.get(shooting.target);
if(target_facet.is_some()) {
auto target_transform =
target_facet.get_or_throw().get<mirrage::ecs::components::Transform_comp>();
if(target_transform.is_some()) {
auto& target_position = target_transform.get_or_throw().position;
auto& my_position = transform.position;
auto dist = glm::length(glm::vec2(target_position.x, target_position.z)
- glm::vec2(my_position.x, my_position.z));
shooting.target_direction = std::asin((target_position.x - my_position.x) / dist);
if (dist <= shooting.attack_radius) {
shooting.idle = false;
} else {
shooting.idle = true;
}
for (auto&& [shooting, transform] : _entity_manager.list<Shooting_comp, components::Transform_comp>()) {
auto my_position = transform.position;
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>()) {
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;
}
}
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;
}
if (closest_dist <= shooting.attack_radius) {
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;
if (beat.beat) {
auto bullet_pattern = shooting.next_pattern();
auto orientation = shooting.target_direction;
if (shooting.idle) {
orientation = shooting.default_orientation + shooting.rotation;
shooting.rotate();
if(shooting.idle && shooting.rotate) {
shooting.do_rotation();
shooting.rotate = false;
}
auto rad_orientation = orientation * mirrage::util::PI / 180.f;
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();
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;
shooting.rotate = true;
} else {
shooting.wait_beats--;
}
}
transform.orientation = glm::rotation(
{0, 0, 1}, glm::vec3(std::sin(rad_orientation), 0, -std::cos(rad_orientation)));
}
}
}
\ No newline at end of file
......@@ -7,6 +7,7 @@
#include "follow_target_comp.hpp"
#include "continuous_path_comp.hpp"
#include "shooting_comp.hpp"
#include "target_comp.hpp"
namespace phase_shifter::gameplay {
......
......@@ -11,16 +11,13 @@ namespace phase_shifter::gameplay {
while (angle < 0) {
angle += 360;
}
while (angle >= 360) {
angle -= 360;
}
}
}
float Fixed_path_comp::next_direction()
{
if(directions.empty()) {
return -1;
return 1000;
}
float dir = directions[next_step];
......@@ -32,7 +29,7 @@ namespace phase_shifter::gameplay {
reverse = true;
}
} else {
if (dir >= 180) {
if (dir >= 180 && dir < 360) {
dir -= 180;
} else {
dir += 180;
......
......@@ -13,9 +13,16 @@ namespace phase_shifter::gameplay {
void update_path(std::vector<float> directions);
float next_direction();
unsigned int pause_between_steps = 0; // number of beats the entity waits between steps
unsigned int wait_beats = 0; // number of beats the entity waits before taking the next step
private:
unsigned int next_step = 0; //index of the next step
std::vector<float> directions; //vector containing the steps as angles (degrees) with 0° being North (-Z)
bool reverse = false; //flag wether the path is now traced backwards
};
sf2_structDef(Fixed_path_comp, pause_between_steps);
}
\ No newline at end of file
......@@ -8,6 +8,12 @@ namespace phase_shifter::gameplay {
static constexpr const char* name() { return "FollowTarget"; }
using Component::Component;
unsigned int pause_between_steps = 0; // number of beats the entity waits between steps
mirrage::ecs::Entity_handle target;
unsigned int wait_beats = 0; // number of beats the entity waits before taking the next step
};
sf2_structDef(Follow_target_comp, pause_between_steps);
} // namespace phase_shifter::gameplay
\ No newline at end of file
......@@ -11,9 +11,9 @@ namespace phase_shifter::gameplay {
return pattern;
}
void Shooting_comp::rotate() {
void Shooting_comp::do_rotation() {
rotation += rotation_per_step;
if (glm::abs(rotation) > max_rotation) {
if (max_rotation >= 0 && glm::abs(rotation) > max_rotation) {
rotation_per_step *= -1;
rotation += 2 * rotation_per_step;
}
......
......@@ -27,24 +27,26 @@ namespace phase_shifter::gameplay {
using Component::Component;
Bulletpattern next_pattern();
void rotate();
void do_rotation();
float default_orientation = 0; // default orientation of "forward" in degrees with 0° beeing North (-Z)
float rotation_per_step = 0; // rotation the turret does per beat in degrees during idle state
float max_rotation = 0; // max rotation the turret does in degrees during idle before reversing rotation direction
float spawn_offset = 0; // offset of the bullet spawn point in meters
float attack_radius = 0; // radius in which the turret targets the target
unsigned int pause_between_shots = 0; // number of beats the turret waits between shots
mirrage::ecs::Entity_handle target; // the target the turret targets while not idle
float target_direction = 0; // angular direction towards the target in degrees with 0° beeing North (-Z)
std::vector<Bulletpattern> patterns; // sequence of bullet patterns that are looped through
float rotation = 0; // current rotation from default_orientation in degrees
bool idle = true; // if false, the turret targets the target
float rotation = 0; // current rotation from default_orientation in degrees
unsigned int wait_beats = 0; // number of beats the turret waits before shooting again
bool rotate = false; //flag whether the turret will rotate next beat
bool idle = true; // if false, the turret targets the target
private:
unsigned int i_next_pattern = 0;
};
sf2_structDef(Shooting_comp, default_orientation, rotation_per_step, max_rotation, spawn_offset, attack_radius);
sf2_structDef(Shooting_comp, default_orientation, rotation_per_step, max_rotation, spawn_offset, attack_radius, pause_between_shots);
}
\ No newline at end of file
#pragma once
#include <mirrage/ecs/ecs.hpp>
#include <mirrage/utils/sf2_glm.hpp>
#include <mirrage/utils/units.hpp>
namespace phase_shifter::gameplay {
struct Target_comp : public mirrage::ecs::Stateless_tag_component<Target_comp> {
static constexpr const char* name() { return "Target"; }
using Stateless_tag_component::Stateless_tag_component;
};
} // namespace phase_shifter::gameplay
......@@ -105,9 +105,9 @@ namespace phase_shifter {
.entity_builder("basic_enemy")
.position({20, 0, 15})
.post_create([=](auto entity) {
entity.process([&](gameplay::Fixed_path_comp& fixed_path) { fixed_path.update_path({}); });
entity.process([&](gameplay::Fixed_path_comp& fixed_path) { fixed_path.update_path({180}); });
entity.process([&](gameplay::Shooting_comp& shooting) {
shooting.patterns.push_back(gameplay::Bulletpattern({{0, 0}}));
shooting.patterns.push_back(gameplay::Bulletpattern({{0, 5}}));
});
})
.create();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment