feat: implemented menus and fixed bugs
This commit is contained in:
		
							parent
							
								
									8e0913b820
								
							
						
					
					
						commit
						1cfe4bc4c3
					
				
							
								
								
									
										
											BIN
										
									
								
								resources/escape.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/escape.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 3.9 KiB | 
							
								
								
									
										
											BIN
										
									
								
								resources/game_over.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/game_over.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 2.5 KiB | 
							
								
								
									
										
											BIN
										
									
								
								resources/maze.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/maze.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 2.4 KiB | 
|  | @ -13,16 +13,16 @@ void CharacterLogic::set_character(Character *character) { | |||
| 
 | ||||
| Character::Character(Tile location, CharacterLogic *logic, CharacterData stats) | ||||
| : data{stats} | ||||
| , logic{logic} | ||||
| , health{stats.health} | ||||
| , location{location} | ||||
| , world{nullptr} { | ||||
| , world{nullptr} | ||||
| , logic{logic} { | ||||
| 	this->logic->set_character(this); | ||||
| } | ||||
| 
 | ||||
| void Character::act() { | ||||
| 	assert(this->world != nullptr && "World generation did not initialize character properly"); | ||||
| 	if(this->health < 0) { | ||||
| 	if(this->health <= 0) { | ||||
| 		return; | ||||
| 	} | ||||
| 	this->logic->set_character(this); | ||||
|  |  | |||
|  | @ -12,12 +12,16 @@ InputHandler::~InputHandler() { | |||
| 	Input::remove_handler(this); | ||||
| } | ||||
| 
 | ||||
| KeyEventHandler::KeyEventHandler(std::set<SDL_Scancode> code, Listener listener) | ||||
| KeyEventHandler::KeyEventHandler(std::set<SDL_Scancode> code, Listener listener, bool allow_repeat) | ||||
| : scancode{code} | ||||
| , listener{listener} {} | ||||
| , listener{listener} | ||||
| , allow_repeat{allow_repeat} | ||||
| {} | ||||
| 
 | ||||
| void KeyEventHandler::process_event(SDL_Event event) { | ||||
| 	if((event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) && this->scancode.contains(event.key.keysym.scancode)) { | ||||
| 	if((event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) | ||||
| 	&& (event.key.repeat == 0 || this->allow_repeat) | ||||
| 	&& this->scancode.contains(event.key.keysym.scancode)) { | ||||
| 		this->listener(event.type == SDL_KEYDOWN); | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -15,10 +15,11 @@ struct InputHandler { | |||
| 
 | ||||
| struct KeyEventHandler : public InputHandler { | ||||
| 	typedef std::function<void(bool down)> Listener; | ||||
| 	KeyEventHandler(std::set<SDL_Scancode> code, Listener listener); | ||||
| 	KeyEventHandler(std::set<SDL_Scancode> code, Listener listener, bool allow_repeat = true); | ||||
| 	virtual void process_event(SDL_Event event) override; | ||||
| 	std::set<SDL_Scancode> scancode{}; | ||||
| 	Listener listener{}; | ||||
| 	bool allow_repeat{false}; | ||||
| }; | ||||
| 
 | ||||
| class Input { | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| #include "renderer.h" | ||||
| #include <SDL2/SDL_log.h> | ||||
| #include <SDL2/SDL_rect.h> | ||||
| #include <SDL2/SDL_render.h> | ||||
| #include <cassert> | ||||
| #include <SDL2/SDL_video.h> | ||||
|  | @ -35,15 +36,15 @@ RenderDataSetup &RenderDataSetup::with_resource_path(std::filesystem::path path) | |||
| } | ||||
| 
 | ||||
| RenderDataSetup &RenderDataSetup::with_sprite(std::filesystem::path sprite_path) { | ||||
| 	assert(this->renderer != nullptr && "Cannot create sprites without a renderer"); | ||||
| 	assert(this->resource_base_path.has_value() && "Resource base path has to be set before loading sprites"); | ||||
| 	std::filesystem::path complete_path{this->resource_base_path.value()/sprite_path}; | ||||
| 	SDL_Log("Adding sprite: %s", complete_path.c_str()); | ||||
| 	assert(std::filesystem::exists(complete_path) && "Sprite path has to exist"); | ||||
| 	if(SDL_Texture *texture{IMG_LoadTexture(this->renderer, complete_path.native().c_str())}) { | ||||
| 	if(SDL_Texture *texture{this->load_texture(sprite_path)}) { | ||||
| 		this->sprites.push_back(texture); | ||||
| 	} else { | ||||
| 		SDL_Log("Failed to load texture %s reason: %s", complete_path.c_str(), SDL_GetError()); | ||||
| 	} | ||||
| 	return *this; | ||||
| } | ||||
| 
 | ||||
| RenderDataSetup &RenderDataSetup::with_menu(std::filesystem::path file_path) { | ||||
| 	if(SDL_Texture *texture{this->load_texture(file_path)}) { | ||||
| 		this->menus.push_back(texture); | ||||
| 	} | ||||
| 	return *this; | ||||
| } | ||||
|  | @ -56,13 +57,29 @@ RenderData RenderDataSetup::build() { | |||
| 	data.window=this->window; | ||||
| 	data.renderer=this->renderer; | ||||
| 	data.sprites = this->sprites; | ||||
| 	data.menus = this->menus; | ||||
| 	return data; | ||||
| } | ||||
| 
 | ||||
| SDL_Texture *RenderDataSetup::load_texture(std::filesystem::path file_path) { | ||||
| 	assert(this->renderer != nullptr && "Cannot create sprites without a renderer"); | ||||
| 	assert(this->resource_base_path.has_value() && "Resource base path has to be set before loading sprites"); | ||||
| 	std::filesystem::path complete_path{this->resource_base_path.value() / file_path}; | ||||
| 	SDL_Log("Adding sprite: %s", complete_path.string().c_str()); | ||||
| 	assert(std::filesystem::exists(complete_path) && "Sprite path has to exist"); | ||||
| 	if(SDL_Texture *texture{IMG_LoadTexture(this->renderer, complete_path.string().c_str())}) { | ||||
| 		return texture; | ||||
| 	} else { | ||||
| 		SDL_Log("Failed to load texture %s reason: %s", complete_path.string().c_str(), SDL_GetError()); | ||||
| 		return nullptr; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| Tile Render::camera_center{0u, 0u}; | ||||
| RenderData* Render::data{nullptr}; | ||||
| unsigned Render::camera_width{10}; | ||||
| Tile Render::camera_offset{0, 0}; | ||||
| SDL_Point Render::window_size{0, 0}; | ||||
| int Render::tile_screen_width{2}; | ||||
| int Render::half_tile_screen_width{1}; | ||||
| 
 | ||||
|  | @ -83,14 +100,13 @@ void Render::clear(Tile camera_center, unsigned fov) { | |||
| 	SDL_SetRenderDrawColor(Render::data->renderer, 0u, 0u, 0u, 255u); | ||||
| 	SDL_RenderClear(Render::data->renderer); | ||||
| 
 | ||||
| 	int width, height; | ||||
| 	SDL_GetWindowSize(Render::data->window, &width, &height); | ||||
| 	Render::tile_screen_width = int(width / Render::camera_width); | ||||
| 	SDL_GetWindowSize(Render::data->window, &window_size.x, &window_size.y); | ||||
| 	Render::tile_screen_width = int(window_size.x / Render::camera_width); | ||||
| 	Render::half_tile_screen_width = Render::tile_screen_width >> 1; | ||||
| 
 | ||||
| 	Render::camera_center = camera_center; | ||||
| 	Render::camera_offset.x = -Render::camera_center.x * Render::tile_screen_width + (width >> 1); | ||||
| 	Render::camera_offset.y = -Render::camera_center.y * Render::tile_screen_width + (height >> 1); | ||||
| 	Render::camera_offset.x = -Render::camera_center.x * Render::tile_screen_width + (window_size.x >> 1); | ||||
| 	Render::camera_offset.y = -Render::camera_center.y * Render::tile_screen_width + (window_size.y >> 1); | ||||
| } | ||||
| 
 | ||||
| void Render::draw(Tile world_space_tile, Sprite sprite) { | ||||
|  | @ -108,6 +124,30 @@ void Render::draw(Tile world_space_tile, Sprite sprite) { | |||
| 	SDL_RenderCopy(Render::data->renderer, Render::data->sprites[sprite], 0 , &rect); | ||||
| } | ||||
| 
 | ||||
| void Render::draw_menu(Sprite menu) { | ||||
| 	assert_render_initialized(); | ||||
| 	Render::clear({0, 0}); | ||||
| 	SDL_Rect const rect { | ||||
| 		.x = window_size.x/2 - window_size.y/2, | ||||
| 		.y = 0, | ||||
| 		.w = window_size.y, | ||||
| 		.h = window_size.y | ||||
| 	}; | ||||
| 	SDL_RenderCopy(Render::data->renderer, Render::data->menus[menu], 0, &rect); | ||||
| } | ||||
| 
 | ||||
| void Render::draw_healthbar(int current, int max, SDL_Rect area, int border) { | ||||
| 	assert_render_initialized(); | ||||
| 	SDL_SetRenderDrawColor(Render::data->renderer, 0,0, 0, 255); | ||||
| 	SDL_RenderFillRect(Render::data->renderer, &area); | ||||
| 	area.x += 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_RenderFillRect(Render::data->renderer, &area); | ||||
| } | ||||
| 
 | ||||
| void Render::present() { | ||||
| 	assert_render_initialized(); | ||||
| 	SDL_RenderPresent(Render::data->renderer); | ||||
|  |  | |||
|  | @ -19,6 +19,7 @@ private: RenderData() = default; | |||
| 	SDL_Window *window{nullptr}; | ||||
| 	SDL_Renderer *renderer{nullptr}; | ||||
| 	std::vector<SDL_Texture*> sprites{}; | ||||
| 	std::vector<SDL_Texture*> menus{}; | ||||
| }; | ||||
| 
 | ||||
| // Global interface for rendering
 | ||||
|  | @ -31,11 +32,14 @@ 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 present(); | ||||
| private: | ||||
| 	static Tile camera_center; | ||||
| 	static unsigned camera_width; | ||||
| 	static Tile camera_offset; | ||||
| 	static SDL_Point window_size; | ||||
| 	static int tile_screen_width; | ||||
| 	static int half_tile_screen_width; | ||||
| 	static RenderData *data; | ||||
|  | @ -49,13 +53,17 @@ public: | |||
| 	RenderDataSetup &with_renderer(Uint32 flags); | ||||
| 	RenderDataSetup &with_resource_path(std::filesystem::path base_path); | ||||
| 	RenderDataSetup &with_sprite(std::filesystem::path sprite_path); | ||||
| 	RenderDataSetup &with_menu(std::filesystem::path sprite_path); | ||||
| 	RenderData build(); | ||||
| private: | ||||
| 	SDL_Texture *load_texture(std::filesystem::path file_path); | ||||
| private: | ||||
| 	unsigned fov{10}; | ||||
| 	std::optional<std::filesystem::path> resource_base_path; | ||||
| 	SDL_Window *window{nullptr}; | ||||
| 	SDL_Renderer *renderer{nullptr}; | ||||
| 	std::vector<SDL_Texture*> sprites{}; | ||||
| 	std::vector<SDL_Texture*> menus{}; | ||||
| }; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -47,15 +47,23 @@ World::World() | |||
| 	std::bind(&World::on_input, this, std::placeholders::_1) | ||||
| } {} | ||||
| 
 | ||||
| void World::act_if_requested() { | ||||
| 	if(this->turn_requested) { | ||||
| 		this->act(); | ||||
| 		this->turn_requested = false; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void World::act() { | ||||
| 	player.act(); | ||||
| 	this->player.act(); | ||||
| 	this->boss.act(); | ||||
| 	for(Character &character : this->characters) { | ||||
| 		character.act(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void World::render() { | ||||
| 	Render::clear(this->player.location, 15); | ||||
| 	Render::clear(this->player.location, 100); | ||||
| 	for(size_t i{0}; i < this->rooms.size(); ++i) { | ||||
| 		this->rooms[i].draw({ | ||||
| 			.x = int(i % this->shear * this->chunk_size), | ||||
|  | @ -65,7 +73,9 @@ void World::render() { | |||
| 	for(Character &character : this->characters) { | ||||
| 		character.draw(); | ||||
| 	} | ||||
| 	boss.draw(); | ||||
| 	player.draw(); | ||||
| 	Render::draw_healthbar(player.health, player.data.health); | ||||
| } | ||||
| 
 | ||||
| Room &World::get_room(Chunk chunk) { | ||||
|  | @ -76,6 +86,10 @@ Character *World::get_player() { | |||
| 	return &this->player; | ||||
| } | ||||
| 
 | ||||
| Character *World::get_boss() { | ||||
| 	return &this->boss; | ||||
| } | ||||
| 
 | ||||
| Chunk World::get_chunk(Tile tile) { | ||||
| 	return {tile.x / this->chunk_size, tile.y / this->chunk_size}; | ||||
| } | ||||
|  | @ -86,8 +100,11 @@ TileData World::query_tile(Tile tile) { | |||
| 		.character = nullptr, | ||||
| 		.is_wall = room.tile_is_wall({tile.x % this->chunk_size, tile.y % this->chunk_size}) | ||||
| 	}; | ||||
| 	if(player.location == tile) { | ||||
| 		out.character = &player; | ||||
| 	if(this->player.location == tile) { | ||||
| 		out.character = &this->player; | ||||
| 		return out; | ||||
| 	} else if(this->boss.location == tile) { | ||||
| 		out.character = &this->boss; | ||||
| 		return out; | ||||
| 	} | ||||
| 	for(Character &character : this->characters) { | ||||
|  | @ -100,8 +117,8 @@ TileData World::query_tile(Tile tile) { | |||
| } | ||||
| 
 | ||||
| void World::on_input(bool pressed) { | ||||
| 	if(!pressed) { | ||||
| 		this->act(); | ||||
| 	if(pressed) { | ||||
| 		this->turn_requested = true; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -128,8 +145,9 @@ WorldGenerator &WorldGenerator::with_room_size(unsigned min_side_length, unsigne | |||
| 	return *this; | ||||
| } | ||||
| 
 | ||||
| WorldGenerator &WorldGenerator::with_player(Character character) { | ||||
| 	this->player.emplace(character); | ||||
| WorldGenerator &WorldGenerator::with_player(std::function<Character(Tile)> spawner) { | ||||
| 	assert(this->player_spawner == nullptr && "Player spawner cannot be assigned twice, remove previous call"); | ||||
| 	this->player_spawner = spawner; | ||||
| 	return *this; | ||||
| } | ||||
| 
 | ||||
|  | @ -138,6 +156,12 @@ WorldGenerator &WorldGenerator::with_enemy(std::function<Character(Tile)> spawne | |||
| 	return *this; | ||||
| } | ||||
| 
 | ||||
| WorldGenerator &WorldGenerator::with_boss(std::function<Character(Tile)> spawner) { | ||||
| 	assert(this->boss_spawner == nullptr && "Boss spawner cannot be assigned twice, remove previous call"); | ||||
| 	this->boss_spawner = spawner; | ||||
| 	return *this; | ||||
| } | ||||
| 
 | ||||
| WorldGenerator &WorldGenerator::with_connecting_chance(unsigned num) { | ||||
| 	this->connecting_chance = num; | ||||
| 	return *this; | ||||
|  | @ -148,7 +172,8 @@ std::unique_ptr<World> WorldGenerator::generate() { | |||
| 	assert(this->min_room_size > 0 && "Room size is required to generate world"); | ||||
| 	assert(this->min_room_size < this->max_room_size && "Room size is required to generate world"); | ||||
| 	assert(this->world_side_length > 0 && "World size is required to generate world"); | ||||
| 	assert(this->player.has_value() && "World cannot be generated without a player");  | ||||
| 	assert(this->player_spawner != nullptr && "World cannot be generated without a player");  | ||||
| 	assert(this->boss_spawner != nullptr && "Boss spawner is required to generate world"); | ||||
| 
 | ||||
| 	std::unique_ptr<World> world{new World()}; | ||||
| 	Chunk start_room{this->world_side_length / 2, this->world_side_length / 2}; | ||||
|  | @ -158,6 +183,7 @@ std::unique_ptr<World> WorldGenerator::generate() { | |||
| } | ||||
| 
 | ||||
| void WorldGenerator::generate_world(std::unique_ptr<World> &world, Chunk start) { | ||||
| 	this->boss_generated = false; | ||||
| 	world->chunk_size = this->chunk_side_length; | ||||
| 	world->shear = this->world_side_length; | ||||
| 	world->rooms.resize(this->world_side_length * this->world_side_length); | ||||
|  | @ -201,7 +227,6 @@ void WorldGenerator::generate_room(std::unique_ptr<World> &world, Chunk const &l | |||
| 		{last.x, last.y + 1}, | ||||
| 		{last.x, last.y - 1} | ||||
| 	}; | ||||
| 	this->add_enemies(world, last, last_room); | ||||
| 	static const Directions directions[]{EAST, WEST, SOUTH, NORTH}; | ||||
| 	static const Directions inv_directions[] {WEST, EAST, NORTH, SOUTH}; | ||||
| 	int const rand_offset{std::rand() % 4}; | ||||
|  | @ -213,7 +238,7 @@ void WorldGenerator::generate_room(std::unique_ptr<World> &world, Chunk const &l | |||
| 			continue; | ||||
| 		} | ||||
| 		Room &option{world->get_room(next)}; | ||||
| 		if(this->connecting_chance > 0 && !option.initialized || WRAP(std::rand(), 0, this->connecting_chance) == 0) { | ||||
| 		if((this->connecting_chance > 0 && !option.initialized) || WRAP(std::rand(), 0, this->connecting_chance) == 0) { | ||||
| 			// register hallway with the last chunk
 | ||||
| 			last_room.hallway_paths |= directions[true_offset]; | ||||
| 			// add returning hallway from the new chunk
 | ||||
|  | @ -225,14 +250,25 @@ void WorldGenerator::generate_room(std::unique_ptr<World> &world, Chunk const &l | |||
| 		// generate surrounding rooms
 | ||||
| 		this->generate_room(world, next); | ||||
| 	} | ||||
| 	this->add_enemies(world, last, last_room); | ||||
| } | ||||
| 
 | ||||
| void WorldGenerator::add_enemies(std::unique_ptr<World> &world, Chunk const &chunk, Room &room) { | ||||
| 	// this will get executed only by the first room to run out of neighbours to fill
 | ||||
| 	if(!this->boss_generated) { // spawn boss at the center of the room
 | ||||
| 		Character boss{this->boss_spawner({ | ||||
| 			chunk.x * this->chunk_side_length + room.rect.x + room.rect.w/2, | ||||
| 			chunk.y * this->chunk_side_length + room.rect.y + room.rect.h/2 | ||||
| 		})}; | ||||
| 		boss.world = world.get(); | ||||
| 		world->boss = boss; | ||||
| 		this->boss_generated = true; | ||||
| 	} | ||||
| 	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) | ||||
| 				WRAP(std::rand(), 1, room.rect.w - 2) + (chunk.x * this->chunk_side_length) + room.rect.x, | ||||
| 				WRAP(std::rand(), 1, room.rect.h - 2) + (chunk.y * this->chunk_side_length) + room.rect.y | ||||
| 			}; | ||||
| 			Character character{pair.first(offset)}; | ||||
| 			character.world = world.get(); | ||||
|  | @ -242,11 +278,10 @@ void WorldGenerator::add_enemies(std::unique_ptr<World> &world, Chunk const &chu | |||
| } | ||||
| 
 | ||||
| void WorldGenerator::setup_player(std::unique_ptr<World> &world, Chunk spawn_chunk) { | ||||
| 	world->player = this->player.value(); | ||||
| 	world->player.world = world.get(); | ||||
| 	world->player.location = { | ||||
| 	world->player = this->player_spawner({ | ||||
| 		(spawn_chunk.x * this->chunk_side_length) + (this->chunk_side_length / 2), | ||||
| 		(spawn_chunk.y * this->chunk_side_length) + (this->chunk_side_length / 2), | ||||
| 	}; | ||||
| 	}); | ||||
| 	world->player.world = world.get(); | ||||
| } | ||||
| } | ||||
|  |  | |||
|  | @ -32,17 +32,21 @@ struct World { | |||
| 	friend class WorldGenerator; | ||||
| 	World(); | ||||
| 	~World() = default; | ||||
| 	void act_if_requested(); | ||||
| 	void act(); | ||||
| 	void render(); | ||||
| 	Room &get_room(Chunk chunk); | ||||
| 	Character *get_player(); | ||||
| 	Character *get_boss(); | ||||
| 	Chunk get_chunk(Tile tile); | ||||
| 	TileData query_tile(Tile tile); | ||||
| 	void on_input(bool); | ||||
| private: | ||||
| 	int chunk_size{1}; | ||||
| 	unsigned shear{0}; | ||||
| 	bool turn_requested{false}; | ||||
| 	Character player{}; | ||||
| 	Character boss{}; | ||||
| 	std::vector<Room> rooms{}; | ||||
| 	std::vector<Character> characters{}; | ||||
| 	KeyEventHandler direction_pressed; | ||||
|  | @ -54,8 +58,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(Character character); | ||||
| 	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_connecting_chance(unsigned num); | ||||
| 	std::unique_ptr<World> generate(); | ||||
| private: | ||||
|  | @ -64,11 +69,13 @@ 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::optional<Character> player; | ||||
| 	std::function<Character(Tile)> player_spawner{}; | ||||
| 	std::function<Character(Tile)> boss_spawner{}; | ||||
| 	std::vector<std::pair<std::function<Character(Tile)>, 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}; | ||||
| 	bool boss_generated{false}; | ||||
| }; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -37,7 +37,7 @@ Tile CritteLogic::move() { | |||
| 	} 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); | ||||
| 		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 { | ||||
|  |  | |||
							
								
								
									
										123
									
								
								src/main.cpp
									
									
									
									
									
								
							
							
						
						
									
										123
									
								
								src/main.cpp
									
									
									
									
									
								
							|  | @ -9,55 +9,142 @@ | |||
| 
 | ||||
| using namespace rogue; | ||||
| 
 | ||||
| // the DOOM I/II solution :)
 | ||||
| enum GameState { | ||||
| 	MainMenu, | ||||
| 	Game, | ||||
| 	GameOver, | ||||
| 	Escape | ||||
| }; | ||||
| 
 | ||||
| CharacterData const player_data{ | ||||
| 	.health = 20, | ||||
| 	.damage = 2, | ||||
| 	.sprite = 3 | ||||
| }; | ||||
| 
 | ||||
| CharacterData const boss_data{ | ||||
| 	.health = 10, | ||||
| 	.damage = 3, | ||||
| 	.sprite = 0 | ||||
| }; | ||||
| 
 | ||||
| CharacterData const critte_data{ | ||||
| 	.health = 5, | ||||
| 	.health = 4, | ||||
| 	.damage = 1, | ||||
| 	.sprite = 2 | ||||
| }; | ||||
| 
 | ||||
| template <typename TLogicType> | ||||
| Character create_character(CharacterData const &stats, Tile tile) { | ||||
| static Character create_character(CharacterData const &stats, Tile tile) { | ||||
| 	return Character(tile, new TLogicType, stats); | ||||
| } | ||||
| 
 | ||||
| int main(int argc, char *argv[]) { | ||||
| 	std::unique_ptr<World> world{WorldGenerator() | ||||
| 		.with_world_size(20) | ||||
| 		.with_chunk_size(10) | ||||
| 		.with_room_size(6, 10) | ||||
| 		.with_connecting_chance(5) | ||||
| 		.with_player(Character({0,0}, new PlayerCharacterLogic(), player_data)) | ||||
| 	GameState game_state{GameState::MainMenu}; | ||||
| 	WorldGenerator generator{WorldGenerator() | ||||
| 		.with_world_size(10) | ||||
| 			.with_chunk_size(10) | ||||
| 			.with_room_size(6, 10) | ||||
| 			.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) | ||||
| 		.generate() | ||||
| 	}; | ||||
| 		.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))}; | ||||
| 	RenderData data{RenderDataSetup() | ||||
| 		// create window and renderer, allowing for texture creation
 | ||||
| 		.with_window("roguelike", SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_FULLSCREEN) | ||||
| 		.with_renderer(SDL_RENDERER_ACCELERATED) | ||||
| 		.with_resource_path("resources/") | ||||
| 		.with_sprite("wizard.png") | ||||
| 		.with_sprite("wall.png") | ||||
| 		.with_sprite("critte.png") | ||||
| 		.with_sprite("player.png") | ||||
| 			// setup resources
 | ||||
| 			.with_resource_path("resources/") | ||||
| 				// sprites
 | ||||
| 				.with_sprite("wizard.png") | ||||
| 				.with_sprite("wall.png") | ||||
| 				.with_sprite("critte.png") | ||||
| 				.with_sprite("player.png") | ||||
| 				// menus
 | ||||
| 				.with_menu("maze.png") | ||||
| 				.with_menu("game_over.png") | ||||
| 				.with_menu("escape.png") | ||||
| 		.build() | ||||
| 	}; | ||||
| 	// use <functional> features to capture game_state and listen for input within main()
 | ||||
| 	KeyEventHandler return_to_main_input{{SDL_SCANCODE_RETURN}, | ||||
| 		[&game_state](bool down) { | ||||
| 			if(!down) return; | ||||
| 			switch(game_state) { | ||||
| 				case GameState::Game: // do nothing, let the input be handled by the world/characters
 | ||||
| 				case GameState::MainMenu: // main menu starts with direction input not enter
 | ||||
| 					break;  | ||||
| 				case GameState::GameOver: | ||||
| 					game_state = GameState::MainMenu; | ||||
| 					break; | ||||
| 				case GameState::Escape: | ||||
| 					game_state = GameState::MainMenu; | ||||
| 					break; | ||||
| 			} | ||||
| 		}, | ||||
| 		false | ||||
| 	}; | ||||
| 	KeyEventHandler start_game_input{{ | ||||
| 			SDL_SCANCODE_H, SDL_SCANCODE_J, SDL_SCANCODE_K, SDL_SCANCODE_L, | ||||
| 			SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_UP, SDL_SCANCODE_DOWN | ||||
| 		}, | ||||
| 		[&game_state](bool down) { | ||||
| 			if(!down) return; | ||||
| 			switch(game_state) { | ||||
| 				case GameState::Game: | ||||
| 				case GameState::GameOver: | ||||
| 				case GameState::Escape: | ||||
| 					break; | ||||
| 				case GameState::MainMenu: | ||||
| 					game_state = GameState::Game; | ||||
| 					break; | ||||
| 			} | ||||
| 		}, | ||||
| 		false | ||||
| 	}; | ||||
| 	Render::provide_render_data(data); | ||||
| 	std::unique_ptr<World> world{nullptr}; | ||||
| 	SDL_Event evt; | ||||
| 	for(;;) { | ||||
| 		world->render(); | ||||
| 		// DOOM-style menus
 | ||||
| 		switch(game_state) { | ||||
| 			case GameState::MainMenu: | ||||
| 				Render::draw_menu(0); | ||||
| 				break; | ||||
| 			case GameState::Game: | ||||
| 				// generate environment when needed
 | ||||
| 				if(world == nullptr) { | ||||
| 					world.reset(generator.generate().release()); | ||||
| 				} | ||||
| 				world->act_if_requested(); | ||||
| 				world->render(); | ||||
| 				// game end conditions (player or boss death)
 | ||||
| 				if(world->get_player()->health <= 0) { | ||||
| 					game_state = GameState::GameOver; | ||||
| 					world.reset(); | ||||
| 				} else if(world->get_boss()->health <= 0) { | ||||
| 					game_state = GameState::Escape; | ||||
| 					world.reset(); | ||||
| 				} | ||||
| 				break; | ||||
| 			case GameState::GameOver: | ||||
| 				Render::draw_menu(1); | ||||
| 				break; | ||||
| 			case GameState::Escape: | ||||
| 				Render::draw_menu(2); | ||||
| 				break; | ||||
| 		} | ||||
| 		Render::present(); | ||||
| 		if(SDL_WaitEvent(&evt)) { | ||||
| 			if(evt.type == SDL_QUIT) { | ||||
| 				goto main_loop; | ||||
| 				goto break_main_loop; | ||||
| 			} else { | ||||
| 				Input::process_event(evt); | ||||
| 			} | ||||
| 		} | ||||
| 	} main_loop: | ||||
| 	} break_main_loop: | ||||
| 	return 0; | ||||
| } | ||||
|  |  | |||
|  | @ -4,7 +4,6 @@ | |||
| #include "core/character.h" | ||||
| #include "core/input.h" | ||||
| #include "core/roguedefs.h" | ||||
| #include <functional> | ||||
| 
 | ||||
| namespace rogue { | ||||
| class PlayerCharacterLogic : public CharacterLogic { | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Sara
						Sara