Various edits going live. #56

Merged
josh merged 5 commits from edits into master 2025-04-08 14:59:56 -04:00
42 changed files with 375 additions and 139 deletions

View File

@@ -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(

View File

@@ -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 <JGL/types/Texture.h>
#include <JGL/types/Font.h>
#include <queue>
#include <Core/Singleton.hpp>
#include <Core/Macros.hpp>
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.

View File

@@ -25,24 +25,14 @@
#include <Core/Item.hpp>
#include "TileTool.hpp"
#include "ContainerWindow.hpp"
#include <Client/PauseMenu.hpp>
namespace CaveGame::Client {
using namespace Core;
class LootTable {
};
class LootTable {};
class CraftingStation {};
class ContainerGUI {
};
class ContainerGUI {};
using namespace JUI::UDimLiterals;
using CaveGame::Core::Entity;
@@ -50,7 +40,7 @@ namespace CaveGame::Client {
public:
GameSession() = default;
GameSession(bool createNewWorld);
explicit GameSession(bool createNewWorld);
void Update(float elapsed) override;
void Draw() override;
void Load() override;
@@ -99,6 +89,7 @@ namespace CaveGame::Client {
LocalWorld* world = nullptr;
TileHotbar hotbar;
JUI::Scene* hud = nullptr;
PauseMenuWidget* pause_menu = nullptr;
Vector2 mouse_pos = {0,0};
RNG tool_rng;

View File

@@ -46,7 +46,7 @@ namespace CaveGame::Client
JUI::Rect* title = nullptr;
JUI::Rect* button_group = nullptr;
JUI::Rect* changelog = nullptr;
JGL::Texture* bg = nullptr; // TODO: RAII on this
std::shared_ptr<JGL::Texture> bg = nullptr; // TODO: RAII on this
Vector2 mpos {0,0};
Vector2 goal {0,0};

View File

@@ -0,0 +1,56 @@
#pragma once
#include <JUI/Widgets/Rect.hpp>
#include <JUI/Widgets/ListLayout.hpp>
#include <JUI/Widgets/TextButton.hpp>
namespace CaveGame::Client
{
using namespace JUI::UDimLiterals;
class PauseMenuWidget : public JUI::Rect {
public:
Event<> OnSettingsButtonPressed;
Event<> OnQuitButtonPressed;
PauseMenuWidget() {
this->Size({100_percent, 100_percent}); // Full Screen.
this->BGColor({128, 128, 128, 128}); // Transparent Gray.
button_box = new JUI::Rect(this);
button_box->Size({150_px, 150_px});
button_list = new JUI::VerticalListLayout(button_box);
resume_btn = new JUI::TextButton(button_list);
resume_btn->SetContent("Resume");
settings_btn = new JUI::TextButton(button_list);
settings_btn->SetContent("Settings");
quit_btn = new JUI::TextButton(button_list);
quit_btn->SetContent("Save & Exit World");
}
explicit PauseMenuWidget(Widget* parent) : PauseMenuWidget() {
this->Parent(parent);
}
void Toggle() {
is_open = !is_open;
this->Visible(is_open);
}
protected:
JUI::Rect* button_box;
JUI::VerticalListLayout* button_list;
JUI::TextButton* resume_btn;
JUI::TextButton* settings_btn;
JUI::TextButton* quit_btn;
bool is_open;
private:
};
}

View File

@@ -12,11 +12,48 @@
#pragma once
#include <JUI/Widgets/Window.hpp>
#include <JUI/Widgets/Collapsible.hpp>
#include <JUI/Widgets/ListLayout.hpp>
#include <Core/Singleton.hpp>
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<SettingsMenu>
{
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:
};
}

View File

@@ -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<JGL::Texture> splash = nullptr;
std::array<JGL::RenderTarget*, 16> column_textures{};
int column_width = 0;

View File

@@ -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:
};
}

View File

@@ -1,6 +1,8 @@
#include <Client/AssetService.hpp>
#include <thread>
#include <JGL/types/Texture.h>
#include <JJX/JSON.hpp>
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: {
auto texture =std::make_shared<Texture>(request.path);
textures[request.path] = texture;
if (textures.contains(request.name)) // TODO: Note repeat request.
return true;
auto texture = std::make_shared<Texture>(request.path, FilteringMode::NEAREST);
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});
}

View File

@@ -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;
}

View File

@@ -24,6 +24,9 @@ void CaveGame::Client::GameSession::Load() {
world->PostInit();
Logs::Info("Building player HUD.");
hud = new JUI::Scene();
pause_menu = new PauseMenuWidget(hud);
hotbar = TileHotbar();
// TODO: Redundant, use the constructor.
hotbar.Load(world);
@@ -98,8 +101,6 @@ void CaveGame::Client::GameSession::WorldEditToolDrawTiles(TileID tile) {
//Vector2i rounded_coords = {Math::FloorInt(world_coords.x), Math::FloorInt(world_coords.y)};
int radius = Math::FloorInt(tile_tool->BrushRadius());
int density = Math::FloorInt(tile_tool->BrushDensity());
int x = Math::FloorInt(world_coords.x);
@@ -183,7 +184,7 @@ void CaveGame::Client::GameSession::Update(float elapsed) {
}
}
// Move the tile cursor when controller thumbstick is moved.
auto rstick = jstick::GetRightThumbstickAxisNormalized();
if (rstick.Magnitude() > 0.1f)
@@ -191,40 +192,6 @@ void CaveGame::Client::GameSession::Update(float elapsed) {
float joystick_cursor_speed = 250.f;
mouse_pos += rstick*elapsed*joystick_cursor_speed;
}
// FIXME: Temporary hack, we poll the bumper button state each frame, cause the jstick event doesn't propagate for some reason.
/* bool lprn = jstick::IsButtonDown(jstick::XBoxButton::BumperL);
bool rprn = jstick::IsButtonDown(jstick::XBoxButton::BumperR);
if (lprn && !lp)
hotbar.PrevSlot();
if (rprn && !rp)
hotbar.NextSlot();
lp = lprn;
rp = rprn;*/
/*
for (int i = 0; i < 10; i++)
{
float angle = tool_rng.Float01Incl() * Math::TwoPi;
Vector2 unit = {Math::Cos(angle), Math::Sin(angle)};
Vector2 velocity = unit * tool_rng.Float(1, 50);
world->Emit({.position = transformed, .velocity = velocity, .color = Colors::Red, .size = 2, .life = 3});
}
*/
// TODO: Do this once JUI can consume input.
//
// WorldEditToolControlsUpdate(elapsed);
//for (auto& entity: entity_list)
//{
// entity->Update(elapsed);
//}
}
void CaveGame::Client::GameSession::WorldEditToolDrawOverlay()
@@ -275,6 +242,9 @@ void CaveGame::Client::GameSession::PassWindowSize(const Vector2 &size) {
void CaveGame::Client::GameSession::PassKeyInput(Key key, bool pressed) {
// DO NOT chain input conditions with else; you will introduce biasing
if (key == Keys::Escape)
pause_menu->Toggle();
hud->ObserveKeyInput(key, pressed);
hotbar.OnKeyInput(key, pressed);
}

View File

@@ -35,7 +35,7 @@ void CaveGame::Client::MainMenu::Draw() {
void CaveGame::Client::MainMenu::Load() {
bg = new JGL::Texture("assets/textures/bg.png");
bg = AssetService::Get()->GetTexture("bg");
Scene::Load();
@@ -86,7 +86,8 @@ void CaveGame::Client::MainMenu::BuildWidgets() {
title->BorderWidth(0);
auto* content = new JUI::Image(title);
content->Content(Client::AssetService::Get()->title_img);
// TODO: Unsafe!
content->Content(AssetService::Get()->GetTexture("title").get());
content->FitImageToParent(true);
button_group = new Rect(scene);

View File

@@ -39,8 +39,8 @@ void CaveGame::Core::Player::Draw() {
// TODO: Utilize land quad when falling enough to take fall damage, player can't move for a half second.
auto myAsset = Client::AssetService::Get()->player_sprite;
JGL::J2D::DrawPartialSprite(myAsset, RenderTopLeft(), quad.minPoint, {16, 24}, 0, {0,0}, {1,1}, Colors::White, dir);
auto myAsset = Client::AssetService::Get()->GetTexture("player");
JGL::J2D::DrawPartialSprite(myAsset.get(), RenderTopLeft(), quad.minPoint, {16, 24}, 0, {0,0}, {1,1}, Colors::White, dir);
JGL::J2D::OutlineRect(Colors::Red, RenderTopLeft(), texture_center * 2.f);
JGL::J2D::OutlineRect(Colors::Blue, TopLeft(), bounding_box);
J2D::DrawString(Colors::White, std::format("vel: {},{}", Math::Round(velocity.x, 2), Math::Round(velocity.y, 2)), position.x, position.y-8, 0.5f, 8);

View File

@@ -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();

View File

@@ -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;
};

View File

@@ -1,3 +1,9 @@
{
"veins": {
"clay": {
"hi-pass-scale-x": 200,
"hi-pass-scale-y": 205,
"hi-pass-offset": 0
}
}
}

View File

@@ -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"
}
]

View File

@@ -0,0 +1,18 @@
{
"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", "assets/textures/title_1.png"],
["gill_potion", "assets/textures/gill_potion.png"],
["honey_jar", "assets/textures/honey_jar.png"],
["hp_potion", "assets/textures/hp_potion.png"],
["hp_potion_2x", "assets/textures/hp_potion_2x.png"],
["hp_potion_3x", "assets/textures/hp_potion_3x.png"]
],
"music": [
]
}

View File

@@ -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",

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 691 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 748 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 779 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 818 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 B

View File

@@ -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);
};
}

View File

@@ -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; }
}

View File

@@ -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;

View File

@@ -162,7 +162,6 @@ namespace CaveGame::Core
unsigned int ColorMap(int range, int wx, int wy);
// TODO: Implement SecondPass, the catch is, it **may** need to load an arbitrary amount of adjacent chunks to correctly place structures.
// TODO: Expert Mode: How do we keep it threaded separately while still accomplishing the above?
@@ -183,7 +182,7 @@ namespace CaveGame::Core
float GetPrecomputedWhiteNoise2D(int x, int y) const;
protected:
int seed = 0;
int seed = 42069;
SimplexNoise simplex;
PerlinNoise perlin;

View File

@@ -6,6 +6,7 @@
#include <J3ML/Math.hpp>
#include <map>
#include <vector>
#include <optional>
//#include <Core/Registry.hpp>
namespace CaveGame::Core

View File

@@ -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;

View File

@@ -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;

View File

@@ -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<Core::Chunk*> ServedChunks;
float tile_ticc_counter;
float physics_ticc = 0;
// Ticcs per second.
float tile_ticc_frequency = 24.f;

View File

@@ -3,6 +3,7 @@
#include <filesystem>
#include <JJX/JSON.hpp>
#include <Core/JsonConversions.hpp>
#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);
}

View File

@@ -108,6 +108,8 @@ namespace CaveGame::Logs {
SetIncludeTimestampAll(false);
}
// TODO: This is duplicated here and in ReCaveGame::Loggers.cpp
// Bring into jlog as utility functions.
std::string format_timestamp(Timestamp ts) {
return std::format(
"{}-{}-{} {}:{}:{}.{}",
@@ -120,6 +122,8 @@ namespace CaveGame::Logs {
ts.Millisecond().count());
}
// TODO: This is duplicated here and in ReCaveGame::Loggers.cpp
// Bring into jlog as utility functions.
std::string format_source_location(const std::source_location &location) {
return std::format("{} @ {}:{}", location.function_name(), location.file_name(), location.line());
}

View File

@@ -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)

View File

@@ -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) {

View File

@@ -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});

View File

@@ -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);
}
}
}
}

View File

@@ -6,11 +6,9 @@
#include "Sockets/UdpServer.hpp"
namespace CaveGame::Server
{
namespace CaveGame::Server {
struct ServerInfoPacket
{
struct ServerInfoPacket {
};