From d054baf34a8b4ac948efa37a3bf2432505b7732a Mon Sep 17 00:00:00 2001 From: Sara Date: Wed, 4 Jun 2025 00:19:03 +0200 Subject: [PATCH] feat: implemented characters that exist within the game world --- src/core/character.cpp | 44 ++++++++++++++++++++++ src/core/character.h | 35 +++++++++++++++++ src/core/world.cpp | 85 ++++++++++++++++++++++++++++++++++++++++++ src/core/world.h | 53 ++++++++++++++++++++++++++ 4 files changed, 217 insertions(+) create mode 100644 src/core/character.cpp create mode 100644 src/core/character.h create mode 100644 src/core/world.cpp create mode 100644 src/core/world.h diff --git a/src/core/character.cpp b/src/core/character.cpp new file mode 100644 index 0000000..55e2277 --- /dev/null +++ b/src/core/character.cpp @@ -0,0 +1,44 @@ +#include "character.h" +#include "core/renderer.h" +#include "world.h" + +namespace rogue { +Character::Character(World &world, Tile location, CharacterData stats) +: data{stats} +, health{stats.health} +, location{location} +, world{world} +{} + +void Character::act() { + if(this->health < 0) { + return; + } + // get motion from logic + Tile target{this->data.logic(*this)}; + if(target == this->location) { + return; + } + // check resulting tile + TileData tile{world.query_tile(target)}; + // if character, deal damage + if(tile.character != nullptr) { + tile.character->deal_damage(this->data.damage); + } else if(!tile.is_wall) { // if empty, move + this->location = target; + } // if wall, do nothing +} + +void Character::draw() { + if(this->health > 0) { + Render::draw(this->location, this->data.sprite); + } // else { // draw gore pile +} + +bool Character::deal_damage(int damage) { + return (this->health -= damage) <= 0; +} +Tile null_character_logic_function(Character &character) { + return character.location; +} +} diff --git a/src/core/character.h b/src/core/character.h new file mode 100644 index 0000000..6bfc618 --- /dev/null +++ b/src/core/character.h @@ -0,0 +1,35 @@ +#ifndef ROGUE_CHARACTER_H +#define ROGUE_CHARACTER_H + +#include "core/roguedefs.h" + +namespace rogue { +class World; +class Character; + +typedef Tile(&CharacterLogicFunction)(Character &character); + +struct CharacterData { + int health{1}; + int damage{1}; + Sprite sprite{0}; + CharacterLogicFunction logic; +}; + +struct Character { + Character(World &world, Tile location, CharacterData stats); + + void act(); + void draw(); + bool deal_damage(int damage); + + CharacterData const data; + int health{1}; + Tile location{0, 0}; + World &world; +}; + +extern Tile null_character_logic_function(Character &character); +} + +#endif // !ROGUE_CHARACTER_H diff --git a/src/core/world.cpp b/src/core/world.cpp new file mode 100644 index 0000000..028aa84 --- /dev/null +++ b/src/core/world.cpp @@ -0,0 +1,85 @@ +#include "world.h" +#include "core/character.h" +#include + +namespace rogue { +void Room::draw() { + // TODO: . +} + +bool Room::tile_is_wall(Tile local_tile) const { + // TODO: I wonder what + return false; +} + +void World::act() { + for(Character &character : this->characters) { + character.act(); + } +} + +void World::render() { + for(Room &room : this->rooms) { + room.draw(); + } + for(Character &character : this->characters) { + character.draw(); + } +} + +Room &World::get_room(Chunk chunk) { + return rooms[chunk.x + chunk.y * this->shear]; +} + +TileData World::query_tile(Tile tile) { + Room &room{this->get_room({tile.x / this->chunk_size, tile.y / this->chunk_size})}; + TileData out{ + .character = nullptr, + .is_wall = room.tile_is_wall({tile.x % this->chunk_size, tile.y % this->chunk_size}) + }; + for(Character &character : this->characters) { + if(character.location == tile) { + out.character = &character; + return out; + } + } + return out; +} + +WorldGenerator &WorldGenerator::with_chunk_size(unsigned side_length) { + assert(this->chunk_side_length == 0); + assert(side_length > 0); + this->chunk_side_length = side_length; + return *this; +} + +WorldGenerator &WorldGenerator::with_world_size(unsigned side_length) { + assert(this->world_side_length == 0); + assert(side_length > 0); + this->world_side_length = side_length; + return *this; +} + +WorldGenerator &WorldGenerator::with_room_size(unsigned min_side_length, unsigned max_side_length) { + assert(chunk_side_length > 0 && "Chunk size needs to be initialized before room size"); + assert(min_side_length < max_side_length); + assert(min_side_length > 0); + this->min_room_size = min_side_length; + this->max_room_size = max_side_length; + return *this; +} + +World WorldGenerator::generate() { + // TODO: the rest of the fucking owl + World world{}; + Character character{Character(world, {0,0}, CharacterData { + .health = 1, + .damage = 1, + .sprite = 0, + .logic = null_character_logic_function + })}; + character.data.logic(character); + world.characters.push_back(character); + return world; +} +} diff --git a/src/core/world.h b/src/core/world.h new file mode 100644 index 0000000..cc5bb55 --- /dev/null +++ b/src/core/world.h @@ -0,0 +1,53 @@ +#ifndef ROGUE_WORLD_H +#define ROGUE_WORLD_H + +#include +#include +#include "character.h" +#include "core/roguedefs.h" + +namespace rogue { +class Character; + +struct TileData { + Character *character{nullptr}; + bool is_wall{false}; +}; + +struct Room { + Directions hallway_paths{0}; + SDL_Rect rect{0, 0, 1, 1}; + void draw(); + bool tile_is_wall(Tile local_tile) const; +}; + +struct World { + friend class WorldGenerator; + ~World() = default; + void act(); + void render(); + Room &get_room(Chunk chunk); + TileData query_tile(Tile tile); +private: + int chunk_size{1}; + unsigned shear{0}; + std::vector rooms{}; + std::vector characters{}; +private: + World() = default; +}; + +class WorldGenerator { +public: + WorldGenerator() = default; + 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); + World generate(); +private: + unsigned chunk_side_length{0}, world_side_length{0}; + unsigned max_room_size{0}, min_room_size{0}; +}; +} + +#endif // !ROGUE_WORLD_H