diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b33f49..e465a8d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,8 +20,8 @@ set(CMAKE_CXX_STANDARD 23) if (UNIX) # TODO: Enable ALL optimization flags for RELEASE builds. - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -floop-nest-optimize -funroll-loops") + #set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3") + #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -floop-nest-optimize -funroll-loops") endif() set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake") @@ -39,7 +39,7 @@ CPMAddPackage( CPMAddPackage( NAME jjx - URL https://git.redacted.cc/josh/jjx/archive/Release-1.zip + URL https://git.redacted.cc/josh/jjx/archive/Release-1.1.zip ) CPMAddPackage( diff --git a/Client/include/Client/AssetService.hpp b/Client/include/Client/AssetService.hpp index 22c7af0..f864944 100644 --- a/Client/include/Client/AssetService.hpp +++ b/Client/include/Client/AssetService.hpp @@ -6,7 +6,7 @@ /// @file AssetService.hpp /// @desc Manages game asset data. -/// @edit 1/28/2025 +/// @edit 3/31/2025 /// @auth Josh O'Leary /// The AssetService is a class / static library that manages on-file game assets. @@ -21,9 +21,10 @@ #include #include #include +#include +#include -namespace CaveGame::Client -{ +namespace CaveGame::Client { using namespace JGL; @@ -32,6 +33,7 @@ namespace CaveGame::Client struct AssetRequest { + std::string name; std::filesystem::path path; AssetType type; }; @@ -62,9 +64,9 @@ namespace CaveGame::Client //return fonts[fontName]; } - void EnqueueTexture(const std::filesystem::path &path); - void EnqueueFont(const std::filesystem::path& path); - void EnqueueSound(const std::filesystem::path& path); + void EnqueueTexture(const std::string& name, const std::filesystem::path &path); + void EnqueueFont(const std::string& name, const std::filesystem::path& path); + void EnqueueSound(const std::string& name, const std::filesystem::path& path); void LoadAllFromQueue(); @@ -81,6 +83,9 @@ namespace CaveGame::Client unsigned int TotalQueued() const { return total_queued; } unsigned int TotalLoaded() const { return total_loaded; } + void ParseManifest(); + void PreloadCertainAssets(); + protected: /// @returns only the filename of the given path. diff --git a/Client/include/Client/PauseMenu.hpp b/Client/include/Client/PauseMenu.hpp new file mode 100644 index 0000000..a31cb81 --- /dev/null +++ b/Client/include/Client/PauseMenu.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace CaveGame::Client +{ + class PauseMenuWidget : public JUI::Rect { + + }; + + +} \ No newline at end of file diff --git a/Client/include/Client/SettingsMenu.hpp b/Client/include/Client/SettingsMenu.hpp index 139d46e..9d0e82c 100644 --- a/Client/include/Client/SettingsMenu.hpp +++ b/Client/include/Client/SettingsMenu.hpp @@ -12,11 +12,48 @@ #pragma once #include +#include +#include +#include -namespace CaveGame::Client -{ - class SettingsMenu : public JUI::Window + +namespace CaveGame::Client { + + // TODO: Analyze behavior of singleton on an object that requires specific initialization, like this. + class SettingsMenu : public JUI::Window, public Singleton { + public: + SettingsMenu() + { + SetTitle("Settings"); + auto* root_layout = new JUI::VerticalListLayout(this); + + general_section = new JUI::Collapsible(root_layout); + general_section->Title("General"); + + auto* gen_layout = new JUI::VerticalListLayout(general_section); + + + sound_section = new JUI::Collapsible(root_layout); + sound_section->Title("Sound"); + + auto* snd_layout = new JUI::VerticalListLayout(sound_section); + + graphics_section = new JUI::Collapsible(root_layout); + graphics_section->Title("Graphics"); + + auto* gfx_layout = new JUI::VerticalListLayout(graphics_section); + + input_section = new JUI::Collapsible(root_layout); + input_section->Title("Input"); + + } + protected: + JUI::Collapsible* general_section; + JUI::Collapsible* sound_section; + JUI::Collapsible* graphics_section; + JUI::Collapsible* input_section; + private: }; } \ No newline at end of file diff --git a/Client/include/Client/Splash.hpp b/Client/include/Client/Splash.hpp index acc5d91..106948f 100644 --- a/Client/include/Client/Splash.hpp +++ b/Client/include/Client/Splash.hpp @@ -32,7 +32,7 @@ namespace CaveGame::Client protected: float splash_timer = 1.5f; float load_percent = 0.f; - JGL::Texture* splash = nullptr; + std::shared_ptr splash = nullptr; std::array column_textures{}; int column_width = 0; diff --git a/Client/include/Client/TileTool.hpp b/Client/include/Client/TileTool.hpp index bf40f90..5a9c01d 100644 --- a/Client/include/Client/TileTool.hpp +++ b/Client/include/Client/TileTool.hpp @@ -63,8 +63,9 @@ namespace CaveGame::Client { TextRect* tool_size_label; TextButton* step_btn; TextButton* step2_btn; + TextButton* step3_btn; bool enabled = false; - bool tile_sim_disabled; + bool tile_sim_disabled = false; private: }; } \ No newline at end of file diff --git a/Client/src/Client/AssetService.cpp b/Client/src/Client/AssetService.cpp index a77d2ad..5adc11c 100644 --- a/Client/src/Client/AssetService.cpp +++ b/Client/src/Client/AssetService.cpp @@ -1,6 +1,8 @@ #include #include #include +#include + using namespace std::chrono_literals; static CaveGame::Client::AssetService* singleton; @@ -9,18 +11,20 @@ CaveGame::Client::AssetService * CaveGame::Client::AssetService::Get() { return CaveGame::Client::AssetService::AssetService() { singleton = this; + + ParseManifest(); } void CaveGame::Client::AssetService::TempLoadSimple() { - player_sprite = new JGL::Texture("assets/textures/player.png", JGL::FilteringMode::NEAREST); - explosion_sprite = new JGL::Texture("assets/textures/explosion.png", JGL::FilteringMode::NEAREST); - title_img = new JGL::Texture("assets/textures/title_1.png"); + //player_sprite = new JGL::Texture("assets/textures/player.png", JGL::FilteringMode::NEAREST); + //explosion_sprite = new JGL::Texture("assets/textures/explosion.png", JGL::FilteringMode::NEAREST); + //title_img = new JGL::Texture("assets/textures/title_1.png"); } void CaveGame::Client::AssetService::EnqueueDefaultAssetsFolder() { //EnqueueContents("assets"); - textures.emplace("player", new JGL::Texture("assets/textures/player.png")); + //textures.emplace("player", new JGL::Texture("assets/textures/player.png")); } @@ -35,27 +39,31 @@ std::string CaveGame::Client::AssetService::FilenameFromPathWithoutExtension(con return base.substr(0, p); } -void CaveGame::Client::AssetService::EnqueueTexture(const std::filesystem::path &path) { - queue.push({path, AssetType::TEXTURE}); +void CaveGame::Client::AssetService::EnqueueTexture(const std::string& name, const std::filesystem::path &path) { + queue.push({name, path, AssetType::TEXTURE}); total_queued++; } -void CaveGame::Client::AssetService::EnqueueFont(const std::filesystem::path &path) { - queue.push({path, AssetType::FONT}); +void CaveGame::Client::AssetService::EnqueueFont(const std::string& name, const std::filesystem::path &path) { + queue.push({name, path, AssetType::FONT}); total_queued++; } -void CaveGame::Client::AssetService::EnqueueSound(const std::filesystem::path &path) { - queue.push({path, AssetType::AUDIO}); +void CaveGame::Client::AssetService::EnqueueSound(const std::string& name, const std::filesystem::path &path) { + queue.push({name, path, AssetType::AUDIO}); total_queued++; } bool CaveGame::Client::AssetService::LoadAsset(const CaveGame::Client::AssetRequest &request) { last_asset_processed = request.path; + switch(request.type) { case AssetType::TEXTURE: { + if (textures.contains(request.name)) // TODO: Note repeat request. + return true; + auto texture =std::make_shared(request.path); - textures[request.path] = texture; + textures[request.name] = texture; } default: { // TODO: We don't support this asset type yet!!! @@ -109,3 +117,53 @@ void CaveGame::Client::AssetService::LoadAllFromQueue() { } } } + +void CaveGame::Client::AssetService::ParseManifest() { + std::string contents = read_file("assets/data/manifest.json"); + + using namespace JJX; + + auto [obj, errcode] = json::parse(contents); + + json::object catalog = obj.as_object(); + + // TODO: in this case, calling obj.at("textures") results in a bad_alloc? + + if (catalog.contains("textures")) { + json::array texlist = catalog.at("textures").as_array(); + for (auto& texture_entry : texlist) { + std::string name = texture_entry[0].string.value(); + std::string path = texture_entry[1].string.value(); + EnqueueTexture(name, path); + } + } + + if (catalog.contains("fonts")) { + for (auto &font_entry: catalog["fonts"].as_array()) { + std::string name = font_entry[0].string.value(); + std::string path = font_entry[1].string.value(); + EnqueueFont(name, path); + } + } + + if (catalog.contains("sfx")) { + for (auto &sfx_entry: catalog["sfx"].as_array()) { + std::string name = sfx_entry[0].string.value(); + std::string path = sfx_entry[1].string.value(); + EnqueueSound(name, path); + } + } + + if (catalog.contains("music")) { + for (auto &sfx_entry: catalog["music"].as_array()) { + std::string name = sfx_entry[0].string.value(); + std::string path = sfx_entry[1].string.value(); + EnqueueSound(name, path); + } + } +} + +void CaveGame::Client::AssetService::PreloadCertainAssets() { + LoadAsset({"redacted", "assets/textures/redacted.png", AssetType::TEXTURE}); + LoadAsset({"title", "assets/textures/title_1.png", AssetType::TEXTURE}); +} diff --git a/Client/src/Client/Camera2D.cpp b/Client/src/Client/Camera2D.cpp index 78624a4..4185775 100644 --- a/Client/src/Client/Camera2D.cpp +++ b/Client/src/Client/Camera2D.cpp @@ -116,13 +116,14 @@ namespace CaveGame::Client Vector2 Camera2D::Position() const { return position; } - void Camera2D::MoveLeft(float rate) { - position -= Vector2(move_speed * rate, 0); + void Camera2D::Move(const Vector2& dir) + { + position += dir * move_speed; + last_free_move = 0; } - void Camera2D::Move(const Vector2& velocity) - { - position += velocity * move_speed; + void Camera2D::MoveLeft(float rate) { + position -= Vector2(move_speed * rate, 0); last_free_move = 0; } diff --git a/Client/src/Client/Splash.cpp b/Client/src/Client/Splash.cpp index d95f517..5ca5862 100644 --- a/Client/src/Client/Splash.cpp +++ b/Client/src/Client/Splash.cpp @@ -133,7 +133,7 @@ void CaveGame::Client::Splash::Update(float elapsed) void CaveGame::Client::Splash::Load() { column_textures.fill(nullptr); - splash = new JGL::Texture("assets/textures/redacted.png"); + splash = AssetService::Get()->GetTexture("redacted"); //new JGL::Texture("assets/textures/redacted.png"); ComputeMatrixTextureCache(); Scene::Load(); @@ -143,7 +143,8 @@ void CaveGame::Client::Splash::Load() { void CaveGame::Client::Splash::Unload() { for (auto& r : column_textures) delete r; - delete splash; + + splash = nullptr; column_textures.fill(nullptr); Scene::Unload(); diff --git a/Client/src/Client/TileTool.cpp b/Client/src/Client/TileTool.cpp index 791c6ec..670fccc 100644 --- a/Client/src/Client/TileTool.cpp +++ b/Client/src/Client/TileTool.cpp @@ -44,7 +44,7 @@ CaveGame::Client::TileTool::TileTool(JUI::Widget *parent) : JUI::Window(parent) brush_size_slider->Maximum(1.f); brush_size_slider->Minimum(0.01f); brush_size_slider->Interval(0.001f); - brush_size_slider->ValueChanged += [&, this] (float val) + brush_size_slider->ValueChanged += [&, this] (float val) mutable { float newval = val * 50; tool_size_label->SetContent(std::format("Size: {}", Math::Round(newval, 1))); @@ -59,7 +59,7 @@ CaveGame::Client::TileTool::TileTool(JUI::Widget *parent) : JUI::Window(parent) brush_percent_slider->Maximum(1.f); brush_percent_slider->Minimum(0.f); brush_percent_slider->Interval(0.001f); - brush_percent_slider->ValueChanged += [&, this, tool_percent_label] (float val) + brush_percent_slider->ValueChanged += [&, this, tool_percent_label] (float val) mutable { float newval = val * 100.f; tool_percent_label->SetContent(std::format("Percent: {}%", Math::Floor(newval))); @@ -71,11 +71,14 @@ CaveGame::Client::TileTool::TileTool(JUI::Widget *parent) : JUI::Window(parent) auto* tile_sim_checkbox = new Checkbox(right_row_layout); tile_sim_checkbox->Size({row_height, row_height, 0, 0}); - tile_sim_checkbox->OnReleaseEvent += [&, this] (Vector2 _, JUI::MouseButton _2, bool _3) { + tile_sim_checkbox->OnClickEvent += [&, this] (Vector2 _, JUI::MouseButton _2) mutable { tile_sim_disabled = !tile_sim_disabled; TileSimulationDisabledChanged.Invoke(tile_sim_disabled); + + // Set the visual state of the "step" buttonns step_btn->SetEnabled(tile_sim_disabled); step2_btn->SetEnabled(tile_sim_disabled); + step3_btn->SetEnabled(tile_sim_disabled); }; @@ -86,26 +89,35 @@ CaveGame::Client::TileTool::TileTool(JUI::Widget *parent) : JUI::Window(parent) step_btn->BGColor(Colors::LightGray); step_btn->BaseBGColor(Colors::LightGray); step_btn->Disable(); - step_btn->OnClickEvent += [&, this] (auto a, auto b) { + step_btn->OnClickEvent += [&, this] (auto a, auto b) mutable { TileSimulationStep.Invoke(1); }; - step2_btn = new TextButton(right_row_layout); + step2_btn = new TextButton(left_row_layout); step2_btn->Size({100_percent, UDim(row_height, 0)}); step2_btn->SetContent("Step 10"); step2_btn->BGColor(Colors::LightGray); step2_btn->BaseBGColor(Colors::LightGray); step2_btn->Disable(); - step2_btn->OnClickEvent += [&, this] (auto a, auto b) { + step2_btn->OnClickEvent += [&, this] (auto a, auto b) mutable { TileSimulationStep.Invoke(10); }; + step3_btn = new TextButton(left_row_layout); + step3_btn->Size({100_percent, UDim(row_height, 0)}); + step3_btn->SetContent("Step 100"); + step3_btn->BGColor(Colors::LightGray); + step3_btn->BaseBGColor(Colors::LightGray); + step3_btn->Disable(); + step3_btn->OnClickEvent += [&, this] (auto a, auto b) mutable { + TileSimulationStep.Invoke(100); + }; - BrushSizeChanged += [this] (float value) { + BrushSizeChanged += [this] (float value) mutable { brush_radius = value; }; - BrushPercentChanged += [this] (float value) { + BrushPercentChanged += [this] (float value) mutable { brush_density = value; }; diff --git a/ClientApp/assets/data/items.json b/ClientApp/assets/data/items.json index c0d4b7d..3c0ce5f 100644 --- a/ClientApp/assets/data/items.json +++ b/ClientApp/assets/data/items.json @@ -21,27 +21,33 @@ }, { "mnemonic-id": "copper-bar", - "display-name": "Copper Bar" + "display-name": "Copper Bar", + "sprite:" : "ingot" }, { "mnemonic-id": "iron-bar", - "display-name": "Iron Bar" + "display-name": "Iron Bar", + "sprite:" : "ingot" }, { "mnemonic-id": "silver-bar", - "display-name": "Silver Bar" + "display-name": "Silver Bar", + "sprite:" : "ingot" }, { "mnemonic-id": "tungsten-bar", - "display-name": "Tungsten Bar" + "display-name": "Tungsten Bar", + "sprite:" : "ingot" }, { "mnemonic-id": "platinum-bar", - "display-name": "Platinum Bar" + "display-name": "Platinum Bar", + "sprite:" : "ingot.png" }, { "mnemonic-id": "gold-bar", - "display-name": "Gold Bar" + "display-name": "Gold Bar", + "sprite:" : "ingot.png" }, { "mnemonic-id": "lead-bar", @@ -84,5 +90,11 @@ "stack": 1, "tags": ["dagger", "blade", "melee", "metal", "iron", "rusty"] + }, + { + "mnemonic-id": "smoke-n-dip" + }, + { + "mnemonic-id": "blick-axe" } ] \ No newline at end of file diff --git a/ClientApp/assets/data/manifest.json b/ClientApp/assets/data/manifest.json new file mode 100644 index 0000000..e608fc6 --- /dev/null +++ b/ClientApp/assets/data/manifest.json @@ -0,0 +1,13 @@ +{ + "textures": [ + ["ingot", "assets/textures/ingot.png"], + ["player", "assets/textures/player.png"], + ["bg", "assets/textures/bg.png"], + ["explosion", "assets/textures/explosion.png"], + ["redacted", "assets/textures/redacted.png"], + ["title_1", "assets/textures/title_1.png"] + ], + "music": [ + + ] +} \ No newline at end of file diff --git a/ClientApp/assets/data/tiles.json b/ClientApp/assets/data/tiles.json index 845d976..141e8e7 100644 --- a/ClientApp/assets/data/tiles.json +++ b/ClientApp/assets/data/tiles.json @@ -133,7 +133,7 @@ "forced-ticc-func": "sand-grav", "solid": true, "color": [102, 118, 124], - "pallet": [[92, 102, 128], [89, 116, 123], [121, 95, 108]] + "pallet": [[92, 102, 128], [82, 82, 82], [102, 102, 102], [132, 132, 132], [89, 116, 123], [121, 95, 108]] }, { "mnemonic-id" : "white-sand", diff --git a/ClientApp/include/ClientApp/CaveGameWindow.hpp b/ClientApp/include/ClientApp/CaveGameWindow.hpp index 28d2807..8333c5d 100644 --- a/ClientApp/include/ClientApp/CaveGameWindow.hpp +++ b/ClientApp/include/ClientApp/CaveGameWindow.hpp @@ -65,7 +65,8 @@ namespace CaveGame::ClientApp { /// This function performs rendering routines, once per refresh. void Draw(); - bool ReadyToClose() const { return wanna_die; } + /// @return True when a flag is set that indicates the game is "preparing" to close. Certain procedures may still be taking place, such as saving and serialization. + bool ReadyToClose() const; /// Returns the game's console GUI, which accepts commands and displays log messages. Client::Console* Console(); @@ -85,14 +86,23 @@ namespace CaveGame::ClientApp { public: #pragma region Input Callbacks + /// Called by the window on each frame. void OnRefresh(float elapsed) override; + /// Called by the window upon the user pressing a mouse button. void OnMouseButtonDown(const ReWindow::MouseButtonDownEvent &ev) override; + /// Called by the window upon the user releasing a mouse button. void OnMouseButtonUp(const ReWindow::MouseButtonUpEvent &ev) override; + /// Called by the window upon the user scrolling a mouse wheel. void OnMouseWheel(const ReWindow::MouseWheelEvent &ev) override; + /// Called by the window upon the user releasing a keyboard key. void OnKeyUp(const ReWindow::KeyUpEvent &ev) override; + /// Called by the window upon the user pressing a keyboard key. void OnKeyDown(const ReWindow::KeyDownEvent& ev) override; + /// Called by the window upon the user moving their pointer device, currently just mice. void OnMouseMove(const ReWindow::MouseMoveEvent &ev) override; + /// Called by the window when it receives a request from the operating-system to resize. bool OnResizeRequest(const ReWindow::WindowResizeRequestEvent &ev) override; + /// Called by the window **before** it closes. void OnClosing() override; #pragma endregion public: @@ -107,7 +117,7 @@ namespace CaveGame::ClientApp { /// Runs exactly one iteration of the game loop. Currently, this is one call to Update(), and then to Draw(). /// @see Refresh(). void Step(); - + /// Creates the in-game console menu. void create_console_window(); void create_stats_window(); void create_settings_window(); @@ -118,9 +128,16 @@ namespace CaveGame::ClientApp { /// Constructs the Splash screen, Main Menu screen, and In-game session. void CreateContexts(); + + + // TODO: Refactor this into irrelevance. void InGameControls(float elapsed); void OnConsoleCommandInput(const std::string &command); - + /// Toggles tile simulation if we are in-game. + bool ToggleTileSim(const CommandArgs& args); + /// Opens a singleplayer session. + /// @param info A structure containing information for joining a singleplayer world. + void OpenWorld(Client::SingleplayerSessionInfo info); /// Logs help information to the console. Called when the user runs the 'help' command. bool HelpCommand(const CommandArgs& args); @@ -128,8 +145,11 @@ namespace CaveGame::ClientApp { bool ListCommand(const CommandArgs& args); bool TileListCmd(const CommandArgs& args); bool ItemListCmd(const CommandArgs& args); - /// Toggles tile simulation if we are - bool ToggleTileSim(const CommandArgs& args); + + + bool NoclipCmd(const CommandArgs &args); + + bool FpsLimitCmd(const CommandArgs &args); /// This table defines the supported console commands, their aliases, and the callback lambda. #pragma region Commands @@ -252,10 +272,6 @@ namespace CaveGame::ClientApp { float our_avg = 0.f; float max_fps = 60.f; - void OpenWorld(Client::SingleplayerSessionInfo info); - bool NoclipCmd(const CommandArgs &args); - - bool FpsLimitCmd(const CommandArgs &args); }; } \ No newline at end of file diff --git a/ClientApp/src/ClientApp/CaveGameWindow.cpp b/ClientApp/src/ClientApp/CaveGameWindow.cpp index 3999e5b..3472e79 100644 --- a/ClientApp/src/ClientApp/CaveGameWindow.cpp +++ b/ClientApp/src/ClientApp/CaveGameWindow.cpp @@ -23,6 +23,8 @@ namespace CaveGame::ClientApp { { Logs::Info("Parsing Tile Data."); CaveGame::Core::LoadTileMetadata(); + Logs::Info("Parsing Item Data."); + CaveGame::Core::LoadItemMetadata(); Logs::Info("Creating game window."); CreateContexts(); @@ -116,17 +118,21 @@ namespace CaveGame::ClientApp { this->SetResizable(true); this->SetVsyncEnabled(false); + this->assets.PreloadCertainAssets(); + this->assets.ParseManifest(); + + // TODO: Replace w/ constructor? this->assets.TempLoadSimple(); this->assets.EnqueueDefaultAssetsFolder(); //for (int i = 0; i < 10; i++) //{ - assets.EnqueueTexture("assets/textures/redacted.png"); - assets.EnqueueTexture("assets/textures/bg.png"); - assets.EnqueueTexture("assets/textures/player.png"); - assets.EnqueueTexture("assets/textures/explosion.png"); - assets.EnqueueTexture("assets/textures/title_1.png"); - assets.EnqueueTexture("assets/textures/ash_wip_potions.png"); + //assets.EnqueueTexture("assets/textures/redacted.png"); + //assets.EnqueueTexture("assets/textures/bg.png"); + //assets.EnqueueTexture("assets/textures/player.png"); + //assets.EnqueueTexture("assets/textures/explosion.png"); + //assets.EnqueueTexture("assets/textures/title_1.png"); + //assets.EnqueueTexture("assets/textures/ash_wip_potions.png"); //} @@ -356,9 +362,15 @@ namespace CaveGame::ClientApp { } if (ev.key == Keys::Escape) { - GameSession()->SaveAndExit(); + // TODO: "Pause menu" + + if (Console()->IsOpen()) + Console()->SetOpen(false); + + + //GameSession()->SaveAndExit(); // TODO: GameContext needs mechanism to **inform** the higher-level object that we are done. - ChangeScene(menu_ctx); + //ChangeScene(menu_ctx); } } @@ -626,6 +638,8 @@ namespace CaveGame::ClientApp { return true; } + bool CaveGameWindow::ReadyToClose() const { return wanna_die; } + } diff --git a/Core/include/Core/Entity.hpp b/Core/include/Core/Entity.hpp index 0e06bca..3fc5ca2 100644 --- a/Core/include/Core/Entity.hpp +++ b/Core/include/Core/Entity.hpp @@ -38,6 +38,7 @@ namespace CaveGame::Core { virtual void Draw() = 0; virtual void Update(float elapsed) = 0; + virtual void PhysicsUpdate(float elapsed) {} Vector2 Position() const; diff --git a/Core/include/Core/PhysicsEntity.hpp b/Core/include/Core/PhysicsEntity.hpp index b2bace9..dd70ef7 100644 --- a/Core/include/Core/PhysicsEntity.hpp +++ b/Core/include/Core/PhysicsEntity.hpp @@ -31,7 +31,7 @@ namespace CaveGame::Core void Accelerate(const Vector2& vector); - virtual void PhysicsUpdate(float elapsed); + void PhysicsUpdate(float elapsed) override; // TODO: Mechanism for figuring out if you're already stuck inside of tiles: i.e. sand void CollisionTest(ITileMap* map, float elapsed) override; diff --git a/Core/include/Core/Player.hpp b/Core/include/Core/Player.hpp index 917739c..337f2e3 100644 --- a/Core/include/Core/Player.hpp +++ b/Core/include/Core/Player.hpp @@ -26,16 +26,17 @@ namespace CaveGame::Core { void Jump(float elapsed) { // TODO: Make it so the player falls **slightly** slower - Vector2 current_velocity = this->Velocity(); + float horiz = 0; if (current_velocity.x > 10.f) horiz = Math::Clamp(current_velocity.x, 10.f, 300.f); if (current_velocity.x < -10.f) horiz = Math::Clamp(current_velocity.x, -300.f, 10.f); + if (on_ground) { - Vector2 projection = Vector2(horiz, -16000*elapsed); + Vector2 projection = Vector2(horiz*elapsed, -16000*elapsed); Accelerate(projection); on_ground = false; diff --git a/Core/include/Core/World.hpp b/Core/include/Core/World.hpp index 888447b..ba004b6 100644 --- a/Core/include/Core/World.hpp +++ b/Core/include/Core/World.hpp @@ -28,6 +28,7 @@ namespace CaveGame::Core { constexpr static unsigned int RandomTileTickCoefficient = 128; constexpr static float PeriodicAutosaveIntervalSeconds = 30.f; + constexpr static float PhysicsTickrate = 100.f; // Desired physics Ticks per second. World() = default; ~World() = default; @@ -184,6 +185,7 @@ namespace CaveGame::Core { ConcurrentQueue ServedChunks; float tile_ticc_counter; + float physics_ticc = 0; // Ticcs per second. float tile_ticc_frequency = 24.f; diff --git a/Core/src/Core/ItemRegistry.cpp b/Core/src/Core/ItemRegistry.cpp index d24541c..913db56 100644 --- a/Core/src/Core/ItemRegistry.cpp +++ b/Core/src/Core/ItemRegistry.cpp @@ -3,6 +3,7 @@ #include #include #include +#include "Core/Loggers.hpp" namespace CaveGame::Core { @@ -28,13 +29,15 @@ namespace CaveGame::Core if (entry_obj.contains("tags")) item.tags = JsonConversions::parse_string_list(entry_obj["tags"]); + + // TODO: Support multiple overlaid "Sprites" with assigned colors. + // TODO: Support animated sprites. + std::string sprite_name; + Items().Register(item); } - for (auto [name, item] : Items().GetItemMap()) { - std::cout << name << std::endl; - } return true; } @@ -45,6 +48,9 @@ namespace CaveGame::Core } void ItemRegistry::Register(const Item& data) { + + Logs::Info(std::format("\tRegister Item: id:{} display_name:{} stack:{} value:{}", data.mnemonic, data.display_name, data.max_stack, data.value)); + registered_items.emplace(data.mnemonic, data); } diff --git a/Core/src/Core/PhysicsEntity.cpp b/Core/src/Core/PhysicsEntity.cpp index 2542b00..ea9fce4 100644 --- a/Core/src/Core/PhysicsEntity.cpp +++ b/Core/src/Core/PhysicsEntity.cpp @@ -35,7 +35,7 @@ namespace CaveGame::Core airtime = 0; // TODO: Sophisticated mechanism to maintain locked-timestep, multiple-iteration physics steps. - PhysicsUpdate(elapsed); + //PhysicsUpdate(elapsed); } @@ -144,7 +144,7 @@ namespace CaveGame::Core if (normal.y == 1) { velocity.y = -velocity.y*0.5f; - std::cout << "Touched your head" << std::endl; + //std::cout << "Touched your head" << std::endl; } if (normal.x == -1) diff --git a/Core/src/Core/Tile.cpp b/Core/src/Core/Tile.cpp index 3fe5ce1..e22bbcc 100644 --- a/Core/src/Core/Tile.cpp +++ b/Core/src/Core/Tile.cpp @@ -103,7 +103,7 @@ namespace CaveGame::Core { void SandGravTiccFunc(const Tile &data, ITileMap *world, int x, int y) { static TileID air = Tiles()["air"].numeric_id; static TileID water = Tiles()["water"].numeric_id; - static TileID sand = data.numeric_id; + TileID sand = data.numeric_id; TileID below = world->GetTile(x, y + 1); if (below == air || below == water) { diff --git a/Core/src/Core/TileRegistry.cpp b/Core/src/Core/TileRegistry.cpp index e4bbe03..034f1de 100644 --- a/Core/src/Core/TileRegistry.cpp +++ b/Core/src/Core/TileRegistry.cpp @@ -33,7 +33,7 @@ namespace CaveGame::Core void TileRegistry::Register(TileID ID, const Tile& data) { - Logs::Info(std::format("\tid:{} mnemonic:{} display-name:{}", data.numeric_id, data.mnemonic_id, data.display_name)); + Logs::Info(std::format("\tRegister Tile:: id:{} mnemonic:{} display-name:{}", data.numeric_id, data.mnemonic_id, data.display_name)); registered_tiles[ID] = data; @@ -91,6 +91,7 @@ namespace CaveGame::Core bool LoadTileMetadata(const std::filesystem::path &path) { + // TODO: Move to it's own thing. ticc_funcs.insert({"sand-grav", SandGravTiccFunc}); ticc_funcs.insert({"liquid-settle", LiquidSettleTiccFunc}); ticc_funcs.insert({"grass-random", GrassRandomTiccFunc}); diff --git a/Core/src/Core/World.cpp b/Core/src/Core/World.cpp index f800864..e5fc498 100644 --- a/Core/src/Core/World.cpp +++ b/Core/src/Core/World.cpp @@ -56,10 +56,34 @@ namespace CaveGame::Core { if (simulate_tiles) SimulateTiles(elapsed); + + physics_ticc+= elapsed; + + static float PhysicsTickrateReciprocal = 1.f / PhysicsTickrate; + + bool do_physics_tick_this_frame = false; + int physics_accumulator = 0; + + // Fixed timestep physics within variable timestep gameloop. + while (physics_ticc > PhysicsTickrateReciprocal) { + do_physics_tick_this_frame = true; + physics_accumulator++; + physics_ticc -= PhysicsTickrateReciprocal; + } + + // Max iterations per frame. + if (physics_accumulator > 10) + physics_accumulator = 10; + for (auto* entity : entities) { entity->Update(elapsed); - entity->CollisionTest(this, elapsed); + if (physics_accumulator > 0) { + for (int iters = 0; iters < physics_accumulator; iters++) { + entity->CollisionTest(this, PhysicsTickrateReciprocal); + entity->PhysicsUpdate(PhysicsTickrateReciprocal); + } + } } } diff --git a/Server/include/Server/GameServer.hpp b/Server/include/Server/GameServer.hpp index fd8c96c..2fa5580 100644 --- a/Server/include/Server/GameServer.hpp +++ b/Server/include/Server/GameServer.hpp @@ -6,11 +6,9 @@ #include "Sockets/UdpServer.hpp" -namespace CaveGame::Server -{ +namespace CaveGame::Server { - struct ServerInfoPacket - { + struct ServerInfoPacket { };