feat: documentation and balancing

This commit is contained in:
Sara 2025-06-07 22:10:28 +02:00
parent f22927e124
commit 7ff9756dcc
7 changed files with 55 additions and 34 deletions

View file

@ -11,9 +11,9 @@ void CharacterLogic::set_character(Character *character) {
this->character = character; this->character = character;
} }
Character::Character(Tile location, CharacterLogic *logic, CharacterData stats) Character::Character(Tile location, CharacterLogic *logic, CharacterData const *stats)
: data{stats} : data{stats}
, health{stats.health} , health{stats->health}
, location{location} , location{location}
, world{nullptr} , world{nullptr}
, logic{logic} { , logic{logic} {
@ -36,7 +36,7 @@ void Character::act() {
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);
} else if(!tile.is_wall) { // if empty, move } else if(!tile.is_wall) { // if empty, move
this->location = target; this->location = target;
} // if wall, do nothing } // if wall, do nothing
@ -44,7 +44,7 @@ void Character::act() {
void Character::draw() { void Character::draw() {
if(this->health > 0) { if(this->health > 0) {
Render::draw(this->location, this->data.sprite); Render::draw(this->location, this->data->sprite);
} // else { // draw gore pile } // else { // draw gore pile
} }

View file

@ -2,20 +2,24 @@
#define ROGUE_CHARACTER_H #define ROGUE_CHARACTER_H
#include "core/roguedefs.h" #include "core/roguedefs.h"
#include <functional>
#include <memory> #include <memory>
namespace rogue { namespace rogue {
class World; class World;
class Character; class Character;
// Character Logic, abstract Subclass Sandbox Pattern implementation that declares the necessary bits to communicate with the character
struct CharacterLogic { struct CharacterLogic {
virtual ~CharacterLogic(); virtual ~CharacterLogic();
virtual Tile move() = 0; virtual Tile move() = 0;
protected: protected:
Character *character{nullptr}; // CharacterLogic doesn't need to do anything else,
//so we limit their interaction with the world to just the character (which exposes the parent World instance)
Character const *character{nullptr};
private: private:
friend class Character; friend class Character; // hide the character
void set_character(Character *character); void set_character(Character *character);
}; };
@ -25,22 +29,44 @@ struct CharacterData {
Sprite sprite{0}; Sprite sprite{0};
}; };
// Character class, serves as the primary entity type in the game
struct Character { struct Character {
Character() = default; Character() = default;
Character(Tile location, CharacterLogic *logic, CharacterData stats); Character(Tile location, CharacterLogic *logic, CharacterData const *stats);
typedef std::function<Character(Tile)> Spawner; // <functional> style Factory Pattern
void act(); void act();
void draw(); void draw();
bool deal_damage(int damage); bool deal_damage(int damage);
CharacterData data{}; CharacterData const *data{}; // Type Object pattern implemented as a Flyweight
// marked constant as it is shared between instances of the same type.
int health{1}; int health{1};
Tile location{0, 0}; Tile location{0, 0};
World *world{nullptr}; World *world{nullptr};
private: private:
// The Subclass Sandbox Pattern instance that implements the behavioural logic of this character.
std::shared_ptr<CharacterLogic> logic{nullptr}; std::shared_ptr<CharacterLogic> logic{nullptr};
public:
// Simplifies creating a new character logic instance for construction of a Character
// bind into a Factory object using make_factory to pass to world generators
template <typename TLogicType>
static Character create(CharacterData const &stats, Tile tile);
// binds Character::create into a std::function,
// making a Factory Pattern object to pass to WorldGenerator as a spawner
template <typename TLogicType>
static Spawner make_factory(CharacterData const &stats);
}; };
template <typename TLogicType>
Character Character::create(CharacterData const &stats, Tile tile) {
return Character(tile, new TLogicType, &stats);
}
template <typename TLogicType>
Character::Spawner Character::make_factory(CharacterData const &stats) {
return std::bind(&Character::create<TLogicType>, stats, std::placeholders::_1);
}
struct NullCharacterLogic : public CharacterLogic { struct NullCharacterLogic : public CharacterLogic {
virtual Tile move() override; virtual Tile move() override;
}; };

View file

@ -144,7 +144,7 @@ void Render::draw_healthbar(int current, int max, SDL_Rect area, int border) {
area.y += border; area.y += border;
area.h -= border * 2; area.h -= border * 2;
area.w = ((area.w - border * 2) * current) / max; area.w = ((area.w - border * 2) * current) / max;
SDL_SetRenderDrawColor(Render::data->renderer, 200,200, 200, 255); SDL_SetRenderDrawColor(Render::data->renderer, 100,100, 100, 255);
SDL_RenderFillRect(Render::data->renderer, &area); SDL_RenderFillRect(Render::data->renderer, &area);
} }

View file

@ -33,7 +33,7 @@ public:
static void clear(Tile camera_center, unsigned fov = 10); static void clear(Tile camera_center, unsigned fov = 10);
static void draw(Tile world_space_tile, Sprite sprite); static void draw(Tile world_space_tile, Sprite sprite);
static void draw_menu(Sprite menu); static void draw_menu(Sprite menu);
static void draw_healthbar(int current, int max, SDL_Rect area = {0, 0, 500, 100}, int border = 5); static void draw_healthbar(int current, int max, SDL_Rect area={0,0,500,50}, int border=10);
static void present(); static void present();
private: private:
static Tile camera_center; static Tile camera_center;

View file

@ -63,7 +63,7 @@ void World::act() {
} }
void World::render() { void World::render() {
Render::clear(this->player.location, 100); Render::clear(this->player.location, 20);
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),
@ -75,7 +75,7 @@ void World::render() {
} }
boss.draw(); boss.draw();
player.draw(); player.draw();
Render::draw_healthbar(player.health, player.data.health); Render::draw_healthbar(player.health, player.data->health);
} }
Room &World::get_room(Chunk chunk) { Room &World::get_room(Chunk chunk) {

View file

@ -1,7 +1,6 @@
#ifndef ROGUE_WORLD_H #ifndef ROGUE_WORLD_H
#define ROGUE_WORLD_H #define ROGUE_WORLD_H
#include <optional>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <SDL2/SDL_rect.h> #include <SDL2/SDL_rect.h>
@ -58,9 +57,9 @@ 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);
WorldGenerator &with_player(std::function<Character(Tile)> spawner); WorldGenerator &with_player(Character::Spawner spawner);
WorldGenerator &with_enemy(std::function<Character(Tile)> spawner, int inv_frequency); WorldGenerator &with_enemy(Character::Spawner spawner, int inv_frequency);
WorldGenerator &with_boss(std::function<Character(Tile)> spawner); WorldGenerator &with_boss(Character::Spawner spawner);
WorldGenerator &with_connecting_chance(unsigned num); WorldGenerator &with_connecting_chance(unsigned num);
std::unique_ptr<World> generate(); std::unique_ptr<World> generate();
private: private:
@ -69,9 +68,9 @@ private:
void add_enemies(std::unique_ptr<World> &world, Chunk const &chunk, Room &room); void add_enemies(std::unique_ptr<World> &world, Chunk const &chunk, Room &room);
void setup_player(std::unique_ptr<World> &world, Chunk spawn_chunk); void setup_player(std::unique_ptr<World> &world, Chunk spawn_chunk);
private: private:
std::function<Character(Tile)> player_spawner{}; Character::Spawner player_spawner{};
std::function<Character(Tile)> boss_spawner{}; Character::Spawner boss_spawner{};
std::vector<std::pair<std::function<Character(Tile)>, int>> enemy_spawners{}; std::vector<std::pair<Character::Spawner, int>> enemy_spawners{};
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};
unsigned connecting_chance{1}; unsigned connecting_chance{1};

View file

@ -18,13 +18,13 @@ enum GameState {
}; };
CharacterData const player_data{ CharacterData const player_data{
.health = 20, .health = 30,
.damage = 2, .damage = 2,
.sprite = 3 .sprite = 3
}; };
CharacterData const boss_data{ CharacterData const boss_data{
.health = 10, .health = 1,
.damage = 3, .damage = 3,
.sprite = 0 .sprite = 0
}; };
@ -35,23 +35,19 @@ CharacterData const critte_data{
.sprite = 2 .sprite = 2
}; };
template <typename TLogicType>
static Character create_character(CharacterData const &stats, Tile tile) {
return Character(tile, new TLogicType, stats);
}
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
GameState game_state{GameState::MainMenu}; GameState game_state{GameState::MainMenu};
WorldGenerator generator{WorldGenerator() WorldGenerator generator{WorldGenerator()
.with_world_size(10) .with_world_size(6)
.with_chunk_size(10) .with_chunk_size(8)
.with_room_size(6, 10) .with_room_size(5, 8)
.with_connecting_chance(5) .with_connecting_chance(5)
.with_player(std::bind(&create_character<PlayerCharacterLogic>, player_data, std::placeholders::_1)) .with_player(Character::make_factory<PlayerCharacterLogic>(player_data))
.with_enemy(std::bind(&create_character<CritteLogic>, critte_data, std::placeholders::_1), 2) .with_enemy(Character::make_factory<CritteLogic>(critte_data), 2)
.with_enemy(std::bind(&create_character<CritteLogic>, critte_data, std::placeholders::_1), 2) .with_enemy(Character::make_factory<CritteLogic>(critte_data), 3)
.with_enemy(std::bind(&create_character<CritteLogic>, critte_data, std::placeholders::_1), 2) .with_enemy(Character::make_factory<CritteLogic>(critte_data), 10)
.with_boss(std::bind(&create_character<CritteLogic>, boss_data, std::placeholders::_1))}; .with_boss(Character::make_factory<CritteLogic>(boss_data))};
RenderData data{RenderDataSetup() RenderData data{RenderDataSetup()
// create window and renderer, allowing for texture creation // create window and renderer, allowing for texture creation
.with_window("roguelike", SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_FULLSCREEN) .with_window("roguelike", SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_FULLSCREEN)