mirror of
https://github.com/zhenyan121/Cubed.git
synced 2026-08-08 09:47:01 +08:00
refactor(item): decouple items from block types
The item system now supports an ItemKind and property, allowing item JSON to declare a type instead of assuming every item is a block. BlockManager was moved to its own header, redesigned around concurrent hash maps, and exposes id_from_name(). Texture and UI code now key items by ItemID, while block placement resolves the block type from the item registry.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"id": 0,
|
||||
"name": "air",
|
||||
"description": ""
|
||||
"description": "",
|
||||
"type": "block"
|
||||
}
|
||||
@@ -2,5 +2,6 @@
|
||||
"id": 2,
|
||||
"name": "dirt",
|
||||
"description": "",
|
||||
"texture": "texture/item/block/dirt.png"
|
||||
}
|
||||
"texture": "texture/item/block/dirt.png",
|
||||
"type": "block"
|
||||
}
|
||||
@@ -2,5 +2,6 @@
|
||||
"id": 9,
|
||||
"name": "grass",
|
||||
"description": "",
|
||||
"texture": "texture/item/block/grass.png"
|
||||
}
|
||||
"texture": "texture/item/block/grass.png",
|
||||
"type": "block"
|
||||
}
|
||||
@@ -2,5 +2,6 @@
|
||||
"id": 1,
|
||||
"name": "grass_block",
|
||||
"description": "",
|
||||
"texture": "texture/item/block/grass_block.png"
|
||||
"texture": "texture/item/block/grass_block.png",
|
||||
"type": "block"
|
||||
}
|
||||
@@ -2,5 +2,6 @@
|
||||
"id": 6,
|
||||
"name": "leaf",
|
||||
"description": "",
|
||||
"texture": "texture/item/block/leaf.png"
|
||||
}
|
||||
"texture": "texture/item/block/leaf.png",
|
||||
"type": "block"
|
||||
}
|
||||
@@ -2,5 +2,6 @@
|
||||
"id": 5,
|
||||
"name": "log",
|
||||
"description": "",
|
||||
"texture": "texture/item/block/log.png"
|
||||
}
|
||||
"texture": "texture/item/block/log.png",
|
||||
"type": "block"
|
||||
}
|
||||
@@ -2,5 +2,6 @@
|
||||
"id": 4,
|
||||
"name": "sand",
|
||||
"description": "",
|
||||
"texture": "texture/item/block/sand.png"
|
||||
}
|
||||
"texture": "texture/item/block/sand.png",
|
||||
"type": "block"
|
||||
}
|
||||
@@ -2,5 +2,6 @@
|
||||
"id": 8,
|
||||
"name": "snowy_grass_block",
|
||||
"description": "",
|
||||
"texture": "texture/item/block/snowy_grass_block.png"
|
||||
}
|
||||
"texture": "texture/item/block/snowy_grass_block.png",
|
||||
"type": "block"
|
||||
}
|
||||
@@ -2,5 +2,6 @@
|
||||
"id": 3,
|
||||
"name": "stone",
|
||||
"description": "",
|
||||
"texture": "texture/item/block/stone.png"
|
||||
}
|
||||
"texture": "texture/item/block/stone.png",
|
||||
"type": "block"
|
||||
}
|
||||
@@ -2,5 +2,6 @@
|
||||
"id": 7,
|
||||
"name": "water",
|
||||
"description": "",
|
||||
"texture": "texture/item/block/water.png"
|
||||
}
|
||||
"texture": "texture/item/block/water.png",
|
||||
"type": "block"
|
||||
}
|
||||
@@ -63,35 +63,10 @@ struct BlockData {
|
||||
is_gas(gas), is_passable(passable), is_cross_plane(cross_plane),
|
||||
is_transparent(transparent), is_discard(discard), is_blend(blend),
|
||||
is_transitional(transitional), roughness(r) {}
|
||||
};
|
||||
|
||||
class BlockManager {
|
||||
|
||||
public:
|
||||
static const std::vector<BlockData>& datas();
|
||||
static void init();
|
||||
static unsigned sums();
|
||||
static unsigned cross_plane_sum();
|
||||
static const std::string& name_form_id(BlockType id);
|
||||
static std::string local_name(BlockType id);
|
||||
static bool is_gas(BlockType id);
|
||||
static bool is_liquid(BlockType id);
|
||||
|
||||
static bool is_cross_plane(BlockType id);
|
||||
static bool is_transparent(BlockType id);
|
||||
static bool is_passable(BlockType id);
|
||||
|
||||
static bool is_discard(BlockType id);
|
||||
static bool is_blend(BlockType id);
|
||||
static bool is_transitional(BlockType id);
|
||||
static float roughness(BlockType id);
|
||||
static BlockType cross_plane_index(BlockType id);
|
||||
|
||||
private:
|
||||
static void set_up_cross_plane_map();
|
||||
static inline std::vector<BlockData> m_datas;
|
||||
static inline bool is_init = false;
|
||||
static inline std::unordered_map<BlockType, BlockType> m_cross_plane_map;
|
||||
BlockData() {
|
||||
name = "";
|
||||
name_key = "";
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
45
include/Cubed/gameplay/block_manager.hpp
Normal file
45
include/Cubed/gameplay/block_manager.hpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/gameplay/block.hpp"
|
||||
|
||||
#include <tbb/concurrent_hash_map.h>
|
||||
namespace Cubed {
|
||||
class BlockManager {
|
||||
|
||||
public:
|
||||
static void init();
|
||||
static unsigned sums();
|
||||
static unsigned cross_plane_sum();
|
||||
static const std::string& name_form_id(BlockType id);
|
||||
static std::string local_name(BlockType id);
|
||||
static bool is_gas(BlockType id);
|
||||
static bool is_liquid(BlockType id);
|
||||
|
||||
static bool is_cross_plane(BlockType id);
|
||||
static bool is_transparent(BlockType id);
|
||||
static bool is_passable(BlockType id);
|
||||
|
||||
static bool is_discard(BlockType id);
|
||||
static bool is_blend(BlockType id);
|
||||
static bool is_transitional(BlockType id);
|
||||
static float roughness(BlockType id);
|
||||
static BlockType cross_plane_index(BlockType id);
|
||||
|
||||
static BlockType id_from_name(const std::string& name);
|
||||
|
||||
private:
|
||||
using BlockMap = tbb::concurrent_hash_map<BlockType, BlockData>;
|
||||
using acc = BlockMap::accessor;
|
||||
using cacc = BlockMap::const_accessor;
|
||||
using IDMap = tbb::concurrent_hash_map<std::string, BlockType>;
|
||||
using CrossPlaneMap = tbb::concurrent_hash_map<BlockType, BlockType>;
|
||||
|
||||
static const BlockData EMPTY;
|
||||
|
||||
static inline BlockMap m_datas;
|
||||
static inline IDMap m_id_map;
|
||||
static inline bool is_init = false;
|
||||
static inline CrossPlaneMap m_cross_plane_map;
|
||||
static void set_up_cross_plane_map();
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -1,14 +1,38 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/block.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
namespace Cubed {
|
||||
using ItemID = uint16_t;
|
||||
|
||||
enum class ItemKind {
|
||||
NONE,
|
||||
BLOCK,
|
||||
SPAWN_EGG
|
||||
|
||||
};
|
||||
|
||||
using ItemProperty = std::variant<BlockType, std::string>;
|
||||
|
||||
struct ItemData {
|
||||
ItemID id = 0;
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::string path;
|
||||
ItemKind kind = ItemKind::NONE;
|
||||
ItemProperty property;
|
||||
};
|
||||
|
||||
inline constexpr ItemKind get_item_kind(std::string_view kind) {
|
||||
if (kind == "block") {
|
||||
return ItemKind::BLOCK;
|
||||
}
|
||||
if (kind == "SPAWN_EGG") {
|
||||
return ItemKind::SPAWN_EGG;
|
||||
}
|
||||
return ItemKind::NONE;
|
||||
}
|
||||
|
||||
} // namespace Cubed
|
||||
@@ -15,6 +15,8 @@ public:
|
||||
static const ItemData& get(std::string_view key);
|
||||
static const ItemData& get(ItemID id);
|
||||
|
||||
static ItemID size();
|
||||
|
||||
const ItemData& get_item_data(std::string_view key) const;
|
||||
const ItemData& get_item_data(ItemID id) const;
|
||||
|
||||
@@ -24,12 +26,13 @@ private:
|
||||
using cacc = ItemMap::const_accessor;
|
||||
|
||||
using IDMap = tbb::concurrent_hash_map<std::string_view, ItemID>;
|
||||
|
||||
using BlockToIDMap = tbb::concurrent_hash_map<BlockType, ItemID>;
|
||||
void add(const std::filesystem::path& path);
|
||||
|
||||
ItemMap m_map;
|
||||
|
||||
IDMap m_id_map;
|
||||
BlockToIDMap m_block_to_id_map;
|
||||
|
||||
static constexpr ItemData EMPTY;
|
||||
};
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/block.hpp"
|
||||
#include "Cubed/gameplay/item.hpp"
|
||||
namespace Cubed {
|
||||
|
||||
struct ItemStack {
|
||||
BlockType type = 0;
|
||||
ItemID id = 0;
|
||||
size_t sum = 0;
|
||||
};
|
||||
} // namespace Cubed
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "Cubed/config.hpp"
|
||||
#include "Cubed/gameplay/block.hpp"
|
||||
#include "Cubed/gameplay/item.hpp"
|
||||
#include "Cubed/input/event.hpp"
|
||||
#include "Cubed/render/texture.hpp"
|
||||
|
||||
@@ -10,6 +11,26 @@
|
||||
namespace Cubed {
|
||||
|
||||
class TextureManager {
|
||||
public:
|
||||
using ItemTextureMap = std::unordered_map<ItemID, std::shared_ptr<Texture>>;
|
||||
TextureManager(Config& config);
|
||||
~TextureManager();
|
||||
|
||||
void delete_texture();
|
||||
const Texture* get_block_status_array() const;
|
||||
const Texture* get_texture_array() const;
|
||||
const Texture* get_cross_plane_array() const;
|
||||
const Texture* get_image_texture(const std::string& path);
|
||||
const Texture* get_pbr_texture() const;
|
||||
const ItemTextureMap& get_item_textures() const;
|
||||
const Texture* get_skin() const;
|
||||
void init_texture();
|
||||
|
||||
void need_reload();
|
||||
void update();
|
||||
int max_aniso() const;
|
||||
bool handle_event(const Event& e);
|
||||
|
||||
private:
|
||||
bool m_need_reload = false;
|
||||
bool m_init = false;
|
||||
@@ -18,7 +39,7 @@ private:
|
||||
std::unique_ptr<Texture> m_cross_plane_array;
|
||||
std::unique_ptr<Texture> m_normal_texture_array;
|
||||
std::unique_ptr<Texture> m_skin;
|
||||
std::vector<std::unique_ptr<Texture>> m_item_textures;
|
||||
ItemTextureMap m_item_textures;
|
||||
std::unordered_map<std::string, std::unique_ptr<Texture>> m_ui_map;
|
||||
GLfloat m_max_aniso = 0.0f;
|
||||
Config& m_config;
|
||||
@@ -26,7 +47,7 @@ private:
|
||||
|
||||
void load_block_status(unsigned status_id);
|
||||
void load_block_texture(unsigned block_id);
|
||||
void load_block_item_texture(unsigned id);
|
||||
void load_item_texture();
|
||||
void load_cross_plane_texture(unsigned id);
|
||||
const Texture* load_image_texture(const std::string& path);
|
||||
void load_pbr_texture(unsigned id);
|
||||
@@ -37,25 +58,6 @@ private:
|
||||
void init_skin();
|
||||
void hot_reload();
|
||||
bool handle_key_event(const KeyEvent& e);
|
||||
|
||||
public:
|
||||
TextureManager(Config& config);
|
||||
~TextureManager();
|
||||
|
||||
void delete_texture();
|
||||
const Texture* get_block_status_array() const;
|
||||
const Texture* get_texture_array() const;
|
||||
const Texture* get_cross_plane_array() const;
|
||||
const Texture* get_image_texture(const std::string& path);
|
||||
const Texture* get_pbr_texture() const;
|
||||
const std::vector<std::unique_ptr<Texture>>& get_item_textures() const;
|
||||
const Texture* get_skin() const;
|
||||
void init_texture();
|
||||
|
||||
void need_reload();
|
||||
void update();
|
||||
int max_aniso() const;
|
||||
bool handle_event(const Event& e);
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
@@ -20,7 +20,7 @@ private:
|
||||
Label* m_item_info = nullptr;
|
||||
|
||||
Image* m_selected_image = nullptr;
|
||||
BlockType m_selected_block = 0;
|
||||
BlockType m_selected_id = 0;
|
||||
void update_item_info();
|
||||
bool handle_mouse_button_event(const MouseButtonEvent& e) override;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/gameplay/block.hpp"
|
||||
#include "Cubed/gameplay/item.hpp"
|
||||
#include "Cubed/ui/image.hpp"
|
||||
#include "Cubed/ui/widget.hpp"
|
||||
namespace Cubed {
|
||||
@@ -13,11 +13,11 @@ public:
|
||||
|
||||
ItemSlot& set_default_background(TextureManager& m_texture_manager);
|
||||
ItemSlot& set_scale(float m_scale);
|
||||
ItemSlot& set_item(BlockType id, const Texture* texture);
|
||||
ItemSlot& set_item(ItemID id, const Texture* texture);
|
||||
float width() const override;
|
||||
float height() const override;
|
||||
bool handle_mouse_move_event(const MouseMoveEvent& e) override;
|
||||
BlockType block() const;
|
||||
ItemID id() const;
|
||||
bool hovered() const;
|
||||
|
||||
private:
|
||||
@@ -26,7 +26,7 @@ private:
|
||||
void on_update(float dt) override;
|
||||
std::unique_ptr<Image> m_background;
|
||||
std::unique_ptr<Image> m_foreground;
|
||||
BlockType m_block_type;
|
||||
ItemID m_id;
|
||||
float m_scale = 1.0f;
|
||||
bool m_hovered = false;
|
||||
};
|
||||
|
||||
@@ -105,4 +105,5 @@ target_sources(${PROJECT_NAME}
|
||||
gameplay/systems/wander_ai_system.cpp
|
||||
tools/json_utils.cpp
|
||||
gameplay/item_manager.cpp
|
||||
gameplay/block_manager.cpp
|
||||
)
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "Cubed/config.hpp"
|
||||
#include "Cubed/debug_collector.hpp"
|
||||
#include "Cubed/gameplay/block_manager.hpp"
|
||||
#include "Cubed/gameplay/item_manager.hpp"
|
||||
#include "Cubed/localization.hpp"
|
||||
#include "Cubed/tools/arg_parser.hpp"
|
||||
@@ -74,6 +75,7 @@ void App::init(int argc, char** argv) {
|
||||
Localization::instance().load_language(
|
||||
m_game_config.get("language", default_value));
|
||||
}
|
||||
BlockManager::init();
|
||||
ItemManager::instance().init();
|
||||
m_window.init(m_argument);
|
||||
m_window.imgui_init();
|
||||
@@ -81,7 +83,7 @@ void App::init(int argc, char** argv) {
|
||||
Logger::info("Window Init Success");
|
||||
|
||||
m_audio.init();
|
||||
BlockManager::init();
|
||||
|
||||
m_renderer.init();
|
||||
Logger::info("Renderer Init Success");
|
||||
// MapTable::init_map();
|
||||
|
||||
@@ -1,191 +0,0 @@
|
||||
#include "Cubed/gameplay/block.hpp"
|
||||
|
||||
#include "Cubed/localization.hpp"
|
||||
#include "Cubed/tools/cubed_assert.hpp"
|
||||
#include "Cubed/tools/log.hpp"
|
||||
#include "Cubed/tools/toml.utils.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
using namespace std::string_literals;
|
||||
using namespace Cubed::TOML;
|
||||
namespace {
|
||||
std::string block_data_dir = ASSETS_PATH + "data/block"s;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Cubed {
|
||||
|
||||
const std::vector<BlockData>& BlockManager::datas() {
|
||||
ASSERT(is_init);
|
||||
return m_datas;
|
||||
}
|
||||
|
||||
unsigned BlockManager::sums() {
|
||||
ASSERT(is_init);
|
||||
return m_datas.size();
|
||||
}
|
||||
unsigned BlockManager::cross_plane_sum() {
|
||||
ASSERT(is_init);
|
||||
return m_cross_plane_map.size();
|
||||
}
|
||||
|
||||
const std::string& BlockManager::name_form_id(BlockType id) {
|
||||
if (id >= sums()) {
|
||||
Logger::error("Id {}, is Over The Max Id", id, sums() - 1);
|
||||
return m_datas[0].name;
|
||||
}
|
||||
return m_datas[id].name;
|
||||
}
|
||||
std::string BlockManager::local_name(BlockType id) {
|
||||
if (id >= sums()) {
|
||||
Logger::error("Id {}, is Over The Max Id", id, sums() - 1);
|
||||
return tr(m_datas[0].name_key);
|
||||
}
|
||||
return tr(m_datas[id].name_key);
|
||||
}
|
||||
bool BlockManager::is_gas(BlockType id) {
|
||||
if (id >= sums()) {
|
||||
Logger::error("Id {}, is Over The Max Id", id, sums() - 1);
|
||||
return m_datas[0].is_gas;
|
||||
}
|
||||
return m_datas[id].is_gas;
|
||||
}
|
||||
bool BlockManager::is_liquid(BlockType id) {
|
||||
if (id >= sums()) {
|
||||
Logger::error("Id {}, is Over The Max Id", id, sums() - 1);
|
||||
return m_datas[0].is_liquid;
|
||||
}
|
||||
return m_datas[id].is_liquid;
|
||||
}
|
||||
|
||||
bool BlockManager::is_cross_plane(BlockType id) {
|
||||
if (id >= sums()) {
|
||||
Logger::error("Id {}, is Over The Max Id", id, sums() - 1);
|
||||
return m_datas[0].is_cross_plane;
|
||||
}
|
||||
return m_datas[id].is_cross_plane;
|
||||
}
|
||||
|
||||
bool BlockManager::is_transparent(BlockType id) {
|
||||
if (id >= sums()) {
|
||||
Logger::error("Id {}, is Over The Max Id", id, sums() - 1);
|
||||
return m_datas[0].is_transparent;
|
||||
}
|
||||
return m_datas[id].is_transparent;
|
||||
}
|
||||
bool BlockManager::is_passable(BlockType id) {
|
||||
if (id >= sums()) {
|
||||
Logger::error("Id {}, is Over The Max Id", id, sums() - 1);
|
||||
return m_datas[0].is_passable;
|
||||
}
|
||||
return m_datas[id].is_passable;
|
||||
}
|
||||
|
||||
bool BlockManager::is_discard(BlockType id) {
|
||||
if (id >= sums()) {
|
||||
Logger::error("Id {}, is Over The Max Id", id, sums() - 1);
|
||||
return m_datas[0].is_discard;
|
||||
}
|
||||
return m_datas[id].is_discard;
|
||||
}
|
||||
bool BlockManager::is_blend(BlockType id) {
|
||||
if (id >= sums()) {
|
||||
Logger::error("Id {}, is Over The Max Id", id, sums() - 1);
|
||||
return m_datas[0].is_blend;
|
||||
}
|
||||
return m_datas[id].is_blend;
|
||||
}
|
||||
bool BlockManager::is_transitional(BlockType id) {
|
||||
if (id >= sums()) {
|
||||
Logger::error("Id {}, is Over The Max Id", id, sums() - 1);
|
||||
return m_datas[0].is_transitional;
|
||||
}
|
||||
return m_datas[id].is_transitional;
|
||||
}
|
||||
|
||||
float BlockManager::roughness(BlockType id) {
|
||||
if (id >= sums()) {
|
||||
Logger::error("Id {}, is Over The Max Id", id, sums() - 1);
|
||||
return m_datas[0].roughness;
|
||||
}
|
||||
return m_datas[id].roughness;
|
||||
}
|
||||
|
||||
void BlockManager::init() {
|
||||
fs::path data_path{block_data_dir};
|
||||
|
||||
for (auto entry : fs::recursive_directory_iterator(data_path)) {
|
||||
if (!entry.is_regular_file()) {
|
||||
continue;
|
||||
}
|
||||
if (entry.path().filename() == "template.toml") {
|
||||
continue;
|
||||
}
|
||||
toml::table block;
|
||||
try {
|
||||
block = toml::parse_file(entry.path().string());
|
||||
} catch (const toml::parse_error& err) {
|
||||
Logger::error("Load Block Data {} Fail, Parser Error {}",
|
||||
entry.path().string(), err.what());
|
||||
ASSERT(false);
|
||||
}
|
||||
auto id = block["id"].value<int>();
|
||||
if (id == std::nullopt) {
|
||||
Logger::error("Very Serious Error, Block Id Not Find !!!, Please "
|
||||
"Check The Block Data Integrity");
|
||||
std::abort();
|
||||
}
|
||||
auto name = block["name"].value<std::string>();
|
||||
if (name == std::nullopt) {
|
||||
Logger::error("Very Serious Error, Block Name Not Find !!!, Please "
|
||||
"Check The Block Data Integrity");
|
||||
std::abort();
|
||||
}
|
||||
auto is_liquid = safe_get_value(block, "is_liquid", false);
|
||||
auto is_passable = safe_get_value(block, "is_passable", false);
|
||||
auto is_cross_plane = safe_get_value(block, "is_cross_plane", false);
|
||||
auto is_transparent = safe_get_value(block, "is_transparent", false);
|
||||
auto is_gas = safe_get_value(block, "is_gas", false);
|
||||
auto is_discard = safe_get_value(block, "is_discard", false);
|
||||
auto is_blend = safe_get_value(block, "is_blend", false);
|
||||
auto is_transitional = safe_get_value(block, "is_transitional", false);
|
||||
auto roughness = safe_get_value(block, "roughness", 1.0);
|
||||
auto name_key = safe_get_value(block, "name_key", "Unknow_key");
|
||||
m_datas.emplace_back(*name, *name_key, *id, *is_liquid, *is_passable,
|
||||
*is_cross_plane, *is_transparent, *is_gas,
|
||||
*is_discard, *is_blend, *is_transitional,
|
||||
static_cast<float>(*roughness));
|
||||
}
|
||||
std::sort(
|
||||
m_datas.begin(), m_datas.end(),
|
||||
[](const BlockData& a, const BlockData& b) { return a.id < b.id; });
|
||||
|
||||
set_up_cross_plane_map();
|
||||
is_init = true;
|
||||
}
|
||||
|
||||
BlockType BlockManager::cross_plane_index(BlockType id) {
|
||||
auto it = m_cross_plane_map.find(id);
|
||||
if (it == m_cross_plane_map.end()) {
|
||||
Logger::error("Can't Find Cross Plane Id {}", id);
|
||||
ASSERT(false);
|
||||
throw std::out_of_range{"Can't Find Cross Plane Id" +
|
||||
std::to_string(id)};
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void BlockManager::set_up_cross_plane_map() {
|
||||
unsigned cur_id = 0;
|
||||
for (const auto& data : m_datas) {
|
||||
if (data.is_cross_plane) {
|
||||
m_cross_plane_map[data.id] = cur_id;
|
||||
cur_id++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Cubed
|
||||
211
src/gameplay/block_manager.cpp
Normal file
211
src/gameplay/block_manager.cpp
Normal file
@@ -0,0 +1,211 @@
|
||||
#include "Cubed/gameplay/block_manager.hpp"
|
||||
|
||||
#include "Cubed/localization.hpp"
|
||||
#include "Cubed/tools/cubed_assert.hpp"
|
||||
#include "Cubed/tools/log.hpp"
|
||||
#include "Cubed/tools/toml.utils.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
using namespace std::string_literals;
|
||||
using namespace Cubed::TOML;
|
||||
namespace {
|
||||
std::string block_data_dir = ASSETS_PATH + "data/block"s;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Cubed {
|
||||
|
||||
unsigned BlockManager::sums() {
|
||||
ASSERT(is_init);
|
||||
return m_datas.size();
|
||||
}
|
||||
unsigned BlockManager::cross_plane_sum() {
|
||||
ASSERT(is_init);
|
||||
return m_cross_plane_map.size();
|
||||
}
|
||||
|
||||
const std::string& BlockManager::name_form_id(BlockType id) {
|
||||
cacc c;
|
||||
if (!m_datas.find(c, id)) {
|
||||
ASSERT(false);
|
||||
return EMPTY.name;
|
||||
}
|
||||
return c->second.name;
|
||||
}
|
||||
std::string BlockManager::local_name(BlockType id) {
|
||||
cacc c;
|
||||
if (!m_datas.find(c, id)) {
|
||||
ASSERT(false);
|
||||
return EMPTY.name_key;
|
||||
}
|
||||
return tr(c->second.name_key);
|
||||
}
|
||||
bool BlockManager::is_gas(BlockType id) {
|
||||
cacc c;
|
||||
if (!m_datas.find(c, id)) {
|
||||
ASSERT(false);
|
||||
return EMPTY.is_gas;
|
||||
}
|
||||
return c->second.is_gas;
|
||||
}
|
||||
bool BlockManager::is_liquid(BlockType id) {
|
||||
cacc c;
|
||||
if (!m_datas.find(c, id)) {
|
||||
ASSERT(false);
|
||||
return EMPTY.is_liquid;
|
||||
}
|
||||
return c->second.is_liquid;
|
||||
}
|
||||
|
||||
bool BlockManager::is_cross_plane(BlockType id) {
|
||||
cacc c;
|
||||
if (!m_datas.find(c, id)) {
|
||||
ASSERT(false);
|
||||
return EMPTY.is_cross_plane;
|
||||
}
|
||||
return c->second.is_cross_plane;
|
||||
}
|
||||
|
||||
bool BlockManager::is_transparent(BlockType id) {
|
||||
cacc c;
|
||||
if (!m_datas.find(c, id)) {
|
||||
ASSERT(false);
|
||||
return EMPTY.is_transparent;
|
||||
}
|
||||
return c->second.is_transparent;
|
||||
}
|
||||
bool BlockManager::is_passable(BlockType id) {
|
||||
cacc c;
|
||||
if (!m_datas.find(c, id)) {
|
||||
ASSERT(false);
|
||||
return EMPTY.is_passable;
|
||||
}
|
||||
return c->second.is_passable;
|
||||
}
|
||||
|
||||
bool BlockManager::is_discard(BlockType id) {
|
||||
cacc c;
|
||||
if (!m_datas.find(c, id)) {
|
||||
ASSERT(false);
|
||||
return EMPTY.is_discard;
|
||||
}
|
||||
return c->second.is_discard;
|
||||
}
|
||||
bool BlockManager::is_blend(BlockType id) {
|
||||
cacc c;
|
||||
if (!m_datas.find(c, id)) {
|
||||
ASSERT(false);
|
||||
return EMPTY.is_blend;
|
||||
}
|
||||
return c->second.is_blend;
|
||||
}
|
||||
bool BlockManager::is_transitional(BlockType id) {
|
||||
cacc c;
|
||||
if (!m_datas.find(c, id)) {
|
||||
ASSERT(false);
|
||||
return EMPTY.is_transitional;
|
||||
}
|
||||
return c->second.is_transitional;
|
||||
}
|
||||
|
||||
float BlockManager::roughness(BlockType id) {
|
||||
cacc c;
|
||||
if (!m_datas.find(c, id)) {
|
||||
ASSERT(false);
|
||||
return EMPTY.roughness;
|
||||
}
|
||||
return c->second.roughness;
|
||||
}
|
||||
|
||||
void BlockManager::init() {
|
||||
fs::path data_path{block_data_dir};
|
||||
|
||||
for (auto entry : fs::recursive_directory_iterator(data_path)) {
|
||||
if (!entry.is_regular_file()) {
|
||||
continue;
|
||||
}
|
||||
if (entry.path().filename() == "template.toml") {
|
||||
continue;
|
||||
}
|
||||
toml::table block;
|
||||
try {
|
||||
block = toml::parse_file(entry.path().string());
|
||||
} catch (const toml::parse_error& err) {
|
||||
Logger::error("Load Block Data {} Fail, Parser Error {}",
|
||||
entry.path().string(), err.what());
|
||||
ASSERT(false);
|
||||
}
|
||||
auto id = block["id"].value<int>();
|
||||
if (id == std::nullopt) {
|
||||
Logger::error("Very Serious Error, Block Id Not Find !!!, Please "
|
||||
"Check The Block Data Integrity");
|
||||
std::abort();
|
||||
}
|
||||
auto name = block["name"].value<std::string>();
|
||||
if (name == std::nullopt) {
|
||||
Logger::error("Very Serious Error, Block Name Not Find !!!, Please "
|
||||
"Check The Block Data Integrity");
|
||||
std::abort();
|
||||
}
|
||||
auto is_liquid = safe_get_value(block, "is_liquid", false);
|
||||
auto is_passable = safe_get_value(block, "is_passable", false);
|
||||
auto is_cross_plane = safe_get_value(block, "is_cross_plane", false);
|
||||
auto is_transparent = safe_get_value(block, "is_transparent", false);
|
||||
auto is_gas = safe_get_value(block, "is_gas", false);
|
||||
auto is_discard = safe_get_value(block, "is_discard", false);
|
||||
auto is_blend = safe_get_value(block, "is_blend", false);
|
||||
auto is_transitional = safe_get_value(block, "is_transitional", false);
|
||||
auto roughness = safe_get_value(block, "roughness", 1.0);
|
||||
auto name_key = safe_get_value(block, "name_key", "Unknow_key");
|
||||
BlockData data{
|
||||
*name, *name_key, static_cast<BlockType>(*id),
|
||||
*is_liquid, *is_passable, *is_cross_plane,
|
||||
*is_transparent, *is_gas, *is_discard,
|
||||
*is_blend, *is_transitional, static_cast<float>(*roughness)};
|
||||
|
||||
if (!m_datas.emplace(static_cast<BlockType>(*id), std::move(data))) {
|
||||
Logger::error("Block Type {} already exist!", *id);
|
||||
}
|
||||
}
|
||||
|
||||
set_up_cross_plane_map();
|
||||
is_init = true;
|
||||
}
|
||||
|
||||
BlockType BlockManager::cross_plane_index(BlockType id) {
|
||||
CrossPlaneMap::const_accessor c;
|
||||
if (!m_cross_plane_map.find(c, id)) {
|
||||
Logger::error("Can't Find Cross Plane Id {}", id);
|
||||
ASSERT(false);
|
||||
throw std::out_of_range{"Can't Find Cross Plane Id" +
|
||||
std::to_string(id)};
|
||||
}
|
||||
|
||||
return c->second;
|
||||
}
|
||||
|
||||
BlockType BlockManager::id_from_name(const std::string& name) {
|
||||
IDMap::const_accessor c;
|
||||
if (m_id_map.find(c, name)) {
|
||||
return c->second;
|
||||
}
|
||||
Logger::error("BlockManager: Can't fin Block {}", name);
|
||||
ASSERT(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void BlockManager::set_up_cross_plane_map() {
|
||||
unsigned cur_id = 0;
|
||||
for (const auto& [id, data] : m_datas) {
|
||||
if (data.is_cross_plane) {
|
||||
m_cross_plane_map.emplace(data.id, cur_id);
|
||||
|
||||
cur_id++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Cubed
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "Cubed/gameplay/item_manager.hpp"
|
||||
|
||||
#include "Cubed/gameplay/block_manager.hpp"
|
||||
#include "Cubed/tools/cubed_assert.hpp"
|
||||
#include "Cubed/tools/log.hpp"
|
||||
|
||||
@@ -63,27 +64,41 @@ void ItemManager::add(const std::filesystem::path& path) {
|
||||
if (doc.HasMember("texture")) {
|
||||
data.path = doc["texture"].GetString();
|
||||
}
|
||||
if (doc.HasMember("type")) {
|
||||
std::string type = doc["type"].GetString();
|
||||
data.kind = get_item_kind(type);
|
||||
}
|
||||
|
||||
acc a;
|
||||
if (m_map.emplace(a, data.id, std::move(data))) {
|
||||
if (!m_id_map.emplace(a->second.name, a->first)) {
|
||||
Logger::error("ItemManager: Can't Insterd {} {} to id map",
|
||||
a->second.name, a->first);
|
||||
}
|
||||
if (a->second.kind == ItemKind::BLOCK) {
|
||||
BlockType b = BlockManager::id_from_name(a->second.name);
|
||||
m_block_to_id_map.emplace(b, a->first);
|
||||
}
|
||||
|
||||
} else {
|
||||
Logger::error("ItemManager: Can't Insterd {} to map", path.string());
|
||||
}
|
||||
}
|
||||
|
||||
const ItemData& ItemManager::get_item_data(std::string_view key) const {
|
||||
IDMap::const_accessor cacc;
|
||||
ItemID id = 0;
|
||||
if (m_id_map.find(cacc, key)) {
|
||||
id = cacc->second;
|
||||
} else {
|
||||
Logger::error("Can't Find key {} in id map", key);
|
||||
ASSERT(false);
|
||||
return EMPTY;
|
||||
{
|
||||
IDMap::const_accessor cacc;
|
||||
|
||||
if (m_id_map.find(cacc, key)) {
|
||||
id = cacc->second;
|
||||
} else {
|
||||
Logger::error("Can't Find key {} in id map", key);
|
||||
ASSERT(false);
|
||||
return EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
return get_item_data(id);
|
||||
}
|
||||
const ItemData& ItemManager::get_item_data(ItemID id) const {
|
||||
@@ -102,5 +117,5 @@ const ItemData& ItemManager::get(std::string_view key) {
|
||||
const ItemData& ItemManager::get(ItemID id) {
|
||||
return instance().get_item_data(id);
|
||||
}
|
||||
|
||||
ItemID ItemManager::size() { return instance().m_map.size(); }
|
||||
} // namespace Cubed
|
||||
@@ -3,9 +3,10 @@
|
||||
#include "Cubed/audio/audio_engine.hpp"
|
||||
#include "Cubed/config.hpp"
|
||||
#include "Cubed/debug_collector.hpp"
|
||||
#include "Cubed/gameplay/block_manager.hpp"
|
||||
#include "Cubed/gameplay/client_world.hpp"
|
||||
#include "Cubed/gameplay/hitbox_manager.hpp"
|
||||
|
||||
#include "Cubed/gameplay/item_manager.hpp"
|
||||
namespace Cubed {
|
||||
|
||||
namespace {
|
||||
@@ -406,7 +407,10 @@ void LocalPlayer::place_block(float dt) {
|
||||
}
|
||||
}
|
||||
if (m_mouse_state.right) {
|
||||
auto type = m_hotbar[m_selected_hotbar].type;
|
||||
auto& data = ItemManager::get(m_hotbar[m_selected_hotbar].id);
|
||||
auto* t = std::get_if<BlockType>(&data.property);
|
||||
ASSERT(t);
|
||||
auto type = *t;
|
||||
if (type != 0) {
|
||||
glm::ivec3 near_pos = m_look_block->pos + m_look_block->normal;
|
||||
if (!m_world.is_solid(near_pos)) {
|
||||
@@ -683,7 +687,7 @@ void LocalPlayer::init(std::string_view name) {
|
||||
});
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
m_hotbar[i].type = i;
|
||||
m_hotbar[i].id = i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
|
||||
#include "Cubed/config.hpp"
|
||||
#include "Cubed/constants.hpp"
|
||||
#include "Cubed/gameplay/block_manager.hpp"
|
||||
#include "Cubed/gameplay/item_manager.hpp"
|
||||
#include "Cubed/tools/cubed_assert.hpp"
|
||||
#include "Cubed/tools/log.hpp"
|
||||
#include "Cubed/tools/shader_tools.hpp"
|
||||
|
||||
namespace {
|
||||
constexpr int BLOCK_SIZE = 16;
|
||||
constexpr int BLOCK_NORMAL_SIZE = 128;
|
||||
@@ -69,7 +70,7 @@ const Texture* TextureManager::get_pbr_texture() const {
|
||||
return m_normal_texture_array.get();
|
||||
}
|
||||
|
||||
const std::vector<std::unique_ptr<Texture>>&
|
||||
const TextureManager::ItemTextureMap&
|
||||
TextureManager::get_item_textures() const {
|
||||
return m_item_textures;
|
||||
}
|
||||
@@ -121,23 +122,20 @@ void TextureManager::load_block_texture(unsigned id) {
|
||||
}
|
||||
}
|
||||
|
||||
void TextureManager::load_block_item_texture(unsigned id) {
|
||||
void TextureManager::load_item_texture() {
|
||||
for (ItemID i = 0; i < ItemManager::size(); ++i) {
|
||||
auto& item = ItemManager::get(i);
|
||||
auto data = Tools::load_image_data(item.path);
|
||||
std::unique_ptr<Texture> texture =
|
||||
std::make_unique<Texture>(TextureType::TEXTURE_2D);
|
||||
texture->tex_image_2d(TextureFormat::RGBA8, TextureFormat::RGBA,
|
||||
GL_UNSIGNED_BYTE, data.data, BLOCK_ITEM_SIZE,
|
||||
BLOCK_ITEM_SIZE);
|
||||
|
||||
ASSERT_MSG(id < BlockManager::sums(), "Exceed the max block sum limit");
|
||||
std::string name{BlockManager::name_form_id(id)};
|
||||
texture->set_nearest();
|
||||
|
||||
std::string path = "texture/item/block/" + name + ".png";
|
||||
|
||||
auto data = Tools::load_image_data(path);
|
||||
std::unique_ptr<Texture> texture =
|
||||
std::make_unique<Texture>(TextureType::TEXTURE_2D);
|
||||
texture->tex_image_2d(TextureFormat::RGBA8, TextureFormat::RGBA,
|
||||
GL_UNSIGNED_BYTE, data.data, BLOCK_ITEM_SIZE,
|
||||
BLOCK_ITEM_SIZE);
|
||||
|
||||
texture->set_nearest();
|
||||
|
||||
m_item_textures.push_back(std::move(texture));
|
||||
m_item_textures.try_emplace(item.id, std::move(texture));
|
||||
}
|
||||
}
|
||||
|
||||
void TextureManager::load_cross_plane_texture(unsigned id) {
|
||||
@@ -224,7 +222,7 @@ void TextureManager::init_block() {
|
||||
BLOCK_NORMAL_SIZE, BLOCK_NORMAL_SIZE, BlockManager::sums() * 6);
|
||||
for (unsigned i = 0; i < BlockManager::sums(); i++) {
|
||||
load_block_texture(i);
|
||||
load_block_item_texture(i);
|
||||
|
||||
load_pbr_texture(i);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "Cubed/ui/inventory_ui.hpp"
|
||||
|
||||
#include "Cubed/app.hpp"
|
||||
#include "Cubed/gameplay/block_manager.hpp"
|
||||
#include "Cubed/scene/scene_manager.hpp"
|
||||
#include "Cubed/scene/world_scene.hpp"
|
||||
#include "Cubed/ui/column_layout.hpp"
|
||||
@@ -20,21 +21,26 @@ void InventoryUI::init() {
|
||||
auto& column = back->add_child<ColumnLayout>();
|
||||
column.set_anchor(Anchor::CENTER);
|
||||
column.set_child_anchor(ColumnLayoutAnchor::LEFT);
|
||||
auto& block_textures = texture_manager.get_item_textures();
|
||||
auto sum = block_textures.size();
|
||||
auto& item_textures = texture_manager.get_item_textures();
|
||||
auto sum = item_textures.size();
|
||||
{
|
||||
auto& row_layout = column.add_child<RowLayout>();
|
||||
auto row = &row_layout;
|
||||
for (size_t i = 1; i < sum; ++i) {
|
||||
if ((i - 1) % 10 == 0) {
|
||||
size_t i = 0;
|
||||
for (auto& [id, texture] : item_textures) {
|
||||
if (i % 10 == 0) {
|
||||
auto& r = column.add_child<RowLayout>();
|
||||
row = &r;
|
||||
}
|
||||
auto& slot = row->add_child<ItemSlot>();
|
||||
slot.set_default_background(texture_manager);
|
||||
slot.set_scale(5.0f);
|
||||
slot.set_item(i, block_textures[i].get());
|
||||
|
||||
slot.set_item(id, texture.get());
|
||||
m_slots.emplace_back(&slot);
|
||||
++i;
|
||||
}
|
||||
for (size_t i = 1; i < sum; ++i) {
|
||||
}
|
||||
}
|
||||
{
|
||||
@@ -62,10 +68,12 @@ void InventoryUI::init() {
|
||||
auto& item = row.add_child<ItemSlot>();
|
||||
item.set_default_background(texture_manager);
|
||||
item.set_scale(5.0f);
|
||||
if (h.type == 0) {
|
||||
if (h.id == 0) {
|
||||
item.set_item(0, nullptr);
|
||||
} else {
|
||||
item.set_item(h.type, block_textures[h.type].get());
|
||||
auto it = item_textures.find(h.id);
|
||||
ASSERT(it != item_textures.end());
|
||||
item.set_item(h.id, it->second.get());
|
||||
}
|
||||
m_hotbar.emplace_back(&item);
|
||||
}
|
||||
@@ -89,7 +97,7 @@ void InventoryUI::update(float dt) {
|
||||
void InventoryUI::update_item_info() {
|
||||
auto show_item_info = [this](ItemSlot* slot) {
|
||||
if (slot && !m_selected_image->has_texture()) {
|
||||
auto type = slot->block();
|
||||
auto type = slot->id();
|
||||
if (type != 0) {
|
||||
m_item_info->set_text(BlockManager::local_name(type))
|
||||
.set_visible(true);
|
||||
@@ -117,16 +125,17 @@ bool InventoryUI::handle_mouse_button_event(const MouseButtonEvent& e) {
|
||||
if (e.action == KeyAction::PRESS && e.key == MouseKey::LEFT_BUTTON) {
|
||||
|
||||
auto& texture_manager = m_scene.scene_manager().app().texture_manager();
|
||||
auto& blocks = texture_manager.get_item_textures();
|
||||
auto& item_textures = texture_manager.get_item_textures();
|
||||
if (!m_selected_image->has_texture()) {
|
||||
|
||||
{
|
||||
auto slot = get_hovered_slot();
|
||||
if (slot) {
|
||||
m_selected_block = slot->block();
|
||||
if (m_selected_block != 0) {
|
||||
m_selected_image->set_texture(
|
||||
blocks[m_selected_block].get(), true);
|
||||
m_selected_id = slot->id();
|
||||
if (m_selected_id != 0) {
|
||||
auto it = item_textures.find(m_selected_id);
|
||||
ASSERT(it != item_textures.end());
|
||||
m_selected_image->set_texture(it->second.get(), true);
|
||||
m_selected_image->set_visible(true);
|
||||
return true;
|
||||
}
|
||||
@@ -135,10 +144,11 @@ bool InventoryUI::handle_mouse_button_event(const MouseButtonEvent& e) {
|
||||
{
|
||||
auto [slot, pos] = get_hovered_hotbar_slot();
|
||||
if (slot) {
|
||||
m_selected_block = slot->block();
|
||||
if (m_selected_block != 0) {
|
||||
m_selected_image->set_texture(
|
||||
blocks[m_selected_block].get(), true);
|
||||
m_selected_id = slot->id();
|
||||
if (m_selected_id != 0) {
|
||||
auto it = item_textures.find(m_selected_id);
|
||||
ASSERT(it != item_textures.end());
|
||||
m_selected_image->set_texture(it->second.get(), true);
|
||||
m_hotbar[pos]->set_item(0, nullptr);
|
||||
auto& player = m_scene.client_world().get_player();
|
||||
player.set_hotbar(pos, {0, 0});
|
||||
@@ -154,10 +164,11 @@ bool InventoryUI::handle_mouse_button_event(const MouseButtonEvent& e) {
|
||||
auto [slot, pos] = get_hovered_hotbar_slot();
|
||||
if (slot) {
|
||||
auto& player = m_scene.client_world().get_player();
|
||||
player.set_hotbar(pos, {m_selected_block, 1});
|
||||
if (m_selected_block != 0) {
|
||||
m_hotbar[pos]->set_item(m_selected_block,
|
||||
blocks[m_selected_block].get());
|
||||
player.set_hotbar(pos, {m_selected_id, 1});
|
||||
if (m_selected_id != 0) {
|
||||
auto it = item_textures.find(m_selected_id);
|
||||
ASSERT(it != item_textures.end());
|
||||
m_hotbar[pos]->set_item(m_selected_id, it->second.get());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -19,9 +19,9 @@ ItemSlot& ItemSlot::set_scale(float scale) {
|
||||
update_border();
|
||||
return *this;
|
||||
}
|
||||
ItemSlot& ItemSlot::set_item(BlockType id, const Texture* texture) {
|
||||
ItemSlot& ItemSlot::set_item(ItemID id, const Texture* texture) {
|
||||
m_foreground->set_texture(texture, false);
|
||||
m_block_type = id;
|
||||
m_id = id;
|
||||
return *this;
|
||||
}
|
||||
float ItemSlot::width() const {
|
||||
@@ -77,6 +77,6 @@ bool ItemSlot::handle_mouse_move_event(const MouseMoveEvent& e) {
|
||||
return Widget::handle_mouse_move_event(e);
|
||||
}
|
||||
|
||||
BlockType ItemSlot::block() const { return m_block_type; }
|
||||
ItemID ItemSlot::id() const { return m_id; }
|
||||
bool ItemSlot::hovered() const { return m_hovered; }
|
||||
} // namespace Cubed
|
||||
@@ -131,7 +131,7 @@ void WorldUIManager::update_hotbar() {
|
||||
auto hotbar = player.get_hotbar();
|
||||
size_t selected = player.selected_hotbar();
|
||||
for (size_t i = 0; i < LocalPlayer::HOTBAR_SUM; ++i) {
|
||||
auto type = hotbar[i].type;
|
||||
auto type = hotbar[i].id;
|
||||
if (selected == i) {
|
||||
m_hotbar_slot[i]->set_border_visale(true);
|
||||
} else {
|
||||
@@ -140,7 +140,9 @@ void WorldUIManager::update_hotbar() {
|
||||
if (type == 0) {
|
||||
m_hotbar_slot[i]->set_item(0, nullptr);
|
||||
} else {
|
||||
m_hotbar_slot[i]->set_item(type, item_texture[type].get());
|
||||
auto it = item_texture.find(type);
|
||||
ASSERT(it != item_texture.end());
|
||||
m_hotbar_slot[i]->set_item(type, it->second.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user