Large Refactor, half-finished, will not compile or run currently.

This commit is contained in:
2025-03-20 23:52:51 -04:00
parent dfb60a45c4
commit e0367b971c
17 changed files with 423 additions and 227 deletions

View File

@@ -13,7 +13,7 @@ if (PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
message(FATAL_ERROR "In-source builds are not allowed")
endif()
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD 23)
# Set for profiling

View File

@@ -82,7 +82,7 @@ namespace CaveGame::Client {
void UnloadHUD();
Core::TileID GetTileUnderMouse();
uint16_t GetTileIDUnderMouse();
[[nodiscard]] JUI::Scene* HUD() const { return hud; }
LocalWorld* World() const { return world;}

View File

@@ -285,7 +285,7 @@ void CaveGame::Client::GameSession::UnloadHUD() {
delete hud;
}
CaveGame::Core::TileID CaveGame::Client::GameSession::GetTileUnderMouse() {
uint16_t CaveGame::Client::GameSession::GetTileIDUnderMouse() {
auto ipos = InputService::GetMousePosition();
Vector2 pos = Vector2(ipos.x, ipos.y);

View File

@@ -1,17 +1,22 @@
[
{
"mnemonic-id" : "void",
"display-name" : "VOID",
"mnemonic-id": "void",
"display-name": "Void",
"item-tooltip": "How did you even get this?",
"solid": true,
"does-random-ticc": false,
"does-forced-ticc": false,
"color": "#FFFFFF",
"pallet": [],
"hardcoded-id": 65536
"hardcoded-id": 65536,
"random-ticc-func": "spread-self",
"spreads-to": "dirt",
"decays-to": "",
"draw-func": ""
},
{
"mnemonic-id" : "air",
"display-name" : "AIR",
"display-name" : "Air",
"solid": true,
"does-random-ticc": false,
"does-forced-ticc": false,
@@ -21,7 +26,7 @@
},
{
"mnemonic-id" : "stone",
"display-name" : "",
"display-name" : "Stone",
"solid": true,
"does-random-ticc": false,
"does-forced-ticc": false,
@@ -29,7 +34,43 @@
"pallet": []
},
{
"mnemonic-id" : "stone",
"mnemonic-id" : "dirt",
"display-name" : "Dirt",
"solid": true,
"does-random-ticc": false,
"does-forced-ticc": false,
"color": "#FFFFFF",
"pallet": []
},
{
"mnemonic-id" : "mud",
"display-name" : "Mud",
"solid": true,
"does-random-ticc": false,
"does-forced-ticc": false,
"color": "#FFFFFF",
"pallet": []
},
{
"mnemonic-id" : "limestone",
"display-name" : "Mud",
"solid": true,
"does-random-ticc": false,
"does-forced-ticc": false,
"color": "#FFFFFF",
"pallet": []
},
{
"mnemonic-id" : "basalt",
"display-name" : "Mud",
"solid": true,
"does-random-ticc": false,
"does-forced-ticc": false,
"color": "#FFFFFF",
"pallet": []
},
{
"mnemonic-id" : "cobblestone",
"display-name" : "",
"solid": true,
"does-random-ticc": false,
@@ -41,7 +82,53 @@
"drops" : null
},
{
"mnemonic-id" : "stone",
"mnemonic-id" : "red-moss",
"display-name" : "Mud",
"solid": true,
"does-random-ticc": false,
"does-forced-ticc": false,
"color": "#FFFFFF",
"pallet": []
},
{
"mnemonic-id" : "brown-moss",
"display-name" : "",
"solid": true,
"does-random-ticc": false,
"does-forced-ticc": false,
"color": "#FFFFFF",
"pallet": [],
"random-ticc-func": "zzyyzz",
"forced-ticc-func": "zzyyzz",
"drops" : null
},
{
"mnemonic-id" : "green-moss",
"display-name" : "",
"solid": true,
"does-random-ticc": false,
"does-forced-ticc": false,
"color": "#FFFFFF",
"pallet": [],
"random-ticc-func": "zzyyzz",
"forced-ticc-func": "zzyyzz",
"drops" : null
},
{
"mnemonic-id" : "lava-moss",
"display-name" : "",
"solid": true,
"does-random-ticc": false,
"does-forced-ticc": false,
"color": "#FFFFFF",
"pallet": [],
"random-ticc-func": "zzyyzz",
"forced-ticc-func": "zzyyzz",
"drops" : null
},
{
"mnemonic-id" : "granite",
"display-name" : "",
"solid": true,
"does-random-ticc": false,

View File

@@ -29,6 +29,7 @@
#include <Core/Player.hpp>
#include <Core/Explosion.hpp>
namespace CaveGame::ClientApp {
using CaveGame::Client::Scene;

View File

@@ -23,75 +23,9 @@
#include <JGL/logger/logger.h>
#include <JJX/JSON.hpp>
std::string read_file(const std::string& file_path)
{
std::ifstream file(file_path, std::ios::binary);
if (!file)
throw std::runtime_error("We couldn't find the file: " + file_path);
std::streamsize file_size;
file.seekg(0, std::ios::end);
file_size = file.tellg();
file.seekg(0, std::ios::beg);
std::string file_content(file_size, '\0');
file.read(&file_content[0], file_size);
file.close();
return file_content;
}
struct tile_meta
{
std::string mnemonic;
std::optional<std::vector<Color4>> pallet;
Color4 color;
bool solid;
bool does_random_ticc;
bool does_forced_ticc;
};
int main(int argc, char** argv) {
std::vector<tile_meta> tile_dataset;
using namespace JJX;
std::string content = read_file("assets/data/tiles/base_tiles.json");
auto [data, parse_error] = json::parse(content);
if (data.type == json::value_type::array)
{
auto arr_data = json::array_val(data);
for (auto& t : arr_data.array.value()) {
std::cout << (int)t.type << std::endl;
auto tile_meta_json = json::object_val(t);
tile_meta data;
data.mnemonic = tile_meta_json["mnemonic-id"];
json::value color_field = tile_meta_json["color"];
if (color_field.type == json::value_type::string)
data.color = Color4::FromHexA(color_field.string.value());
else if (color_field.type == json::value_type::array)
{
auto color_array = json::array_val(color_field);
int r = color_array[0].number.value();
int g = color_array[1].number.value();
int b = color_array[2].number.value();
int a = 255;
if (color_array.array.value().size() == 4)
a = color_array[3].number.value();
data.color = Color4(r, g, b, a);
}
tile_dataset.push_back(data);
}
}
// Hide logs from engine components so we can focus on CaveGame.
JGL::Logger::Warning.EnableConsole(false);
JGL::Logger::Debug.EnableConsole(false);

View File

@@ -8,11 +8,74 @@
#include <ReWindow/InputService.h>
#include <Core/Macros.hpp>
#include <jstick.hpp>
#include <Core/TileRegistry.hpp>
#include <JJX/JSON.hpp>
namespace CaveGame::ClientApp
{
std::string read_file(const std::string& file_path)
{
std::ifstream file(file_path, std::ios::binary);
if (!file)
throw std::runtime_error("We couldn't find the file: " + file_path);
std::streamsize file_size;
file.seekg(0, std::ios::end);
file_size = file.tellg();
file.seekg(0, std::ios::beg);
std::string file_content(file_size, '\0');
file.read(&file_content[0], file_size);
file.close();
return file_content;
}
void ReadTileDataAndRegister()
{
Core::Tile tile;
using namespace JJX;
std::string content = read_file("assets/data/tiles/base_tiles.json");
auto [data, parse_error] = json::parse(content);
if (data.type == json::value_type::array)
{
auto arr_data = json::array_val(data);
for (auto& t : arr_data.array.value()) {
std::cout << (int)t.type << std::endl;
auto tile_meta_json = json::object_val(t);
tile.mnemonic_id = tile_meta_json["mnemonic-id"];
json::value color_field = tile_meta_json["color"];
if (color_field.type == json::value_type::string)
tile.color = Color4::FromHexA(color_field.string.value());
else if (color_field.type == json::value_type::array)
{
auto color_array = json::array_val(color_field);
int r = color_array[0].number.value();
int g = color_array[1].number.value();
int b = color_array[2].number.value();
int a = 255;
if (color_array.array.value().size() == 4)
a = color_array[3].number.value();
tile.color = Color4(r, g, b, a);
}
}
}
Core::Tiles().Register(tile);
}
CaveGameWindow::CaveGameWindow(const std::string& title, int width, int height): ReWindow::OpenGLWindow(title, width, height, 2, 1)
{
Logs::Info("Parsing Tile Data.");
ReadTileDataAndRegister();
Logs::Info("Creating game window.");
CreateContexts();
@@ -281,8 +344,8 @@ namespace CaveGame::ClientApp
if (current_scene == GameSession()) {
Core::TileID id = game_ctx->GetTileUnderMouse();
Core::Tile* data = Core::GetByNumeric(id);
Core::TileID id = game_ctx->GetTileIDUnderMouse();
Core::Tile& data = Core::Tiles::GetInstance()->GetByNumericID(id);
debug_lines.push_back(std::format("tile: {} id: {}", data->MnemonicID(), (unsigned int)data->NumericID()));
debug_lines.push_back(std::format("entities: {}", game_ctx->World()->GetCurrentEntityCount()));
@@ -567,11 +630,12 @@ namespace CaveGame::ClientApp
void CaveGameWindow::MarkReadyToClose(bool close) { wanna_die = close;}
bool CaveGameWindow::TileListCmd(const CommandArgs &args) {
auto tile_list = Core::GetAllTiles();
auto tile_list = Core::Tiles::GetInstance()->GetTileList();
for (const Core::Tile& tile : tile_list) {
Log(std::format("ID: {}, Mnemonic: {}, DisplayName: {}", tile.numeric_id, tile.mnemonic_id, tile.display_name));
//if (tile != nullptr)
for (Core::Tile* tile : tile_list) {
if (tile != nullptr)
Log(std::format("ID: {}, Mnemonic: {}, DisplayName: {}", (int)tile->NumericID(), tile->MnemonicID(), tile->Name()));
}
return true;

View File

@@ -1,12 +1,23 @@
#pragma once
#include <cstdint>
#include <Core/TileRegistry.hpp>
namespace CaveGame::Core
{
// TODO: Consider implementing traits.
enum class TileID : std::uint16_t
using TileID = uint16_t;
#define SYMBOL(NAME) Tile NAME() { return TileRegistry().GetByMnemonicID("NAME"); }
namespace MappedIDs
{
SYMBOL(Void);
}
MappedIDs::Void();
/*enum class TileID : std::uint16_t
{
//
AIR = 0,
@@ -151,6 +162,6 @@ namespace CaveGame::Core
TORCH_EMBER = 256,
VOID = 65535,
};
};*/
}

View File

@@ -7,11 +7,14 @@ namespace CaveGame::Core
{
using TileState = uint16_t;
class ITileMap
{
public:
[[nodiscard]] virtual TileID GetTile(int x, int y) const = 0;
virtual void SetTile(int x, int y, TileID tile, bool flag_update = true) = 0;
virtual void SetTile(int x, int y, const std::string& tile_name, bool flag_update = true) = 0;
virtual void SwapTile(int source_x, int source_y, int dest_x, int dest_y, bool flag_update = true) = 0;
[[nodiscard]] virtual bool GetTileUpdateFlag(int x, int y) const = 0;
virtual void SetTileUpdateFlag(int x, int y, bool flag = true) = 0;

View File

@@ -1,6 +1,5 @@
#pragma once
namespace CaveGame::Core
{

View File

@@ -0,0 +1,65 @@
/// CaveGame - A procedural 2D platformer sandbox.
/// Created by Josh O'Leary @ Redacted Software, 2020-2024
/// Contact: josh@redacted.cc
/// Contributors: william@redacted.cc maxi@redacted.cc
/// This work is dedicated to the public domain.
/// @file Singleton.hpp
/// @desc An abstract class template that creates a corruption-safe, thread-safe Singleton.
/// @edit 3/20/2025
/// @auth Josh O'Leary
#pragma once
#include <mutex>
template <class T>
class Singleton
{
protected:
static std::mutex mutex;
static T* instance;
Singleton();
public:
/// Delete the copy constructor to prevent copies.
Singleton(const Singleton& obj) = delete;
/// Static method to get the Singleton instance.
static T* GetInstance();
static T* Instance() { return GetInstance();}
static T* operator()() { return GetInstance();}
T* operator->() { return instance;}
const T* operator->() const { return instance;}
T& operator*() { return *instance;}
const T& operator*() const { return *instance; }
};
template<class T>
T *Singleton<T>::GetInstance() {
if (instance == nullptr) {
std::lock_guard<std::mutex> lock(mutex);
instance = new T();
}
return instance;
}
template<class T>
Singleton<T>::Singleton() {
// Compile-time sanity check
static_assert(std::is_base_of<Singleton, T>::value, "T not derived from Singleton");
}
template <class T>
class ReferenceSingleton
{
protected:
static std::mutex mutex;
public:
static T& GetInstance()
{
static T& instance{};
return instance;
}
};

View File

@@ -9,9 +9,14 @@
#include <Core/Interfaces.hpp>
#include <J3ML/LinearAlgebra/Vector2i.hpp>
namespace CaveGame::Core {
using J3ML::LinearAlgebra::Vector2i;
using TileID = uint16_t;
struct Tile;
/*
*
* Tile and TileTrait example structure
@@ -105,114 +110,52 @@ namespace CaveGame::Core {
uint8_t g;
};
class TestTile;
class TileTrait {
public:
virtual void Invoke(ITileMap* world, int x, int y) {};
};
// Tile trait code to be fully implemented
/*//////////////////////////////////////////////////////////////////////////////*/
class TestTile
{
public:
TileID numeric_id = TileID::VOID;
std::string mnemonic_id;
public:
Color4 base_color;
std::vector<Color4> color_pallet;
//TileFlags flags = NonSolid;
public:
TestTile(TileID id, std::string mnemonic);
TileID NumericID() { return numeric_id; }
std::string MnemonicID() { return mnemonic_id; }
/*
bool HasTrait(TileTrait* tr) {
Tile* d;
if (Tile* d = dynamic_cast<Tile*>(tr); d != nullptr) {
return true;
}
return false;
}
*/
//TileFlags Flags();
//bool HasFlag(TileFlags f);
virtual void InvokeTraits(ITileMap* world, int x, int y) {};
};
class Ticker : public TileTrait {};
class RandomTicker : public Ticker {};
class ForcedTicker : public Ticker {};
class OnCondition : public TileTrait
class OnCondition
{
public:
OnCondition();
virtual bool Condition(ITileMap* world, int x, int y) { return true; }
virtual void Perform(ITileMap* world, int x, int y) {}
virtual void Invoke(ITileMap* world, int x, int y) override {
virtual void Invoke(ITileMap* world, int x, int y) {
if (Condition(world, x, y))
Perform(world, x, y);
}
};
class DecayTo : public OnCondition
{
public:
TestTile* dt;
public:
DecayTo(TestTile* dt) : OnCondition(), dt(dt) {};
virtual bool Condition(ITileMap* world, int x, int y) override { return true; }
virtual void Perform(ITileMap* world, int x, int y) override {
world->SetTile(x, y, dt->NumericID());
}
using ColorPallet = std::vector<Color4>;
using TileTiccFunc = std::function<void(const Tile& data, ITileMap* world, int x, int y)>;
std::unordered_map<std::string, TileTiccFunc> tile_functions {
{"check-spread", [](const Tile& data, ITileMap* world, int x, int y){
}},
{"check-decay", [](const Tile& data, ITileMap* world, int x, int y){}},
{"check-decay-spread", [](const Tile& data, ITileMap* world, int x, int y){}},
{"sand-ticc", [](const Tile& data, ITileMap* world, int x, int y){}},
{"water-ticc", [](const Tile& data, ITileMap* world, int x, int y){}},
{"liquid-ticc", [](const Tile& data, ITileMap* world, int x, int y)
{
}}
};
class Spreader : public OnCondition
{
public:
TestTile* st;
public:
Spreader(TestTile* st) : OnCondition(), st(st) {};
virtual bool Condition(ITileMap* world, int x, int y) override {
return (world->GetTile(x, y) == st->NumericID()) && world->HasAdjacentOrDiagonalAirBlock(x, y);
}
virtual void Perform(ITileMap* world, int x, int y) override {
world->SetTile(x, y, st->NumericID());
}
struct Tile {
std::string mnemonic_id;
std::string display_name;
std::optional<uint16_t> assigned_numeric_id;
uint16_t numeric_id;
std::optional<ColorPallet> pallet;
bool solid = true;
bool collides = false;
bool does_random_ticc = false;
bool does_forced_ticc = false;
Color4 color;
};
/*
class Suffocator : public TileTrait
{
public:
Suffocator() : TileTrait() {};
public:
virtual void Invoke(ITileMap* world, int x, int y) override {
if (!world->HasAdjacentOrDiagonalAirBlock(x, y))
return true;
return false;
}
};*/
/*//////////////////////////////////////////////////////////////////////////////*/
class TileGroup {};
class Tile;
void RegisterTile(Tile* data);
std::vector<Tile*> GetAllTiles();
Tile* GetByNumeric(TileID id);
Tile* GetByName(const std::string& name);
/// Tiles are instantiated as static members in the Tiles namespace.
/// Chunks store tiles by their numeric ID, which can be used to retrieve the tile
class Tile
/*class Tile
{
protected:
TileID numeric_id = TileID::VOID;
@@ -249,15 +192,7 @@ namespace CaveGame::Core {
virtual bool ShouldSuffocate(ITileMap* world, int x, int y) const;
virtual bool DecayCheck(ITileMap* world, TileState state, int x, int y, TileID decays_to);
virtual bool SpreadCheck(ITileMap* world, TileState state, int x, int y, TileID spreads_to) { return false;}
};
class PalletTile : public Tile
{
void Draw(int wx, int wy, int tx, int ty) override
{
}
};
};*/
class LiquidTile : public Tile
{
@@ -522,7 +457,7 @@ namespace CaveGame::Core {
#define TILE static const Tile
namespace Tiles {
/*namespace Tiles {
using ID = Core::TileID;
static const Tile Void {ID::VOID, "Void", Colors::Transparent};
@@ -676,6 +611,6 @@ namespace CaveGame::Core {
static const Tile Rope;
}
}*/
}

View File

@@ -0,0 +1,120 @@
#pragma once
#include "Singleton.hpp"
#include <string>
#include <cstdint>
#include <vector>
#include "Color4.hpp"
#include <optional>
#include <functional>
#include <array>
#include <Core/Tile.hpp>
namespace CaveGame::Core
{
class ITileMap;
using ColorPallet = std::vector<Color4>;
using TileTiccFunc = std::function<void(const Tile& data, ITileMap* world, int x, int y)>;
/*std::unordered_map<std::string, TileTiccFunc> tile_functions {
{"check-spread", [](const Tile& data, ITileMap* world, int x, int y){
}},
{"check-decay"},
{"check-decay-spread"},
{"sand-ticc"},
{"water-ticc"},
};*/
//using TileID = uint16_t;
class TileRegistry : public Singleton<TileRegistry> {
public:
static constexpr int MaxTiles = 65535;
void Register(Tile data) {
// If an ID is hardcoded to this tile, try to assign it.
if (data.assigned_numeric_id.has_value()) {
// Jump our current_index to this point
current_index = data.assigned_numeric_id.value();
// If this ID is consumed, scream an error.
if (consumed_ids.at(current_index))
throw std::runtime_error("ID is in use!!");
}
// If no ID is hardcoded, assign one from an increasing index.
data.numeric_id = current_index;
registered_tiles[current_index] = data;
// We also cache LUTs for fast access of name and integer ID from each other
id_to_name_mapping[current_index] = data.mnemonic_id;
name_to_id_mapping[data.mnemonic_id] = current_index;
consumed_ids[current_index] = true;
current_index++;
}
const Tile& Get(const std::string& mnemonic);
const Tile& Get(uint16_t ID);
// TODO: Why convert to vector?
std::vector<Tile> GetTileList() {
return {std::begin(registered_tiles), std::end(registered_tiles)};
}
const std::array<Tile, MaxTiles>& GetTileArray() { return registered_tiles; }
bool Exists(const std::string& mnemonic);
bool Exists(uint16_t ID);
const Tile& GetByMnemonicID(const std::string& mnemonic)
{
return registered_tiles[MnemonicToNumeric(mnemonic)];
}
const Tile& GetByNumericID(uint16_t ID)
{
return registered_tiles[ID];
}
uint16_t MnemonicToNumeric(const std::string& mnemonic);
std::string NumericToMnemonic(uint16_t ID)
{
return id_to_name_mapping[ID];
}
const Tile& operator[](const std::string& mnemonic)
{
return GetByMnemonicID(mnemonic);
}
const Tile& operator[](uint16_t ID)
{
return GetByNumericID(ID);
}
protected:
TileRegistry() {}
std::array<bool, MaxTiles> consumed_ids;
std::array<Tile, MaxTiles> registered_tiles{};
std::unordered_map<std::string, uint16_t> name_to_id_mapping;
std::unordered_map<uint16_t, std::string> id_to_name_mapping;
uint16_t current_index;
private:
};
using Tiles = TileRegistry;
void use_test()
{
//u16 id = TileRegistry::GetInstance()->GetByMnemonicID("stone");
}
}

View File

@@ -39,7 +39,7 @@ namespace CaveGame::Core
float rand = GetPrecomputedWhiteNoise2D(wx, wy)*(TopSoilDepth/2.f);
if (wy > 1000)
return TileID::STONE;
return TileRegistry::Get()["stone"].numeric_id;
if (depth < 0) { return TileID::AIR; }
else if (depth < 2.5f) { return TileID::GRASS; }

View File

@@ -1,13 +1,19 @@
#include <Core/Interfaces.hpp>
#include <Core/TileRegistry.hpp>
namespace CaveGame::Core
{
bool ITileMap::IsNonSolidTile(int x, int y) const
{
TileID tile = GetTile(x, y);
if (tile == TileID::AIR || tile == TileID::VINE || tile == TileID::WATER)
//if (tile == TileID::AIR || tile == TileID::VINE || tile == TileID::WATER)
//return true;
if (tile == 0 || !Tiles::GetInstance()->GetByNumericID(tile).solid)
return true;
return false;
}

View File

@@ -3,44 +3,7 @@
#include <array>
#include "Core/Item.hpp"
namespace CaveGame::Core
{
// TODO: Use unique_ptr?
// TODO: Create singleton TileRegistry class
std::array<Tile*, 65536> registered_tiles{};
std::vector<Tile*> GetAllTiles() {
return {std::begin(registered_tiles), std::end(registered_tiles)};
}
Tile *GetByNumeric(TileID id) {
// TODO: Optimize with additional mapping!!
//if (registered_tiles.at((uint16_t)id))
return registered_tiles[(uint16_t)id];
//for(auto& tile : registered_tiles)
//if (tile->numeric_id == id)
//return tile;
//throw std::runtime_error("ID not in tile registry!");
return nullptr;
}
void RegisterTile(Tile *data) {
if (data == nullptr)
return;
uint16_t index = (uint16_t)data->NumericID();
registered_tiles[index] = data;
//std::string name = std::format("{}_tile", data->MnemonicID());
//Core::Item item_for_this_tile = Core::Item(name);
//item_for_this_tile->mnemonic = name;
//item_for_this_tile->display_name = std::format("{} Tile", data->MnemonicID());
//RegisterItem(name, item_for_this_tile);
}
namespace CaveGame::Core {
void Tile::DecayTo(ITileMap *world, TileState state, int x, int y, TileID TDecaysTo) {
world->SetTile(x, y, TDecaysTo);

View File

@@ -0,0 +1,8 @@
#include <Core/TileRegistry.hpp>
namespace CaveGame::Core
{
uint16_t TileRegistry::MnemonicToNumeric(const std::string &mnemonic) {
return name_to_id_mapping[mnemonic];
}
}