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
50 changes: 50 additions & 0 deletions src/spaced/spaced/game/arsenal.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#include <spaced/game/arsenal.hpp>

namespace spaced {
using bave::Seconds;
using bave::Shader;

auto Arsenal::get_weapon() const -> Weapon const& {
if (m_special) { return *m_special; }
return m_primary;
}

auto Arsenal::get_weapon() -> Weapon& {
return const_cast<Weapon&>(std::as_const(*this).get_weapon()); // NOLINT(cppcoreguidelines-pro-type-const-cast)
}

void Arsenal::tick(IWeaponRound::State const& round_state, bool const fire, Seconds const dt) {
tick_weapons(dt);
check_switch_weapon();
if (fire) { fire_weapon(round_state.muzzle_position); }
tick_rounds(round_state, dt);
}

void Arsenal::draw(Shader& shader) const {
for (auto const& round : m_rounds) { round->draw(shader); }
}

void Arsenal::tick_weapons(Seconds const dt) {
m_primary.tick(dt);

if (m_special) {
m_special->tick(dt);
// if the special weapon has no more rounds and is idle, reset it.
if (m_special->get_rounds_remaining() == 0 && m_special->is_idle()) { m_special.reset(); }
}
}

void Arsenal::check_switch_weapon() {
// if there is a next weapon on standby and the current weapon is idle, switch to the next weapon.
if (m_next && get_weapon().is_idle()) { m_special = std::move(m_next); }
}

void Arsenal::fire_weapon(glm::vec2 const muzzle_position) {
if (auto round = get_weapon().fire(muzzle_position)) { m_rounds.push_back(std::move(round)); }
}

void Arsenal::tick_rounds(IWeaponRound::State const& round_state, Seconds const dt) {
for (auto const& round : m_rounds) { round->tick(round_state, dt); }
std::erase_if(m_rounds, [](auto const& round) { return round->is_destroyed(); });
}
} // namespace spaced
32 changes: 32 additions & 0 deletions src/spaced/spaced/game/arsenal.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#pragma once
#include <spaced/game/weapons/gun_kinetic.hpp>

namespace spaced {
// Arsenal models a main/primary weapon, and an possible special weapon.
// Weapons only switch when they are idle.
class Arsenal {
public:
explicit Arsenal(Services const& services) : m_primary(services) {}

[[nodiscard]] auto get_weapon() const -> Weapon const&;
[[nodiscard]] auto get_weapon() -> Weapon&;

[[nodiscard]] auto is_special_active() const -> bool { return m_special != nullptr; }

void set_special(std::unique_ptr<Weapon> weapon) { m_next = std::move(weapon); }

void tick(IWeaponRound::State const& round_state, bool fire, bave::Seconds dt);
void draw(bave::Shader& shader) const;

private:
void tick_weapons(bave::Seconds dt);
void check_switch_weapon();
void fire_weapon(glm::vec2 muzzle_position);
void tick_rounds(IWeaponRound::State const& round_state, bave::Seconds dt);

GunKinetic m_primary; // main weapon
std::unique_ptr<Weapon> m_special{}; // special weapon
std::unique_ptr<Weapon> m_next{}; // next special weapon (on standby until current weapon is idle)
std::vector<std::unique_ptr<Weapon::Round>> m_rounds{};
};
} // namespace spaced
67 changes: 19 additions & 48 deletions src/spaced/spaced/game/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

// temp for testing
#include <spaced/game/weapons/gun_beam.hpp>
#include <spaced/game/weapons/gun_kinetic.hpp>

namespace spaced {
using bave::im_text;
Expand All @@ -19,11 +18,7 @@ using bave::RoundedQuad;
using bave::Seconds;
using bave::Shader;

Player::Player(Services const& services, std::unique_ptr<IController> controller) : m_services(&services), m_controller(std::move(controller)) {
setup_ship();

debug_switch_weapon();
}
Player::Player(Services const& services, std::unique_ptr<IController> controller) : m_services(&services), m_controller(std::move(controller)) { setup_ship(); }

void Player::on_focus(bave::FocusChange const& /*focus_changed*/) { m_controller->untap(); }

Expand All @@ -35,32 +30,17 @@ void Player::tick(std::span<NotNull<IDamageable*> const> targets, Seconds const
auto const y_position = m_controller->tick(dt);
set_y(y_position);

auto const muzzle_position = get_muzzle_position();
if (m_controller->is_firing() && m_debug.shots_remaining > 0) {
if (auto round = m_weapon->fire(muzzle_position)) {
m_weapon_rounds.push_back(std::move(round));
--m_debug.shots_remaining;
}
}

auto const round_state = IWeaponRound::State{.targets = targets, .muzzle_position = muzzle_position};
for (auto const& round : m_weapon_rounds) { round->tick(round_state, dt); }
std::erase_if(m_weapon_rounds, [](auto const& charge) { return charge->is_destroyed(); });

m_weapon->tick(dt);
auto const round_state = IWeaponRound::State{.targets = targets, .muzzle_position = get_muzzle_position()};
m_arsenal.tick(round_state, m_controller->is_firing(), dt);

m_exhaust.set_position(get_exhaust_position());

m_exhaust.tick(dt);

if (m_debug.shots_remaining <= 0) { debug_switch_weapon(); }
}

void Player::draw(Shader& shader) const {
m_exhaust.draw(shader);
ship.draw(shader);

for (auto const& round : m_weapon_rounds) { round->draw(shader); }
m_arsenal.draw(shader);
}

void Player::setup(WorldSpec::Player const& spec) {
Expand All @@ -83,15 +63,28 @@ void Player::set_controller(std::unique_ptr<IController> controller) {
m_controller = std::move(controller);
}

void Player::setup_ship() {
auto const& layout = m_services->get<ILayout>();
ship.transform.position.x = layout.get_player_x();
auto rounded_quad = RoundedQuad{};
rounded_quad.size = layout.get_player_size();
rounded_quad.corner_radius = 20.0f;
ship.set_shape(rounded_quad);
}

void Player::do_inspect() {
if constexpr (bave::imgui_v) {
if (ImGui::TreeNodeEx("Controller", ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_DefaultOpen)) {
m_controller->inspect();
ImGui::TreePop();
}
if (ImGui::TreeNodeEx("Weapon", ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_DefaultOpen)) {
m_weapon->inspect();
im_text("shots remaining: {}", m_debug.shots_remaining);
if (ImGui::Button("Switch to Beam")) {
auto beam = std::make_unique<GunBeam>(*m_services);
beam->rounds = 2;
m_arsenal.set_special(std::move(beam));
}
m_arsenal.get_weapon().inspect();
ImGui::TreePop();
}
if (ImGui::TreeNodeEx("Status", ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_DefaultOpen)) {
Expand All @@ -100,26 +93,4 @@ void Player::do_inspect() {
}
}
}

void Player::setup_ship() {
auto const& layout = m_services->get<ILayout>();
ship.transform.position.x = layout.get_player_x();
auto rounded_quad = RoundedQuad{};
rounded_quad.size = layout.get_player_size();
rounded_quad.corner_radius = 20.0f;
ship.set_shape(rounded_quad);
}

void Player::debug_switch_weapon() {
if (m_weapon && !m_weapon->is_idle()) { return; }

if (dynamic_cast<GunKinetic const*>(m_weapon.get()) != nullptr) {
m_weapon = std::make_unique<GunBeam>(*m_services);
m_debug.shots_remaining = 2;
return;
}

m_weapon = std::make_unique<GunKinetic>(*m_services);
m_debug.shots_remaining = 10;
}
} // namespace spaced
11 changes: 4 additions & 7 deletions src/spaced/spaced/game/player.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
#include <bave/graphics/particle_system.hpp>
#include <bave/graphics/shape.hpp>
#include <bave/logger.hpp>
#include <spaced/game/arsenal.hpp>
#include <spaced/game/controller.hpp>
#include <spaced/game/health.hpp>
#include <spaced/game/weapon.hpp>
#include <spaced/game/world_spec.hpp>

namespace spaced {
Expand All @@ -30,6 +30,8 @@ class Player : public bave::IDrawable {
void set_controller(std::unique_ptr<IController> controller);
[[nodiscard]] auto get_controller() const -> IController const& { return *m_controller; }

void set_special_weapon(std::unique_ptr<Weapon> weapon) { m_arsenal.set_special(std::move(weapon)); }

void inspect() {
if constexpr (bave::debug_v) { do_inspect(); }
}
Expand All @@ -41,17 +43,12 @@ class Player : public bave::IDrawable {
void setup_ship();

void do_inspect();
void debug_switch_weapon();

bave::Logger m_log{"Player"};
bave::NotNull<Services const*> m_services;
std::unique_ptr<IController> m_controller;
bave::ParticleEmitter m_exhaust{};
std::unique_ptr<Weapon> m_weapon{};
std::vector<std::unique_ptr<Weapon::Round>> m_weapon_rounds{};

struct {
int shots_remaining{};
} m_debug{};
Arsenal m_arsenal{*m_services};
};
} // namespace spaced
10 changes: 10 additions & 0 deletions src/spaced/spaced/game/weapon.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include <bave/imgui/im_text.hpp>
#include <spaced/game/weapon.hpp>

namespace spaced {
using bave::im_text;

void Weapon::do_inspect() {
if constexpr (bave::imgui_v) { im_text("rounds remaining: {}", get_rounds_remaining()); }
}
} // namespace spaced
7 changes: 6 additions & 1 deletion src/spaced/spaced/game/weapon.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ class Weapon : public bave::Polymorphic {

explicit Weapon(Services const& services, std::string name) : m_log{std::move(name)}, m_layout(&services.get<ILayout>()) {}

[[nodiscard]] auto get_rounds_remaining() const -> int { return rounds < 0 ? 1 : rounds; }

virtual auto fire(glm::vec2 muzzle_position) -> std::unique_ptr<Round> = 0;
[[nodiscard]] virtual auto is_idle() const -> bool = 0;

Expand All @@ -21,9 +23,12 @@ class Weapon : public bave::Polymorphic {
if constexpr (bave::debug_v) { do_inspect(); }
}

int rounds{-1};

protected:
[[nodiscard]] auto get_layout() const -> ILayout const& { return *m_layout; }
virtual void do_inspect() {}

virtual void do_inspect();

bave::Logger m_log{};

Expand Down
4 changes: 3 additions & 1 deletion src/spaced/spaced/game/weapons/gun_beam.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,9 @@ GunBeam::GunBeam(Services const& services) : Weapon(services, "GunBeam") {
}

auto GunBeam::fire(glm::vec2 const muzzle_position) -> std::unique_ptr<Round> {
if (!is_idle() || m_reload_remain > 0s) { return {}; }
if (!is_idle() || m_reload_remain > 0s || rounds == 0) { return {}; }

if (rounds > 0) { --rounds; }
m_fire_remain = config.fire_duration;
m_reload_remain = 0s;
return std::make_unique<LaserCharge>(&get_layout(), config, muzzle_position);
Expand All @@ -124,6 +125,7 @@ void GunBeam::tick(Seconds const dt) {
void GunBeam::do_inspect() {
if constexpr (bave::imgui_v) {
im_text("type: GunBeam");
Weapon::do_inspect();
ImGui::DragFloat("beam height", &config.beam_height, 0.25f, 1.0f, 100.0f);
auto fire_duration = config.fire_duration.count();
if (ImGui::DragFloat("fire duration (s)", &fire_duration, 0.25f, 0.25f, 10.0f)) { config.fire_duration = Seconds{fire_duration}; }
Expand Down
4 changes: 3 additions & 1 deletion src/spaced/spaced/game/weapons/gun_kinetic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ using bave::Seconds;
GunKinetic::GunKinetic(Services const& services) : Weapon(services, "GunKinetic") { projectile_config.tint = services.get<Styles>().rgbas["black"]; }

auto GunKinetic::fire(glm::vec2 const muzzle_position) -> std::unique_ptr<Round> {
if (m_reload_remain > 0s) { return {}; }
if (m_reload_remain > 0s || rounds == 0) { return {}; }

if (rounds > 0) { --rounds; }
m_reload_remain = reload_delay;
return std::make_unique<Projectile>(&get_layout(), projectile_config, muzzle_position);
}
Expand All @@ -23,6 +24,7 @@ void GunKinetic::tick(Seconds const dt) {
void GunKinetic::do_inspect() {
if constexpr (bave::imgui_v) {
im_text("type: GunKinetic");
Weapon::do_inspect();
ImGui::DragFloat2("projectile size", &projectile_config.size.x);
ImGui::DragFloat("x speed", &projectile_config.x_speed, 10.0f, 100.0f, 10000.0f);
auto tint = projectile_config.tint.to_vec4();
Expand Down