From 851e591c3c0ca879496ba3fcae1247efa6b42b6d Mon Sep 17 00:00:00 2001 From: Maximilian Giller Date: Mon, 10 Jul 2023 01:46:58 +0200 Subject: [PATCH] Implemented procedural world generation --- CMakeLists.txt | 2 +- src/config.h | 4 +- src/game/game.cpp | 25 ++++++ src/game/game.h | 4 + src/game/level/level_generator.cpp | 134 +++++++++++++++++++++++++++++ src/game/level/level_generator.hpp | 21 +++++ src/levels.hpp | 2 +- src/screens/join_screen.cpp | 3 +- src/screens/loading_screen.cpp | 17 ++++ src/screens/loading_screen.hpp | 14 +++ src/texture_config.h | 4 +- 11 files changed, 223 insertions(+), 7 deletions(-) create mode 100644 src/game/level/level_generator.cpp create mode 100644 src/game/level/level_generator.hpp create mode 100644 src/screens/loading_screen.cpp create mode 100644 src/screens/loading_screen.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 669ae61..4ab4e5c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -138,7 +138,7 @@ set(SOURCES src/typography/font_manager.cpp src/typography/font_manager.hpp src/screens/winner_screen.cpp - src/screens/winner_screen.hpp src/screens/screen.cpp src/screens/screen.hpp src/screens/join_screen.cpp src/screens/join_screen.hpp) + src/screens/winner_screen.hpp src/screens/screen.cpp src/screens/screen.hpp src/screens/join_screen.cpp src/screens/join_screen.hpp src/game/level/level_generator.cpp src/game/level/level_generator.hpp src/screens/loading_screen.cpp src/screens/loading_screen.hpp) set(PHYSICS_00_SOURCES src/prototypes/physics_00.cpp) diff --git a/src/config.h b/src/config.h index 3438257..19e44f0 100644 --- a/src/config.h +++ b/src/config.h @@ -18,7 +18,7 @@ // World #define WORLD_GRAVITY b2Vec2(0.f, -9.8f) -#define SKY_HEIGHT_SCALE 2.f +#define SKY_HEIGHT_SCALE 5.f #define CONSIDER_COLLECTABLE_DEPTH_MOVEMENT false // Might cost perfomance and is currently not in use // FPS @@ -47,7 +47,7 @@ #define DEF_TV_MIN_VIEW_SIZE sf::Vector2f(6, 6) * WORLD_TO_ISO_SCALE #define DEF_TV_MAX_VIEW_SIZE sf::Vector2f(0, 0) #define DEF_TV_IS_ABSOLUTE_VIEW_SIZE_PADDING false -#define DEF_TV_VIEW_SIZE_PADDING sf::Vector2f(3.5f, 3.5f) +#define DEF_TV_VIEW_SIZE_PADDING sf::Vector2f(2.f, 2.f) #define MP_VIEW_ADD_NEW_PLAYERS true #define MP_VIEW_REMOVE_DISCONNECTED_PLAYERS true #define MP_VIEW_BORDER_COLOR sf::Color::Black diff --git a/src/game/game.cpp b/src/game/game.cpp index c99d070..d8a7829 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -10,6 +10,8 @@ #include "player/player_collection.hpp" #include "../screens/winner_screen.hpp" #include "../screens/join_screen.hpp" +#include "level/level_generator.hpp" +#include "../screens/loading_screen.hpp" Game::Game(std::shared_ptr window) : window(std::move(window)) { @@ -223,3 +225,26 @@ void Game::showJoinScreen() addGameObject(GlobalLayer::getInstance()); addGameObject(PlayerCollection::getInstance()); } + +void Game::generateNewLevel() +{ + showLoadingScreen("Generating new level ..."); + + LevelConfig newLevel = LevelGenerator::generateLevel("Procedural Level"); + LevelLoader::loadLevel(newLevel); +} + +void Game::showLoadingScreen(const std::string &message) +{ + GlobalLayer::getInstance()->clear(); + clearGameObjects(); + + auto loadingScreen = std::make_shared(message); + GlobalLayer::getInstance()->add(loadingScreen); + + addGameObject(GlobalLayer::getInstance()); + + // Mini loop + update(); + drawFrame(); +} diff --git a/src/game/game.h b/src/game/game.h index 35b1568..71b633e 100644 --- a/src/game/game.h +++ b/src/game/game.h @@ -41,6 +41,8 @@ public: void showJoinScreen(); + void generateNewLevel(); + private: static inline std::shared_ptr singletonInstance = nullptr; std::vector> gameObjects = {}; @@ -58,6 +60,8 @@ private: std::shared_ptr checkForWinner(); void handleWinning(); + + void showLoadingScreen(const std::string &message = "Loading..."); }; diff --git a/src/game/level/level_generator.cpp b/src/game/level/level_generator.cpp new file mode 100644 index 0000000..769564d --- /dev/null +++ b/src/game/level/level_generator.cpp @@ -0,0 +1,134 @@ +#include "level_generator.hpp" + +LevelConfig LevelGenerator::generateLevel(const std::string &name) +{ + int levelSize = 50; + + TileMapConfig map = generateRandomMap(levelSize, levelSize); + std::vector collectables = generateRandomCollectables(levelSize, levelSize); + + return LevelConfig(name, + 120, + { + {1, 2}, + {2, 1}, + {0, 3}, + {3, 0} + }, + collectables, + { + // Blues + sf::Color(2, 100, 234), + sf::Color(2, 100, 234), + sf::Color(2, 100, 234), + sf::Color(2, 195, 234), + + // Neutral + sf::Color::White, + + // Browns + sf::Color(163, 128, 68), + sf::Color(100, 80, 40), + sf::Color(20, 18, 11), + sf::Color::Black + }, + map + ); +} + +TileMapConfig LevelGenerator::generateRandomMap(int width, int height) +{ + int tileVariants = 6 * 2; + std::vector> tiles; + for (int h = 0; h < height; h++) + { + std::vector row; + for (int w = 0; w < width; w++) + { + row.push_back(rand() % tileVariants); + } + tiles.push_back(row); + } + + return TileMapConfig("iso-tiles", tiles); +} + +std::vector LevelGenerator::generateRandomCollectables(int width, int height) +{ + std::vector collectables; + + // Add rose fields + for (int i = 0; i < 20; i++) + { + // Random coordinates for center of rose field with enough distance from edges + int x = rand() % int(width - 2 * roseFieldRadius) + roseFieldRadius; + int y = rand() % int(height - 2 * roseFieldRadius) + roseFieldRadius; + + GridCoordinates fieldCenter = GridCoordinates(x, y); + + // Add rose field by creating a dense field of roses around the center + int roseCount = rand() % 15 + 5; + for (int j = 0; j < roseCount; j++) + { + // Use cosine distribution to get a more dense field around the center + float angle = rand() % 360; + float radius = rand() % 1000 / 1000.f; + + // Skew radius to avoid too dense at center + radius = pow(radius, 0.3f) * roseFieldRadius; + + float xOffset = radius * cos(angle); + float yOffset = radius * sin(angle); + + GridCoordinates roseCoordinates = GridCoordinates(fieldCenter.x + xOffset, fieldCenter.y + yOffset); + collectables.push_back(CollectableInLevel("rose", roseCoordinates)); + } + } + + // Place rows of lanterns + int rowCount = rand() % 3 + 2; + int heightDelta = height / (rowCount + 1); + int stepSize = 7; + for (int i = 0; i < rowCount; i++) + { + int y = (i + 1) * heightDelta; + for (int j = stepSize; j < width; j += stepSize) + { + collectables.push_back(CollectableInLevel("lantern", GridCoordinates(j, y))); + } + } + + // Place random bikes + int bikeCount = rand() % 5 + 5; + for (int i = 0; i < bikeCount; i++) + { + int x = rand() % width; + int y = rand() % height; + collectables.push_back(CollectableInLevel("bike", GridCoordinates(x, y))); + } + + // Place random rose bushes + int roseBushCount = rand() % 40 + 40; + for (int i = 0; i < roseBushCount; i++) + { + int x = rand() % width; + int y = rand() % height; + collectables.push_back(CollectableInLevel("rose-bush", GridCoordinates(x, y))); + } + + // Place random stones + int stoneCount = rand() % 20 + 20; + for (int i = 0; i < stoneCount; i++) + { + int x = rand() % width; + int y = rand() % height; + collectables.push_back(CollectableInLevel("stone", GridCoordinates(x, y))); + } + + // Place a single tree + int x = rand() % width; + int y = rand() % height; + collectables.push_back(CollectableInLevel("small-tree", GridCoordinates(x, y))); + + return collectables; +} diff --git a/src/game/level/level_generator.hpp b/src/game/level/level_generator.hpp new file mode 100644 index 0000000..cddab17 --- /dev/null +++ b/src/game/level/level_generator.hpp @@ -0,0 +1,21 @@ +#ifndef HOLESOME_LEVEL_GENERATOR_HPP +#define HOLESOME_LEVEL_GENERATOR_HPP + + +#include "level_config.hpp" + +class LevelGenerator +{ +public: + static LevelConfig generateLevel(const std::string& name); + + static TileMapConfig generateRandomMap(int width, int height); + + static std::vector generateRandomCollectables(int width, int height); + +private: + static const inline float roseFieldRadius = 3.f; +}; + + +#endif //HOLESOME_LEVEL_GENERATOR_HPP diff --git a/src/levels.hpp b/src/levels.hpp index 18bc5db..66c0697 100644 --- a/src/levels.hpp +++ b/src/levels.hpp @@ -9,7 +9,7 @@ std::map const all_levels = { {"default", LevelConfig("Default", - 5, + 120, { {0, 0}, {18, 18}, diff --git a/src/screens/join_screen.cpp b/src/screens/join_screen.cpp index 5a6a368..20cf629 100644 --- a/src/screens/join_screen.cpp +++ b/src/screens/join_screen.cpp @@ -4,6 +4,7 @@ #include "../game/player/player_collection.hpp" #include "../levels.hpp" #include "../game/level/level_loader.hpp" +#include "../game/game.h" JoinScreen::JoinScreen() { @@ -39,7 +40,7 @@ void JoinScreen::update() if (input->isPerformingAction(GameAction::CONFIRM)) { // Start game - LevelLoader::loadLevel(INITIAL_LEVEL); + Game::getInstance()->generateNewLevel(); return; } } diff --git a/src/screens/loading_screen.cpp b/src/screens/loading_screen.cpp new file mode 100644 index 0000000..f23fecc --- /dev/null +++ b/src/screens/loading_screen.cpp @@ -0,0 +1,17 @@ +#include +#include "loading_screen.hpp" +#include "../typography/font_manager.hpp" +#include "../config.h" + +LoadingScreen::LoadingScreen(const std::string &message) +{ + auto text = std::make_shared(); + text->setString(message); + text->setFont(*FontManager::getInstance()->getDefaultFont()); + text->setCharacterSize(24); + text->setStyle(sf::Text::Italic); + text->setFillColor(sf::Color::White); + text->setPosition(REFERENCE_SIZE.x / 2.f - text->getGlobalBounds().width / 2.f, + REFERENCE_SIZE.y / 2.f - text->getGlobalBounds().height / 2.f); + add(text); +} diff --git a/src/screens/loading_screen.hpp b/src/screens/loading_screen.hpp new file mode 100644 index 0000000..ac8b695 --- /dev/null +++ b/src/screens/loading_screen.hpp @@ -0,0 +1,14 @@ +#ifndef HOLESOME_LOADING_SCREEN_HPP +#define HOLESOME_LOADING_SCREEN_HPP + + +#include "screen.hpp" + +class LoadingScreen : public Screen +{ +public: + explicit LoadingScreen(const std::string &message = "Loading..."); +}; + + +#endif //HOLESOME_LOADING_SCREEN_HPP diff --git a/src/texture_config.h b/src/texture_config.h index 5630699..3d47d8e 100644 --- a/src/texture_config.h +++ b/src/texture_config.h @@ -9,7 +9,7 @@ #include "sprites/tiling/tileset_config.hpp" #include "sprites/configs/masked_sprite_config.hpp" -#define PLAYER_SKINS std::vector { "player-0", "player-1", "player-2", "player-3" } +#define PLAYER_SKINS std::vector { "player", "player-1", "player-2", "player-3" } /** * All textures used in the game. @@ -91,7 +91,7 @@ std::map const all_masked_sprites = { * The key is the name of the tileset, the value is the tileset config. */ std::map const all_tilesets = { - {"iso-tiles", TileSetConfig("custom-grass-tiles", {0, 1, 2, 3, 4, 5})} + {"iso-tiles", TileSetConfig("custom-grass-tiles", {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11})} }; #endif //HOLESOME_TEXTURE_CONFIG_H