Commit 14cccc95 authored by Georg Schäfer's avatar Georg Schäfer
Browse files

Merge branch 'develop' into feature/11-ui-calibration-screen

parents ce194bf1 a670493a
{
// Informationen zu dieser Datei finden Sie unter https://go.microsoft.com//fwlink//?linkid=834763.
"configurations": [
{
"name": "x64-Debug",
"generator": "Ninja",
"configurationType": "Debug",
"inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "-v",
"ctestCommandArgs": ""
}
]
}
\ No newline at end of file
...@@ -17,9 +17,19 @@ ...@@ -17,9 +17,19 @@
"step_time_percentage": 0.2, "step_time_percentage": 0.2,
"off_beat_threshold": 0.0 "off_beat_threshold": 0.0
}, },
"FixedPath": {
"pause_between_steps": 1,
"wait_beats": 1
},
"Shooting": {
"default_orientation": 270,
"rotation_per_step": 15,
"max_rotation": 30,
"spawn_offset": 1,
"attack_radius": 5,
"pause_between_shots": 1
},
"Killable": { "Killable": {
"radius": 1 "radius": 1
}, }
"FixedPath": {
}
} }
{ {
"Transform": { "Transform": {
"scale": { "scale": {
"x": 0.3, "x": 0.2,
"y": 0.3, "y": 0.2,
"z": 0.3 "z": 0.2
} }
}, },
"Model": { "Model": {
...@@ -19,7 +19,9 @@ ...@@ -19,7 +19,9 @@
}, },
"Fragile": {}, "Fragile": {},
"ContinuousPath": { "ContinuousPath": {
"direction": 270, },
"curvature": 0 "Rigid_body": {
} "radius": 0.3
},
"Bullet": {}
} }
...@@ -25,7 +25,8 @@ ...@@ -25,7 +25,8 @@
"Stationary_attack": { "Stationary_attack": {
"radius": 3 "radius": 3
}, },
"Rigid_body": { "Rigid_body": {
"radius": 0.5 "radius": 0.5
} },
"Target": {}
} }
...@@ -41,7 +41,7 @@ namespace phase_shifter::gameplay { ...@@ -41,7 +41,7 @@ namespace phase_shifter::gameplay {
_beat_index++; _beat_index++;
LOG(plog::debug) << "beat"; LOG(plog::debug) << "beat";
beats_left = size - 1 - _beat_index - _beat_offset; beats_left = size - 1 - _beat_index + _beat_offset;
if(beats_left <= 0 || _beat_index + 1 >= size) { if(beats_left <= 0 || _beat_index + 1 >= size) {
_bus.send<Lose_msg>(); _bus.send<Lose_msg>();
} }
......
#pragma once
#include <mirrage/ecs/ecs.hpp>
#include <mirrage/utils/sf2_glm.hpp>
#include <mirrage/utils/units.hpp>
namespace phase_shifter::gameplay {
struct Bullet_comp : public mirrage::ecs::Stateless_tag_component<Bullet_comp> {
static constexpr const char* name() { return "Bullet"; }
using Stateless_tag_component::Stateless_tag_component;
};
} // namespace phase_shifter::gameplay
...@@ -12,8 +12,10 @@ namespace phase_shifter::gameplay { ...@@ -12,8 +12,10 @@ namespace phase_shifter::gameplay {
float direction = 0; // angle of direction with 0° beeing North (-Z) float direction = 0; // angle of direction with 0° beeing North (-Z)
float curvature = 0; // angle of curvature in degrees 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, wait_beats);
} // namespace phase_shifter::gameplay } // namespace phase_shifter::gameplay
\ No newline at end of file
#include "enemy_system.hpp" #include "enemy_system.hpp"
#include "beat_system.hpp"
#include "combat_system.hpp"
#include "movement_comp.hpp" #include "movement_comp.hpp"
#include "player_comp.hpp"
#include "rigid_body_comp.hpp"
#include <mirrage/ecs/components/transform_comp.hpp> #include <mirrage/ecs/components/transform_comp.hpp>
namespace phase_shifter::gameplay { namespace phase_shifter::gameplay {
Enemy_system::Enemy_system(mirrage::ecs::Entity_manager& entity_manager) : _entity_manager(entity_manager) using namespace mirrage::ecs;
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)
{ {
_entity_manager.register_component_type<Fixed_path_comp>(); _entity_manager.register_component_type<Fixed_path_comp>();
_entity_manager.register_component_type<Follow_target_comp>(); _entity_manager.register_component_type<Follow_target_comp>();
_entity_manager.register_component_type<Continuous_path_comp>(); _entity_manager.register_component_type<Continuous_path_comp>();
_entity_manager.register_component_type<Shooting_comp>();
_entity_manager.register_component_type<Target_comp>();
_entity_manager.register_component_type<Bullet_comp>();
} }
void Enemy_system::update(mirrage::util::Time dt) void Enemy_system::update(mirrage::util::Time dt)
{ {
for (auto&& [movement, fixed_path] : _entity_manager.list<Movement_comp, Fixed_path_comp>()) { auto beat = _beat_system.beat_state();
if (!movement.move) {
float rad_direction = fixed_path.next_direction() * mirrage::util::PI / 180.f; do_movement(beat);
if (rad_direction >= 0) { do_shooting(beat);
movement.aim.x = std::sin(rad_direction); do_bullet_hit_detection();
movement.aim.y = -std::cos(rad_direction); }
movement.move = true;
void Enemy_system::do_movement(Beat_state& beat)
{
do_fixed_path_movement(beat);
do_cont_path_movement(beat);
do_hunting_movement(beat);
}
void Enemy_system::do_fixed_path_movement(Beat_state& beat)
{
for(auto&& [movement, fixed_path] : _entity_manager.list<Movement_comp, Fixed_path_comp>()) {
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--;
}
}
}
}
void Enemy_system::do_cont_path_movement(Beat_state& beat)
{
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--;
} }
} }
} }
}
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);
if(target_facet.is_some()) { void Enemy_system::do_hunting_movement(Beat_state& beat)
auto target_transform = target_facet.get_or_throw().get<mirrage::ecs::components::Transform_comp>(); {
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_transform.is_some()) { if(target_facet.is_some()) {
auto& target_position = target_transform.get_or_throw().position; auto target_transform = target_facet.get_or_throw().get<components::Transform_comp>();
auto& my_position = transform.position;
movement.aim = glm::vec2({target_position.x - my_position.x, target_position.z - my_position.z}); if(target_transform.is_some()) {
movement.move = true; 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>()) { void Enemy_system::do_shooting(Beat_state& beat)
if(!movement.move) { {
float rad_direction = cont_path.next_direction() * mirrage::util::PI / 180.f; for(auto&& [entity, shooting, transform] :
movement.aim.x = std::sin(rad_direction); _entity_manager.list<Entity_facet, Shooting_comp, components::Transform_comp>()) {
movement.aim.y = -std::cos(rad_direction); auto my_position = transform.position;
movement.move = 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;
}
}
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) {
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));
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();
}
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)));
entity.process([&](Movement_comp& movement) { movement.want_orientation = false; });
} }
} }
}
\ No newline at end of file void Enemy_system::do_bullet_hit_detection()
{
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>()) {
glm::vec2 bullet_pos{bullet_transform.position.x, bullet_transform.position.z};
glm::vec2 last_bullet_pos = bullet_movement.last_position;
glm::vec2 player_pos{player_transform.position.x, player_transform.position.z};
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})) {
_entity_manager.erase(bullet_handle);
_bus.send<Damaged_msg>(player_handle);
}
}
}
}
bool Enemy_system::intersect(const Stadium& stad1, const Stadium& stad2)
{
glm::vec2 direction1 = stad1.point2 - stad1.point1;
glm::vec2 direction2 = stad2.point2 - stad2.point1;
// 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) {
return true;
}
}
// check the distance from each end point to the other line segment
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(const glm::vec2& a, const glm::vec2& b) { return a.x * b.x + a.y * b.y; }
} // namespace phase_shifter::gameplay
#pragma once #pragma once
#include <mirrage/utils/units.hpp>
#include <mirrage/ecs/entity_manager.hpp> #include <mirrage/ecs/entity_manager.hpp>
#include <mirrage/utils/units.hpp>
#include "bullet_comp.hpp"
#include "continuous_path_comp.hpp"
#include "fixed_path_comp.hpp" #include "fixed_path_comp.hpp"
#include "follow_target_comp.hpp" #include "follow_target_comp.hpp"
#include "continuous_path_comp.hpp" #include "shooting_comp.hpp"
#include "target_comp.hpp"
#include "beat_system.hpp"
namespace phase_shifter::gameplay { namespace phase_shifter::gameplay {
struct Stadium {
glm::vec2 point1;
glm::vec2 point2;
float radius;
};
class Enemy_system { class Enemy_system {
public: public:
Enemy_system(mirrage::ecs::Entity_manager& entity_manager); Enemy_system(mirrage::util::Message_bus& bus,
mirrage::ecs::Entity_manager& entity_manager,
const Beat_system& beat_system);
void update(mirrage::util::Time dt); void update(mirrage::util::Time dt);
private: private:
mirrage::util::Message_bus& _bus;
mirrage::ecs::Entity_manager& _entity_manager; mirrage::ecs::Entity_manager& _entity_manager;
const Beat_system& _beat_system;
void do_movement(Beat_state& beat);
void do_fixed_path_movement(Beat_state& beat);
void do_cont_path_movement(Beat_state& beat);
void do_hunting_movement(Beat_state& beat);
void do_shooting(Beat_state& beat);
void do_bullet_hit_detection();
bool intersect(const Stadium& stad1, const Stadium& stad2);
float dot(const glm::vec2& a, const glm::vec2& b);
}; };
} } // namespace phase_shifter::gameplay
\ No newline at end of file
...@@ -4,15 +4,20 @@ namespace phase_shifter::gameplay { ...@@ -4,15 +4,20 @@ namespace phase_shifter::gameplay {
void Fixed_path_comp::update_path(std::vector<float> directions) void Fixed_path_comp::update_path(std::vector<float> directions)
{ {
next_step = 0; next_step = 0;
reverse = false; reverse = false;
this->directions = directions; this->directions = directions;
for (float angle : this->directions) {
while (angle < 0) {
angle += 360;
}
}
} }
float Fixed_path_comp::next_direction() float Fixed_path_comp::next_direction()
{ {
if(directions.empty()) { if(directions.empty()) {
return -1; return 1000;
} }
float dir = directions[next_step]; float dir = directions[next_step];
...@@ -24,7 +29,7 @@ namespace phase_shifter::gameplay { ...@@ -24,7 +29,7 @@ namespace phase_shifter::gameplay {
reverse = true; reverse = true;
} }
} else { } else {
if (dir >= 180) { if (dir >= 180 && dir < 360) {
dir -= 180; dir -= 180;
} else { } else {
dir += 180; dir += 180;
......
...@@ -13,9 +13,15 @@ namespace phase_shifter::gameplay { ...@@ -13,9 +13,15 @@ namespace phase_shifter::gameplay {
void update_path(std::vector<float> directions); void update_path(std::vector<float> directions);
float next_direction(); 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: private:
unsigned int next_step = 0; //index of the next step 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) std::vector<float> directions; //vector containing the steps as angles (degrees) with 0° being North (-Z)