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;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							Room &option{world->get_room(next)};
 | 
				
			||||||
 | 
							if(this->connecting_chance > 0 && !option.initialized || WRAP(std::rand(), 0, this->connecting_chance) == 0) {
 | 
				
			||||||
			// register hallway with the last chunk
 | 
								// register hallway with the last chunk
 | 
				
			||||||
		option.hallway_paths = Directions(option.hallway_paths | directions[i]);
 | 
								last_room.hallway_paths |= directions[true_offset];
 | 
				
			||||||
		// generate random rect that encloses center of chunk
 | 
								// add returning hallway from the new chunk
 | 
				
			||||||
		// add hallway to 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