Skip to content

Commit 9eb333a

Browse files
authored
Add Enemy health bars. (#30)
* Balance weapon damage a bit. * Refactor progress bar. Use rounded corners for background, shrinking quad as progress. * Data drive creep initial health, set to 2. * Add enemy health bars. * Use orange RGBA for enemy health bars. * Remove test code.
1 parent 325b770 commit 9eb333a

16 files changed

+80
-45
lines changed

assets/styles.json

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,16 @@
3131
},
3232
"progress_bars": {
3333
"default": {
34-
"background": "#ffffffff",
35-
"fill": "#9f2b68ff",
36-
"outline": "#702963ff",
37-
"outline_width": 5
34+
"background": "#9f2b68ff",
35+
"fill": "#ffffffff",
36+
"corner_ratio": 0.5,
37+
"padding": 10
38+
},
39+
"enemy": {
40+
"background": "#f75c03ff",
41+
"fill": "#ffffffff",
42+
"corner_ratio": 0.5,
43+
"padding": 5
3844
}
3945
},
4046
"loading_screen": {
@@ -50,7 +56,7 @@
5056
"n_width": 0.800000,
5157
"height": 50.000000,
5258
"bottom_offset": 300.000000,
53-
"outline_width": 10
59+
"padding": 20
5460
}
5561
}
56-
}
62+
}

assets/worlds/playground.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"milk"
1414
],
1515
"spawn_rate": 2,
16+
"initial_health": 2,
1617
"death_emitter": "particles/explode.json"
1718
}
1819
]

src/spaced/spaced/game/enemies/basic_creep_factory.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ BasicCreepFactory::BasicCreepFactory(NotNull<Services const*> services, NotNull<
1717
for (auto const& tint : json["tints"].array_view()) { tints.push_back(tint.as<std::string>()); }
1818
if (auto const in_death_emitter = services->get<Resources>().get<ParticleEmitter>(json["death_emitter"].as_string())) { death_emitter = *in_death_emitter; }
1919
spawn_rate = Seconds{json["spawn_rate"].as<float>(spawn_rate.count())};
20+
initial_health = json["initial_health"].as<float>(initial_health);
2021
}
2122

2223
auto BasicCreepFactory::spawn_enemy() -> std::unique_ptr<Enemy> {
@@ -25,6 +26,7 @@ auto BasicCreepFactory::spawn_enemy() -> std::unique_ptr<Enemy> {
2526
auto const& rgbas = m_services->get<Styles>().rgbas;
2627
auto const tint_index = random_in_range(std::size_t{}, tints.size() - 1);
2728
ret->shape.tint = rgbas[tints.at(tint_index)];
29+
ret->health = initial_health;
2830
}
2931
return ret;
3032
}

src/spaced/spaced/game/enemies/basic_creep_factory.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class BasicCreepFactory : public IEnemyFactory {
1818
std::vector<std::string> tints{};
1919
bave::ParticleEmitter death_emitter{};
2020
bave::Seconds spawn_rate{2s};
21+
float initial_health{1.0f};
2122

2223
private:
2324
void do_inspect() final;

src/spaced/spaced/game/enemies/creep.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ namespace spaced {
44
using bave::Seconds;
55

66
void Creep::tick(Seconds const dt) {
7+
Enemy::tick(dt);
78
shape.transform.position.x -= x_speed * dt.count();
89
if (shape.transform.position.x < -0.5f * (get_layout().get_world_space().x + shape.get_shape().size.x)) { set_destroyed(); }
910
}

src/spaced/spaced/game/enemy.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,25 @@
11
#include <bave/core/random.hpp>
22
#include <bave/imgui/im_text.hpp>
33
#include <spaced/game/enemy.hpp>
4+
#include <spaced/services/services.hpp>
5+
#include <spaced/services/styles.hpp>
46

57
namespace spaced {
68
using bave::im_text;
79
using bave::random_in_range;
810
using bave::RoundedQuad;
11+
using bave::Seconds;
12+
using bave::Shader;
913

1014
Enemy::Enemy(Services const& services, bave::NotNull<IEnemyDeathListener*> listener, std::string_view const type)
11-
: m_layout(&services.get<ILayout>()), m_listener(listener), m_type(type) {
15+
: health_bar(services), m_layout(&services.get<ILayout>()), m_listener(listener), m_type(type) {
1216
static constexpr auto init_size_v = glm::vec2{100.0f};
1317
auto const play_area = m_layout->get_play_area();
1418
auto const y_min = play_area.rb.y + 0.5f * init_size_v.y;
1519
auto const y_max = play_area.lt.y - 0.5f * init_size_v.y;
1620
setup(init_size_v, random_in_range(y_min, y_max));
21+
22+
health_bar.set_style(services.get<Styles>().progress_bars["enemy"]);
1723
}
1824

1925
auto Enemy::take_damage(float const damage) -> bool {
@@ -23,6 +29,19 @@ auto Enemy::take_damage(float const damage) -> bool {
2329
return true;
2430
}
2531

32+
void Enemy::tick(Seconds const dt) {
33+
health_bar.position = shape.transform.position;
34+
health_bar.position.y += 0.5f * shape.get_shape().size.y + 20.0f;
35+
health_bar.size = {shape.get_shape().size.x, 10.0f};
36+
health_bar.set_progress(health.get_hit_points() / health.get_total_hit_points());
37+
health_bar.tick(dt);
38+
}
39+
40+
void Enemy::draw(Shader& shader) const {
41+
shape.draw(shader);
42+
health_bar.draw(shader);
43+
}
44+
2645
void Enemy::setup(glm::vec2 max_size, float y_position) {
2746
auto rounded_quad = RoundedQuad{};
2847
rounded_quad.size = max_size;

src/spaced/spaced/game/enemy.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <spaced/game/health.hpp>
88
#include <spaced/services/layout.hpp>
99
#include <spaced/services/services.hpp>
10+
#include <spaced/ui/progress_bar.hpp>
1011

1112
namespace spaced {
1213
class Enemy : public IDamageable, public bave::IDrawable {
@@ -20,8 +21,8 @@ class Enemy : public IDamageable, public bave::IDrawable {
2021
[[nodiscard]] auto is_destroyed() const -> bool { return is_dead() || m_destroyed; }
2122
void set_destroyed() { m_destroyed = true; }
2223

23-
virtual void tick(bave::Seconds dt) = 0;
24-
void draw(bave::Shader& shader) const override { shape.draw(shader); }
24+
virtual void tick(bave::Seconds dt);
25+
void draw(bave::Shader& shader) const override;
2526

2627
void setup(glm::vec2 max_size, float y_position);
2728

@@ -33,6 +34,7 @@ class Enemy : public IDamageable, public bave::IDrawable {
3334
}
3435

3536
bave::RoundedQuadShape shape{};
37+
ui::ProgressBar health_bar;
3638
Health health{};
3739
std::int64_t points{10};
3840

src/spaced/spaced/game/health.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,21 @@
55
#include <algorithm>
66

77
namespace spaced {
8-
void Health::inflict_damage(float const amount) { set_hit_points(m_hit_points - amount); }
8+
void Health::inflict_damage(float const amount) { set_hit_points(m_current_hp - amount); }
99

10-
bool Health::is_dead() const { return m_hit_points <= 0.f; }
10+
bool Health::is_dead() const { return m_current_hp <= 0.f; }
1111

12-
float Health::get_hit_points() const { return m_hit_points; }
12+
float Health::get_hit_points() const { return m_current_hp; }
1313

14-
void Health::set_hit_points(float const hit_points) { m_hit_points = std::max(hit_points, 0.0f); }
14+
void Health::set_hit_points(float const hit_points) { m_current_hp = std::max(hit_points, 0.0f); }
1515

1616
void Health::do_inspect() {
1717
if constexpr (bave::imgui_v) {
1818
if (ImGui::TreeNode("health")) {
19-
bave::im_text("health: {:.2f}", m_hit_points);
19+
bave::im_text("health: {:.2f}", m_current_hp);
2020
bave::im_text("dead: {}", is_dead() ? "yes" : "no");
2121
if (ImGui::Button("inflict 10 damage")) { inflict_damage(10.f); }
22-
ImGui::SliderFloat("health", &m_hit_points, 0.f, 100.f, "%.2f");
22+
ImGui::SliderFloat("health", &m_current_hp, 0.f, 100.f, "%.2f");
2323
ImGui::TreePop();
2424
}
2525
}

src/spaced/spaced/game/health.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@ class Health {
66
public:
77
static constexpr auto hit_points_v{1.0f};
88

9-
/*implicit*/ Health(float const hit_points = hit_points_v) : m_hit_points(hit_points) {}
9+
/*implicit*/ Health(float const hit_points = hit_points_v) : m_total_hp(hit_points), m_current_hp(hit_points) {}
1010

1111
void inflict_damage(float amount);
1212
[[nodiscard]] bool is_dead() const;
1313
[[nodiscard]] float get_hit_points() const;
14+
[[nodiscard]] float get_total_hit_points() const { return m_total_hp; }
1415
void set_hit_points(float hit_points);
1516

1617
operator float() const { return get_hit_points(); }
@@ -22,6 +23,7 @@ class Health {
2223
private:
2324
void do_inspect();
2425

25-
float m_hit_points{};
26+
float m_total_hp{};
27+
float m_current_hp{};
2628
};
2729
} // namespace spaced

src/spaced/spaced/game/weapons/gun_beam.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class GunBeam final : public Weapon {
99
bave::Seconds fire_duration{2s};
1010
bave::Seconds reload_delay{1s};
1111
bave::Rgba beam_tint{bave::red_v};
12-
float dps{1.0f};
12+
float dps{10.0f};
1313
};
1414

1515
explicit GunBeam(Services const& services);

0 commit comments

Comments
 (0)