Skip to content

Fixes, balance, polish. #53

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jul 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
name: ci
on: [push]
on:
push:
branches:
- '*'
jobs:
format-check:
runs-on: ubuntu-latest
Expand All @@ -10,17 +13,15 @@ jobs:
- name: check diff
run: .github/format_check_diff.sh
build-linux:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: init
run: |
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt update -yqq && sudo apt install -yqq ninja-build xorg-dev g++-13
run: sudo apt update -yqq && sudo apt install -yqq ninja-build xorg-dev
- name: configure gcc
run: cmake -S . --preset=default -B build -DBAVE_BUILD_SHADERS=OFF -DBAVE_USE_FREETYPE=OFF -DCAPO_USE_OPENAL=OFF -DCMAKE_CXX_COMPILER=g++-13
run: cmake -S . --preset=default -B build -DBAVE_BUILD_SHADERS=OFF -DBAVE_USE_FREETYPE=OFF -DCAPO_USE_OPENAL=OFF
- name: configure clang
run: cmake -S . --preset=default -B clang -DBAVE_BUILD_SHADERS=OFF -DBAVE_USE_FREETYPE=OFF -DCAPO_USE_OPENAL=OFF -DCMAKE_CXX_COMPILER=clang++-15
run: cmake -S . --preset=ninja-clang -B clang -DBAVE_BUILD_SHADERS=OFF -DBAVE_USE_FREETYPE=OFF -DCAPO_USE_OPENAL=OFF
- name: build gcc
run: cmake --build build --config=Release -- -d explain
- name: build clang
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ bave*.log
/spaced
/notes.txt
/local_store
/massif.*
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ include(FetchContent)
FetchContent_Declare(
bgf
GIT_REPOSITORY https://github.com/karnkaul/bgf
GIT_TAG v0.1.7
GIT_TAG v0.1.8
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ext/bgf"
)

Expand Down
Binary file modified assets/images/ship_trooper.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/sfx/crunch.wav
Binary file not shown.
Binary file added assets/sfx/interact.wav
Binary file not shown.
Binary file added assets/sfx/lose.wav
Binary file not shown.
6 changes: 3 additions & 3 deletions assets/styles.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@
},
"progress_bars": {
"default": {
"background": "#9f2b68ff",
"fill": "milk",
"background": "#db7a7eff",
"fill": "#9f2b68ff",
"corner_ratio": 0.5,
"padding": 20
},
Expand Down Expand Up @@ -81,4 +81,4 @@
"padding": 20
}
}
}
}
1 change: 1 addition & 0 deletions src/spaced/spaced/game/enemies/creep.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ Creep::Creep(bave::Services const& services) : Enemy(services, "Creep") {

health = 1.0f;
speed = 100.0f;
points = 10;
}
} // namespace spaced::enemy
1 change: 1 addition & 0 deletions src/spaced/spaced/game/enemies/gunner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ Gunner::Gunner(bave::Services const& services, bave::NotNull<GunKinetic*> gun) :

health = 2.0f;
speed = 120.0f;
points = 20;
}
} // namespace spaced::enemy
11 changes: 6 additions & 5 deletions src/spaced/spaced/game/enemies/trooper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ using bave::Seconds;
using bave::Services;
using bave::Texture;

namespace {
constexpr auto at_end(float const y, float const y_min, float const y_max) { return y_min > y || y > y_max; }
} // namespace

Trooper::Trooper(Services const& services, NotNull<GunKinetic*> gun) : GunnerBase(services, gun, "Trooper") {
if (auto texture = services.get<Resources>().get<Texture>("images/ship_trooper.png")) {
m_sprite.set_size(texture->get_size());
Expand All @@ -24,6 +20,7 @@ Trooper::Trooper(Services const& services, NotNull<GunKinetic*> gun) : GunnerBas

health = 3.0f;
speed = 150.0f;
points = 30;
}

void Trooper::do_tick(Seconds const dt) {
Expand All @@ -32,6 +29,10 @@ void Trooper::do_tick(Seconds const dt) {
m_sprite.transform.position.y += m_direction * m_y_speed * dt.count();
auto const y_min = get_layout().play_area.rb.y + m_sprite.get_size().y;
auto const y_max = get_layout().play_area.lt.y - m_sprite.get_size().y;
if (at_end(m_sprite.transform.position.y, y_min, y_max)) { m_direction = -m_direction; }
if (m_sprite.transform.position.y < y_min) {
m_direction = 1.0f;
} else if (m_sprite.transform.position.y > y_max) {
m_direction = -1.0f;
}
}
} // namespace spaced::enemy
6 changes: 3 additions & 3 deletions src/spaced/spaced/game/hud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ using bave::Texture;

namespace ui = bave::ui;

Hud::Hud(Services const& services) : ui::View(services), m_layout(&services.get<Layout>()), m_styles(&services.get<Styles>()) {
Hud::Hud(Services const& services) : m_layout(&services.get<Layout>()), m_styles(&services.get<Styles>()) {
create_background();
create_score(services);
create_lives(services);
Expand Down Expand Up @@ -76,7 +76,7 @@ void Hud::create_score(Services const& services) {
m_hi_score = text.get();
text->text.transform.position.x = m_layout->hud_area.rb.x - 50.0f;
text->text.transform.position.y -= 0.5f * m_text_bounds_size.y;
text->text.set_align(Text::Align::eLeft);
text->text.set_align(Text::Align::eRight);
set_hi_score(0);
push(std::move(text));
}
Expand All @@ -98,7 +98,7 @@ void Hud::create_lives(Services const& services) {
auto text = make_text(services);
text->text.transform.position.y -= 0.5f * m_text_bounds_size.y;
text->text.transform.position.x = m_lives_icon->get_position().x + m_lives_icon->get_size().x;
text->text.set_align(Text::Align::eRight);
text->text.set_align(Text::Align::eLeft);
text->text.set_string("0");
m_lives_count = text.get();
push(std::move(text));
Expand Down
9 changes: 6 additions & 3 deletions src/spaced/spaced/game/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <spaced/game/weapons/gun_beam.hpp>

namespace spaced {
using bave::IAudio;
using bave::ParticleEmitter;
using bave::PointerMove;
using bave::PointerTap;
Expand All @@ -24,8 +25,8 @@ using bave::Styles;
using bave::Texture;

Player::Player(Services const& services, std::unique_ptr<IController> controller)
: m_services(&services), m_stats(&services.get<Stats>()), m_controller(std::move(controller)), m_on_1up(&services.get<GameSignals>().one_up),
m_shield(services), m_arsenal(services) {
: m_services(&services), m_audio(&services.get<IAudio>()), m_stats(&services.get<Stats>()), m_controller(std::move(controller)),
m_on_1up(&services.get<GameSignals>().one_up), m_shield(services), m_arsenal(services) {
auto const& layout = services.get<Layout>();
ship.transform.position.x = layout.player_x;

Expand Down Expand Up @@ -134,6 +135,8 @@ void Player::set_shield(Seconds const ttl) {
void Player::one_up() { m_on_1up->dispatch(); }

void Player::on_death(Seconds const dt) {
m_audio->play_sfx("sfx/crunch.wav");

m_health = 0.0f;
m_death = m_death_source;
m_death->set_position(ship.transform.position);
Expand Down Expand Up @@ -176,7 +179,7 @@ void Player::do_inspect() {
}
if (ImGui::TreeNodeEx("Shield", ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_DefaultOpen)) {
auto ttl = m_shield.ttl.count();
if (ImGui::DragFloat("ttl", &ttl, 0.25f, 0.0f, 60.0f, "%.2f")) { m_shield.ttl = Seconds{ttl}; }
if (ImGui::DragFloat("ttl", &ttl, 0.25f, 0.0f, 600.0f, "%.2f")) { m_shield.ttl = Seconds{ttl}; }
ImGui::TreePop();
}
if (ImGui::TreeNodeEx("Status", ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_DefaultOpen)) {
Expand Down
1 change: 1 addition & 0 deletions src/spaced/spaced/game/player.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class Player : public IDamageable, public bave::IDrawable {

bave::Logger m_log{"Player"};
bave::NotNull<bave::Services const*> m_services;
bave::NotNull<bave::IAudio*> m_audio;
bave::NotNull<Stats*> m_stats;
std::unique_ptr<IController> m_controller;
bave::NotNull<Sig1up*> m_on_1up;
Expand Down
3 changes: 1 addition & 2 deletions src/spaced/spaced/prefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ void Prefs::save(App const& app) const {
persistor.write_json(uri_v, json);
}

Prefs::View::View(NotNull<App const*> app, Services const& services)
: ui::View(services), m_app(app), m_audio(&services.get<IAudio>()), m_prefs(Prefs::load(*app)) {
Prefs::View::View(NotNull<App const*> app, Services const& services) : m_app(app), m_audio(&services.get<IAudio>()), m_prefs(Prefs::load(*app)) {
auto const& styles = services.get<Styles>();

auto bg = std::make_unique<ui::OutlineQuad>();
Expand Down
112 changes: 101 additions & 11 deletions src/spaced/spaced/scenes/endless.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <bave/core/fixed_string.hpp>
#include <bave/core/random.hpp>
#include <bave/services/resources.hpp>
#include <spaced/game/enemies/creep.hpp>
Expand All @@ -10,6 +11,7 @@

namespace spaced {
using bave::App;
using bave::FixedString;
using bave::NotNull;
using bave::random_in_range;
using bave::Resources;
Expand Down Expand Up @@ -39,12 +41,39 @@ struct GunnerSpawnerT : SpawnTimer<Enemy> {

using GunnerSpawner = GunnerSpawnerT<enemy::Gunner>;
using TrooperSpawner = GunnerSpawnerT<enemy::Trooper>;

auto roll_check(int const space) {
if (space <= 0) { return true; }
return random_in_range(0, space) == 0;
}

auto get_creep_spawn_rate(Seconds const elapsed) -> Seconds {
static constexpr auto base_rate_v = 0.9s;
if (elapsed < 50s) { return base_rate_v; }
if (elapsed < 100s) { return 0.75f * base_rate_v; }
return 0.6f * base_rate_v;
}

auto get_gunner_spawn_rate(Seconds const elapsed) -> Seconds {
static constexpr auto base_rate_v = 9.3s;
if (elapsed < 40s) { return base_rate_v; }
if (elapsed < 80s) { return 0.75f * base_rate_v; }
if (elapsed < 120s) { return 0.5f * base_rate_v; }
return 0.25f * base_rate_v;
}

auto get_trooper_spawn_rate(Seconds const elapsed) -> Seconds {
static constexpr auto base_rate_v = 29.4s;
if (elapsed < 60s) { return base_rate_v; }
if (elapsed < 100s) { return 0.5f * base_rate_v; }
if (elapsed < 150s) { return 0.25f * base_rate_v; }
if (elapsed < 180s) { return 0.125f * base_rate_v; }
return 0.0625f * base_rate_v;
}
} // namespace

EndlessScene::EndlessScene(App& app, Services const& services) : GameScene(app, services), m_enemy_gun(services) {
m_on_player_scored = services.get<GameSignals>().player_scored.connect([this](Enemy const& e) {
if (random_in_range(0, 10) < 3) { debug_spawn_powerup(e.get_position()); }
});
m_on_player_scored = services.get<GameSignals>().player_scored.connect([this](Enemy const& e) { on_player_scored(e); });
}

void EndlessScene::on_loaded() {
Expand All @@ -59,29 +88,90 @@ void EndlessScene::on_loaded() {
m_enemy_gun.projectile_config.x_speed = -m_enemy_gun.projectile_config.x_speed;
m_enemy_gun.projectile_config.x_scale = -1.0f;

m_spawn_timers.push_back(std::make_unique<CreepSpawner>(&get_services(), 1s));
m_spawn_timers.push_back(std::make_unique<GunnerSpawner>(&get_services(), &m_enemy_gun, 2s));
m_spawn_timers.push_back(std::make_unique<TrooperSpawner>(&get_services(), &m_enemy_gun, 5s));
on_start();
}

void EndlessScene::on_start() {
m_elapsed = 0s;
m_spawn_timers.clear();
m_spawner.creep = m_spawn_timers.emplace_back(make_creep_spawner()).get();
m_spawner.gunner = m_spawn_timers.emplace_back(make_gunner_spawner()).get();
m_spawner.trooper = m_spawn_timers.emplace_back(make_trooper_spawner()).get();
}

void EndlessScene::tick(Seconds const dt) {
GameScene::tick(dt);

if (is_game_over()) { return; }

m_elapsed += dt;
m_since_powerup += dt;

ramp_difficulty();

for (auto const& spawn_timer : m_spawn_timers) {
if (auto enemy = spawn_timer->tick(dt)) { push_enemy(std::move(enemy)); }
}
}

void EndlessScene::debug_spawn_powerup(glm::vec2 const position) {
void EndlessScene::ramp_difficulty() {
m_spawner.creep->spawn_rate = get_creep_spawn_rate(m_elapsed);
m_spawner.gunner->spawn_rate = get_gunner_spawn_rate(m_elapsed);
m_spawner.trooper->spawn_rate = get_trooper_spawn_rate(m_elapsed);
}

void EndlessScene::on_player_scored(Enemy const& enemy) {
if (m_since_powerup < 5s) { return; }

if (!roll_check(30)) { return; }

auto powerup = std::unique_ptr<Powerup>{};
if (random_in_range(0, 1) == 0) {
powerup = std::make_unique<powerup::Beam>(get_services());
if (roll_check(20)) {
powerup = make_1up_powerup();
} else {
powerup = std::make_unique<powerup::OneUp>(get_services());
powerup = make_beam_powerup();
}
powerup->shape.transform.position = position;

if (!powerup) { return; }

powerup->shape.transform.position = enemy.get_position();
push_powerup(std::move(powerup));
m_since_powerup = 0s;
}

auto EndlessScene::make_beam_powerup() const -> std::unique_ptr<Powerup> { return std::make_unique<powerup::Beam>(get_services()); }

auto EndlessScene::make_1up_powerup() const -> std::unique_ptr<Powerup> { return std::make_unique<powerup::OneUp>(get_services()); }

auto EndlessScene::make_creep_spawner() const -> std::unique_ptr<SpawnTimer<Enemy>> {
return std::make_unique<CreepSpawner>(&get_services(), get_creep_spawn_rate(0s));
}

auto EndlessScene::make_gunner_spawner() -> std::unique_ptr<SpawnTimer<Enemy>> {
return std::make_unique<GunnerSpawner>(&get_services(), &m_enemy_gun, get_gunner_spawn_rate(0s));
}

auto EndlessScene::make_trooper_spawner() -> std::unique_ptr<SpawnTimer<Enemy>> {
return std::make_unique<TrooperSpawner>(&get_services(), &m_enemy_gun, get_trooper_spawn_rate(0s));
}

void EndlessScene::do_inspect(Seconds const /*dt*/) {
if constexpr (bave::imgui_v) {
auto elapsed = m_elapsed.count();
if (ImGui::DragFloat("elapsed", &elapsed, 1.0f, 0.0f, 2000.0f)) { m_elapsed = Seconds{elapsed}; }
if (ImGui::TreeNode("spawn timers")) {
for (std::size_t i = 0; i < m_spawn_timers.size(); ++i) {
if (ImGui::TreeNode(FixedString{"[{}]", i}.c_str())) {
auto& spawn_timer = *m_spawn_timers.at(i);
auto f = spawn_timer.spawn_rate.count();
if (ImGui::DragFloat("period", &f, 0.25f, 0.25f, 20.0f)) { spawn_timer.spawn_rate = Seconds{f}; }
f = spawn_timer.elapsed.count();
if (ImGui::DragFloat("elapsed", &f, 0.25f, 0.25f, 20.0f)) { spawn_timer.elapsed = Seconds{f}; }
ImGui::TreePop();
}
}
ImGui::TreePop();
}
}
}
} // namespace spaced
23 changes: 21 additions & 2 deletions src/spaced/spaced/scenes/endless.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,33 @@ class EndlessScene : public GameScene {

private:
void on_loaded() final;

void tick(bave::Seconds dt) final;

void debug_spawn_powerup(glm::vec2 position);
void on_start() final;

void ramp_difficulty();
void on_player_scored(Enemy const& enemy);

[[nodiscard]] auto make_beam_powerup() const -> std::unique_ptr<Powerup>;
[[nodiscard]] auto make_1up_powerup() const -> std::unique_ptr<Powerup>;

[[nodiscard]] auto make_creep_spawner() const -> std::unique_ptr<SpawnTimer<Enemy>>;
[[nodiscard]] auto make_gunner_spawner() -> std::unique_ptr<SpawnTimer<Enemy>>;
[[nodiscard]] auto make_trooper_spawner() -> std::unique_ptr<SpawnTimer<Enemy>>;

void do_inspect(bave::Seconds dt) final;

SignalHandle m_on_player_scored{};
GunKinetic m_enemy_gun;

bave::Seconds m_elapsed{};
bave::Seconds m_since_powerup{};
std::vector<std::unique_ptr<SpawnTimer<Enemy>>> m_spawn_timers{};

struct {
bave::Ptr<SpawnTimer<Enemy>> creep{};
bave::Ptr<SpawnTimer<Enemy>> gunner{};
bave::Ptr<SpawnTimer<Enemy>> trooper{};
} m_spawner{};
};
} // namespace spaced
Loading