From b2d7d94dc10b2d92909a9d0662507b183ee7630a Mon Sep 17 00:00:00 2001 From: Bilal Kahraman <45990633+bilalkah@users.noreply.github.com> Date: Wed, 27 Nov 2024 21:22:04 +0300 Subject: [PATCH] Add basic member functions to character (#12) * Add basic member functions to character Remove bot and weapon name from states Remove texture maps Remove unnecessary dependencies * Fix threading problem and make weapon states synchronized with game clock * Remove hardcoded style from texture manager * Add middle class for enemy state for enemy specific members * Update animation * Create single ray caster service for bots * Add object id field in ray * Move camera into player instead of having them separate * Create shooting manager * Update enemy states and use shooting logic --- CMakeLists.txt | 1 + src/Animation/CMakeLists.txt | 5 + src/Animation/include/Animation/animation.h | 1 + .../Animation/time_based_single_animation.h | 6 + .../src/time_based_single_animation.cpp | 27 +- src/Camera/CMakeLists.txt | 21 +- src/Camera/include/Camera/camera.h | 13 +- src/Camera/include/Camera/ray.h | 2 + src/Camera/include/Camera/single_raycaster.h | 50 +++ src/Camera/src/camera.cpp | 27 +- src/Camera/src/ray.cpp | 3 + src/Camera/src/single_raycaster.cpp | 108 ++++++ src/Characters/CMakeLists.txt | 10 +- src/Characters/include/Characters/character.h | 12 + src/Characters/include/Characters/enemy.h | 51 ++- src/Characters/include/Characters/player.h | 10 +- src/Characters/src/enemy.cpp | 93 +++++- src/Characters/src/player.cpp | 25 +- src/Core/CMakeLists.txt | 2 + src/Core/include/Core/game.h | 12 +- src/Core/src/game.cpp | 68 ++-- src/Core/src/scene.cpp | 8 +- src/Graphics/include/Graphics/renderer.h | 37 ++- src/Graphics/src/renderer.cpp | 68 ++-- src/Math/include/Math/vector.h | 1 + src/Math/src/vector.cpp | 4 + .../NavigationManager/navigation_manager.h | 6 + .../src/navigation_manager.cpp | 32 +- src/ShootingManager/CMakeLists.txt | 15 + .../ShootingManager/shooting_manager.h | 51 +++ src/ShootingManager/src/shooting_manager.cpp | 51 +++ src/State/CMakeLists.txt | 3 + src/State/include/State/enemy_state.h | 81 +++-- src/State/include/State/weapon_state.h | 68 ++-- src/State/src/enemy_state.cpp | 227 +++++++------ src/State/src/weapon_state.cpp | 236 ++++---------- src/Strike/include/Strike/weapon.h | 14 +- src/Strike/src/weapon.cpp | 23 +- .../include/TextureManager/texture_manager.h | 3 +- src/TextureManager/src/texture_manager.cpp | 308 ++++++++++-------- 40 files changed, 1164 insertions(+), 619 deletions(-) create mode 100644 src/Camera/include/Camera/single_raycaster.h create mode 100644 src/Camera/src/single_raycaster.cpp create mode 100644 src/ShootingManager/CMakeLists.txt create mode 100644 src/ShootingManager/include/ShootingManager/shooting_manager.h create mode 100644 src/ShootingManager/src/shooting_manager.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9bf535d..ffcd097 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,6 +59,7 @@ add_subdirectory(src/Graphics) add_subdirectory(src/Map) add_subdirectory(src/Math) add_subdirectory(src/NavigationManager) +add_subdirectory(src/ShootingManager) add_subdirectory(src/Strike) add_subdirectory(src/State) add_subdirectory(src/TextureManager) diff --git a/src/Animation/CMakeLists.txt b/src/Animation/CMakeLists.txt index e2f3ec9..158583b 100644 --- a/src/Animation/CMakeLists.txt +++ b/src/Animation/CMakeLists.txt @@ -6,4 +6,9 @@ add_library( target_include_directories(animation PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include ) +target_link_libraries( + animation + PUBLIC + texture_manager +) diff --git a/src/Animation/include/Animation/animation.h b/src/Animation/include/Animation/animation.h index 7f11be5..3d4864d 100644 --- a/src/Animation/include/Animation/animation.h +++ b/src/Animation/include/Animation/animation.h @@ -22,6 +22,7 @@ class IAnimation virtual void Update(const double& delta_time) = 0; virtual void Reset() = 0; virtual int GetCurrentFrame() const = 0; + virtual bool IsAnimationFinishedOnce() const = 0; }; } // namespace wolfenstein diff --git a/src/Animation/include/Animation/time_based_single_animation.h b/src/Animation/include/Animation/time_based_single_animation.h index 667b06e..9820a03 100644 --- a/src/Animation/include/Animation/time_based_single_animation.h +++ b/src/Animation/include/Animation/time_based_single_animation.h @@ -14,6 +14,7 @@ #include "Animation/animation.h" #include +#include #include namespace wolfenstein { @@ -23,18 +24,23 @@ class TBSAnimation : public IAnimation public: TBSAnimation(const std::vector& textures, const double animation_speed); + TBSAnimation(const std::string collection_name, + const double animation_speed); ~TBSAnimation() = default; void Update(const double& delta_time) override; void Reset() override; int GetCurrentFrame() const override; + bool IsAnimationFinishedOnce() const override; private: std::vector textures; + int textures_size; int current_frame; double animation_speed; double counter; + bool is_animation_finished_once; }; } // namespace wolfenstein diff --git a/src/Animation/src/time_based_single_animation.cpp b/src/Animation/src/time_based_single_animation.cpp index 1994685..4104d21 100644 --- a/src/Animation/src/time_based_single_animation.cpp +++ b/src/Animation/src/time_based_single_animation.cpp @@ -1,19 +1,34 @@ #include "Animation/time_based_single_animation.h" - +#include "TextureManager/texture_manager.h" namespace wolfenstein { TBSAnimation::TBSAnimation(const std::vector& textures, const double animation_speed) : textures(textures), + textures_size(textures.size()), current_frame(0), animation_speed(animation_speed), - counter(0) {} + counter(0), + is_animation_finished_once(false) {} + +TBSAnimation::TBSAnimation(const std::string collection_name, + const double animation_speed) + : textures( + TextureManager::GetInstance().GetTextureCollection(collection_name)), + textures_size(textures.size()), + current_frame(0), + animation_speed(animation_speed / textures_size), + counter(0), + is_animation_finished_once(false) {} void TBSAnimation::Update(const double& delta_time) { counter += delta_time; - if (counter > animation_speed) { - current_frame = (current_frame + 1) % textures.size(); + if (counter >= animation_speed) { + current_frame = (current_frame + 1) % textures_size; counter = 0; + if (!is_animation_finished_once && current_frame == textures_size - 1) { + is_animation_finished_once = true; + } } } @@ -26,4 +41,8 @@ int TBSAnimation::GetCurrentFrame() const { return textures[current_frame]; } +bool TBSAnimation::IsAnimationFinishedOnce() const { + return is_animation_finished_once; +} + } // namespace wolfenstein diff --git a/src/Camera/CMakeLists.txt b/src/Camera/CMakeLists.txt index 81b8d8a..7fa1e99 100644 --- a/src/Camera/CMakeLists.txt +++ b/src/Camera/CMakeLists.txt @@ -27,9 +27,22 @@ target_link_libraries( PUBLIC game_object ray - character - vector - map scene - texture_manager + vector +) + +add_library( + single_raycaster + STATIC + src/single_raycaster.cpp ) +target_include_directories(single_raycaster PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include +) +target_link_libraries( + single_raycaster + PUBLIC + ray + vector + map +) \ No newline at end of file diff --git a/src/Camera/include/Camera/camera.h b/src/Camera/include/Camera/camera.h index dee5016..660586d 100644 --- a/src/Camera/include/Camera/camera.h +++ b/src/Camera/include/Camera/camera.h @@ -14,11 +14,7 @@ #include "Camera/ray.h" #include "Camera/raycaster.h" -#include "Characters/character.h" -#include "Core/scene.h" #include "GameObjects/game_object.h" -#include "GameObjects/static_object.h" -#include "Map/map.h" #include #include @@ -28,6 +24,9 @@ namespace wolfenstein { struct Camera2DConfig { + Camera2DConfig(int width, double fov, double depth) + : width(width), fov(fov), depth(depth) {} + int width; double fov; double depth; @@ -35,13 +34,16 @@ struct Camera2DConfig typedef std::pair RayPair; +class Scene; class Camera2D { public: - explicit Camera2D(const Camera2DConfig& config); + explicit Camera2D(const Camera2DConfig& config, + const std::shared_ptr& scene); ~Camera2D() = default; void Update(const std::shared_ptr& scene); + void Update(); std::shared_ptr GetRays() const; std::shared_ptr GetCrosshairRay() const; @@ -58,6 +60,7 @@ class Camera2D double WorldAngleToCameraAngle(double angle) const; Camera2DConfig config_; + std::shared_ptr scene_; Position2D position_; std::shared_ptr ray_cast_; std::shared_ptr rays_; diff --git a/src/Camera/include/Camera/ray.h b/src/Camera/include/Camera/ray.h index 80f39a6..9da13c0 100644 --- a/src/Camera/include/Camera/ray.h +++ b/src/Camera/include/Camera/ray.h @@ -14,6 +14,7 @@ #include "Math/vector.h" +#include #include namespace wolfenstein { @@ -35,6 +36,7 @@ struct Ray double perpendicular_distance; int wall_id; + std::string object_id; bool is_hit; bool is_hit_vertical; diff --git a/src/Camera/include/Camera/single_raycaster.h b/src/Camera/include/Camera/single_raycaster.h new file mode 100644 index 0000000..3042339 --- /dev/null +++ b/src/Camera/include/Camera/single_raycaster.h @@ -0,0 +1,50 @@ +/** + * @file single_raycaster.h + * @author Bilal Kahraman (kahramannbilal@gmail.com) + * @brief + * @version 0.1 + * @date 2024-11-05 + * + * @copyright Copyright (c) 2024 + * + */ + +#ifndef CAMERA_INCLUDE_CAMERA_SINGLE_RAYCASTER_H_ +#define CAMERA_INCLUDE_CAMERA_SINGLE_RAYCASTER_H_ + +#include "Camera/ray.h" +#include "Map/map.h" +#include "Math/vector.h" + +#include + +namespace wolfenstein { + +class SingleRayCasterService +{ + public: + static SingleRayCasterService& GetInstance(); + SingleRayCasterService(const SingleRayCasterService&) = delete; + SingleRayCasterService& operator=(const SingleRayCasterService&) = delete; + ~SingleRayCasterService() = default; + + void InitService(const std::shared_ptr& map_ptr); + Ray Cast(const vector2d& src); + + void SubscribePlayerPose(const vector2d& pose); + + private: + SingleRayCasterService() = default; + + void PrepareRay(const vector2d& src, Ray& ray, vector2d& ray_unit_step, + vector2d& ray_length_1d, vector2i& step, + vector2i& map_check); + + vector2d dest{0, 0}; + std::shared_ptr map_ptr_; + static SingleRayCasterService* instance_; +}; + +} // namespace wolfenstein + +#endif // CAMERA_INCLUDE_CAMERA_SINGLE_RAYCASTER_H_ diff --git a/src/Camera/src/camera.cpp b/src/Camera/src/camera.cpp index 0771f84..b21246d 100644 --- a/src/Camera/src/camera.cpp +++ b/src/Camera/src/camera.cpp @@ -11,10 +11,11 @@ #include "Camera/camera.h" #include "Camera/ray.h" +#include "Core/scene.h" #include "Math/vector.h" -#include "TextureManager/texture_manager.h" #include +#include #include namespace wolfenstein { @@ -26,23 +27,24 @@ void Camera2D::InitRays() { } } -Camera2D::Camera2D(const Camera2DConfig& config) +Camera2D::Camera2D(const Camera2DConfig& config, + const std::shared_ptr& scene) : config_(config), + scene_(scene), ray_cast_(std::make_shared(config.width / 2, config.fov, config.depth)) { InitRays(); } -void Camera2D::Update(const std::shared_ptr& scene) { - ray_cast_->Update(scene->GetMap(), position_, rays_); +void Camera2D::Update() { + ray_cast_->Update(scene_->GetMap(), position_, rays_); crosshair_ray_ = std::make_shared(rays_->at(config_.width / 4)); + crosshair_ray_->is_hit = false; // Update object rays objects_.clear(); - for (const auto& object : scene->GetObjects()) { - if (object->GetObjectType() != ObjectType::CHARACTER_PLAYER) { - Calculate(object); - } + for (const auto& object : scene_->GetObjects()) { + Calculate(object); } } @@ -135,6 +137,15 @@ void Camera2D::Calculate(const std::shared_ptr& object) { object_ray_pair.second.wall_id = texture_id; objects_[object->GetId()] = object_ray_pair; + + // Calculate if the object is in the crosshair + if (object_distance < crosshair_ray_->perpendicular_distance && + camera_angle_left <= 0 && camera_angle_right >= 0) { + crosshair_ray_->is_hit = true; + crosshair_ray_->perpendicular_distance = object_distance; + crosshair_ray_->object_id = object->GetId(); + crosshair_ray_->hit_point = object_pose; + } } double Camera2D::WorldAngleToCameraAngle(double angle) const { diff --git a/src/Camera/src/ray.cpp b/src/Camera/src/ray.cpp index b1ea5c4..4328f9d 100644 --- a/src/Camera/src/ray.cpp +++ b/src/Camera/src/ray.cpp @@ -12,6 +12,7 @@ Ray::Ray() distance(0), perpendicular_distance(0), wall_id(0), + object_id(""), is_hit(false), is_hit_vertical(false) {} @@ -23,6 +24,7 @@ Ray::Ray(vector2d direction_, double theta_) distance(0), perpendicular_distance(0), wall_id(0), + object_id(""), is_hit(false), is_hit_vertical(false) {} @@ -37,6 +39,7 @@ void Ray::Reset(const vector2d ray_orig, const double ray_theta) { distance = 0; perpendicular_distance = 0; wall_id = 0; + object_id = ""; is_hit = false; is_hit_vertical = false; } diff --git a/src/Camera/src/single_raycaster.cpp b/src/Camera/src/single_raycaster.cpp new file mode 100644 index 0000000..a658165 --- /dev/null +++ b/src/Camera/src/single_raycaster.cpp @@ -0,0 +1,108 @@ +#include "Camera/single_raycaster.h" +#include "Math/vector.h" +#include + +namespace wolfenstein { + +SingleRayCasterService* SingleRayCasterService::instance_ = nullptr; + +SingleRayCasterService& SingleRayCasterService::GetInstance() { + if (instance_ == nullptr) { + instance_ = new SingleRayCasterService(); + } + return *instance_; +} + +void SingleRayCasterService::InitService(const std::shared_ptr& map_ptr) { + map_ptr_ = map_ptr; +} + +Ray SingleRayCasterService::Cast(const vector2d& src) { + + auto map_ = map_ptr_->GetMap(); + const auto& row_size = map_ptr_->GetSizeX(); + const auto& col_size = map_ptr_->GetSizeY(); + Ray ray; + vector2d ray_unit_step, ray_length_1d; + vector2i step, map_check, dest_i; + PrepareRay(src, ray, ray_unit_step, ray_length_1d, step, map_check); + auto depth_ = src.Distance(dest); + while (!ray.is_hit && ray.distance < depth_) { + if (ray_length_1d.x < ray_length_1d.y) { + ray.is_hit_vertical = true; + ray.perpendicular_distance = ray_length_1d.x; + ray.distance = ray_length_1d.x; + ray_length_1d.x += ray_unit_step.x; + map_check.x += step.x; + } + else { + ray.is_hit_vertical = false; + ray.perpendicular_distance = ray_length_1d.y; + ray.distance = ray_length_1d.y; + ray_length_1d.y += ray_unit_step.y; + map_check.y += step.y; + } + + if (map_check.x >= 0 && map_check.x < row_size && map_check.y >= 0 && + map_check.y < col_size) { + if (map_[map_check.x][map_check.y] != 0) { + break; + } + else if (ToVector2i(dest) == map_check) { + ray.is_hit = true; + ray.hit_point = ray.origin + ray.direction * depth_; + } + } + } + if (ToVector2i(dest) == ToVector2i(src)) { + ray.is_hit = true; + ray.hit_point = dest; + } + return ray; +} + +void SingleRayCasterService::SubscribePlayerPose(const vector2d& pose) { + dest = pose; +} + +void SingleRayCasterService::PrepareRay(const vector2d& src, Ray& ray, + vector2d& ray_unit_step, + vector2d& ray_length_1d, vector2i& step, + vector2i& map_check) { + ray.origin = src; + ray.direction = dest - src; + ray.direction.Norm(); + ray.is_hit = false; + ray.theta = std::atan2(ray.direction.y, ray.direction.x); + + ray_unit_step.x = + ray.direction.x == 0 ? 1e30 : std::abs(1 / ray.direction.x); + ray_unit_step.y = + ray.direction.y == 0 ? 1e30 : std::abs(1 / ray.direction.y); + + map_check.FromVector2d(ray.origin); + + if (ray.direction.x < 0) { + step.x = -1; + ray_length_1d.x = + (ray.origin.x - double(map_check.x)) * ray_unit_step.x; + } + else { + step.x = 1; + ray_length_1d.x = + (double(map_check.x + 1) - ray.origin.x) * ray_unit_step.x; + } + + if (ray.direction.y < 0) { + step.y = -1; + ray_length_1d.y = + (ray.origin.y - double(map_check.y)) * ray_unit_step.y; + } + else { + step.y = 1; + ray_length_1d.y = + (double(map_check.y + 1) - ray.origin.y) * ray_unit_step.y; + } +} + +} // namespace wolfenstein diff --git a/src/Characters/CMakeLists.txt b/src/Characters/CMakeLists.txt index 0e13dea..6fb903a 100644 --- a/src/Characters/CMakeLists.txt +++ b/src/Characters/CMakeLists.txt @@ -10,12 +10,14 @@ target_include_directories(character PUBLIC target_link_libraries( character PUBLIC - game_object - vector - collision_manager animation + collision_manager + enemy_state + game_object SDL2 + vector weapon - enemy_state + camera + single_raycaster ) diff --git a/src/Characters/include/Characters/character.h b/src/Characters/include/Characters/character.h index 33d9361..6e12c58 100644 --- a/src/Characters/include/Characters/character.h +++ b/src/Characters/include/Characters/character.h @@ -29,9 +29,18 @@ struct Position2D struct CharacterConfig { + CharacterConfig(Position2D initial_position, double translation_speed, + double rotation_speed, double width, double height) + : initial_position(initial_position), + translation_speed(translation_speed), + rotation_speed(rotation_speed), + width(width), + height(height) {} Position2D initial_position; double translation_speed; double rotation_speed; + double width; + double height; }; class ICharacter @@ -41,6 +50,9 @@ class ICharacter virtual void SetPosition(Position2D position) = 0; virtual Position2D GetPosition() const = 0; + virtual void IncreaseHealth(double amount) = 0; + virtual void DecreaseHealth(double amount) = 0; + virtual double GetHealth() const = 0; }; } // namespace wolfenstein diff --git a/src/Characters/include/Characters/enemy.h b/src/Characters/include/Characters/enemy.h index 0d264d0..36e1ec4 100644 --- a/src/Characters/include/Characters/enemy.h +++ b/src/Characters/include/Characters/enemy.h @@ -12,51 +12,90 @@ #ifndef CHARACTERS_INCLUDE_ENEMY_H_ #define CHARACTERS_INCLUDE_ENEMY_H_ +#include "Camera/single_raycaster.h" #include "Characters/character.h" #include "GameObjects/game_object.h" #include "Math/vector.h" #include "State/enemy_state.h" #include - +#include namespace wolfenstein { +struct AnimationTime +{ + double idle_animation_speed; +}; + +struct StateConfig +{ + AnimationTime animation_time; + double follow_range_max; + double follow_range_min; + double attack_range; + double attack_rate; +}; + +class EnemyFactory; class Enemy : public ICharacter, public IGameObject, public std::enable_shared_from_this { public: - explicit Enemy(CharacterConfig config, EnemyStatePtr state, double width, - double height); + explicit Enemy(std::string bot_name, CharacterConfig config); ~Enemy() = default; - void Init(); void Update(double delta_time) override; void TransitionTo(EnemyStatePtr state); + bool IsPlayerInShootingRange() const; + bool IsAttacked() const; + void SetNextPose(vector2d pose); + void SetAttacked(bool value); + void SetDeath(); void SetPose(const vector2d& pose) override; void SetPosition(Position2D position) override; + void IncreaseHealth(double amount) override; + void DecreaseHealth(double amount) override; + double GetHealth() const override; ObjectType GetObjectType() const override; vector2d GetPose() const override; Position2D GetPosition() const override; std::string GetId() const override; + std::string GetBotName() const; int GetTextureId() const override; double GetWidth() const override; double GetHeight() const override; + StateConfig GetStateConfig() const; + Ray GetCrosshairRay() const; - void SetNextPose(vector2d pose); + friend class EnemyFactory; private: Enemy() = default; + void Move(double delta_time); + std::string bot_name_; Position2D position_; double rotation_speed_; double translation_speed_; - EnemyStatePtr state_; double width; double height; + double health_{100}; std::string id_; vector2d next_pose; + StateConfig state_config_; + EnemyStatePtr state_; + Ray crosshair_ray; + bool is_attacked_; + bool is_alive_; +}; + +class EnemyFactory +{ + public: + static std::shared_ptr CreateEnemy(std::string bot_name, + CharacterConfig config); }; } // namespace wolfenstein diff --git a/src/Characters/include/Characters/player.h b/src/Characters/include/Characters/player.h index e4c10b9..53b362f 100644 --- a/src/Characters/include/Characters/player.h +++ b/src/Characters/include/Characters/player.h @@ -20,11 +20,13 @@ namespace wolfenstein { +class Camera2D; +class Ray; // Player.h class Player : public ICharacter, public IGameObject { public: - explicit Player(CharacterConfig& config); + explicit Player(CharacterConfig& config, std::shared_ptr& camera); ~Player() = default; void Update(double delta_time) override; @@ -33,11 +35,15 @@ class Player : public ICharacter, public IGameObject ObjectType GetObjectType() const override; vector2d GetPose() const override; void SetPosition(Position2D position) override; + void IncreaseHealth(double amount) override; + void DecreaseHealth(double amount) override; + double GetHealth() const override; Position2D GetPosition() const override; std::string GetId() const override; int GetTextureId() const override; double GetWidth() const override; double GetHeight() const override; + std::shared_ptr GetCrosshairRay() const; void SubscribeToPlayerPosition(std::function subscriber); private: @@ -50,7 +56,9 @@ class Player : public ICharacter, public IGameObject double translation_speed_; double width_{0.4}; double height_{1.0}; + double health_{100}; std::string id_; + std::shared_ptr camera_; std::shared_ptr weapon_; std::vector> player_position_subscribers_; }; diff --git a/src/Characters/src/enemy.cpp b/src/Characters/src/enemy.cpp index 66d5e0b..9185e9c 100644 --- a/src/Characters/src/enemy.cpp +++ b/src/Characters/src/enemy.cpp @@ -1,4 +1,6 @@ #include "Characters/enemy.h" +#include "Camera/ray.h" +#include "Camera/single_raycaster.h" #include "CollisionManager/collision_manager.h" #include "Math/vector.h" #include "Utility/uuid_generator.h" @@ -6,26 +8,55 @@ namespace wolfenstein { -Enemy::Enemy(CharacterConfig config, EnemyStatePtr state, - double width, double height) - : position_(config.initial_position), +namespace { +auto GetBotStateConfig = [](const std::string& bot_name) -> StateConfig { + if (bot_name == "soldier") { + return {{0.8}, 5.0, 1.0, 5.0, 1.0}; + } + else if (bot_name == "caco_demon") { + return {{0.8}, 5.0, 1.0, 5.0, 1.0}; + } + else if (bot_name == "cyber_demon") { + return {{0.8}, 5.0, 1.0, 5.0, 1.0}; + } + else { + throw std::invalid_argument("Invalid bot name"); + } +}; +} // namespace + +Enemy::Enemy(std::string bot_name, CharacterConfig config) + : bot_name_(bot_name), + position_(config.initial_position), rotation_speed_(config.rotation_speed), translation_speed_(config.translation_speed), - state_(state), - width(width), - height(height), - id_(UuidGenerator::GetInstance().GenerateUuid().bytes()) {} - -void Enemy::Init() { - state_->SetContext(shared_from_this()); -} + width(config.width), + height(config.height), + id_(UuidGenerator::GetInstance().GenerateUuid().bytes()), + next_pose(position_.pose), + state_config_(GetBotStateConfig(bot_name)), + crosshair_ray(Ray{}), + is_attacked_(false), + is_alive_(true) {} void Enemy::TransitionTo(EnemyStatePtr state) { state_ = state; state_->SetContext(shared_from_this()); } +bool Enemy::IsPlayerInShootingRange() const { + return crosshair_ray.is_hit; +} + +bool Enemy::IsAttacked() const { + return is_attacked_; +} + void Enemy::Update(double delta_time) { + if (!is_alive_) { + return; + } + crosshair_ray = SingleRayCasterService::GetInstance().Cast(position_.pose); state_->Update(delta_time); if (next_pose != position_.pose) { Move(delta_time); @@ -48,6 +79,18 @@ void Enemy::SetPosition(Position2D position) { position_ = position; } +void Enemy::IncreaseHealth(double amount) { + health_ += amount; +} + +void Enemy::DecreaseHealth(double amount) { + health_ -= amount; +} + +double Enemy::GetHealth() const { + return health_; +} + Position2D Enemy::GetPosition() const { return position_; } @@ -56,6 +99,10 @@ std::string Enemy::GetId() const { return id_; } +std::string Enemy::GetBotName() const { + return bot_name_; +} + void Enemy::Move(double delta_time) { vector2d direction = next_pose - position_.pose; direction.Norm(); @@ -74,6 +121,15 @@ void Enemy::SetNextPose(vector2d pose) { next_pose = pose; } +void Enemy::SetAttacked(bool value) { + is_attacked_ = value; +} + +void Enemy::SetDeath() { + crosshair_ray = Ray{}; + is_alive_ = false; +} + int Enemy::GetTextureId() const { return state_->GetCurrentFrame(); } @@ -85,4 +141,19 @@ double Enemy::GetHeight() const { return height; } +StateConfig Enemy::GetStateConfig() const { + return state_config_; +} + +Ray Enemy::GetCrosshairRay() const { + return crosshair_ray; +} + +std::shared_ptr EnemyFactory::CreateEnemy(std::string bot_name, + CharacterConfig config) { + auto enemy_ptr = std::make_shared(bot_name, config); + enemy_ptr->TransitionTo(std::make_shared()); + return enemy_ptr; +} + } // namespace wolfenstein diff --git a/src/Characters/src/player.cpp b/src/Characters/src/player.cpp index 2e6c0af..a556a5f 100644 --- a/src/Characters/src/player.cpp +++ b/src/Characters/src/player.cpp @@ -1,29 +1,34 @@ #include "Characters/player.h" +#include "Camera/camera.h" #include "CollisionManager/collision_manager.h" #include "Math/vector.h" #include "State/weapon_state.h" #include "Utility/uuid_generator.h" #include +#include namespace wolfenstein { -Player::Player(CharacterConfig& config) +Player::Player(CharacterConfig& config, std::shared_ptr& camera) : position_(config.initial_position), rotation_speed_(config.rotation_speed), translation_speed_(config.translation_speed) { id_ = UuidGenerator::GetInstance().GenerateUuid().bytes(); + camera_ = camera; weapon_ = std::make_shared("mp5"); - WeaponStatePtr loaded_state = std::make_shared("mp5"); + WeaponStatePtr loaded_state = std::make_shared(); weapon_->TransitionTo(loaded_state); } void Player::Update(double delta_time) { ShootOrReload(); + weapon_->Update(delta_time); Move(delta_time); Rotate(delta_time); for (auto& subscriber : player_position_subscribers_) { subscriber(position_); } + camera_->Update(); } void Player::SetPose(const vector2d& pose) { @@ -42,6 +47,18 @@ void Player::SetPosition(Position2D position) { position_ = position; } +void Player::IncreaseHealth(double amount) { + health_ += amount; +} + +void Player::DecreaseHealth(double amount) { + health_ -= amount; +} + +double Player::GetHealth() const { + return health_; +} + Position2D Player::GetPosition() const { return position_; } @@ -60,6 +77,10 @@ double Player::GetHeight() const { return height_; } +std::shared_ptr Player::GetCrosshairRay() const { + return camera_->GetCrosshairRay(); +} + void Player::SubscribeToPlayerPosition( std::function updator) { player_position_subscribers_.push_back(updator); diff --git a/src/Core/CMakeLists.txt b/src/Core/CMakeLists.txt index 98642f6..ff83203 100644 --- a/src/Core/CMakeLists.txt +++ b/src/Core/CMakeLists.txt @@ -13,6 +13,7 @@ target_link_libraries( map navigation_manager collision_manager + character ) add_library( @@ -41,4 +42,5 @@ target_link_libraries( SDL2_image SDL2_ttf enemy_state + single_raycaster ) diff --git a/src/Core/include/Core/game.h b/src/Core/include/Core/game.h index c1470bc..72e7a5e 100644 --- a/src/Core/include/Core/game.h +++ b/src/Core/include/Core/game.h @@ -28,6 +28,17 @@ enum class RenderType { TEXTURE, LINE }; struct GeneralConfig { + GeneralConfig(int screen_width, int screen_height, int padding, int scale, + int fps, double view_distance, double fov, bool fullscreen) + : screen_width(screen_width), + screen_height(screen_height), + padding(padding), + scale(scale), + fps(fps), + view_distance(view_distance), + fov(fov), + fullscreen(fullscreen) {} + int screen_width; int screen_height; int padding; @@ -57,7 +68,6 @@ class Game std::shared_ptr renderer_; std::shared_ptr scene_; - std::shared_ptr camera_; std::shared_ptr player_; std::shared_ptr map_; diff --git a/src/Core/src/game.cpp b/src/Core/src/game.cpp index bf98007..1d08030 100644 --- a/src/Core/src/game.cpp +++ b/src/Core/src/game.cpp @@ -1,12 +1,13 @@ #include "Core/game.h" -// #include "Animation/animator.h" #include "Animation/time_based_single_animation.h" +#include "Camera/single_raycaster.h" #include "Characters/enemy.h" #include "GameObjects/dynamic_object.h" #include "GameObjects/static_object.h" #include "Graphics/renderer.h" #include "Math/vector.h" #include "NavigationManager/navigation_manager.h" +#include "ShootingManager/shooting_manager.h" #include "State/enemy_state.h" #include "TextureManager/texture_manager.h" #include "TimeManager/time_manager.h" @@ -32,25 +33,39 @@ void Game::Init() { CollisionManager::GetInstance().InitManager(map_); NavigationManager::GetInstance().InitManager(map_); - + SingleRayCasterService::GetInstance().InitService(map_); scene_ = std::make_shared(); scene_->SetMap(map_); + Camera2DConfig camera_config = {config_.screen_width, config_.fov, + config_.view_distance}; + auto camera_ = std::make_shared(camera_config, scene_); + RenderConfig render_config = {config_.screen_width, config_.screen_height, config_.padding, config_.scale, config_.fps, config_.view_distance, config_.fov, config_.fullscreen}; - renderer_ = std::make_shared("Wolfenstein", render_config); + renderer_ = + std::make_shared("Wolfenstein", render_config, camera_); - Camera2DConfig camera_config = {config_.screen_width, config_.fov, - config_.view_distance}; - camera_ = std::make_shared(camera_config); + CharacterConfig player_config = {Position2D({3, 1.5}, 1.50), 2.0, 0.4, 0.4, + 1.0}; + player_ = std::make_shared(player_config, camera_); - CharacterConfig player_config = {Position2D({3, 1.5}, 1.50), 2.0, 0.4}; - player_ = std::make_shared(player_config); + player_->SubscribeToPlayerPosition(std::bind( + [camera_](Position2D position) { camera_->SetPosition(position); }, + std::placeholders::_1)); + player_->SubscribeToPlayerPosition(std::bind( + [](Position2D position) { + NavigationManager::GetInstance().SubscribePlayerPosition(position); + }, + std::placeholders::_1)); player_->SubscribeToPlayerPosition(std::bind( - [this](Position2D position) { camera_->SetPosition(position); }, + [](Position2D position) { + SingleRayCasterService::GetInstance().SubscribePlayerPose( + position.pose); + }, std::placeholders::_1)); scene_->SetPlayer(player_); @@ -59,6 +74,9 @@ void Game::Init() { PrepareDynamicObjects(); PrepareStaticObjects(); + ShootingManager::GetInstance().InitManager(map_, player_, + scene_->GetEnemies()); + is_running_ = true; TimeManager::GetInstance().InitClock(); } @@ -103,31 +121,37 @@ void Game::Run() { CheckEvent(); TimeManager::GetInstance().CalculateDeltaTime(); scene_->Update(TimeManager::GetInstance().GetDeltaTime()); - camera_->Update(scene_); switch (render_type_) { case RenderType::TEXTURE: - renderer_->RenderScene(scene_, camera_); + renderer_->RenderScene(scene_); break; case RenderType::LINE: - renderer_->RenderScene2D(scene_, camera_); + renderer_->RenderScene2D(scene_); break; } } } void Game::PrepareEnemies() { - auto caco_demon = std::make_shared( - CharacterConfig(Position2D({13.5, 2.5}, 1.50), 0.8, 0.4), - std::make_shared("caco_demon"), 0.4, 0.8); - - auto soldier = std::make_shared( - CharacterConfig(Position2D({9, 7}, 1.50), 0.8, 0.4), - std::make_shared("soldier"), 0.3, 0.6); - auto cyber_demon = std::make_shared( - CharacterConfig(Position2D({23.0, 4}, 1.50), 0.8, 0.4), - std::make_shared("cyber_demon"), 0.5, 1.0); + auto caco_demon = EnemyFactory::CreateEnemy( + "caco_demon", + CharacterConfig(Position2D({13.5, 2.5}, 1.50), 0.8, 0.4, 0.4, 0.8)); + + auto soldier = EnemyFactory::CreateEnemy( + "soldier", + CharacterConfig(Position2D({9, 7}, 1.50), 0.8, 0.4, 0.3, 0.6)); + + auto soldier_2 = EnemyFactory::CreateEnemy( + "soldier", + CharacterConfig(Position2D({9, 8}, 1.50), 0.8, 0.4, 0.3, 0.6)); + + auto cyber_demon = EnemyFactory::CreateEnemy( + "cyber_demon", + CharacterConfig(Position2D({23.0, 4}, 1.50), 0.8, 0.4, 0.5, 1.0)); + scene_->AddObject(caco_demon); scene_->AddObject(soldier); + scene_->AddObject(soldier_2); scene_->AddObject(cyber_demon); } diff --git a/src/Core/src/scene.cpp b/src/Core/src/scene.cpp index 4de3d3c..785751d 100644 --- a/src/Core/src/scene.cpp +++ b/src/Core/src/scene.cpp @@ -8,7 +8,6 @@ void Scene::AddObject(std::shared_ptr object) { objects.push_back(object); if (object->GetObjectType() == ObjectType::CHARACTER_ENEMY) { auto enemy = std::dynamic_pointer_cast(object); - enemy->Init(); enemies.push_back(enemy); } } @@ -22,14 +21,11 @@ void Scene::SetPlayer(std::shared_ptr player) { } void Scene::Update(double delta_time) { - player->Update(delta_time); - for (auto& enemy : enemies) { - enemy->SetNextPose(NavigationManager::GetInstance().FindPath( - enemy->GetPosition(), player->GetPosition(), enemy->GetId())); - } for (auto& object : objects) { object->Update(delta_time); } + + player->Update(delta_time); } std::vector> Scene::GetObjects() const { diff --git a/src/Graphics/include/Graphics/renderer.h b/src/Graphics/include/Graphics/renderer.h index 9758cba..b814d1c 100644 --- a/src/Graphics/include/Graphics/renderer.h +++ b/src/Graphics/include/Graphics/renderer.h @@ -14,8 +14,8 @@ #include "Camera/camera.h" #include "Characters/enemy.h" -#include "GameObjects/game_object.h" #include "Core/scene.h" +#include "GameObjects/game_object.h" #include #include #include @@ -27,6 +27,16 @@ namespace wolfenstein { struct RenderConfig { + RenderConfig(int width, int height, int padding, int scale, int fps, + double view_distance, double fov, bool fullscreen) + : width(width), + height(height), + padding(padding), + scale(scale), + fps(fps), + view_distance(view_distance), + fov(fov), + fullscreen(fullscreen) {} int width; int height; int padding; @@ -68,30 +78,25 @@ class Renderer public: explicit Renderer(const std::string& window_name, - const RenderConfig& config); + const RenderConfig& config, + std::shared_ptr& camera); ~Renderer(); - void RenderScene(const std::shared_ptr& scene_ptr, - const std::shared_ptr& camera_ptr); + void RenderScene(const std::shared_ptr& scene_ptr); - void RenderScene2D(const std::shared_ptr& scene_ptr, - const std::shared_ptr& camera_ptr); + void RenderScene2D(const std::shared_ptr& scene_ptr); private: void RenderBackground(); void RenderWalls(const std::shared_ptr& map_ptr, - const std::shared_ptr& camera_ptr, RenderQueue& render_queue); void RenderIfRayHit(const int horizontal_slice, const Ray& ray, - const std::shared_ptr& camera_ptr, RenderQueue& render_queue); void RenderIfRayHitNot(const int horizontal_slice, RenderQueue& render_queue); void RenderObjects(const std::vector>& objects, - const std::shared_ptr& camera_ptr, RenderQueue& render_queue); - int CalculateHorizontalSlice(const double& angle, - const std::shared_ptr camera_ptr); + int CalculateHorizontalSlice(const double& angle); std::tuple CalculateVerticalSlice(const double& distance); void RenderWeapon(const std::shared_ptr& player_ptr, RenderQueue& render_queue); @@ -102,17 +107,17 @@ class Renderer void SetDrawColor(SDL_Color color); void DrawFilledRectangle(vector2i start, vector2i end); void RenderMap(const std::shared_ptr map_ptr); - void RenderPlayer(const std::shared_ptr player_ptr, - const std::shared_ptr camera_ptr); - void RenderObjects(const std::vector>& objects, - const std::shared_ptr camera_ptr); + void RenderPlayer(const std::shared_ptr player_ptr); + void RenderObjects( + const std::vector>& objects); void RenderPaths(const std::vector>& enemies); - + void RenderCrosshair(const std::vector>& enemies); void DrawLine(vector2i start, vector2i end); SDL_Renderer* renderer_; SDL_Window* window_; RenderConfig config_; + std::shared_ptr camera_ptr; }; } // namespace wolfenstein diff --git a/src/Graphics/src/renderer.cpp b/src/Graphics/src/renderer.cpp index 22481d6..56638ae 100644 --- a/src/Graphics/src/renderer.cpp +++ b/src/Graphics/src/renderer.cpp @@ -1,5 +1,6 @@ #include "Graphics/renderer.h" #include "Camera/ray.h" +#include "Camera/single_raycaster.h" #include "Characters/player.h" #include "GameObjects/static_object.h" #include "Map/map.h" @@ -31,8 +32,9 @@ std::vector GenerateCirclePoints(vector2i center, int radius, } // namespace -Renderer::Renderer(const std::string& window_name, const RenderConfig& config) - : config_(config) { +Renderer::Renderer(const std::string& window_name, const RenderConfig& config, + std::shared_ptr& camera) + : config_(config), camera_ptr(camera) { if (SDL_Init(SDL_INIT_VIDEO) != 0) { SDL_Log("Unable to initialize SDL: %s", SDL_GetError()); exit(EXIT_FAILURE); @@ -65,13 +67,12 @@ Renderer::~Renderer() { SDL_Quit(); } -void Renderer::RenderScene(const std::shared_ptr& scene_ptr, - const std::shared_ptr& camera_ptr) { +void Renderer::RenderScene(const std::shared_ptr& scene_ptr) { RenderQueue render_queue(Compare); ClearScreen(); RenderBackground(); - RenderWalls(scene_ptr->GetMap(), camera_ptr, render_queue); - RenderObjects(scene_ptr->GetObjects(), camera_ptr, render_queue); + RenderWalls(scene_ptr->GetMap(), render_queue); + RenderObjects(scene_ptr->GetObjects(), render_queue); RenderWeapon(scene_ptr->GetPlayer(), render_queue); RenderTextures(render_queue); SDL_RenderPresent(renderer_); @@ -91,7 +92,6 @@ void Renderer::RenderBackground() { } void Renderer::RenderWalls(const std::shared_ptr& map_ptr, - const std::shared_ptr& camera_ptr, RenderQueue& render_queue) { const auto rays = camera_ptr->GetRays(); @@ -101,14 +101,13 @@ void Renderer::RenderWalls(const std::shared_ptr& map_ptr, RenderIfRayHitNot(horizontal_slice, render_queue); } else { - RenderIfRayHit(horizontal_slice, ray, camera_ptr, render_queue); + RenderIfRayHit(horizontal_slice, ray, render_queue); } horizontal_slice += 2; }; } void Renderer::RenderIfRayHit(const int horizontal_slice, const Ray& ray, - const std::shared_ptr& camera_ptr, RenderQueue& render_queue) { const auto distance = ray.perpendicular_distance * std::cos(camera_ptr->GetPosition().theta - ray.theta); @@ -141,7 +140,7 @@ void Renderer::RenderIfRayHitNot(const int horizontal_slice, void Renderer::RenderObjects( const std::vector>& objects, - const std::shared_ptr& camera_ptr, RenderQueue& render_queue) { + RenderQueue& render_queue) { for (const auto& object : objects) { const auto ray_pair = camera_ptr->GetObjectRay(object->GetId()); @@ -163,11 +162,9 @@ void Renderer::RenderObjects( const auto texture_width = TextureManager::GetInstance().GetTexture(first.wall_id).width; - const auto first_slice = - CalculateHorizontalSlice(first.theta, camera_ptr); + const auto first_slice = CalculateHorizontalSlice(first.theta); - const auto last_slice = - CalculateHorizontalSlice(last.theta, camera_ptr); + const auto last_slice = CalculateHorizontalSlice(last.theta); SDL_Rect src_rect = {0, 0, texture_width, texture_height}; @@ -179,8 +176,7 @@ void Renderer::RenderObjects( } } -int Renderer::CalculateHorizontalSlice( - const double& angle, const std::shared_ptr camera_ptr) { +int Renderer::CalculateHorizontalSlice(const double& angle) { const auto horizontal_slice = static_cast(angle / camera_ptr->GetDeltaAngle()) * 2 + @@ -208,9 +204,10 @@ void Renderer::RenderWeapon(const std::shared_ptr& player_ptr, static_cast(crosshair_height) / crosshair_width; const int crosshair_width_slice = config_.width / 40; const int crosshair_height_slice = crosshair_width_slice * crosshair_ratio; - SDL_Rect crosshair_dest_rect{config_.width / 2 - crosshair_width_slice / 2, - config_.height / 2 - crosshair_height_slice / 2, - crosshair_width_slice, crosshair_height_slice}; + SDL_Rect crosshair_dest_rect{ + config_.width / 2 - crosshair_width_slice / 2, + config_.height / 2 - crosshair_height_slice / 2, crosshair_width_slice, + crosshair_height_slice}; render_queue.push({6, crosshair_src_rect, crosshair_dest_rect, 0.0}); auto texture_id = player_ptr->GetTextureId(); @@ -244,18 +241,17 @@ void Renderer::ClearScreen() { SDL_RenderClear(renderer_); } -void Renderer::RenderScene2D(const std::shared_ptr& scene_ptr, - const std::shared_ptr& camera_ptr) { +void Renderer::RenderScene2D(const std::shared_ptr& scene_ptr) { ClearScreen(); RenderMap(scene_ptr->GetMap()); - RenderPlayer(scene_ptr->GetPlayer(), camera_ptr); - RenderObjects(scene_ptr->GetObjects(), camera_ptr); + RenderPlayer(scene_ptr->GetPlayer()); + RenderObjects(scene_ptr->GetObjects()); RenderPaths(scene_ptr->GetEnemies()); + RenderCrosshair(scene_ptr->GetEnemies()); SDL_RenderPresent(renderer_); } -void Renderer::RenderPlayer(const std::shared_ptr player_ptr, - const std::shared_ptr camera_ptr) { +void Renderer::RenderPlayer(const std::shared_ptr player_ptr) { const auto position = player_ptr->GetPosition(); const auto crosshair_ray = camera_ptr->GetCrosshairRay(); @@ -278,15 +274,12 @@ void Renderer::RenderPlayer(const std::shared_ptr player_ptr, for (unsigned int i = 0; i < circle_points.size(); i++) { SDL_RenderDrawPoint(renderer_, circle_points[i].x, circle_points[i].y); } - DrawLine( - ToVector2i(position.pose * config_.scale), - ToVector2i((crosshair_ray->origin + crosshair_ray->direction * 10) * - config_.scale)); + DrawLine(ToVector2i(crosshair_ray->origin * config_.scale), + ToVector2i(crosshair_ray->hit_point * config_.scale)); } void Renderer::RenderObjects( - const std::vector>& objects, - const std::shared_ptr camera_ptr) { + const std::vector>& objects) { for (const auto& object : objects) { @@ -346,6 +339,19 @@ void Renderer::RenderPaths(const std::vector>& enemies) { } } +void Renderer::RenderCrosshair( + const std::vector>& enemies) { + SetDrawColor({255, 0, 255, 255}); + for (const auto& enemy : enemies) { + const auto crosshair_ray = enemy->GetCrosshairRay(); + if (!crosshair_ray.is_hit) { + continue; + } + DrawLine(ToVector2i(enemy->GetPose() * config_.scale), + ToVector2i((crosshair_ray.hit_point) * config_.scale)); + } +} + void Renderer::DrawLine(vector2i start, vector2i end) { SDL_RenderDrawLine(renderer_, start.x, start.y, end.x, end.y); } diff --git a/src/Math/include/Math/vector.h b/src/Math/include/Math/vector.h index 71e0087..bc26fed 100644 --- a/src/Math/include/Math/vector.h +++ b/src/Math/include/Math/vector.h @@ -60,6 +60,7 @@ struct vector2d double Magnitude() const; double Determinant(const vector2d& v) const; double Distance(const vector2d& v) const; + double MDistance(const vector2d& v) const; void Norm(); }; diff --git a/src/Math/src/vector.cpp b/src/Math/src/vector.cpp index d81134d..11dfd71 100644 --- a/src/Math/src/vector.cpp +++ b/src/Math/src/vector.cpp @@ -133,6 +133,10 @@ double vector2d::Distance(const vector2d& v) const { return std::sqrt(std::pow(x - v.x, 2) + std::pow(y - v.y, 2)); } +double vector2d::MDistance(const vector2d& v) const { + return std::abs(x - v.x) + std::abs(y - v.y); +} + void vector2d::Norm() { double size = std::sqrt(x * x + y * y); x /= size; diff --git a/src/NavigationManager/include/NavigationManager/navigation_manager.h b/src/NavigationManager/include/NavigationManager/navigation_manager.h index 10c2e82..5f94a3e 100644 --- a/src/NavigationManager/include/NavigationManager/navigation_manager.h +++ b/src/NavigationManager/include/NavigationManager/navigation_manager.h @@ -32,7 +32,12 @@ class NavigationManager void InitManager(std::shared_ptr map); vector2d FindPath(Position2D start, Position2D end, std::string id); + vector2d FindPathToPlayer(Position2D start, std::string id); std::vector GetPath(std::string id); + void ResetPath(std::string id); + void SubscribePlayerPosition(const Position2D& position); + double EuclideanDistanceToPlayer(const Position2D& position); + double ManhattanDistanceToPlayer(const Position2D& position); private: NavigationManager() = default; @@ -41,6 +46,7 @@ class NavigationManager std::shared_ptr map_; std::shared_ptr path_planner_; std::unordered_map> paths_; + Position2D player_position_; }; } // namespace wolfenstein diff --git a/src/NavigationManager/src/navigation_manager.cpp b/src/NavigationManager/src/navigation_manager.cpp index bd96e79..9a10ad3 100644 --- a/src/NavigationManager/src/navigation_manager.cpp +++ b/src/NavigationManager/src/navigation_manager.cpp @@ -1,6 +1,7 @@ #include "NavigationManager/navigation_manager.h" #include "Math/vector.h" #include "NavigationManager/navigation_helper.h" +#include #include namespace wolfenstein { @@ -22,14 +23,11 @@ void NavigationManager::InitManager(std::shared_ptr map) { //@Note apply caching mechanism later vector2d NavigationManager::FindPath(Position2D start, Position2D end, std::string id) { - if (start.pose.Distance(end.pose) < 0.1) { - return start.pose; - } - if (start.pose.Distance(end.pose) > 5) { + const auto res = map_->GetResolution(); + if (start.pose.Distance(end.pose) < (res * 0.9)) { paths_[id] = {start.pose}; return start.pose; } - const auto res = map_->GetResolution(); planning::Node start_node = FromVector2d(start.pose / res); planning::Node end_node = FromVector2d(end.pose / res); @@ -51,12 +49,34 @@ vector2d NavigationManager::FindPath(Position2D start, Position2D end, } path_vector.erase(path_vector.begin()); auto next = path_vector.front(); - + return next; } +vector2d NavigationManager::FindPathToPlayer(Position2D start, std::string id) { + return FindPath(start, player_position_, id); +} + std::vector NavigationManager::GetPath(std::string id) { return paths_[id]; } +void NavigationManager::ResetPath(std::string id) { + paths_[id].clear(); +} + +void NavigationManager::SubscribePlayerPosition(const Position2D& position) { + player_position_ = position; +} + +double NavigationManager::EuclideanDistanceToPlayer( + const Position2D& position) { + return player_position_.pose.Distance(position.pose); +} + +double NavigationManager::ManhattanDistanceToPlayer( + const Position2D& position) { + return player_position_.pose.MDistance(position.pose); +} + } // namespace wolfenstein \ No newline at end of file diff --git a/src/ShootingManager/CMakeLists.txt b/src/ShootingManager/CMakeLists.txt new file mode 100644 index 0000000..4c10d09 --- /dev/null +++ b/src/ShootingManager/CMakeLists.txt @@ -0,0 +1,15 @@ +add_library( + shooting_manager + STATIC + src/shooting_manager.cpp +) +target_include_directories(shooting_manager PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include +) +target_link_libraries( + shooting_manager + PUBLIC + single_raycaster + vector + character +) diff --git a/src/ShootingManager/include/ShootingManager/shooting_manager.h b/src/ShootingManager/include/ShootingManager/shooting_manager.h new file mode 100644 index 0000000..e1178f9 --- /dev/null +++ b/src/ShootingManager/include/ShootingManager/shooting_manager.h @@ -0,0 +1,51 @@ +/** + * @file shooting_manager.h + * @author Bilal Kahraman (kahramannbilal@gmail.com) + * @brief + * @version 0.1 + * @date 2024-11-05 + * + * @copyright Copyright (c) 2024 + * + */ + +#ifndef SHOOTING_MANAGER_INCLUDE_SHOOTING_MANAGER_SHOOTING_MANAGER_H +#define SHOOTING_MANAGER_INCLUDE_SHOOTING_MANAGER_SHOOTING_MANAGER_H + +#include "Camera/single_raycaster.h" +#include "Characters/character.h" +#include "Characters/enemy.h" +#include "Characters/player.h" +#include "Math/vector.h" +#include "NavigationManager/navigation_manager.h" +#include +#include + +namespace wolfenstein { + +class ShootingManager +{ + public: + static ShootingManager& GetInstance(); + ShootingManager(const ShootingManager&) = delete; + ShootingManager& operator=(const ShootingManager&) = delete; + ~ShootingManager() = default; + + void InitManager(std::shared_ptr map, std::shared_ptr player, + std::vector> enemies); + void PlayerShoot(); + void EnemyShoot(); + + private: + ShootingManager() = default; + double CalculateDamage(const std::shared_ptr enemy); + + static ShootingManager* instance_; + std::shared_ptr player_; + std::vector> enemies_; + std::shared_ptr map_; +}; + +} // namespace wolfenstein + +#endif // SHOOTING_MANAGER_INCLUDE_SHOOTING_MANAGER_SHOOTING_MANAGER_H diff --git a/src/ShootingManager/src/shooting_manager.cpp b/src/ShootingManager/src/shooting_manager.cpp new file mode 100644 index 0000000..b3975cf --- /dev/null +++ b/src/ShootingManager/src/shooting_manager.cpp @@ -0,0 +1,51 @@ +#include "ShootingManager/shooting_manager.h" +#include "Math/vector.h" + +namespace wolfenstein { + +ShootingManager* ShootingManager::instance_ = nullptr; + +ShootingManager& ShootingManager::GetInstance() { + if (instance_ == nullptr) { + instance_ = new ShootingManager(); + } + return *instance_; +} + +void ShootingManager::InitManager(std::shared_ptr map, + std::shared_ptr player, + std::vector> enemies) { + map_ = map; + player_ = player; + enemies_ = enemies; +} + +void ShootingManager::PlayerShoot() { + auto enemy = std::find_if( + enemies_.begin(), enemies_.end(), [this](const auto& enemy) { + return enemy->GetId() == player_->GetCrosshairRay()->object_id; + }); + if (enemy != enemies_.end()) { + (*enemy)->DecreaseHealth(CalculateDamage(*enemy)); + (*enemy)->SetAttacked(true); + std::cout << "Enemy Name: " << (*enemy)->GetBotName() + << " Enemy health: " << (*enemy)->GetHealth() << std::endl; + if ((*enemy)->GetHealth() <= 0) { + // remove and erase + std::cout << "Enemy is dead" << std::endl; + enemies_.erase(enemy); + } + } +} + +void ShootingManager::EnemyShoot() { + std::cout << "Enemy shoot" << std::endl; + player_->DecreaseHealth(10); + std::cout << "Player health: " << player_->GetHealth() << std::endl; +} + +double ShootingManager::CalculateDamage(const std::shared_ptr enemy) { + return 10; +} + +} // namespace wolfenstein diff --git a/src/State/CMakeLists.txt b/src/State/CMakeLists.txt index 57f6095..f949441 100644 --- a/src/State/CMakeLists.txt +++ b/src/State/CMakeLists.txt @@ -12,6 +12,8 @@ target_link_libraries( enemy_state PUBLIC character + animation + navigation_manager texture_manager ) @@ -30,4 +32,5 @@ target_link_libraries( PUBLIC weapon texture_manager + shooting_manager ) \ No newline at end of file diff --git a/src/State/include/State/enemy_state.h b/src/State/include/State/enemy_state.h index 81d8637..7e5f1f1 100644 --- a/src/State/include/State/enemy_state.h +++ b/src/State/include/State/enemy_state.h @@ -12,13 +12,14 @@ #ifndef STATE_INCLUDE_STATE_ENEMY_STATE_H_ #define STATE_INCLUDE_STATE_ENEMY_STATE_H_ +#include "Animation/time_based_single_animation.h" #include "State/state.h" +#include #include namespace wolfenstein { class Enemy; -typedef std::shared_ptr> EnemyStatePtr; template <> struct StateType @@ -27,104 +28,112 @@ struct StateType }; typedef StateType::Type EnemyStateType; -class IdleState : public State +class EnemyState : public State { public: - IdleState(const std::string bot_name); + virtual ~EnemyState() = default; +}; + +typedef std::shared_ptr EnemyStatePtr; + +// ########################################### IdleState ########################################### +class IdleState : public EnemyState +{ + public: + IdleState(); ~IdleState(); void Update(const double& delta_time) override; void Reset() override; + void OnContextSet() override; int GetCurrentFrame() const override; EnemyStateType GetType() const override; private: - std::string bot_name_; - int current_frame; - double counter; - double interval; double animation_speed_; - std::vector textures; + double range_; + std::unique_ptr animation_; }; -class WalkState : public State +// ########################################### WalkState ########################################### +class WalkState : public EnemyState { public: - WalkState(const std::string bot_name); + WalkState(); ~WalkState(); void Update(const double& delta_time) override; void Reset() override; + void OnContextSet() override; int GetCurrentFrame() const override; EnemyStateType GetType() const override; private: - std::string bot_name_; - int current_frame; - double counter; - double interval; double animation_speed_; - std::vector textures; + double range_max_; + double range_min_; + double attack_range_; + double attack_rate_; + double attack_counter_; + bool is_attacked_; + std::unique_ptr animation_; }; -class AttackState : public State +// ########################################### AttackState ########################################### +class AttackState : public EnemyState { public: - AttackState(const std::string bot_name); + AttackState(); ~AttackState(); void Update(const double& delta_time) override; void Reset() override; + void OnContextSet() override; int GetCurrentFrame() const override; EnemyStateType GetType() const override; private: - std::string bot_name_; - int current_frame; - double counter; - double interval; double animation_speed_; - std::vector textures; + double attack_counter_; + std::unique_ptr animation_; }; -class PainState : public State +// ########################################### PainState ########################################### +class PainState : public EnemyState { public: - PainState(const std::string bot_name); + PainState(); ~PainState(); void Update(const double& delta_time) override; void Reset() override; + void OnContextSet() override; int GetCurrentFrame() const override; EnemyStateType GetType() const override; private: - std::string bot_name_; - int current_frame; - double counter; - double interval; double animation_speed_; - std::vector textures; + double counter; + std::unique_ptr animation_; }; -class DeathState : public State +// ########################################### DeathState ########################################### +class DeathState : public EnemyState { public: - DeathState(const std::string bot_name); + DeathState(); ~DeathState(); void Update(const double& delta_time) override; void Reset() override; + void OnContextSet() override; int GetCurrentFrame() const override; EnemyStateType GetType() const override; private: - std::string bot_name_; - int current_frame; - double counter; - double interval; double animation_speed_; - std::vector textures; + double counter; + std::unique_ptr animation_; }; } // namespace wolfenstein diff --git a/src/State/include/State/weapon_state.h b/src/State/include/State/weapon_state.h index f68b1e1..97c4624 100644 --- a/src/State/include/State/weapon_state.h +++ b/src/State/include/State/weapon_state.h @@ -19,7 +19,6 @@ namespace wolfenstein { class Weapon; -typedef std::shared_ptr> WeaponStatePtr; template <> struct StateType @@ -28,60 +27,64 @@ struct StateType }; typedef StateType::Type WeaponStateType; -class LoadedState : public State +class WeaponState : public State { public: - LoadedState(const std::string weapon_name); + virtual ~WeaponState() = default; + virtual void PullTrigger() {}; +}; + +typedef std::shared_ptr WeaponStatePtr; + +// ########################################### LoadedState ########################################### +class LoadedState : public WeaponState +{ + public: + LoadedState(); ~LoadedState(); void Update(const double&) override; void Reset() override; void OnContextSet() override; - void TransitionRequest(std::shared_ptr>& state) override; int GetCurrentFrame() const override; WeaponStateType GetType() const override; + void PullTrigger() override; + private: - void AttackAnimation(); - std::string weapon_name_; - double last_attack_time_; - double animation_speed_; - bool cooldown_; - bool interrupt_; - bool destroyed_; - WeaponStatePtr requested_state_; - std::shared_ptr animation_; + bool trigger_pulled_; + double trigger_pull_time_; + double fire_rate_; + std::unique_ptr animation_; }; -class OutOfAmmoState : public State +// ########################################### OutOfAmmoState ########################################### +class OutOfAmmoState : public WeaponState { public: - OutOfAmmoState(const std::string weapon_name); + OutOfAmmoState(); ~OutOfAmmoState(); void Update(const double&) override; void Reset() override; void OnContextSet() override; - void TransitionRequest(std::shared_ptr>& state) override; int GetCurrentFrame() const override; WeaponStateType GetType() const override; + void PullTrigger() override; + private: - void NoAttackAnimation(); - std::string weapon_name_; - double last_attack_time_; - double animation_speed_; - bool cooldown_; - bool interrupt_; - bool destroyed_; - WeaponStatePtr requested_state_; - std::shared_ptr animation_; + bool trigger_pulled_; + double trigger_pull_time_; + double fire_rate_; + std::unique_ptr animation_; }; -class ReloadingState : public State +// ########################################### ReloadingState ########################################### +class ReloadingState : public WeaponState { public: - ReloadingState(const std::string weapon_name); + ReloadingState(); ~ReloadingState(); void Update(const double&) override; @@ -91,14 +94,9 @@ class ReloadingState : public State WeaponStateType GetType() const override; private: - void ReloadAnimation(); - std::string weapon_name_; - double last_attack_time_; - double animation_speed_; - bool cooldown_; - bool interrupt_; - bool destroyed_; - std::shared_ptr animation_; + double reload_time_; + double reload_speed_; + std::unique_ptr animation_; }; } // namespace wolfenstein diff --git a/src/State/src/enemy_state.cpp b/src/State/src/enemy_state.cpp index ec674a2..835fdaa 100644 --- a/src/State/src/enemy_state.cpp +++ b/src/State/src/enemy_state.cpp @@ -1,196 +1,217 @@ #include "State/enemy_state.h" #include "Characters/enemy.h" +#include "NavigationManager/navigation_manager.h" +#include "ShootingManager/shooting_manager.h" #include "TextureManager/texture_manager.h" -#include + namespace wolfenstein { -namespace { -const std::unordered_map> - enemy_data_{{"soldier", - {{"idle", "soldier_idle"}, - {"walk", "soldier_walk"}, - {"attack", "soldier_attack"}, - {"death", "soldier_death"}, - {"pain", "soldier_pain"}}}, - {"caco_demon", - {{"idle", "caco_demon_idle"}, - {"walk", "caco_demon_walk"}, - {"attack", "caco_demon_attack"}, - {"death", "caco_demon_death"}, - {"pain", "caco_demon_pain"}}}, - {"cyber_demon", - {{"idle", "cyber_demon_idle"}, - {"walk", "cyber_demon_walk"}, - {"attack", "cyber_demon_attack"}, - {"death", "cyber_demon_death"}, - {"pain", "cyber_demon_pain"}}}}; -} // namespace - -IdleState::IdleState(const std::string bot_name) - : bot_name_(bot_name), current_frame(0), counter(0), interval(0) { - animation_speed_ = 0.3; - textures = TextureManager::GetInstance().GetTextureCollection( - enemy_data_.at(bot_name_).at("idle")); -} +// ########################################### IdleState ########################################### +IdleState::IdleState() {} IdleState::~IdleState() {} void IdleState::Update(const double& delta_time) { - counter += delta_time; - if (counter > animation_speed_) { - current_frame = (current_frame + 1) % textures.size(); - counter = 0; + animation_->Update(delta_time); + + if (context_->IsPlayerInShootingRange()) { + context_->TransitionTo(std::make_shared()); } - interval += delta_time; - if (interval > 5) { - context_->TransitionTo(std::make_shared(bot_name_)); + if (context_->IsAttacked()) { + context_->TransitionTo(std::make_shared()); } } void IdleState::Reset() { - current_frame = 0; - counter = 0; + animation_->Reset(); +} + +void IdleState::OnContextSet() { + const auto config = context_->GetStateConfig(); + animation_speed_ = config.animation_time.idle_animation_speed; + range_ = config.follow_range_max; + animation_ = std::make_unique( + TextureManager::GetInstance().GetTextureCollection( + context_->GetBotName() + "_idle"), + animation_speed_); } int IdleState::GetCurrentFrame() const { - return textures[current_frame]; + return animation_->GetCurrentFrame(); } EnemyStateType IdleState::GetType() const { return EnemyStateType::Idle; } -WalkState::WalkState(const std::string bot_name) - : bot_name_(bot_name), current_frame(0), counter(0), interval(0) { - animation_speed_ = 0.3; - textures = TextureManager::GetInstance().GetTextureCollection( - enemy_data_.at(bot_name_).at("walk")); -} +// ########################################### WalkState ########################################### +WalkState::WalkState() + : animation_speed_(1.2), + range_max_(5.0), + range_min_(1.5), + attack_range_(5.0), + attack_rate_(1.0), + attack_counter_(0.0), + is_attacked_(false) {} WalkState::~WalkState() {} void WalkState::Update(const double& delta_time) { - counter += delta_time; - if (counter > animation_speed_) { - current_frame = (current_frame + 1) % textures.size(); - counter = 0; + const auto bot_position = context_->GetPosition(); + const auto distance = + NavigationManager::GetInstance().EuclideanDistanceToPlayer( + bot_position); + if (context_->IsAttacked()) { + NavigationManager::GetInstance().ResetPath(context_->GetId()); + context_->SetNextPose(bot_position.pose); + context_->TransitionTo(std::make_shared()); + return; + } + if (!context_->IsPlayerInShootingRange()) { + if (distance > range_max_) { + NavigationManager::GetInstance().ResetPath(context_->GetId()); + context_->TransitionTo(std::make_shared()); + return; + } + } + if (distance <= attack_range_ && context_->IsPlayerInShootingRange()) { + attack_counter_ += delta_time; + if (attack_counter_ > attack_rate_) { + context_->SetNextPose(bot_position.pose); + context_->TransitionTo(std::make_shared()); + return; + } } - interval += delta_time; - if (interval > 5) { - context_->TransitionTo(std::make_shared(bot_name_)); + if ((!context_->IsPlayerInShootingRange()) || + ((distance > range_min_) && context_->IsPlayerInShootingRange())) { + auto next_position = NavigationManager::GetInstance().FindPathToPlayer( + bot_position, context_->GetId()); + context_->SetNextPose(next_position); } + else { + NavigationManager::GetInstance().ResetPath(context_->GetId()); + context_->SetNextPose(bot_position.pose); + } + + animation_->Update(delta_time); } void WalkState::Reset() { - current_frame = 0; - counter = 0; + animation_->Reset(); +} + +void WalkState::OnContextSet() { + animation_ = std::make_unique( + context_->GetBotName() + "_walk", animation_speed_); } int WalkState::GetCurrentFrame() const { - return textures[current_frame]; + return animation_->GetCurrentFrame(); } EnemyStateType WalkState::GetType() const { return EnemyStateType::Walk; } -AttackState::AttackState(const std::string bot_name) - : bot_name_(bot_name), current_frame(0), counter(0), interval(0) { - animation_speed_ = 0.3; - textures = TextureManager::GetInstance().GetTextureCollection( - enemy_data_.at(bot_name_).at("attack")); -} +// ########################################### AttackState ########################################### +AttackState::AttackState() : animation_speed_(0.5), attack_counter_(0.0) {} AttackState::~AttackState() {} void AttackState::Update(const double& delta_time) { - counter += delta_time; - if (counter > animation_speed_) { - current_frame = (current_frame + 1) % textures.size(); - counter = 0; + animation_->Update(delta_time); + if (attack_counter_ == 0.0) { + ShootingManager::GetInstance().EnemyShoot(); + } + if (context_->IsAttacked()) { + context_->TransitionTo(std::make_shared()); + return; } - interval += delta_time; - if (interval > 5) { - context_->TransitionTo(std::make_shared(bot_name_)); + if (attack_counter_ > animation_speed_) { + context_->TransitionTo(std::make_shared()); + return; } + attack_counter_ += delta_time; } void AttackState::Reset() { - current_frame = 0; - counter = 0; + animation_->Reset(); +} + +void AttackState::OnContextSet() { + animation_ = std::make_unique( + context_->GetBotName() + "_attack", animation_speed_); } int AttackState::GetCurrentFrame() const { - return textures[current_frame]; + return animation_->GetCurrentFrame(); } EnemyStateType AttackState::GetType() const { return EnemyStateType::Attack; } -PainState::PainState(const std::string bot_name) - : bot_name_(bot_name), current_frame(0), counter(0), interval(0) { - animation_speed_ = 0.3; - textures = TextureManager::GetInstance().GetTextureCollection( - enemy_data_.at(bot_name_).at("pain")); -} +// ########################################### PainState ########################################### +PainState::PainState() : animation_speed_(0.25), counter(0.0) {} PainState::~PainState() {} void PainState::Update(const double& delta_time) { + animation_->Update(delta_time); counter += delta_time; if (counter > animation_speed_) { - current_frame = (current_frame + 1) % textures.size(); - counter = 0; - } - interval += delta_time; - if (interval > 5) { - context_->TransitionTo(std::make_shared(bot_name_)); + if (context_->GetHealth() <= 0) { + context_->TransitionTo(std::make_shared()); + return; + } + context_->SetAttacked(false); + context_->TransitionTo(std::make_shared()); } } void PainState::Reset() { - current_frame = 0; - counter = 0; + animation_->Reset(); +} + +void PainState::OnContextSet() { + animation_ = std::make_unique( + context_->GetBotName() + "_pain", animation_speed_); } int PainState::GetCurrentFrame() const { - return textures[current_frame]; + return animation_->GetCurrentFrame(); } EnemyStateType PainState::GetType() const { return EnemyStateType::Pain; } -DeathState::DeathState(const std::string bot_name) - : bot_name_(bot_name), current_frame(0), counter(0), interval(0) { - animation_speed_ = 0.3; - textures = TextureManager::GetInstance().GetTextureCollection( - enemy_data_.at(bot_name_).at("death")); -} +// ########################################### DeathState ########################################### +DeathState::DeathState() : animation_speed_(1.0) {} DeathState::~DeathState() {} void DeathState::Update(const double& delta_time) { - counter += delta_time; - if (counter > animation_speed_) { - current_frame = (current_frame + 1) % textures.size(); - counter = 0; + if (!animation_->IsAnimationFinishedOnce()) { + animation_->Update(delta_time); + counter += delta_time; } - interval += delta_time; - if (interval > 5) { - context_->TransitionTo(std::make_shared(bot_name_)); + else { + context_->SetDeath(); } } void DeathState::Reset() { - current_frame = 0; - counter = 0; + animation_->Reset(); +} + +void DeathState::OnContextSet() { + animation_ = std::make_unique( + context_->GetBotName() + "_death", animation_speed_); } int DeathState::GetCurrentFrame() const { - return textures[current_frame]; + return animation_->GetCurrentFrame(); } EnemyStateType DeathState::GetType() const { diff --git a/src/State/src/weapon_state.cpp b/src/State/src/weapon_state.cpp index 9b44f95..cfa015e 100644 --- a/src/State/src/weapon_state.cpp +++ b/src/State/src/weapon_state.cpp @@ -1,44 +1,31 @@ #include "State/weapon_state.h" +#include "Animation/time_based_single_animation.h" #include "State/state.h" #include "Strike/weapon.h" -#include "TextureManager/texture_manager.h" -#include "TimeManager/time_manager.h" -#include #include -#include -#include +#include "ShootingManager/shooting_manager.h" namespace wolfenstein { -namespace { - -const std::unordered_map> - weapon_data_{{"mp5", - {{"loaded", "mp5_loaded"}, - {"outofammo", "mp5_outofammo"}, - {"reload", "mp5_reload"}}}}; +// ########################################### LoadedState ########################################### +LoadedState::LoadedState() + : trigger_pulled_(false), trigger_pull_time_(0.0), fire_rate_(0.0) {} -} +LoadedState::~LoadedState() {} -// ########################################### LoadedState ########################################### -LoadedState::LoadedState(const std::string weapon_name) - : weapon_name_(weapon_name), - last_attack_time_(0), - cooldown_(false), - interrupt_(false), - destroyed_(false) {} - -LoadedState::~LoadedState() { - destroyed_ = true; -} - -void LoadedState::Update(const double&) { - if (!cooldown_) { - cooldown_ = true; - last_attack_time_ = TimeManager::GetInstance().GetCurrentTime(); - std::thread attack_thread(&LoadedState::AttackAnimation, this); - attack_thread.detach(); +void LoadedState::Update(const double& delta_time) { + if (trigger_pulled_) { + animation_->Update(delta_time); + trigger_pull_time_ += delta_time; + if (trigger_pull_time_ >= fire_rate_) { + trigger_pulled_ = false; + animation_->Reset(); + if (context_->GetAmmo() == 0) { + WeaponStatePtr out_of_ammo_state = + std::make_shared(); + context_->TransitionTo(out_of_ammo_state); + } + } } } @@ -46,78 +33,44 @@ WeaponStateType LoadedState::GetType() const { return WeaponStateType::Loaded; } -void LoadedState::AttackAnimation() { - auto attack_time = last_attack_time_; - auto current_time = TimeManager::GetInstance().GetCurrentTime(); - while (current_time - last_attack_time_ < animation_speed_ && !interrupt_ && - !destroyed_) { - auto time_elapsed = current_time - attack_time; - animation_->Update(time_elapsed); - attack_time = current_time; - current_time = TimeManager::GetInstance().GetCurrentTime(); - } - if (destroyed_) { - return; - } - animation_->Reset(); - context_->DecreaseAmmo(); - if (interrupt_) { - context_->TransitionTo(requested_state_); - } - if (context_->GetAmmo() == 0) { - std::shared_ptr> out_of_ammo_state = - std::make_shared(weapon_name_); - context_->TransitionTo(out_of_ammo_state); - } - cooldown_ = false; -} - void LoadedState::Reset() { animation_->Reset(); } void LoadedState::OnContextSet() { - animation_speed_ = context_->GetAttackSpeed(); - auto textures = TextureManager::GetInstance().GetTextureCollection( - weapon_data_.at(weapon_name_).at("loaded")); - animation_ = std::make_shared( - textures, animation_speed_ / textures.size()); -} - -void LoadedState::TransitionRequest(std::shared_ptr>& state) { - if (!cooldown_) { - context_->TransitionTo(state); - } - if (interrupt_) { - return; - } - interrupt_ = true; - requested_state_ = state; + fire_rate_ = context_->GetAttackSpeed(); + animation_ = std::make_unique( + context_->GetWeaponName() + "_loaded", fire_rate_); } int LoadedState::GetCurrentFrame() const { return animation_->GetCurrentFrame(); } -// ########################################### OutOfAmmoState ########################################### -OutOfAmmoState::OutOfAmmoState(const std::string weapon_name) - : weapon_name_(weapon_name), - last_attack_time_(0), - cooldown_(false), - interrupt_(false), - destroyed_(false) {} - -OutOfAmmoState::~OutOfAmmoState() { - destroyed_ = true; +void LoadedState::PullTrigger() { + if (trigger_pulled_) { + return; + } + trigger_pulled_ = true; + trigger_pull_time_ = 0; + context_->DecreaseAmmo(); + ShootingManager::GetInstance().PlayerShoot(); } -void OutOfAmmoState::Update(const double&) { +// ########################################### OutOfAmmoState ########################################### +OutOfAmmoState::OutOfAmmoState() + : trigger_pulled_(false), trigger_pull_time_(0.0), fire_rate_(0.0) {} + +OutOfAmmoState::~OutOfAmmoState() {} - if (!cooldown_) { - cooldown_ = true; - last_attack_time_ = TimeManager::GetInstance().GetCurrentTime(); - std::thread attack_thread(&OutOfAmmoState::NoAttackAnimation, this); - attack_thread.detach(); +void OutOfAmmoState::Update(const double& delta_time) { + if (trigger_pulled_) { + animation_->Update(delta_time); + trigger_pull_time_ += delta_time; + if (trigger_pull_time_ >= fire_rate_) { + trigger_pulled_ = false; + animation_->Reset(); + } } } @@ -125,71 +78,41 @@ WeaponStateType OutOfAmmoState::GetType() const { return WeaponStateType::OutOfAmmo; } -void OutOfAmmoState::NoAttackAnimation() { - auto attack_time = last_attack_time_; - auto current_time = TimeManager::GetInstance().GetCurrentTime(); - while (current_time - last_attack_time_ < animation_speed_ && !interrupt_ && - !destroyed_) { - auto time_elapsed = current_time - attack_time; - animation_->Update(time_elapsed); - attack_time = current_time; - current_time = TimeManager::GetInstance().GetCurrentTime(); - } - if (destroyed_) { - return; - } - if (interrupt_) { - context_->TransitionTo(requested_state_); - } - animation_->Reset(); - cooldown_ = false; -} - void OutOfAmmoState::Reset() { animation_->Reset(); } void OutOfAmmoState::OnContextSet() { - animation_speed_ = context_->GetAttackSpeed(); - auto textures = TextureManager::GetInstance().GetTextureCollection( - weapon_data_.at(weapon_name_).at("outofammo")); - animation_ = std::make_shared( - textures, animation_speed_ / textures.size()); -} - -void OutOfAmmoState::TransitionRequest(std::shared_ptr>& state) { - if (!cooldown_) { - context_->TransitionTo(state); - } - if (interrupt_) { - return; - } - interrupt_ = true; - requested_state_ = state; + fire_rate_ = context_->GetAttackSpeed(); + animation_ = std::make_unique( + context_->GetWeaponName() + "_outofammo", fire_rate_); } int OutOfAmmoState::GetCurrentFrame() const { return animation_->GetCurrentFrame(); } +void OutOfAmmoState::PullTrigger() { + if (trigger_pulled_) { + return; + } + trigger_pulled_ = true; + trigger_pull_time_ = 0; +} + // ########################################### ReloadingState ########################################### -ReloadingState::ReloadingState(const std::string weapon_name) - : weapon_name_(weapon_name), - last_attack_time_(0), - cooldown_(false), - interrupt_(false), - destroyed_(false) {} - -ReloadingState::~ReloadingState() { - destroyed_ = true; -} - -void ReloadingState::Update(const double&) { - if (!cooldown_) { - cooldown_ = true; - last_attack_time_ = TimeManager::GetInstance().GetCurrentTime(); - std::thread attack_thread(&ReloadingState::ReloadAnimation, this); - attack_thread.detach(); +ReloadingState::ReloadingState() : reload_time_(0.0) {} + +ReloadingState::~ReloadingState() {} + +void ReloadingState::Update(const double& delta_time) { + animation_->Update(delta_time); + reload_time_ += delta_time; + if (reload_time_ >= reload_speed_) { + animation_->Reset(); + context_->SetAmmo(context_->GetAmmoCapacity()); + WeaponStatePtr loaded_state = std::make_shared(); + context_->TransitionTo(loaded_state); } } @@ -197,35 +120,14 @@ WeaponStateType ReloadingState::GetType() const { return WeaponStateType::Reloading; } -void ReloadingState::ReloadAnimation() { - auto attack_time = last_attack_time_; - auto current_time = TimeManager::GetInstance().GetCurrentTime(); - while (current_time - last_attack_time_ < animation_speed_ && !interrupt_ && - !destroyed_) { - auto time_elapsed = current_time - attack_time; - animation_->Update(time_elapsed); - attack_time = current_time; - current_time = TimeManager::GetInstance().GetCurrentTime(); - } - if (destroyed_) { - return; - } - context_->Charge(); - std::shared_ptr> loaded_state = - std::make_shared(weapon_name_); - context_->TransitionTo(loaded_state); -} - void ReloadingState::Reset() { animation_->Reset(); } void ReloadingState::OnContextSet() { - animation_speed_ = context_->GetReloadSpeed(); - auto textures = TextureManager::GetInstance().GetTextureCollection( - weapon_data_.at(weapon_name_).at("reload")); - animation_ = std::make_shared( - textures, animation_speed_ / textures.size()); + reload_speed_ = context_->GetReloadSpeed(); + animation_ = std::make_unique( + context_->GetWeaponName() + "_reload", reload_speed_); } int ReloadingState::GetCurrentFrame() const { diff --git a/src/Strike/include/Strike/weapon.h b/src/Strike/include/Strike/weapon.h index ab5de29..6698ab5 100644 --- a/src/Strike/include/Strike/weapon.h +++ b/src/Strike/include/Strike/weapon.h @@ -20,6 +20,15 @@ namespace wolfenstein { struct WeaponConfig { + WeaponConfig(std::string weapon_name, size_t ammo_capacity, + double attack_damage, double attack_range, double attack_speed, + double reload_speed) + : weapon_name(weapon_name), + ammo_capacity(ammo_capacity), + attack_damage(attack_damage), + attack_range(attack_range), + attack_speed(attack_speed), + reload_speed(reload_speed) {} std::string weapon_name; size_t ammo_capacity; double attack_damage; @@ -31,16 +40,17 @@ struct WeaponConfig class Weapon : public IStrike, public std::enable_shared_from_this { public: - enum class StateType { Loaded, OutOfAmmo, Reloading }; Weapon(std::string weapon_name); ~Weapon(); void Init(); void Attack() override; + void Update(double delta_time); void Charge(); void Reload(); void TransitionTo(WeaponStatePtr& state); + void SetAmmo(size_t ammo); void IncreaseAmmo(); void IncreaseAmmo(size_t amount); void DecreaseAmmo(); @@ -58,6 +68,8 @@ class Weapon : public IStrike, public std::enable_shared_from_this WeaponConfig weapon_properties_; size_t ammo_; WeaponStatePtr state_; + bool cooldown_; + double attack_time_; }; } // namespace wolfenstein diff --git a/src/Strike/src/weapon.cpp b/src/Strike/src/weapon.cpp index 77720e2..5f767c2 100644 --- a/src/Strike/src/weapon.cpp +++ b/src/Strike/src/weapon.cpp @@ -1,6 +1,8 @@ #include "Strike/weapon.h" #include "State/weapon_state.h" +#include "TimeManager/time_manager.h" #include +#include #include #include namespace wolfenstein { @@ -17,9 +19,9 @@ auto GetWeaponConfig = [](const std::string& weapon_name) -> WeaponConfig { } // namespace Weapon::Weapon(std::string weapon_name) - : weapon_properties_(GetWeaponConfig(weapon_name)), - ammo_(weapon_properties_.ammo_capacity) { - state_ = std::make_shared(weapon_properties_.weapon_name); + : weapon_properties_(GetWeaponConfig(weapon_name)) { + ammo_ = weapon_properties_.ammo_capacity; + state_ = std::make_shared(); } Weapon::~Weapon() {} @@ -29,7 +31,11 @@ void Weapon::Init() { } void Weapon::Attack() { - state_->Update(0.0); + state_->PullTrigger(); +} + +void Weapon::Update(double delta_time) { + state_->Update(delta_time); } void Weapon::Charge() { @@ -37,9 +43,8 @@ void Weapon::Charge() { } void Weapon::Reload() { - WeaponStatePtr reloading_state = - std::make_shared(weapon_properties_.weapon_name); - state_->TransitionRequest(reloading_state); + WeaponStatePtr reloading_state = std::make_shared(); + TransitionTo(reloading_state); } void Weapon::TransitionTo(WeaponStatePtr& state) { @@ -50,6 +55,10 @@ void Weapon::TransitionTo(WeaponStatePtr& state) { } } +void Weapon::SetAmmo(size_t ammo) { + ammo_ = ammo; +} + void Weapon::IncreaseAmmo() { ammo_++; } diff --git a/src/TextureManager/include/TextureManager/texture_manager.h b/src/TextureManager/include/TextureManager/texture_manager.h index 6919151..4d768a4 100644 --- a/src/TextureManager/include/TextureManager/texture_manager.h +++ b/src/TextureManager/include/TextureManager/texture_manager.h @@ -50,12 +50,13 @@ class TextureManager void LoadSpriteTextures(); void LoadNpcTextures(); void LoadWeaponTextures(); + void FillAscendingIds(std::string key, uint16_t begin, uint16_t end); static TextureManager* instance_; std::unordered_map textures_; std::unordered_map> texture_collections_; SDL_Renderer* renderer_; - size_t texture_count_; + uint16_t t_count_; }; } // namespace wolfenstein diff --git a/src/TextureManager/src/texture_manager.cpp b/src/TextureManager/src/texture_manager.cpp index 5f988bc..02063e9 100644 --- a/src/TextureManager/src/texture_manager.cpp +++ b/src/TextureManager/src/texture_manager.cpp @@ -15,7 +15,7 @@ TextureManager& TextureManager::GetInstance() { void TextureManager::InitManager(SDL_Renderer* renderer) { renderer_ = renderer; - texture_count_ = 0; + t_count_ = 0; LoadStaticTextures(); LoadSpriteTextures(); @@ -44,183 +44,207 @@ std::vector TextureManager::GetTextureCollection( void TextureManager::LoadStaticTextures() { // Static textures std::string texture_path = std::string(RESOURCE_DIR) + "textures/"; - LoadTexture(0, texture_path + "sky.png"); - LoadTexture(1, texture_path + "1.png"); - LoadTexture(2, texture_path + "2.png"); - LoadTexture(3, texture_path + "3.png"); - LoadTexture(4, texture_path + "4.png"); - LoadTexture(5, texture_path + "5.png"); - LoadTexture(6, texture_path + "crosshair.png"); - LoadTexture(7, texture_path + "solid_black.png"); + LoadTexture(t_count_++, texture_path + "sky.png"); + LoadTexture(t_count_++, texture_path + "1.png"); + LoadTexture(t_count_++, texture_path + "2.png"); + LoadTexture(t_count_++, texture_path + "3.png"); + LoadTexture(t_count_++, texture_path + "4.png"); + LoadTexture(t_count_++, texture_path + "5.png"); + LoadTexture(t_count_++, texture_path + "crosshair.png"); + LoadTexture(t_count_++, texture_path + "solid_black.png"); } void TextureManager::LoadSpriteTextures() { // Static sprites std::string sprite_path = std::string(RESOURCE_DIR) + "sprites/"; - LoadTexture(8, sprite_path + "static_sprites/candlebra.png"); + LoadTexture(t_count_++, sprite_path + "static_sprites/candlebra.png"); // Animated sprites - LoadTexture(9, sprite_path + "animated_sprites/green_light/0.png"); - LoadTexture(10, sprite_path + "animated_sprites/green_light/1.png"); - LoadTexture(11, sprite_path + "animated_sprites/green_light/2.png"); - LoadTexture(12, sprite_path + "animated_sprites/green_light/3.png"); - texture_collections_["green_light"] = {9, 10, 11, 12}; - LoadTexture(13, sprite_path + "animated_sprites/red_light/0.png"); - LoadTexture(14, sprite_path + "animated_sprites/red_light/1.png"); - LoadTexture(15, sprite_path + "animated_sprites/red_light/2.png"); - LoadTexture(16, sprite_path + "animated_sprites/red_light/3.png"); - texture_collections_["red_light"] = {13, 14, 15, 16}; + auto begin = t_count_; + LoadTexture(t_count_++, sprite_path + "animated_sprites/green_light/0.png"); + LoadTexture(t_count_++, sprite_path + "animated_sprites/green_light/1.png"); + LoadTexture(t_count_++, sprite_path + "animated_sprites/green_light/2.png"); + LoadTexture(t_count_++, sprite_path + "animated_sprites/green_light/3.png"); + FillAscendingIds("green_light", begin, t_count_); + begin = t_count_; + LoadTexture(t_count_++, sprite_path + "animated_sprites/red_light/0.png"); + LoadTexture(t_count_++, sprite_path + "animated_sprites/red_light/1.png"); + LoadTexture(t_count_++, sprite_path + "animated_sprites/red_light/2.png"); + LoadTexture(t_count_++, sprite_path + "animated_sprites/red_light/3.png"); + FillAscendingIds("red_light", begin, t_count_); } void TextureManager::LoadNpcTextures() { // Npc sprites std::string npc_path = std::string(RESOURCE_DIR) + "sprites/npc/"; // Caco demon Attack - LoadTexture(17, npc_path + "caco_demon/attack/0.png"); - LoadTexture(18, npc_path + "caco_demon/attack/1.png"); - LoadTexture(19, npc_path + "caco_demon/attack/2.png"); - LoadTexture(20, npc_path + "caco_demon/attack/3.png"); - LoadTexture(21, npc_path + "caco_demon/attack/4.png"); - texture_collections_["caco_demon_attack"] = {17, 18, 19, 20, 21}; + auto begin = t_count_; + LoadTexture(t_count_++, npc_path + "caco_demon/attack/0.png"); + LoadTexture(t_count_++, npc_path + "caco_demon/attack/1.png"); + LoadTexture(t_count_++, npc_path + "caco_demon/attack/2.png"); + LoadTexture(t_count_++, npc_path + "caco_demon/attack/3.png"); + LoadTexture(t_count_++, npc_path + "caco_demon/attack/4.png"); + FillAscendingIds("caco_demon_attack", begin, t_count_); // Caco demon Death - LoadTexture(22, npc_path + "caco_demon/death/0.png"); - LoadTexture(23, npc_path + "caco_demon/death/1.png"); - LoadTexture(24, npc_path + "caco_demon/death/2.png"); - LoadTexture(25, npc_path + "caco_demon/death/3.png"); - LoadTexture(26, npc_path + "caco_demon/death/4.png"); - LoadTexture(27, npc_path + "caco_demon/death/5.png"); - texture_collections_["caco_demon_death"] = {22, 23, 24, 25, 26, 27}; + begin = t_count_; + LoadTexture(t_count_++, npc_path + "caco_demon/death/0.png"); + LoadTexture(t_count_++, npc_path + "caco_demon/death/1.png"); + LoadTexture(t_count_++, npc_path + "caco_demon/death/2.png"); + LoadTexture(t_count_++, npc_path + "caco_demon/death/3.png"); + LoadTexture(t_count_++, npc_path + "caco_demon/death/4.png"); + LoadTexture(t_count_++, npc_path + "caco_demon/death/5.png"); + FillAscendingIds("caco_demon_death", begin, t_count_); // Caco demon Idle - LoadTexture(28, npc_path + "caco_demon/idle/0.png"); - LoadTexture(29, npc_path + "caco_demon/idle/1.png"); - LoadTexture(30, npc_path + "caco_demon/idle/2.png"); - LoadTexture(31, npc_path + "caco_demon/idle/3.png"); - LoadTexture(32, npc_path + "caco_demon/idle/4.png"); - LoadTexture(33, npc_path + "caco_demon/idle/5.png"); - LoadTexture(34, npc_path + "caco_demon/idle/6.png"); - LoadTexture(35, npc_path + "caco_demon/idle/7.png"); - texture_collections_["caco_demon_idle"] = {28, 29, 30, 31, 32, 33, 34, 35}; + begin = t_count_; + LoadTexture(t_count_++, npc_path + "caco_demon/idle/0.png"); + LoadTexture(t_count_++, npc_path + "caco_demon/idle/1.png"); + LoadTexture(t_count_++, npc_path + "caco_demon/idle/2.png"); + LoadTexture(t_count_++, npc_path + "caco_demon/idle/3.png"); + LoadTexture(t_count_++, npc_path + "caco_demon/idle/4.png"); + LoadTexture(t_count_++, npc_path + "caco_demon/idle/5.png"); + LoadTexture(t_count_++, npc_path + "caco_demon/idle/6.png"); + LoadTexture(t_count_++, npc_path + "caco_demon/idle/7.png"); + FillAscendingIds("caco_demon_idle", begin, t_count_); // Caco demon Pain - LoadTexture(36, npc_path + "caco_demon/pain/0.png"); - LoadTexture(37, npc_path + "caco_demon/pain/1.png"); - texture_collections_["caco_demon_pain"] = {36, 37}; + begin = t_count_; + LoadTexture(t_count_++, npc_path + "caco_demon/pain/0.png"); + LoadTexture(t_count_++, npc_path + "caco_demon/pain/1.png"); + FillAscendingIds("caco_demon_pain", begin, t_count_); // Caco demon Walk - LoadTexture(38, npc_path + "caco_demon/walk/0.png"); - LoadTexture(39, npc_path + "caco_demon/walk/1.png"); - LoadTexture(40, npc_path + "caco_demon/walk/2.png"); - texture_collections_["caco_demon_walk"] = {38, 39, 40}; - + begin = t_count_; + LoadTexture(t_count_++, npc_path + "caco_demon/walk/0.png"); + LoadTexture(t_count_++, npc_path + "caco_demon/walk/1.png"); + LoadTexture(t_count_++, npc_path + "caco_demon/walk/2.png"); + FillAscendingIds("caco_demon_walk", begin, t_count_); // Cyber demon Attack - LoadTexture(41, npc_path + "cyber_demon/attack/0.png"); - LoadTexture(42, npc_path + "cyber_demon/attack/1.png"); - texture_collections_["cyber_demon_attack"] = {41, 42}; + begin = t_count_; + LoadTexture(t_count_++, npc_path + "cyber_demon/attack/0.png"); + LoadTexture(t_count_++, npc_path + "cyber_demon/attack/1.png"); + FillAscendingIds("cyber_demon_attack", begin, t_count_); // Cyber demon Death - LoadTexture(43, npc_path + "cyber_demon/death/0.png"); - LoadTexture(44, npc_path + "cyber_demon/death/1.png"); - LoadTexture(45, npc_path + "cyber_demon/death/2.png"); - LoadTexture(46, npc_path + "cyber_demon/death/3.png"); - LoadTexture(47, npc_path + "cyber_demon/death/4.png"); - LoadTexture(48, npc_path + "cyber_demon/death/5.png"); - LoadTexture(49, npc_path + "cyber_demon/death/6.png"); - LoadTexture(50, npc_path + "cyber_demon/death/7.png"); - LoadTexture(51, npc_path + "cyber_demon/death/8.png"); - texture_collections_["cyber_demon_death"] = {43, 44, 45, 46, 47, - 48, 49, 50, 51}; + begin = t_count_; + LoadTexture(t_count_++, npc_path + "cyber_demon/death/0.png"); + LoadTexture(t_count_++, npc_path + "cyber_demon/death/1.png"); + LoadTexture(t_count_++, npc_path + "cyber_demon/death/2.png"); + LoadTexture(t_count_++, npc_path + "cyber_demon/death/3.png"); + LoadTexture(t_count_++, npc_path + "cyber_demon/death/4.png"); + LoadTexture(t_count_++, npc_path + "cyber_demon/death/5.png"); + LoadTexture(t_count_++, npc_path + "cyber_demon/death/6.png"); + LoadTexture(t_count_++, npc_path + "cyber_demon/death/7.png"); + LoadTexture(t_count_++, npc_path + "cyber_demon/death/8.png"); + FillAscendingIds("cyber_demon_death", begin, t_count_); // Cyber demon Idle - LoadTexture(52, npc_path + "cyber_demon/idle/0.png"); - LoadTexture(53, npc_path + "cyber_demon/idle/1.png"); - LoadTexture(54, npc_path + "cyber_demon/idle/2.png"); - LoadTexture(55, npc_path + "cyber_demon/idle/3.png"); - LoadTexture(56, npc_path + "cyber_demon/idle/4.png"); - LoadTexture(57, npc_path + "cyber_demon/idle/5.png"); - LoadTexture(58, npc_path + "cyber_demon/idle/6.png"); - LoadTexture(59, npc_path + "cyber_demon/idle/7.png"); - texture_collections_["cyber_demon_idle"] = {52, 53, 54, 55, 56, 57, 58, 59}; + begin = t_count_; + LoadTexture(t_count_++, npc_path + "cyber_demon/idle/0.png"); + LoadTexture(t_count_++, npc_path + "cyber_demon/idle/1.png"); + LoadTexture(t_count_++, npc_path + "cyber_demon/idle/2.png"); + LoadTexture(t_count_++, npc_path + "cyber_demon/idle/3.png"); + LoadTexture(t_count_++, npc_path + "cyber_demon/idle/4.png"); + LoadTexture(t_count_++, npc_path + "cyber_demon/idle/5.png"); + LoadTexture(t_count_++, npc_path + "cyber_demon/idle/6.png"); + LoadTexture(t_count_++, npc_path + "cyber_demon/idle/7.png"); + FillAscendingIds("cyber_demon_idle", begin, t_count_); // Cyber demon Pain - LoadTexture(60, npc_path + "cyber_demon/pain/0.png"); - LoadTexture(61, npc_path + "cyber_demon/pain/1.png"); - texture_collections_["cyber_demon_pain"] = {60, 61}; + begin = t_count_; + LoadTexture(t_count_++, npc_path + "cyber_demon/pain/0.png"); + LoadTexture(t_count_++, npc_path + "cyber_demon/pain/1.png"); + FillAscendingIds("cyber_demon_pain", begin, t_count_); // Cyber demon Walk - LoadTexture(62, npc_path + "cyber_demon/walk/0.png"); - LoadTexture(63, npc_path + "cyber_demon/walk/1.png"); - LoadTexture(64, npc_path + "cyber_demon/walk/3.png"); - LoadTexture(65, npc_path + "cyber_demon/walk/4.png"); - texture_collections_["cyber_demon_walk"] = {62, 63, 64, 65}; + begin = t_count_; + LoadTexture(t_count_++, npc_path + "cyber_demon/walk/0.png"); + LoadTexture(t_count_++, npc_path + "cyber_demon/walk/1.png"); + LoadTexture(t_count_++, npc_path + "cyber_demon/walk/3.png"); + LoadTexture(t_count_++, npc_path + "cyber_demon/walk/4.png"); + FillAscendingIds("cyber_demon_walk", begin, t_count_); // Soldier Attack - LoadTexture(66, npc_path + "soldier/attack/0.png"); - LoadTexture(67, npc_path + "soldier/attack/1.png"); - texture_collections_["soldier_attack"] = {66, 67}; + begin = t_count_; + LoadTexture(t_count_++, npc_path + "soldier/attack/0.png"); + LoadTexture(t_count_++, npc_path + "soldier/attack/1.png"); + FillAscendingIds("soldier_attack", begin, t_count_); // Soldier Death - LoadTexture(68, npc_path + "soldier/death/0.png"); - LoadTexture(69, npc_path + "soldier/death/1.png"); - LoadTexture(70, npc_path + "soldier/death/2.png"); - LoadTexture(71, npc_path + "soldier/death/3.png"); - LoadTexture(72, npc_path + "soldier/death/4.png"); - LoadTexture(73, npc_path + "soldier/death/5.png"); - LoadTexture(74, npc_path + "soldier/death/6.png"); - LoadTexture(75, npc_path + "soldier/death/7.png"); - LoadTexture(76, npc_path + "soldier/death/8.png"); - texture_collections_["soldier_death"] = {68, 69, 70, 71, 72, - 73, 74, 75, 76}; + begin = t_count_; + LoadTexture(t_count_++, npc_path + "soldier/death/0.png"); + LoadTexture(t_count_++, npc_path + "soldier/death/1.png"); + LoadTexture(t_count_++, npc_path + "soldier/death/2.png"); + LoadTexture(t_count_++, npc_path + "soldier/death/3.png"); + LoadTexture(t_count_++, npc_path + "soldier/death/4.png"); + LoadTexture(t_count_++, npc_path + "soldier/death/5.png"); + LoadTexture(t_count_++, npc_path + "soldier/death/6.png"); + LoadTexture(t_count_++, npc_path + "soldier/death/7.png"); + LoadTexture(t_count_++, npc_path + "soldier/death/8.png"); + FillAscendingIds("soldier_death", begin, t_count_); // Soldier Idle - LoadTexture(77, npc_path + "soldier/idle/0.png"); - LoadTexture(78, npc_path + "soldier/idle/1.png"); - LoadTexture(79, npc_path + "soldier/idle/2.png"); - LoadTexture(80, npc_path + "soldier/idle/3.png"); - LoadTexture(81, npc_path + "soldier/idle/4.png"); - LoadTexture(82, npc_path + "soldier/idle/5.png"); - LoadTexture(83, npc_path + "soldier/idle/6.png"); - LoadTexture(84, npc_path + "soldier/idle/7.png"); - texture_collections_["soldier_idle"] = {77, 78, 79, 80, 81, 82, 83, 84}; + begin = t_count_; + LoadTexture(t_count_++, npc_path + "soldier/idle/0.png"); + LoadTexture(t_count_++, npc_path + "soldier/idle/1.png"); + LoadTexture(t_count_++, npc_path + "soldier/idle/2.png"); + LoadTexture(t_count_++, npc_path + "soldier/idle/3.png"); + LoadTexture(t_count_++, npc_path + "soldier/idle/4.png"); + LoadTexture(t_count_++, npc_path + "soldier/idle/5.png"); + LoadTexture(t_count_++, npc_path + "soldier/idle/6.png"); + LoadTexture(t_count_++, npc_path + "soldier/idle/7.png"); + FillAscendingIds("soldier_idle", begin, t_count_); // Soldier Pain - LoadTexture(85, npc_path + "soldier/pain/0.png"); - texture_collections_["soldier_pain"] = {85}; + begin = t_count_; + LoadTexture(t_count_++, npc_path + "soldier/pain/0.png"); + FillAscendingIds("soldier_pain", begin, t_count_); // Soldier Walk - LoadTexture(86, npc_path + "soldier/walk/0.png"); - LoadTexture(87, npc_path + "soldier/walk/1.png"); - LoadTexture(88, npc_path + "soldier/walk/2.png"); - LoadTexture(89, npc_path + "soldier/walk/3.png"); - texture_collections_["soldier_walk"] = {86, 87, 88, 89}; + begin = t_count_; + LoadTexture(t_count_++, npc_path + "soldier/walk/0.png"); + LoadTexture(t_count_++, npc_path + "soldier/walk/1.png"); + LoadTexture(t_count_++, npc_path + "soldier/walk/2.png"); + LoadTexture(t_count_++, npc_path + "soldier/walk/3.png"); + FillAscendingIds("soldier_walk", begin, t_count_); } void TextureManager::LoadWeaponTextures() { const auto weapon_path = std::string(RESOURCE_DIR) + "sprites/weapon/"; // MP5 sprites // Loaded - LoadTexture(90, weapon_path + "/mp5/loaded/" + "0.png"); - LoadTexture(91, weapon_path + "/mp5/loaded/" + "1.png"); - LoadTexture(92, weapon_path + "/mp5/loaded/" + "2.png"); - LoadTexture(93, weapon_path + "/mp5/loaded/" + "3.png"); - LoadTexture(94, weapon_path + "/mp5/loaded/" + "4.png"); - LoadTexture(95, weapon_path + "/mp5/loaded/" + "5.png"); - texture_collections_["mp5_loaded"] = {90, 91, 92, 93, 94, 95}; + auto begin = t_count_; + LoadTexture(t_count_++, weapon_path + "/mp5/loaded/" + "0.png"); + LoadTexture(t_count_++, weapon_path + "/mp5/loaded/" + "1.png"); + LoadTexture(t_count_++, weapon_path + "/mp5/loaded/" + "2.png"); + LoadTexture(t_count_++, weapon_path + "/mp5/loaded/" + "3.png"); + LoadTexture(t_count_++, weapon_path + "/mp5/loaded/" + "4.png"); + LoadTexture(t_count_++, weapon_path + "/mp5/loaded/" + "5.png"); + FillAscendingIds("mp5_loaded", begin, t_count_); // outofammo - LoadTexture(96, weapon_path + "/mp5/outofammo/" + "0.png"); - LoadTexture(97, weapon_path + "/mp5/outofammo/" + "1.png"); - LoadTexture(98, weapon_path + "/mp5/outofammo/" + "2.png"); - LoadTexture(99, weapon_path + "/mp5/outofammo/" + "3.png"); - texture_collections_["mp5_outofammo"] = {96, 97, 98, 99}; + begin = t_count_; + LoadTexture(t_count_++, weapon_path + "/mp5/outofammo/" + "0.png"); + LoadTexture(t_count_++, weapon_path + "/mp5/outofammo/" + "1.png"); + LoadTexture(t_count_++, weapon_path + "/mp5/outofammo/" + "2.png"); + LoadTexture(t_count_++, weapon_path + "/mp5/outofammo/" + "3.png"); + FillAscendingIds("mp5_outofammo", begin, t_count_); // reload - LoadTexture(100, weapon_path + "/mp5/reload/" + "0.png"); - LoadTexture(101, weapon_path + "/mp5/reload/" + "1.png"); - LoadTexture(102, weapon_path + "/mp5/reload/" + "2.png"); - LoadTexture(103, weapon_path + "/mp5/reload/" + "3.png"); - LoadTexture(104, weapon_path + "/mp5/reload/" + "4.png"); - LoadTexture(105, weapon_path + "/mp5/reload/" + "5.png"); - LoadTexture(106, weapon_path + "/mp5/reload/" + "6.png"); - LoadTexture(107, weapon_path + "/mp5/reload/" + "7.png"); - LoadTexture(108, weapon_path + "/mp5/reload/" + "8.png"); - LoadTexture(109, weapon_path + "/mp5/reload/" + "9.png"); - LoadTexture(110, weapon_path + "/mp5/reload/" + "10.png"); - LoadTexture(111, weapon_path + "/mp5/reload/" + "11.png"); - LoadTexture(112, weapon_path + "/mp5/reload/" + "12.png"); - LoadTexture(113, weapon_path + "/mp5/reload/" + "13.png"); - LoadTexture(114, weapon_path + "/mp5/reload/" + "14.png"); - LoadTexture(115, weapon_path + "/mp5/reload/" + "15.png"); - texture_collections_["mp5_reload"] = {100, 101, 102, 103, 104, 105, - 106, 107, 108, 109, 110, 111, - 112, 113, 114, 115}; + begin = t_count_; + LoadTexture(t_count_++, weapon_path + "/mp5/reload/" + "0.png"); + LoadTexture(t_count_++, weapon_path + "/mp5/reload/" + "1.png"); + LoadTexture(t_count_++, weapon_path + "/mp5/reload/" + "2.png"); + LoadTexture(t_count_++, weapon_path + "/mp5/reload/" + "3.png"); + LoadTexture(t_count_++, weapon_path + "/mp5/reload/" + "4.png"); + LoadTexture(t_count_++, weapon_path + "/mp5/reload/" + "5.png"); + LoadTexture(t_count_++, weapon_path + "/mp5/reload/" + "6.png"); + LoadTexture(t_count_++, weapon_path + "/mp5/reload/" + "7.png"); + LoadTexture(t_count_++, weapon_path + "/mp5/reload/" + "8.png"); + LoadTexture(t_count_++, weapon_path + "/mp5/reload/" + "9.png"); + LoadTexture(t_count_++, weapon_path + "/mp5/reload/" + "10.png"); + LoadTexture(t_count_++, weapon_path + "/mp5/reload/" + "11.png"); + LoadTexture(t_count_++, weapon_path + "/mp5/reload/" + "12.png"); + LoadTexture(t_count_++, weapon_path + "/mp5/reload/" + "13.png"); + LoadTexture(t_count_++, weapon_path + "/mp5/reload/" + "14.png"); + LoadTexture(t_count_++, weapon_path + "/mp5/reload/" + "15.png"); + FillAscendingIds("mp5_reload", begin, t_count_); +} + +void TextureManager::FillAscendingIds(std::string key, uint16_t begin, + uint16_t end) { + std::vector textures; + for (uint16_t i = begin; i < end; i++) { + textures.push_back(i); + } + texture_collections_[key] = textures; } } // namespace wolfenstein