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

View file

@ -2,20 +2,24 @@
#define ROGUE_CHARACTER_H
#include "core/roguedefs.h"
#include <functional>
#include <memory>
namespace rogue {
class World;
class Character;
// Character Logic, abstract Subclass Sandbox Pattern implementation that declares the necessary bits to communicate with the character
struct CharacterLogic {
virtual ~CharacterLogic();
virtual Tile move() = 0;
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:
friend class Character;
friend class Character; // hide the character
void set_character(Character *character);
};
@ -25,22 +29,44 @@ struct CharacterData {
Sprite sprite{0};
};
// Character class, serves as the primary entity type in the game
struct Character {
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 draw();
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};
Tile location{0, 0};
World *world{nullptr};
private:
// The Subclass Sandbox Pattern instance that implements the behavioural logic of this character.
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 {
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.h -= border * 2;
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);
}

View file

@ -33,7 +33,7 @@ public:
static void clear(Tile camera_center, unsigned fov = 10);
static void draw(Tile world_space_tile, Sprite sprite);
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();
private:
static Tile camera_center;

View file

@ -63,7 +63,7 @@ void World::act() {
}
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) {
this->rooms[i].draw({
.x = int(i % this->shear * this->chunk_size),
@ -75,7 +75,7 @@ void World::render() {
}
boss.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) {

View file

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

View file

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