diff --git a/assets/fonts/specialElite.ttf b/assets/fonts/specialElite.ttf new file mode 100644 index 0000000..ba58d36 Binary files /dev/null and b/assets/fonts/specialElite.ttf differ diff --git a/src/EnemyColliderInfo.hpp b/src/EnemyColliderInfo.hpp new file mode 100644 index 0000000..7d06d9c --- /dev/null +++ b/src/EnemyColliderInfo.hpp @@ -0,0 +1,9 @@ +#pragma once +#include + +namespace miracle { +struct CollisionParams { + glm::vec2 pos; + float diameter; +}; +} // namespace miracle diff --git a/src/enemy.cpp b/src/enemy.cpp index 5f5f757..8f4d28e 100644 --- a/src/enemy.cpp +++ b/src/enemy.cpp @@ -17,7 +17,9 @@ Enemy::Enemy(gsl::not_null services, EnemyParams cons // TODO: add proper textures } -void Enemy::render(le::Renderer& renderer) const { m_sprite.draw(renderer); } +void Enemy::render(le::Renderer& renderer) const { + if (m_can_render) { m_sprite.draw(renderer); } +} void Enemy::translate(kvf::Seconds const dt) { glm::vec2 const direction = glm::normalize(m_target_pos - m_sprite.transform.position); @@ -25,8 +27,10 @@ void Enemy::translate(kvf::Seconds const dt) { m_sprite.transform.position += movement; } -void Enemy::check_collision(glm::vec2 pos, float radius) { - if (glm::distance(pos, m_sprite.transform.position) < radius + m_diameter / 2) { m_health = 0; } +CollisionParams Enemy::get_collision_params() const { return {.pos = m_sprite.transform.position, .diameter = m_diameter}; } +void Enemy::take_damage(std::size_t dmg) { + m_can_render = true; + m_health = (dmg >= m_health) ? 0 : (m_health - dmg); } } // namespace miracle diff --git a/src/enemy.hpp b/src/enemy.hpp index ab6da0e..b3a10ad 100644 --- a/src/enemy.hpp +++ b/src/enemy.hpp @@ -6,7 +6,9 @@ #include #include #include +#include #include +#include "EnemyColliderInfo.hpp" #include "enemy_params.hpp" #include "glm/vec2.hpp" #include "le2d/texture.hpp" @@ -18,9 +20,10 @@ class Enemy { void render(le::Renderer& renderer) const; void translate(kvf::Seconds dt); - // There are temporary parameters, will take the light beam object once it is created - void check_collision(glm::vec2 pos, float radius); + void take_damage(std::size_t dmg); [[nodiscard]] std::size_t get_health() const { return m_health; } + [[nodiscard]] CollisionParams get_collision_params() const; + bool m_can_render{false}; private: gsl::not_null m_services; diff --git a/src/game.cpp b/src/game.cpp index fc79e5e..401b402 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -2,17 +2,26 @@ #include #include #include +#include +#include +#include #include #include "enemy.hpp" #include "enemy_params.hpp" #include "kvf/time.hpp" +#include "le2d/asset_loader.hpp" +#include "le2d/data_loader.hpp" +#include "le2d/drawable/text.hpp" #include "lighhouse.hpp" #include "util/random.hpp" namespace miracle { -Game::Game(gsl::not_null services) : m_services(services), m_lighthouse(services) { - m_circle.create(70.0f); +Game::Game(gsl::not_null services) : m_services(services), m_lighthouse(services), m_light(services) { spawn_wave(); + auto const& data_loader = services->get(); + auto const& context = services->get(); + auto const asset_loader = le::AssetLoader{&data_loader, &context}; + m_font = asset_loader.load_font("fonts/specialElite.ttf"); } void Game::on_cursor_pos(le::event::CursorPos const& cursor_pos) { @@ -28,18 +37,21 @@ void Game::tick([[maybe_unused]] kvf::Seconds const dt) { m_time_since_last_wave_spawn = kvf::Seconds{}; } for (auto& enemy : m_enemies) { - enemy.check_collision(m_circle.transform.position, 50.0f); + m_light.check_enemy_collision(enemy); enemy.translate(dt); } - std::erase_if(m_enemies, [](Enemy const& enemy) { return !enemy.get_health(); }); - m_circle.transform.position = m_cursor_pos; + // Keep track of how many enemies were defeated and calculate score + auto res = std::erase_if(m_enemies, [](Enemy const& enemy) { return !enemy.get_health(); }); + update_score(static_cast(res * 10)); + m_light.set_position(m_cursor_pos); m_lighthouse.rotate_towards_cursor(m_cursor_pos); } void Game::render(le::Renderer& renderer) const { - m_circle.draw(renderer); + m_light.render(renderer); m_lighthouse.render(renderer); for (auto const& enemy : m_enemies) { enemy.render(renderer); } + m_score_text.draw(renderer); } void Game::spawn_wave() { @@ -53,4 +65,13 @@ void Game::spawn_wave() { } m_enemies.insert(m_enemies.end(), std::make_move_iterator(new_wave.begin()), std::make_move_iterator(new_wave.end())); } + +void Game::update_score(int points) { + auto const framebuffer_size = m_services->get().framebuffer_size(); + m_score_text.transform.position.y = static_cast(framebuffer_size.y) / 2.0f - 50.0f; + m_score += points; + m_score_str.clear(); + std::format_to(std::back_inserter(m_score_str), "Score: {}", m_score); + m_score_text.set_string(m_font, m_score_str); +} } // namespace miracle diff --git a/src/game.hpp b/src/game.hpp index 19c0b2d..ddb4749 100644 --- a/src/game.hpp +++ b/src/game.hpp @@ -6,7 +6,10 @@ #include #include #include "enemy.hpp" +#include "le2d/drawable/text.hpp" +#include "le2d/font.hpp" #include "lighhouse.hpp" +#include "light.hpp" namespace miracle { class Game { @@ -17,13 +20,19 @@ class Game { void tick(kvf::Seconds dt); void render(le::Renderer& renderer) const; + void update_score(int points); void spawn_wave(); private: gsl::not_null m_services; - - le::drawable::Circle m_circle{}; Lighthouse m_lighthouse; + Light m_light; + + le::Font m_font{}; + le::drawable::Text m_score_text{}; + int m_score{}; + std::string m_score_str; + glm::vec2 m_cursor_pos{}; std::size_t m_wave_count{}; bool m_running{true}; diff --git a/src/light.cpp b/src/light.cpp new file mode 100644 index 0000000..5866311 --- /dev/null +++ b/src/light.cpp @@ -0,0 +1,17 @@ +#include +#include "kvf/color.hpp" + +namespace miracle { +Light::Light(gsl::not_null services) : m_services(services), m_diameter(100) { m_sprite.create(150.0f, kvf::white_v); } + +void Light::check_enemy_collision(Enemy& enemy) { + auto const [pos, diameter] = enemy.get_collision_params(); + if (glm::distance(pos, m_sprite.transform.position) < (diameter + m_diameter) / 2) { + enemy.take_damage(1); + } else { + enemy.m_can_render = false; + } +} +void Light::render(le::Renderer& renderer) const { m_sprite.draw(renderer); } +void Light::set_position(glm::vec2 cursor_pos) { m_sprite.transform.position = cursor_pos; } +} // namespace miracle diff --git a/src/light.hpp b/src/light.hpp new file mode 100644 index 0000000..5b0a1f4 --- /dev/null +++ b/src/light.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include "enemy.hpp" +#include "glm/vec2.hpp" +#include "gsl/pointers" +#include "le2d/drawable/shape.hpp" +#include "le2d/renderer.hpp" +#include "le2d/service_locator.hpp" +#include "le2d/texture.hpp" + +namespace miracle { +class Light { + public: + explicit Light(gsl::not_null services); + + void render(le::Renderer& renderer) const; + void set_position(glm::vec2 cursor_pos); + void check_enemy_collision(Enemy& enemy); + + private: + gsl::not_null m_services; + std::optional m_texture; + le::drawable::Circle m_sprite{}; + float m_diameter{}; +}; +} // namespace miracle