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
35 changes: 35 additions & 0 deletions lib/include/chomper/collectibles.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#pragma once
#include "chomper/player.hpp"
#include <le2d/drawable/sprite.hpp>
#include <le2d/random.hpp>
#include <le2d/renderer.hpp>

namespace chomper {
class Collectibles {
public:
explicit Collectibles(le::ITexture const& texture);

void spawn(Player const& player);

void draw(le::IRenderer& renderer) const;

void eraseInstance(std::size_t index) {
if (index >= m_sprites.instances.size()) {
return;
}
m_sprites.instances.erase(m_sprites.instances.begin() + static_cast<std::vector<le::RenderInstance>::difference_type>(index));
}

[[nodiscard]] std::span<le::RenderInstance const> getInstances() const {
return m_sprites.instances;
}

private:
void findEmptyTiles(Player const& player);

std::vector<int> m_emptyTiles{};
le::Random m_random{};

le::drawable::InstancedSprite m_sprites{};
};
} // namespace chomper
14 changes: 3 additions & 11 deletions lib/include/chomper/runtimes/game.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#pragma once
#include "chomper/collectible.hpp"
#include "chomper/collectibles.hpp"
#include "chomper/engine.hpp"
#include "chomper/player.hpp"
#include "chomper/runtime.hpp"
Expand All @@ -8,9 +8,6 @@
#include <le2d/drawable/text.hpp>
#include <le2d/input/action.hpp>
#include <le2d/input/scoped_mapping.hpp>
#include <le2d/random.hpp>
#include <le2d/resource/texture.hpp>
#include <unordered_set>

namespace chomper::runtime {
// driven by Engine, owner (whether indirectly) of all game things.
Expand All @@ -32,10 +29,8 @@ class Game : public IRuntime, public klib::Pinned {

void bindActions();
void createPlayer();
void createCollectibleTexture();
void createCollectibles();

void findEmptyTiles();
void spawnCollectibles();
void collideCollectibles();

void onGoBack();
Expand All @@ -47,12 +42,9 @@ class Game : public IRuntime, public klib::Pinned {
le::input::ScopedActionMapping m_mapping;
Actions m_actions{};

le::Random m_random{};
std::unordered_set<int> m_occupied;

std::unique_ptr<Player> m_player{};
std::unique_ptr<World> m_world{};
std::vector<Collectible> m_collectibles{};
std::unique_ptr<Collectibles> m_collectibles{};
klib::Ptr<le::ITexture const> m_collectibleTexture{};

std::vector<int> m_emptyTiles{};
Expand Down
66 changes: 66 additions & 0 deletions lib/src/collectibles.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#include "chomper/collectibles.hpp"
#include "chomper/world_size.hpp"
#include "chomper/world_space.hpp"
#include "le2d/renderer.hpp"

namespace chomper {
namespace {
constexpr auto collectibleAmount_v = 10;
}

Collectibles::Collectibles(le::ITexture const& texture) {
m_sprites.set_base_size(tileSize_v);
m_sprites.set_texture(&texture);
}

void Collectibles::spawn(Player const& player) {
findEmptyTiles(player);

for (auto i = m_sprites.instances.size(); i < collectibleAmount_v; i++) {
if (m_emptyTiles.empty()) {
return;
}
// find a random tile
auto random = m_random.next_index(m_emptyTiles.size());
auto tile = m_emptyTiles[random];
// remove said tile from the vector
std::erase_if(m_emptyTiles, [&](auto const& v) {
return v == m_emptyTiles[random];
});
// place the collectible on the tile
auto width = static_cast<int>(worldSize_v.x);
m_sprites.instances.emplace_back();
m_sprites.instances.back().transform.position = worldSpace::gridToWorld({tile % width, tile / width});
}
}

void Collectibles::findEmptyTiles(Player const& player) {
m_emptyTiles.clear();
m_emptyTiles.reserve(static_cast<int>(worldSize_v.x * worldSize_v.y));
for (auto i = 0; i < static_cast<int>(worldSize_v.x * worldSize_v.y); i++) {
m_emptyTiles.push_back(i);
}

auto const removeTile = [this](int tile) {
auto it = std::ranges::find(m_emptyTiles, tile);
if (it != m_emptyTiles.end()) {
*it = m_emptyTiles.back();
m_emptyTiles.pop_back();
}
};

for (auto const& seg : player.getSegments()) {
auto p = worldSpace::worldToGrid(seg.transform.position);
removeTile(static_cast<int>((p.y * worldSize_v.x) + p.x));
}

for (auto const& c : m_sprites.instances) {
auto p = worldSpace::worldToGrid(c.transform.position);
removeTile(static_cast<int>((p.y * worldSize_v.x) + p.x));
}
}

void Collectibles::draw(le::IRenderer& renderer) const {
m_sprites.draw(renderer);
}
} // namespace chomper
70 changes: 10 additions & 60 deletions lib/src/runtimes/game.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
#include "chomper/runtimes/game.hpp"
#include "chomper/collectible.hpp"
#include "chomper/im_util.hpp"
#include "chomper/runtimes/entrypoint.hpp"
#include "chomper/world_size.hpp"
#include "chomper/world_space.hpp"
#include <le2d/random.hpp>
#include <algorithm>
Expand All @@ -12,17 +10,16 @@ namespace {
constexpr auto countdownParams_v = le::drawable::Text::Params{
.height = le::TextHeight{60},
};
constexpr auto collectibleAmount_v = 10;
} // namespace
using ActionValue = le::input::action::Value;

Game::Game(gsl::not_null<Engine*> engine) : m_engine(engine), m_mapping(&engine->getInputRouter()) {
createPlayer();
m_world = std::make_unique<World>(m_engine);

createCollectibleTexture();
createCollectibles();

spawnCollectibles();
m_collectibles->spawn(*m_player);

m_countdownText.set_string(engine->getResources().getMainFont(), "3", countdownParams_v);
}
Expand Down Expand Up @@ -52,9 +49,7 @@ void Game::tick(kvf::Seconds const dt) {

void Game::render(le::IRenderer& renderer) const {
m_world->draw(renderer);
for (auto const& collectible : m_collectibles) {
collectible.draw(renderer);
}
m_collectibles->draw(renderer);
m_player->draw(renderer);
if (m_countdown.count() > 0) {
m_countdownText.draw(renderer);
Expand Down Expand Up @@ -89,67 +84,22 @@ void Game::createPlayer() {
m_player = std::make_unique<Player>(m_mapping, m_engine);
}

void Game::createCollectibleTexture() {
void Game::createCollectibles() {
m_collectibleTexture = m_engine->getResources().load<le::ITexture>("images/apple.png");
}

void Game::findEmptyTiles() {
m_emptyTiles.clear();
m_emptyTiles.reserve(static_cast<int>(worldSize_v.x * worldSize_v.y));
for (auto i = 0; i < static_cast<int>(worldSize_v.x * worldSize_v.y); i++) {
m_emptyTiles.push_back(i);
}

auto const removeTile = [this](int tile) {
auto it = std::ranges::find(m_emptyTiles, tile);
if (it != m_emptyTiles.end()) {
*it = m_emptyTiles.back();
m_emptyTiles.pop_back();
}
};

for (auto const& seg : m_player->getSegments()) {
auto p = worldSpace::worldToGrid(seg.transform.position);
removeTile(static_cast<int>((p.y * worldSize_v.x) + p.x));
}

for (auto const& c : m_collectibles) {
auto p = c.getGridPosition();
removeTile(static_cast<int>((p.y * worldSize_v.x) + p.x));
}
}

void Game::spawnCollectibles() {
findEmptyTiles();

for (auto i = m_collectibles.size(); i < collectibleAmount_v; i++) {
if (m_emptyTiles.empty()) {
return;
}
// find a random tile
auto random = m_random.next_index(m_emptyTiles.size());
auto tile = m_emptyTiles[random];
// remove said tile from the vector
std::erase_if(m_emptyTiles, [&](auto const& v) {
return v == m_emptyTiles[random];
});
// place the collectible on the tile
auto width = static_cast<int>(worldSize_v.x);
m_collectibles.emplace_back(*m_collectibleTexture, worldSpace::gridToWorld({tile % width, tile / width}));
}
m_collectibles = std::make_unique<Collectibles>(*m_collectibleTexture);
}

void Game::collideCollectibles() {
auto it = std::ranges::find_if(m_collectibles, [&](auto const& collectible) {
return collectible.getGridPosition() == worldSpace::worldToGrid(m_player->getSegments().back().transform.position);
auto it = std::ranges::find_if(m_collectibles->getInstances(), [&](auto const& collectible) {
return worldSpace::worldToGrid(collectible.transform.position) == worldSpace::worldToGrid(m_player->getSegments().back().transform.position);
});
if (it == m_collectibles.end()) {
if (it == m_collectibles->getInstances().end()) {
return;
}

m_collectibles.erase(it);
m_player->grow();
spawnCollectibles();
m_collectibles->eraseInstance(static_cast<std::size_t>(std::distance(m_collectibles->getInstances().begin(), it)));
m_collectibles->spawn(*m_player);
}

void Game::onGoBack() {
Expand Down