Lots more partial progress on the data-driven refactoring.

This commit is contained in:
2025-03-21 03:00:21 -05:00
parent e0367b971c
commit 79f77a4fcf
14 changed files with 290 additions and 190 deletions

View File

@@ -0,0 +1,88 @@
[
{
"mnemonic-id": "gel",
"display-name": "Gel",
"tooltip": "",
"stack": 9999,
"value": 1,
"rarity": 0,
"sprite": "gel.png",
"usable" : true
},
{
"mnemonic-id": "glowstick",
"display-name": "Glowstick",
"tooltip": "",
"stack": 9999,
"value": 1,
"rarity": 0,
"sprite": "gel.png",
"usable" : true
},
{
"mnemonic-id": "copper-bar",
"display-name": "Copper Bar"
},
{
"mnemonic-id": "iron-bar",
"display-name": "Iron Bar"
},
{
"mnemonic-id": "silver-bar",
"display-name": "Silver Bar"
},
{
"mnemonic-id": "tungsten-bar",
"display-name": "Tungsten Bar"
},
{
"mnemonic-id": "platinum-bar",
"display-name": "Platinum Bar"
},
{
"mnemonic-id": "gold-bar",
"display-name": "Gold Bar"
},
{
"mnemonic-id": "lead-bar",
"display-name": "Lead Bar"
},
{
"mnemonic-id": "ziggy-stardust",
"display-name": "Ziggy Stardust"
},
{
"mnemonic-id": "nimdoc",
"display-name": "Nimdoc"
},
{
"mnemonic-id": "sawns",
"display-name": "Sawed-off Shotgun"
},
{
"mnemonic-id": "occupied-dwellinator",
"display-name": "Occupied Dwellinator"
},
{
"mnemonic-id": "copper-dagger",
"display-name": "Copper Dagger",
"sprite": "dagger.png",
"stack": 1,
"tags": ["dagger", "blade", "melee", "metal", "copper"]
},
{
"mnemonic-id": "iron-dagger",
"display-name": "Iron Dagger",
"sprite": "dagger.png",
"stack": 1,
"tags": ["dagger", "blade", "melee", "metal", "iron"]
},
{
"mnemonic-id": "rusty-iron-dagger",
"display-name": "Rusty Iron Dagger",
"sprite": "dagger.png",
"stack": 1,
"tags": ["dagger", "blade", "melee", "metal", "iron", "rusty"]
}
]

View File

@@ -1,11 +0,0 @@
[
{
},
{
},
{
}
]

View File

@@ -0,0 +1,20 @@
[
{ "station": "furnace", "consume" : ["copper-ore", 3], "produce" : "copper-bar" },
{ "station": "furnace", "consume" : ["iron-ore", 3], "produce" : "iron-bar" },
{ "station": "furnace", "consume" : ["silver-ore", 3], "produce" : "silver-bar" },
{ "station": "furnace", "consume" : ["tungsten-ore", 3], "produce" : "tungsten-bar" },
{ "station": "furnace", "consume" : ["platinum-ore", 3], "produce" : "platinum-bar" },
{ "station": "furnace", "consume" : ["gold-ore", 3], "produce" : "gold-bar" },
{ "station": "furnace", "consume" : ["lead-ore", 3], "produce" : "lead-bar" },
{
"station": "any",
"consume" : "wood-plank",
"produce" : ["stick", 8]
},
{
"station": "alchemy-lab",
"consume" : "lead-nugget",
"produce" : "gold-nugget",
"catalyst": "philosophers-stone"
}
]

View File

@@ -14,6 +14,9 @@
namespace CaveGame::ClientApp
{
using namespace CaveGame::Core;
using namespace CaveGame::Client;
std::string read_file(const std::string& file_path)
{
std::ifstream file(file_path, std::ios::binary);
@@ -32,12 +35,28 @@ namespace CaveGame::ClientApp
return file_content;
}
void ReadRecipesAndRegister() {}
void ReadItemDataAndRegister() {
Core::Item item;
using namespace JJX;
std::string content = read_file("assets/data/items.json");
auto [data, parse_error] = json::parse(content);
if (data.type != json::value_type::array) {
// TODO: Error
return;
}
}
void ReadTileDataAndRegister()
{
Core::Tile tile;
using namespace JJX;
std::string content = read_file("assets/data/tiles/base_tiles.json");
std::string content = read_file("assets/data/tiles.json");
auto [data, parse_error] = json::parse(content);
@@ -344,8 +363,8 @@ namespace CaveGame::ClientApp
if (current_scene == GameSession()) {
Core::TileID id = game_ctx->GetTileIDUnderMouse();
Core::Tile& data = Core::Tiles::GetInstance()->GetByNumericID(id);
u16 id = game_ctx->GetTileIDUnderMouse();
Core::Tile data = Tiles().Get(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()));
@@ -630,7 +649,7 @@ namespace CaveGame::ClientApp
void CaveGameWindow::MarkReadyToClose(bool close) { wanna_die = close;}
bool CaveGameWindow::TileListCmd(const CommandArgs &args) {
auto tile_list = Core::Tiles::GetInstance()->GetTileList();
auto tile_list = Core::Tiles().GetTileList();
for (const Core::Tile& tile : tile_list) {
Log(std::format("ID: {}, Mnemonic: {}, DisplayName: {}", tile.numeric_id, tile.mnemonic_id, tile.display_name));

View File

@@ -9,14 +9,7 @@ namespace CaveGame::Core
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
{
//

View File

@@ -1,13 +1,13 @@
#pragma once
#include <Color4.hpp>
#include <set>
#include <string>
#include <J3ML/Math.hpp>
#include <map>
#include <vector>
//#include <Core/Registry.hpp>
namespace CaveGame::Core
{
@@ -23,7 +23,6 @@ namespace CaveGame::Core
/// 7. Support attaching custom metadata to certain items.
/// This enum defines combinatorial category flags to items to enable smart sorting.
enum class ItemCategory
{
@@ -64,7 +63,7 @@ namespace CaveGame::Core
ANCIENT, VICTORIOUS, CONDEMNED, CLEAN, LUCKY, POWERFUL, SUPER,
};
class ItemComponent
/*class ItemComponent
{
};
@@ -72,21 +71,19 @@ namespace CaveGame::Core
class UseCooldown {};
class Consumable {};
class Drinkable {};
class Eatable {};
class Eatable {};*/
class Item
struct Item
{
public:
std::string mnemonic;
std::string display_name;
std::string tooltip;
std::string author;
int max_stack;
int value;
std::vector<std::string> tags;
unsigned int max_stack;
int value = 1;
Item() {}
explicit Item(const std::string& mnemonic);
std::optional<Color4> sprite_color;
protected:
@@ -94,18 +91,6 @@ namespace CaveGame::Core
private:
};
class TileItem : public Item {
};
void RegisterItem(const std::string& mnemonic, Item* item);
void RegisterNewItem(const std::string& mnemonic, const Item& item);
Item* GetItem(const std::string& mnemonic);
std::map<std::string, Item*> GetAllItems();
class ItemFilter
{
@@ -121,6 +106,7 @@ namespace CaveGame::Core
}
/*
namespace Items {
static const Item Straw;
@@ -244,7 +230,7 @@ namespace CaveGame::Core
static const Item EctoplasmBucket;
#pragma endregion
}
}*/
}

View File

@@ -1,6 +1,41 @@
#pragma once
#include <Core/Singleton.hpp>
#include <Core/Item.hpp>
#include <unordered_map>
#include <string>
#include <vector>
namespace CaveGame::Core
{
class ItemRegistry : public Singleton<ItemRegistry>
{
public:
void Register(Item data) {
Register(data.mnemonic, data);
}
bool Exists(const std::string& name) {
return registered_items.contains(name);
}
const Item& Get(const std::string& name) {
return registered_items.at(name);
}
std::unordered_map<std::string, Item> GetItemMap() { return registered_items;}
const Item& operator[](const std::string& name) { return Get(name);}
protected:
void Register(const std::string& mnemonic, const Item& data) {
registered_items.emplace(mnemonic, data);
}
protected:
std::unordered_map<std::string, Item> registered_items;
};
using Items = ItemRegistry;
}

View File

@@ -5,61 +5,36 @@
/// This work is dedicated to the public domain.
/// @file Singleton.hpp
/// @desc An abstract class template that creates a corruption-safe, thread-safe Singleton.
/// @desc An abstract class template that creates a Singleton.
/// @edit 3/20/2025
/// @auth Josh O'Leary
#pragma once
#include <mutex>
/// Template class for creating a Meyers' Singleton.
/// One and only one instance of this class can exist, and is alive throughout the lifetime of the program.
/// https://vlsiuniverse.blogspot.com/2016/04/meyers-singleton-pattern.html
/// The instance is lazy-initialized, meaning that:
/// if it is not a compile-time constant, and does not have a constructor,
/// it is only created upon the first call to Instance().
/// As per C++11, this is considered thread-safe out of the box.
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()
{
static T& instance{};
// Since it's a static scoped variable, if the class has already been created, it won't be created again.
// And it **is** thread safe (C++11 or later),
static T instance;
return instance;
}
static T& operator()() { return Instance();}
protected:
Singleton() = default;
virtual ~Singleton() = default;
Singleton(const Singleton&) = delete; // Copy constructor
Singleton(Singleton&&) = delete; // Move constructor
Singleton& operator=(const Singleton&) = delete; // Copy assign
Singleton& operator=(Singleton&&) = delete; // Move assign
};

View File

@@ -8,15 +8,32 @@
#include <functional>
#include <Core/Interfaces.hpp>
#include <J3ML/LinearAlgebra/Vector2i.hpp>
#include <J3ML/J3ML.hpp>
namespace CaveGame::Core {
using J3ML::LinearAlgebra::Vector2i;
using TileID = uint16_t;
using TileID = u16;
struct Tile;
using ColorPallet = std::vector<Color4>;
using TileTiccFunc = std::function<void(const Tile& data, ITileMap* world, int x, int y)>;
struct Tile {
std::string mnemonic_id;
std::string display_name;
std::optional<TileID> assigned_numeric_id;
TileID numeric_id;
std::optional<ColorPallet> pallet;
bool solid = true;
bool collides = false;
bool does_random_ticc = false;
bool does_forced_ticc = false;
Color4 color;
};
/*
*
* Tile and TileTrait example structure
@@ -90,18 +107,6 @@ namespace CaveGame::Core {
};
*/
enum TileFlags : uint16_t {
SOLID = 0x0000,
NONSOLID = !SOLID,
/*
NonSolid = 0x0000,
Solid = 0x0002,
RandomTick = 0x0004,
ForcedTick = 0x0008,
HasColorPallet = 0x0010,
*/
};
// Use this for space-efficiency and convert to Color3 for doing color manipulations.
// Struct allocation is cheaper.
struct Light3 {
@@ -111,6 +116,26 @@ namespace CaveGame::Core {
};
/*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 OnCondition
{
public:
@@ -123,36 +148,6 @@ namespace CaveGame::Core {
}
};
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)
{
}}
};
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;
};
/// 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
@@ -194,7 +189,7 @@ namespace CaveGame::Core {
virtual bool SpreadCheck(ITileMap* world, TileState state, int x, int y, TileID spreads_to) { return false;}
};*/
class LiquidTile : public Tile
/*class LiquidTile : public Tile
{
public:
LiquidTile();
@@ -245,7 +240,7 @@ namespace CaveGame::Core {
}
world->SwapTile(x, y, rng_roll ? (x + 2) : (x - 2), y);
return;
}*/
}
//if (world->GetTile(rng_roll ? (x + 1) : (x - 1), y) == TileID::AIR) {
TileID rollright = rng_roll ? right : left;
@@ -324,7 +319,7 @@ namespace CaveGame::Core {
world->SetTile(x-1, y+1, numeric_id);
world->SetTile(x, y, TileID::AIR);
return;
}*/
}
//if (world->GetTile(x-1, y) == TileID::AIR && world->GetTile(x+1, y) == TileID::AIR) {
//world->SetTile(x-1, y, numeric_id);

View File

@@ -8,13 +8,13 @@
#include <functional>
#include <array>
#include <Core/Tile.hpp>
#include <J3ML/J3ML.hpp>
namespace CaveGame::Core
{
class ITileMap;
namespace CaveGame::Core {
//class ITileMap;
using ColorPallet = std::vector<Color4>;
using TileTiccFunc = std::function<void(const Tile& data, ITileMap* world, int x, int y)>;
//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){
@@ -34,35 +34,12 @@ namespace CaveGame::Core
public:
static constexpr int MaxTiles = 65535;
void Register(Tile data) {
// If an ID is hardcoded to this tile, try to assign it.
void Register(Tile data);
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++;
}
void Register(TileID ID, const Tile& data);
const Tile& Get(const std::string& mnemonic);
const Tile& Get(uint16_t ID);
const Tile& Get(u16 ID);
// TODO: Why convert to vector?
std::vector<Tile> GetTileList() {
@@ -71,7 +48,7 @@ namespace CaveGame::Core
const std::array<Tile, MaxTiles>& GetTileArray() { return registered_tiles; }
bool Exists(const std::string& mnemonic);
bool Exists(uint16_t ID);
bool Exists(u16 ID);
const Tile& GetByMnemonicID(const std::string& mnemonic)
{
return registered_tiles[MnemonicToNumeric(mnemonic)];
@@ -90,7 +67,7 @@ namespace CaveGame::Core
{
return GetByMnemonicID(mnemonic);
}
const Tile& operator[](uint16_t ID)
const Tile& operator[](u16 ID)
{
return GetByNumericID(ID);
}
@@ -98,23 +75,13 @@ namespace CaveGame::Core
protected:
TileRegistry() {}
std::array<bool, MaxTiles> consumed_ids;
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;
std::unordered_map<std::string, u16> name_to_id_mapping{};
std::unordered_map<u16, std::string> id_to_name_mapping{};
u16 current_index = 0;
private:
};
using Tiles = TileRegistry;
void use_test()
{
//u16 id = TileRegistry::GetInstance()->GetByMnemonicID("stone");
}
}
}

View File

@@ -11,12 +11,12 @@ namespace CaveGame::Core
assert("Out of bounds!");
if (tiles[x][y] != t) {
Tile* old_tile = GetByNumeric(tiles[x][y]);
Tile* new_tile = GetByNumeric(t);
const Tile& old_tile = Tiles().GetByNumericID(tiles[x][y]);
const Tile& new_tile = Tiles().GetByNumericID(t);
if (old_tile->DoesRandomTicc())
if (old_tile.does_random_ticc)
tiles_with_random_tick_count--;
if (new_tile->DoesRandomTicc())
if (new_tile.does_random_ticc)
tiles_with_random_tick_count++;
tiles[x][y] = t;

View File

@@ -0,0 +1,6 @@
#include <Core/ItemRegistry.hpp>
namespace CaveGame::Core
{
}

View File

@@ -2,6 +2,33 @@
namespace CaveGame::Core
{
void TileRegistry::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++;
}
uint16_t TileRegistry::MnemonicToNumeric(const std::string &mnemonic) {
return name_to_id_mapping[mnemonic];
}