Compare commits
2 commits
7b6855321f
...
96a5a37733
Author | SHA1 | Date | |
---|---|---|---|
![]() |
96a5a37733 | ||
![]() |
e75acb9747 |
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -1,3 +0,0 @@
|
||||||
[submodule "vendor/callable"]
|
|
||||||
path = vendor/callable
|
|
||||||
url = forgejo@git.objectionable.solutions:Sara/cpp-callable.git
|
|
|
@ -1,26 +1,39 @@
|
||||||
#include "character.h"
|
#include "character.h"
|
||||||
#include "core/renderer.h"
|
#include "core/renderer.h"
|
||||||
#include "world.h"
|
#include "world.h"
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
namespace rogue {
|
namespace rogue {
|
||||||
Character::Character(World &world, Tile location, CharacterData stats)
|
CharacterLogic::~CharacterLogic() {}
|
||||||
|
|
||||||
|
void CharacterLogic::set_character(Character *character) {
|
||||||
|
this->character = character;
|
||||||
|
}
|
||||||
|
|
||||||
|
Character::Character(Tile location, CharacterLogic *logic, CharacterData stats)
|
||||||
: data{stats}
|
: data{stats}
|
||||||
|
, logic{logic}
|
||||||
, health{stats.health}
|
, health{stats.health}
|
||||||
, location{location}
|
, location{location}
|
||||||
, world{world}
|
, world{nullptr} {
|
||||||
{}
|
this->logic->set_character(this);
|
||||||
|
}
|
||||||
|
|
||||||
void Character::act() {
|
void Character::act() {
|
||||||
|
assert(this->world != nullptr && "World generation did not initialize character properly");
|
||||||
if(this->health < 0) {
|
if(this->health < 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this->logic->set_character(this);
|
||||||
// get motion from logic
|
// get motion from logic
|
||||||
Tile target{this->data.logic(*this)};
|
Tile target{this->logic->move()};
|
||||||
if(target == this->location) {
|
if(target == this->location
|
||||||
|
|| std::abs(target.x - this->location.x) + std::abs(target.y - this->location.y) != 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// check resulting tile
|
// check resulting tile
|
||||||
TileData tile{world.query_tile(target)};
|
TileData tile{world->query_tile(target)};
|
||||||
// if character, deal damage
|
// if character, deal damage
|
||||||
if(tile.character != nullptr) {
|
if(tile.character != nullptr) {
|
||||||
tile.character->deal_damage(this->data.damage);
|
tile.character->deal_damage(this->data.damage);
|
||||||
|
@ -38,7 +51,8 @@ void Character::draw() {
|
||||||
bool Character::deal_damage(int damage) {
|
bool Character::deal_damage(int damage) {
|
||||||
return (this->health -= damage) <= 0;
|
return (this->health -= damage) <= 0;
|
||||||
}
|
}
|
||||||
Tile null_character_logic_function(Character &character) {
|
|
||||||
return character.location;
|
Tile NullCharacterLogic::move() {
|
||||||
|
return this->character->location;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,34 +2,46 @@
|
||||||
#define ROGUE_CHARACTER_H
|
#define ROGUE_CHARACTER_H
|
||||||
|
|
||||||
#include "core/roguedefs.h"
|
#include "core/roguedefs.h"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace rogue {
|
namespace rogue {
|
||||||
class World;
|
class World;
|
||||||
class Character;
|
class Character;
|
||||||
|
|
||||||
typedef Tile(&CharacterLogicFunction)(Character &character);
|
struct CharacterLogic {
|
||||||
|
virtual ~CharacterLogic();
|
||||||
|
|
||||||
|
virtual Tile move() = 0;
|
||||||
|
protected:
|
||||||
|
Character *character{nullptr};
|
||||||
|
private:
|
||||||
|
friend class Character;
|
||||||
|
void set_character(Character *character);
|
||||||
|
};
|
||||||
|
|
||||||
struct CharacterData {
|
struct CharacterData {
|
||||||
int health{1};
|
int health{1};
|
||||||
int damage{1};
|
int damage{1};
|
||||||
Sprite sprite{0};
|
Sprite sprite{0};
|
||||||
CharacterLogicFunction logic;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Character {
|
struct Character {
|
||||||
Character(World &world, Tile location, CharacterData stats);
|
Character(Tile location, CharacterLogic *logic, CharacterData stats);
|
||||||
|
|
||||||
void act();
|
void act();
|
||||||
void draw();
|
void draw();
|
||||||
bool deal_damage(int damage);
|
bool deal_damage(int damage);
|
||||||
|
|
||||||
CharacterData const data;
|
CharacterData const data;
|
||||||
|
std::shared_ptr<CharacterLogic> logic{nullptr};
|
||||||
int health{1};
|
int health{1};
|
||||||
Tile location{0, 0};
|
Tile location{0, 0};
|
||||||
World &world;
|
World *world{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Tile null_character_logic_function(Character &character);
|
struct NullCharacterLogic : public CharacterLogic {
|
||||||
|
virtual Tile move() override;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // !ROGUE_CHARACTER_H
|
#endif // !ROGUE_CHARACTER_H
|
||||||
|
|
40
src/core/input.cpp
Normal file
40
src/core/input.cpp
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#include "input.h"
|
||||||
|
#include <SDL2/SDL_events.h>
|
||||||
|
#include <SDL2/SDL_log.h>
|
||||||
|
#include <SDL2/SDL_scancode.h>
|
||||||
|
|
||||||
|
namespace rogue {
|
||||||
|
InputHandler::InputHandler() {
|
||||||
|
Input::add_handler(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
InputHandler::~InputHandler() {
|
||||||
|
Input::remove_handler(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyEventHandler::KeyEventHandler(std::set<SDL_Scancode> code, Listener listener)
|
||||||
|
: scancode{code}
|
||||||
|
, listener{listener} {}
|
||||||
|
|
||||||
|
void KeyEventHandler::process_event(SDL_Event event) {
|
||||||
|
if((event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) && this->scancode.contains(event.key.keysym.scancode)) {
|
||||||
|
this->listener(event.type == SDL_KEYDOWN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Input::add_handler(InputHandler *handler) {
|
||||||
|
Input::handlers.insert(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Input::remove_handler(InputHandler *handler) {
|
||||||
|
Input::handlers.erase(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Input::process_event(SDL_Event event) {
|
||||||
|
for(InputHandler *handler : Input::handlers) {
|
||||||
|
handler->process_event(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<InputHandler*> Input::handlers{};
|
||||||
|
}
|
34
src/core/input.h
Normal file
34
src/core/input.h
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef ROGUE_INPUT_H
|
||||||
|
#define ROGUE_INPUT_H
|
||||||
|
|
||||||
|
#include <SDL2/SDL_events.h>
|
||||||
|
#include <SDL2/SDL_scancode.h>
|
||||||
|
#include <functional>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
namespace rogue {
|
||||||
|
struct InputHandler {
|
||||||
|
InputHandler();
|
||||||
|
virtual ~InputHandler();
|
||||||
|
virtual void process_event(SDL_Event event) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct KeyEventHandler : public InputHandler {
|
||||||
|
typedef std::function<void(bool down)> Listener;
|
||||||
|
KeyEventHandler(std::set<SDL_Scancode> code, Listener listener);
|
||||||
|
virtual void process_event(SDL_Event event) override;
|
||||||
|
std::set<SDL_Scancode> scancode{};
|
||||||
|
Listener listener{};
|
||||||
|
};
|
||||||
|
|
||||||
|
class Input {
|
||||||
|
public:
|
||||||
|
static void add_handler(InputHandler *handler);
|
||||||
|
static void remove_handler(InputHandler *handler);
|
||||||
|
static void process_event(SDL_Event event);
|
||||||
|
private:
|
||||||
|
static std::set<InputHandler*> handlers;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // !ROGUE_INPUT_H
|
|
@ -4,6 +4,7 @@
|
||||||
#include "core/roguedefs.h"
|
#include "core/roguedefs.h"
|
||||||
#include <SDL2/SDL_log.h>
|
#include <SDL2/SDL_log.h>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace rogue {
|
namespace rogue {
|
||||||
void Room::draw(Tile world_pivot) {
|
void Room::draw(Tile world_pivot) {
|
||||||
|
@ -36,6 +37,14 @@ bool Room::tile_is_wall(Tile local_tile) const {
|
||||||
return is_outside_room && is_outside_hallways;
|
return is_outside_room && is_outside_hallways;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
World::World()
|
||||||
|
: direction_pressed{{
|
||||||
|
SDL_SCANCODE_H, SDL_SCANCODE_J, SDL_SCANCODE_K, SDL_SCANCODE_L,
|
||||||
|
SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_UP, SDL_SCANCODE_DOWN
|
||||||
|
},
|
||||||
|
std::bind(&World::on_input, this, std::placeholders::_1)
|
||||||
|
} {}
|
||||||
|
|
||||||
void World::act() {
|
void World::act() {
|
||||||
for(Character &character : this->characters) {
|
for(Character &character : this->characters) {
|
||||||
character.act();
|
character.act();
|
||||||
|
@ -43,7 +52,7 @@ void World::act() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::render() {
|
void World::render() {
|
||||||
Render::clear({this->chunk_size/2, this->chunk_size/2});
|
Render::clear(this->player->location);
|
||||||
for(size_t i{0}; i < this->rooms.size(); ++i) {
|
for(size_t i{0}; i < this->rooms.size(); ++i) {
|
||||||
this->rooms[i].draw({
|
this->rooms[i].draw({
|
||||||
.x = int(i % this->shear * this->chunk_size),
|
.x = int(i % this->shear * this->chunk_size),
|
||||||
|
@ -74,6 +83,12 @@ TileData World::query_tile(Tile tile) {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void World::on_input(bool pressed) {
|
||||||
|
if(!pressed) {
|
||||||
|
this->act();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
WorldGenerator &WorldGenerator::with_chunk_size(unsigned side_length) {
|
WorldGenerator &WorldGenerator::with_chunk_size(unsigned side_length) {
|
||||||
assert(this->chunk_side_length == 0);
|
assert(this->chunk_side_length == 0);
|
||||||
assert(side_length > 0);
|
assert(side_length > 0);
|
||||||
|
@ -97,22 +112,25 @@ WorldGenerator &WorldGenerator::with_room_size(unsigned min_side_length, unsigne
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
World WorldGenerator::generate() {
|
WorldGenerator &WorldGenerator::with_player(Character character) {
|
||||||
|
this->player.emplace(character);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<World> WorldGenerator::generate() {
|
||||||
assert(this->chunk_side_length > 0 && "Chunk size is required to generate world");
|
assert(this->chunk_side_length > 0 && "Chunk size is required to generate world");
|
||||||
assert(this->min_room_size > 0 && "Room size is required to generate world");
|
assert(this->min_room_size > 0 && "Room size is required to generate world");
|
||||||
assert(this->min_room_size < this->max_room_size && "Room size is required to generate world");
|
assert(this->min_room_size < this->max_room_size && "Room size is required to generate world");
|
||||||
assert(this->world_side_length > 0 && "World size is required to generate world");
|
assert(this->world_side_length > 0 && "World size is required to generate world");
|
||||||
World world{};
|
assert(this->player.has_value() && "World cannot be generated without a player");
|
||||||
world.chunk_size = this->chunk_side_length;
|
std::unique_ptr<World> world{new World()};
|
||||||
world.shear = this->world_side_length;
|
world->characters.push_back(player.value());
|
||||||
world.characters.push_back(Character(world, {this->chunk_side_length/2, this->chunk_side_length/2}, CharacterData {
|
world->player = &world->characters[0];
|
||||||
.health = 1,
|
world->player->world = world.get();
|
||||||
.damage = 1,
|
world->chunk_size = this->chunk_side_length;
|
||||||
.sprite = 0,
|
world->shear = this->world_side_length;
|
||||||
.logic = null_character_logic_function
|
world->rooms.reserve(this->world_side_length * this->world_side_length);
|
||||||
}));
|
world->rooms.push_back(Room {
|
||||||
world.rooms.reserve(this->world_side_length * this->world_side_length);
|
|
||||||
world.rooms.push_back(Room {
|
|
||||||
.hallway_paths = Directions(Directions::NORTH | Directions::EAST),
|
.hallway_paths = Directions(Directions::NORTH | Directions::EAST),
|
||||||
.chunk_size = this->chunk_side_length,
|
.chunk_size = this->chunk_side_length,
|
||||||
.rect = {
|
.rect = {
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
#ifndef ROGUE_WORLD_H
|
#ifndef ROGUE_WORLD_H
|
||||||
#define ROGUE_WORLD_H
|
#define ROGUE_WORLD_H
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <SDL2/SDL_rect.h>
|
#include <SDL2/SDL_rect.h>
|
||||||
#include "character.h"
|
#include "character.h"
|
||||||
|
#include "core/input.h"
|
||||||
#include "core/roguedefs.h"
|
#include "core/roguedefs.h"
|
||||||
|
|
||||||
namespace rogue {
|
namespace rogue {
|
||||||
|
@ -26,18 +28,21 @@ struct Room {
|
||||||
|
|
||||||
struct World {
|
struct World {
|
||||||
friend class WorldGenerator;
|
friend class WorldGenerator;
|
||||||
|
World();
|
||||||
~World() = default;
|
~World() = default;
|
||||||
void act();
|
void act();
|
||||||
void render();
|
void render();
|
||||||
Room &get_room(Chunk chunk);
|
Room &get_room(Chunk chunk);
|
||||||
TileData query_tile(Tile tile);
|
TileData query_tile(Tile tile);
|
||||||
|
void on_input(bool);
|
||||||
private:
|
private:
|
||||||
int chunk_size{1};
|
int chunk_size{1};
|
||||||
unsigned shear{0};
|
unsigned shear{0};
|
||||||
|
Character *player{nullptr};
|
||||||
std::vector<Room> rooms{};
|
std::vector<Room> rooms{};
|
||||||
std::vector<Character> characters{};
|
std::vector<Character> characters{};
|
||||||
|
KeyEventHandler direction_pressed;
|
||||||
private:
|
private:
|
||||||
World() = default;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class WorldGenerator {
|
class WorldGenerator {
|
||||||
|
@ -46,8 +51,10 @@ public:
|
||||||
WorldGenerator &with_chunk_size(unsigned side_length);
|
WorldGenerator &with_chunk_size(unsigned side_length);
|
||||||
WorldGenerator &with_world_size(unsigned side_length);
|
WorldGenerator &with_world_size(unsigned side_length);
|
||||||
WorldGenerator &with_room_size(unsigned min_side_length, unsigned max_side_length);
|
WorldGenerator &with_room_size(unsigned min_side_length, unsigned max_side_length);
|
||||||
World generate();
|
WorldGenerator &with_player(Character character);
|
||||||
|
std::unique_ptr<World> generate();
|
||||||
private:
|
private:
|
||||||
|
std::optional<Character> player;
|
||||||
int chunk_side_length{0}, world_side_length{0};
|
int chunk_side_length{0}, world_side_length{0};
|
||||||
int max_room_size{0}, min_room_size{0};
|
int max_room_size{0}, min_room_size{0};
|
||||||
};
|
};
|
||||||
|
|
18
src/main.cpp
18
src/main.cpp
|
@ -1,14 +1,26 @@
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
#include <memory>
|
||||||
|
#include "core/character.h"
|
||||||
|
#include "core/input.h"
|
||||||
#include "core/renderer.h"
|
#include "core/renderer.h"
|
||||||
#include "core/world.h"
|
#include "core/world.h"
|
||||||
|
#include "player_logic.h"
|
||||||
|
|
||||||
using namespace rogue;
|
using namespace rogue;
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
World world{WorldGenerator()
|
std::unique_ptr<World> world{WorldGenerator()
|
||||||
.with_world_size(5)
|
.with_world_size(5)
|
||||||
.with_chunk_size(9)
|
.with_chunk_size(9)
|
||||||
.with_room_size(2, 5)
|
.with_room_size(2, 5)
|
||||||
|
.with_player(Character({9/2, 9/2},
|
||||||
|
new PlayerCharacterLogic(),
|
||||||
|
CharacterData {
|
||||||
|
.health = 1,
|
||||||
|
.damage = 1,
|
||||||
|
.sprite = 0
|
||||||
|
}
|
||||||
|
))
|
||||||
.generate()
|
.generate()
|
||||||
};
|
};
|
||||||
RenderData data{RenderDataSetup()
|
RenderData data{RenderDataSetup()
|
||||||
|
@ -22,11 +34,13 @@ int main(int argc, char *argv[]) {
|
||||||
Render::provide_render_data(data);
|
Render::provide_render_data(data);
|
||||||
SDL_Event evt;
|
SDL_Event evt;
|
||||||
for(;;) {
|
for(;;) {
|
||||||
world.render();
|
world->render();
|
||||||
Render::present();
|
Render::present();
|
||||||
if(SDL_WaitEvent(&evt)) {
|
if(SDL_WaitEvent(&evt)) {
|
||||||
if(evt.type == SDL_QUIT) {
|
if(evt.type == SDL_QUIT) {
|
||||||
goto main_loop;
|
goto main_loop;
|
||||||
|
} else {
|
||||||
|
Input::process_event(evt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} main_loop:
|
} main_loop:
|
||||||
|
|
46
src/player_logic.cpp
Normal file
46
src/player_logic.cpp
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
#include "player_logic.h"
|
||||||
|
#include <SDL2/SDL_log.h>
|
||||||
|
|
||||||
|
namespace rogue {
|
||||||
|
PlayerCharacterLogic::PlayerCharacterLogic()
|
||||||
|
: up{{SDL_SCANCODE_K, SDL_SCANCODE_UP}, std::bind(&PlayerCharacterLogic::on_up, this, std::placeholders::_1)}
|
||||||
|
, down{{SDL_SCANCODE_J, SDL_SCANCODE_DOWN}, std::bind(&PlayerCharacterLogic::on_down, this, std::placeholders::_1)}
|
||||||
|
, right{{SDL_SCANCODE_L, SDL_SCANCODE_RIGHT}, std::bind(&PlayerCharacterLogic::on_right, this, std::placeholders::_1)}
|
||||||
|
, left{{SDL_SCANCODE_H, SDL_SCANCODE_LEFT}, std::bind(&PlayerCharacterLogic::on_left, this, std::placeholders::_1)}
|
||||||
|
{}
|
||||||
|
|
||||||
|
Tile PlayerCharacterLogic::move() {
|
||||||
|
return {
|
||||||
|
this->character->location.x + input_x,
|
||||||
|
this->character->location.y + input_y
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerCharacterLogic::on_up(bool pressed) {
|
||||||
|
if(pressed) {
|
||||||
|
input_x = 0;
|
||||||
|
input_y = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerCharacterLogic::on_down(bool pressed) {
|
||||||
|
if(pressed) {
|
||||||
|
input_x = 0;
|
||||||
|
input_y = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerCharacterLogic::on_left(bool pressed) {
|
||||||
|
if(pressed) {
|
||||||
|
input_x = -1;
|
||||||
|
input_y = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerCharacterLogic::on_right(bool pressed) {
|
||||||
|
if(pressed) {
|
||||||
|
input_x = 1;
|
||||||
|
input_y = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
27
src/player_logic.h
Normal file
27
src/player_logic.h
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#ifndef PLAYER_LOGIC_H
|
||||||
|
#define PLAYER_LOGIC_H
|
||||||
|
|
||||||
|
#include "core/character.h"
|
||||||
|
#include "core/input.h"
|
||||||
|
#include "core/roguedefs.h"
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace rogue {
|
||||||
|
class PlayerCharacterLogic : public CharacterLogic {
|
||||||
|
public:
|
||||||
|
PlayerCharacterLogic();
|
||||||
|
private:
|
||||||
|
virtual Tile move() override;
|
||||||
|
void on_up(bool pressed);
|
||||||
|
void on_down(bool pressed);
|
||||||
|
void on_left(bool pressed);
|
||||||
|
void on_right(bool pressed);
|
||||||
|
int input_x{0}, input_y{0};
|
||||||
|
KeyEventHandler up;
|
||||||
|
KeyEventHandler down;
|
||||||
|
KeyEventHandler right;
|
||||||
|
KeyEventHandler left;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // !PLAYER_LOGIC_H
|
1
vendor/callable
vendored
1
vendor/callable
vendored
|
@ -1 +0,0 @@
|
||||||
Subproject commit ea96247ecb258ea9938f038f410e0fd2739aa5de
|
|
Loading…
Reference in a new issue