Skip to content
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
7 changes: 7 additions & 0 deletions assets/styles.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@
"content_text_tint": "#231d2aff"
}
},
"sliders": {
"default": {
"progress_bar": "default",
"knob_diameter": 30,
"knob_tint": "#9f2b68ff"
}
},
"loading_screen": {
"background_tint": "#231d2aff",
"spinner": {
Expand Down
3 changes: 1 addition & 2 deletions src/spaced/spaced/game/enemy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,11 @@ void Enemy::force_death() {
m_health_bar.set_progress(0.0f);
}

void Enemy::tick(Seconds const dt, bool const /*in_play*/) {
void Enemy::tick(Seconds const /*dt*/, bool const /*in_play*/) {
m_health_bar.position = shape.transform.position;
m_health_bar.position.y += 0.5f * shape.get_shape().size.y + 20.0f;
m_health_bar.size = {shape.get_shape().size.x, 10.0f};
m_health_bar.set_progress(health.get_hit_points() / health.get_total_hit_points());
m_health_bar.tick(dt);
}

void Enemy::draw(Shader& shader) const {
Expand Down
85 changes: 85 additions & 0 deletions src/spaced/spaced/prefs.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#include <bave/persistor.hpp>
#include <djson/json.hpp>
#include <spaced/prefs.hpp>
#include <spaced/services/audio.hpp>
#include <spaced/services/styles.hpp>
#include <spaced/ui/button.hpp>
#include <spaced/ui/slider.hpp>
#include <spaced/ui/text.hpp>

namespace spaced {
using bave::App;
using bave::NotNull;
using bave::Persistor;

namespace {
constexpr std::string_view uri_v{"spaced/prefs.json"};
} // namespace

auto Prefs::load(App const& app) -> Prefs {
auto ret = Prefs{};
auto const persistor = Persistor{app};
if (persistor.exists(uri_v)) {
auto const json = persistor.read_json(uri_v);
from_json(json["music_gain"], ret.music_gain);
from_json(json["sfx_gain"], ret.sfx_gain);
}
return ret;
}

void Prefs::save(App const& app) const {
auto json = dj::Json{};
to_json(json["music_gain"], music_gain);
to_json(json["sfx_gain"], sfx_gain);
auto const persistor = Persistor{app};
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)) {
auto const& styles = services.get<Styles>();

auto bg = std::make_unique<ui::OutlineQuad>();
bg->set_size({400.0f, 400.0f});
bg->set_outline_width(5.0f);
bg->set_corner_ratio(0.1f);
bg->set_tint(styles.rgbas["milk"]);
push(std::move(bg));

auto text = std::make_unique<ui::Text>(services);
text->text.set_string("music");
text->set_position({0.0f, 130.0f});
push(std::move(text));

auto slider = std::make_unique<ui::Slider>(services);
slider->set_value(m_prefs.music_gain);
slider->on_change = [this](float const val) {
m_prefs.music_gain = val;
m_audio->set_music_gain(val);
};
slider->set_position({0.0f, 100.0f});
push(std::move(slider));

text = std::make_unique<ui::Text>(services);
text->text.set_string("sfx");
text->set_position({0.0f, 30.0f});
push(std::move(text));

slider = std::make_unique<ui::Slider>(services);
slider->set_value(m_prefs.sfx_gain);
slider->on_change = [this](float const val) {
m_prefs.sfx_gain = val;
m_audio->set_sfx_gain(val);
};
slider->set_position({0.0f, 0.0f});
push(std::move(slider));

auto button = std::make_unique<ui::Button>(services);
button->set_text("close");
button->callback = [this] { set_destroyed(); };
button->set_position({0.0f, -100.0f});
push(std::move(button));
}

Prefs::View::~View() { m_prefs.save(*m_app); }
} // namespace spaced
33 changes: 33 additions & 0 deletions src/spaced/spaced/prefs.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#pragma once
#include <bave/app.hpp>
#include <spaced/ui/view.hpp>

namespace spaced {
class IAudio;

struct Prefs {
class View;

float music_gain{1.0f};
float sfx_gain{1.0f};

static auto load(bave::App const& app) -> Prefs;
void save(bave::App const& app) const;
};

class Prefs::View : public ui::View {
public:
View(View const&) = delete;
View(View&&) = delete;
auto operator=(View const&) = delete;
auto operator=(View&&) = delete;

explicit View(bave::NotNull<bave::App const*> app, Services const& services);
~View() override;

private:
bave::NotNull<bave::App const*> m_app;
bave::NotNull<IAudio*> m_audio;
Prefs m_prefs;
};
} // namespace spaced
12 changes: 10 additions & 2 deletions src/spaced/spaced/scene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ void Scene::push_view(std::unique_ptr<ui::View> view) {

void Scene::tick_frame(Seconds const dt) {
tick(dt);
for (auto const& view : m_views) { view->tick(dt); }
for (auto const& view : cache_views()) { view->tick(dt); }
std::erase_if(m_views, [](auto const& view) { return view->is_destroyed(); });
}

Expand All @@ -51,11 +51,19 @@ void Scene::render_frame() const {

template <typename F>
auto Scene::on_ui_event(F per_view) -> bool {
for (auto it = m_views.rbegin(); it != m_views.rend(); ++it) {
auto const cached_views = cache_views();
for (auto it = cached_views.rbegin(); it != cached_views.rend(); ++it) {
auto const& view = *it;
per_view(*view);
if (view->block_input_events) { return true; }
}
return false;
}

auto Scene::cache_views() -> std::span<bave::Ptr<ui::View> const> {
m_cached_views.clear();
m_cached_views.reserve(m_views.size());
for (auto const& view : m_views) { m_cached_views.push_back(view.get()); }
return m_cached_views;
}
} // namespace spaced
5 changes: 5 additions & 0 deletions src/spaced/spaced/scene.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ class Scene : public bave::PolyPinned {
void tick_frame(bave::Seconds dt);
void render_frame() const;

[[nodiscard]] virtual auto get_music_uri() const -> std::string_view { return {}; }

[[nodiscard]] auto get_app() const -> bave::App& { return m_app; }
[[nodiscard]] auto get_services() const -> Services const& { return m_services; }

Expand Down Expand Up @@ -49,8 +51,11 @@ class Scene : public bave::PolyPinned {
template <typename F>
auto on_ui_event(F per_view) -> bool;

auto cache_views() -> std::span<bave::Ptr<ui::View> const>;

bave::App& m_app;
Services const& m_services;
std::vector<std::unique_ptr<ui::View>> m_views{};
std::vector<bave::Ptr<ui::View>> m_cached_views{};
};
} // namespace spaced
35 changes: 20 additions & 15 deletions src/spaced/spaced/scenes/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#include <bave/imgui/im_text.hpp>
#include <spaced/assets/asset_list.hpp>
#include <spaced/scenes/game.hpp>
#include <spaced/scenes/home.hpp>
#include <spaced/scenes/menu.hpp>
#include <spaced/services/scene_switcher.hpp>
#include <spaced/services/stats.hpp>
#include <spaced/services/styles.hpp>
Expand All @@ -23,11 +23,13 @@ using bave::Ptr;
using bave::Seconds;
using bave::Shader;

auto Game::get_manifest() -> AssetManifest {
auto GameScene::get_manifest() -> AssetManifest {
return AssetManifest{
.audio_clips =
{
"sfx/bubble.wav",
"music/menu.mp3",
"music/game.mp3",
},
.particle_emitters =
{
Expand All @@ -38,7 +40,7 @@ auto Game::get_manifest() -> AssetManifest {
};
}

Game::Game(App& app, Services const& services) : Scene(app, services, "Game"), m_save(&app), m_world(&services, this) {
GameScene::GameScene(App& app, Services const& services) : Scene(app, services, "Game"), m_save(&app), m_world(&services, this) {
clear_colour = services.get<Styles>().rgbas["mocha"];

auto hud = std::make_unique<Hud>(services);
Expand All @@ -49,19 +51,19 @@ Game::Game(App& app, Services const& services) : Scene(app, services, "Game"), m
++services.get<Stats>().game.play_count;
}

void Game::on_focus(FocusChange const& focus_change) { m_world.player.on_focus(focus_change); }
void GameScene::on_focus(FocusChange const& focus_change) { m_world.player.on_focus(focus_change); }

void Game::on_key(KeyInput const& key_input) {
void GameScene::on_key(KeyInput const& key_input) {
if (key_input.key == Key::eEscape && key_input.action == Action::eRelease && key_input.mods == KeyMods{}) {
get_services().get<ISceneSwitcher>().switch_to<Home>();
get_services().get<ISceneSwitcher>().switch_to<MenuScene>();
}
}

void Game::on_move(PointerMove const& pointer_move) { m_world.player.on_move(pointer_move); }
void GameScene::on_move(PointerMove const& pointer_move) { m_world.player.on_move(pointer_move); }

void Game::on_tap(PointerTap const& pointer_tap) { m_world.player.on_tap(pointer_tap); }
void GameScene::on_tap(PointerTap const& pointer_tap) { m_world.player.on_tap(pointer_tap); }

void Game::tick(Seconds const dt) {
void GameScene::tick(Seconds const dt) {
auto ft = bave::DeltaTime{};

m_world.tick(dt);
Expand All @@ -70,19 +72,19 @@ void Game::tick(Seconds const dt) {
if constexpr (bave::debug_v) { inspect(dt, ft.update()); }
}

void Game::render(Shader& shader) const { m_world.draw(shader); }
void GameScene::render(Shader& shader) const { m_world.draw(shader); }

void Game::add_score(std::int64_t const score) {
void GameScene::add_score(std::int64_t const score) {
m_score += score;
m_hud->set_score(m_score);
update_hi_score();
}

void Game::on_game_over() {
void GameScene::on_game_over() {
auto dci = ui::DialogCreateInfo{
.size = {600.0f, 200.0f},
.content_text = "GAME OVER",
.main_button = {.text = "RESTART", .callback = [this] { get_services().get<ISceneSwitcher>().switch_to<Game>(); }},
.main_button = {.text = "RESTART", .callback = [this] { get_services().get<ISceneSwitcher>().switch_to<GameScene>(); }},
.second_button = {.text = "QUIT", .callback = [this] { get_app().shutdown(); }},
};

Expand All @@ -91,13 +93,13 @@ void Game::on_game_over() {
push_view(std::move(dialog));
}

void Game::update_hi_score() {
void GameScene::update_hi_score() {
if (m_score <= m_save.get_hi_score()) { return; }
m_save.set_hi_score(m_score);
m_hud->set_hi_score(m_save.get_hi_score());
}

void Game::inspect(Seconds const dt, Seconds const frame_time) {
void GameScene::inspect(Seconds const dt, Seconds const frame_time) {
if constexpr (bave::imgui_v) {
m_debug.fps.tick(dt);

Expand All @@ -118,6 +120,9 @@ void Game::inspect(Seconds const dt, Seconds const frame_time) {
im_text("fps: {}", m_debug.fps.fps);
ImGui::SliderInt("fps limit", &m_debug.fps.limit, 5, 1000);
ImGui::Checkbox("fps lock", &m_debug.fps.lock);

ImGui::Separator();
if (ImGui::Button("reload scene")) { get_services().get<ISceneSwitcher>().switch_to<GameScene>(); }
}
ImGui::End();

Expand Down
6 changes: 4 additions & 2 deletions src/spaced/spaced/scenes/game.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
#include <spaced/scene.hpp>

namespace spaced {
class Game : public Scene, public IScorer {
class GameScene : public Scene, public IScorer {
public:
static auto get_manifest() -> AssetManifest;

Game(bave::App& app, Services const& services);
GameScene(bave::App& app, Services const& services);

private:
void on_focus(bave::FocusChange const& focus_change) final;
Expand All @@ -23,6 +23,8 @@ class Game : public Scene, public IScorer {
void tick(bave::Seconds dt) final;
void render(bave::Shader& shader) const final;

[[nodiscard]] auto get_music_uri() const -> std::string_view final { return "music/game.mp3"; }

[[nodiscard]] auto get_score() const -> std::int64_t final { return m_score; }
void add_score(std::int64_t score) final;
void on_game_over();
Expand Down
8 changes: 4 additions & 4 deletions src/spaced/spaced/scenes/load_assets.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#include <spaced/assets/asset_list.hpp>
#include <spaced/scenes/game.hpp>
#include <spaced/scenes/home.hpp>
#include <spaced/scenes/load_assets.hpp>
#include <spaced/scenes/menu.hpp>
#include <spaced/services/resources.hpp>
#include <spaced/services/scene_switcher.hpp>
#include <spaced/util.hpp>
Expand All @@ -15,19 +15,19 @@ using bave::Shader;
namespace {
auto make_load_stages(Loader loader, Services const& services) -> std::vector<AsyncExec::Stage> {
auto asset_list = AssetList{std::move(loader), services};
asset_list.add_manifest(Game::get_manifest());
asset_list.add_manifest(GameScene::get_manifest());
auto ret = asset_list.build_task_stages();
auto& stage = ret.emplace_back();
auto& resources = services.get<Resources>();
stage.push_back(util::create_font_atlas_task(resources.main_font, Home::get_text_heights()));
stage.push_back(util::create_font_atlas_task(resources.main_font, MenuScene::get_text_heights()));
return ret;
}
} // namespace

LoadAssets::LoadAssets(App& app, Services const& services)
: Scene(app, services, "LoadAssets"), m_loading_screen(services), m_load(make_load_stages(make_loader(), services)) {}

void LoadAssets::on_loaded() { get_services().get<ISceneSwitcher>().switch_to<Home>(); }
void LoadAssets::on_loaded() { get_services().get<ISceneSwitcher>().switch_to<MenuScene>(); }

void LoadAssets::tick(Seconds const dt) {
auto const load_status = m_load.update();
Expand Down
Loading