initial commit
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
CBAssets.rar
|
||||||
|
|
||||||
|
CBAssets
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
56
CMakeLists.txt
Normal file
56
CMakeLists.txt
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.14)
|
||||||
|
project(cursebreakerParser)
|
||||||
|
|
||||||
|
# Set C++ standard
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
# Collect all source files
|
||||||
|
file(GLOB_RECURSE SOURCES "src/*.cpp")
|
||||||
|
|
||||||
|
# Include FetchContent
|
||||||
|
include(FetchContent)
|
||||||
|
|
||||||
|
message(STATUS "Using rapidyaml")
|
||||||
|
message(STATUS "Using GLM")
|
||||||
|
message(STATUS "Using TinyXML2")
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
rapidyaml
|
||||||
|
GIT_REPOSITORY https://github.com/biojppm/rapidyaml.git
|
||||||
|
GIT_TAG master
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
glm
|
||||||
|
GIT_REPOSITORY https://github.com/g-truc/glm.git
|
||||||
|
GIT_TAG master
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
tinyxml2
|
||||||
|
GIT_REPOSITORY https://github.com/leethomason/tinyxml2.git
|
||||||
|
GIT_TAG master
|
||||||
|
)
|
||||||
|
|
||||||
|
# Enable exceptions in rapidyaml
|
||||||
|
set(RYML_DEFAULT_CALLBACK_USES_EXCEPTIONS ON CACHE BOOL "" FORCE)
|
||||||
|
set(RYML_DBG OFF CACHE BOOL "" FORCE)
|
||||||
|
|
||||||
|
FetchContent_MakeAvailable(rapidyaml glm tinyxml2)
|
||||||
|
|
||||||
|
# Create the main executable
|
||||||
|
add_executable(${PROJECT_NAME} ${SOURCES})
|
||||||
|
|
||||||
|
# Link libraries to our executable
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC ryml::ryml)
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC tinyxml2::tinyxml2)
|
||||||
|
|
||||||
|
# Add include directories for rapidyaml
|
||||||
|
target_include_directories(${PROJECT_NAME} PRIVATE ${rapidyaml_SOURCE_DIR}/src)
|
||||||
|
|
||||||
|
# Add include directories for GLM
|
||||||
|
target_include_directories(${PROJECT_NAME} PRIVATE ${glm_SOURCE_DIR})
|
||||||
|
|
||||||
|
# Include directories
|
||||||
|
target_include_directories(${PROJECT_NAME} PRIVATE include)
|
||||||
86
include/assets/asset_base.hpp
Normal file
86
include/assets/asset_base.hpp
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <array>
|
||||||
|
#include <string_view>
|
||||||
|
#include <span>
|
||||||
|
|
||||||
|
#include <ryml.hpp>
|
||||||
|
#include <ryml_std.hpp>
|
||||||
|
#include <c4/format.hpp>
|
||||||
|
|
||||||
|
struct AssetBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int64_t ID;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AssetGUID
|
||||||
|
{
|
||||||
|
AssetGUID() = default;
|
||||||
|
AssetGUID(const char* data) : AssetGUID(std::string_view(data, 32)) {};
|
||||||
|
AssetGUID(std::string_view data)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < 16; ++i) Data[0] = (Data[0] << 4) | hexToVal(data[i]);
|
||||||
|
for (size_t i = 16; i < 32; ++i) Data[1] = (Data[1] << 4) | hexToVal(data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t hexToVal(char c)
|
||||||
|
{
|
||||||
|
return (c >= '0' && c <= '9') ? (c - '0') : (c - 'a' + 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const AssetGUID& other) const
|
||||||
|
{
|
||||||
|
return Data[0] == other.Data[0] && Data[1] == other.Data[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr uint64_t DefaultVal = 0xe000000000000000;
|
||||||
|
|
||||||
|
std::array<uint64_t, 2> Data{ 0, DefaultVal };
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
template <>
|
||||||
|
struct hash<AssetGUID> {
|
||||||
|
size_t operator()(const AssetGUID& v) const noexcept
|
||||||
|
{
|
||||||
|
auto h1 = std::hash<uint64_t>{}(v.Data[0]);
|
||||||
|
auto h2 = std::hash<uint64_t>{}(v.Data[1]);
|
||||||
|
return h1 ^ (h2 << 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T* ParseAssetRef(const ryml::ConstNodeRef& node)
|
||||||
|
{
|
||||||
|
T* asset;
|
||||||
|
node["fileID"] >> asset;
|
||||||
|
return asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool LinkAssetRef(const std::unordered_map<int64_t, AssetBase*>& assetsMap, T*& asset)
|
||||||
|
{
|
||||||
|
auto it = assetsMap.find(reinterpret_cast<int64_t>(asset));
|
||||||
|
if (it != assetsMap.end())
|
||||||
|
{
|
||||||
|
asset = reinterpret_cast<T*>(it->second);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
asset = nullptr;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline AssetGUID ParseAssetGUID(const ryml::ConstNodeRef& node)
|
||||||
|
{
|
||||||
|
auto guidStringSpan2 = node["guid"].val();
|
||||||
|
|
||||||
|
assert(guidStringSpan2.size() == 32);
|
||||||
|
|
||||||
|
return AssetGUID{std::string_view(guidStringSpan2.data(), guidStringSpan2.size())};
|
||||||
|
}
|
||||||
21
include/assets/custom_assets.hpp
Normal file
21
include/assets/custom_assets.hpp
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "asset_base.hpp"
|
||||||
|
|
||||||
|
struct Drop
|
||||||
|
{
|
||||||
|
int itemId;
|
||||||
|
int minAmount;
|
||||||
|
int maxAmount;
|
||||||
|
int dropChance;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Interactable_Resource : public AssetBase
|
||||||
|
{
|
||||||
|
int maxHealth;
|
||||||
|
int respawnTime;
|
||||||
|
std::vector<Drop> drops;
|
||||||
|
std::vector<int> requiredTools;
|
||||||
|
int xp;
|
||||||
|
int typeId;
|
||||||
|
};
|
||||||
54
include/assets/game_object.hpp
Normal file
54
include/assets/game_object.hpp
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <ryml_std.hpp>
|
||||||
|
|
||||||
|
#include "asset_base.hpp"
|
||||||
|
#include "transform.hpp"
|
||||||
|
|
||||||
|
struct GameObjectAsset : public AssetBase
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
uint8_t layer{ 0 };
|
||||||
|
uint8_t namMeshLayer{ 0 };
|
||||||
|
std::string tag;
|
||||||
|
|
||||||
|
TransformAsset* transform{ nullptr };
|
||||||
|
std::vector<AssetBase*> Components;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline GameObjectAsset ParseGameObject(const ryml::ConstNodeRef& node)
|
||||||
|
{
|
||||||
|
GameObjectAsset gameObject;
|
||||||
|
|
||||||
|
node["m_Name"] >> gameObject.name;
|
||||||
|
node["m_Layer"] >> gameObject.layer;
|
||||||
|
node["m_TagString"] >> gameObject.tag;
|
||||||
|
|
||||||
|
auto components = node["m_Component"];
|
||||||
|
auto it = components.begin();
|
||||||
|
|
||||||
|
gameObject.transform = ParseAssetRef<TransformAsset>((*it)["component"]);
|
||||||
|
++it;
|
||||||
|
for (; it != components.end(); ++it)
|
||||||
|
{
|
||||||
|
gameObject.Components.push_back(ParseAssetRef<AssetBase>((*it)["component"]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return gameObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void LinkGameObject(const std::unordered_map<int64_t, AssetBase*>& assetsMap, GameObjectAsset& gameObject)
|
||||||
|
{
|
||||||
|
LinkAssetRef<TransformAsset>(assetsMap, gameObject.transform);
|
||||||
|
for (int i = static_cast<int>(gameObject.Components.size()) - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (!LinkAssetRef<AssetBase>(assetsMap, gameObject.Components[i]))
|
||||||
|
{
|
||||||
|
std::swap(gameObject.Components[i], gameObject.Components.back());
|
||||||
|
gameObject.Components.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
include/assets/mesh_filter.hpp
Normal file
29
include/assets/mesh_filter.hpp
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "asset_base.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
struct GameObjectAsset;
|
||||||
|
|
||||||
|
struct MeshFilterAsset : AssetBase
|
||||||
|
{
|
||||||
|
AssetGUID Mesh;
|
||||||
|
GameObjectAsset* GameObject{ nullptr };
|
||||||
|
};
|
||||||
|
|
||||||
|
inline MeshFilterAsset ParseMeshFilter(const ryml::ConstNodeRef& node)
|
||||||
|
{
|
||||||
|
MeshFilterAsset meshFilter;
|
||||||
|
|
||||||
|
meshFilter.Mesh = ParseAssetGUID(node["m_Mesh"]);
|
||||||
|
meshFilter.GameObject = ParseAssetRef<GameObjectAsset>(node["m_GameObject"]);
|
||||||
|
|
||||||
|
return meshFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void LinkMeshFilter(const std::unordered_map<int64_t, AssetBase*>& assetsMap, MeshFilterAsset& meshFilter)
|
||||||
|
{
|
||||||
|
LinkAssetRef<GameObjectAsset>(assetsMap, meshFilter.GameObject);
|
||||||
|
}
|
||||||
25
include/assets/prefab_instance.hpp
Normal file
25
include/assets/prefab_instance.hpp
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "asset_base.hpp"
|
||||||
|
|
||||||
|
//struct PrefabInstanceAsset : AssetBase
|
||||||
|
//{
|
||||||
|
// AssetGUID prefabGUID;
|
||||||
|
// ryml::Tree Data{};
|
||||||
|
//};
|
||||||
|
|
||||||
|
struct PrefabAsset
|
||||||
|
{
|
||||||
|
std::filesystem::path path;
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
std::unordered_map<int64_t, ryml::Tree> Data;
|
||||||
|
};
|
||||||
|
|
||||||
|
//inline PrefabInstanceAsset ParsePrefabInstance(const ryml::ConstNodeRef& node)
|
||||||
|
//{
|
||||||
|
// PrefabInstanceAsset prefabInstance;
|
||||||
|
// prefabInstance.prefabGUID = ParseAssetGUID(node["m_SourcePrefab"]);
|
||||||
|
// // prefabInstance.Data = node;
|
||||||
|
// return prefabInstance;
|
||||||
|
//}
|
||||||
26
include/assets/scene.hpp
Normal file
26
include/assets/scene.hpp
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "game_object.hpp"
|
||||||
|
#include "transform.hpp"
|
||||||
|
#include "mesh_filter.hpp"
|
||||||
|
#include "prefab_instance.hpp"
|
||||||
|
|
||||||
|
struct SceneAsset
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::filesystem::path path;
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
std::vector<GameObjectAsset> gameObjects;
|
||||||
|
std::vector<TransformAsset> transforms;
|
||||||
|
std::vector<MeshFilterAsset> meshFilters;
|
||||||
|
// std::vector<PrefabInstanceAsset> prefabInstances;
|
||||||
|
|
||||||
|
std::unordered_map<int64_t, void*> IDtoAsset;
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
75
include/assets/transform.hpp
Normal file
75
include/assets/transform.hpp
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
|
||||||
|
#include "asset_base.hpp"
|
||||||
|
|
||||||
|
struct GameObjectAsset;
|
||||||
|
|
||||||
|
struct TransformAsset : public AssetBase
|
||||||
|
{
|
||||||
|
glm::quat Rotation;
|
||||||
|
glm::vec3 Position;
|
||||||
|
glm::vec3 Scale;
|
||||||
|
|
||||||
|
glm::mat4 GlobalMatrix;
|
||||||
|
|
||||||
|
GameObjectAsset* GameObject{ nullptr };
|
||||||
|
std::vector<TransformAsset*> Children;
|
||||||
|
TransformAsset* Parent{ nullptr };
|
||||||
|
};
|
||||||
|
|
||||||
|
inline glm::vec3 ParseVector3(const ryml::ConstNodeRef& node)
|
||||||
|
{
|
||||||
|
glm::vec3 vector;
|
||||||
|
node["x"] >> vector.x;
|
||||||
|
node["y"] >> vector.y;
|
||||||
|
node["z"] >> vector.z;
|
||||||
|
return vector;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline glm::quat ParseQuaternion(const ryml::ConstNodeRef& node)
|
||||||
|
{
|
||||||
|
glm::quat quaternion;
|
||||||
|
node["x"] >> quaternion.x;
|
||||||
|
node["y"] >> quaternion.y;
|
||||||
|
node["z"] >> quaternion.z;
|
||||||
|
node["w"] >> quaternion.w;
|
||||||
|
return quaternion;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline TransformAsset ParseTransform(const ryml::ConstNodeRef& node)
|
||||||
|
{
|
||||||
|
TransformAsset transform;
|
||||||
|
transform.Rotation = ParseQuaternion(node["m_LocalRotation"]);
|
||||||
|
transform.Position = ParseVector3(node["m_LocalPosition"]);
|
||||||
|
transform.Scale = ParseVector3(node["m_LocalScale"]);
|
||||||
|
|
||||||
|
transform.Parent = ParseAssetRef<TransformAsset>(node["m_Father"]);
|
||||||
|
transform.GameObject = ParseAssetRef<GameObjectAsset>(node["m_GameObject"]);
|
||||||
|
|
||||||
|
auto children = node["m_Children"];
|
||||||
|
for (const auto& child : children)
|
||||||
|
{
|
||||||
|
transform.Children.push_back(ParseAssetRef<TransformAsset>(child));
|
||||||
|
}
|
||||||
|
|
||||||
|
return transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void LinkTransform(const std::unordered_map<int64_t, AssetBase*>& assetsMap, TransformAsset& transform)
|
||||||
|
{
|
||||||
|
LinkAssetRef<TransformAsset>(assetsMap, transform.Parent);
|
||||||
|
for (int i = static_cast<int>(transform.Children.size()) - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (!LinkAssetRef<TransformAsset>(assetsMap, transform.Children[i]))
|
||||||
|
{
|
||||||
|
std::swap(transform.Children[i], transform.Children.back());
|
||||||
|
transform.Children.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LinkAssetRef<GameObjectAsset>(assetsMap, transform.GameObject);
|
||||||
|
}
|
||||||
208
include/configs/constants.h
Normal file
208
include/configs/constants.h
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace cursebreaker {
|
||||||
|
|
||||||
|
enum class SkillType : uint8_t
|
||||||
|
{
|
||||||
|
woodcutting,
|
||||||
|
fishing,
|
||||||
|
swordsmanship,
|
||||||
|
mining,
|
||||||
|
archery,
|
||||||
|
magic,
|
||||||
|
defence,
|
||||||
|
blacksmithy,
|
||||||
|
tailoring,
|
||||||
|
carpentry,
|
||||||
|
alchemy,
|
||||||
|
cooking,
|
||||||
|
none,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class WorkbenchType : uint8_t
|
||||||
|
{
|
||||||
|
none,
|
||||||
|
anvil,
|
||||||
|
oven,
|
||||||
|
cooking,
|
||||||
|
carpenter,
|
||||||
|
tailor,
|
||||||
|
forge,
|
||||||
|
alchemist,
|
||||||
|
mystic,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class StatType : uint8_t
|
||||||
|
{
|
||||||
|
health,
|
||||||
|
accuracyPhysical,
|
||||||
|
accuracyMagical,
|
||||||
|
accuracyRanged,
|
||||||
|
damagePhysical,
|
||||||
|
damageMagical,
|
||||||
|
damageRanged,
|
||||||
|
resistancePhysical,
|
||||||
|
resistanceMagical,
|
||||||
|
resistanceRanged,
|
||||||
|
healing,
|
||||||
|
movementSpeed,
|
||||||
|
mana,
|
||||||
|
manaregen,
|
||||||
|
healthregen,
|
||||||
|
power,
|
||||||
|
critical,
|
||||||
|
DamageVsUndead,
|
||||||
|
DamageVsBeasts,
|
||||||
|
CritterSlaying,
|
||||||
|
none,
|
||||||
|
harvestingSpeedWoodcutting
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ItemType : uint8_t
|
||||||
|
{
|
||||||
|
weapon,
|
||||||
|
shield,
|
||||||
|
armor,
|
||||||
|
head,
|
||||||
|
resource,
|
||||||
|
consumable,
|
||||||
|
trinket,
|
||||||
|
bracelet
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ItemCategory : uint8_t
|
||||||
|
{
|
||||||
|
none,
|
||||||
|
bone,
|
||||||
|
bow,
|
||||||
|
crossbow,
|
||||||
|
constructable,
|
||||||
|
torch,
|
||||||
|
blacksmithhammer,
|
||||||
|
questitem,
|
||||||
|
heavyArmor,
|
||||||
|
warhammer,
|
||||||
|
shield,
|
||||||
|
hatchet,
|
||||||
|
blade,
|
||||||
|
armor,
|
||||||
|
pickaxe,
|
||||||
|
fish,
|
||||||
|
fishingrod,
|
||||||
|
shears,
|
||||||
|
hammer,
|
||||||
|
battleaxe,
|
||||||
|
morningstar,
|
||||||
|
wand,
|
||||||
|
staff,
|
||||||
|
dagger
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Tool : uint8_t
|
||||||
|
{
|
||||||
|
hatchet,
|
||||||
|
pickaxe,
|
||||||
|
broom,
|
||||||
|
fishingrod,
|
||||||
|
none
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class SpellBookType : uint8_t
|
||||||
|
{
|
||||||
|
none,
|
||||||
|
spells,
|
||||||
|
abilities
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ActionType : uint8_t
|
||||||
|
{
|
||||||
|
NpcDeath,
|
||||||
|
PlayerDeath,
|
||||||
|
PlayerRespawn,
|
||||||
|
NpcInteract,
|
||||||
|
QuestUpdate,
|
||||||
|
QuestTimerEnd,
|
||||||
|
UseItemOnItem,
|
||||||
|
UseItemOnNpc,
|
||||||
|
ConsumeItem,
|
||||||
|
NpcCombatInteract,
|
||||||
|
none,
|
||||||
|
NpcKilledByPlayer,
|
||||||
|
EnterMap,
|
||||||
|
VarUpdated,
|
||||||
|
GrantAchievement,
|
||||||
|
PlayerKilledByNpc,
|
||||||
|
UseItem,
|
||||||
|
CraftItem,
|
||||||
|
HarvestItem,
|
||||||
|
BuyItem,
|
||||||
|
NpcTagKilledByPlayer,
|
||||||
|
UseAbility,
|
||||||
|
LootItem,
|
||||||
|
UseItemCategoryOnItem,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Material : uint8_t
|
||||||
|
{
|
||||||
|
none,
|
||||||
|
copper,
|
||||||
|
iron,
|
||||||
|
imbersteel,
|
||||||
|
titanium,
|
||||||
|
spruce,
|
||||||
|
oak,
|
||||||
|
evark,
|
||||||
|
deadwood,
|
||||||
|
sheep,
|
||||||
|
troll,
|
||||||
|
ogre,
|
||||||
|
demon,
|
||||||
|
wool,
|
||||||
|
cotton,
|
||||||
|
flax,
|
||||||
|
jute,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class GenstatType : uint8_t
|
||||||
|
{
|
||||||
|
none,
|
||||||
|
dagger,
|
||||||
|
broadsword,
|
||||||
|
battleaxe,
|
||||||
|
greatsword,
|
||||||
|
morningstar,
|
||||||
|
hammer,
|
||||||
|
spear,
|
||||||
|
bow,
|
||||||
|
staff,
|
||||||
|
wand,
|
||||||
|
crossbow,
|
||||||
|
woodenshield,
|
||||||
|
wizardhat,
|
||||||
|
wizardrobe,
|
||||||
|
grandwizardhat,
|
||||||
|
grandwizardrobe,
|
||||||
|
leatherhood,
|
||||||
|
leatherbracelet,
|
||||||
|
leatherarmor,
|
||||||
|
studdedleatherhood,
|
||||||
|
studdedleatherbracelet,
|
||||||
|
studdedleatherarmor,
|
||||||
|
helmet,
|
||||||
|
shield,
|
||||||
|
armor,
|
||||||
|
platehelmet,
|
||||||
|
kiteshield,
|
||||||
|
platearmor,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StatValue
|
||||||
|
{
|
||||||
|
StatType type;
|
||||||
|
uint8_t value;
|
||||||
|
bool isPercentage;
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
103
include/configs/items.hpp
Normal file
103
include/configs/items.hpp
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include <tinyxml2.h>
|
||||||
|
|
||||||
|
#include "constants.h"
|
||||||
|
|
||||||
|
namespace cursebreaker {
|
||||||
|
|
||||||
|
struct CraftingItemEntry
|
||||||
|
{
|
||||||
|
uint16_t itemId{};
|
||||||
|
uint16_t amount{};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Struct for item crafting requirements
|
||||||
|
struct ItemCrafting
|
||||||
|
{
|
||||||
|
WorkbenchType workbench;
|
||||||
|
SkillType craftingskill;
|
||||||
|
std::vector<CraftingItemEntry> craftingitems;
|
||||||
|
std::string checks;
|
||||||
|
|
||||||
|
ItemCrafting() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Main item struct
|
||||||
|
struct Item {
|
||||||
|
uint16_t id = 0;
|
||||||
|
uint16_t price = 0;
|
||||||
|
uint16_t abilityid = 0;
|
||||||
|
uint16_t learnabilityid = 0;
|
||||||
|
|
||||||
|
SkillType skill = SkillType::none;
|
||||||
|
ItemCategory category = ItemCategory::none;
|
||||||
|
ItemType slot = ItemType::resource;
|
||||||
|
Tool tool = Tool::none;
|
||||||
|
GenstatType generation = GenstatType::none;
|
||||||
|
uint8_t level = 0;
|
||||||
|
uint8_t foodlevel = 0;
|
||||||
|
uint8_t foodamount = 0;
|
||||||
|
uint8_t foodfrequency = 0;
|
||||||
|
uint8_t foodtime = 0;
|
||||||
|
uint8_t maxstack = 0;
|
||||||
|
uint8_t book = 0;
|
||||||
|
|
||||||
|
bool stackable : 1;
|
||||||
|
bool twohanded : 1;
|
||||||
|
bool undroppable : 1;
|
||||||
|
bool hidemilestone : 1;
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
std::string description;
|
||||||
|
std::string comment;
|
||||||
|
|
||||||
|
std::vector<StatValue> stats;
|
||||||
|
std::vector<ItemCrafting> craftings;
|
||||||
|
|
||||||
|
Item() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper functions for enum conversions
|
||||||
|
StatType stringToStatType(const std::string& str);
|
||||||
|
ItemCategory stringToItemCategory(const std::string& str);
|
||||||
|
ItemType stringToItemType(const std::string& str);
|
||||||
|
Tool stringToTool(const std::string& str);
|
||||||
|
SkillType stringToSkillType(const std::string& str);
|
||||||
|
WorkbenchType stringToWorkbenchType(const std::string& str);
|
||||||
|
GenstatType stringToGenstatType(const std::string& str);
|
||||||
|
|
||||||
|
// Items configuration manager
|
||||||
|
class ItemsConfig {
|
||||||
|
public:
|
||||||
|
static ItemsConfig& getInstance() {
|
||||||
|
static ItemsConfig instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool loadFromXML(const std::string& filepath);
|
||||||
|
const Item* getItemById(int id) const;
|
||||||
|
const std::unordered_map<int, Item>& getAllItems() const { return m_items; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ItemsConfig() = default;
|
||||||
|
~ItemsConfig() = default;
|
||||||
|
ItemsConfig(const ItemsConfig&) = delete;
|
||||||
|
ItemsConfig& operator=(const ItemsConfig&) = delete;
|
||||||
|
|
||||||
|
std::unordered_map<int, Item> m_items;
|
||||||
|
|
||||||
|
// Helper methods for parsing
|
||||||
|
void parseItem(tinyxml2::XMLElement* itemElement);
|
||||||
|
void parseStats(tinyxml2::XMLElement* itemElement, Item& item);
|
||||||
|
void parseCraftings(tinyxml2::XMLElement* itemElement, Item& item);
|
||||||
|
std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue = "");
|
||||||
|
int getAttributeValueInt(tinyxml2::XMLElement* element, const char* attribute, int defaultValue = 0);
|
||||||
|
bool getAttributeValueBool(tinyxml2::XMLElement* element, const char* attribute, bool defaultValue = false);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace cursebreaker
|
||||||
38
include/project_parser.h
Normal file
38
include/project_parser.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "assets/asset_base.hpp"
|
||||||
|
#include "assets/scene.hpp"
|
||||||
|
|
||||||
|
struct AssetPath
|
||||||
|
{
|
||||||
|
std::filesystem::path path{};
|
||||||
|
AssetGUID GUID{};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ParsedProject
|
||||||
|
{
|
||||||
|
std::filesystem::path m_projectRoot;
|
||||||
|
|
||||||
|
std::vector<AssetPath> m_scriptFiles;
|
||||||
|
std::vector<AssetPath> m_imageFiles;
|
||||||
|
std::vector<AssetPath> m_meshFiles;
|
||||||
|
std::vector<AssetPath> m_sceneFiles;
|
||||||
|
std::vector<AssetPath> m_prefabFiles;
|
||||||
|
|
||||||
|
std::vector<PrefabAsset> m_prefabAssets;
|
||||||
|
std::vector<SceneAsset> m_sceneAssets;
|
||||||
|
|
||||||
|
std::unordered_map<AssetGUID, PrefabAsset*> m_prefabsMap;
|
||||||
|
std::unordered_map<AssetGUID, uint32_t> m_scriptToClassHash;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
void ParseProject(const std::filesystem::path& projectRoot);
|
||||||
|
|
||||||
|
extern ParsedProject g_parsedProject;
|
||||||
5
include/tree_builder.h
Normal file
5
include/tree_builder.h
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "assets/scene.hpp"
|
||||||
|
|
||||||
|
void BuildTree(SceneAsset& sceneAsset);
|
||||||
334
src/configs/items.cpp
Normal file
334
src/configs/items.cpp
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
#include "configs/items.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <sstream>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace cursebreaker {
|
||||||
|
|
||||||
|
// Helper functions to convert strings to enums
|
||||||
|
StatType stringToStatType(const std::string& str) {
|
||||||
|
static const std::unordered_map<std::string, StatType> statMap = {
|
||||||
|
{"damagephysical", StatType::damagePhysical},
|
||||||
|
{"damagemagical", StatType::damageMagical},
|
||||||
|
{"damageranged", StatType::damageRanged},
|
||||||
|
{"accuracyphysical", StatType::accuracyPhysical},
|
||||||
|
{"accuracymagical", StatType::accuracyMagical},
|
||||||
|
{"accuracyranged", StatType::accuracyRanged},
|
||||||
|
{"resistancephysical", StatType::resistancePhysical},
|
||||||
|
{"resistancemagical", StatType::resistanceMagical},
|
||||||
|
{"resistanceranged", StatType::resistanceRanged},
|
||||||
|
{"health", StatType::health},
|
||||||
|
{"mana", StatType::mana},
|
||||||
|
{"manaregen", StatType::manaregen},
|
||||||
|
{"healthregen", StatType::healthregen},
|
||||||
|
{"movementSpeed", StatType::movementSpeed},
|
||||||
|
{"power", StatType::power},
|
||||||
|
{"critical", StatType::critical},
|
||||||
|
{"healing", StatType::healing},
|
||||||
|
{"DamageVsUndead", StatType::DamageVsUndead},
|
||||||
|
{"DamageVsBeasts", StatType::DamageVsBeasts},
|
||||||
|
{"CritterSlaying", StatType::CritterSlaying},
|
||||||
|
{"harvestingSpeedWoodcutting", StatType::harvestingSpeedWoodcutting}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto it = statMap.find(str);
|
||||||
|
return (it != statMap.end()) ? it->second : StatType::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemCategory stringToItemCategory(const std::string& str) {
|
||||||
|
static const std::unordered_map<std::string, ItemCategory> categoryMap = {
|
||||||
|
{"bone", ItemCategory::bone},
|
||||||
|
{"bow", ItemCategory::bow},
|
||||||
|
{"crossbow", ItemCategory::crossbow},
|
||||||
|
{"constructable", ItemCategory::constructable},
|
||||||
|
{"torch", ItemCategory::torch},
|
||||||
|
{"blacksmithhammer", ItemCategory::blacksmithhammer},
|
||||||
|
{"questitem", ItemCategory::questitem},
|
||||||
|
{"heavyArmor", ItemCategory::heavyArmor},
|
||||||
|
{"warhammer", ItemCategory::warhammer},
|
||||||
|
{"shield", ItemCategory::shield},
|
||||||
|
{"hatchet", ItemCategory::hatchet},
|
||||||
|
{"blade", ItemCategory::blade},
|
||||||
|
{"armor", ItemCategory::armor},
|
||||||
|
{"pickaxe", ItemCategory::pickaxe},
|
||||||
|
{"fish", ItemCategory::fish},
|
||||||
|
{"fishingrod", ItemCategory::fishingrod},
|
||||||
|
{"shears", ItemCategory::shears},
|
||||||
|
{"hammer", ItemCategory::hammer},
|
||||||
|
{"battleaxe", ItemCategory::battleaxe},
|
||||||
|
{"morningstar", ItemCategory::morningstar},
|
||||||
|
{"wand", ItemCategory::wand},
|
||||||
|
{"staff", ItemCategory::staff},
|
||||||
|
{"dagger", ItemCategory::dagger}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto it = categoryMap.find(str);
|
||||||
|
return (it != categoryMap.end()) ? it->second : ItemCategory::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemType stringToItemType(const std::string& str) {
|
||||||
|
static const std::unordered_map<std::string, ItemType> typeMap = {
|
||||||
|
{"weapon", ItemType::weapon},
|
||||||
|
{"shield", ItemType::shield},
|
||||||
|
{"armor", ItemType::armor},
|
||||||
|
{"head", ItemType::head},
|
||||||
|
{"resource", ItemType::resource},
|
||||||
|
{"consumable", ItemType::consumable},
|
||||||
|
{"trinket", ItemType::trinket},
|
||||||
|
{"bracelet", ItemType::bracelet}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto it = typeMap.find(str);
|
||||||
|
return (it != typeMap.end()) ? it->second : ItemType::resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tool stringToTool(const std::string& str) {
|
||||||
|
static const std::unordered_map<std::string, Tool> toolMap = {
|
||||||
|
{"hatchet", Tool::hatchet},
|
||||||
|
{"pickaxe", Tool::pickaxe},
|
||||||
|
{"broom", Tool::broom},
|
||||||
|
{"fishingrod", Tool::fishingrod}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto it = toolMap.find(str);
|
||||||
|
return (it != toolMap.end()) ? it->second : Tool::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
SkillType stringToSkillType(const std::string& str) {
|
||||||
|
static const std::unordered_map<std::string, SkillType> skillMap = {
|
||||||
|
{"woodcutting", SkillType::woodcutting},
|
||||||
|
{"fishing", SkillType::fishing},
|
||||||
|
{"swordsmanship", SkillType::swordsmanship},
|
||||||
|
{"mining", SkillType::mining},
|
||||||
|
{"archery", SkillType::archery},
|
||||||
|
{"magic", SkillType::magic},
|
||||||
|
{"defence", SkillType::defence},
|
||||||
|
{"blacksmithy", SkillType::blacksmithy},
|
||||||
|
{"tailoring", SkillType::tailoring},
|
||||||
|
{"carpentry", SkillType::carpentry},
|
||||||
|
{"alchemy", SkillType::alchemy},
|
||||||
|
{"cooking", SkillType::cooking}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto it = skillMap.find(str);
|
||||||
|
return (it != skillMap.end()) ? it->second : SkillType::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
WorkbenchType stringToWorkbenchType(const std::string& str) {
|
||||||
|
static const std::unordered_map<std::string, WorkbenchType> workbenchMap = {
|
||||||
|
{"none", WorkbenchType::none},
|
||||||
|
{"anvil", WorkbenchType::anvil},
|
||||||
|
{"oven", WorkbenchType::oven},
|
||||||
|
{"cooking", WorkbenchType::cooking},
|
||||||
|
{"carpenter", WorkbenchType::carpenter},
|
||||||
|
{"tailor", WorkbenchType::tailor},
|
||||||
|
{"forge", WorkbenchType::forge},
|
||||||
|
{"alchemist", WorkbenchType::alchemist},
|
||||||
|
{"mystic", WorkbenchType::mystic}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto it = workbenchMap.find(str);
|
||||||
|
return (it != workbenchMap.end()) ? it->second : WorkbenchType::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
GenstatType stringToGenstatType(const std::string& str) {
|
||||||
|
static const std::unordered_map<std::string, GenstatType> genstatMap = {
|
||||||
|
{"dagger", GenstatType::dagger},
|
||||||
|
{"broadsword", GenstatType::broadsword},
|
||||||
|
{"battleaxe", GenstatType::battleaxe},
|
||||||
|
{"greatsword", GenstatType::greatsword},
|
||||||
|
{"morningstar", GenstatType::morningstar},
|
||||||
|
{"hammer", GenstatType::hammer},
|
||||||
|
{"spear", GenstatType::spear},
|
||||||
|
{"bow", GenstatType::bow},
|
||||||
|
{"staff", GenstatType::staff},
|
||||||
|
{"wand", GenstatType::wand},
|
||||||
|
{"crossbow", GenstatType::crossbow},
|
||||||
|
{"woodenshield", GenstatType::woodenshield},
|
||||||
|
{"wizardhat", GenstatType::wizardhat},
|
||||||
|
{"wizardrobe", GenstatType::wizardrobe},
|
||||||
|
{"grandwizardhat", GenstatType::grandwizardhat},
|
||||||
|
{"grandwizardrobe", GenstatType::grandwizardrobe},
|
||||||
|
{"leatherhood", GenstatType::leatherhood},
|
||||||
|
{"leatherbracelet", GenstatType::leatherbracelet},
|
||||||
|
{"leatherarmor", GenstatType::leatherarmor},
|
||||||
|
{"studdedleatherhood", GenstatType::studdedleatherhood},
|
||||||
|
{"studdedleatherbracelet", GenstatType::studdedleatherbracelet},
|
||||||
|
{"studdedleatherarmor", GenstatType::studdedleatherarmor},
|
||||||
|
{"helmet", GenstatType::helmet},
|
||||||
|
{"shield", GenstatType::shield},
|
||||||
|
{"armor", GenstatType::armor},
|
||||||
|
{"platehelmet", GenstatType::platehelmet},
|
||||||
|
{"kiteshield", GenstatType::kiteshield},
|
||||||
|
{"platearmor", GenstatType::platearmor}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto it = genstatMap.find(str);
|
||||||
|
return (it != genstatMap.end()) ? it->second : GenstatType::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ItemsConfig::loadFromXML(const std::string& filepath) {
|
||||||
|
tinyxml2::XMLDocument doc;
|
||||||
|
tinyxml2::XMLError result = doc.LoadFile(filepath.c_str());
|
||||||
|
|
||||||
|
if (result != tinyxml2::XML_SUCCESS) {
|
||||||
|
std::cerr << "Failed to load items XML file: " << filepath << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
tinyxml2::XMLElement* root = doc.FirstChildElement("items");
|
||||||
|
if (!root) {
|
||||||
|
std::cerr << "Invalid XML structure: missing 'items' root element" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_items.clear();
|
||||||
|
|
||||||
|
// Parse all items
|
||||||
|
for (tinyxml2::XMLElement* itemElement = root->FirstChildElement("item");
|
||||||
|
itemElement != nullptr;
|
||||||
|
itemElement = itemElement->NextSiblingElement("item")) {
|
||||||
|
parseItem(itemElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Loaded " << m_items.size() << " items from XML" << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ItemsConfig::parseItem(tinyxml2::XMLElement* itemElement) {
|
||||||
|
Item item;
|
||||||
|
|
||||||
|
// Parse basic attributes
|
||||||
|
item.id = static_cast<uint16_t>(getAttributeValueInt(itemElement, "id"));
|
||||||
|
item.name = getAttributeValue(itemElement, "name");
|
||||||
|
item.description = getAttributeValue(itemElement, "description");
|
||||||
|
item.category = stringToItemCategory(getAttributeValue(itemElement, "category"));
|
||||||
|
item.slot = stringToItemType(getAttributeValue(itemElement, "slot"));
|
||||||
|
item.tool = stringToTool(getAttributeValue(itemElement, "tool"));
|
||||||
|
item.skill = stringToSkillType(getAttributeValue(itemElement, "skill"));
|
||||||
|
item.comment = getAttributeValue(itemElement, "comment");
|
||||||
|
|
||||||
|
// Parse numeric attributes
|
||||||
|
item.price = static_cast<uint16_t>(getAttributeValueInt(itemElement, "price"));
|
||||||
|
item.abilityid = static_cast<uint16_t>(getAttributeValueInt(itemElement, "abilityid"));
|
||||||
|
item.learnabilityid = static_cast<uint16_t>(getAttributeValueInt(itemElement, "learnabilityid"));
|
||||||
|
item.level = static_cast<uint8_t>(getAttributeValueInt(itemElement, "level"));
|
||||||
|
item.foodlevel = static_cast<uint8_t>(getAttributeValueInt(itemElement, "foodlevel"));
|
||||||
|
item.foodamount = static_cast<uint8_t>(getAttributeValueInt(itemElement, "foodamount"));
|
||||||
|
item.foodfrequency = static_cast<uint8_t>(getAttributeValueInt(itemElement, "foodfrequency"));
|
||||||
|
item.foodtime = static_cast<uint8_t>(getAttributeValueInt(itemElement, "foodtime"));
|
||||||
|
item.maxstack = static_cast<uint8_t>(getAttributeValueInt(itemElement, "maxstack"));
|
||||||
|
item.book = static_cast<uint8_t>(getAttributeValueInt(itemElement, "book"));
|
||||||
|
|
||||||
|
// Parse boolean attributes
|
||||||
|
item.stackable = getAttributeValueBool(itemElement, "stackable");
|
||||||
|
item.twohanded = getAttributeValueBool(itemElement, "twohanded");
|
||||||
|
item.undroppable = getAttributeValueBool(itemElement, "undroppable");
|
||||||
|
item.hidemilestone = getAttributeValueBool(itemElement, "hidemilestone");
|
||||||
|
|
||||||
|
// Parse child elements
|
||||||
|
parseStats(itemElement, item);
|
||||||
|
parseCraftings(itemElement, item);
|
||||||
|
|
||||||
|
m_items[item.id] = std::move(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ItemsConfig::parseStats(tinyxml2::XMLElement* itemElement, Item& item) {
|
||||||
|
for (tinyxml2::XMLElement* statElement = itemElement->FirstChildElement("stat");
|
||||||
|
statElement != nullptr;
|
||||||
|
statElement = statElement->NextSiblingElement("stat")) {
|
||||||
|
|
||||||
|
// Get all attributes of the stat element
|
||||||
|
const tinyxml2::XMLAttribute* attribute = statElement->FirstAttribute();
|
||||||
|
while (attribute) {
|
||||||
|
StatType statType = stringToStatType(attribute->Name());
|
||||||
|
std::string valueStr = attribute->Value();
|
||||||
|
bool isPercentage = false;
|
||||||
|
uint8_t value = 0;
|
||||||
|
|
||||||
|
// Check if value ends with '%' to indicate percentage
|
||||||
|
if (!valueStr.empty() && valueStr.back() == '%') {
|
||||||
|
isPercentage = true;
|
||||||
|
valueStr = valueStr.substr(0, valueStr.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the numeric value
|
||||||
|
try {
|
||||||
|
value = static_cast<uint8_t>(std::stoi(valueStr));
|
||||||
|
} catch (const std::exception&) {
|
||||||
|
value = 0; // Default to 0 if parsing fails
|
||||||
|
}
|
||||||
|
|
||||||
|
item.stats.push_back({statType, value, isPercentage});
|
||||||
|
attribute = attribute->Next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ItemsConfig::parseCraftings(tinyxml2::XMLElement* itemElement, Item& item) {
|
||||||
|
for (tinyxml2::XMLElement* craftElement = itemElement->FirstChildElement("crafting");
|
||||||
|
craftElement != nullptr;
|
||||||
|
craftElement = craftElement->NextSiblingElement("crafting")) {
|
||||||
|
|
||||||
|
ItemCrafting craft;
|
||||||
|
craft.workbench = stringToWorkbenchType(getAttributeValue(craftElement, "workbench"));
|
||||||
|
craft.craftingskill = stringToSkillType(getAttributeValue(craftElement, "craftingskill"));
|
||||||
|
craft.checks = getAttributeValue(craftElement, "checks");
|
||||||
|
|
||||||
|
// Parse crafting items (format: "itemId-amount,itemId-amount,..."
|
||||||
|
std::string craftingItemsStr = getAttributeValue(craftElement, "craftingitems");
|
||||||
|
if (!craftingItemsStr.empty()) {
|
||||||
|
std::stringstream ss(craftingItemsStr);
|
||||||
|
std::string itemEntry;
|
||||||
|
while (std::getline(ss, itemEntry, ',')) {
|
||||||
|
// Remove any whitespace
|
||||||
|
itemEntry.erase(itemEntry.begin(), std::find_if(itemEntry.begin(), itemEntry.end(), [](unsigned char ch) {
|
||||||
|
return !std::isspace(ch);
|
||||||
|
}));
|
||||||
|
itemEntry.erase(std::find_if(itemEntry.rbegin(), itemEntry.rend(), [](unsigned char ch) {
|
||||||
|
return !std::isspace(ch);
|
||||||
|
}).base(), itemEntry.end());
|
||||||
|
|
||||||
|
// Parse "itemId-amount"
|
||||||
|
size_t dashPos = itemEntry.find('-');
|
||||||
|
if (dashPos != std::string::npos) {
|
||||||
|
try {
|
||||||
|
uint16_t itemId = static_cast<uint16_t>(std::stoi(itemEntry.substr(0, dashPos)));
|
||||||
|
uint16_t amount = static_cast<uint16_t>(std::stoi(itemEntry.substr(dashPos + 1)));
|
||||||
|
craft.craftingitems.push_back({itemId, amount});
|
||||||
|
} catch (const std::exception&) {
|
||||||
|
// Skip invalid entries
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
item.craftings.push_back(std::move(craft));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Item* ItemsConfig::getItemById(int id) const {
|
||||||
|
auto it = m_items.find(id);
|
||||||
|
return (it != m_items.end()) ? &it->second : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ItemsConfig::getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue) {
|
||||||
|
const char* value = element->Attribute(attribute);
|
||||||
|
return value ? std::string(value) : defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ItemsConfig::getAttributeValueInt(tinyxml2::XMLElement* element, const char* attribute, int defaultValue) {
|
||||||
|
int value = defaultValue;
|
||||||
|
element->QueryIntAttribute(attribute, &value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ItemsConfig::getAttributeValueBool(tinyxml2::XMLElement* element, const char* attribute, bool defaultValue) {
|
||||||
|
bool value = defaultValue;
|
||||||
|
element->QueryBoolAttribute(attribute, &value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace cursebreaker
|
||||||
41
src/main.cpp
Normal file
41
src/main.cpp
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
#include "project_parser.h"
|
||||||
|
#include "tree_builder.h"
|
||||||
|
#include "assets/scene.hpp"
|
||||||
|
#include "configs/items.hpp"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
ParseProject("../../CBAssets/_GameAssets/");
|
||||||
|
|
||||||
|
// build tree
|
||||||
|
for (auto& scene : g_parsedProject.m_sceneAssets)
|
||||||
|
{
|
||||||
|
BuildTree(scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test items parser
|
||||||
|
std::cout << "Testing items parser..." << std::endl;
|
||||||
|
auto& itemsConfig = cursebreaker::ItemsConfig::getInstance();
|
||||||
|
bool success = itemsConfig.loadFromXML("../../CBAssets/Data/XMLs/Items/Items.xml");
|
||||||
|
if (success) {
|
||||||
|
std::cout << "Successfully loaded items XML!" << std::endl;
|
||||||
|
|
||||||
|
// Test getting a specific item
|
||||||
|
const auto* item = itemsConfig.getItemById(150);
|
||||||
|
if (item) {
|
||||||
|
std::cout << "Found item: " << item->name << " (ID: " << item->id << ")" << std::endl;
|
||||||
|
std::cout << "Description: " << item->description << std::endl;
|
||||||
|
std::cout << "Level: " << item->level << ", Price: " << item->price << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cout << "Item with ID 150 not found" << std::endl;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::cout << "Failed to load items XML!" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
463
src/project_parser.cpp
Normal file
463
src/project_parser.cpp
Normal file
@@ -0,0 +1,463 @@
|
|||||||
|
#include "project_parser.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <string_view>
|
||||||
|
#include <span>
|
||||||
|
#include <thread>
|
||||||
|
#include <future>
|
||||||
|
#include <mutex>
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
|
#include <regex>
|
||||||
|
#include <ranges>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
#include <ryml.hpp>
|
||||||
|
#include <ryml_std.hpp>
|
||||||
|
#include <c4/format.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
#include "assets/asset_base.hpp"
|
||||||
|
#include "assets/game_object.hpp"
|
||||||
|
#include "assets/transform.hpp"
|
||||||
|
#include "assets/mesh_filter.hpp"
|
||||||
|
#include "assets/prefab_instance.hpp"
|
||||||
|
|
||||||
|
constexpr uint32_t fnv1a_hash(const char* str, uint32_t hash = 2166136261u)
|
||||||
|
{
|
||||||
|
return (*str == '\0') ? hash : fnv1a_hash(str + 1, (hash ^ static_cast<uint32_t>(*str)) * 16777619u);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t fnv1a_hash(c4::csubstr substr)
|
||||||
|
{
|
||||||
|
uint32_t hash = 2166136261u;
|
||||||
|
for (const auto& c : substr)
|
||||||
|
{
|
||||||
|
hash = (hash ^ static_cast<uint32_t>(c)) * 16777619u;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t fnv1a_hash(std::string_view substr)
|
||||||
|
{
|
||||||
|
uint32_t hash = 2166136261u;
|
||||||
|
for (const auto& c : substr)
|
||||||
|
{
|
||||||
|
hash = (hash ^ static_cast<uint32_t>(c)) * 16777619u;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AssetGUID GetAssetGUIDFromFile(const std::filesystem::path& path)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
// read file as yaml and get "guid"
|
||||||
|
std::ifstream file(path);
|
||||||
|
std::string content{ std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>() };
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ryml::Tree tree = ryml::parse_in_place(c4::substr{ content.data(), content.size() });
|
||||||
|
ryml::ConstNodeRef root = tree.crootref();
|
||||||
|
return ParseAssetGUID(root);
|
||||||
|
}
|
||||||
|
catch (const std::runtime_error& e)
|
||||||
|
{
|
||||||
|
std::cerr << "Error parsing asset GUID: " << e.what() << std::endl;
|
||||||
|
return AssetGUID{};
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// regex match the GUID from the contents of the file
|
||||||
|
static auto pattern = std::regex(R"(guid: (\w+))");
|
||||||
|
std::smatch match{};
|
||||||
|
std::ifstream file(path);
|
||||||
|
std::string content{ std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>() };
|
||||||
|
if (std::regex_search(content, match, pattern))
|
||||||
|
{
|
||||||
|
assert(match[1].length() == 32);
|
||||||
|
AssetGUID guid{ std::string_view{ &*match[1].first, static_cast<size_t>(match[1].length()) } };
|
||||||
|
return guid;
|
||||||
|
}
|
||||||
|
return AssetGUID{};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_map<int64_t, ryml::Tree> ParseYamlFile(std::string& content, bool in_place)
|
||||||
|
{
|
||||||
|
std::unordered_map<int64_t, ryml::Tree> Data{};
|
||||||
|
|
||||||
|
std::string::size_type start = content.find("---", 0);
|
||||||
|
std::string::size_type end = content.find("---", start + 1);
|
||||||
|
|
||||||
|
while (end != std::string::npos)
|
||||||
|
{
|
||||||
|
auto document = std::string_view(content.data() + start, content.data() + end);
|
||||||
|
|
||||||
|
// if the document contains "stripped", skip it
|
||||||
|
auto str_view = std::string_view(document.data(), document.size());
|
||||||
|
if (str_view.find("stripped") != std::string_view::npos)
|
||||||
|
goto skip;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto secondLine = str_view.find('\n') + 1;
|
||||||
|
auto classStr = std::string_view(document.data() + secondLine, str_view.find(':', secondLine) - secondLine);
|
||||||
|
uint32_t classHash = fnv1a_hash(classStr);
|
||||||
|
|
||||||
|
constexpr std::array<uint32_t, 5> availableClasses = {
|
||||||
|
fnv1a_hash("GameObject"),
|
||||||
|
fnv1a_hash("Transform"),
|
||||||
|
fnv1a_hash("MeshFilter"),
|
||||||
|
fnv1a_hash("PrefabInstance"),
|
||||||
|
fnv1a_hash("MonoBehaviour")
|
||||||
|
};
|
||||||
|
if (std::ranges::find(availableClasses, classHash) == availableClasses.end()) goto skip;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ryml::Tree tree{};
|
||||||
|
|
||||||
|
if (in_place)
|
||||||
|
{
|
||||||
|
tree = ryml::parse_in_place(c4::substr{ const_cast<char*>(document.data()), document.size() });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tree = ryml::parse_in_arena(c4::csubstr{ const_cast<char*>(document.data()), document.size() });
|
||||||
|
}
|
||||||
|
auto anchorData = tree.crootref().child(0).val_anchor();
|
||||||
|
int64_t ID{ };
|
||||||
|
auto [ptr, ec] = std::from_chars(anchorData.data(), anchorData.data() + anchorData.size(), ID);
|
||||||
|
assert(ec == std::errc{});
|
||||||
|
|
||||||
|
Data.emplace(ID, std::move(tree));
|
||||||
|
}
|
||||||
|
catch (const std::runtime_error& e)
|
||||||
|
{
|
||||||
|
std::cerr << "Error parsing scene asset: " << e.what() << std::endl;
|
||||||
|
std::cerr << std::string_view(document.data(), document.size()) << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
skip:;
|
||||||
|
|
||||||
|
start = end;
|
||||||
|
end = content.find("---", start + 1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t GetNewID(const SceneAsset& scene, int64_t lastID = 0)
|
||||||
|
{
|
||||||
|
for (;; ++lastID)
|
||||||
|
{
|
||||||
|
if (scene.IDtoAsset.find(lastID) == scene.IDtoAsset.end())
|
||||||
|
return lastID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void MergeComponents(SceneAsset& scene, std::unordered_map<int64_t, int64_t>& idMap, std::vector<T>& to, const std::vector<T>& from, int64_t& lastID)
|
||||||
|
{
|
||||||
|
for (const auto& asset : from)
|
||||||
|
{
|
||||||
|
// create a copy of the asset
|
||||||
|
auto assetCopy = asset;
|
||||||
|
|
||||||
|
// get a new ID
|
||||||
|
lastID = GetNewID(scene, lastID);
|
||||||
|
assetCopy.ID = lastID;
|
||||||
|
|
||||||
|
// register the new ID to the scene
|
||||||
|
scene.IDtoAsset.emplace(assetCopy.ID, nullptr);
|
||||||
|
|
||||||
|
// add the asset to the scene
|
||||||
|
to.emplace_back(assetCopy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MergeScenes(SceneAsset& to, const SceneAsset& from)
|
||||||
|
{
|
||||||
|
std::unordered_map<int64_t, int64_t> newIDMap{};
|
||||||
|
|
||||||
|
// randomize the IDs to avoid conflicts
|
||||||
|
int64_t lastID = std::random_device{}();
|
||||||
|
MergeComponents(to, newIDMap, to.gameObjects, from.gameObjects, lastID);
|
||||||
|
MergeComponents(to, newIDMap, to.transforms, from.transforms, lastID);
|
||||||
|
MergeComponents(to, newIDMap, to.meshFilters, from.meshFilters, lastID);
|
||||||
|
|
||||||
|
auto& IDMapLinked = reinterpret_cast<std::unordered_map<int64_t, AssetBase*>&>(newIDMap);
|
||||||
|
for (auto& gameObject : to.gameObjects) LinkGameObject(IDMapLinked, gameObject);
|
||||||
|
for (auto& transform : to.transforms) LinkTransform(IDMapLinked, transform);
|
||||||
|
for (auto& meshFilter : to.meshFilters) LinkMeshFilter(IDMapLinked, meshFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParseScene(SceneAsset& asset, const std::unordered_map<int64_t, ryml::Tree>& documents);
|
||||||
|
|
||||||
|
void InstantiatePrefab(SceneAsset& parentScene, const ryml::Tree& instance)
|
||||||
|
{
|
||||||
|
auto instanceData = instance.crootref().child(0).child(0);
|
||||||
|
|
||||||
|
// parse the prebad UID
|
||||||
|
AssetGUID prefabAssetGUID = ParseAssetGUID(instanceData["m_SourcePrefab"]);
|
||||||
|
auto prefabAssetIt = g_parsedProject.m_prefabsMap.find(prefabAssetGUID);
|
||||||
|
if (prefabAssetIt == g_parsedProject.m_prefabsMap.end())
|
||||||
|
{
|
||||||
|
std::cerr << "Prefab asset not found: " << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const PrefabAsset& prefabAsset = *prefabAssetIt->second;
|
||||||
|
auto prefabAssetTree = prefabAsset.Data;
|
||||||
|
|
||||||
|
// apply the modifications to the prefab asset tree
|
||||||
|
const auto& overrides = instanceData["m_Modification"]["m_Modifications"];
|
||||||
|
for (auto override : overrides)
|
||||||
|
{
|
||||||
|
int64_t ref = reinterpret_cast<int64_t>(ParseAssetRef<void>(override["target"]));
|
||||||
|
auto prefabAssetTreeIt = prefabAssetTree.find(ref);
|
||||||
|
if (prefabAssetTreeIt == prefabAssetTree.end())
|
||||||
|
{
|
||||||
|
std::cerr << "Prefab asset tree not found: " << ref << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto& targetTree = prefabAssetTreeIt->second;
|
||||||
|
|
||||||
|
const auto propertyPath{ override["propertyPath"].val() };
|
||||||
|
const auto& overrideValue = override["value"];
|
||||||
|
|
||||||
|
// Use lookup_path_or_modify to set the value at the property path
|
||||||
|
// Start from the root of the document (child(0) is the document root)
|
||||||
|
auto rootId = targetTree.crootref().child(0).id();
|
||||||
|
targetTree.lookup_path_or_modify(&instance, overrideValue.id(), propertyPath, rootId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse the prefab asset tree into a scene
|
||||||
|
SceneAsset scene{};
|
||||||
|
ParseScene(scene, prefabAssetTree);
|
||||||
|
|
||||||
|
// get the index of the added transform
|
||||||
|
size_t addedTransformIndex = scene.transforms.size();
|
||||||
|
|
||||||
|
// merge the scenes
|
||||||
|
MergeScenes(parentScene, scene);
|
||||||
|
|
||||||
|
// find the parent transform and add a the instantiated prebab to the children
|
||||||
|
auto parentID = ParseAssetRef<TransformAsset>(instanceData["m_TransformParent"]);
|
||||||
|
scene.transforms[addedTransformIndex].Parent = parentID;
|
||||||
|
auto parentTransform = std::find_if(scene.transforms.begin(), scene.transforms.end(), [parentID](const TransformAsset& asset)
|
||||||
|
{
|
||||||
|
return asset.ID == reinterpret_cast<int64_t>(parentID);
|
||||||
|
});
|
||||||
|
if (parentTransform != scene.transforms.end())
|
||||||
|
{
|
||||||
|
parentTransform->Children.push_back(reinterpret_cast<TransformAsset*>(scene.transforms[addedTransformIndex].ID));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void LinkAssets(SceneAsset& sceneAsset)
|
||||||
|
{
|
||||||
|
std::unordered_map<int64_t, AssetBase*> assetsMap;
|
||||||
|
|
||||||
|
for (auto& gameObject : sceneAsset.gameObjects) assetsMap.emplace(gameObject.ID, &gameObject);
|
||||||
|
for (auto& transform : sceneAsset.transforms) assetsMap.emplace(transform.ID, &transform);
|
||||||
|
for (auto& meshFilter : sceneAsset.meshFilters) assetsMap.emplace(meshFilter.ID, &meshFilter);
|
||||||
|
|
||||||
|
for (auto& gameObject : sceneAsset.gameObjects) LinkGameObject(assetsMap, gameObject);
|
||||||
|
for (auto& transform : sceneAsset.transforms) LinkTransform(assetsMap, transform);
|
||||||
|
for (auto& meshFilter : sceneAsset.meshFilters) LinkMeshFilter(assetsMap, meshFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParseScene(SceneAsset& asset, const std::unordered_map<int64_t, ryml::Tree>& documents)
|
||||||
|
{
|
||||||
|
for (auto& [ID, tree] : documents)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ryml::ConstNodeRef root = tree.crootref();
|
||||||
|
|
||||||
|
// Each document should have one root node
|
||||||
|
if (root.num_children() > 0)
|
||||||
|
{
|
||||||
|
auto node = root.child(0).child(0); // The document root
|
||||||
|
auto assetType = node.key();
|
||||||
|
|
||||||
|
switch (fnv1a_hash(assetType))
|
||||||
|
{
|
||||||
|
case fnv1a_hash("GameObject"): asset.gameObjects.emplace_back(ParseGameObject(node)).ID = ID; break;
|
||||||
|
case fnv1a_hash("Transform"): asset.transforms.emplace_back(ParseTransform(node)).ID = ID; break;
|
||||||
|
case fnv1a_hash("MeshFilter"): asset.meshFilters.emplace_back(ParseMeshFilter(node)).ID = ID; break;
|
||||||
|
case fnv1a_hash("PrefabInstance"):
|
||||||
|
{
|
||||||
|
InstantiatePrefab(asset, tree);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case fnv1a_hash("MonoBehaviour"):
|
||||||
|
{
|
||||||
|
auto scriptGuid = ParseAssetGUID(node["m_Script"]);
|
||||||
|
auto it = g_parsedProject.m_scriptToClassHash.find(scriptGuid);
|
||||||
|
if (it != g_parsedProject.m_scriptToClassHash.end())
|
||||||
|
{
|
||||||
|
switch (it->second)
|
||||||
|
{
|
||||||
|
case fnv1a_hash("Interactable_Resource"):
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
std::cerr << assetType << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
asset.IDtoAsset.emplace(ID, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
std::cerr << e.what();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LinkAssets(asset);
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneAsset ParseSceneAsset(const AssetPath& path)
|
||||||
|
{
|
||||||
|
SceneAsset sceneAsset;
|
||||||
|
sceneAsset.path = path.path;
|
||||||
|
sceneAsset.name = path.path.stem().string();
|
||||||
|
|
||||||
|
// read the file
|
||||||
|
std::ifstream file(path.path);
|
||||||
|
std::string content{ std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>() };
|
||||||
|
|
||||||
|
auto objects = ParseYamlFile(content, true);
|
||||||
|
|
||||||
|
ParseScene(sceneAsset, objects);
|
||||||
|
|
||||||
|
return sceneAsset;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<SceneAsset> ParseScenes(const std::vector<AssetPath>& sceneFiles)
|
||||||
|
{
|
||||||
|
std::vector<SceneAsset> sceneAssets;
|
||||||
|
for (const auto& sceneFile : sceneFiles)
|
||||||
|
{
|
||||||
|
sceneAssets.emplace_back(ParseSceneAsset(sceneFile));
|
||||||
|
std::cout << "Parsed: " << sceneFile.path.filename() << '\n';
|
||||||
|
}
|
||||||
|
return sceneAssets;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrefabAsset ParsePrefabAsset(const AssetPath& path)
|
||||||
|
{
|
||||||
|
PrefabAsset prefabAsset;
|
||||||
|
prefabAsset.path = path.path;
|
||||||
|
prefabAsset.name = path.path.stem().string();
|
||||||
|
|
||||||
|
{
|
||||||
|
// read the file
|
||||||
|
std::ifstream file(path.path);
|
||||||
|
std::string content{ std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>() };
|
||||||
|
|
||||||
|
prefabAsset.Data = ParseYamlFile(content, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return prefabAsset;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<PrefabAsset> ParsePrefabs(const std::vector<AssetPath>& prefabFiles, std::unordered_map<AssetGUID, PrefabAsset*>& prefabMap)
|
||||||
|
{
|
||||||
|
std::vector<PrefabAsset> prefabAssets;
|
||||||
|
for (const auto& prefabFile : prefabFiles)
|
||||||
|
{
|
||||||
|
prefabAssets.emplace_back(ParsePrefabAsset(prefabFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
prefabMap.reserve(prefabAssets.size());
|
||||||
|
for (size_t i = 0; i < prefabAssets.size(); ++i)
|
||||||
|
{
|
||||||
|
prefabMap.emplace(prefabFiles[i].GUID, &prefabAssets[i]);
|
||||||
|
}
|
||||||
|
return prefabAssets;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParseDirectory(ParsedProject& project)
|
||||||
|
{
|
||||||
|
for (const auto& entry : std::filesystem::recursive_directory_iterator(project.m_projectRoot))
|
||||||
|
{
|
||||||
|
std::string extension = entry.path().extension().string();
|
||||||
|
|
||||||
|
if (!entry.is_directory())
|
||||||
|
{
|
||||||
|
if (extension == ".unity")
|
||||||
|
{
|
||||||
|
project.m_sceneFiles.push_back(AssetPath{ entry.path(), GetAssetGUIDFromFile(entry.path().string() + ".meta") });
|
||||||
|
}
|
||||||
|
else if (extension == ".prefab")
|
||||||
|
{
|
||||||
|
project.m_prefabFiles.push_back(AssetPath{ entry.path(), GetAssetGUIDFromFile(entry.path().string() + ".meta") });
|
||||||
|
}
|
||||||
|
else if (extension == ".cs")
|
||||||
|
{
|
||||||
|
auto guid = GetAssetGUIDFromFile(entry.path().string() + ".meta");
|
||||||
|
project.m_scriptFiles.push_back(AssetPath{ entry.path(), guid });
|
||||||
|
project.m_scriptToClassHash.emplace(guid, fnv1a_hash(entry.path().filename().string()));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
if (extension == ".png" || extension == ".jpg" || extension == ".jpeg" || extension == ".bmp" || extension == ".tiff" || extension == ".webp")
|
||||||
|
{
|
||||||
|
project.m_imageFiles.push_back(AssetPath{ entry.path(), GetAssetGUIDFromFile(entry.path().string() + ".meta") });
|
||||||
|
}
|
||||||
|
else if (extension == ".fbx" || extension == ".obj" || extension == ".glb" || extension == ".gltf")
|
||||||
|
{
|
||||||
|
project.m_meshFiles.push_back(AssetPath{ entry.path(), GetAssetGUIDFromFile(entry.path().string() + ".meta") });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ParsedProject g_parsedProject;
|
||||||
|
|
||||||
|
void ParseProject(const std::filesystem::path& projectRoot)
|
||||||
|
{
|
||||||
|
g_parsedProject = ParsedProject{};
|
||||||
|
ParsedProject& project = g_parsedProject;
|
||||||
|
project.m_projectRoot = projectRoot;
|
||||||
|
|
||||||
|
ParseDirectory(project);
|
||||||
|
|
||||||
|
auto tilesFilesFilter = std::views::filter([](const AssetPath& path)
|
||||||
|
{
|
||||||
|
#ifdef _DEBUG
|
||||||
|
static auto pattern = std::regex(R"(Tiles\/10_3)");
|
||||||
|
#else
|
||||||
|
static auto pattern = std::regex(R"(Tiles\/\d+_\d+)");
|
||||||
|
#endif
|
||||||
|
return std::regex_search(path.path.generic_string(), pattern);
|
||||||
|
});
|
||||||
|
auto tilesFiles = project.m_sceneFiles | tilesFilesFilter;
|
||||||
|
|
||||||
|
auto prefabFilter = std::views::filter([](const AssetPath& path)
|
||||||
|
{
|
||||||
|
constexpr std::string_view prefabPath = "_GameAssets/Prefabs";
|
||||||
|
return path.path.generic_string().find(prefabPath) != std::string::npos;
|
||||||
|
});
|
||||||
|
auto prefabFiles = project.m_prefabFiles | prefabFilter;
|
||||||
|
|
||||||
|
project.m_prefabAssets = ParsePrefabs(std::vector<AssetPath>(prefabFiles.begin(), prefabFiles.end()), project.m_prefabsMap);
|
||||||
|
|
||||||
|
project.m_sceneAssets = ParseScenes(std::vector<AssetPath>(tilesFiles.begin(), tilesFiles.end()));
|
||||||
|
}
|
||||||
|
|
||||||
54
src/tree_builder.cpp
Normal file
54
src/tree_builder.cpp
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#include "tree_builder.h"
|
||||||
|
#include "assets/scene.hpp"
|
||||||
|
#include "assets/transform.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
|
||||||
|
glm::mat4 composeMatrix(const glm::vec3& pos, const glm::vec3& scale, const glm::quat& rotation) {
|
||||||
|
// Create translation matrix
|
||||||
|
glm::mat4 translationMatrix = glm::translate(glm::mat4(1.0f), pos);
|
||||||
|
|
||||||
|
// Convert quaternion to rotation matrix
|
||||||
|
glm::mat4 rotationMatrix = glm::mat4_cast(rotation);
|
||||||
|
|
||||||
|
// Create scale matrix
|
||||||
|
glm::mat4 scaleMatrix = glm::scale(glm::mat4(1.0f), scale);
|
||||||
|
|
||||||
|
// Combine: translation * rotation * scale
|
||||||
|
return translationMatrix * rotationMatrix * scaleMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuildTreeHelper(TransformAsset& transform, const glm::mat4& parentMatrix)
|
||||||
|
{
|
||||||
|
for (auto& child : transform.Children)
|
||||||
|
{
|
||||||
|
auto childMatrix = composeMatrix(transform.Position, transform.Scale, transform.Rotation);
|
||||||
|
transform.GlobalMatrix = parentMatrix * childMatrix;
|
||||||
|
|
||||||
|
BuildTreeHelper(*child, transform.GlobalMatrix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuildTree(SceneAsset& sceneAsset)
|
||||||
|
{
|
||||||
|
// find transforms with no parent
|
||||||
|
std::vector<TransformAsset*> rootTransforms;
|
||||||
|
|
||||||
|
for (auto& transform : sceneAsset.transforms)
|
||||||
|
{
|
||||||
|
if (transform.Parent == nullptr)
|
||||||
|
{
|
||||||
|
rootTransforms.push_back(&transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// build tree
|
||||||
|
for (auto rootTransform : rootTransforms)
|
||||||
|
{
|
||||||
|
BuildTreeHelper(*rootTransform, glm::mat4(1.0f));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user