├── resources ├── settings.ini ├── audio │ ├── swing.wav │ └── sounds.json ├── fonts │ ├── daniel.ttf │ ├── type_writer.ttf │ ├── dogica │ │ ├── dogica.ttf │ │ ├── dogicabold.ttf │ │ ├── dogicapixel.ttf │ │ ├── dogicapixelbold.ttf │ │ ├── dogica_license.txt │ │ └── dogica_pixel_license.txt │ └── credits.md ├── images │ ├── backdrop.png │ ├── player.png │ ├── skeleton.png │ ├── wizard.png │ ├── lpc │ │ ├── exterior.png │ │ ├── interior.png │ │ ├── outside.png │ │ ├── terrain.png │ │ └── credits.md │ ├── ardentryst │ │ ├── glow.png │ │ └── credits.md │ └── dontmind8 │ │ ├── heart.png │ │ └── credits.md ├── binds.ini └── maps │ ├── levels.yaml │ ├── old │ └── objecttypes.xml │ └── tent1.yaml ├── meta ├── house.png ├── piano.png ├── tent.png ├── bedroom.png ├── kitchen.png ├── wanderer.png ├── wanderer.afdesign ├── screenshot_plants.png └── screenshot_trees.png ├── test ├── test-resources │ ├── snapshot.png │ └── test.ini ├── unit-tests │ ├── test_main.cpp │ ├── misc │ │ └── fmt_specializations_test.cpp │ └── core │ │ └── centurion_utils_test.cpp └── CMakeLists.txt ├── source ├── wanderer │ ├── data │ │ ├── mob.hpp │ │ ├── menu_id.hpp │ │ ├── components │ │ │ ├── tags.hpp │ │ │ ├── levels.hpp │ │ │ ├── world.hpp │ │ │ ├── tiles.hpp │ │ │ ├── ui.hpp │ │ │ └── rendering.hpp │ │ ├── direction.hpp │ │ ├── cfg.hpp │ │ ├── cfg.cpp │ │ ├── day.cpp │ │ └── day.hpp │ ├── core │ │ ├── win32.hpp │ │ ├── random.cpp │ │ ├── action.hpp │ │ ├── game_loop.hpp │ │ ├── win32.cpp │ │ ├── input_state.hpp │ │ ├── input_state.cpp │ │ ├── centurion_utils.hpp │ │ ├── random.hpp │ │ ├── math.hpp │ │ ├── game_loop.cpp │ │ └── graphics.hpp │ ├── events │ │ ├── misc_events.hpp │ │ └── player_events.hpp │ ├── fwd.hpp │ ├── io │ │ ├── json.hpp │ │ ├── level-parsing │ │ │ ├── parse_levels.hpp │ │ │ ├── tiled-json │ │ │ │ ├── tiled_json_layer_parser.hpp │ │ │ │ ├── tiled_json_tileset_parser.hpp │ │ │ │ ├── tiled_json_parser.hpp │ │ │ │ └── tiled_json_utils.hpp │ │ │ └── parse_levels.cpp │ │ ├── directories.cpp │ │ ├── json.cpp │ │ ├── directories.hpp │ │ ├── settings.hpp │ │ └── settings.cpp │ ├── misc │ │ ├── boost.hpp │ │ ├── exception.hpp │ │ ├── assert.cpp │ │ ├── assert.hpp │ │ ├── logging.cpp │ │ ├── logging.hpp │ │ └── fmt_specializations.hpp │ ├── systems │ │ ├── time_system.hpp │ │ ├── viewport_system.hpp │ │ ├── input_system.hpp │ │ ├── tile_system.hpp │ │ ├── registry_system.hpp │ │ ├── animation_system.hpp │ │ ├── cinematic_system.hpp │ │ ├── registry_system.cpp │ │ ├── rendering_system.hpp │ │ ├── viewport_system.cpp │ │ ├── physics_system.hpp │ │ ├── ui_system.hpp │ │ ├── animation_system.cpp │ │ ├── rendering_system.cpp │ │ ├── time_system.cpp │ │ ├── physics_system.cpp │ │ ├── tile_system.cpp │ │ ├── cinematic_system.cpp │ │ └── input_system.cpp │ ├── meta │ │ ├── profile.hpp │ │ └── build.hpp │ ├── wanderer_game.hpp │ └── common.hpp ├── doxy.hpp ├── main.cpp └── CMakeLists.txt ├── .gitattributes ├── proto ├── settings.proto ├── color.proto ├── CMakeLists.txt └── vector.proto ├── docs ├── credits.md └── troubleshooting.md ├── lib └── centurion │ ├── centurion │ ├── detail │ │ ├── sdl_deleter.hpp │ │ ├── sdl_version_at_least.hpp │ │ ├── array_utils.hpp │ │ ├── tuple_type_index.hpp │ │ ├── owner_handle_api.hpp │ │ └── stdlib.hpp │ ├── audio_events.hpp │ ├── input.hpp │ ├── features.hpp │ ├── locale.hpp │ ├── endian.hpp │ ├── vulkan.hpp │ └── window_events.hpp │ └── centurion.hpp ├── vcpkg.json ├── .gitignore ├── README.md ├── .clang-tidy ├── LICENSE ├── cmake └── Utilities.cmake ├── doxygen ├── footer.html ├── custom.css ├── header.html └── doxygen-awesome-fancy-scrollbars.css ├── .clang-format ├── CMakeLists.txt └── .github └── workflows ├── macos.yml └── windows.yml /resources/settings.ini: -------------------------------------------------------------------------------- 1 | [Graphics] 2 | Fullscreen=true 3 | UseIntegerScaling=true -------------------------------------------------------------------------------- /meta/house.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albin-johansson/wanderer/HEAD/meta/house.png -------------------------------------------------------------------------------- /meta/piano.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albin-johansson/wanderer/HEAD/meta/piano.png -------------------------------------------------------------------------------- /meta/tent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albin-johansson/wanderer/HEAD/meta/tent.png -------------------------------------------------------------------------------- /meta/bedroom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albin-johansson/wanderer/HEAD/meta/bedroom.png -------------------------------------------------------------------------------- /meta/kitchen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albin-johansson/wanderer/HEAD/meta/kitchen.png -------------------------------------------------------------------------------- /meta/wanderer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albin-johansson/wanderer/HEAD/meta/wanderer.png -------------------------------------------------------------------------------- /meta/wanderer.afdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albin-johansson/wanderer/HEAD/meta/wanderer.afdesign -------------------------------------------------------------------------------- /meta/screenshot_plants.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albin-johansson/wanderer/HEAD/meta/screenshot_plants.png -------------------------------------------------------------------------------- /meta/screenshot_trees.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albin-johansson/wanderer/HEAD/meta/screenshot_trees.png -------------------------------------------------------------------------------- /resources/audio/swing.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albin-johansson/wanderer/HEAD/resources/audio/swing.wav -------------------------------------------------------------------------------- /resources/fonts/daniel.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albin-johansson/wanderer/HEAD/resources/fonts/daniel.ttf -------------------------------------------------------------------------------- /resources/images/backdrop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albin-johansson/wanderer/HEAD/resources/images/backdrop.png -------------------------------------------------------------------------------- /resources/images/player.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albin-johansson/wanderer/HEAD/resources/images/player.png -------------------------------------------------------------------------------- /resources/images/skeleton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albin-johansson/wanderer/HEAD/resources/images/skeleton.png -------------------------------------------------------------------------------- /resources/images/wizard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albin-johansson/wanderer/HEAD/resources/images/wizard.png -------------------------------------------------------------------------------- /resources/fonts/type_writer.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albin-johansson/wanderer/HEAD/resources/fonts/type_writer.ttf -------------------------------------------------------------------------------- /resources/fonts/dogica/dogica.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albin-johansson/wanderer/HEAD/resources/fonts/dogica/dogica.ttf -------------------------------------------------------------------------------- /resources/images/lpc/exterior.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albin-johansson/wanderer/HEAD/resources/images/lpc/exterior.png -------------------------------------------------------------------------------- /resources/images/lpc/interior.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albin-johansson/wanderer/HEAD/resources/images/lpc/interior.png -------------------------------------------------------------------------------- /resources/images/lpc/outside.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albin-johansson/wanderer/HEAD/resources/images/lpc/outside.png -------------------------------------------------------------------------------- /resources/images/lpc/terrain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albin-johansson/wanderer/HEAD/resources/images/lpc/terrain.png -------------------------------------------------------------------------------- /test/test-resources/snapshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albin-johansson/wanderer/HEAD/test/test-resources/snapshot.png -------------------------------------------------------------------------------- /resources/binds.ini: -------------------------------------------------------------------------------- 1 | [Input] 2 | MoveUp=W 3 | MoveLeft=A 4 | MoveRight=D 5 | MoveDown=S 6 | Attack=Space 7 | Interact=E 8 | Inventory=I -------------------------------------------------------------------------------- /resources/images/ardentryst/glow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albin-johansson/wanderer/HEAD/resources/images/ardentryst/glow.png -------------------------------------------------------------------------------- /resources/images/dontmind8/heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albin-johansson/wanderer/HEAD/resources/images/dontmind8/heart.png -------------------------------------------------------------------------------- /resources/maps/levels.yaml: -------------------------------------------------------------------------------- 1 | # This file specifies the maps that will be loaded 2 | levels: 3 | - id: 1 4 | source: old/main.json -------------------------------------------------------------------------------- /resources/fonts/dogica/dogicabold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albin-johansson/wanderer/HEAD/resources/fonts/dogica/dogicabold.ttf -------------------------------------------------------------------------------- /resources/fonts/dogica/dogicapixel.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albin-johansson/wanderer/HEAD/resources/fonts/dogica/dogicapixel.ttf -------------------------------------------------------------------------------- /resources/fonts/dogica/dogicapixelbold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albin-johansson/wanderer/HEAD/resources/fonts/dogica/dogicapixelbold.ttf -------------------------------------------------------------------------------- /source/wanderer/data/mob.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace wanderer { 4 | 5 | enum class MobType 6 | { 7 | player 8 | }; 9 | 10 | } // namespace wanderer -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | docs linguist-documentation 5 | doxygen linguist-documentation 6 | lib linguist-vendored -------------------------------------------------------------------------------- /resources/audio/sounds.json: -------------------------------------------------------------------------------- 1 | { 2 | "sounds": [ 3 | { 4 | "id": "SwordSwing", 5 | "file": "resources/audio/swing.wav", 6 | "volume": 40 7 | } 8 | ] 9 | } -------------------------------------------------------------------------------- /proto/settings.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package wanderer.proto; 4 | 5 | message settings 6 | { 7 | optional bool fullscreen = 1; 8 | optional bool vsync = 2; 9 | optional bool integer_scaling = 3; 10 | } -------------------------------------------------------------------------------- /test/test-resources/test.ini: -------------------------------------------------------------------------------- 1 | [Graphics] 2 | Fullscreen=true 3 | 4 | [Input] 5 | ; This is a comment! 6 | MoveUp=W 7 | MoveLeft=A 8 | MoveRight=D 9 | MoveDown=S 10 | Attack=Space 11 | Interact=E 12 | Foo=123 13 | Bar=12.3 -------------------------------------------------------------------------------- /proto/color.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package wanderer.proto; 4 | 5 | message color 6 | { 7 | optional uint32 red = 1; 8 | optional uint32 green = 2; 9 | optional uint32 blue = 3; 10 | optional uint32 alpha = 4; 11 | } -------------------------------------------------------------------------------- /source/wanderer/core/win32.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "wanderer/common.hpp" 6 | 7 | namespace wanderer::win32 { 8 | 9 | void use_immersive_dark_mode(cen::window& window); 10 | 11 | } // namespace wanderer::win32 -------------------------------------------------------------------------------- /source/wanderer/events/misc_events.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "wanderer/common.hpp" 4 | #include "wanderer/core/action.hpp" 5 | 6 | namespace wanderer { 7 | 8 | struct ActionEvent final 9 | { 10 | Action action{Action::noop}; 11 | }; 12 | 13 | } // namespace wanderer -------------------------------------------------------------------------------- /source/wanderer/fwd.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace wanderer { 4 | 5 | struct GameConfig; 6 | struct Settings; 7 | 8 | struct ActionEvent; 9 | struct MovePlayerEvent; 10 | struct StopPlayerEvent; 11 | 12 | class Graphics; 13 | class InputState; 14 | 15 | } // namespace wanderer -------------------------------------------------------------------------------- /docs/credits.md: -------------------------------------------------------------------------------- 1 | # Credits 2 | 3 | This document provides the credits to third-party assets and library dependencies used in Wanderer. 4 | 5 | ## Assets 6 | 7 | N/A 8 | 9 | ## Acknowledgements 10 | 11 | - [Theo Wiik](https://github.com/theowiik) Provided feedback and ideas. 12 | - [Oscar Almström](https://github.com/oscaralmstrom) Provided feedback and ideas. -------------------------------------------------------------------------------- /source/wanderer/events/player_events.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "wanderer/common.hpp" 4 | #include "wanderer/data/direction.hpp" 5 | 6 | namespace wanderer { 7 | 8 | struct MovePlayerEvent final 9 | { 10 | uint32 direction_mask{}; 11 | }; 12 | 13 | struct StopPlayerEvent final 14 | { 15 | uint32 direction_mask{}; 16 | }; 17 | 18 | } // namespace wanderer -------------------------------------------------------------------------------- /source/wanderer/data/menu_id.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace wanderer { 4 | 5 | /** 6 | * \brief Represents the available game menus. 7 | */ 8 | enum class MenuId 9 | { 10 | game, ///< In-game menu. 11 | home, ///< Main menu. 12 | options, ///< Options menu. 13 | saves, ///< Saves menu. 14 | credits, ///< Credits menu. 15 | }; 16 | 17 | } // namespace wanderer 18 | -------------------------------------------------------------------------------- /lib/centurion/centurion/detail/sdl_deleter.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CENTURION_DETAIL_SDL_DELETER_HPP_ 2 | #define CENTURION_DETAIL_SDL_DELETER_HPP_ 3 | 4 | #include 5 | 6 | /// \cond FALSE 7 | 8 | namespace cen::detail { 9 | 10 | struct sdl_deleter final 11 | { 12 | void operator()(void* ptr) noexcept { SDL_free(ptr); } 13 | }; 14 | 15 | } // namespace cen::detail 16 | 17 | /// \endcond 18 | 19 | #endif // CENTURION_DETAIL_SDL_DELETER_HPP_ 20 | -------------------------------------------------------------------------------- /source/wanderer/data/components/tags.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace wanderer::comp { 4 | 5 | /// \addtogroup components 6 | /// \{ 7 | 8 | /** 9 | * \brief Tag component used to identify the player entity. 10 | */ 11 | struct Player final 12 | {}; 13 | 14 | /** 15 | * \brief Tag component used to identify humanoid entities. 16 | */ 17 | struct Humanoid final 18 | {}; 19 | 20 | /// \} End of group components 21 | 22 | } // namespace wanderer::comp -------------------------------------------------------------------------------- /source/wanderer/io/json.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace wanderer { 6 | 7 | /** 8 | * \brief Attempts to parse a JSON file. 9 | * 10 | * \param path the file path to the source JSON file. 11 | * 12 | * \return a JSON object. 13 | * 14 | * \throws wanderer_error if something goes wrong. 15 | */ 16 | [[nodiscard]] auto read_json(const std::filesystem::path& path) -> nlohmann::json; 17 | 18 | } // namespace wanderer 19 | -------------------------------------------------------------------------------- /resources/images/ardentryst/credits.md: -------------------------------------------------------------------------------- 1 | # Credits: Ardentryst 2 | 3 | ## Assets 4 | 5 | * `glow.png` 6 | 7 | ## Authors 8 | 9 | * Jordan Trudgett, 2008 10 | 11 | ## Licenses 12 | 13 | * [CC-BY 3.0](http://creativecommons.org/licenses/by/3.0/) 14 | 15 | ## Sources 16 | 17 | * [OpenGameArt](https://opengameart.org/content/magic-sprite-effects-for-ardentryst-by-jordan-trudgett) 18 | 19 | ## Additional information 20 | 21 | * See `license.txt` for full license. 22 | -------------------------------------------------------------------------------- /test/unit-tests/test_main.cpp: -------------------------------------------------------------------------------- 1 | #define DOCTEST_CONFIG_IMPLEMENT 2 | 3 | #include 4 | #include 5 | 6 | auto main(int, char**) -> int 7 | { 8 | const cen::sdl sdl; 9 | const cen::img img; 10 | const cen::mix mix; 11 | const cen::ttf ttf; 12 | 13 | doctest::Context context; 14 | const auto result = context.run(); 15 | 16 | if (context.shouldExit()) { 17 | return result; 18 | } 19 | 20 | return result; 21 | } 22 | -------------------------------------------------------------------------------- /vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json", 3 | "name": "wanderer", 4 | "version": "0.1.0", 5 | "dependencies": [ 6 | "sdl2", 7 | "sdl2-image", 8 | "sdl2-ttf", 9 | "sdl2-mixer", 10 | "protobuf", 11 | "entt", 12 | "glm", 13 | "fmt", 14 | "magic-enum", 15 | "yaml-cpp", 16 | "nlohmann-json", 17 | "argparse", 18 | "box2d", 19 | "doctest" 20 | ] 21 | } -------------------------------------------------------------------------------- /source/wanderer/io/level-parsing/parse_levels.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "wanderer/common.hpp" 6 | #include "wanderer/fwd.hpp" 7 | 8 | namespace wanderer { 9 | 10 | /** 11 | * \brief Loads all levels into the main registry. 12 | * 13 | * \param shared the shared main registry. 14 | * \param graphics the current graphics context. 15 | */ 16 | void parse_levels(entt::registry& shared, Graphics& graphics); 17 | 18 | } // namespace wanderer 19 | -------------------------------------------------------------------------------- /source/wanderer/io/level-parsing/tiled-json/tiled_json_layer_parser.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "wanderer/common.hpp" 8 | 9 | namespace wanderer::io::tiled { 10 | 11 | void parse_layer(const nlohmann::json& json, 12 | entt::registry& registry, 13 | int32 z, 14 | const glm::vec2& tileSizeRatio); 15 | 16 | } // namespace wanderer::io::tiled -------------------------------------------------------------------------------- /proto/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | project(wanderer-proto CXX) 4 | 5 | file(GLOB_RECURSE PROTO_FILES CONFIGURE_DEPENDS *.proto) 6 | 7 | protobuf_generate_cpp(PROTO_SOURCES PROTO_HEADERS ${PROTO_FILES}) 8 | 9 | add_library(${TARGET_WANDERER_PROTO} ${PROTO_HEADERS} ${PROTO_SOURCES}) 10 | 11 | target_include_directories(${TARGET_WANDERER_PROTO} SYSTEM PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) 12 | 13 | target_link_libraries(${TARGET_WANDERER_PROTO} PRIVATE protobuf::libprotobuf) 14 | -------------------------------------------------------------------------------- /source/wanderer/misc/boost.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "wanderer/meta/build.hpp" 4 | 5 | #if WANDERER_COMPILER_GCC || WANDERER_COMPILER_CLANG 6 | #pragma GCC diagnostic push 7 | #pragma GCC diagnostic ignored "-Wsign-conversion" 8 | #endif // TACTILE_COMPILER_GCC || TACTILE_COMPILER_CLANG 9 | 10 | #include 11 | #include 12 | 13 | #if WANDERER_COMPILER_GCC || WANDERER_COMPILER_CLANG 14 | #pragma GCC diagnostic pop 15 | #endif // TACTILE_COMPILER_GCC || TACTILE_COMPILER_CLANG 16 | -------------------------------------------------------------------------------- /source/wanderer/io/directories.cpp: -------------------------------------------------------------------------------- 1 | #include "directories.hpp" 2 | 3 | #include 4 | 5 | namespace wanderer { 6 | 7 | auto get_persistent_file_dir() -> const std::filesystem::path& 8 | { 9 | static const std::filesystem::path path = 10 | cen::preferred_path("albin-johansson", "wanderer").copy(); 11 | return path; 12 | } 13 | 14 | auto get_saves_dir() -> const std::filesystem::path& 15 | { 16 | static const auto path = get_persistent_file_dir() / "saves"; 17 | return path; 18 | } 19 | 20 | } // namespace wanderer 21 | -------------------------------------------------------------------------------- /source/wanderer/io/level-parsing/tiled-json/tiled_json_tileset_parser.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // path 4 | 5 | #include 6 | #include 7 | 8 | #include "wanderer/common.hpp" 9 | #include "wanderer/fwd.hpp" 10 | 11 | namespace wanderer::io::tiled { 12 | 13 | void parse_tileset(const nlohmann::json& json, 14 | const std::filesystem::path& dir, 15 | entt::registry& registry, 16 | Graphics& graphics); 17 | 18 | } // namespace wanderer::io::tiled -------------------------------------------------------------------------------- /source/wanderer/io/json.cpp: -------------------------------------------------------------------------------- 1 | #include "json.hpp" 2 | 3 | #include // ifstream 4 | #include // ios 5 | 6 | #include "wanderer/misc/exception.hpp" 7 | 8 | namespace wanderer { 9 | 10 | auto read_json(const std::filesystem::path& path) -> nlohmann::json 11 | { 12 | try { 13 | std::ifstream stream{path, std::ios::in}; 14 | 15 | nlohmann::json json; 16 | stream >> json; 17 | 18 | return json; 19 | } 20 | catch (const std::exception& e) { 21 | throw_traced(WandererError{e.what()}); 22 | } 23 | } 24 | 25 | } // namespace wanderer -------------------------------------------------------------------------------- /source/wanderer/io/directories.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // path 4 | 5 | namespace wanderer { 6 | 7 | /** 8 | * \brief Returns the path to the directory used for storing persistent files. 9 | * 10 | * \return the persistent file directory. 11 | */ 12 | [[nodiscard]] auto get_persistent_file_dir() -> const std::filesystem::path&; 13 | 14 | /** 15 | * \brief Returns the path to the persistent save directory. 16 | * 17 | * \return the persistent save file directory. 18 | */ 19 | [[nodiscard]] auto get_saves_dir() -> const std::filesystem::path&; 20 | 21 | } // namespace wanderer 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | *.filters 35 | *.user 36 | 37 | .DS_Store 38 | .idea 39 | .vs 40 | .vscode 41 | 42 | compile_commands.json 43 | 44 | build 45 | cmake-build-* 46 | doxygen-out 47 | resources/reserves/* -------------------------------------------------------------------------------- /source/wanderer/data/components/levels.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "wanderer/common.hpp" 6 | 7 | namespace wanderer::comp { 8 | 9 | /// \addtogroup components 10 | /// \{ 11 | 12 | /** 13 | * \brief A context component used to keep track of all levels in the shared registry. 14 | */ 15 | struct Levels final 16 | { 17 | WANDERER_DELETE_COPY(Levels) 18 | WANDERER_DEFAULT_MOVE(Levels) 19 | 20 | Levels() = default; 21 | 22 | hash_map levels; 23 | level_id current{}; 24 | }; 25 | 26 | /// \} End of group components 27 | 28 | } // namespace wanderer::comp -------------------------------------------------------------------------------- /source/wanderer/data/direction.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "wanderer/common.hpp" 4 | 5 | namespace wanderer { 6 | 7 | inline constexpr uint32 direction_up_bit = 1u << 0u; 8 | inline constexpr uint32 direction_down_bit = 1u << 1u; 9 | inline constexpr uint32 direction_right_bit = 1u << 2u; 10 | inline constexpr uint32 direction_left_bit = 1u << 3u; 11 | 12 | inline constexpr uint32 direction_up_and_down = direction_up_bit | direction_down_bit; 13 | inline constexpr uint32 direction_left_and_right = direction_left_bit | // 14 | direction_right_bit; 15 | 16 | } // namespace wanderer -------------------------------------------------------------------------------- /source/wanderer/data/cfg.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "wanderer/common.hpp" 6 | 7 | namespace wanderer { 8 | 9 | /** 10 | * \brief Provides runtime "constants" used by the game. 11 | */ 12 | struct GameConfig final 13 | { 14 | glm::ivec2 logical_size{}; 15 | glm::vec2 logical_size_f{}; 16 | glm::vec2 tile_size{}; 17 | glm::vec2 humanoid_draw_size{}; 18 | }; 19 | 20 | /** 21 | * \brief Creates a game configuration appropriate for use on the current system. 22 | * 23 | * \return a game configuration. 24 | */ 25 | [[nodiscard]] auto make_game_cfg() -> GameConfig; 26 | 27 | } // namespace wanderer 28 | -------------------------------------------------------------------------------- /resources/fonts/credits.md: -------------------------------------------------------------------------------- 1 | # Credits: Fonts 2 | 3 | ## Daniel Regular 4 | 5 | * Asset: `daniel.ttf` 6 | * Author: Daniel Midgley 7 | * License: [CC-BY-ND](https://creativecommons.org/licenses/by-nd/3.0/) 8 | * Source: [www.1001fonts.com](https://www.1001fonts.com/daniel-font.html) 9 | * Notes: This font has not been modified in any way. 10 | 11 | ## Type Writer 12 | 13 | * Asset: `type_writer.ttf` 14 | * Author: Mandy Smith 15 | * License: [CC-BY-SA 3.0](http://creativecommons.org/licenses/by-sa/3.0/) 16 | * Source: [www.fontstruct.com](https://fontstruct.com/fontstructions/show/212255/type_writer) 17 | * Notes: This font has not been modified in any way. 18 | -------------------------------------------------------------------------------- /source/wanderer/systems/time_system.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "wanderer/common.hpp" 6 | 7 | namespace wanderer::sys { 8 | 9 | /** 10 | * \ingroup systems 11 | * \defgroup time-system Time system 12 | */ 13 | 14 | /// \addtogroup time-system 15 | /// \{ 16 | 17 | /** 18 | * \brief Updates the state of the in-game time. 19 | * 20 | * \param registry the level registry. 21 | * \param dispatcher the event dispatcher. 22 | * \param dt the delta time. 23 | */ 24 | void update_time(entt::registry& registry, entt::dispatcher& dispatcher, float32 dt); 25 | 26 | /// \} End of group time-system 27 | 28 | } // namespace wanderer::sys 29 | -------------------------------------------------------------------------------- /lib/centurion/centurion/detail/sdl_version_at_least.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CENTURION_DETAIL_SDL_VERSION_AT_LEAST_HPP_ 2 | #define CENTURION_DETAIL_SDL_VERSION_AT_LEAST_HPP_ 3 | 4 | #include 5 | 6 | #include "../version.hpp" 7 | 8 | /// \cond FALSE 9 | 10 | namespace cen::detail { 11 | 12 | [[nodiscard]] constexpr auto sdl_version_at_least(const int major, 13 | const int minor, 14 | const int patch) noexcept -> bool 15 | { 16 | return SDL_COMPILEDVERSION >= SDL_VERSIONNUM(major, minor, patch); 17 | } 18 | 19 | } // namespace cen::detail 20 | 21 | /// \endcond 22 | 23 | #endif // CENTURION_DETAIL_SDL_VERSION_AT_LEAST_HPP_ 24 | -------------------------------------------------------------------------------- /source/wanderer/core/random.cpp: -------------------------------------------------------------------------------- 1 | #include "random.hpp" 2 | 3 | #include // generate 4 | #include // array 5 | #include // ref 6 | #include // random_device, seed_seq 7 | 8 | namespace wanderer { 9 | 10 | auto make_random_engine() -> random_engine 11 | { 12 | using result_type = std::random_device::result_type; 13 | 14 | constexpr auto n = random_engine::state_size; 15 | 16 | std::random_device device; 17 | std::array data{}; 18 | 19 | std::generate(data.begin(), data.end(), std::ref(device)); 20 | std::seed_seq seeds(data.begin(), data.end()); 21 | 22 | return random_engine(seeds); 23 | } 24 | 25 | } // namespace wanderer -------------------------------------------------------------------------------- /source/wanderer/core/action.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace wanderer { 4 | 5 | enum class Action 6 | { 7 | noop, ///< Do nothing. 8 | quit, ///< Quit the game. 9 | 10 | goto_game, ///< Play the game (enter the in-game "menu"). 11 | goto_main_menu, ///< Go to the main menu. 12 | goto_options_menu, ///< Go to the options menu. 13 | goto_saves_menu, ///< Go to the saves menu. 14 | goto_credits_menu, ///< Go to the options menu. 15 | 16 | quick_save, ///< Create a quick save. 17 | 18 | toggle_fullscreen, ///< Toggle fullscreen window mode. 19 | toggle_vsync, ///< Toggle VSync. 20 | toggle_integer_scaling ///< Toggle integer scaling for logical viewports. 21 | }; 22 | 23 | } // namespace wanderer -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Wanderer 2 | 3 | [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT) 4 | [![CI: Windows](https://github.com/albin-johansson/wanderer/actions/workflows/windows.yml/badge.svg?branch=dev)](https://github.com/albin-johansson/wanderer/actions/workflows/windows.yml) 5 | [![CI: macOS](https://github.com/albin-johansson/wanderer/actions/workflows/macos.yml/badge.svg?branch=dev)](https://github.com/albin-johansson/wanderer/actions/workflows/macos.yml) 6 | 7 | Wanderer is an 2D indie game, being developed for fun. At the time of writing, the game is still in an experimental 8 | state and isn't ready to be played. This project also serves as a "tech demo" for the Centurion library, which is also 9 | developed by me. 10 | -------------------------------------------------------------------------------- /source/wanderer/io/level-parsing/tiled-json/tiled_json_parser.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // path 4 | 5 | #include 6 | 7 | #include "wanderer/fwd.hpp" 8 | 9 | namespace wanderer::io { 10 | 11 | /** 12 | * \brief Parses a map file using the Tiled JSON format. 13 | * 14 | * \param path the file path to the source JSON file. 15 | * \param graphics the associated graphics context. 16 | * \param cfg the current game configuration. 17 | * 18 | * \return a level registry. 19 | */ 20 | [[nodiscard]] auto parse_tiled_json_map(const std::filesystem::path& path, 21 | Graphics& graphics, 22 | const GameConfig& cfg) -> entt::registry; 23 | 24 | } // namespace wanderer::io 25 | -------------------------------------------------------------------------------- /proto/vector.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package wanderer.proto; 4 | 5 | message fvec2 6 | { 7 | optional float x = 1; 8 | optional float y = 2; 9 | } 10 | 11 | message ivec2 12 | { 13 | optional int32 x = 1; 14 | optional int32 y = 2; 15 | } 16 | 17 | message fvec3 18 | { 19 | optional float x = 1; 20 | optional float y = 2; 21 | optional float z = 3; 22 | } 23 | 24 | message ivec3 25 | { 26 | optional int32 x = 1; 27 | optional int32 y = 2; 28 | optional int32 z = 3; 29 | } 30 | 31 | message fvec4 32 | { 33 | optional float x = 1; 34 | optional float y = 2; 35 | optional float z = 3; 36 | optional float w = 4; 37 | } 38 | 39 | message ivec4 40 | { 41 | optional int32 x = 1; 42 | optional int32 y = 2; 43 | optional int32 z = 3; 44 | optional int32 w = 4; 45 | } -------------------------------------------------------------------------------- /resources/images/dontmind8/credits.md: -------------------------------------------------------------------------------- 1 | # Credits: DontMind8 2 | 3 | ## Assets 4 | 5 | * `heart.png` 6 | 7 | ## Authors 8 | 9 | * Dan Sevenstar, 2017 10 | 11 | ## Licenses 12 | 13 | * [CC-BY 4.0](https://creativecommons.org/licenses/by/4.0/) 14 | * [CC-BY 3.0](http://creativecommons.org/licenses/by/3.0/) 15 | 16 | ## Sources 17 | 18 | * [OpenGameArt](https://opengameart.org/content/heart-pixel-art) 19 | * [dontmind8.blogspot.com](http://dontmind8.blogspot.com/) 20 | 21 | ## Additional information 22 | 23 | "These Arts are completely royalty free, meaning you can use them commercially without paying a cent. Creative Commons Attribution 4.0 simply means you can use these arts commercially in things like movies, games, and anything else. You can modify or edit the arts as you like. Download Free Background Music At www.soundemperor.com" 24 | -------------------------------------------------------------------------------- /source/wanderer/data/cfg.cpp: -------------------------------------------------------------------------------- 1 | #include "cfg.hpp" 2 | 3 | #include 4 | 5 | #include "wanderer/misc/logging.hpp" 6 | #include "wanderer/core/math.hpp" 7 | 8 | namespace wanderer { 9 | 10 | auto make_game_cfg() -> GameConfig 11 | { 12 | // TODO persistent UI scale factor? 13 | 14 | const auto display = cen::display_mode::desktop(); 15 | const auto screen = display.size().as_f(); 16 | 17 | debug("Screen size is {}", glm::vec2{screen.width, screen.height}); 18 | 19 | GameConfig cfg; 20 | cfg.logical_size_f.x = 960; 21 | cfg.logical_size_f.y = cfg.logical_size_f.x * (screen.height / screen.width); 22 | 23 | cfg.logical_size = glmx::as_i(cfg.logical_size_f); 24 | 25 | cfg.tile_size = {48, 48}; 26 | cfg.humanoid_draw_size = cfg.tile_size * 2.0f; 27 | 28 | return cfg; 29 | } 30 | 31 | } // namespace wanderer 32 | -------------------------------------------------------------------------------- /source/wanderer/meta/profile.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "wanderer/misc/logging.hpp" 6 | 7 | #ifdef NDEBUG 8 | #define WANDERER_PROFILE_START 9 | #else 10 | #define WANDERER_PROFILE_START const auto wndr_profile_start = cen::now(); 11 | #endif // NDEBUG 12 | 13 | #ifdef NDEBUG 14 | #define WANDERER_PROFILE_END(Msg) 15 | #else 16 | #define WANDERER_PROFILE_END(Msg) \ 17 | const auto wndr_profile_end = cen::now(); \ 18 | const auto wndr_profile_diff = \ 19 | static_cast(wndr_profile_end - wndr_profile_start); \ 20 | const auto wndr_profile_freq = static_cast(cen::frequency()); \ 21 | wanderer::debug(Msg " in {} seconds", wndr_profile_diff / wndr_profile_freq); 22 | #endif // NDEBUG -------------------------------------------------------------------------------- /source/wanderer/misc/exception.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // exception 4 | 5 | #include "boost.hpp" 6 | #include "wanderer/meta/build.hpp" 7 | 8 | namespace wanderer { 9 | 10 | class WandererError : public std::exception 11 | { 12 | public: 13 | WandererError() noexcept = default; 14 | 15 | explicit WandererError(const char* msg) noexcept : mWhat{msg} {} 16 | 17 | [[nodiscard]] auto what() const noexcept -> const char* override { return mWhat; } 18 | 19 | private: 20 | const char* mWhat{}; 21 | }; 22 | 23 | using TraceInfo = boost::error_info; 24 | 25 | template 26 | [[noreturn]] void throw_traced(const Exception& e) 27 | { 28 | throw boost::enable_error_info(e) << TraceInfo{boost::stacktrace::stacktrace()}; 29 | } 30 | 31 | } // namespace wanderer -------------------------------------------------------------------------------- /source/wanderer/systems/viewport_system.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "wanderer/common.hpp" 6 | 7 | namespace wanderer::sys { 8 | 9 | /** 10 | * \ingroup systems 11 | * \defgroup viewport-system Viewport system 12 | * 13 | * \brief Responsible for viewport related aspects. 14 | */ 15 | 16 | /// \addtogroup viewport-system 17 | /// \{ 18 | 19 | /** 20 | * \brief Updates the tilemap render bounds for a level registry. 21 | * 22 | * \param registry the current level registry. 23 | */ 24 | void update_render_bounds(entt::registry& registry); 25 | 26 | /** 27 | * \brief Updates the viewport state. 28 | * 29 | * \param registry the current level registry. 30 | * \param dt the current delta time. 31 | */ 32 | void update_viewport(entt::registry& registry, float32 dt); 33 | 34 | /// \} End of group viewport-system 35 | 36 | } // namespace wanderer::sys -------------------------------------------------------------------------------- /source/wanderer/misc/assert.cpp: -------------------------------------------------------------------------------- 1 | #include "assert.hpp" 2 | 3 | #include // abort 4 | #include // stringstream 5 | 6 | #include "boost.hpp" 7 | #include "wanderer/meta/build.hpp" 8 | #include "wanderer/misc/logging.hpp" 9 | 10 | namespace boost { 11 | 12 | void assertion_failed_msg(const char* expr, 13 | const char* msg, 14 | const char*, 15 | const char* file, 16 | const long line) 17 | { 18 | std::stringstream stream; 19 | stream << stacktrace::stacktrace{}; 20 | 21 | wanderer::print(fmt::color::orange_red, 22 | "{}:{} expression '{}' evaluated to false: {}\n{}", 23 | file, 24 | line, 25 | expr, 26 | msg ? msg : "n/a", 27 | stream.str()); 28 | std::abort(); 29 | } 30 | 31 | } // namespace boost -------------------------------------------------------------------------------- /source/wanderer/data/day.cpp: -------------------------------------------------------------------------------- 1 | #include "day.hpp" 2 | 3 | #include 4 | 5 | #include "wanderer/misc/exception.hpp" 6 | 7 | namespace wanderer { 8 | 9 | auto full_name(const DayOfWeek day) -> std::string_view 10 | { 11 | return magic_enum::enum_name(day); 12 | } 13 | 14 | auto short_name(const DayOfWeek day) -> std::string_view 15 | { 16 | switch (day) { 17 | case DayOfWeek::monday: 18 | return "MON"; 19 | 20 | case DayOfWeek::tuesday: 21 | return "TUE"; 22 | 23 | case DayOfWeek::wednesday: 24 | return "WED"; 25 | 26 | case DayOfWeek::thursday: 27 | return "THU"; 28 | 29 | case DayOfWeek::friday: 30 | return "FRI"; 31 | 32 | case DayOfWeek::saturday: 33 | return "SAT"; 34 | 35 | case DayOfWeek::sunday: 36 | return "SUN"; 37 | 38 | default: 39 | throw_traced(WandererError{"Invalid day!"}); 40 | } 41 | } 42 | 43 | } // namespace wanderer -------------------------------------------------------------------------------- /source/wanderer/core/game_loop.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "wanderer/common.hpp" 4 | 5 | namespace wanderer { 6 | 7 | struct LoopState final 8 | { 9 | float64 rate{}; ///< Refresh rate (in Hz). 10 | float64 fixed_dt{}; ///< Fixed delta time. 11 | float64 then{}; ///< Time of last update. 12 | float64 frequency{}; ///< The frequency of the system counter. 13 | int32 max_ticks_per_frame{}; ///< Maximum amount of ticks per frame. 14 | }; 15 | 16 | class GameLoop 17 | { 18 | public: 19 | GameLoop(); 20 | 21 | virtual ~GameLoop() noexcept = default; 22 | 23 | void start(); 24 | 25 | void stop(); 26 | 27 | protected: 28 | virtual void process_events() {} 29 | 30 | virtual void update([[maybe_unused]] float32 dt) {} 31 | 32 | virtual void render() {} 33 | 34 | private: 35 | LoopState _state; 36 | bool _running{}; 37 | }; 38 | 39 | } // namespace wanderer 40 | -------------------------------------------------------------------------------- /source/wanderer/data/day.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // string_view 4 | 5 | namespace wanderer { 6 | 7 | /// \addtogroup core 8 | /// \{ 9 | 10 | /** 11 | * \brief Represents the days of the week. 12 | */ 13 | enum class DayOfWeek 14 | { 15 | monday, 16 | tuesday, 17 | wednesday, 18 | thursday, 19 | friday, 20 | saturday, 21 | sunday 22 | }; 23 | 24 | /** 25 | * \brief Returns the full name of a day enumerator, e.g. "monday". 26 | * 27 | * \param day the day to get the name of. 28 | * 29 | * \return the full day name. 30 | */ 31 | [[nodiscard]] auto full_name(DayOfWeek day) -> std::string_view; 32 | 33 | /** 34 | * \brief Returns the abbreviated name of a day enumerator, e.g. "MON". 35 | * 36 | * \param day the day to get the name of. 37 | * 38 | * \return the abbreviated day name. 39 | */ 40 | [[nodiscard]] auto short_name(DayOfWeek day) -> std::string_view; 41 | 42 | /// \} End of group core 43 | 44 | } // namespace wanderer -------------------------------------------------------------------------------- /source/wanderer/systems/input_system.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "wanderer/common.hpp" 7 | #include "wanderer/fwd.hpp" 8 | 9 | namespace wanderer::sys { 10 | 11 | /** 12 | * \ingroup systems 13 | * \defgroup input-system Input system 14 | * 15 | * \brief Responsible for responding to user input. 16 | */ 17 | 18 | /// \addtogroup input-system 19 | /// \{ 20 | 21 | /** 22 | * \brief Processes the current input state and dispatches appropriate events. 23 | * 24 | * \param dispatcher the event dispatcher that will be used. 25 | * \param input the current input state. 26 | */ 27 | void update_input(entt::dispatcher& dispatcher, const InputState& input); 28 | 29 | void on_move_player(entt::registry& registry, const MovePlayerEvent& event); 30 | 31 | void on_stop_player(entt::registry& registry, const StopPlayerEvent& event); 32 | 33 | /// \} End of group input-system 34 | 35 | } // namespace wanderer::sys 36 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | FormatStyle: google 2 | Checks: "-*, 3 | performance-*, 4 | bugprone-*, 5 | -bugprone-easily-swappable-parameters, 6 | 7 | readability-*, 8 | -readability-identifier-length, 9 | -readability-uppercase-literal-suffix, 10 | -readability-magic-numbers, 11 | -readability-implicit-bool-conversion, 12 | -readability-else-after-return, 13 | -readability-named-parameter, 14 | -readability-function-cognitive-complexity, 15 | modernize-*, 16 | clang-analyzer-*, 17 | 18 | misc-*, 19 | -misc-non-private-member-variables-in-classes, 20 | 21 | cppcoreguidelines-*, 22 | -cppcoreguidelines-avoid-magic-numbers, 23 | -cppcoreguidelines-pro-bounds-array-to-pointer-decay, 24 | -cppcoreguidelines-pro-type-vararg, 25 | -cppcoreguidelines-owning-memory, 26 | -cppcoreguidelines-avoid-goto, 27 | " -------------------------------------------------------------------------------- /source/wanderer/misc/assert.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "wanderer/meta/build.hpp" 6 | 7 | #if WANDERER_DEBUG_BUILD 8 | 9 | #define WANDERER_ASSERT(Expr) BOOST_ASSERT(Expr) 10 | #define WANDERER_ASSERT_MSG(Expr, Msg) BOOST_ASSERT_MSG(Expr, Msg) 11 | 12 | #else 13 | 14 | #define WANDERER_ASSERT(Expr) 15 | #define WANDERER_ASSERT_MSG(Expr, Msg) 16 | 17 | #endif // WANDERER_DEBUG_BUILD 18 | 19 | namespace boost { 20 | 21 | void assertion_failed_msg(const char* expr, 22 | const char* msg, 23 | const char* function, 24 | const char* file, 25 | long line); 26 | 27 | inline void assertion_failed(const char* expr, 28 | const char* function, 29 | const char* file, 30 | const long line) 31 | { 32 | assertion_failed_msg(expr, nullptr, function, file, line); 33 | } 34 | 35 | } // namespace boost -------------------------------------------------------------------------------- /source/wanderer/core/win32.cpp: -------------------------------------------------------------------------------- 1 | #include "win32.hpp" 2 | 3 | #include 4 | 5 | #include "wanderer/meta/build.hpp" 6 | 7 | #if WANDERER_PLATFORM_WINDOWS 8 | 9 | #include // DWMWA_USE_IMMERSIVE_DARK_MODE 10 | 11 | #endif // WANDERER_PLATFORM_WINDOWS 12 | 13 | namespace wanderer::win32 { 14 | 15 | void use_immersive_dark_mode([[maybe_unused]] cen::window& window) 16 | { 17 | #if WANDERER_PLATFORM_WINDOWS 18 | 19 | SDL_SysWMinfo wm{}; 20 | SDL_VERSION(&wm.version); 21 | 22 | if (SDL_GetWindowWMInfo(window.get(), &wm)) { 23 | HWND hwnd = wm.info.win.window; 24 | 25 | const cen::shared_object dwmapi{"dwmapi.dll"}; 26 | 27 | using signature = HRESULT(HWND, DWORD, LPCVOID, DWORD); 28 | if (auto* setAttribute = dwmapi.load_function("DwmSetWindowAttribute")) { 29 | BOOL mode = 1; 30 | setAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &mode, sizeof mode); 31 | } 32 | } 33 | 34 | #endif // WANDERER_PLATFORM_WINDOWS 35 | } 36 | 37 | } // namespace wanderer::win32 -------------------------------------------------------------------------------- /source/wanderer/core/input_state.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "wanderer/common.hpp" 6 | 7 | namespace wanderer { 8 | 9 | class InputState final 10 | { 11 | public: 12 | void refresh(const cen::renderer& renderer); 13 | 14 | [[nodiscard]] auto is_pressed(const cen::scan_code& code) const -> bool; 15 | 16 | [[nodiscard]] auto is_held(const cen::scan_code& code) const -> bool; 17 | 18 | [[nodiscard]] auto was_released(const cen::scan_code& code) const -> bool; 19 | 20 | [[nodiscard]] auto was_lmb_released() const noexcept -> bool; 21 | 22 | [[nodiscard]] auto was_rmb_released() const noexcept -> bool; 23 | 24 | [[nodiscard]] auto mouse_logical_x() const noexcept -> float { return mMouseLogicalX; } 25 | 26 | [[nodiscard]] auto mouse_logical_y() const noexcept -> float { return mMouseLogicalY; } 27 | 28 | private: 29 | cen::keyboard mKeyboard; 30 | uint32 mMouseCurrentMask{}; 31 | uint32 mMousePreviousMask{}; 32 | float mMouseLogicalX{}; 33 | float mMouseLogicalY{}; 34 | }; 35 | 36 | } // namespace wanderer 37 | -------------------------------------------------------------------------------- /resources/images/lpc/credits.md: -------------------------------------------------------------------------------- 1 | # Credits: LPC 2 | 3 | ## Assets 4 | 5 | * `exterior.png` 6 | * `interior.png` 7 | * `outside.png` 8 | * `terrain.png` 9 | 10 | ## Authors 11 | 12 | * "DeadlyEssence01" 13 | * Lanea "Sharm" Zimmerman 14 | * Daniel Eddeland 15 | * William Thompsonj 16 | * Matthew "Makrohn" Krohn 17 | * Matthew Nash 18 | * "Nushio" 19 | * Casper Nilson 20 | * "Caeless" 21 | * Barbara Rivera 22 | * C Phillips 23 | * Skorpio 24 | * Daniel "HughSpectrum" Armstrong 25 | * "Xenodora" 26 | * Tuomo Untinen 27 | * "Amaris" 28 | * "Krusimira" 29 | * Leo Villeveygoux 30 | * Skyler Robert Collady 31 | * "Janna" 32 | 33 | ## Licenses 34 | 35 | * [CC-BY-SA 3.0](http://creativecommons.org/licenses/by-sa/3.0/) 36 | * [GPL 3.0](http://www.gnu.org/licenses/gpl-3.0.html) 37 | * [GPL 2.0](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html) 38 | 39 | ## Sources 40 | 41 | * [OpenGameArt](https://opengameart.org/content/lpc-submissions-merged) 42 | 43 | ## Additional information 44 | 45 | See the `attribution.txt` file for exact attribution to the many authors that contributed to the LPC assets. 46 | -------------------------------------------------------------------------------- /source/doxy.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \defgroup core 3 | */ 4 | 5 | /** 6 | * \defgroup data 7 | */ 8 | 9 | /** 10 | * \ingroup data 11 | * \defgroup components 12 | */ 13 | 14 | /** 15 | * \defgroup events 16 | */ 17 | 18 | /** 19 | * \defgroup io I/O 20 | */ 21 | 22 | /** 23 | * \defgroup misc Miscellaneous 24 | */ 25 | 26 | /** 27 | * \defgroup systems 28 | */ 29 | 30 | /** 31 | * \namespace wanderer::comp 32 | * \ingroup components 33 | */ 34 | 35 | /** 36 | * \namespace wanderer::sys 37 | * \ingroup systems 38 | */ 39 | 40 | /** 41 | * \namespace wanderer::sys::ui 42 | * \ingroup ui-system 43 | */ 44 | 45 | /** 46 | * \mainpage Wanderer 47 | * 48 | * Welcome to the source documentation for the Wanderer project! 49 | * 50 | * \section section-api Modules 51 | * 52 | * - \ref core 53 | * - \ref data 54 | * - \ref events 55 | * - \ref misc 56 | * - \ref systems 57 | * 58 | * \section section-namespaces Namespaces 59 | * 60 | * - \ref wanderer 61 | * - \ref wanderer::comp 62 | * - \ref wanderer::sys 63 | * 64 | * \section section-misc Other 65 | * 66 | * - TODO 67 | */ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2022 Albin Johansson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /source/wanderer/io/level-parsing/tiled-json/tiled_json_utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "wanderer/misc/exception.hpp" 6 | 7 | namespace wanderer::io::tiled { 8 | 9 | template 10 | [[nodiscard]] auto get_property(const nlohmann::json& json, const char* name) -> T 11 | { 12 | if (const auto props = json.find("properties"); props != json.end()) { 13 | for (const auto& [key, object] : props->items()) { 14 | if (name == object.at("name")) { 15 | return object.at("value").get(); 16 | } 17 | } 18 | } 19 | 20 | throw_traced(WandererError{"Could not find property with the specified name!"}); 21 | } 22 | 23 | template 24 | [[nodiscard]] auto get_property(const nlohmann::json& json, const char* name, T fallback) 25 | -> T 26 | { 27 | if (const auto props = json.find("properties"); props != json.end()) { 28 | for (const auto& [key, object] : props->items()) { 29 | if (name == object.at("name")) { 30 | return object.at("value").get(); 31 | } 32 | } 33 | } 34 | 35 | return fallback; 36 | } 37 | 38 | } // namespace wanderer::io::tiled 39 | -------------------------------------------------------------------------------- /lib/centurion/centurion/detail/array_utils.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CENTURION_DETAIL_ARRAY_UTILS_HPP_ 2 | #define CENTURION_DETAIL_ARRAY_UTILS_HPP_ 3 | 4 | #include // array, to_array 5 | #include // size_t 6 | 7 | #include "../common.hpp" 8 | #include "../features.hpp" 9 | 10 | /// \cond FALSE 11 | 12 | namespace cen::detail { 13 | 14 | template 15 | constexpr void assign(const std::array& array, bounded_array_ref out) 16 | { 17 | std::size_t index = 0; 18 | for (auto&& value : array) { 19 | out[index] = value; 20 | ++index; 21 | } 22 | } 23 | 24 | template 25 | [[nodiscard]] constexpr auto to_array(bounded_array_ref data) 26 | -> std::array 27 | { 28 | #if CENTURION_HAS_FEATURE_TO_ARRAY 29 | return std::to_array(data); 30 | #else 31 | std::array array; // NOLINT 32 | 33 | for (std::size_t i = 0; i < Size; ++i) { 34 | array[i] = data[i]; 35 | } 36 | 37 | return array; 38 | #endif // CENTURION_HAS_FEATURE_TO_ARRAY 39 | } 40 | 41 | } // namespace cen::detail 42 | 43 | /// \endcond 44 | 45 | #endif // CENTURION_DETAIL_ARRAY_UTILS_HPP_ 46 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | project(wanderer-test CXX) 4 | 5 | enable_testing() 6 | 7 | file(GLOB_RECURSE TEST_FILES 8 | CONFIGURE_DEPENDS 9 | unit-tests/**/*.cpp 10 | unit-tests/*.cpp 11 | ) 12 | 13 | add_executable(${TARGET_WANDERER_TEST} ${TEST_FILES}) 14 | add_dependencies(${TARGET_WANDERER_TEST} ${TARGET_WANDERER_LIB}) 15 | 16 | wanderer_add_compiler_options(${TARGET_WANDERER_TEST}) 17 | 18 | target_include_directories(${TARGET_WANDERER_TEST} PUBLIC unit-tests) 19 | 20 | target_link_libraries(${TARGET_WANDERER_TEST} 21 | PRIVATE 22 | ${TARGET_WANDERER_LIB} 23 | SDL2::SDL2main 24 | doctest::doctest 25 | ) 26 | 27 | target_precompile_headers(${TARGET_WANDERER_TEST} REUSE_FROM ${TARGET_WANDERER_LIB}) 28 | 29 | add_test(NAME ${TARGET_WANDERER_TEST} COMMAND ${TARGET_WANDERER_TEST}) 30 | 31 | copy_directory_post_build(${TARGET_WANDERER_TEST} "${CMAKE_CURRENT_SOURCE_DIR}/test-resources" "${CMAKE_CURRENT_BINARY_DIR}/test-resources") 32 | copy_directory_post_build(${TARGET_WANDERER_TEST} ${WANDERER_RESOURCE_DIR} "${CMAKE_CURRENT_BINARY_DIR}/resources") 33 | -------------------------------------------------------------------------------- /source/wanderer/wanderer_game.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "wanderer/common.hpp" 6 | #include "wanderer/core/game_loop.hpp" 7 | #include "wanderer/core/graphics.hpp" 8 | #include "wanderer/core/input_state.hpp" 9 | #include "wanderer/data/cfg.hpp" 10 | #include "wanderer/fwd.hpp" 11 | #include "wanderer/io/settings.hpp" 12 | 13 | namespace wanderer { 14 | 15 | /** 16 | * \brief The heart of the game. 17 | */ 18 | class WandererGame final : GameLoop 19 | { 20 | public: 21 | explicit WandererGame(const GameConfig& cfg); 22 | 23 | void run(); 24 | 25 | protected: 26 | void process_events() override; 27 | 28 | void update(float32 dt) override; 29 | 30 | void render() override; 31 | 32 | private: 33 | GameConfig mCfg; 34 | Settings mSettings; 35 | 36 | Graphics mGraphics; 37 | InputState mInput; 38 | 39 | entt::dispatcher mDispatcher; 40 | entt::registry mMainRegistry; 41 | 42 | void on_action(const ActionEvent& event); 43 | 44 | void on_move_player(const MovePlayerEvent& event); 45 | 46 | void on_stop_player(const StopPlayerEvent& event); 47 | 48 | [[nodiscard]] auto current_registry() -> entt::registry&; 49 | }; 50 | 51 | } // namespace wanderer 52 | -------------------------------------------------------------------------------- /lib/centurion/centurion/detail/tuple_type_index.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CENTURION_DETAIL_TUPLE_TYPE_INDEX_HPP_ 2 | #define CENTURION_DETAIL_TUPLE_TYPE_INDEX_HPP_ 3 | 4 | #include // size_t 5 | #include // tuple 6 | #include // is_same_v 7 | #include // index_sequence, index_sequence_for 8 | 9 | #include "../common.hpp" 10 | 11 | /// \cond FALSE 12 | 13 | namespace cen::detail { 14 | 15 | template 16 | class tuple_type_index; 17 | 18 | template 19 | class tuple_type_index> 20 | { 21 | private: 22 | template 23 | [[nodiscard]] constexpr static auto Find([[maybe_unused]] std::index_sequence seq) 24 | -> int 25 | { 26 | return -1 + ((std::is_same_v ? static_cast(Index) + 1 : 0) + ...); 27 | } 28 | 29 | public: 30 | inline constexpr static auto value = Find(std::index_sequence_for{}); 31 | }; 32 | 33 | template 34 | inline constexpr int tuple_type_index_v = tuple_type_index::value; 35 | 36 | } // namespace cen::detail 37 | 38 | /// \endcond 39 | 40 | #endif // CENTURION_DETAIL_TUPLE_TYPE_INDEX_HPP_ 41 | -------------------------------------------------------------------------------- /cmake/Utilities.cmake: -------------------------------------------------------------------------------- 1 | # Copies a file. 2 | # target: the associated target. 3 | # from: the file that will be copied. 4 | # to: the target destination of the copied file. 5 | function(copy_file_post_build [target [from [to]]]) 6 | add_custom_command( 7 | TARGET ${ARGV0} POST_BUILD 8 | COMMAND ${CMAKE_COMMAND} -E copy 9 | ${ARGV1} 10 | ${ARGV2}) 11 | endfunction() 12 | 13 | # Copies a directory. 14 | # target: the associated target. 15 | # from: the directory that will be copied. 16 | # to: the target destination of the copied directory. 17 | function(copy_directory_post_build [target [from [to]]]) 18 | add_custom_command( 19 | TARGET ${ARGV0} POST_BUILD 20 | COMMAND ${CMAKE_COMMAND} -E copy_directory 21 | ${ARGV1} 22 | ${ARGV2}) 23 | endfunction() 24 | 25 | # Checks if an environment variable is defined. 26 | # envVar: the name of the actual environment variable. 27 | # name: the name of the library associated with the environment variable. 28 | function(find_env_var [envVar [name]]) 29 | if (DEFINED ENV{${ARGV0}}) 30 | message("Found ${ARGV1} environment variable! Path: " $ENV{${ARGV0}}) 31 | else () 32 | message("Couldn't find environment variable ${ARGV0}!") 33 | endif () 34 | endfunction() 35 | -------------------------------------------------------------------------------- /source/wanderer/core/input_state.cpp: -------------------------------------------------------------------------------- 1 | #include "input_state.hpp" 2 | 3 | namespace wanderer { 4 | 5 | void InputState::refresh(const cen::renderer& renderer) 6 | { 7 | mKeyboard.refresh(); 8 | 9 | mMousePreviousMask = mMouseCurrentMask; 10 | 11 | int realX{}; 12 | int realY{}; 13 | mMouseCurrentMask = SDL_GetMouseState(&realX, &realY); 14 | 15 | const auto logicalMousePos = renderer.to_logical(realX, realY); 16 | mMouseLogicalX = logicalMousePos.x(); 17 | mMouseLogicalY = logicalMousePos.y(); 18 | } 19 | 20 | auto InputState::is_pressed(const cen::scan_code& code) const -> bool 21 | { 22 | return mKeyboard.is_pressed(code); 23 | } 24 | 25 | auto InputState::is_held(const cen::scan_code& code) const -> bool 26 | { 27 | return mKeyboard.is_held(code); 28 | } 29 | 30 | auto InputState::was_released(const cen::scan_code& code) const -> bool 31 | { 32 | return mKeyboard.just_released(code); 33 | } 34 | 35 | auto InputState::was_lmb_released() const noexcept -> bool 36 | { 37 | return !(mMouseCurrentMask & SDL_BUTTON_LMASK) && mMousePreviousMask & SDL_BUTTON_LMASK; 38 | } 39 | 40 | auto InputState::was_rmb_released() const noexcept -> bool 41 | { 42 | return !(mMouseCurrentMask & SDL_BUTTON_RMASK) && mMousePreviousMask & SDL_BUTTON_RMASK; 43 | } 44 | 45 | } // namespace wanderer -------------------------------------------------------------------------------- /doxygen/footer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 15 | 16 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /lib/centurion/centurion/audio_events.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CENTURION_AUDIO_EVENTS_HPP_ 2 | #define CENTURION_AUDIO_EVENTS_HPP_ 3 | 4 | #include 5 | 6 | #include "common.hpp" 7 | #include "event_base.hpp" 8 | 9 | namespace cen { 10 | 11 | /// \addtogroup event 12 | /// \{ 13 | 14 | class audio_device_event final : public event_base 15 | { 16 | public: 17 | audio_device_event() : event_base{event_type::audio_device_added} {} 18 | 19 | explicit audio_device_event(const SDL_AudioDeviceEvent& event) noexcept : event_base{event} 20 | {} 21 | 22 | void set_which(const uint32 which) noexcept { mEvent.which = which; } 23 | 24 | void set_capture(const bool capture) noexcept { mEvent.iscapture = capture; } 25 | 26 | [[nodiscard]] auto which() const noexcept -> uint32 { return mEvent.which; } 27 | 28 | [[nodiscard]] auto is_capture() const noexcept -> bool { return mEvent.iscapture; } 29 | 30 | [[nodiscard]] auto is_output() const noexcept -> bool { return !is_capture(); } 31 | }; 32 | 33 | template <> 34 | inline auto as_sdl_event(const event_base& event) -> SDL_Event 35 | { 36 | SDL_Event e; 37 | e.adevice = event.get(); 38 | return e; 39 | } 40 | 41 | /// \} End of group event 42 | 43 | } // namespace cen 44 | 45 | #endif // CENTURION_AUDIO_EVENTS_HPP_ 46 | -------------------------------------------------------------------------------- /source/wanderer/systems/tile_system.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "wanderer/fwd.hpp" 6 | 7 | namespace wanderer::sys { 8 | 9 | /** 10 | * \ingroup systems 11 | * \defgroup tile-system Tile system 12 | * 13 | * \brief Handles aspects related to tilemap tiles and tile objects. 14 | */ 15 | 16 | /// \addtogroup tile-system 17 | /// \{ 18 | 19 | /** 20 | * \brief Clears the frame-by-frame cache for the effective appearance of tiles. 21 | * 22 | * \param registry the level registry. 23 | */ 24 | void clear_effective_appearance_tile_cache(entt::registry& registry); 25 | 26 | /** 27 | * \brief Updates the state of tile objects, such as syncing their effective appearance. 28 | * 29 | * \param registry the level registry. 30 | */ 31 | void update_tile_objects(entt::registry& registry); 32 | 33 | /** 34 | * \brief Renders all tile layers. 35 | * 36 | * \details This function iterates all tile layers in a registry and renders all 37 | * visible tiles according to the predetermined render bounds. 38 | * 39 | * \param registry the current level registry. 40 | * \param graphics the graphics context that will be used. 41 | */ 42 | void render_tiles(const entt::registry& registry, Graphics& graphics); 43 | 44 | /// \} End of group tile-system 45 | 46 | } // namespace wanderer::sys 47 | -------------------------------------------------------------------------------- /lib/centurion/centurion/input.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CENTURION_INPUT_HPP_ 2 | #define CENTURION_INPUT_HPP_ 3 | 4 | #include 5 | 6 | #include // ostream 7 | #include // string_view 8 | 9 | #include "common.hpp" 10 | 11 | namespace cen { 12 | 13 | /** 14 | * \defgroup input Input 15 | * 16 | * \brief Provides APIs related to different kinds of input. 17 | */ 18 | 19 | /// \addtogroup input 20 | /// \{ 21 | 22 | enum class button_state : uint8 23 | { 24 | released = SDL_RELEASED, ///< Corresponds to `SDL_RELEASED`. 25 | pressed = SDL_PRESSED ///< Corresponds to `SDL_PRESSED`. 26 | }; 27 | 28 | /// \name Button state functions 29 | /// \{ 30 | 31 | [[nodiscard]] constexpr auto to_string(const button_state state) -> std::string_view 32 | { 33 | switch (state) { 34 | case button_state::released: 35 | return "released"; 36 | 37 | case button_state::pressed: 38 | return "pressed"; 39 | 40 | default: 41 | throw exception{"Did not recognize button state!"}; 42 | } 43 | } 44 | 45 | inline auto operator<<(std::ostream& stream, const button_state state) -> std::ostream& 46 | { 47 | return stream << to_string(state); 48 | } 49 | 50 | /// \} End of button state functions 51 | 52 | /// \} End of group input 53 | 54 | } // namespace cen 55 | 56 | #endif // CENTURION_INPUT_HPP_ -------------------------------------------------------------------------------- /resources/maps/old/objecttypes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /source/wanderer/core/centurion_utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace wanderer { 7 | 8 | [[nodiscard]] constexpr auto as_point(const glm::vec2& vec) noexcept -> cen::fpoint 9 | { 10 | return {vec.x, vec.y}; 11 | } 12 | 13 | [[nodiscard]] constexpr auto as_point(const glm::ivec2& vec) noexcept -> cen::ipoint 14 | { 15 | return {vec.x, vec.y}; 16 | } 17 | 18 | [[nodiscard]] constexpr auto as_area(const glm::vec2& vec) noexcept -> cen::farea 19 | { 20 | return {vec.x, vec.y}; 21 | } 22 | 23 | [[nodiscard]] constexpr auto as_area(const glm::ivec2& vec) noexcept -> cen::iarea 24 | { 25 | return {vec.x, vec.y}; 26 | } 27 | 28 | [[nodiscard]] constexpr auto as_rect(const glm::vec4& vec) noexcept -> cen::frect 29 | { 30 | return {vec.x, vec.y, vec.z, vec.w}; 31 | } 32 | 33 | [[nodiscard]] constexpr auto as_rect(const glm::ivec4& vec) noexcept -> cen::irect 34 | { 35 | return {vec.x, vec.y, vec.z, vec.w}; 36 | } 37 | 38 | [[nodiscard]] constexpr auto as_rect(const glm::vec2& pos, const glm::vec2& size) noexcept 39 | -> cen::frect 40 | { 41 | return {pos.x, pos.y, size.x, size.y}; 42 | } 43 | 44 | [[nodiscard]] constexpr auto as_rect(const glm::ivec2& pos, 45 | const glm::ivec2& size) noexcept -> cen::irect 46 | { 47 | return {pos.x, pos.y, size.x, size.y}; 48 | } 49 | 50 | } // namespace wanderer 51 | -------------------------------------------------------------------------------- /source/wanderer/systems/registry_system.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "wanderer/fwd.hpp" 6 | 7 | namespace wanderer::sys { 8 | 9 | /** 10 | * \ingroup systems 11 | * \defgroup registry-system Registry system 12 | * 13 | * \brief Minor system responsible for creation of all registries. 14 | */ 15 | 16 | /// \addtogroup registry-system 17 | /// \{ 18 | 19 | /** 20 | * \brief Creates a main registry, used as the "top-level" registry for the game. 21 | * 22 | * \details The main registry has the following context components: 23 | * - `game_cfg` 24 | * - `comp::level_ctx` 25 | * - `comp::ui_menu_ctx` 26 | * 27 | * \param cfg the immutable game configuration added as a context component. 28 | * 29 | * \return a registry. 30 | */ 31 | [[nodiscard]] auto make_main_registry(const GameConfig& cfg) -> entt::registry; 32 | 33 | /** 34 | * \brief Creates a registry for use by individual levels. 35 | * 36 | * \details A level registry has the following context components: 37 | * - `game_cfg` 38 | * - `comp::tilemap` 39 | * - `comp::tilesets` 40 | * - `comp::render_bounds` 41 | * - `comp::viewport` 42 | * - `comp::physics_world` 43 | * 44 | * \param cfg the immutable game configuration added as a context component. 45 | * 46 | * \return a registry. 47 | */ 48 | [[nodiscard]] auto make_level_registry(const GameConfig& cfg) -> entt::registry; 49 | 50 | /// \} End of group registry-system 51 | 52 | } // namespace wanderer::sys 53 | -------------------------------------------------------------------------------- /source/wanderer/misc/logging.cpp: -------------------------------------------------------------------------------- 1 | #include "logging.hpp" 2 | 3 | #include // time 4 | 5 | #include 6 | 7 | namespace wanderer::logging { 8 | namespace { 9 | 10 | void log_impl([[maybe_unused]] const fmt::color color, 11 | const std::string_view category, 12 | const std::string_view fmt, 13 | const fmt::format_args args) 14 | { 15 | const auto msg = fmt::vformat(fmt, args); 16 | 17 | const auto time = fmt::localtime(std::time(nullptr)); 18 | const auto full = fmt::vformat("{:>9} {:%H:%M:%S} > {}\n", 19 | fmt::make_format_args(category, time, msg)); 20 | 21 | if constexpr (on_windows) { 22 | fmt::print("{}", full); 23 | } 24 | else { 25 | fmt::print(fmt::fg(color), "{}", full); 26 | } 27 | } 28 | 29 | } // namespace 30 | 31 | void log_debug(const std::string_view fmt, const fmt::format_args args) 32 | { 33 | log_impl(fmt::color::aquamarine, "[debug]", fmt, args); 34 | } 35 | 36 | void log_info(const std::string_view fmt, const fmt::format_args args) 37 | { 38 | log_impl(fmt::color::floral_white, "[info]", fmt, args); 39 | } 40 | 41 | void log_warn(const std::string_view fmt, const fmt::format_args args) 42 | { 43 | log_impl(fmt::color::yellow, "[warn]", fmt, args); 44 | } 45 | 46 | void log_error(const std::string_view fmt, const fmt::format_args args) 47 | { 48 | log_impl(fmt::color::orange_red, "[error]", fmt, args); 49 | } 50 | 51 | } // namespace wanderer::logging -------------------------------------------------------------------------------- /doxygen/custom.css: -------------------------------------------------------------------------------- 1 | :root 2 | { 3 | --code-foreground: #F97583; 4 | 5 | --fragment-background: #151B25; 6 | --fragment-foreground: #FFFFFF; 7 | --fragment-keyword: #FF7B72; /* #C792EA */ 8 | --fragment-keywordtype: var(--fragment-keyword); 9 | --fragment-keywordflow: var(--fragment-keyword); 10 | --fragment-token: #A5FFC9; 11 | --fragment-comment: #959DA5; 12 | --fragment-link: #79C0FF; 13 | --fragment-preprocessor: #FF7B72; 14 | --fragment-linenumber-background: #151B25; 15 | } 16 | 17 | /* light-mode variable overrides go here */ 18 | html { 19 | } 20 | 21 | /* dark-mode variable overrides go here */ 22 | html.dark-mode { 23 | --page-background-color: #0D1117; 24 | --page-foreground-color: rgb(255, 255, 255); 25 | 26 | --odd-color: #141A24; 27 | 28 | --side-nav-background: var(--page-background-color); 29 | --code-background: var(--page-background-color); 30 | 31 | --primary-color: #3194E0; 32 | --primary-dark-color: #2C82C4; 33 | --primary-light-color: #3BA9FD; 34 | --primary-lighter-color: #191E21; 35 | --primary-lightest-color: rgb(11, 12, 12); 36 | 37 | --note-color: rgba(55, 49, 10, 0.5); 38 | --note-color-dark: rgb(255, 246, 86); 39 | --note-color-darker: rgba(255, 246, 86, 0.9); 40 | 41 | --warning-color: rgba(171, 0, 0, 0.5); 42 | --warning-color-dark: rgb(232, 43, 43); 43 | --warning-color-darker: rgb(202, 43, 43); 44 | } 45 | 46 | .directory .levels span { 47 | color: gray; 48 | } 49 | 50 | address { 51 | color: gray; 52 | } 53 | -------------------------------------------------------------------------------- /source/wanderer/systems/animation_system.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "wanderer/common.hpp" 6 | 7 | namespace wanderer::sys { 8 | 9 | /** 10 | * \ingroup systems 11 | * \defgroup animation-system 12 | * 13 | * \brief Manages animation aspects. 14 | */ 15 | 16 | /// \addtogroup animation-system 17 | /// \{ 18 | 19 | /** 20 | * \brief Updates the state of all animations. 21 | * 22 | * \param registry the current level registry. 23 | */ 24 | void update_animations(entt::registry& registry); 25 | 26 | /** 27 | * \brief Makes a humanoid enter its idle animation. 28 | * 29 | * \param registry a level registry. 30 | * \param humanoidEntity the associated humanoid entity. 31 | * \param directionMask the current direction mask of the humanoid. 32 | */ 33 | void enter_humanoid_idle_animation(entt::registry& registry, 34 | entt::entity humanoidEntity, 35 | uint32 directionMask); 36 | 37 | /** 38 | * \brief Makes a humanoid enter its walk animation. 39 | * 40 | * \param registry a level registry. 41 | * \param humanoidEntity the associated humanoid entity. 42 | * \param directionMask the current direction mask of the humanoid. 43 | */ 44 | void enter_humanoid_walk_animation(entt::registry& registry, 45 | entt::entity humanoidEntity, 46 | uint32 directionMask); 47 | 48 | /// \} End of group animation-system 49 | 50 | } // namespace wanderer::sys -------------------------------------------------------------------------------- /source/wanderer/io/settings.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "wanderer/common.hpp" 4 | 5 | namespace wanderer { 6 | 7 | /** 8 | * \brief Represents persistent game settings. 9 | */ 10 | struct Settings final 11 | { 12 | inline static constexpr uint64 fullscreen_bit = 1u << 0u; 13 | inline static constexpr uint64 vsync_bit = 1u << 1u; 14 | inline static constexpr uint64 integer_scaling_bit = 1u << 2u; 15 | 16 | /** 17 | * \brief Bit mask of all boolean settings. 18 | */ 19 | uint64 flags{}; 20 | 21 | /** 22 | * \brief Sets the value of a boolean setting. 23 | * 24 | * \param flag the boolean setting bit. 25 | * \param value the new value of the setting. 26 | */ 27 | void set_flag(uint64 flag, bool value) noexcept; 28 | 29 | /** 30 | * \brief Returns the value of a boolean setting. 31 | * 32 | * \param flag the boolean setting flag to query. 33 | * 34 | * \return `true` if the setting is enabled; `false` otherwise. 35 | */ 36 | [[nodiscard]] auto test_flag(uint64 flag) const noexcept -> bool; 37 | }; 38 | 39 | /** 40 | * \brief Loads settings from the persistent file directory. 41 | * 42 | * \details The default settings are returned if no settings file is found. 43 | * 44 | * \return the loaded settings. 45 | */ 46 | [[nodiscard]] auto load_settings() -> Settings; 47 | 48 | /** 49 | * \brief Saves settings to the persistent file directory. 50 | * 51 | * \param s the settings that will be saved. 52 | */ 53 | void save_settings(const Settings& s); 54 | 55 | } // namespace wanderer 56 | -------------------------------------------------------------------------------- /docs/troubleshooting.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting 2 | 3 | This document provides instructions for dealing with possible issues that might arise when trying to run Wanderer. 4 | 5 | ## There are black margins around my game viewport 6 | 7 | ### Option 1 (Windows) 8 | 9 | If you're using a 16:9 monitor then this is likely solved by overriding the DPI-scaling settings for the executable in 10 | Windows. 11 | 12 | 1. Navigate to the Wanderer executable. 13 | 1. Right-click the executable, select the `Properties` item. 14 | 1. Select the `Compatibility` tab. 15 | 1. In the `Settings` group, press the `Change high DPI settings` button. 16 | 1. In the `High DPI scaling override` group, enable the `Override high DPI scaling behaviour` checkbox. 17 | 1. Select the `Application` item in the dropdown. 18 | 1. That's it! The scaling should now be fixed the next time you Run the game. 19 | 20 | ### Option 2 21 | 22 | If your monitor uses an uncommon resolution, the issue might be caused by the fact that the ratio between the logical 23 | viewport size and the monitor size is not integral. This can be solved by disabling "Integer scaling" in the options. 24 | However, this can introduce minor rendering artifacts. 25 | 26 | ### Option 3 27 | 28 | An alternative to Option 2 is to override the logical viewport size manually. It is recommended to use a logical 29 | viewport size with a width around 1000, and it should be an integral scale factor of the screen size. 30 | 31 | ```bash 32 | ./Wanderer -ls 960 600 # Terse syntax 33 | ./Wanderer --logical-size 960 600 # Verbose syntax 34 | ``` 35 | -------------------------------------------------------------------------------- /source/wanderer/core/random.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // mt19937, uniform_real_distribution, uniform_int_distribution 4 | #include // is_floating_point_v 5 | 6 | #include "wanderer/common.hpp" 7 | 8 | namespace wanderer { 9 | 10 | using random_engine = std::mt19937; 11 | 12 | /** 13 | * \brief Creates a seeded pseudo-random number generation engine. 14 | * 15 | * \return a seeded random engine. 16 | */ 17 | [[nodiscard]] auto make_random_engine() -> random_engine; 18 | 19 | /** 20 | * \brief Returns a random value in the range [min, max]. 21 | * 22 | * \param min the minimum possible value. 23 | * \param max the maximum possible value. 24 | * 25 | * \return a random value in the range. 26 | */ 27 | template 28 | [[nodiscard]] auto next_random(const T min, const T max) -> T 29 | { 30 | static auto engine = make_random_engine(); 31 | 32 | if constexpr (std::is_floating_point_v) { 33 | return std::uniform_real_distribution{min, max}(engine); 34 | } 35 | else { 36 | return std::uniform_int_distribution{min, max}(engine); 37 | } 38 | } 39 | 40 | /** 41 | * \brief Returns a random boolean value. 42 | * 43 | * \return a random boolean value. 44 | */ 45 | [[nodiscard]] inline auto next_bool() -> bool 46 | { 47 | return next_random(0, 100) <= 50; 48 | } 49 | 50 | /** 51 | * \brief Returns a random float in the interval [0, 1]. 52 | * 53 | * \return a random normalized float. 54 | */ 55 | [[nodiscard]] inline auto next_float() -> float32 56 | { 57 | return next_random(0.0f, 1.0f); 58 | } 59 | 60 | } // namespace wanderer -------------------------------------------------------------------------------- /source/wanderer/systems/cinematic_system.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "wanderer/common.hpp" 6 | #include "wanderer/fwd.hpp" 7 | 8 | namespace wanderer::sys { 9 | 10 | /** 11 | * \ingroup systems 12 | * \defgroup cinematic-system Cinematic system 13 | * 14 | * \brief Manages blocking cinematic transitions intended to display basic information. 15 | * 16 | * \todo Allow scheduling multiple consecutive cinematics. 17 | */ 18 | 19 | /// \addtogroup cinematic-system 20 | /// \{ 21 | 22 | /** 23 | * \brief Schedules the "Wanderer" title cinematic fade, intended to shown at startup. 24 | * 25 | * \param registry the source registry. 26 | */ 27 | void schedule_startup_cinematic_fade(entt::registry& registry); 28 | 29 | /** 30 | * \brief Updates the state of the current cinematic fade, if there is one. 31 | * 32 | * \param registry the source registry. 33 | */ 34 | void update_cinematic_fade(entt::registry& registry); 35 | 36 | /** 37 | * \brief Renders any current cinematic fade, if there is one. 38 | * 39 | * \param registry the source registry. 40 | * \param graphics the current graphics context. 41 | */ 42 | void render_cinematic_fade(const entt::registry& registry, Graphics& graphics); 43 | 44 | /** 45 | * \brief Indicates whether there is an active cinematic transition. 46 | * 47 | * \param registry the source registry. 48 | * 49 | * \return `true` if a cinematic fade is active; `false` otherwise. 50 | */ 51 | [[nodiscard]] auto is_cinematic_fade_active(const entt::registry& registry) -> bool; 52 | 53 | /// \} End of group cinematic-system 54 | 55 | } // namespace wanderer::sys 56 | -------------------------------------------------------------------------------- /source/wanderer/misc/logging.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // string_view 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "fmt_specializations.hpp" 10 | #include "wanderer/meta/build.hpp" 11 | 12 | namespace wanderer { 13 | namespace logging { 14 | 15 | void log_debug(std::string_view fmt, fmt::format_args args); 16 | void log_info(std::string_view fmt, fmt::format_args args); 17 | void log_warn(std::string_view fmt, fmt::format_args args); 18 | void log_error(std::string_view fmt, fmt::format_args args); 19 | 20 | } // namespace logging 21 | 22 | template 23 | void print([[maybe_unused]] const fmt::color color, 24 | const std::string_view fmt, 25 | const Args&... args) 26 | { 27 | if constexpr (is_windows) { 28 | fmt::print(fmt::runtime(fmt), args...); 29 | } 30 | else { 31 | fmt::print(fmt::fg(color), fmt, args...); 32 | } 33 | } 34 | 35 | template 36 | void debug(const std::string_view fmt, const Args&... args) 37 | { 38 | if constexpr (is_debug_build) { 39 | logging::log_debug(fmt, fmt::make_format_args(args...)); 40 | } 41 | } 42 | 43 | template 44 | void info(const std::string_view fmt, const Args&... args) 45 | { 46 | logging::log_info(fmt, fmt::make_format_args(args...)); 47 | } 48 | 49 | template 50 | void warn(const std::string_view fmt, const Args&... args) 51 | { 52 | logging::log_warn(fmt, fmt::make_format_args(args...)); 53 | } 54 | 55 | template 56 | void error(const std::string_view fmt, const Args&... args) 57 | { 58 | logging::log_error(fmt, fmt::make_format_args(args...)); 59 | } 60 | 61 | } // namespace wanderer 62 | -------------------------------------------------------------------------------- /source/wanderer/core/math.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "wanderer/common.hpp" 7 | 8 | namespace wanderer::glmx { 9 | 10 | /** 11 | * \brief Indicates whether a vector is the zero vector. 12 | * 13 | * \param vec the vector to check. 14 | * 15 | * \return `true` if both components are zero; `false` otherwise. 16 | */ 17 | [[nodiscard]] constexpr auto is_zero(const glm::vec2& vec) noexcept -> bool 18 | { 19 | return vec.x == 0 && vec.y == 0; 20 | } 21 | 22 | /** 23 | * \brief Ensures that the magnitude of a vector does not exceed a threshold. 24 | * 25 | * \details This function normalizes the vector and scales it by the specified magnitude, 26 | * to maintain the direction of the vector whilst enforcing the magnitude limit. The 27 | * function does nothing if the supplied vector is the zero vector. 28 | * 29 | * \param vec the vector that will be modified. 30 | * \param magnitude the maximum allowed magnitude. 31 | */ 32 | inline void cap_magnitude(glm::vec2& vec, const float32 magnitude) 33 | { 34 | if (!is_zero(vec)) { 35 | vec = glm::normalize(vec) * magnitude; 36 | } 37 | } 38 | 39 | [[nodiscard]] constexpr auto as_i(const glm::vec2& vec) noexcept -> glm::ivec2 40 | { 41 | return {static_cast(vec.x), static_cast(vec.y)}; 42 | } 43 | 44 | [[nodiscard]] constexpr auto as_f(const glm::ivec2& vec) noexcept -> glm::vec2 45 | { 46 | return {static_cast(vec.x), static_cast(vec.y)}; 47 | } 48 | 49 | [[nodiscard]] inline auto from_b2(const b2Vec2& vec) noexcept -> glm::vec2 50 | { 51 | return {vec.x, vec.y}; 52 | } 53 | 54 | [[nodiscard]] inline auto as_b2(const glm::vec2& vec) noexcept -> b2Vec2 55 | { 56 | return {vec.x, vec.y}; 57 | } 58 | 59 | } // namespace wanderer::glmx 60 | -------------------------------------------------------------------------------- /source/wanderer/systems/registry_system.cpp: -------------------------------------------------------------------------------- 1 | #include "registry_system.hpp" 2 | 3 | #include "physics_system.hpp" 4 | #include "ui_system.hpp" 5 | #include "wanderer/data/cfg.hpp" 6 | #include "wanderer/data/components/levels.hpp" 7 | #include "wanderer/data/components/rendering.hpp" 8 | #include "wanderer/data/components/tiles.hpp" 9 | #include "wanderer/data/components/world.hpp" 10 | 11 | namespace wanderer::sys { 12 | 13 | auto make_main_registry(const GameConfig& cfg) -> entt::registry 14 | { 15 | entt::registry registry; 16 | 17 | registry.ctx().emplace(cfg); 18 | registry.ctx().emplace(); 19 | 20 | load_menus(registry); 21 | 22 | return registry; 23 | } 24 | 25 | auto make_level_registry(const GameConfig& cfg) -> entt::registry 26 | { 27 | entt::registry registry; 28 | 29 | registry.ctx().emplace(cfg); 30 | registry.ctx().emplace(); 31 | registry.ctx().emplace(); 32 | registry.ctx().emplace(); 33 | 34 | // TODO remember to sync this when switching active levels 35 | auto& date = registry.ctx().emplace(); 36 | date.day = DayOfWeek::monday; 37 | date.hour = 0; 38 | date.minute = 0; 39 | date.seconds = 14.0f * 3'600.0f; 40 | date.tint = cen::colors::transparent; 41 | 42 | auto& viewport = registry.ctx().emplace(); 43 | viewport.size = cfg.logical_size_f; 44 | 45 | registry.on_destroy().connect<&sys::on_destroy_physics_object>(); 46 | 47 | auto& world = registry.ctx().emplace(); 48 | 49 | /* Assume that a tile is 1x1 meters */ 50 | world.scale.x = 1.0f / cfg.tile_size.x; 51 | world.scale.y = 1.0f / cfg.tile_size.y; 52 | 53 | return registry; 54 | } 55 | 56 | } // namespace wanderer::sys -------------------------------------------------------------------------------- /test/unit-tests/misc/fmt_specializations_test.cpp: -------------------------------------------------------------------------------- 1 | #include "wanderer/misc/fmt_specializations.hpp" 2 | 3 | #include 4 | 5 | TEST_SUITE("fmt::formatter specializations") 6 | { 7 | TEST_CASE("cen::point") 8 | { 9 | const cen::fpoint fp{48.3f, 19.2f}; 10 | const cen::ipoint ip{787, 903}; 11 | 12 | CHECK("(48.3, 19.2)" == fmt::format("{}", fp)); 13 | CHECK("(787, 903)" == fmt::format("{}", ip)); 14 | } 15 | 16 | TEST_CASE("cen::area") 17 | { 18 | const cen::farea fa{258.6f, 193.2f}; 19 | const cen::iarea ia{1543, 2956}; 20 | 21 | CHECK("(258.6, 193.2)" == fmt::format("{}", fa)); 22 | CHECK("(1543, 2956)" == fmt::format("{}", ia)); 23 | } 24 | 25 | TEST_CASE("cen::rect") 26 | { 27 | const cen::frect frect{78.9f, 32.8f, 743.3f, 341.1f}; 28 | const cen::irect irect{98, 134, 84, 42}; 29 | 30 | CHECK("(78.9, 32.8, 743.3, 341.1)" == fmt::format("{}", frect)); 31 | CHECK("(98, 134, 84, 42)" == fmt::format("{}", irect)); 32 | } 33 | 34 | TEST_CASE("glm::vec2") 35 | { 36 | const glm::fvec2 fvec{83.2f, 93.5f}; 37 | const glm::ivec2 ivec{42, -93}; 38 | 39 | CHECK("(83.2, 93.5)" == fmt::format("{}", fvec)); 40 | CHECK("(42, -93)" == fmt::format("{}", ivec)); 41 | } 42 | 43 | TEST_CASE("glm::vec3") 44 | { 45 | const glm::fvec3 fvec{849.0f, -245.7f, 563.3f}; 46 | const glm::ivec3 ivec{-342, 59, 123}; 47 | 48 | CHECK("(849, -245.7, 563.3)" == fmt::format("{}", fvec)); 49 | CHECK("(-342, 59, 123)" == fmt::format("{}", ivec)); 50 | } 51 | 52 | TEST_CASE("glm::vec4") 53 | { 54 | const glm::fvec4 fvec{78.5f, 76.8f, 31.1f, 44.4f}; 55 | const glm::ivec4 ivec{9, 51, 3821, 832}; 56 | 57 | CHECK("(78.5, 76.8, 31.1, 44.4)" == fmt::format("{}", fvec)); 58 | CHECK("(9, 51, 3821, 832)" == fmt::format("{}", ivec)); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /source/wanderer/meta/build.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef NDEBUG 4 | 5 | #define WANDERER_DEBUG_BUILD 0 6 | #define WANDERER_RELEASE_BUILD 1 7 | 8 | #else 9 | 10 | #define WANDERER_DEBUG_BUILD 1 11 | #define WANDERER_RELEASE_BUILD 0 12 | 13 | #endif // NDEBUG 14 | 15 | #ifdef WIN32 16 | #define WANDERER_PLATFORM_WINDOWS 1 17 | #else 18 | #define WANDERER_PLATFORM_WINDOWS 0 19 | #endif // WIN32 20 | 21 | #ifdef __APPLE__ 22 | #define WANDERER_PLATFORM_OSX 1 23 | #else 24 | #define WANDERER_PLATFORM_OSX 0 25 | #endif // __APPLE__ 26 | 27 | #ifdef _MSC_VER 28 | #define WANDERER_COMPILER_MSVC 1 29 | #else 30 | #define WANDERER_COMPILER_MSVC 0 31 | #endif // _MSC_VER 32 | 33 | #ifdef __GNUC__ 34 | #define WANDERER_COMPILER_GCC 1 35 | #else 36 | #define WANDERER_COMPILER_GCC 0 37 | #endif // __GNUC__ 38 | 39 | #ifdef __clang__ 40 | #define WANDERER_COMPILER_CLANG 1 41 | #else 42 | #define WANDERER_COMPILER_CLANG 0 43 | #endif // __clang__ 44 | 45 | namespace wanderer { 46 | 47 | inline constexpr auto wanderer_version = "0.1.0"; 48 | 49 | #if WANDERER_DEBUG_BUILD 50 | inline constexpr bool is_debug_build = true; 51 | inline constexpr bool is_release_build = !is_debug_build; 52 | #else 53 | inline constexpr bool is_debug_build = false; 54 | inline constexpr bool is_release_build = !is_debug_build; 55 | #endif // WANDERER_DEBUG_BUILD 56 | 57 | #if WANDERER_PLATFORM_WINDOWS 58 | inline constexpr bool is_windows = true; 59 | inline constexpr bool on_windows = true; 60 | #else 61 | inline constexpr bool is_windows = false; 62 | inline constexpr bool on_windows = false; 63 | #endif // WANDERER_PLATFORM_WINDOWS 64 | 65 | #ifdef WANDERER_PLATFORM_OSX 66 | inline constexpr bool is_osx = true; 67 | inline constexpr bool on_osx = true; 68 | #else 69 | inline constexpr bool is_osx = false; 70 | inline constexpr bool on_osx = false; 71 | #endif // WANDERER_PLATFORM_OSX 72 | 73 | } // namespace wanderer -------------------------------------------------------------------------------- /test/unit-tests/core/centurion_utils_test.cpp: -------------------------------------------------------------------------------- 1 | #include "wanderer/core/centurion_utils.hpp" 2 | 3 | #include 4 | 5 | using namespace wanderer; 6 | 7 | TEST_SUITE("Centurion utilities") 8 | { 9 | TEST_CASE("as_point[glm::vec2]") 10 | { 11 | const glm::vec2 vec{42.5f, 95.2f}; 12 | const auto point = as_point(vec); 13 | CHECK(vec.x == point.x()); 14 | CHECK(vec.y == point.y()); 15 | } 16 | 17 | TEST_CASE("as_point[glm::ivec2]") 18 | { 19 | const glm::ivec2 vec{832, -392}; 20 | const auto point = as_point(vec); 21 | CHECK(vec.x == point.x()); 22 | CHECK(vec.y == point.y()); 23 | } 24 | 25 | TEST_CASE("as_rect[glm::vec4]") 26 | { 27 | const glm::vec4 vec{54, 393, 422, 284}; 28 | const auto rect = as_rect(vec); 29 | CHECK(vec.x == rect.x()); 30 | CHECK(vec.y == rect.y()); 31 | CHECK(vec.z == rect.width()); 32 | CHECK(vec.w == rect.height()); 33 | } 34 | 35 | TEST_CASE("as_rect[glm::ivec4]") 36 | { 37 | const glm::ivec4 vec{-82, 730, 41, 9384}; 38 | const auto rect = as_rect(vec); 39 | CHECK(vec.x == rect.x()); 40 | CHECK(vec.y == rect.y()); 41 | CHECK(vec.z == rect.width()); 42 | CHECK(vec.w == rect.height()); 43 | } 44 | 45 | TEST_CASE("as_rect[glm::vec2, glm::vec2]") 46 | { 47 | const glm::vec2 pos{6374, 769}; 48 | const glm::vec2 size{387, 92}; 49 | const auto rect = as_rect(pos, size); 50 | CHECK(pos.x == rect.x()); 51 | CHECK(pos.y == rect.y()); 52 | CHECK(size.x == rect.width()); 53 | CHECK(size.y == rect.height()); 54 | } 55 | 56 | TEST_CASE("as_rect[glm::ivec2, glm::ivec2]") 57 | { 58 | const glm::ivec2 pos{7, -132}; 59 | const glm::ivec2 size{724, 8473}; 60 | const auto rect = as_rect(pos, size); 61 | CHECK(pos.x == rect.x()); 62 | CHECK(pos.y == rect.y()); 63 | CHECK(size.x == rect.width()); 64 | CHECK(size.y == rect.height()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /source/wanderer/systems/rendering_system.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "wanderer/common.hpp" 6 | #include "wanderer/fwd.hpp" 7 | 8 | namespace wanderer::sys { 9 | 10 | /** 11 | * \ingroup systems 12 | * \defgroup rendering-system 13 | * 14 | * \brief Covers the main rendering logic. 15 | * 16 | * \todo Merge viewport system into this system? 17 | */ 18 | 19 | /// \addtogroup rendering-system 20 | /// \{ 21 | 22 | /** 23 | * \brief Represents different approaches to sorting. 24 | */ 25 | enum class SortStrategy 26 | { 27 | std, ///< Use std::sort, which works well with large sets of unsorted data. 28 | insertion ///< Use insertion sort, works well when the data is almost sorted. 29 | }; 30 | 31 | /** 32 | * \brief Sorts all drawable entities. 33 | * 34 | * \param registry the level registry. 35 | * \param strategy the strategy used for sorting. 36 | */ 37 | void sort_drawables(entt::registry& registry, SortStrategy strategy); 38 | 39 | /** 40 | * \brief Updates the state of all lights. 41 | * 42 | * \param registry the level registry. 43 | */ 44 | void update_lights(entt::registry& registry); 45 | 46 | /** 47 | * \brief Renders all visible drawable entities. 48 | * 49 | * \details Remember to call `sort_drawables()` before this function. 50 | * 51 | * \param registry the level registry. 52 | * \param graphics the current graphics context. 53 | */ 54 | void render_drawables(const entt::registry& registry, Graphics& graphics); 55 | 56 | /** 57 | * \brief Renders all point lights. 58 | * 59 | * \param registry the level registry. 60 | * \param graphics the current graphics context. 61 | * 62 | * \todo Consider excluding drawable entities here, and drawing lights on drawables in the 63 | * correct order in render_drawables. 64 | */ 65 | void render_lights(const entt::registry& registry, Graphics& graphics); 66 | 67 | /// \} End of group rendering-system 68 | 69 | } // namespace wanderer::sys 70 | -------------------------------------------------------------------------------- /lib/centurion/centurion/features.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CENTURION_FEATURES_HPP_ 2 | #define CENTURION_FEATURES_HPP_ 3 | 4 | /// Do we have general C++20 support? 5 | #if __cplusplus >= 202002L 6 | #define CENTURION_HAS_FEATURE_CPP20 1 7 | #else 8 | #define CENTURION_HAS_FEATURE_CPP20 0 9 | #endif // __cplusplus >= 202002L 10 | 11 | /// C++20 nodiscard constructors 12 | #if nodiscard >= 201907L 13 | #define CENTURION_NODISCARD_CTOR [[nodiscard]] 14 | #else 15 | #define CENTURION_NODISCARD_CTOR 16 | #endif // nodiscard >= 201907L 17 | 18 | #ifdef __has_include 19 | 20 | #if __has_include() 21 | #include 22 | #endif // __has_include() 23 | 24 | #ifdef __cpp_lib_format 25 | #define CENTURION_HAS_FEATURE_FORMAT 1 26 | #else 27 | #define CENTURION_HAS_FEATURE_FORMAT 0 28 | #endif // __cpp_lib_format 29 | 30 | #ifdef __cpp_lib_concepts 31 | #define CENTURION_HAS_FEATURE_CONCEPTS 1 32 | #else 33 | #define CENTURION_HAS_FEATURE_CONCEPTS 0 34 | #endif // __cpp_lib_concepts 35 | 36 | #ifdef __cpp_lib_interpolate 37 | #define CENTURION_HAS_FEATURE_LERP 1 38 | #else 39 | #define CENTURION_HAS_FEATURE_LERP 0 40 | #endif // __cpp_lib_interpolate 41 | 42 | #ifdef __cpp_lib_three_way_comparison 43 | #define CENTURION_HAS_FEATURE_SPACESHIP 1 44 | #else 45 | #define CENTURION_HAS_FEATURE_SPACESHIP 0 46 | #endif // __cpp_lib_three_way_comparison 47 | 48 | #if __cpp_lib_chrono >= 201907L 49 | #define CENTURION_HAS_FEATURE_CHRONO_TIME_ZONES 1 50 | #else 51 | #define CENTURION_HAS_FEATURE_CHRONO_TIME_ZONES 0 52 | #endif // __cpp_lib_chrono >= 201907L 53 | 54 | #if __cpp_lib_to_array >= 201907L 55 | #define CENTURION_HAS_FEATURE_TO_ARRAY 1 56 | #else 57 | #define CENTURION_HAS_FEATURE_TO_ARRAY 0 58 | #endif // __cpp_lib_to_array >= 201907L 59 | 60 | #if __cpp_lib_to_chars >= 201611L 61 | #define CENTURION_HAS_FEATURE_CHARCONV 1 62 | #else 63 | #define CENTURION_HAS_FEATURE_CHARCONV 0 64 | #endif // __cpp_lib_to_chars >= 201611L 65 | 66 | #endif // __has_include 67 | 68 | #endif // CENTURION_FEATURES_HPP_ 69 | -------------------------------------------------------------------------------- /source/wanderer/core/game_loop.cpp: -------------------------------------------------------------------------------- 1 | #include "game_loop.hpp" 2 | 3 | #include // min 4 | 5 | #include 6 | 7 | #include "wanderer/misc/logging.hpp" 8 | 9 | namespace wanderer { 10 | namespace { 11 | 12 | [[nodiscard]] auto make_loop_state() -> LoopState 13 | { 14 | const auto display = cen::display_mode::desktop(); 15 | 16 | LoopState state; 17 | 18 | state.rate = std::min(120.0, static_cast(display.refresh_rate().value())); 19 | state.fixed_dt = 1.0 / state.rate; 20 | 21 | state.frequency = static_cast(cen::frequency()); 22 | state.then = static_cast(cen::now()) / state.frequency; 23 | 24 | state.max_ticks_per_frame = 5; 25 | 26 | return state; 27 | } 28 | 29 | } // namespace 30 | 31 | GameLoop::GameLoop() : _state{make_loop_state()} 32 | { 33 | debug("Game loop refresh rate is '{}'", _state.rate); 34 | debug("Game loop fixed delta is '{}'", _state.fixed_dt); 35 | debug("Maximum amount of ticks per frame is '{}'", _state.max_ticks_per_frame); 36 | } 37 | 38 | void GameLoop::start() 39 | { 40 | const auto now = [this]() noexcept { 41 | return static_cast(cen::now()) / _state.frequency; 42 | }; 43 | 44 | _running = true; 45 | _state.then = now(); 46 | 47 | while (_running) { 48 | const auto newTime = now(); 49 | auto frameTime = newTime - _state.then; 50 | _state.then = newTime; 51 | 52 | int32 steps = 0; 53 | while (frameTime > 0) { 54 | /* Avoids spiral-of-death by limiting maximum amount of steps */ 55 | if (steps > _state.max_ticks_per_frame) { 56 | break; 57 | } 58 | 59 | process_events(); 60 | 61 | if (!_running) { 62 | break; 63 | } 64 | 65 | const auto dt = std::min(frameTime, _state.fixed_dt); 66 | update(static_cast(dt)); 67 | 68 | frameTime -= dt; 69 | ++steps; 70 | } 71 | 72 | render(); 73 | } 74 | } 75 | 76 | void GameLoop::stop() 77 | { 78 | _running = false; 79 | } 80 | 81 | } // namespace wanderer -------------------------------------------------------------------------------- /source/wanderer/common.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // size_t 4 | #include // int{}_t, uint{}_t 5 | #include // less 6 | #include // map 7 | #include // optional, nullopt_t, nullopt 8 | #include // unordered_map 9 | 10 | #include 11 | 12 | #define WANDERER_DEFAULT_COPY(Class) \ 13 | Class(const Class&) = default; \ 14 | Class& operator=(const Class&) = default; 15 | 16 | #define WANDERER_DELETE_COPY(Class) \ 17 | Class(const Class&) = delete; \ 18 | Class& operator=(const Class&) = delete; 19 | 20 | #define WANDERER_DEFAULT_MOVE(Class) \ 21 | Class(Class&&) noexcept = default; \ 22 | Class& operator=(Class&&) noexcept = default; 23 | 24 | #define WANDERER_DELETE_MOVE(Class) \ 25 | Class(Class&&) noexcept = delete; \ 26 | Class& operator=(Class&&) noexcept = delete; 27 | 28 | #define WANDERER_DEFAULT_SPECIAL_MEMBERS(Class) \ 29 | Class() = default; \ 30 | WANDERER_DEFAULT_COPY(Class) \ 31 | WANDERER_DEFAULT_MOVE(Class) 32 | 33 | namespace wanderer { 34 | 35 | using usize = std::size_t; 36 | 37 | using uchar = unsigned char; 38 | using uint = unsigned int; 39 | using ulonglong = unsigned long long int; 40 | 41 | using int8 = std::int8_t; 42 | using int16 = std::int16_t; 43 | using int32 = std::int32_t; 44 | using int64 = std::int64_t; 45 | 46 | using uint8 = std::uint8_t; 47 | using uint16 = std::uint16_t; 48 | using uint32 = std::uint32_t; 49 | using uint64 = std::uint64_t; 50 | 51 | using float32 = float; 52 | using float64 = double; 53 | 54 | static_assert(sizeof(float32) == 4); 55 | static_assert(sizeof(float64) == 8); 56 | 57 | template 58 | using tree_map = std::map>; 59 | 60 | template 61 | using hash_map = std::unordered_map; 62 | 63 | template 64 | using maybe = std::optional; 65 | 66 | inline constexpr std::nullopt_t nothing = std::nullopt; 67 | 68 | using texture_id = usize; 69 | using level_id = uint32; 70 | using tile_id = uint32; 71 | 72 | inline constexpr tile_id empty_tile = 0; 73 | 74 | } // namespace wanderer -------------------------------------------------------------------------------- /source/wanderer/data/components/world.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "wanderer/common.hpp" 8 | #include "wanderer/data/day.hpp" 9 | #include "wanderer/data/mob.hpp" 10 | 11 | namespace wanderer::comp { 12 | 13 | /// \addtogroup components 14 | /// \{ 15 | 16 | /** 17 | * \brief Component featured by all physical objects in the game world. 18 | */ 19 | struct GameObject final 20 | { 21 | glm::vec2 position{}; ///< The position in logical coordinates. 22 | glm::vec2 size{}; ///< The size of the object. 23 | }; 24 | 25 | /** 26 | * \brief Component featured by all entities that are a part of the physics simulation. 27 | */ 28 | struct PhysicsBody final 29 | { 30 | b2Body* data{}; ///< The simulation body data. 31 | b2Vec2 offset{}; ///< Body offset from the game object origin, in simulation scale. 32 | b2Vec2 size{}; ///< The size of the body, in simulation scale. 33 | float32 max_speed{}; ///< Maximum total speed, in simulation scale. 34 | }; 35 | 36 | /** 37 | * \brief Context component representing the physics simulation. 38 | */ 39 | struct PhysicsWorld final 40 | { 41 | /** 42 | * \brief The physics simulation world. 43 | */ 44 | b2World simulation{{0, 0}}; 45 | 46 | /** 47 | * \brief Scaling required to go from logical coordinates to simulation coordinates. 48 | */ 49 | glm::vec2 scale{}; 50 | }; 51 | 52 | /** 53 | * \brief Context component that provides information about the in-game date and time. 54 | */ 55 | struct DateAndTime final 56 | { 57 | float32 hour{}; ///< [0, 24) 58 | float32 minute{}; ///< [0, 60) 59 | float32 seconds{}; ///< Reset once per in-game day. 60 | int32 week{}; ///< The week index. 61 | DayOfWeek day{DayOfWeek::monday}; ///< The current day. 62 | cen::color tint; ///< Light tint. 63 | }; 64 | 65 | /** 66 | * \brief Represents a possible spawn point for a mob type in the world. 67 | */ 68 | struct SpawnPoint final 69 | { 70 | MobType mob{}; ///< The mob type that should be spawned. 71 | }; 72 | 73 | /// \} End of group components 74 | 75 | } // namespace wanderer::comp -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | 3 | Language: Cpp 4 | Standard: c++20 5 | 6 | ColumnLimit: 90 7 | IndentWidth: 2 8 | 9 | DerivePointerAlignment: false 10 | PointerAlignment: Left 11 | 12 | IndentRequires: true 13 | IndentCaseLabels: true 14 | 15 | IndentAccessModifiers: false 16 | AccessModifierOffset: -1 17 | 18 | NamespaceIndentation: None 19 | FixNamespaceComments: true 20 | 21 | EmptyLineBeforeAccessModifier: Always 22 | 23 | AllowAllArgumentsOnNextLine: false 24 | AllowAllParametersOfDeclarationOnNextLine: false 25 | AllowAllConstructorInitializersOnNextLine: false 26 | 27 | BinPackParameters: false 28 | BinPackArguments: false 29 | 30 | AllowShortFunctionsOnASingleLine: Inline 31 | AllowShortIfStatementsOnASingleLine: Never 32 | AllowShortBlocksOnASingleLine: Empty 33 | AllowShortLambdasOnASingleLine: All 34 | AllowShortEnumsOnASingleLine: false 35 | AllowShortLoopsOnASingleLine: false 36 | 37 | BreakBeforeBinaryOperators: None 38 | BreakBeforeTernaryOperators: true 39 | BreakBeforeConceptDeclarations: true 40 | 41 | BreakBeforeBraces: Custom 42 | BraceWrapping: 43 | AfterCaseLabel: false 44 | AfterClass: true 45 | AfterStruct: true 46 | AfterControlStatement: Never 47 | AfterEnum: false 48 | AfterFunction: true 49 | AfterNamespace: false 50 | AfterObjCDeclaration: false 51 | AfterUnion: false 52 | AfterExternBlock: false 53 | BeforeCatch: true 54 | BeforeElse: true 55 | BeforeLambdaBody: false 56 | BeforeWhile: false 57 | IndentBraces: false 58 | SplitEmptyFunction: false 59 | SplitEmptyRecord: false 60 | SplitEmptyNamespace: false 61 | 62 | BreakStringLiterals: true 63 | 64 | BreakInheritanceList: BeforeComma 65 | BreakConstructorInitializers: BeforeComma 66 | 67 | SortIncludes: CaseSensitive 68 | IncludeBlocks: Regroup 69 | IncludeCategories: 70 | # Standard headers, located in <> with no extension. 71 | - Regex: '<([A-Za-z0-9\_\-]+)>' 72 | SortPriority: 1 73 | Priority: 1 74 | 75 | # Headers in <> with extension and optional prefix. 76 | - Regex: '<([A-Za-z0-9\_\-]+\/)*(\.\.)*([A-Za-z0-9\_\-]+)\.([A-Za-z0-9\_\-]+)>' 77 | SortPriority: 2 78 | Priority: 2 79 | 80 | # Headers in "" with extension. 81 | - Regex: '"([A-Za-z0-9.\Q/-_\E])+"' 82 | SortPriority: 3 83 | Priority: 3 -------------------------------------------------------------------------------- /source/wanderer/data/components/tiles.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // vector 4 | 5 | #include 6 | 7 | #include "wanderer/common.hpp" 8 | 9 | namespace wanderer::comp { 10 | 11 | /// \addtogroup components 12 | /// \{ 13 | 14 | /** 15 | * \brief Represents a layer of static tiles. 16 | */ 17 | struct TileLayer final 18 | { 19 | using tile_row = std::vector; 20 | using tile_matrix = std::vector>; 21 | 22 | tile_matrix tiles; ///< The tile data. 23 | int32 z{}; ///< Rendering depth index. 24 | }; 25 | 26 | /** 27 | * \brief Component featured by tile objects, i.e. non-empty tiles in non-ground layers. 28 | */ 29 | struct TileObject final 30 | { 31 | entt::entity tile_entity{entt::null}; ///< The associated tile info entity. 32 | }; 33 | 34 | /** 35 | * \brief Provides information about a single tilemap, which consists of several layers. 36 | */ 37 | struct Tilemap final 38 | { 39 | int32 humanoid_layer_index{}; ///< The layer index inhabited by humanoids. 40 | usize row_count{}; ///< Total amount of tile rows. 41 | usize col_count{}; ///< Total amount of tile columns. 42 | glm::vec2 size{}; ///< Map size in pixels. 43 | }; 44 | 45 | /** 46 | * \brief Component describing the hitbox of a tile. 47 | */ 48 | struct TileHitbox final 49 | { 50 | glm::vec2 offset{}; ///< Offset from origin in the parent tile. 51 | glm::vec2 size{}; ///< The size of the hitbox. 52 | }; 53 | 54 | /** 55 | * \brief Provides information about a tile in a tileset. 56 | */ 57 | struct TileInfo final 58 | { 59 | texture_id texture{}; ///< Associated tileset texture. 60 | int32 depth_index{}; ///< The rendering depth index. 61 | glm::ivec4 source{}; ///< The region of the source tileset that represents the tile. 62 | }; 63 | 64 | /** 65 | * \brief Provides information about all available tiles in a level. 66 | */ 67 | struct Tileset final 68 | { 69 | hash_map tiles; 70 | 71 | /** 72 | * \brief A frame-by-frame cache that maps tile entities to rendered tile entities. 73 | */ 74 | mutable hash_map effective_appearance; 75 | }; 76 | 77 | /// \} End of group components 78 | 79 | } // namespace wanderer::comp -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | if (DEFINED ENV{VCPKG_ROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE) 4 | set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake") 5 | endif () 6 | 7 | project(wanderer 8 | VERSION 0.1.0 9 | LANGUAGES CXX 10 | HOMEPAGE_URL "https://github.com/albin-johansson/wanderer") 11 | 12 | set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") 13 | set(CMAKE_CXX_STANDARD 20) 14 | set(CMAKE_CXX_EXTENSIONS OFF) 15 | 16 | # Helper variables for directory paths 17 | set(WANDERER_ROOT_DIR "${PROJECT_SOURCE_DIR}") 18 | set(WANDERER_RESOURCE_DIR "${PROJECT_SOURCE_DIR}/resources") 19 | set(WANDERER_LIBRARY_DIR "${PROJECT_SOURCE_DIR}/lib") 20 | 21 | set(TARGET_WANDERER_PROTO WandererProto) 22 | set(TARGET_WANDERER_LIB WandererCore) 23 | set(TARGET_WANDERER_TEST WandererTests) 24 | set(TARGET_WANDERER_EXE Wanderer) 25 | 26 | include(Utilities) 27 | 28 | find_package(SDL2 CONFIG REQUIRED) 29 | find_package(sdl2-image CONFIG REQUIRED) 30 | find_package(SDL2_mixer CONFIG REQUIRED) 31 | find_package(SDL2_ttf CONFIG REQUIRED) 32 | find_package(Protobuf CONFIG REQUIRED) 33 | find_package(EnTT CONFIG REQUIRED) 34 | find_package(glm CONFIG REQUIRED) 35 | find_package(fmt CONFIG REQUIRED) 36 | find_package(magic_enum CONFIG REQUIRED) 37 | find_package(yaml-cpp CONFIG REQUIRED) 38 | find_package(nlohmann_json CONFIG REQUIRED) 39 | find_package(argparse CONFIG REQUIRED) 40 | find_package(box2d CONFIG REQUIRED) 41 | find_package(doctest CONFIG REQUIRED) 42 | 43 | find_package(Boost REQUIRED) 44 | 45 | function(wanderer_add_compiler_options target) 46 | if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") 47 | target_compile_options(${target} PRIVATE 48 | /EHsc 49 | /MP 50 | /WX 51 | /Zc:__cplusplus 52 | /Zc:preprocessor 53 | ) 54 | elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU|AppleClang") 55 | target_compile_options(${target} PRIVATE 56 | -Wall 57 | -Wextra 58 | -Wpedantic 59 | -Wconversion 60 | ) 61 | endif () 62 | endfunction() 63 | 64 | add_subdirectory(proto) 65 | add_subdirectory(source) 66 | add_subdirectory(test) -------------------------------------------------------------------------------- /lib/centurion/centurion/detail/owner_handle_api.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CENTURION_DETAIL_OWNER_HANDLE_API_HPP_ 2 | #define CENTURION_DETAIL_OWNER_HANDLE_API_HPP_ 3 | 4 | #include // assert 5 | #include // unique_ptr 6 | #include // enable_if_t, is_same_v, true_type, false_type 7 | 8 | #include "../common.hpp" 9 | #include "../memory.hpp" 10 | 11 | /// \cond FALSE 12 | 13 | namespace cen::detail { 14 | 15 | using owner_tag = std::true_type; 16 | using handle_tag = std::false_type; 17 | 18 | template 19 | inline constexpr bool is_owner = std::is_same_v; 20 | 21 | template 22 | inline constexpr bool is_handle = std::is_same_v; 23 | 24 | template 25 | using enable_for_owner = std::enable_if_t, int>; 26 | 27 | template 28 | using enable_for_handle = std::enable_if_t, int>; 29 | 30 | template 31 | class pointer final 32 | { 33 | public: 34 | using smart_ptr = managed_ptr; 35 | using raw_ptr = Type*; 36 | using pointer_type = std::conditional_t; 37 | 38 | pointer() noexcept = default; 39 | 40 | explicit pointer(Type* ptr) noexcept : mPtr{ptr} {} 41 | 42 | template = 0> 43 | void reset(Type* ptr) noexcept 44 | { 45 | mPtr.reset(ptr); 46 | } 47 | 48 | auto operator->() noexcept -> Type* { return get(); } 49 | 50 | auto operator->() const noexcept -> const Type* { return get(); } 51 | 52 | auto operator*() noexcept -> Type& 53 | { 54 | assert(mPtr); 55 | return *mPtr; 56 | } 57 | 58 | auto operator*() const noexcept -> const Type& 59 | { 60 | assert(mPtr); 61 | return *mPtr; 62 | } 63 | 64 | explicit operator bool() const noexcept { return mPtr != nullptr; } 65 | 66 | /*implicit*/ operator Type*() const noexcept { return get(); } 67 | 68 | template = 0> 69 | [[nodiscard]] auto release() noexcept -> Type* 70 | { 71 | return mPtr.release(); 72 | } 73 | 74 | [[nodiscard]] auto get() const noexcept -> Type* 75 | { 76 | if constexpr (B::value) { 77 | return mPtr.get(); 78 | } 79 | else { 80 | return mPtr; 81 | } 82 | } 83 | 84 | private: 85 | pointer_type mPtr{}; 86 | }; 87 | 88 | } // namespace cen::detail 89 | 90 | /// \endcond 91 | 92 | #endif // CENTURION_DETAIL_OWNER_HANDLE_API_HPP_ 93 | -------------------------------------------------------------------------------- /source/wanderer/io/level-parsing/parse_levels.cpp: -------------------------------------------------------------------------------- 1 | #include "parse_levels.hpp" 2 | 3 | #include // path 4 | #include // string 5 | #include // vector 6 | 7 | #include 8 | 9 | #include "tiled-json/tiled_json_parser.hpp" 10 | #include "wanderer/data/components/levels.hpp" 11 | #include "wanderer/misc/exception.hpp" 12 | #include "wanderer/misc/logging.hpp" 13 | 14 | namespace wanderer { 15 | namespace { 16 | 17 | struct level_info final 18 | { 19 | level_id id{}; 20 | std::filesystem::path source; 21 | }; 22 | 23 | [[nodiscard]] auto parse_level_paths() -> std::vector 24 | { 25 | std::vector levels; 26 | 27 | const std::filesystem::path resources{"resources/maps"}; 28 | const auto path = resources / "levels.yaml"; 29 | 30 | #if WANDERER_COMPILER_MSVC 31 | const auto root = YAML::LoadFile(path.string()); 32 | #else 33 | const auto root = YAML::LoadFile(path); 34 | #endif // WANDERER_COMPILER_MSVC 35 | 36 | if (auto sequence = root["levels"]) { 37 | levels.reserve(sequence.size()); 38 | 39 | for (auto node : sequence) { 40 | const auto relativePath = node["source"].as(); 41 | auto& info = levels.emplace_back(); 42 | info.id = node["id"].as(); 43 | info.source = resources / relativePath; 44 | } 45 | } 46 | 47 | if (levels.empty()) { 48 | throw_traced(WandererError{"Found no levels to load!"}); 49 | } 50 | 51 | return levels; 52 | } 53 | 54 | } // namespace 55 | 56 | void parse_levels(entt::registry& shared, Graphics& graphics) 57 | { 58 | const auto& cfg = shared.ctx().at(); 59 | auto& levels = shared.ctx().at(); 60 | maybe first; 61 | 62 | for (const auto& info : parse_level_paths()) { 63 | debug("Loading level '{}' from {}", info.id, info.source.string()); 64 | 65 | if (levels.levels.contains(info.id)) { 66 | throw_traced(WandererError{"Detected duplicate level identifiers!"}); 67 | } 68 | 69 | if (!first) { 70 | first = info.id; 71 | } 72 | 73 | const auto ext = info.source.extension(); 74 | if (ext == ".json") { 75 | levels.levels[info.id] = io::parse_tiled_json_map(info.source, graphics, cfg); 76 | } 77 | // TODO 78 | // else if (ext == ".yaml") { 79 | // 80 | // } 81 | else { 82 | throw_traced(WandererError{"Unsupported map file extension!"}); 83 | } 84 | } 85 | 86 | levels.current = first.value(); 87 | } 88 | 89 | } // namespace wanderer -------------------------------------------------------------------------------- /.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | name: "CI: macOS" 2 | 3 | on: [ push, pull_request ] 4 | 5 | # See https://github.com/actions/virtual-environments/blob/main/images/macos 6 | env: 7 | BOOST_ROOT: /opt/homebrew/Cellar/boost/1.78.0 8 | VCPKG_ROOT: ${{github.workspace}}/vcpkg 9 | VCPKG_DEFAULT_BINARY_CACHE: ${{github.workspace}}/vcpkg/bincache # https://github.com/microsoft/vcpkg/blob/master/docs/users/binarycaching.md#configuration 10 | VCPKG_COMMIT: b1642553ec6049bb5ccdf59bea6b4aaca1033e82 11 | VCPKG_CACHE_EDITION: 1 12 | VCPKG_TARGET_TRIPLET: x64-osx 13 | 14 | jobs: 15 | macos-test: 16 | runs-on: macos-latest 17 | if: contains(github.event.head_commit.message, '[skip-ci]') == false 18 | steps: 19 | - uses: actions/checkout@main 20 | 21 | - name: Update Brew 22 | run: brew update 23 | 24 | - name: Install Ninja 25 | shell: bash 26 | run: brew install ninja 27 | 28 | # TODO cache boost 29 | - name: Install Boost 30 | run: brew install boost 31 | 32 | - name: Restore cached Vcpkg 33 | id: restore-vcpkg-and-artifacts 34 | uses: actions/cache@main 35 | with: 36 | path: | 37 | ${{env.VCPKG_ROOT}} 38 | !${{env.VCPKG_ROOT}}\buildtrees 39 | !${{env.VCPKG_ROOT}}\packages 40 | !${{env.VCPKG_ROOT}}\downloads 41 | !${{env.VCPKG_ROOT}}\installed 42 | key: wanderer-macos-vcpkg-${{hashFiles('vcpkg.json')}}-${{env.VCPKG_COMMIT}}-${{env.VCPKG_CACHE_EDITION}} 43 | 44 | - name: Checkout Vcpkg 45 | if: steps.restore-vcpkg-and-artifacts.outputs.cache-hit != 'true' 46 | uses: actions/checkout@main 47 | with: 48 | repository: microsoft/vcpkg 49 | path: vcpkg 50 | ref: ${{env.VCPKG_COMMIT}} 51 | 52 | - name: Install Vcpkg 53 | if: steps.restore-vcpkg-and-artifacts.outputs.cache-hit != 'true' 54 | working-directory: ./vcpkg 55 | run: ./bootstrap-vcpkg.sh -disableMetrics 56 | 57 | - name: Create directory ${{env.VCPKG_DEFAULT_BINARY_CACHE}} 58 | if: steps.restore-vcpkg-and-artifacts.outputs.cache-hit != 'true' 59 | run: mkdir ${{env.VCPKG_DEFAULT_BINARY_CACHE}} 60 | 61 | - name: Make build directory 62 | run: mkdir build 63 | 64 | - name: Generate build files 65 | working-directory: ./build 66 | run: cmake .. -DCMAKE_BUILD_TYPE=Debug -GNinja 67 | 68 | - name: Build 69 | working-directory: ./build 70 | run: ninja 71 | 72 | - name: Run tests 73 | working-directory: ./build/test 74 | run: ./WandererTests -------------------------------------------------------------------------------- /source/wanderer/systems/viewport_system.cpp: -------------------------------------------------------------------------------- 1 | #include "viewport_system.hpp" 2 | 3 | #include "wanderer/data/cfg.hpp" 4 | #include "wanderer/data/components/rendering.hpp" 5 | #include "wanderer/data/components/tiles.hpp" 6 | #include "wanderer/data/components/world.hpp" 7 | #include "wanderer/misc/assert.hpp" 8 | 9 | namespace wanderer::sys { 10 | namespace { 11 | 12 | constexpr float32 _camera_speed = 10; 13 | 14 | } // namespace 15 | 16 | void update_render_bounds(entt::registry& registry) 17 | { 18 | const auto& cfg = registry.ctx().at(); 19 | const auto& map = registry.ctx().at(); 20 | const auto& viewport = registry.ctx().at(); 21 | 22 | const auto viewportMax = viewport.offset + viewport.size; 23 | 24 | /* Overwrite previous render bounds context */ 25 | auto& bounds = registry.ctx().emplace(); 26 | 27 | const auto min = viewport.offset / cfg.tile_size; 28 | bounds.begin_row = (min.y > 0) ? static_cast(min.y) : 0u; 29 | bounds.begin_col = (min.x > 0) ? static_cast(min.x) : 0u; 30 | 31 | const auto maxRow = static_cast(viewportMax.y / cfg.tile_size.y) + 1u; 32 | const auto maxCol = static_cast(viewportMax.x / cfg.tile_size.x) + 1u; 33 | bounds.end_row = (maxRow < map.row_count) ? maxRow : map.row_count; 34 | bounds.end_col = (maxCol < map.col_count) ? maxCol : map.col_count; 35 | } 36 | 37 | void update_viewport(entt::registry& registry, const float32 dt) 38 | { 39 | const auto& map = registry.ctx().at(); 40 | auto& viewport = registry.ctx().at(); 41 | 42 | /* Don't move the viewport if there is no viewport target */ 43 | const auto view = registry.view(); 44 | if (view.empty()) { 45 | return; 46 | } 47 | 48 | WANDERER_ASSERT_MSG(view.size() == 1, "There cannot be more than 1 viewport target!"); 49 | const auto targetEntity = view.front(); 50 | 51 | WANDERER_ASSERT(registry.all_of(targetEntity)); 52 | const auto& object = registry.get(targetEntity); 53 | 54 | const auto target = (object.position + (object.size / 2.0f)) - (viewport.size / 2.0f); 55 | auto next = viewport.offset + (target - viewport.offset) * (_camera_speed * dt); 56 | 57 | if (viewport.keep_in_bounds) { 58 | next.x = (next.x < 0) ? 0 : next.x; 59 | next.y = (next.y < 0) ? 0 : next.y; 60 | 61 | const auto diff = map.size - viewport.size; 62 | next.x = (next.x > diff.x) ? diff.x : next.x; 63 | next.y = (next.y > diff.y) ? diff.y : next.y; 64 | } 65 | 66 | viewport.offset = next; 67 | } 68 | 69 | } // namespace wanderer::sys -------------------------------------------------------------------------------- /doxygen/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | $projectname: $title 10 | $title 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | $treeview 20 | $search 21 | $mathjax 22 | 23 | $extrastylesheet 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 | 32 | 33 |
34 | 35 | 36 |
37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 49 | 50 | 51 | 52 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 |
45 |
$projectname $projectnumber 46 |
47 |
$projectbrief
48 |
53 |
$projectbrief
54 |
$searchbox
$searchbox
72 |
73 | 74 | 75 | -------------------------------------------------------------------------------- /lib/centurion/centurion.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2019-2022 Albin Johansson 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef CENTURION_HPP_ 26 | #define CENTURION_HPP_ 27 | 28 | #ifndef CENTURION_NO_PRAGMA_ONCE 29 | #pragma once 30 | #endif // CENTURION_NO_PRAGMA_ONCE 31 | 32 | #include "centurion/audio.hpp" 33 | #include "centurion/audio_events.hpp" 34 | #include "centurion/color.hpp" 35 | #include "centurion/common.hpp" 36 | #include "centurion/concurrency.hpp" 37 | #include "centurion/controller.hpp" 38 | #include "centurion/controller_events.hpp" 39 | #include "centurion/endian.hpp" 40 | #include "centurion/event.hpp" 41 | #include "centurion/event_base.hpp" 42 | #include "centurion/features.hpp" 43 | #include "centurion/filesystem.hpp" 44 | #include "centurion/font.hpp" 45 | #include "centurion/initialization.hpp" 46 | #include "centurion/input.hpp" 47 | #include "centurion/joystick.hpp" 48 | #include "centurion/joystick_events.hpp" 49 | #include "centurion/keyboard.hpp" 50 | #include "centurion/locale.hpp" 51 | #include "centurion/logging.hpp" 52 | #include "centurion/math.hpp" 53 | #include "centurion/memory.hpp" 54 | #include "centurion/message_box.hpp" 55 | #include "centurion/misc_events.hpp" 56 | #include "centurion/mouse.hpp" 57 | #include "centurion/mouse_events.hpp" 58 | #include "centurion/opengl.hpp" 59 | #include "centurion/pixels.hpp" 60 | #include "centurion/power.hpp" 61 | #include "centurion/render.hpp" 62 | #include "centurion/sensor.hpp" 63 | #include "centurion/surface.hpp" 64 | #include "centurion/system.hpp" 65 | #include "centurion/texture.hpp" 66 | #include "centurion/touch.hpp" 67 | #include "centurion/unicode.hpp" 68 | #include "centurion/version.hpp" 69 | #include "centurion/video.hpp" 70 | #include "centurion/vulkan.hpp" 71 | #include "centurion/window.hpp" 72 | #include "centurion/window_events.hpp" 73 | 74 | #endif // CENTURION_HPP_ 75 | -------------------------------------------------------------------------------- /source/main.cpp: -------------------------------------------------------------------------------- 1 | #include // abort 2 | #include // set_terminate 3 | #include // stringstream 4 | #include // vector 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "wanderer/core/math.hpp" 11 | #include "wanderer/data/cfg.hpp" 12 | #include "wanderer/io/directories.hpp" 13 | #include "wanderer/meta/build.hpp" 14 | #include "wanderer/misc/assert.hpp" // Do not remove! 15 | #include "wanderer/misc/exception.hpp" 16 | #include "wanderer/misc/logging.hpp" 17 | #include "wanderer/wanderer_game.hpp" 18 | 19 | namespace { 20 | 21 | struct protobuf_context final 22 | { 23 | protobuf_context() { GOOGLE_PROTOBUF_VERIFY_VERSION; } 24 | 25 | ~protobuf_context() { google::protobuf::ShutdownProtobufLibrary(); } 26 | }; 27 | 28 | [[nodiscard]] auto _make_program_args_parser() -> argparse::ArgumentParser 29 | { 30 | argparse::ArgumentParser parser{"Wanderer", wanderer::wanderer_version}; 31 | 32 | parser.add_argument("--logical-size", "-ls") 33 | .help("override the logical viewport size") 34 | .scan<'i', int>() 35 | .nargs(2); 36 | 37 | parser.add_argument("--tile-size", "-ts") 38 | .help("override the tile size") 39 | .scan<'i', int>() 40 | .nargs(2); 41 | 42 | return parser; 43 | } 44 | 45 | } // namespace 46 | 47 | int main(int argc, char** argv) 48 | { 49 | std::set_terminate([] { std::abort(); }); /* Avoid redundant logging */ 50 | 51 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest"); 52 | 53 | try { 54 | auto parser = _make_program_args_parser(); 55 | parser.parse_args(argc, argv); 56 | 57 | protobuf_context protobuf; 58 | 59 | const cen::sdl sdl; 60 | const cen::img img; 61 | const cen::mix mix; 62 | const cen::ttf ttf; 63 | 64 | wanderer::debug("Persistent file directory is {}", 65 | wanderer::get_persistent_file_dir().string()); 66 | 67 | auto cfg = wanderer::make_game_cfg(); 68 | 69 | if (const auto ls = parser.present>("-ls")) { 70 | cfg.logical_size.x = ls->at(0); 71 | cfg.logical_size.y = ls->at(1); 72 | cfg.logical_size_f = wanderer::glmx::as_f(cfg.logical_size); 73 | } 74 | 75 | if (const auto ts = parser.present>("-ts")) { 76 | cfg.tile_size.x = static_cast(ts->at(0)); 77 | cfg.tile_size.y = static_cast(ts->at(1)); 78 | } 79 | 80 | wanderer::WandererGame game{cfg}; 81 | game.run(); 82 | 83 | return 0; 84 | } 85 | catch (const std::exception& e) { 86 | wanderer::print(fmt::color::hot_pink, 87 | "Unhandled exception message: '{}'\n", 88 | e.what()); 89 | 90 | if (const auto* stacktrace = boost::get_error_info(e)) { 91 | std::stringstream stream; 92 | stream << *stacktrace; 93 | wanderer::print(fmt::color::hot_pink, "{}\n", stream.str()); 94 | } 95 | 96 | std::abort(); 97 | } 98 | } -------------------------------------------------------------------------------- /source/wanderer/io/settings.cpp: -------------------------------------------------------------------------------- 1 | #include "settings.hpp" 2 | 3 | #include // exists 4 | #include // ifstream, ofstream 5 | #include // ios 6 | 7 | #include "directories.hpp" 8 | #include "settings.pb.h" 9 | #include "wanderer/misc/logging.hpp" 10 | 11 | namespace wanderer { 12 | namespace { 13 | 14 | [[nodiscard]] auto _default_settings() -> Settings 15 | { 16 | Settings s; 17 | 18 | s.flags = Settings::fullscreen_bit | Settings::vsync_bit; 19 | 20 | return s; 21 | } 22 | 23 | [[nodiscard]] auto _settings_file_path() -> const std::filesystem::path& 24 | { 25 | static const auto path = get_persistent_file_dir() / "settings.bin"; 26 | return path; 27 | } 28 | 29 | } // namespace 30 | 31 | void Settings::set_flag(const uint64 flag, const bool value) noexcept 32 | { 33 | if (value) { 34 | flags |= flag; 35 | } 36 | else { 37 | flags &= ~flag; 38 | } 39 | } 40 | 41 | auto Settings::test_flag(const uint64 flag) const noexcept -> bool 42 | { 43 | return flags & flag; 44 | } 45 | 46 | auto load_settings() -> Settings 47 | { 48 | const auto& path = _settings_file_path(); 49 | debug("Loading settings from {}", path.string()); 50 | 51 | if (std::filesystem::exists(path)) { 52 | std::ifstream stream{path, std::ios::binary | std::ios::in}; 53 | 54 | proto::settings in; 55 | if (in.ParseFromIstream(&stream)) { 56 | Settings result = _default_settings(); 57 | 58 | if (in.has_fullscreen()) { 59 | result.set_flag(Settings::fullscreen_bit, in.fullscreen()); 60 | } 61 | 62 | if (in.has_vsync()) { 63 | result.set_flag(Settings::vsync_bit, in.vsync()); 64 | } 65 | 66 | if (in.has_integer_scaling()) { 67 | result.set_flag(Settings::integer_scaling_bit, in.integer_scaling()); 68 | } 69 | 70 | info("[OPTION] fullscreen = '{}'", result.test_flag(Settings::fullscreen_bit)); 71 | info("[OPTION] vsync = '{}'", result.test_flag(Settings::vsync_bit)); 72 | info("[OPTION] integer scaling = '{}'", 73 | result.test_flag(Settings::integer_scaling_bit)); 74 | 75 | return result; 76 | } 77 | else { 78 | warn("Failed to parse persistent settings, assuming defaults..."); 79 | return _default_settings(); 80 | } 81 | } 82 | else { 83 | info("Found no persistent settings file, assuming defaults..."); 84 | return _default_settings(); 85 | } 86 | } 87 | 88 | void save_settings(const Settings& s) 89 | { 90 | proto::settings out; 91 | out.set_fullscreen(s.test_flag(Settings::fullscreen_bit)); 92 | out.set_vsync(s.test_flag(Settings::vsync_bit)); 93 | out.set_integer_scaling(s.test_flag(Settings::integer_scaling_bit)); 94 | 95 | const auto& path = _settings_file_path(); 96 | debug("Saving settings to {}", path.string()); 97 | 98 | std::ofstream stream{path, std::ios::binary | std::ios::out | std::ios::trunc}; 99 | 100 | out.SerializeToOstream(&stream); 101 | } 102 | 103 | } // namespace wanderer -------------------------------------------------------------------------------- /resources/maps/tent1.yaml: -------------------------------------------------------------------------------- 1 | version: 1 2 | row-count: 7 3 | column-count: 5 4 | tile-width: 32 5 | tile-height: 32 6 | next-layer-id: 6 7 | next-object-id: 7 8 | properties: 9 | - name: humanoidLayer 10 | type: int 11 | value: 2 12 | - name: id 13 | type: int 14 | value: 1 15 | - name: is-outside 16 | type: bool 17 | value: false 18 | layers: 19 | - name: Ground 1 20 | id: 1 21 | type: tile-layer 22 | data: 168 168 1974 1975 1976 168 168 2006 2007 2008 168 169 2006 2007 2008 168 168 2038 2039 2040 168 168 169 168 169 168 169 169 169 168 169 169 168 168 168 23 | properties: 24 | - name: ground 25 | type: bool 26 | value: true 27 | - name: T1 28 | id: 3 29 | type: tile-layer 30 | data: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1279 0 0 31 | properties: 32 | - name: ground 33 | type: bool 34 | value: false 35 | - name: T2 (Humanoid layer) 36 | id: 4 37 | type: tile-layer 38 | data: 1461 0 0 1622 1623 0 0 0 1654 1655 1427 0 0 1686 1687 1459 0 0 0 0 0 0 0 0 1207 1458 0 0 0 1424 1490 0 0 0 0 39 | properties: 40 | - name: ground 41 | type: bool 42 | value: false 43 | - name: T3 44 | id: 5 45 | type: tile-layer 46 | data: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1277 0 0 0 0 0 0 0 0 0 0 47 | properties: 48 | - name: ground 49 | type: bool 50 | value: false 51 | - name: O1 52 | id: 2 53 | type: object-layer 54 | objects: 55 | - id: 1 56 | type: rect 57 | tag: Portal 58 | x: 64 59 | y: 192 60 | width: 32 61 | height: 32 62 | properties: 63 | - name: path 64 | type: file 65 | value: . 66 | - name: target 67 | type: int 68 | value: 0 69 | - id: 3 70 | type: point 71 | name: Player 72 | tag: Spawnpoint 73 | x: 80 74 | y: 176 75 | properties: 76 | - name: entity 77 | type: string 78 | value: player 79 | - id: 4 80 | type: rect 81 | tag: ContainerTrigger 82 | x: 32 83 | y: 84 84 | width: 16 85 | height: 32 86 | properties: 87 | - name: container 88 | type: object 89 | value: 5 90 | - id: 5 91 | type: rect 92 | name: Chest 93 | tag: Container 94 | x: 9 95 | y: 80 96 | width: 16 97 | height: 16 98 | properties: 99 | - name: capacity 100 | type: int 101 | value: 20 102 | - name: hasRandomLoot 103 | type: bool 104 | value: false 105 | - id: 6 106 | type: rect 107 | tag: BedTrigger 108 | x: 88 109 | y: 51 110 | width: 16 111 | height: 32 112 | tilesets: 113 | - first-global-id: 1025 114 | path: tilesets/interior.yaml 115 | - first-global-id: 1 116 | path: tilesets/terrain.yaml -------------------------------------------------------------------------------- /doxygen/doxygen-awesome-fancy-scrollbars.css: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | Doxygen Awesome 4 | https://github.com/jothepro/doxygen-awesome-css 5 | 6 | MIT License 7 | 8 | Copyright (c) 2022 jothepro 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | */ 29 | 30 | /* 31 | Scrollbars for Webkit 32 | */ 33 | 34 | html { 35 | --webkit-scrollbar-size: 7px; 36 | --webkit-scrollbar-padding: 4px; 37 | --webkit-scrollbar-color: var(--separator-color); 38 | } 39 | 40 | #nav-tree::-webkit-scrollbar, 41 | div.fragment::-webkit-scrollbar, 42 | pre.fragment::-webkit-scrollbar, 43 | div.memproto::-webkit-scrollbar, 44 | .contents center::-webkit-scrollbar { 45 | width: calc(var(--webkit-scrollbar-size) + var(--webkit-scrollbar-padding) + var(--webkit-scrollbar-padding)); 46 | height: calc(var(--webkit-scrollbar-size) + var(--webkit-scrollbar-padding) + var(--webkit-scrollbar-padding)); 47 | } 48 | 49 | #nav-tree::-webkit-scrollbar-thumb, 50 | div.fragment::-webkit-scrollbar-thumb, 51 | pre.fragment::-webkit-scrollbar-thumb, 52 | div.memproto::-webkit-scrollbar-thumb, 53 | .contents center::-webkit-scrollbar-thumb { 54 | background-color: transparent; 55 | border: var(--webkit-scrollbar-padding) solid transparent; 56 | border-radius: calc(var(--webkit-scrollbar-padding) + var(--webkit-scrollbar-padding)); 57 | background-clip: padding-box; 58 | } 59 | 60 | #nav-tree:hover::-webkit-scrollbar-thumb, 61 | div.fragment:hover::-webkit-scrollbar-thumb, 62 | pre.fragment:hover::-webkit-scrollbar-thumb, 63 | div.memproto:hover::-webkit-scrollbar-thumb, 64 | .contents center:hover::-webkit-scrollbar-thumb { 65 | background-color: var(--webkit-scrollbar-color); 66 | } 67 | 68 | #nav-tree::-webkit-scrollbar-track, 69 | div.fragment::-webkit-scrollbar-track, 70 | pre.fragment::-webkit-scrollbar-track, 71 | div.memproto::-webkit-scrollbar-track, 72 | .contents center::-webkit-scrollbar-track { 73 | background: transparent; 74 | } 75 | 76 | #nav-tree, div.fragment, pre.fragment, div.memproto, .contents center { 77 | overflow-x: overlay; 78 | } 79 | 80 | /* 81 | Scrollbars for Firefox 82 | */ 83 | 84 | #nav-tree, div.fragment, pre.fragment, div.memproto, .contents center { 85 | scrollbar-width: thin; 86 | } -------------------------------------------------------------------------------- /source/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | project(wanderer-source CXX) 4 | 5 | file(GLOB_RECURSE SOURCE_FILES 6 | CONFIGURE_DEPENDS 7 | wanderer/**/*.cpp 8 | wanderer/**/*.hpp 9 | wanderer/*.cpp 10 | wanderer/*.hpp 11 | ) 12 | 13 | add_library(${TARGET_WANDERER_LIB} ${SOURCE_FILES}) 14 | 15 | add_dependencies(${TARGET_WANDERER_LIB} ${TARGET_WANDERER_PROTO}) 16 | 17 | target_include_directories(${TARGET_WANDERER_LIB} 18 | PUBLIC 19 | ${PROJECT_SOURCE_DIR} 20 | 21 | SYSTEM PUBLIC 22 | ${WANDERER_LIBRARY_DIR}/centurion 23 | ${Boost_INCLUDE_DIRS} 24 | ) 25 | 26 | target_link_libraries(${TARGET_WANDERER_LIB} 27 | PUBLIC 28 | ${TARGET_WANDERER_PROTO} 29 | SDL2::SDL2 30 | SDL2::SDL2_image 31 | $,SDL2_mixer::SDL2_mixer,SDL2_mixer::SDL2_mixer-static> 32 | $,SDL2_ttf::SDL2_ttf,SDL2_ttf::SDL2_ttf-static> 33 | EnTT::EnTT 34 | glm::glm 35 | fmt::fmt 36 | magic_enum::magic_enum 37 | yaml-cpp 38 | nlohmann_json::nlohmann_json 39 | argparse::argparse 40 | box2d::box2d 41 | ${Boost_LIBRARIES} 42 | ) 43 | 44 | target_compile_definitions(${TARGET_WANDERER_LIB} 45 | PUBLIC 46 | BOOST_ENABLE_ASSERT_DEBUG_HANDLER 47 | BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED 48 | ) 49 | 50 | wanderer_add_compiler_options(${TARGET_WANDERER_LIB}) 51 | 52 | target_precompile_headers(${TARGET_WANDERER_LIB} PRIVATE 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | ) 66 | 67 | if (CMAKE_BUILD_TYPE STREQUAL RELEASE) 68 | add_executable(${TARGET_WANDERER_EXE} WIN32 main.cpp) 69 | else () 70 | add_executable(${TARGET_WANDERER_EXE} main.cpp) 71 | endif () 72 | 73 | wanderer_add_compiler_options(${TARGET_WANDERER_EXE}) 74 | 75 | add_dependencies(${TARGET_WANDERER_EXE} ${TARGET_WANDERER_LIB}) 76 | 77 | target_link_libraries(${TARGET_WANDERER_EXE} 78 | PRIVATE 79 | ${TARGET_WANDERER_LIB} 80 | SDL2::SDL2main 81 | ) 82 | 83 | target_precompile_headers(${TARGET_WANDERER_EXE} REUSE_FROM ${TARGET_WANDERER_LIB}) 84 | 85 | copy_directory_post_build(${TARGET_WANDERER_EXE} ${WANDERER_RESOURCE_DIR} "${CMAKE_CURRENT_BINARY_DIR}/resources") 86 | -------------------------------------------------------------------------------- /source/wanderer/misc/fmt_specializations.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace fmt { 8 | 9 | template 10 | struct formatter> 11 | { 12 | template 13 | constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) 14 | { 15 | return ctx.begin(); 16 | } 17 | 18 | template 19 | auto format(const cen::basic_point& point, FormatContext& ctx) -> decltype(ctx.out()) 20 | { 21 | return format_to(ctx.out(), "({}, {})", point.x(), point.y()); 22 | } 23 | }; 24 | 25 | template 26 | struct formatter> 27 | { 28 | template 29 | constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) 30 | { 31 | return ctx.begin(); 32 | } 33 | 34 | template 35 | auto format(const cen::basic_area& area, FormatContext& ctx) -> decltype(ctx.out()) 36 | { 37 | return format_to(ctx.out(), "({}, {})", area.width, area.height); 38 | } 39 | }; 40 | 41 | template 42 | struct formatter> 43 | { 44 | template 45 | constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) 46 | { 47 | return ctx.begin(); 48 | } 49 | 50 | template 51 | auto format(const cen::basic_rect& rect, FormatContext& ctx) -> decltype(ctx.out()) 52 | { 53 | return format_to(ctx.out(), 54 | "({}, {}, {}, {})", 55 | rect.x(), 56 | rect.y(), 57 | rect.width(), 58 | rect.height()); 59 | } 60 | }; 61 | 62 | template 63 | struct formatter> 64 | { 65 | template 66 | constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) 67 | { 68 | return ctx.begin(); 69 | } 70 | 71 | template 72 | auto format(const glm::vec<2, T>& vec, FormatContext& ctx) -> decltype(ctx.out()) 73 | { 74 | return format_to(ctx.out(), "({}, {})", vec.x, vec.y); 75 | } 76 | }; 77 | 78 | template 79 | struct formatter> 80 | { 81 | template 82 | constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) 83 | { 84 | return ctx.begin(); 85 | } 86 | 87 | template 88 | auto format(const glm::vec<3, T>& vec, FormatContext& ctx) -> decltype(ctx.out()) 89 | { 90 | return format_to(ctx.out(), "({}, {}, {})", vec.x, vec.y, vec.z); 91 | } 92 | }; 93 | 94 | template 95 | struct formatter> 96 | { 97 | template 98 | constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) 99 | { 100 | return ctx.begin(); 101 | } 102 | 103 | template 104 | auto format(const glm::vec<4, T>& vec, FormatContext& ctx) -> decltype(ctx.out()) 105 | { 106 | return format_to(ctx.out(), "({}, {}, {}, {})", vec.x, vec.y, vec.z, vec.w); 107 | } 108 | }; 109 | 110 | } // namespace fmt 111 | -------------------------------------------------------------------------------- /source/wanderer/systems/physics_system.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "wanderer/common.hpp" 7 | #include "wanderer/fwd.hpp" 8 | 9 | namespace wanderer::sys { 10 | 11 | /** 12 | * \ingroup systems 13 | * \defgroup physics-system Physics system 14 | * 15 | * \brief Manages physics-related aspects of the game. 16 | */ 17 | 18 | /// \addtogroup physics-system 19 | /// \{ 20 | 21 | /** 22 | * \brief Makes an entity a part of the physics simulation. 23 | * 24 | * \details The entity will have a `comp::physics_body` component added to it. 25 | * 26 | * \param registry the level registry. 27 | * \param entity the target entity. 28 | * \param type the body type. 29 | * \param logicalPos the position of the game object, in logical space. 30 | * \param logicalSize the size of the game object, in logical space. 31 | * \param maxSpeed the maximum total speed of the object. 32 | * \param offset the offset of the physics body to the game object, in logical space. 33 | */ 34 | void add_physics_body(entt::registry& registry, 35 | entt::entity entity, 36 | b2BodyType type, 37 | const glm::vec2& logicalPos, 38 | const glm::vec2& logicalSize, 39 | float32 maxSpeed, 40 | const glm::vec2& offset = {}); 41 | 42 | /** 43 | * \brief Destroys the simulation data associated with a physics object. 44 | * 45 | * \details This function is meant to be called for each time an entity featuring a 46 | * `physics_object` component is destroyed. 47 | * 48 | * \note The `b2World` class automatically frees all created bodies and fixtures upon 49 | * destruction, so this is just used for simulated entities that are destroyed 50 | * "in-flight". 51 | * 52 | * \param registry the level registry. 53 | * \param entity the entity that will be destroyed. 54 | */ 55 | void on_destroy_physics_object(entt::registry& registry, entt::entity entity); 56 | 57 | /** 58 | * \brief Updates the physics simulation. 59 | * 60 | * \param registry the level registry. 61 | * \param dt the current delta time. 62 | */ 63 | void update_physics(entt::registry& registry, float32 dt); 64 | 65 | /** 66 | * \brief Debug the physics simulation by rendering collision shapes. 67 | * 68 | * \param registry the level registry. 69 | * \param graphics the current graphics context. 70 | */ 71 | void debug_physics(const entt::registry& registry, Graphics& graphics); 72 | 73 | /** 74 | * \brief Converts a vector in logical coordinates to simulation coordinates. 75 | * 76 | * \param registry the level registry. 77 | * \param vec the logical vector. 78 | * 79 | * \return a corresponding vector in simulation coordinates. 80 | */ 81 | [[nodiscard]] auto to_physics_scale(const entt::registry& registry, const glm::vec2& vec) 82 | -> b2Vec2; 83 | 84 | /** 85 | * \brief Converts a vector in simulation coordinates to logical coordinates. 86 | * 87 | * \param registry the level registry. 88 | * \param vec the simulation vector. 89 | * 90 | * \return a corresponding vector in logical coordinates. 91 | */ 92 | [[nodiscard]] auto to_logical_scale(const entt::registry& registry, const b2Vec2& vec) 93 | -> glm::vec2; 94 | 95 | /// \} End of group physics-system 96 | 97 | } // namespace wanderer::sys -------------------------------------------------------------------------------- /.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | name: "CI: Windows" 2 | 3 | on: [ push, pull_request ] 4 | 5 | # See https://github.com/actions/virtual-environments/tree/main/images/win 6 | env: 7 | BOOST_ROOT: ${{github.workspace}}\boost\boost_1_78_0 8 | BOOST_VERSION: 1.78.0 9 | BOOST_VERSION_IN_URL: 1_78_0 10 | BOOST_CACHE_EDITION: 1 11 | VCPKG_ROOT: ${{github.workspace}}\vcpkg 12 | VCPKG_DEFAULT_BINARY_CACHE: ${{github.workspace}}\vcpkg\bincache # https://github.com/microsoft/vcpkg/blob/master/docs/users/binarycaching.md#configuration 13 | VCPKG_COMMIT: b1642553ec6049bb5ccdf59bea6b4aaca1033e82 14 | VCPKG_CACHE_EDITION: 1 15 | VCPKG_DEFAULT_TRIPLET: x64-windows-static-md 16 | VCPKG_TARGET_TRIPLET: x64-windows-static-md 17 | 18 | jobs: 19 | windows-test: 20 | runs-on: windows-latest 21 | if: contains(github.event.head_commit.message, '[skip-ci]') == false 22 | steps: 23 | - uses: actions/checkout@main 24 | - uses: ilammy/msvc-dev-cmd@master 25 | - uses: lukka/get-cmake@latest 26 | 27 | - name: Restore cached Boost 28 | id: restore-cached-boost 29 | uses: actions/cache@v2 30 | with: 31 | path: ${{env.BOOST_ROOT}} 32 | key: wanderer-windows-boost-${{env.BOOST_VERSION}}-${{env.BOOST_CACHE_EDITION}} 33 | 34 | - name: Install Boost 35 | if: steps.restore-cached-boost.outputs.cache-hit != 'true' 36 | shell: powershell 37 | run: | 38 | mkdir boost 39 | cd boost 40 | Invoke-WebRequest -Uri "https://boostorg.jfrog.io/artifactory/main/release/${{env.BOOST_VERSION}}/source/boost_${{env.BOOST_VERSION_IN_URL}}.zip" ` 41 | -OutFile boost.zip 42 | 7z x -y boost.zip 43 | 44 | - name: Restore cached Vcpkg 45 | id: restore-vcpkg-and-artifacts 46 | uses: actions/cache@v2 47 | with: 48 | path: | 49 | ${{env.VCPKG_ROOT}} 50 | !${{env.VCPKG_ROOT}}\buildtrees 51 | !${{env.VCPKG_ROOT}}\packages 52 | !${{env.VCPKG_ROOT}}\downloads 53 | !${{env.VCPKG_ROOT}}\installed 54 | key: wanderer-windows-vcpkg-${{hashFiles('vcpkg.json')}}-${{env.VCPKG_COMMIT}}-${{env.VCPKG_CACHE_EDITION}} 55 | 56 | - name: Checkout Vcpkg 57 | if: steps.restore-vcpkg-and-artifacts.outputs.cache-hit != 'true' 58 | uses: actions/checkout@main 59 | with: 60 | repository: microsoft/vcpkg 61 | path: vcpkg 62 | ref: ${{env.VCPKG_COMMIT}} 63 | 64 | - name: Install Vcpkg 65 | if: steps.restore-vcpkg-and-artifacts.outputs.cache-hit != 'true' 66 | working-directory: ./vcpkg 67 | run: .\bootstrap-vcpkg.bat -disableMetrics 68 | 69 | - name: Create Vcpkg binary cache directory 70 | if: steps.restore-vcpkg-and-artifacts.outputs.cache-hit != 'true' 71 | run: mkdir ${{env.VCPKG_DEFAULT_BINARY_CACHE}} 72 | 73 | - name: Create build directory 74 | run: mkdir build 75 | 76 | - name: Generate build files 77 | working-directory: ./build 78 | run: cmake .. -DCMAKE_BUILD_TYPE=Debug -GNinja 79 | 80 | - name: Build 81 | working-directory: ./build 82 | run: ninja 83 | 84 | # - name: Run tests 85 | # working-directory: ./build/test 86 | # shell: cmd 87 | # run: ctest -------------------------------------------------------------------------------- /source/wanderer/core/graphics.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "wanderer/common.hpp" 6 | #include "wanderer/fwd.hpp" 7 | 8 | /* On MSVC using the Visual Studio 2022 toolchain, a Windows header leaks this macro :( */ 9 | #undef small 10 | 11 | namespace wanderer { 12 | 13 | /** 14 | * \brief Represents the different supported font sizes. 15 | */ 16 | enum class FontSize 17 | { 18 | small, 19 | medium, 20 | large, 21 | huge 22 | }; 23 | 24 | /** 25 | * \brief Manages the game window and renderer, along with fonts and textures. 26 | */ 27 | class Graphics final 28 | { 29 | public: 30 | Graphics(const GameConfig& cfg, const Settings& s); 31 | 32 | /** 33 | * \brief Toggles the fullscreen mode of the window. 34 | * 35 | * \return `true` if the window is now fullscreen; `false` otherwise. 36 | */ 37 | auto toggle_fullscreen() -> bool; 38 | 39 | /** 40 | * \brief Toggles the use of VSync by the renderer. 41 | * 42 | * \return `true` if VSync was enabled; `false` otherwise. 43 | */ 44 | auto toggle_vsync() -> bool; 45 | 46 | /** 47 | * \brief Toggles the use of integer scaling by the renderer. 48 | * 49 | * \return `true` if integer scaling was enabled; `false` otherwise. 50 | */ 51 | auto toggle_integer_scaling() -> bool; 52 | 53 | /** 54 | * \brief Loads a texture. 55 | * 56 | * \param path the file path to the source texture. 57 | * 58 | * \return the assigned texture identifier. 59 | */ 60 | auto load_texture(const std::filesystem::path& path) -> texture_id; 61 | 62 | /** 63 | * \brief Renders a previously loaded texture. 64 | * 65 | * \param id the identifier associated with the texture to render. 66 | * \param source the source region of the texture to render. 67 | * \param dest the destination position and size of the texture. 68 | */ 69 | void render_texture(texture_id id, const glm::ivec4& source, const glm::vec4& dest); 70 | 71 | /// \copydoc render_texture() 72 | void render_texture(texture_id id, const cen::irect& source, const cen::frect& dest); 73 | 74 | /** 75 | * \brief Renders a light. 76 | * 77 | * \note Make sure the light canvas is the render target before calling this function. 78 | * 79 | * \param dest the destination position and size. 80 | */ 81 | void render_light(const cen::frect& dest); 82 | 83 | [[nodiscard]] auto get_light_canvas() -> cen::texture&; 84 | 85 | [[nodiscard]] auto get_pixelated_font(FontSize size) -> cen::font&; 86 | 87 | [[nodiscard]] auto get_handwriting_font(FontSize size) -> cen::font&; 88 | 89 | [[nodiscard]] auto window() -> cen::window&; 90 | 91 | [[nodiscard]] auto renderer() -> cen::renderer&; 92 | 93 | private: 94 | cen::window mWindow; 95 | cen::renderer mRenderer; 96 | 97 | /* Texture identifiers are just indices into this vector */ 98 | std::vector mTextures; 99 | texture_id mLightTextureId{}; 100 | 101 | /* This is used to emulate point lights illuminating areas during the night */ 102 | cen::texture mLightCanvas; 103 | 104 | cen::font_bundle mFontBundle; 105 | cen::font_bundle::id_type mPixelatedFontId; 106 | cen::font_bundle::id_type mHandwritingFontId; 107 | 108 | bool mVsync{}; 109 | }; 110 | 111 | } // namespace wanderer -------------------------------------------------------------------------------- /source/wanderer/systems/ui_system.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "wanderer/common.hpp" 6 | #include "wanderer/data/components/ui.hpp" 7 | #include "wanderer/data/menu_id.hpp" 8 | #include "wanderer/fwd.hpp" 9 | 10 | namespace wanderer::sys { 11 | 12 | /** 13 | * \ingroup systems 14 | * \defgroup ui-system UI System 15 | * 16 | * \brief Manages all UI related aspects, such as menus, buttons, and labels. 17 | */ 18 | 19 | /// \addtogroup ui-system 20 | /// \{ 21 | 22 | /** 23 | * \brief Loads all game menus into a registry. 24 | * 25 | * \details The registry will have the following context components added: 26 | * - `comp::ui_menu_ctx` 27 | * 28 | * \param registry the registry that will host the menus. 29 | */ 30 | void load_menus(entt::registry& registry); 31 | 32 | /** 33 | * \brief Updates the currently active menu. 34 | * 35 | * \param registry the menu registry. 36 | * \param dispatcher the event dispatcher that will be used. 37 | * \param input the current input state. 38 | */ 39 | void update_menus(entt::registry& registry, 40 | entt::dispatcher& dispatcher, 41 | const InputState& input); 42 | 43 | /** 44 | * \brief Changes the active menu. 45 | * 46 | * \param registry the menu registry. 47 | * \param menu the identifier associated with the menu to enable. 48 | */ 49 | void switch_menu(entt::registry& registry, MenuId menu); 50 | 51 | /** 52 | * \brief Indicates whether the currently active menu is blocking. 53 | * 54 | * \param registry the menu registry. 55 | * 56 | * \return `true` if the menu is blocking; `false` otherwise. 57 | */ 58 | [[nodiscard]] auto is_current_menu_blocking(const entt::registry& registry) -> bool; 59 | 60 | /// \name UI rendering 61 | /// \{ 62 | 63 | /** 64 | * \brief Initializes text labels by rendering their text to textures. 65 | * 66 | * \details This function should be called very early in the render stage, since many 67 | * UI components feature labels that they expect to be initialized by the time they are 68 | * rendered. 69 | * 70 | * \param registry the registry hosting the text labels. 71 | * \param graphics the associated graphics context. 72 | * 73 | * \see `comp::ui_label` 74 | */ 75 | void init_text_labels(const entt::registry& registry, Graphics& graphics); 76 | 77 | /** 78 | * \brief Render the currently active menu. 79 | * 80 | * \param registry the menu registry. 81 | * \param graphics the associated graphics context. 82 | * \param settings the current settings. 83 | */ 84 | void render_active_menu(const entt::registry& registry, 85 | Graphics& graphics, 86 | const Settings& settings); 87 | 88 | void render_button(const entt::registry& registry, 89 | entt::entity buttonEntity, 90 | Graphics& graphics, 91 | const Settings& settings); 92 | 93 | void render_label(const entt::registry& registry, 94 | entt::entity labelEntity, 95 | Graphics& graphics); 96 | 97 | void render_line(const entt::registry& registry, 98 | entt::entity lineEntity, 99 | Graphics& graphics); 100 | 101 | /// \} End of UI rendering 102 | 103 | /// \} End of group ui-system 104 | 105 | } // namespace wanderer::sys 106 | -------------------------------------------------------------------------------- /lib/centurion/centurion/locale.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CENTURION_LOCALE_HPP_ 2 | #define CENTURION_LOCALE_HPP_ 3 | 4 | #include 5 | 6 | #include // assert 7 | #include // size_t 8 | #include // unique_ptr 9 | 10 | #include "detail/sdl_deleter.hpp" 11 | #include "detail/stdlib.hpp" 12 | 13 | namespace cen { 14 | 15 | /** 16 | * \ingroup system 17 | * \defgroup locale Locale 18 | * 19 | * \brief Provides locale information. 20 | */ 21 | 22 | /// \addtogroup locale 23 | /// \{ 24 | 25 | #if SDL_VERSION_ATLEAST(2, 0, 14) 26 | 27 | /** 28 | * \brief Represents a set of locale entries. 29 | * 30 | * \see `SDL_Locale` 31 | */ 32 | class locale final 33 | { 34 | public: 35 | using size_type = std::size_t; 36 | 37 | /** 38 | * \brief Returns the current preferred locales on the system. 39 | * 40 | * \note The preferred locale might change during the execution of the program. 41 | * 42 | * \return the preferred locales on the system. 43 | */ 44 | [[nodiscard]] static auto get_preferred() noexcept -> locale 45 | { 46 | return locale{SDL_GetPreferredLocales()}; 47 | } 48 | 49 | /** 50 | * \brief Indicates whether a language (and optionally a country) is part of the locale. 51 | * 52 | * \param language the language that will be checked, e.g. "en" for english. 53 | * \param country optional country code that will be checked, e.g. "US" or "GB". 54 | * 55 | * \return `true` if the language and country is a part of the locale; `false` otherwise. 56 | */ 57 | [[nodiscard]] auto has_language(const char* language, 58 | const char* country = nullptr) const noexcept -> bool 59 | { 60 | assert(language); 61 | 62 | if (const auto* array = mLocales.get()) { 63 | for (auto index = 0u; array[index].language; ++index) { 64 | const auto& item = array[index]; 65 | 66 | if (country && item.country) { 67 | if (detail::cmp(language, item.language) && detail::cmp(country, item.country)) { 68 | return true; 69 | } 70 | } 71 | else { 72 | if (detail::cmp(language, item.language)) { 73 | return true; 74 | } 75 | } 76 | } 77 | } 78 | 79 | return false; 80 | } 81 | 82 | /** 83 | * \brief Returns the amount of entries in the locale. 84 | * 85 | * \return the locale entry count. 86 | */ 87 | [[nodiscard]] auto size() const noexcept -> size_type 88 | { 89 | size_type result{0}; 90 | 91 | if (const auto* array = mLocales.get()) { 92 | for (auto index = 0u; array[index].language; ++index) { 93 | ++result; 94 | } 95 | } 96 | 97 | return result; 98 | } 99 | 100 | /** 101 | * \brief Indicates whether the locale contains a non-null pointer. 102 | * 103 | * \return `true` if the internal pointer is non-null; `false` otherwise. 104 | */ 105 | explicit operator bool() const noexcept { return mLocales != nullptr; } 106 | 107 | private: 108 | std::unique_ptr mLocales; 109 | 110 | explicit locale(SDL_Locale* locales) noexcept : mLocales{locales} {} 111 | }; 112 | 113 | #endif // SDL_VERSION_ATLEAST(2, 0, 14) 114 | 115 | /// \} End of group locale 116 | 117 | } // namespace cen 118 | 119 | #endif // CENTURION_LOCALE_HPP_ 120 | -------------------------------------------------------------------------------- /lib/centurion/centurion/detail/stdlib.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CENTURION_DETAIL_STDLIB_HPP_ 2 | #define CENTURION_DETAIL_STDLIB_HPP_ 3 | 4 | #include "../common.hpp" 5 | #include "../features.hpp" 6 | 7 | #include // assert 8 | #include // from_chars 9 | #include // lerp 10 | #include // strcmp, strlen 11 | #include // optional, nullopt 12 | #include // stringstream 13 | #include // string 14 | #include // string_view 15 | #include // errc 16 | #include // is_integral_v 17 | 18 | #if CENTURION_HAS_FEATURE_FORMAT 19 | 20 | #include // format 21 | 22 | #endif // CENTURION_HAS_FEATURE_FORMAT 23 | 24 | /// \cond FALSE 25 | 26 | namespace cen::detail { 27 | 28 | /* Clamps a value in the range [min, max] */ 29 | template 30 | [[nodiscard]] constexpr auto clamp(const T& value, 31 | const T& min, 32 | const T& max) noexcept(noexcept(value < min)) -> T 33 | { 34 | assert(min <= max); 35 | if (value < min) { 36 | return min; 37 | } 38 | else if (max < value) { 39 | return max; 40 | } 41 | else { 42 | return value; 43 | } 44 | } 45 | 46 | template 47 | [[nodiscard]] constexpr auto (min)(const T& a, const T& b) noexcept(noexcept(a < b)) -> T 48 | { 49 | return (a < b) ? a : b; 50 | } 51 | 52 | template 53 | [[nodiscard]] constexpr auto (max)(const T& a, const T& b) noexcept(noexcept(a < b)) -> T 54 | { 55 | /* Purposefully use less-than operator, since it's more commonly overloaded */ 56 | return (a < b) ? b : a; 57 | } 58 | 59 | [[nodiscard]] constexpr auto lerp(const float a, const float b, const float bias) noexcept 60 | -> float 61 | { 62 | #if CENTURION_HAS_FEATURE_LERP 63 | return std::lerp(a, b, bias); 64 | #else 65 | return (a * (1.0f - bias)) + (b * bias); 66 | #endif // CENTURION_HAS_FEATURE_LERP 67 | } 68 | 69 | template 70 | [[nodiscard]] auto stoi(const std::string_view str, const int base = 10) noexcept(on_msvc) 71 | -> std::optional 72 | { 73 | /* We don't check if the compiler provides here because all major compilers we 74 | support provide the integral overloads of from_chars */ 75 | 76 | T value{}; 77 | const auto* begin = str.data(); 78 | const auto* end = begin + str.size(); 79 | const auto [ptr, err] = std::from_chars(begin, end, value, base); 80 | if (ptr == end && err == std::errc{}) { 81 | return value; 82 | } 83 | else { 84 | return std::nullopt; 85 | } 86 | } 87 | 88 | [[nodiscard]] inline auto cmp(const char* a, const char* b) noexcept -> bool 89 | { 90 | if (a && b) { 91 | return std::strcmp(a, b) == 0; 92 | } 93 | else { 94 | return false; 95 | } 96 | } 97 | 98 | /* Returns a string that represents the address of the supplied pointer */ 99 | [[nodiscard]] inline auto address_of(const void* ptr) -> std::string 100 | { 101 | #if CENTURION_HAS_FEATURE_FORMAT 102 | return std::format("{}", ptr); 103 | #else 104 | if (ptr) { 105 | std::stringstream stream; 106 | 107 | if constexpr (on_msvc) { 108 | stream << "0x"; // Only MSVC seems to omit this, add it for consistency 109 | } 110 | 111 | stream << ptr; 112 | return stream.str(); 113 | } 114 | else { 115 | return std::string{}; 116 | } 117 | #endif // CENTURION_HAS_FEATURE_FORMAT 118 | } 119 | 120 | } // namespace cen::detail 121 | 122 | /// \endcond 123 | 124 | #endif // CENTURION_DETAIL_STDLIB_HPP_ 125 | -------------------------------------------------------------------------------- /lib/centurion/centurion/endian.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CENTURION_ENDIAN_HPP_ 2 | #define CENTURION_ENDIAN_HPP_ 3 | 4 | #include 5 | 6 | #include "common.hpp" 7 | 8 | namespace cen { 9 | 10 | /** 11 | * \ingroup system 12 | * \defgroup endian Endian 13 | * 14 | * \brief Provides utilities related to the system endianness. 15 | */ 16 | 17 | /// \addtogroup endian 18 | /// \{ 19 | 20 | /** 21 | * \brief Indicates whether the CPU uses little-endian byte ordering. 22 | * 23 | * \return `true` if the CPI is little-endian; `false` otherwise. 24 | */ 25 | [[nodiscard]] constexpr auto is_little_endian() noexcept -> bool 26 | { 27 | return SDL_BYTEORDER == SDL_LIL_ENDIAN; 28 | } 29 | 30 | /** 31 | * \brief Indicates whether the CPU uses big-endian byte ordering. 32 | * 33 | * \return `true` if the CPI is big-endian; `false` otherwise. 34 | */ 35 | [[nodiscard]] constexpr auto is_big_endian() noexcept -> bool 36 | { 37 | return SDL_BYTEORDER == SDL_BIG_ENDIAN; 38 | } 39 | 40 | /** 41 | * \brief Swaps the byte order of an integer. 42 | * 43 | * \param value the value that will be swapped. 44 | * 45 | * \return the swapped integer. 46 | */ 47 | [[nodiscard]] inline auto swap_byte_order(const uint16 value) noexcept -> uint16 48 | { 49 | return SDL_Swap16(value); 50 | } 51 | 52 | /// \copydoc swap_byte_order(uint16) 53 | [[nodiscard]] inline auto swap_byte_order(const uint32 value) noexcept -> uint32 54 | { 55 | return SDL_Swap32(value); 56 | } 57 | 58 | /// \copydoc swap_byte_order(uint32) 59 | [[nodiscard]] inline auto swap_byte_order(const uint64 value) noexcept -> uint64 60 | { 61 | return SDL_Swap64(value); 62 | } 63 | 64 | /** 65 | * \brief Swaps the byte order of a float. 66 | * 67 | * \param value the value that will be swapped. 68 | * 69 | * \return the swapped float. 70 | */ 71 | [[nodiscard]] inline auto swap_byte_order(const float value) noexcept -> float 72 | { 73 | return SDL_SwapFloat(value); 74 | } 75 | 76 | /** 77 | * \brief Swaps the byte order from big endian to the native endianness. 78 | * 79 | * \param value the value that will be swapped. 80 | * 81 | * \return the value in the native endianness. 82 | */ 83 | [[nodiscard]] inline auto swap_big_endian(const uint16 value) noexcept -> uint16 84 | { 85 | return SDL_SwapBE16(value); 86 | } 87 | 88 | /// \copydoc swap_big_endian() 89 | [[nodiscard]] inline auto swap_big_endian(const uint32 value) noexcept -> uint32 90 | { 91 | return SDL_SwapBE32(value); 92 | } 93 | 94 | /// \copydoc swap_big_endian() 95 | [[nodiscard]] inline auto swap_big_endian(const uint64 value) noexcept -> uint64 96 | { 97 | return SDL_SwapBE64(value); 98 | } 99 | 100 | /// \copydoc swap_big_endian() 101 | [[nodiscard]] inline auto swap_big_endian(const float value) noexcept -> float 102 | { 103 | return SDL_SwapFloatBE(value); 104 | } 105 | 106 | /** 107 | * \brief Swaps the byte order from little endian to the native endianness. 108 | * 109 | * \param value the value that will be swapped. 110 | * 111 | * \return the value in the native endianness. 112 | */ 113 | [[nodiscard]] inline auto swap_little_endian(const uint16 value) noexcept -> uint16 114 | { 115 | return SDL_SwapLE16(value); 116 | } 117 | 118 | /// \copydoc swap_little_endian() 119 | [[nodiscard]] inline auto swap_little_endian(const uint32 value) noexcept -> uint32 120 | { 121 | return SDL_SwapLE32(value); 122 | } 123 | 124 | /// \copydoc swap_little_endian() 125 | [[nodiscard]] inline auto swap_little_endian(const uint64 value) noexcept -> uint64 126 | { 127 | return SDL_SwapLE64(value); 128 | } 129 | 130 | /// \copydoc swap_little_endian() 131 | [[nodiscard]] inline auto swap_little_endian(const float value) noexcept -> float 132 | { 133 | return SDL_SwapFloatLE(value); 134 | } 135 | 136 | /// \} End of group endian 137 | 138 | } // namespace cen 139 | 140 | #endif // CENTURION_ENDIAN_HPP_ 141 | -------------------------------------------------------------------------------- /source/wanderer/systems/animation_system.cpp: -------------------------------------------------------------------------------- 1 | #include "animation_system.hpp" 2 | 3 | #include "wanderer/core/centurion_utils.hpp" 4 | #include "wanderer/data/components/rendering.hpp" 5 | #include "wanderer/data/components/tags.hpp" 6 | #include "wanderer/data/direction.hpp" 7 | #include "wanderer/misc/assert.hpp" 8 | 9 | namespace wanderer::sys { 10 | namespace { 11 | 12 | [[nodiscard]] auto _dominant_direction(const uint32 mask) -> uint32 13 | { 14 | if (mask & direction_left_bit) { 15 | return direction_left_bit; 16 | } 17 | else if (mask & direction_right_bit) { 18 | return direction_right_bit; 19 | } 20 | else if (mask & direction_up_bit) { 21 | return direction_up_bit; 22 | } 23 | else if (mask & direction_down_bit) { 24 | return direction_down_bit; 25 | } 26 | else { 27 | return direction_down_bit; 28 | } 29 | } 30 | 31 | } // namespace 32 | 33 | void update_animations(entt::registry& registry) 34 | { 35 | for (auto&& [entity, animation] : registry.view().each()) { 36 | const auto now = cen::ticks64(); 37 | const auto elapsed = now - animation.then; 38 | 39 | const auto frameDuration = animation.delays.size() == 1 40 | ? animation.delays.front() 41 | : animation.delays.at(animation.frame); 42 | 43 | if (elapsed >= frameDuration) { 44 | animation.then = now; 45 | animation.frame = (animation.frame + 1u) % animation.frame_count; 46 | } 47 | } 48 | 49 | for (auto&& [entity, animation, seq, drawable] : 50 | registry.view().each()) { 51 | drawable.src.set_x(static_cast(animation.frame) * seq.frame_size.x); 52 | } 53 | } 54 | 55 | void enter_humanoid_idle_animation(entt::registry& registry, 56 | const entt::entity humanoidEntity, 57 | const uint32 directionMask) 58 | { 59 | WANDERER_ASSERT(humanoidEntity != entt::null); 60 | WANDERER_ASSERT(registry.all_of(humanoidEntity)); 61 | 62 | auto& animation = registry.get(humanoidEntity); 63 | animation.then = cen::ticks64(); 64 | animation.frame = 0; 65 | animation.frame_count = 1; 66 | 67 | using namespace cen::literals::time_literals; 68 | animation.delays.clear(); 69 | animation.delays.push_back(100_ms); 70 | 71 | auto& drawable = registry.get(humanoidEntity); 72 | drawable.src.set_x(0); 73 | drawable.src.set_y(64); // TODO direction 74 | } 75 | 76 | void enter_humanoid_walk_animation(entt::registry& registry, 77 | const entt::entity humanoidEntity, 78 | const uint32 directionMask) 79 | { 80 | WANDERER_ASSERT(humanoidEntity != entt::null); 81 | WANDERER_ASSERT(registry.all_of(humanoidEntity)); 82 | 83 | auto& animation = registry.get(humanoidEntity); 84 | animation.then = cen::ticks64(); 85 | animation.frame = 0; 86 | animation.frame_count = 9; 87 | 88 | using namespace cen::literals::time_literals; 89 | animation.delays.clear(); 90 | animation.delays.push_back(80_ms); 91 | 92 | const auto& seq = registry.get(humanoidEntity); 93 | auto& drawable = registry.get(humanoidEntity); 94 | drawable.src.set_x(0); 95 | 96 | const auto direction = _dominant_direction(directionMask); 97 | if (direction & direction_up_bit) { 98 | drawable.src.set_y(8 * seq.frame_size.y); 99 | } 100 | else if (direction & direction_left_bit) { 101 | drawable.src.set_y(9 * seq.frame_size.y); 102 | } 103 | else if (direction & direction_down_bit) { 104 | drawable.src.set_y(10 * seq.frame_size.y); 105 | } 106 | else if (direction & direction_right_bit) { 107 | drawable.src.set_y(11 * seq.frame_size.y); 108 | } 109 | } 110 | 111 | } // namespace wanderer::sys -------------------------------------------------------------------------------- /source/wanderer/systems/rendering_system.cpp: -------------------------------------------------------------------------------- 1 | #include "rendering_system.hpp" 2 | 3 | #include // clamp 4 | #include // tie 5 | 6 | #include "wanderer/core/centurion_utils.hpp" 7 | #include "wanderer/core/graphics.hpp" 8 | #include "wanderer/core/random.hpp" 9 | #include "wanderer/data/components/rendering.hpp" 10 | #include "wanderer/data/components/world.hpp" 11 | #include "wanderer/misc/exception.hpp" 12 | 13 | namespace wanderer::sys { 14 | 15 | void sort_drawables(entt::registry& registry, const SortStrategy strategy) 16 | { 17 | auto sorter = [&](const entt::entity a, const entt::entity b) { 18 | const auto& lhsDrawable = registry.get(a); 19 | const auto& rhsDrawable = registry.get(b); 20 | 21 | const auto& lhsObject = registry.get(a); 22 | const auto& rhsObject = registry.get(b); 23 | 24 | const auto lhsCenterY = lhsObject.position.y + (lhsObject.size.y / 2.0f); 25 | const auto rhsCenterY = rhsObject.position.y + (rhsObject.size.y / 2.0f); 26 | 27 | return std::tie(lhsDrawable.layer_index, lhsDrawable.depth_index, lhsCenterY) < 28 | std::tie(rhsDrawable.layer_index, rhsDrawable.depth_index, rhsCenterY); 29 | }; 30 | 31 | switch (strategy) { 32 | case SortStrategy::std: 33 | registry.sort(sorter, entt::std_sort{}); 34 | break; 35 | 36 | case SortStrategy::insertion: 37 | registry.sort(sorter, entt::insertion_sort{}); 38 | break; 39 | 40 | default: 41 | throw_traced(WandererError{"Invalid sort strategy!"}); 42 | } 43 | } 44 | 45 | void update_lights(entt::registry& registry) 46 | { 47 | for (auto&& [entity, light] : registry.view().each()) { 48 | light.fluctuation += next_bool() ? light.step_size : -light.step_size; 49 | 50 | const auto min = light.size - light.limit; 51 | const auto max = light.size + light.limit; 52 | 53 | light.fluctuation = std::clamp(light.fluctuation, min, max); 54 | } 55 | } 56 | 57 | void render_drawables(const entt::registry& registry, Graphics& graphics) 58 | { 59 | const auto& viewport = registry.ctx().at(); 60 | const auto viewportRect = as_rect(viewport.offset, viewport.size); 61 | 62 | for (auto&& [entity, drawable] : registry.view().each()) { 63 | const auto& object = registry.get(entity); 64 | cen::frect dest{object.position.x, object.position.y, object.size.x, object.size.y}; 65 | 66 | if (cen::intersects(viewportRect, dest)) { 67 | dest.offset_x(-viewport.offset.x); 68 | dest.offset_y(-viewport.offset.y); 69 | graphics.render_texture(drawable.texture, drawable.src, dest); 70 | } 71 | } 72 | } 73 | 74 | void render_lights(const entt::registry& registry, Graphics& graphics) 75 | { 76 | const auto& viewport = registry.ctx().at(); 77 | const auto viewportRect = as_rect(viewport.offset, viewport.size); 78 | 79 | auto& renderer = graphics.renderer(); 80 | auto& canvas = graphics.get_light_canvas(); 81 | 82 | const auto& date = registry.ctx().at(); 83 | renderer.set_target(canvas); 84 | renderer.clear_with(date.tint); 85 | 86 | for (auto&& [entity, object, light] : 87 | registry.view().each()) { 88 | const auto pos = object.position + light.offset; 89 | const auto size = light.size + light.fluctuation; 90 | 91 | const auto halfSize = size / 2.0f; 92 | cen::frect dest{pos.x - halfSize, pos.y - halfSize, size, size}; 93 | 94 | if (cen::intersects(viewportRect, dest)) { 95 | dest.offset_x(-viewport.offset.x); 96 | dest.offset_y(-viewport.offset.y); 97 | graphics.render_light(dest); 98 | } 99 | } 100 | 101 | renderer.reset_target(); 102 | renderer.render(canvas, cen::ipoint{0, 0}); 103 | } 104 | 105 | } // namespace wanderer::sys -------------------------------------------------------------------------------- /lib/centurion/centurion/vulkan.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CENTURION_VULKAN_HPP_ 2 | #define CENTURION_VULKAN_HPP_ 3 | 4 | #ifndef CENTURION_NO_VULKAN 5 | 6 | #include 7 | #include 8 | 9 | #include // assert 10 | #include // optional, nullopt 11 | #include // vector 12 | 13 | #include "common.hpp" 14 | #include "features.hpp" 15 | #include "math.hpp" 16 | #include "window.hpp" 17 | 18 | namespace cen { 19 | 20 | /// \addtogroup video 21 | /// \{ 22 | 23 | /** 24 | * \defgroup vulkan Vulkan 25 | * 26 | * \brief Contains utilities related to Vulkan. 27 | */ 28 | 29 | /// \addtogroup vulkan 30 | /// \{ 31 | 32 | /** 33 | * \brief Responsible for loading and unloading a Vulkan library. 34 | */ 35 | class vk_library final 36 | { 37 | public: 38 | CENTURION_DISABLE_COPY(vk_library) 39 | CENTURION_DISABLE_MOVE(vk_library) 40 | 41 | /** 42 | * \brief Loads a Vulkan library. 43 | * 44 | * \param path optional file path to a Vulkan library; a null path indicates that the default 45 | * library will be used. 46 | * 47 | * \throws sdl_error if the library cannot be loaded. 48 | */ 49 | CENTURION_NODISCARD_CTOR explicit vk_library(const char* path = nullptr) 50 | { 51 | if (SDL_Vulkan_LoadLibrary(path) == -1) { 52 | throw sdl_error{}; 53 | } 54 | } 55 | 56 | ~vk_library() noexcept { SDL_Vulkan_UnloadLibrary(); } 57 | }; 58 | 59 | /// \} End of group vulkan 60 | 61 | /// \} End of group video 62 | 63 | /// \ingroup vulkan 64 | namespace vk { 65 | 66 | /// \addtogroup vulkan 67 | /// \{ 68 | 69 | /** 70 | * \brief Returns the address of the `vkGetInstanceProcAddr` function. 71 | * 72 | * \return the `vkGetInstanceProcAddr` address. 73 | */ 74 | [[nodiscard]] inline auto get_instance_proc_addr() noexcept -> void* 75 | { 76 | return SDL_Vulkan_GetVkGetInstanceProcAddr(); 77 | } 78 | 79 | /** 80 | * \brief Creates a rendering surface for a Vulkan window. 81 | * 82 | * \pre `window` must be a Vulkan window. 83 | * 84 | * \param window the Vulkan window. 85 | * \param instance the current Vulkan instance. 86 | * \param[out] outSurface the handle that will receive the handle of the created surface. 87 | * 88 | * \return `success` if the surface was successfully created; `failure` otherwise. 89 | */ 90 | template 91 | auto create_surface(basic_window& window, 92 | VkInstance instance, 93 | VkSurfaceKHR* outSurface) noexcept -> result 94 | { 95 | assert(window.is_vulkan()); 96 | return SDL_Vulkan_CreateSurface(window.get(), instance, outSurface) == SDL_TRUE; 97 | } 98 | 99 | /** 100 | * \brief Returns the extensions required to create a Vulkan surface. 101 | * 102 | * \return the required Vulkan extensions; an empty optional is returned if something goes 103 | * wrong. 104 | */ 105 | inline auto required_extensions() -> std::optional> 106 | { 107 | uint count{}; 108 | if (!SDL_Vulkan_GetInstanceExtensions(nullptr, &count, nullptr)) { 109 | return std::nullopt; 110 | } 111 | 112 | std::vector names(count); 113 | if (!SDL_Vulkan_GetInstanceExtensions(nullptr, &count, names.data())) { 114 | return std::nullopt; 115 | } 116 | 117 | return names; 118 | } 119 | 120 | /** 121 | * \brief Returns the size of the drawable surface associated with the window. 122 | * 123 | * \pre `window` must be a Vulkan window. 124 | * 125 | * \param window the Vulkan window that will be queried. 126 | * 127 | * \return the size of the window drawable. 128 | */ 129 | template 130 | [[nodiscard]] auto drawable_size(const basic_window& window) noexcept -> iarea 131 | { 132 | assert(window.is_vulkan()); 133 | 134 | int width{}; 135 | int height{}; 136 | SDL_Vulkan_GetDrawableSize(window.get(), &width, &height); 137 | 138 | return {width, height}; 139 | } 140 | 141 | /// \} End of group vulkan 142 | 143 | } // namespace vk 144 | } // namespace cen 145 | 146 | #endif // CENTURION_NO_VULKAN 147 | #endif // CENTURION_VULKAN_HPP_ 148 | -------------------------------------------------------------------------------- /source/wanderer/systems/time_system.cpp: -------------------------------------------------------------------------------- 1 | #include "time_system.hpp" 2 | 3 | #include // floor, ceil 4 | #include // vector 5 | 6 | #include "wanderer/data/components/world.hpp" 7 | #include "wanderer/misc/logging.hpp" 8 | 9 | namespace wanderer::sys { 10 | namespace { 11 | 12 | constexpr float32 _time_rate = 5'000; 13 | 14 | constexpr float32 _sunrise_hour = 6; 15 | constexpr float32 _daytime_hour = 9; 16 | constexpr float32 _sunset_hour = 18; 17 | constexpr float32 _night_hour = 21; 18 | 19 | struct day_phase final 20 | { 21 | float32 start_hour{}; 22 | float32 end_hour{}; 23 | std::vector colors; 24 | }; 25 | 26 | constexpr auto _black = cen::colors::black; 27 | constexpr auto _orange = cen::colors::orange; 28 | constexpr auto _navy = cen::colors::navy; 29 | 30 | const auto _sunrise_color = cen::blend(_black, _orange, 0.5f).with_alpha(0x20); 31 | const auto _sunrise_end_color = cen::blend(_black, _orange, 0.25f).with_alpha(0x20); 32 | 33 | const auto _day_color = _black.with_alpha(0); 34 | const auto _day_end_color = cen::blend(_black, _orange, 0.25f).with_alpha(0x20); 35 | 36 | const auto _sunset_color = cen::blend(_black, _orange, 0.5f).with_alpha(0x20); 37 | const auto _night_color = cen::blend(_black, _navy, 0.3f).with_alpha(0xDD); 38 | 39 | const day_phase _sunrise_phase{_sunrise_hour, 40 | _daytime_hour, 41 | {_night_color, _sunrise_color, _sunrise_end_color}}; 42 | 43 | const day_phase _day_phase{_daytime_hour, 44 | _sunset_hour, 45 | {_sunrise_end_color, _day_color, _day_end_color}}; 46 | 47 | const day_phase _sunset_phase{_sunset_hour, 48 | _night_hour, 49 | {_day_end_color, _sunset_color, _night_color}}; 50 | 51 | /* Night phase can only have one color */ 52 | const day_phase _night_phase{_night_hour, _sunrise_hour, {_night_color}}; 53 | 54 | [[nodiscard]] auto _get_tint(const day_phase& current, const float32 hour) -> cen::color 55 | { 56 | if (current.colors.size() == 1) { 57 | return current.colors.at(0); 58 | } 59 | else { 60 | const auto now = hour - current.start_hour; 61 | const auto duration = current.end_hour - current.start_hour; 62 | const auto progression = now / duration; 63 | 64 | const auto lastIndex = static_cast(cen::isize(current.colors)) - 1.0f; 65 | const auto index = progression * lastIndex; 66 | const auto indexLower = std::floor(index); 67 | const auto indexUpper = std::ceil(index); 68 | 69 | const auto c1 = current.colors.at(static_cast(indexLower)); 70 | const auto c2 = current.colors.at(static_cast(indexUpper)); 71 | 72 | return cen::blend(c1, c2, index - indexLower); 73 | } 74 | } 75 | 76 | [[nodiscard]] auto _get_phase(const float32 hour) -> const day_phase& 77 | { 78 | if (hour > _sunrise_hour && hour <= _daytime_hour) { 79 | return _sunrise_phase; 80 | } 81 | else if (hour > _daytime_hour && hour <= _sunset_hour) { 82 | return _day_phase; 83 | } 84 | else if (hour > _sunset_hour && hour <= _night_hour) { 85 | return _sunset_phase; 86 | } 87 | else { 88 | return _night_phase; 89 | } 90 | } 91 | 92 | [[nodiscard]] constexpr auto _day_after(const DayOfWeek day) noexcept -> DayOfWeek 93 | { 94 | return DayOfWeek{(cen::to_underlying(day) + 1) % 7}; 95 | } 96 | 97 | } // namespace 98 | 99 | void update_time(entt::registry& registry, entt::dispatcher& dispatcher, const float32 dt) 100 | { 101 | auto& date = registry.ctx().at(); 102 | 103 | date.seconds += _time_rate * dt; 104 | date.minute = date.seconds / 60.0f; 105 | date.hour = date.minute / 60.0f; 106 | 107 | const auto& phase = _get_phase(date.hour); 108 | date.tint = _get_tint(phase, date.hour); 109 | 110 | if (date.hour >= 24) { 111 | date.day = _day_after(date.day); 112 | date.hour = 0; 113 | date.minute = 0; 114 | date.seconds = 0; 115 | 116 | debug("Changing day to '{}'", full_name(date.day)); 117 | 118 | // TODO emit event 119 | } 120 | } 121 | 122 | } // namespace wanderer::sys -------------------------------------------------------------------------------- /source/wanderer/systems/physics_system.cpp: -------------------------------------------------------------------------------- 1 | #include "physics_system.hpp" 2 | 3 | #include "wanderer/core/centurion_utils.hpp" 4 | #include "wanderer/core/graphics.hpp" 5 | #include "wanderer/core/math.hpp" 6 | #include "wanderer/data/cfg.hpp" 7 | #include "wanderer/data/components/rendering.hpp" 8 | #include "wanderer/data/components/world.hpp" 9 | #include "wanderer/misc/assert.hpp" 10 | 11 | namespace wanderer::sys { 12 | namespace { 13 | 14 | /* These are the values recommended in the Box2D docs, so they should be fine! */ 15 | constexpr int32 _n_velocity_iterations = 8; 16 | constexpr int32 _n_position_iterations = 3; 17 | 18 | } // namespace 19 | 20 | void add_physics_body(entt::registry& registry, 21 | const entt::entity entity, 22 | const b2BodyType type, 23 | const glm::vec2& logicalPos, 24 | const glm::vec2& logicalSize, 25 | const float32 maxSpeed, 26 | const glm::vec2& offset) 27 | { 28 | WANDERER_ASSERT(entity != entt::null); 29 | WANDERER_ASSERT(!registry.all_of(entity)); 30 | 31 | auto& world = registry.ctx().at(); 32 | 33 | b2BodyDef bodyDef; 34 | bodyDef.position = sys::to_physics_scale(registry, logicalPos + offset); 35 | bodyDef.type = type; 36 | bodyDef.fixedRotation = true; 37 | bodyDef.gravityScale = 0; 38 | 39 | auto& body = registry.emplace(entity); 40 | body.data = world.simulation.CreateBody(&bodyDef); 41 | body.offset = sys::to_physics_scale(registry, offset); 42 | body.size = sys::to_physics_scale(registry, logicalSize); 43 | body.max_speed = maxSpeed; 44 | 45 | const b2Vec2 center{body.size.x / 2.0f, body.size.y / 2.0f}; 46 | 47 | b2PolygonShape shape; 48 | shape.SetAsBox(body.size.x / 2.0f, body.size.y / 2.0f, center, 0); 49 | 50 | b2FixtureDef fixtureDef; 51 | fixtureDef.shape = &shape; 52 | fixtureDef.density = (type == b2_dynamicBody) ? 1.0f : 0.0f; 53 | fixtureDef.friction = 0; 54 | 55 | body.data->CreateFixture(&fixtureDef); 56 | } 57 | 58 | void on_destroy_physics_object(entt::registry& registry, const entt::entity entity) 59 | { 60 | WANDERER_ASSERT(entity != entt::null); 61 | WANDERER_ASSERT(registry.all_of(entity)); 62 | 63 | auto& world = registry.ctx().at(); 64 | auto& body = registry.get(entity); 65 | 66 | world.simulation.DestroyBody(body.data); 67 | } 68 | 69 | void update_physics(entt::registry& registry, const float32 dt) 70 | { 71 | auto& world = registry.ctx().at(); 72 | world.simulation.Step(dt, _n_velocity_iterations, _n_position_iterations); 73 | 74 | for (auto&& [entity, object, body] : 75 | registry.view().each()) { 76 | auto pos = glmx::from_b2(body.data->GetPosition() - body.offset); 77 | object.position = pos / world.scale; 78 | } 79 | } 80 | 81 | void debug_physics(const entt::registry& registry, Graphics& graphics) 82 | { 83 | const auto& viewport = registry.ctx().at(); 84 | const auto viewportRect = as_rect(viewport.offset, viewport.size); 85 | 86 | auto& renderer = graphics.renderer(); 87 | renderer.set_color(cen::colors::magenta); 88 | 89 | for (auto&& [entity, body] : registry.view().each()) { 90 | const auto position = to_logical_scale(registry, body.data->GetPosition()); 91 | const auto size = to_logical_scale(registry, body.size); 92 | 93 | cen::frect hitbox{position.x, position.y, size.x, size.y}; 94 | if (cen::intersects(viewportRect, hitbox)) { 95 | hitbox.offset_x(-viewport.offset.x); 96 | hitbox.offset_y(-viewport.offset.y); 97 | 98 | renderer.draw_rect(hitbox); 99 | } 100 | } 101 | } 102 | 103 | auto to_physics_scale(const entt::registry& registry, const glm::vec2& vec) -> b2Vec2 104 | { 105 | const auto& physics = registry.ctx().at(); 106 | return {vec.x * physics.scale.x, vec.y * physics.scale.y}; 107 | } 108 | 109 | auto to_logical_scale(const entt::registry& registry, const b2Vec2& vec) -> glm::vec2 110 | { 111 | const auto& physics = registry.ctx().at(); 112 | return {vec.x / physics.scale.x, vec.y / physics.scale.y}; 113 | } 114 | 115 | } // namespace wanderer::sys -------------------------------------------------------------------------------- /source/wanderer/systems/tile_system.cpp: -------------------------------------------------------------------------------- 1 | #include "tile_system.hpp" 2 | 3 | #include "wanderer/core/centurion_utils.hpp" 4 | #include "wanderer/core/graphics.hpp" 5 | #include "wanderer/data/cfg.hpp" 6 | #include "wanderer/data/components/rendering.hpp" 7 | #include "wanderer/data/components/tiles.hpp" 8 | #include "wanderer/misc/assert.hpp" 9 | 10 | namespace wanderer::sys { 11 | namespace { 12 | 13 | [[nodiscard]] auto _tile_to_render(const entt::registry& registry, 14 | const comp::Tileset& tileset, 15 | const entt::entity tileEntity) -> entt::entity 16 | { 17 | if (const auto* animation = registry.try_get(tileEntity)) { 18 | if (const auto iter = tileset.effective_appearance.find(tileEntity); 19 | iter == tileset.effective_appearance.end()) { 20 | const auto& tileAnimation = registry.get(tileEntity); 21 | 22 | const auto tileToRenderId = tileAnimation.frames.at(animation->frame); 23 | const auto tileToRenderEntity = tileset.tiles.at(tileToRenderId); 24 | tileset.effective_appearance[tileEntity] = tileToRenderEntity; 25 | 26 | return tileToRenderEntity; 27 | } 28 | else { 29 | /* In this case, we have already computed the tile to render for this tile */ 30 | return iter->second; 31 | } 32 | } 33 | else { 34 | return tileEntity; 35 | } 36 | } 37 | 38 | void _render_tile(const entt::registry& registry, 39 | const comp::Tileset& tileset, 40 | const entt::entity tileEntity, 41 | const glm::vec4& dest, 42 | Graphics& graphics) 43 | { 44 | const auto tileToRenderEntity = _tile_to_render(registry, tileset, tileEntity); 45 | const auto tileToRender = registry.get(tileToRenderEntity); 46 | graphics.render_texture(tileToRender.texture, tileToRender.source, dest); 47 | } 48 | 49 | [[nodiscard]] auto _determine_destination(const usize row, 50 | const usize col, 51 | const glm::vec2& tileSize, 52 | const glm::vec2& viewportOffset) noexcept 53 | -> glm::vec4 54 | { 55 | glm::vec4 dest; 56 | dest.x = (static_cast(col) * tileSize.x) - viewportOffset.x; 57 | dest.y = (static_cast(row) * tileSize.y) - viewportOffset.y; 58 | dest.z = tileSize.x; 59 | dest.w = tileSize.y; 60 | return dest; 61 | } 62 | 63 | } // namespace 64 | 65 | void clear_effective_appearance_tile_cache(entt::registry& registry) 66 | { 67 | auto& tileset = registry.ctx().at(); 68 | tileset.effective_appearance.clear(); 69 | } 70 | 71 | void update_tile_objects(entt::registry& registry) 72 | { 73 | const auto& tileset = registry.ctx().at(); 74 | for (auto&& [entity, object] : registry.view().each()) { 75 | const auto renderedTileEntity = 76 | _tile_to_render(registry, tileset, object.tile_entity); 77 | if (renderedTileEntity != object.tile_entity) { 78 | const auto& tile = registry.get(renderedTileEntity); 79 | auto& drawable = registry.get(entity); 80 | drawable.src = as_rect(tile.source); 81 | } 82 | } 83 | } 84 | 85 | void render_tiles(const entt::registry& registry, Graphics& graphics) 86 | { 87 | const auto& cfg = registry.ctx().at(); 88 | const auto& bounds = registry.ctx().at(); 89 | const auto& viewport = registry.ctx().at(); 90 | const auto& tileset = registry.ctx().at(); 91 | 92 | for (auto&& [entity, layer] : registry.view().each()) { 93 | for (usize row = bounds.begin_row; row < bounds.end_row; ++row) { 94 | for (usize col = bounds.begin_col; col < bounds.end_col; ++col) { 95 | WANDERER_ASSERT(row < layer.tiles.size()); 96 | WANDERER_ASSERT(col < layer.tiles[0].size()); 97 | const auto tileId = layer.tiles[row][col]; 98 | 99 | if (tileId == empty_tile) { 100 | continue; 101 | } 102 | 103 | const auto dest = 104 | _determine_destination(row, col, cfg.tile_size, viewport.offset); 105 | 106 | const auto tileEntity = tileset.tiles.at(tileId); 107 | _render_tile(registry, tileset, tileEntity, dest, graphics); 108 | } 109 | } 110 | } 111 | } 112 | 113 | } // namespace wanderer::sys -------------------------------------------------------------------------------- /lib/centurion/centurion/window_events.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CENTURION_WINDOW_EVENTS_HPP_ 2 | #define CENTURION_WINDOW_EVENTS_HPP_ 3 | 4 | #include 5 | 6 | #include "common.hpp" 7 | #include "event_base.hpp" 8 | 9 | namespace cen { 10 | 11 | /// \addtogroup event 12 | /// \{ 13 | 14 | enum class window_event_id 15 | { 16 | none = SDL_WINDOWEVENT_NONE, 17 | shown = SDL_WINDOWEVENT_SHOWN, 18 | hidden = SDL_WINDOWEVENT_HIDDEN, 19 | exposed = SDL_WINDOWEVENT_EXPOSED, 20 | moved = SDL_WINDOWEVENT_MOVED, 21 | resized = SDL_WINDOWEVENT_RESIZED, 22 | size_changed = SDL_WINDOWEVENT_SIZE_CHANGED, 23 | minimized = SDL_WINDOWEVENT_MINIMIZED, 24 | maximized = SDL_WINDOWEVENT_MAXIMIZED, 25 | restored = SDL_WINDOWEVENT_RESTORED, 26 | enter = SDL_WINDOWEVENT_ENTER, 27 | leave = SDL_WINDOWEVENT_LEAVE, 28 | focus_gained = SDL_WINDOWEVENT_FOCUS_GAINED, 29 | focus_lost = SDL_WINDOWEVENT_FOCUS_LOST, 30 | close = SDL_WINDOWEVENT_CLOSE, 31 | take_focus = SDL_WINDOWEVENT_TAKE_FOCUS, 32 | 33 | #if SDL_VERSION_ATLEAST(2, 0, 18) 34 | display_changed = SDL_WINDOWEVENT_DISPLAY_CHANGED, 35 | icc_profile_changed = SDL_WINDOWEVENT_ICCPROF_CHANGED, 36 | #endif // SDL_VERSION_ATLEAST(2, 0, 18) 37 | 38 | hit_test = SDL_WINDOWEVENT_HIT_TEST 39 | }; 40 | 41 | /// \name Window event ID functions 42 | /// \{ 43 | 44 | [[nodiscard]] constexpr auto to_string(const window_event_id id) -> std::string_view 45 | { 46 | switch (id) { 47 | case window_event_id::none: 48 | return "none"; 49 | 50 | case window_event_id::shown: 51 | return "shown"; 52 | 53 | case window_event_id::hidden: 54 | return "hidden"; 55 | 56 | case window_event_id::exposed: 57 | return "exposed"; 58 | 59 | case window_event_id::moved: 60 | return "moved"; 61 | 62 | case window_event_id::resized: 63 | return "resized"; 64 | 65 | case window_event_id::size_changed: 66 | return "size_changed"; 67 | 68 | case window_event_id::minimized: 69 | return "minimized"; 70 | 71 | case window_event_id::maximized: 72 | return "maximized"; 73 | 74 | case window_event_id::restored: 75 | return "restored"; 76 | 77 | case window_event_id::enter: 78 | return "enter"; 79 | 80 | case window_event_id::leave: 81 | return "leave"; 82 | 83 | case window_event_id::focus_gained: 84 | return "focus_gained"; 85 | 86 | case window_event_id::focus_lost: 87 | return "focus_lost"; 88 | 89 | case window_event_id::close: 90 | return "close"; 91 | 92 | case window_event_id::take_focus: 93 | return "take_focus"; 94 | 95 | case window_event_id::hit_test: 96 | return "hit_test"; 97 | 98 | #if SDL_VERSION_ATLEAST(2, 0, 18) 99 | 100 | case window_event_id::display_changed: 101 | return "display_changed"; 102 | 103 | case window_event_id::icc_profile_changed: 104 | return "icc_profile_changed"; 105 | 106 | #endif // SDL_VERSION_ATLEAST(2, 0, 18) 107 | 108 | default: 109 | throw exception{"Did not recognize window event ID!"}; 110 | } 111 | } 112 | 113 | inline auto operator<<(std::ostream& stream, const window_event_id id) -> std::ostream& 114 | { 115 | return stream << to_string(id); 116 | } 117 | 118 | /// \} End of window event ID functions 119 | 120 | class window_event final : public event_base 121 | { 122 | public: 123 | window_event() : event_base{event_type::window} {} 124 | 125 | explicit window_event(const SDL_WindowEvent& event) noexcept : event_base{event} {} 126 | 127 | void set_event_id(const window_event_id id) noexcept 128 | { 129 | mEvent.event = static_cast(id); 130 | } 131 | 132 | void set_data1(const int32 value) noexcept { mEvent.data1 = value; } 133 | 134 | void set_data2(const int32 value) noexcept { mEvent.data2 = value; } 135 | 136 | [[nodiscard]] auto event_id() const noexcept -> window_event_id 137 | { 138 | return static_cast(mEvent.event); 139 | } 140 | 141 | [[nodiscard]] auto data1() const noexcept -> int32 { return mEvent.data1; } 142 | 143 | [[nodiscard]] auto data2() const noexcept -> int32 { return mEvent.data2; } 144 | }; 145 | 146 | template <> 147 | inline auto as_sdl_event(const event_base& event) -> SDL_Event 148 | { 149 | SDL_Event e; 150 | e.window = event.get(); 151 | return e; 152 | } 153 | 154 | /// \} End of group event 155 | 156 | } // namespace cen 157 | 158 | #endif // CENTURION_WINDOW_EVENTS_HPP_ 159 | -------------------------------------------------------------------------------- /source/wanderer/data/components/ui.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // string 4 | #include // vector 5 | 6 | #include 7 | #include 8 | 9 | #include "wanderer/common.hpp" 10 | #include "wanderer/core/action.hpp" 11 | #include "wanderer/core/graphics.hpp" 12 | #include "wanderer/data/menu_id.hpp" 13 | 14 | namespace wanderer { 15 | 16 | enum class HAnchor 17 | { 18 | left, 19 | center, 20 | right 21 | }; 22 | 23 | enum class VAnchor 24 | { 25 | top, 26 | center, 27 | bottom 28 | }; 29 | 30 | namespace comp { 31 | 32 | /// \addtogroup components 33 | /// \{ 34 | 35 | /** 36 | * \brief Represents key binds in menus. 37 | */ 38 | struct UiBind final 39 | { 40 | /** 41 | * \brief The scan code that triggers the action event. 42 | */ 43 | cen::scan_code key; 44 | 45 | /** 46 | * \brief The action emitted when the key bind is triggered. 47 | */ 48 | Action action{Action::noop}; 49 | }; 50 | 51 | /** 52 | * \brief Provides information about how to position UI components. 53 | */ 54 | struct UiAnchor final 55 | { 56 | HAnchor horizontal{HAnchor::left}; 57 | VAnchor vertical{VAnchor::top}; 58 | }; 59 | 60 | /** 61 | * \brief Represents a line between two points for decorative use. 62 | */ 63 | struct UiLine final 64 | { 65 | glm::vec2 start{}; ///< The start point. 66 | glm::vec2 end{}; ///< The end point. 67 | cen::color color{cen::colors::white}; ///< The color of the line. 68 | }; 69 | 70 | /** 71 | * \brief Represents a simple text label. 72 | */ 73 | struct UiLabel final 74 | { 75 | /** 76 | * \brief The normalized offset from the anchor position. 77 | */ 78 | glm::vec2 offset{}; 79 | 80 | /** 81 | * \brief The label text (cannot be empty). 82 | */ 83 | std::string text; 84 | 85 | /** 86 | * \brief The foreground text color. 87 | */ 88 | cen::color color; 89 | 90 | /** 91 | * \brief Size of the font used. 92 | */ 93 | FontSize size{}; 94 | 95 | /** 96 | * \brief The lazily initialized label screen position (computed frame-by-frame). 97 | */ 98 | mutable maybe position; 99 | 100 | /** 101 | * \brief The lazily initialized texture that holds the rendered label text. 102 | */ 103 | mutable maybe texture; 104 | }; 105 | 106 | struct UiButton final 107 | { 108 | inline constexpr static uint32 hover_bit = 1u << 1u; 109 | 110 | /** 111 | * \brief Bit mask of button state. 112 | */ 113 | uint32 state{}; 114 | 115 | /** 116 | * \brief The action associated with button pressed. 117 | * 118 | * \details An `action_event` with this action is emitted when the button is activated. 119 | */ 120 | Action action{}; 121 | 122 | /** 123 | * \brief Lazily initialized position, based on label attributes. 124 | */ 125 | mutable maybe position; 126 | 127 | /** 128 | * \brief The button size (determined when the text label has been initialized). 129 | */ 130 | mutable maybe size; 131 | }; 132 | 133 | /** 134 | * \brief Component used by toggle buttons for settings. 135 | */ 136 | struct UiSettingsToggle final 137 | { 138 | uint64 flag{}; ///< Boolean setting flag. 139 | }; 140 | 141 | struct UiMenu final 142 | { 143 | /** 144 | * \brief Menu title shown in a large font (can be empty). 145 | */ 146 | std::string title; 147 | 148 | /** 149 | * \brief All button entities associated with the menu. 150 | */ 151 | std::vector buttons; 152 | 153 | /** 154 | * \brief All label entities associated with the menu. 155 | */ 156 | std::vector labels; 157 | 158 | /** 159 | * \brief All key bind entities associated with the menu. 160 | */ 161 | std::vector binds; 162 | 163 | /** 164 | * \brief All line entities associated with the menu. 165 | */ 166 | std::vector lines; 167 | 168 | /** 169 | * \brief Determines whether the menu should block game updates whilst active. 170 | */ 171 | bool blocking{}; 172 | }; 173 | 174 | /** 175 | * \brief Context component used to keep track of all available menus. 176 | */ 177 | struct UiMenus final 178 | { 179 | /** 180 | * \brief The currently active menu entity (shouldn't ever be null). 181 | */ 182 | entt::entity active_menu{entt::null}; 183 | 184 | /** 185 | * \brief Maps menu identifiers to menu entities. 186 | */ 187 | hash_map menus; 188 | }; 189 | 190 | /// \} End of group components 191 | 192 | } // namespace comp 193 | } // namespace wanderer -------------------------------------------------------------------------------- /source/wanderer/systems/cinematic_system.cpp: -------------------------------------------------------------------------------- 1 | #include "cinematic_system.hpp" 2 | 3 | #include 4 | 5 | #include "ui_system.hpp" 6 | #include "wanderer/core/centurion_utils.hpp" 7 | #include "wanderer/core/graphics.hpp" 8 | #include "wanderer/data/cfg.hpp" 9 | #include "wanderer/data/components/rendering.hpp" 10 | #include "wanderer/data/components/ui.hpp" 11 | #include "wanderer/meta/build.hpp" 12 | 13 | using namespace cen::literals::time_literals; 14 | 15 | namespace wanderer::sys { 16 | namespace { 17 | 18 | [[nodiscard]] auto _determine_cinematic_text_alpha(const comp::CinematicFade& cinematic) 19 | -> uint8 20 | { 21 | auto asFloat = [](auto duration) { return static_cast(duration.count()); }; 22 | 23 | const auto duration = cen::ticks64() - cinematic.start; 24 | 25 | if (duration <= cinematic.transition) { 26 | const auto ratio = asFloat(duration) / asFloat(cinematic.transition); 27 | return static_cast(ratio * 255.0); 28 | } 29 | else if (duration > cinematic.transition && 30 | duration <= cinematic.transition + cinematic.pause) { 31 | return 255; 32 | } 33 | else { 34 | const auto progress = duration - (cinematic.transition + cinematic.pause); 35 | const auto ratio = asFloat(progress) / asFloat(cinematic.transition); 36 | return 255 - static_cast(ratio * 255.0); 37 | } 38 | } 39 | 40 | } // namespace 41 | 42 | void schedule_startup_cinematic_fade(entt::registry& registry) 43 | { 44 | auto& cinematic = registry.ctx().emplace(); 45 | cinematic.start = cen::ticks64(); 46 | if constexpr (is_release_build) { 47 | cinematic.transition = 750_ms; 48 | cinematic.pause = 1'500_ms; 49 | } 50 | else { 51 | cinematic.transition = 200_ms; 52 | cinematic.pause = 400_ms; 53 | } 54 | cinematic.bg = cen::colors::black; 55 | 56 | { 57 | const auto titleEntity = cinematic.labels.emplace_back(registry.create()); 58 | auto& title = registry.emplace(titleEntity); 59 | title.offset = {0, -0.1f}; 60 | title.text = "Wanderer"; 61 | title.color = cen::colors::white; 62 | title.size = FontSize::huge; 63 | 64 | auto& anchor = registry.emplace(titleEntity); 65 | anchor.horizontal = HAnchor::center; 66 | anchor.vertical = VAnchor::center; 67 | } 68 | 69 | { 70 | const auto labelEntity = cinematic.labels.emplace_back(registry.create()); 71 | auto& text = registry.emplace(labelEntity); 72 | text.offset = {0, 0}; 73 | text.text = "A game by Albin Johansson"; 74 | text.color = cen::colors::white; 75 | text.size = FontSize::medium; 76 | 77 | auto& anchor = registry.emplace(labelEntity); 78 | anchor.horizontal = HAnchor::center; 79 | anchor.vertical = VAnchor::center; 80 | } 81 | 82 | { 83 | const auto labelEntity = cinematic.labels.emplace_back(registry.create()); 84 | auto& text = registry.emplace(labelEntity); 85 | text.offset = {0.01f, 0.01f}; 86 | text.text = wanderer_version; 87 | text.color = cen::color{0x50, 0x50, 0x50}; 88 | text.size = FontSize::medium; 89 | 90 | auto& anchor = registry.emplace(labelEntity); 91 | anchor.horizontal = HAnchor::left; 92 | anchor.vertical = VAnchor::bottom; 93 | } 94 | } 95 | 96 | void update_cinematic_fade(entt::registry& registry) 97 | { 98 | if (auto* cinematic = registry.ctx().find()) { 99 | const auto diff = cen::ticks64() - cinematic->start; 100 | const auto total = (cinematic->transition * 2u) + cinematic->pause; 101 | if (diff >= total) { 102 | for (const auto labelEntity : cinematic->labels) { 103 | registry.destroy(labelEntity); 104 | } 105 | 106 | registry.ctx().erase(); 107 | } 108 | } 109 | } 110 | 111 | void render_cinematic_fade(const entt::registry& registry, Graphics& graphics) 112 | { 113 | if (const auto* cinematic = registry.ctx().find()) { 114 | graphics.renderer().fill_with(cinematic->bg); 115 | 116 | const auto alpha = _determine_cinematic_text_alpha(*cinematic); 117 | 118 | for (const auto labelEntity : cinematic->labels) { 119 | const auto& label = registry.get(labelEntity); 120 | label.texture.value().set_alpha_mod(alpha); 121 | render_label(registry, labelEntity, graphics); 122 | } 123 | } 124 | } 125 | 126 | auto is_cinematic_fade_active(const entt::registry& registry) -> bool 127 | { 128 | return registry.ctx().find() != nullptr; 129 | } 130 | 131 | } // namespace wanderer::sys -------------------------------------------------------------------------------- /source/wanderer/systems/input_system.cpp: -------------------------------------------------------------------------------- 1 | #include "input_system.hpp" 2 | 3 | #include "animation_system.hpp" 4 | #include "physics_system.hpp" 5 | #include "wanderer/core/input_state.hpp" 6 | #include "wanderer/core/math.hpp" 7 | #include "wanderer/data/components/tags.hpp" 8 | #include "wanderer/data/components/world.hpp" 9 | #include "wanderer/events/player_events.hpp" 10 | 11 | namespace wanderer::sys { 12 | namespace { 13 | 14 | // TODO make customizable 15 | constexpr cen::scan_code move_up_key = cen::scancodes::w; 16 | constexpr cen::scan_code move_down_key = cen::scancodes::s; 17 | constexpr cen::scan_code move_right_key = cen::scancodes::d; 18 | constexpr cen::scan_code move_left_key = cen::scancodes::a; 19 | 20 | } // namespace 21 | 22 | void update_input(entt::dispatcher& dispatcher, const InputState& input) 23 | { 24 | const auto moveUp = input.is_pressed(move_up_key); 25 | const auto moveDown = input.is_pressed(move_down_key); 26 | const auto moveLeft = input.is_pressed(move_left_key); 27 | const auto moveRight = input.is_pressed(move_right_key); 28 | 29 | uint32 mask = 0; 30 | 31 | if (moveUp && moveDown) { 32 | dispatcher.enqueue(direction_up_and_down); 33 | } 34 | else if (moveUp) { 35 | mask |= direction_up_bit; 36 | } 37 | else if (moveDown) { 38 | mask |= direction_down_bit; 39 | } 40 | 41 | if (moveLeft && moveRight) { 42 | dispatcher.enqueue(direction_left_and_right); 43 | } 44 | else if (moveLeft) { 45 | mask |= direction_left_bit; 46 | } 47 | else if (moveRight) { 48 | mask |= direction_right_bit; 49 | } 50 | 51 | if (mask != 0) { 52 | dispatcher.enqueue(mask); 53 | } 54 | 55 | if (input.was_released(move_up_key)) { 56 | dispatcher.enqueue(direction_up_bit); 57 | } 58 | 59 | if (input.was_released(move_down_key)) { 60 | dispatcher.enqueue(direction_down_bit); 61 | } 62 | 63 | if (input.was_released(move_left_key)) { 64 | dispatcher.enqueue(direction_left_bit); 65 | } 66 | 67 | if (input.was_released(move_right_key)) { 68 | dispatcher.enqueue(direction_right_bit); 69 | } 70 | } 71 | 72 | void on_move_player(entt::registry& registry, const MovePlayerEvent& event) 73 | { 74 | const auto entity = registry.view().front(); 75 | auto& body = registry.get(entity); 76 | 77 | auto velocity = body.data->GetLinearVelocity(); 78 | const auto prevVelocity = glmx::from_b2(velocity); 79 | 80 | if (event.direction_mask & direction_up_bit) { 81 | velocity.y = -body.max_speed; 82 | } 83 | else if (event.direction_mask & direction_down_bit) { 84 | velocity.y = body.max_speed; 85 | } 86 | 87 | if (event.direction_mask & direction_right_bit) { 88 | velocity.x = body.max_speed; 89 | } 90 | else if (event.direction_mask & direction_left_bit) { 91 | velocity.x = -body.max_speed; 92 | } 93 | 94 | auto limited = glmx::from_b2(velocity); 95 | glmx::cap_magnitude(limited, body.max_speed); 96 | 97 | /* Only update animation states if the direction changed */ 98 | if (!glmx::is_zero(limited) && !glmx::is_zero(prevVelocity - limited)) { 99 | uint32 mask{}; 100 | 101 | if (limited.x > 0) { 102 | mask |= direction_right_bit; 103 | } 104 | else if (limited.x < 0) { 105 | mask |= direction_left_bit; 106 | } 107 | 108 | if (limited.y > 0) { 109 | mask |= direction_down_bit; 110 | } 111 | else if (limited.y < 0) { 112 | mask |= direction_up_bit; 113 | } 114 | 115 | enter_humanoid_walk_animation(registry, entity, mask); 116 | } 117 | 118 | body.data->SetLinearVelocity(glmx::as_b2(limited)); 119 | } 120 | 121 | void on_stop_player(entt::registry& registry, const StopPlayerEvent& event) 122 | { 123 | const auto entity = registry.view().front(); 124 | auto& body = registry.get(entity); 125 | 126 | auto velocity = body.data->GetLinearVelocity(); 127 | const auto prevVelocity = glmx::from_b2(velocity); 128 | 129 | if (event.direction_mask | direction_left_and_right) { 130 | velocity.x = 0; 131 | } 132 | 133 | if (event.direction_mask | direction_up_and_down) { 134 | velocity.y = 0; 135 | } 136 | 137 | auto limited = glmx::from_b2(velocity); 138 | glmx::cap_magnitude(limited, body.max_speed); 139 | 140 | if (glmx::is_zero(limited)) { 141 | enter_humanoid_idle_animation(registry, entity, direction_left_bit); // TODO 142 | } 143 | 144 | body.data->SetLinearVelocity(glmx::as_b2(limited)); 145 | } 146 | 147 | } // namespace wanderer::sys -------------------------------------------------------------------------------- /source/wanderer/data/components/rendering.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // string 4 | #include // vector 5 | 6 | #include 7 | #include 8 | 9 | #include "wanderer/common.hpp" 10 | #include "wanderer/core/graphics.hpp" 11 | 12 | namespace wanderer::comp { 13 | 14 | /// \addtogroup components 15 | /// \{ 16 | 17 | /** 18 | * \brief Represents the region of a tilemap that needs to be rendered. 19 | */ 20 | struct RenderBounds final 21 | { 22 | usize begin_row{}; ///< The first row index (inclusive). 23 | usize begin_col{}; ///< The first column index (inclusive). 24 | usize end_row{}; ///< The terminating row index (exclusive). 25 | usize end_col{}; ///< The terminating column index (exclusive). 26 | }; 27 | 28 | /** 29 | * \brief Represents the region of the game shown to the player. 30 | */ 31 | struct Viewport final 32 | { 33 | glm::vec2 offset{}; ///< The viewport offset. 34 | glm::vec2 size{}; ///< The viewport size. 35 | bool keep_in_bounds{}; ///< Flag to prevent showing areas outside of the map. 36 | }; 37 | 38 | /** 39 | * \brief A tag component used by the viewport system for tracking. 40 | * 41 | * \details The viewport performs no tracking if no entity features this component. 42 | * Furthermore, it is expected that a maximum of one entity per registry features this 43 | * component. 44 | * 45 | * \note An entity featuring this component must also feature a `game_object` component. 46 | */ 47 | struct ViewportTarget final 48 | {}; 49 | 50 | /** 51 | * \brief Describes a blocking animation displaying some text over an opaque background. 52 | */ 53 | struct CinematicFade final 54 | { 55 | cen::u64ms start{}; ///< Time of start. 56 | cen::u64ms transition{}; ///< Duration of the transitions. 57 | cen::u64ms pause{}; ///< Duration of the pause between transitions. 58 | cen::color bg; ///< The background color. 59 | 60 | std::vector labels; ///< Associated lazy_text entities. 61 | }; 62 | 63 | /** 64 | * \brief Represents a general frame-based animation. 65 | */ 66 | struct Animation final 67 | { 68 | usize frame{0}; ///< Current frame index. 69 | usize frame_count{1}; ///< Amount of frames in the animation. 70 | cen::u64ms then{0}; ///< Time of the previous update of the animation. 71 | std::vector delays; ///< Duration of each frame in the animation 72 | }; 73 | 74 | /** 75 | * \brief Component associated with the `animation` component for sequenced animations. 76 | */ 77 | struct SeqAnimation final 78 | { 79 | glm::ivec2 frame_size{}; 80 | }; 81 | 82 | /** 83 | * \brief Component intended to accompany the `animation` component for tile animations. 84 | */ 85 | struct TileAnimation final 86 | { 87 | std::vector frames; ///< Tiles to be rendered for each frame. 88 | }; 89 | 90 | /** 91 | * \brief Component by all drawable game objects. 92 | * 93 | * \details Drawable entities are first arranged according to their layers. Within each 94 | * layer, the render order is determined using the depth index heuristic and he 95 | * y-coordinates of center points. The lower the depth value, the earlier an entity is 96 | * rendered. 97 | * 98 | * \details The y-coordinate of an entity's center point is used when two entities feature 99 | * the same depth index. Similarly to the depth, an entity with a smaller center 100 | * y-coordinate value will be rendered before an entity with a higher value, since they 101 | * are "above" and therefore behind the other. 102 | * 103 | * \note Drawable entities are expected to feature `game_object` components. 104 | */ 105 | struct Drawable final 106 | { 107 | texture_id texture{}; ///< The associated texture. 108 | cen::irect src; ///< Region of associated texture that will be rendered. 109 | int32 layer_index{}; ///< Layer index. 110 | int32 depth_index{5}; ///< Render order heuristic. 111 | }; 112 | 113 | /** 114 | * \brief Represents a light at a specific point in the world. 115 | * 116 | * \note Point light entities are expected to feature `game_object` components. 117 | */ 118 | struct PointLight final 119 | { 120 | glm::vec2 offset{}; ///< Offset of the light relative to the game object position. 121 | float32 size{}; ///< Base size of the light. 122 | float32 fluctuation{}; ///< Fluctuation size in [-limit, limit]. 123 | float32 step_size{}; ///< Size of each fluctuation increment. 124 | float32 limit{}; ///< Fluctuation size limit. 125 | }; 126 | 127 | /// \} End of group components 128 | 129 | } // namespace wanderer::comp -------------------------------------------------------------------------------- /resources/fonts/dogica/dogica_license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020, Roberto Mocci (), 2 | with Reserved Font Name Dogica. 3 | 4 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 5 | This license is copied below, and is also available with a FAQ at: 6 | http://scripts.sil.org/OFL 7 | 8 | 9 | ----------------------------------------------------------- 10 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 11 | ----------------------------------------------------------- 12 | 13 | PREAMBLE 14 | The goals of the Open Font License (OFL) are to stimulate worldwide 15 | development of collaborative font projects, to support the font creation 16 | efforts of academic and linguistic communities, and to provide a free and 17 | open framework in which fonts may be shared and improved in partnership 18 | with others. 19 | 20 | The OFL allows the licensed fonts to be used, studied, modified and 21 | redistributed freely as long as they are not sold by themselves. The 22 | fonts, including any derivative works, can be bundled, embedded, 23 | redistributed and/or sold with any software provided that any reserved 24 | names are not used by derivative works. The fonts and derivatives, 25 | however, cannot be released under any other type of license. The 26 | requirement for fonts to remain under this license does not apply 27 | to any document created using the fonts or their derivatives. 28 | 29 | DEFINITIONS 30 | "Font Software" refers to the set of files released by the Copyright 31 | Holder(s) under this license and clearly marked as such. This may 32 | include source files, build scripts and documentation. 33 | 34 | "Reserved Font Name" refers to any names specified as such after the 35 | copyright statement(s). 36 | 37 | "Original Version" refers to the collection of Font Software components as 38 | distributed by the Copyright Holder(s). 39 | 40 | "Modified Version" refers to any derivative made by adding to, deleting, 41 | or substituting -- in part or in whole -- any of the components of the 42 | Original Version, by changing formats or by porting the Font Software to a 43 | new environment. 44 | 45 | "Author" refers to any designer, engineer, programmer, technical 46 | writer or other person who contributed to the Font Software. 47 | 48 | PERMISSION & CONDITIONS 49 | Permission is hereby granted, free of charge, to any person obtaining 50 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 51 | redistribute, and sell modified and unmodified copies of the Font 52 | Software, subject to the following conditions: 53 | 54 | 1) Neither the Font Software nor any of its individual components, 55 | in Original or Modified Versions, may be sold by itself. 56 | 57 | 2) Original or Modified Versions of the Font Software may be bundled, 58 | redistributed and/or sold with any software, provided that each copy 59 | contains the above copyright notice and this license. These can be 60 | included either as stand-alone text files, human-readable headers or 61 | in the appropriate machine-readable metadata fields within text or 62 | binary files as long as those fields can be easily viewed by the user. 63 | 64 | 3) No Modified Version of the Font Software may use the Reserved Font 65 | Name(s) unless explicit written permission is granted by the corresponding 66 | Copyright Holder. This restriction only applies to the primary font name as 67 | presented to the users. 68 | 69 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 70 | Software shall not be used to promote, endorse or advertise any 71 | Modified Version, except to acknowledge the contribution(s) of the 72 | Copyright Holder(s) and the Author(s) or with their explicit written 73 | permission. 74 | 75 | 5) The Font Software, modified or unmodified, in part or in whole, 76 | must be distributed entirely under this license, and must not be 77 | distributed under any other license. The requirement for fonts to 78 | remain under this license does not apply to any document created 79 | using the Font Software. 80 | 81 | TERMINATION 82 | This license becomes null and void if any of the above conditions are 83 | not met. 84 | 85 | DISCLAIMER 86 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 87 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 88 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 89 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 90 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 91 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 92 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 93 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 94 | OTHER DEALINGS IN THE FONT SOFTWARE. -------------------------------------------------------------------------------- /resources/fonts/dogica/dogica_pixel_license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020, Roberto Mocci (), 2 | with Reserved Font Name Dogica Pixel. 3 | 4 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 5 | This license is copied below, and is also available with a FAQ at: 6 | http://scripts.sil.org/OFL 7 | 8 | 9 | ----------------------------------------------------------- 10 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 11 | ----------------------------------------------------------- 12 | 13 | PREAMBLE 14 | The goals of the Open Font License (OFL) are to stimulate worldwide 15 | development of collaborative font projects, to support the font creation 16 | efforts of academic and linguistic communities, and to provide a free and 17 | open framework in which fonts may be shared and improved in partnership 18 | with others. 19 | 20 | The OFL allows the licensed fonts to be used, studied, modified and 21 | redistributed freely as long as they are not sold by themselves. The 22 | fonts, including any derivative works, can be bundled, embedded, 23 | redistributed and/or sold with any software provided that any reserved 24 | names are not used by derivative works. The fonts and derivatives, 25 | however, cannot be released under any other type of license. The 26 | requirement for fonts to remain under this license does not apply 27 | to any document created using the fonts or their derivatives. 28 | 29 | DEFINITIONS 30 | "Font Software" refers to the set of files released by the Copyright 31 | Holder(s) under this license and clearly marked as such. This may 32 | include source files, build scripts and documentation. 33 | 34 | "Reserved Font Name" refers to any names specified as such after the 35 | copyright statement(s). 36 | 37 | "Original Version" refers to the collection of Font Software components as 38 | distributed by the Copyright Holder(s). 39 | 40 | "Modified Version" refers to any derivative made by adding to, deleting, 41 | or substituting -- in part or in whole -- any of the components of the 42 | Original Version, by changing formats or by porting the Font Software to a 43 | new environment. 44 | 45 | "Author" refers to any designer, engineer, programmer, technical 46 | writer or other person who contributed to the Font Software. 47 | 48 | PERMISSION & CONDITIONS 49 | Permission is hereby granted, free of charge, to any person obtaining 50 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 51 | redistribute, and sell modified and unmodified copies of the Font 52 | Software, subject to the following conditions: 53 | 54 | 1) Neither the Font Software nor any of its individual components, 55 | in Original or Modified Versions, may be sold by itself. 56 | 57 | 2) Original or Modified Versions of the Font Software may be bundled, 58 | redistributed and/or sold with any software, provided that each copy 59 | contains the above copyright notice and this license. These can be 60 | included either as stand-alone text files, human-readable headers or 61 | in the appropriate machine-readable metadata fields within text or 62 | binary files as long as those fields can be easily viewed by the user. 63 | 64 | 3) No Modified Version of the Font Software may use the Reserved Font 65 | Name(s) unless explicit written permission is granted by the corresponding 66 | Copyright Holder. This restriction only applies to the primary font name as 67 | presented to the users. 68 | 69 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 70 | Software shall not be used to promote, endorse or advertise any 71 | Modified Version, except to acknowledge the contribution(s) of the 72 | Copyright Holder(s) and the Author(s) or with their explicit written 73 | permission. 74 | 75 | 5) The Font Software, modified or unmodified, in part or in whole, 76 | must be distributed entirely under this license, and must not be 77 | distributed under any other license. The requirement for fonts to 78 | remain under this license does not apply to any document created 79 | using the Font Software. 80 | 81 | TERMINATION 82 | This license becomes null and void if any of the above conditions are 83 | not met. 84 | 85 | DISCLAIMER 86 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 87 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 88 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 89 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 90 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 91 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 92 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 93 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 94 | OTHER DEALINGS IN THE FONT SOFTWARE. --------------------------------------------------------------------------------