feat: world generation and initial work for enemy spawning
This commit is contained in:
parent
1f6be6b92b
commit
9b332a09a9
BIN
resources/critte.png
Normal file
BIN
resources/critte.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 216 B |
BIN
resources/player.png
Normal file
BIN
resources/player.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 296 B |
Binary file not shown.
Before Width: | Height: | Size: 386 B After Width: | Height: | Size: 383 B |
Binary file not shown.
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 380 B |
|
@ -26,17 +26,19 @@ struct CharacterData {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Character {
|
struct Character {
|
||||||
|
Character() = default;
|
||||||
Character(Tile location, CharacterLogic *logic, 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 data{};
|
||||||
std::shared_ptr<CharacterLogic> logic{nullptr};
|
|
||||||
int health{1};
|
int health{1};
|
||||||
Tile location{0, 0};
|
Tile location{0, 0};
|
||||||
World *world{nullptr};
|
World *world{nullptr};
|
||||||
|
private:
|
||||||
|
std::shared_ptr<CharacterLogic> logic{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NullCharacterLogic : public CharacterLogic {
|
struct NullCharacterLogic : public CharacterLogic {
|
||||||
|
|
|
@ -77,14 +77,15 @@ void Render::provide_render_data(RenderData &data) {
|
||||||
Render::data = &data;
|
Render::data = &data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Render::clear(Tile camera_center) {
|
void Render::clear(Tile camera_center, unsigned fov) {
|
||||||
assert_render_initialized();
|
assert_render_initialized();
|
||||||
|
Render::camera_width = fov;
|
||||||
SDL_SetRenderDrawColor(Render::data->renderer, 0u, 0u, 0u, 255u);
|
SDL_SetRenderDrawColor(Render::data->renderer, 0u, 0u, 0u, 255u);
|
||||||
SDL_RenderClear(Render::data->renderer);
|
SDL_RenderClear(Render::data->renderer);
|
||||||
|
|
||||||
int width, height;
|
int width, height;
|
||||||
SDL_GetWindowSize(Render::data->window, &width, &height);
|
SDL_GetWindowSize(Render::data->window, &width, &height);
|
||||||
Render::tile_screen_width = int(width / camera_width);
|
Render::tile_screen_width = int(width / Render::camera_width);
|
||||||
Render::half_tile_screen_width = Render::tile_screen_width >> 1;
|
Render::half_tile_screen_width = Render::tile_screen_width >> 1;
|
||||||
|
|
||||||
Render::camera_center = camera_center;
|
Render::camera_center = camera_center;
|
||||||
|
@ -94,6 +95,10 @@ void Render::clear(Tile camera_center) {
|
||||||
|
|
||||||
void Render::draw(Tile world_space_tile, Sprite sprite) {
|
void Render::draw(Tile world_space_tile, Sprite sprite) {
|
||||||
assert_render_initialized();
|
assert_render_initialized();
|
||||||
|
if(abs(world_space_tile.x - Render::camera_center.x) > Render::camera_width / 2
|
||||||
|
|| abs(world_space_tile.y - Render::camera_center.y) > Render::camera_width / 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
SDL_Rect const rect{
|
SDL_Rect const rect{
|
||||||
.x = (world_space_tile.x * Render::tile_screen_width) - Render::half_tile_screen_width + Render::camera_offset.x,
|
.x = (world_space_tile.x * Render::tile_screen_width) - Render::half_tile_screen_width + Render::camera_offset.x,
|
||||||
.y = (world_space_tile.y * Render::tile_screen_width) - Render::half_tile_screen_width + Render::camera_offset.y,
|
.y = (world_space_tile.y * Render::tile_screen_width) - Render::half_tile_screen_width + Render::camera_offset.y,
|
||||||
|
|
|
@ -29,7 +29,7 @@ private:
|
||||||
public:
|
public:
|
||||||
static void provide_render_data(RenderData &data);
|
static void provide_render_data(RenderData &data);
|
||||||
|
|
||||||
static void clear(Tile camera_center);
|
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 present();
|
static void present();
|
||||||
private:
|
private:
|
||||||
|
@ -51,6 +51,7 @@ public:
|
||||||
RenderDataSetup &with_sprite(std::filesystem::path sprite_path);
|
RenderDataSetup &with_sprite(std::filesystem::path sprite_path);
|
||||||
RenderData build();
|
RenderData build();
|
||||||
private:
|
private:
|
||||||
|
unsigned fov{10};
|
||||||
std::optional<std::filesystem::path> resource_base_path;
|
std::optional<std::filesystem::path> resource_base_path;
|
||||||
SDL_Window *window{nullptr};
|
SDL_Window *window{nullptr};
|
||||||
SDL_Renderer *renderer{nullptr};
|
SDL_Renderer *renderer{nullptr};
|
||||||
|
|
|
@ -3,12 +3,14 @@
|
||||||
|
|
||||||
#include "SDL2/SDL_rect.h"
|
#include "SDL2/SDL_rect.h"
|
||||||
|
|
||||||
enum Directions : unsigned short {
|
|
||||||
NORTH = 0x1,
|
typedef unsigned short Directions;
|
||||||
EAST = 0x2,
|
#define NORTH 0x1u
|
||||||
SOUTH = 0x4,
|
#define EAST (0x1u << 1)
|
||||||
WEST = 0x8,
|
#define SOUTH (0x1u << 2)
|
||||||
};
|
#define WEST (0x1u << 3)
|
||||||
|
|
||||||
|
#define WRAP(M_x, M_min, M_max) (((M_x - M_min) % (M_max - M_min)) + M_min)
|
||||||
|
|
||||||
#define casel(M_switch, M_case)\
|
#define casel(M_switch, M_case)\
|
||||||
case M_case:\
|
case M_case:\
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "core/renderer.h"
|
#include "core/renderer.h"
|
||||||
#include "core/roguedefs.h"
|
#include "core/roguedefs.h"
|
||||||
#include <SDL2/SDL_log.h>
|
#include <SDL2/SDL_log.h>
|
||||||
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
@ -30,10 +31,10 @@ bool Room::tile_is_wall(Tile local_tile) const {
|
||||||
|| local_tile.y >= this->rect.y + this->rect.h-1};
|
|| local_tile.y >= this->rect.y + this->rect.h-1};
|
||||||
int chunk_size2 = chunk_size/2;
|
int chunk_size2 = chunk_size/2;
|
||||||
bool const is_outside_hallways{
|
bool const is_outside_hallways{
|
||||||
((this->hallway_paths & Directions::NORTH) == 0 || local_tile.x != chunk_size / 2 || local_tile.y > chunk_size2)
|
((this->hallway_paths & NORTH) == 0 || local_tile.x != chunk_size / 2 || local_tile.y > chunk_size2)
|
||||||
&& ((this->hallway_paths & Directions::SOUTH) == 0 || local_tile.x != chunk_size / 2 || local_tile.y < chunk_size2)
|
&& ((this->hallway_paths & SOUTH) == 0 || local_tile.x != chunk_size / 2 || local_tile.y < chunk_size2)
|
||||||
&& ((this->hallway_paths & Directions::WEST) == 0 || local_tile.y != chunk_size / 2 || local_tile.x < chunk_size2)
|
&& ((this->hallway_paths & WEST) == 0 || local_tile.y != chunk_size / 2 || local_tile.x > chunk_size2)
|
||||||
&& ((this->hallway_paths & Directions::EAST) == 0 || local_tile.y != chunk_size / 2 || local_tile.x > chunk_size2)
|
&& ((this->hallway_paths & EAST) == 0 || local_tile.y != chunk_size / 2 || local_tile.x < chunk_size2)
|
||||||
};
|
};
|
||||||
return is_outside_room && is_outside_hallways;
|
return is_outside_room && is_outside_hallways;
|
||||||
}
|
}
|
||||||
|
@ -47,13 +48,14 @@ World::World()
|
||||||
} {}
|
} {}
|
||||||
|
|
||||||
void World::act() {
|
void World::act() {
|
||||||
|
player.act();
|
||||||
for(Character &character : this->characters) {
|
for(Character &character : this->characters) {
|
||||||
character.act();
|
character.act();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::render() {
|
void World::render() {
|
||||||
Render::clear(this->player->location);
|
Render::clear(this->player.location, 15);
|
||||||
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),
|
||||||
|
@ -63,20 +65,33 @@ void World::render() {
|
||||||
for(Character &character : this->characters) {
|
for(Character &character : this->characters) {
|
||||||
character.draw();
|
character.draw();
|
||||||
}
|
}
|
||||||
|
player.draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
Room &World::get_room(Chunk chunk) {
|
Room &World::get_room(Chunk chunk) {
|
||||||
return rooms[chunk.x + chunk.y * this->shear];
|
return rooms[chunk.x + chunk.y * this->shear];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Character *World::get_player() {
|
||||||
|
return &this->player;
|
||||||
|
}
|
||||||
|
|
||||||
|
Chunk World::get_chunk(Tile tile) {
|
||||||
|
return {tile.x / this->chunk_size, tile.y / this->chunk_size};
|
||||||
|
}
|
||||||
|
|
||||||
TileData World::query_tile(Tile tile) {
|
TileData World::query_tile(Tile tile) {
|
||||||
Room &room{this->get_room({tile.x / this->chunk_size, tile.y / this->chunk_size})};
|
Room &room{this->get_room({tile.x / this->chunk_size, tile.y / this->chunk_size})};
|
||||||
TileData out{
|
TileData out{
|
||||||
.character = nullptr,
|
.character = nullptr,
|
||||||
.is_wall = room.tile_is_wall({tile.x % this->chunk_size, tile.y % this->chunk_size})
|
.is_wall = room.tile_is_wall({tile.x % this->chunk_size, tile.y % this->chunk_size})
|
||||||
};
|
};
|
||||||
|
if(player.location == tile) {
|
||||||
|
out.character = &player;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
for(Character &character : this->characters) {
|
for(Character &character : this->characters) {
|
||||||
if(character.location == tile) {
|
if(character.location == tile && character.health > 0) {
|
||||||
out.character = &character;
|
out.character = &character;
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
@ -118,6 +133,16 @@ WorldGenerator &WorldGenerator::with_player(Character character) {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WorldGenerator &WorldGenerator::with_enemy(std::function<Character(Tile)> spawner, int inv_frequency) {
|
||||||
|
this->enemy_spawners.push_back({spawner, inv_frequency});
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
WorldGenerator &WorldGenerator::with_connecting_chance(unsigned num) {
|
||||||
|
this->connecting_chance = num;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<World> WorldGenerator::generate() {
|
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");
|
||||||
|
@ -126,28 +151,12 @@ std::unique_ptr<World> WorldGenerator::generate() {
|
||||||
assert(this->player.has_value() && "World cannot be generated without a player");
|
assert(this->player.has_value() && "World cannot be generated without a player");
|
||||||
|
|
||||||
std::unique_ptr<World> world{new World()};
|
std::unique_ptr<World> world{new World()};
|
||||||
Chunk start_room{this->select_start()};
|
Chunk start_room{this->world_side_length / 2, this->world_side_length / 2};
|
||||||
this->setup_player(world, start_room);
|
this->setup_player(world, start_room);
|
||||||
|
this->generate_world(world, start_room);
|
||||||
return world;
|
return world;
|
||||||
}
|
}
|
||||||
|
|
||||||
Chunk WorldGenerator::select_start() const {
|
|
||||||
Chunk start{0, 0};
|
|
||||||
switch(std::rand() % 4) {
|
|
||||||
case 0:
|
|
||||||
start.y = this->world_side_length - 1;
|
|
||||||
case 1:
|
|
||||||
start.x = std::rand() % this->world_side_length;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
start.x = this->world_side_length - 1;
|
|
||||||
case 3:
|
|
||||||
start.y = std::rand() % this->world_side_length;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return start;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WorldGenerator::generate_world(std::unique_ptr<World> &world, Chunk start) {
|
void WorldGenerator::generate_world(std::unique_ptr<World> &world, Chunk start) {
|
||||||
world->chunk_size = this->chunk_side_length;
|
world->chunk_size = this->chunk_side_length;
|
||||||
world->shear = this->world_side_length;
|
world->shear = this->world_side_length;
|
||||||
|
@ -157,48 +166,85 @@ void WorldGenerator::generate_world(std::unique_ptr<World> &world, Chunk start)
|
||||||
.hallway_paths = Directions(0x0),
|
.hallway_paths = Directions(0x0),
|
||||||
.chunk_size = this->chunk_side_length,
|
.chunk_size = this->chunk_side_length,
|
||||||
.rect = {
|
.rect = {
|
||||||
(this->chunk_side_length - this->max_room_size) / 2,
|
0,
|
||||||
(this->chunk_side_length - this->max_room_size) / 2,
|
0,
|
||||||
this->max_room_size,
|
this->chunk_side_length,
|
||||||
this->max_room_size
|
this->chunk_side_length
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
world->get_room(start).rect = SDL_Rect {
|
||||||
|
.x = this->chunk_side_length/2-2,
|
||||||
|
.y = this->chunk_side_length/2-2,
|
||||||
|
.w = 5,
|
||||||
|
.h = 5
|
||||||
|
};
|
||||||
world->get_room(start).initialized = true;
|
world->get_room(start).initialized = true;
|
||||||
this->generate_room(world, start);
|
this->generate_room(world, start);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldGenerator::generate_room(std::unique_ptr<World> &world, Chunk last) {
|
void WorldGenerator::generate_room(std::unique_ptr<World> &world, Chunk const &last) {
|
||||||
static const Chunk options[]{
|
Room &last_room{world->get_room(last)};
|
||||||
|
last_room.initialized = true;
|
||||||
|
// generate a room that encloses center tile of this chunk
|
||||||
|
last_room.rect.w = WRAP(std::rand(), this->min_room_size, this->max_room_size);
|
||||||
|
last_room.rect.x = WRAP(std::rand(),
|
||||||
|
std::max(this->chunk_side_length/2 - last_room.rect.w + 2, 0),
|
||||||
|
1 + std::min(this->chunk_side_length - last_room.rect.w - 1, this->chunk_side_length/2));
|
||||||
|
last_room.rect.h = WRAP(std::rand(), this->min_room_size, this->max_room_size);
|
||||||
|
last_room.rect.y = WRAP(std::rand(),
|
||||||
|
std::max(this->chunk_side_length/2 - last_room.rect.h + 2, 0),
|
||||||
|
1 + std::min(this->chunk_side_length - last_room.rect.h - 1, this->chunk_side_length/2));
|
||||||
|
const Chunk options[]{
|
||||||
{last.x + 1, last.y},
|
{last.x + 1, last.y},
|
||||||
{last.x - 1, last.y},
|
{last.x - 1, last.y},
|
||||||
{last.x, last.y + 1},
|
{last.x, last.y + 1},
|
||||||
{last.x, last.y - 1}
|
{last.x, last.y - 1}
|
||||||
};
|
};
|
||||||
static const Directions directions[]{
|
this->add_enemies(world, last, last_room);
|
||||||
EAST, WEST, SOUTH, NORTH
|
static const Directions directions[]{EAST, WEST, SOUTH, NORTH};
|
||||||
};
|
static const Directions inv_directions[] {WEST, EAST, NORTH, SOUTH};
|
||||||
|
int const rand_offset{std::rand() % 4};
|
||||||
for(size_t i{0}; i < 4; ++i) {
|
for(size_t i{0}; i < 4; ++i) {
|
||||||
Chunk next{options[i]};
|
size_t const true_offset{(i + rand_offset) % 4};
|
||||||
Room &option{world->get_room(next)};
|
Chunk const &next{options[true_offset]};
|
||||||
if(options[i].x < 0 || options[i].x >= this->world_side_length
|
if(next.x < 0 || next.x >= this->world_side_length
|
||||||
|| options[i].y < 0 || options[i].y >= this->world_side_length) {
|
|| next.y < 0 || next.y >= this->world_side_length) {
|
||||||
continue;
|
|
||||||
} else if(option.initialized) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// register hallway with the last chunk
|
Room &option{world->get_room(next)};
|
||||||
option.hallway_paths = Directions(option.hallway_paths | directions[i]);
|
if(this->connecting_chance > 0 && !option.initialized || WRAP(std::rand(), 0, this->connecting_chance) == 0) {
|
||||||
// generate random rect that encloses center of chunk
|
// register hallway with the last chunk
|
||||||
// add hallway to the new chunk
|
last_room.hallway_paths |= directions[true_offset];
|
||||||
|
// add returning hallway from the new chunk
|
||||||
|
option.hallway_paths |= inv_directions[true_offset];
|
||||||
|
}
|
||||||
|
if(option.initialized) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// generate surrounding rooms
|
||||||
|
this->generate_room(world, next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldGenerator::add_enemies(std::unique_ptr<World> &world, Chunk const &chunk, Room &room) {
|
||||||
|
for(std::pair<std::function<Character(Tile)>, int> const &pair : this->enemy_spawners) {
|
||||||
|
if(WRAP(std::rand(), 0, pair.second) == 0) {
|
||||||
|
Tile offset{
|
||||||
|
WRAP(std::rand(), 1, room.rect.w - 2) + (chunk.x * this->chunk_side_length),
|
||||||
|
WRAP(std::rand(), 1, room.rect.h - 2) + (chunk.y * this->chunk_side_length)
|
||||||
|
};
|
||||||
|
Character character{pair.first(offset)};
|
||||||
|
character.world = world.get();
|
||||||
|
world->characters.push_back(character);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldGenerator::setup_player(std::unique_ptr<World> &world, Chunk spawn_chunk) {
|
void WorldGenerator::setup_player(std::unique_ptr<World> &world, Chunk spawn_chunk) {
|
||||||
world->characters.push_back(player.value());
|
world->player = this->player.value();
|
||||||
world->player = &world->characters[0];
|
world->player.world = world.get();
|
||||||
world->player->world = world.get();
|
world->player.location = {
|
||||||
world->player->location = {
|
|
||||||
(spawn_chunk.x * this->chunk_side_length) + (this->chunk_side_length / 2),
|
(spawn_chunk.x * this->chunk_side_length) + (this->chunk_side_length / 2),
|
||||||
(spawn_chunk.y * this->chunk_side_length) + (this->chunk_side_length / 2),
|
(spawn_chunk.y * this->chunk_side_length) + (this->chunk_side_length / 2),
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#define ROGUE_WORLD_H
|
#define ROGUE_WORLD_H
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <SDL2/SDL_rect.h>
|
#include <SDL2/SDL_rect.h>
|
||||||
#include "character.h"
|
#include "character.h"
|
||||||
|
@ -34,16 +35,17 @@ struct World {
|
||||||
void act();
|
void act();
|
||||||
void render();
|
void render();
|
||||||
Room &get_room(Chunk chunk);
|
Room &get_room(Chunk chunk);
|
||||||
|
Character *get_player();
|
||||||
|
Chunk get_chunk(Tile tile);
|
||||||
TileData query_tile(Tile tile);
|
TileData query_tile(Tile tile);
|
||||||
void on_input(bool);
|
void on_input(bool);
|
||||||
private:
|
private:
|
||||||
int chunk_size{1};
|
int chunk_size{1};
|
||||||
unsigned shear{0};
|
unsigned shear{0};
|
||||||
Character *player{nullptr};
|
Character player{};
|
||||||
std::vector<Room> rooms{};
|
std::vector<Room> rooms{};
|
||||||
std::vector<Character> characters{};
|
std::vector<Character> characters{};
|
||||||
KeyEventHandler direction_pressed;
|
KeyEventHandler direction_pressed;
|
||||||
private:
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class WorldGenerator {
|
class WorldGenerator {
|
||||||
|
@ -53,16 +55,20 @@ public:
|
||||||
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(Character character);
|
WorldGenerator &with_player(Character character);
|
||||||
|
WorldGenerator &with_enemy(std::function<Character(Tile)> spawner, int inv_frequency);
|
||||||
|
WorldGenerator &with_connecting_chance(unsigned num);
|
||||||
std::unique_ptr<World> generate();
|
std::unique_ptr<World> generate();
|
||||||
private:
|
private:
|
||||||
Chunk select_start() const;
|
|
||||||
void generate_world(std::unique_ptr<World> &world, Chunk start);
|
void generate_world(std::unique_ptr<World> &world, Chunk start);
|
||||||
void generate_room(std::unique_ptr<World> &world, Chunk last);
|
void generate_room(std::unique_ptr<World> &world, Chunk const &last);
|
||||||
|
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::optional<Character> player;
|
std::optional<Character> player;
|
||||||
|
std::vector<std::pair<std::function<Character(Tile)>, 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};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
48
src/enemies.cpp
Normal file
48
src/enemies.cpp
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
#include "enemies.h"
|
||||||
|
#include "core/roguedefs.h"
|
||||||
|
#include "core/world.h"
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace rogue {
|
||||||
|
Tile CritteLogic::move() {
|
||||||
|
Tile location{this->character->location};
|
||||||
|
Character *player{this->character->world->get_player()};
|
||||||
|
if(this->is_aware_of_player) {
|
||||||
|
// cache query_tile call for repeated use
|
||||||
|
std::function<TileData(Tile)> query_tile{std::bind(&World::query_tile, this->character->world, std::placeholders::_1)};
|
||||||
|
Tile difference{
|
||||||
|
player->location.x - location.x,
|
||||||
|
player->location.y - location.y
|
||||||
|
};
|
||||||
|
Tile abs{std::abs(difference.x), std::abs(difference.y)};
|
||||||
|
Tile direction{
|
||||||
|
difference.x == 0 ? 0 : difference.x / std::abs(difference.x),
|
||||||
|
difference.y == 0 ? 0 : difference.y / std::abs(difference.y)
|
||||||
|
};
|
||||||
|
Tile x_motion{location.x + direction.x, location.y};
|
||||||
|
Tile y_motion{location.x, location.y + direction.y};
|
||||||
|
if(abs.x > abs.y) {
|
||||||
|
if(!query_tile(x_motion).is_wall)
|
||||||
|
return x_motion;
|
||||||
|
else if(!query_tile(y_motion).is_wall) {
|
||||||
|
return y_motion;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(!query_tile(y_motion).is_wall) {
|
||||||
|
return y_motion;
|
||||||
|
} else if(!query_tile(x_motion).is_wall) {
|
||||||
|
return x_motion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// cache get_chunk call for easier reading
|
||||||
|
std::function<Chunk(Tile)> get_chunk{std::bind(&World::get_chunk, this->character->world, std::placeholders::_1)};
|
||||||
|
this->is_aware_of_player |= get_chunk(player->location) != get_chunk(location);
|
||||||
|
}
|
||||||
|
// fallback return value, for if there is no desirable tile to move to
|
||||||
|
return {
|
||||||
|
.x = location.x,
|
||||||
|
.y = location.y
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
13
src/enemies.h
Normal file
13
src/enemies.h
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef ENEMIES_H
|
||||||
|
#define ENEMIES_H
|
||||||
|
|
||||||
|
#include "core/character.h"
|
||||||
|
|
||||||
|
namespace rogue {
|
||||||
|
class CritteLogic : public CharacterLogic {
|
||||||
|
virtual Tile move() override;
|
||||||
|
bool is_aware_of_player{false};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // !ENEMIES_H
|
37
src/main.cpp
37
src/main.cpp
|
@ -4,23 +4,36 @@
|
||||||
#include "core/input.h"
|
#include "core/input.h"
|
||||||
#include "core/renderer.h"
|
#include "core/renderer.h"
|
||||||
#include "core/world.h"
|
#include "core/world.h"
|
||||||
|
#include "enemies.h"
|
||||||
#include "player_logic.h"
|
#include "player_logic.h"
|
||||||
|
|
||||||
using namespace rogue;
|
using namespace rogue;
|
||||||
|
|
||||||
|
CharacterData const player_data{
|
||||||
|
.health = 20,
|
||||||
|
.damage = 2,
|
||||||
|
.sprite = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
CharacterData const critte_data{
|
||||||
|
.health = 5,
|
||||||
|
.damage = 1,
|
||||||
|
.sprite = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename TLogicType>
|
||||||
|
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[]) {
|
||||||
std::unique_ptr<World> world{WorldGenerator()
|
std::unique_ptr<World> world{WorldGenerator()
|
||||||
.with_world_size(5)
|
.with_world_size(20)
|
||||||
.with_chunk_size(9)
|
.with_chunk_size(10)
|
||||||
.with_room_size(2, 5)
|
.with_room_size(6, 10)
|
||||||
.with_player(Character({9/2, 9/2},
|
.with_connecting_chance(5)
|
||||||
new PlayerCharacterLogic(),
|
.with_player(Character({0,0}, new PlayerCharacterLogic(), player_data))
|
||||||
CharacterData {
|
.with_enemy(std::bind(&create_character<CritteLogic>, critte_data, std::placeholders::_1), 2)
|
||||||
.health = 1,
|
|
||||||
.damage = 1,
|
|
||||||
.sprite = 0
|
|
||||||
}
|
|
||||||
))
|
|
||||||
.generate()
|
.generate()
|
||||||
};
|
};
|
||||||
RenderData data{RenderDataSetup()
|
RenderData data{RenderDataSetup()
|
||||||
|
@ -29,6 +42,8 @@ int main(int argc, char *argv[]) {
|
||||||
.with_resource_path("resources/")
|
.with_resource_path("resources/")
|
||||||
.with_sprite("wizard.png")
|
.with_sprite("wizard.png")
|
||||||
.with_sprite("wall.png")
|
.with_sprite("wall.png")
|
||||||
|
.with_sprite("critte.png")
|
||||||
|
.with_sprite("player.png")
|
||||||
.build()
|
.build()
|
||||||
};
|
};
|
||||||
Render::provide_render_data(data);
|
Render::provide_render_data(data);
|
||||||
|
|
Loading…
Reference in a new issue