diff --git a/resources/config2/npcs.json b/resources/config2/npcs.json new file mode 100644 index 0000000..1866615 --- /dev/null +++ b/resources/config2/npcs.json @@ -0,0 +1,36 @@ +{ + "npcs": [ + { + "id": "npc_01_default", + "name": "NPC Default Guard", + "texturePath": "resources/w/default_skin001.png", + "animationIdlePath": "resources/w/default_idle002.txt", + "animationWalkPath": "resources/w/default_walk001.txt", + "positionX": 0.0, + "positionY": 0.0, + "positionZ": -10.0, + "walkSpeed": 1.5, + "rotationSpeed": 8.0, + "modelScale": 0.01, + "modelCorrectionRotX": 0.0, + "modelCorrectionRotY": 180.0, + "modelCorrectionRotZ": 0.0 + }, + { + "id": "npc_02_ghost", + "name": "NPC Floating Ghost", + "texturePath": "resources/w/ghost_skin001.png", + "animationIdlePath": "resources/w/default_float001.txt", + "animationWalkPath": "resources/w/default_float001.txt", + "positionX": 0.0, + "positionY": 0.0, + "positionZ": -20.0, + "walkSpeed": 1.5, + "rotationSpeed": 8.0, + "modelScale": 0.01, + "modelCorrectionRotX": 0.0, + "modelCorrectionRotY": 180.0, + "modelCorrectionRotZ": 0.0 + } + ] + } \ No newline at end of file diff --git a/src/Character.cpp b/src/Character.cpp index a65a9d9..eec5c21 100644 --- a/src/Character.cpp +++ b/src/Character.cpp @@ -14,9 +14,9 @@ void Character::loadAnimation(AnimationState state, const std::string& filename, } } -void Character::setTexture(std::shared_ptr tex) { +/*void Character::setTexture(std::shared_ptr tex) { texture = tex; -} +}*/ void Character::setTarget(const Eigen::Vector3f& target, std::function onArrived) { diff --git a/src/Character.h b/src/Character.h index 2c66043..f82a51b 100644 --- a/src/Character.h +++ b/src/Character.h @@ -18,7 +18,10 @@ enum class AnimationState { class Character { public: void loadAnimation(AnimationState state, const std::string& filename, const std::string& zipFile = ""); - void setTexture(std::shared_ptr texture); + // void setTexture(std::shared_ptr texture); + void setTexture(std::shared_ptr texture) { + this->texture = texture; + } void update(int64_t deltaMs); // onArrived is called once when the character reaches this target. // From Python you can chain further commands (walk, wait, trigger others). diff --git a/src/Game.cpp b/src/Game.cpp index fdc82fb..e8bce5d 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -169,6 +169,13 @@ namespace ZL Eigen::Quaternionf(Eigen::AngleAxisf(M_PI, Eigen::Vector3f::UnitZ())); std::cout << "Load resurces step 9" << std::endl; + + // Load NPCs from JSON + npcs = GameObjectLoader::loadAndCreateNpcs("resources/config2/npcs.json", CONST_ZIP_FILE); + + /* + // === СТАРЫЙ КОД - ЗАКОММЕНТИРОВАНО, НПС ТЕПЕРЬ ГРУЗЯТСЯ ИЗ JSON === + auto defaultTexture = std::make_shared(CreateTextureDataFromPng("resources/w/default_skin001.png", CONST_ZIP_FILE)); auto npc01 = std::make_unique(); @@ -207,6 +214,9 @@ namespace ZL npcs.push_back(std::move(npc02)); std::cout << "Load resurces step 12" << std::endl; + + // === КОНЕЦ СТАРОГО КОДА === + */ loadingCompleted = true; diff --git a/src/items/GameObjectLoader.cpp b/src/items/GameObjectLoader.cpp index 1d92298..7cfe2e4 100644 --- a/src/items/GameObjectLoader.cpp +++ b/src/items/GameObjectLoader.cpp @@ -244,4 +244,134 @@ namespace ZL { return interactiveObjects; } + std::vector GameObjectLoader::loadNpcsFromJson(const std::string& jsonPath, const std::string& zipPath) + { + std::vector npcs; + json j; + + try { + std::ifstream file(jsonPath); + if (!file.is_open()) { + throw std::runtime_error("Could not open file: " + jsonPath); + } + + if (file.peek() == std::ifstream::traits_type::eof()) { + throw std::runtime_error("JSON file is empty: " + jsonPath); + } + + file >> j; + + if (!j.contains("npcs") || !j["npcs"].is_array()) { + std::cerr << "Warning: 'npcs' array not found in " << jsonPath << std::endl; + return npcs; + } + + for (const auto& item : j["npcs"]) { + NpcData data; + + data.id = item.value("id", "npc_unknown"); + data.name = item.value("name", "Unknown NPC"); + data.texturePath = item.value("texturePath", ""); + data.animationIdlePath = item.value("animationIdlePath", ""); + data.animationWalkPath = item.value("animationWalkPath", ""); + + data.positionX = item.value("positionX", 0.0f); + data.positionY = item.value("positionY", 0.0f); + data.positionZ = item.value("positionZ", 0.0f); + + data.walkSpeed = item.value("walkSpeed", 1.5f); + data.rotationSpeed = item.value("rotationSpeed", 8.0f); + data.modelScale = item.value("modelScale", 0.01f); + + // Model correction rotation (in degrees, will convert to radians) + data.modelCorrectionRotX = item.value("modelCorrectionRotX", 0.0f) * static_cast(M_PI) / 180.0f; + data.modelCorrectionRotY = item.value("modelCorrectionRotY", 0.0f) * static_cast(M_PI) / 180.0f; + data.modelCorrectionRotZ = item.value("modelCorrectionRotZ", 0.0f) * static_cast(M_PI) / 180.0f; + + npcs.push_back(data); + } + + std::cout << "Successfully loaded " << npcs.size() << " NPCs from " << jsonPath << std::endl; + + } catch (const std::exception& e) { + std::cerr << "Error loading NPCs from JSON: " << e.what() << std::endl; + } + + return npcs; + } + + std::vector> GameObjectLoader::loadAndCreateNpcs( + const std::string& jsonPath, + const std::string& zipPath) + { + std::vector> npcs; + std::vector npcsData = loadNpcsFromJson(jsonPath, zipPath); + + for (const auto& npcData : npcsData) { + std::cout << "Loading NPC: " << npcData.name << std::endl; + + auto npc = std::make_unique(); + + // Load animations + try { + npc->loadAnimation(AnimationState::IDLE, npcData.animationIdlePath, zipPath); + std::cout << " Loaded IDLE animation: " << npcData.animationIdlePath << std::endl; + } + catch (const std::exception& e) { + std::cerr << "GameObjectLoader: Failed to load IDLE animation for '" << npcData.name << "': " << e.what() << std::endl; + continue; + } + + try { + npc->loadAnimation(AnimationState::WALK, npcData.animationWalkPath, zipPath); + std::cout << " Loaded WALK animation: " << npcData.animationWalkPath << std::endl; + } + catch (const std::exception& e) { + std::cerr << "GameObjectLoader: Failed to load WALK animation for '" << npcData.name << "': " << e.what() << std::endl; + continue; + } + + // Load texture + try { + auto texture = std::make_shared(CreateTextureDataFromPng(npcData.texturePath, zipPath.c_str())); + npc->setTexture(texture); + std::cout << " Loaded texture: " << npcData.texturePath << std::endl; + } + catch (const std::exception& e) { + std::cerr << "GameObjectLoader: Failed to load texture for '" << npcData.name << "': " << e.what() << std::endl; + continue; + } + + // Set NPC properties + npc->walkSpeed = npcData.walkSpeed; + npc->rotationSpeed = npcData.rotationSpeed; + npc->modelScale = npcData.modelScale; + npc->position = Eigen::Vector3f(npcData.positionX, npcData.positionY, npcData.positionZ); + + // Set model correction rotation + Eigen::Quaternionf corrRotQuat = Eigen::Quaternionf::Identity(); + if (npcData.modelCorrectionRotX != 0.0f) { + corrRotQuat = Eigen::Quaternionf(Eigen::AngleAxisf(npcData.modelCorrectionRotX, Eigen::Vector3f::UnitX())) * corrRotQuat; + } + if (npcData.modelCorrectionRotY != 0.0f) { + corrRotQuat = Eigen::Quaternionf(Eigen::AngleAxisf(npcData.modelCorrectionRotY, Eigen::Vector3f::UnitY())) * corrRotQuat; + } + if (npcData.modelCorrectionRotZ != 0.0f) { + corrRotQuat = Eigen::Quaternionf(Eigen::AngleAxisf(npcData.modelCorrectionRotZ, Eigen::Vector3f::UnitZ())) * corrRotQuat; + } + npc->modelCorrectionRotation = corrRotQuat; + + // Set initial target to current position + npc->setTarget(npc->position); + + npcs.push_back(std::move(npc)); + + std::cout << "Successfully loaded NPC: " << npcData.name << " at position (" + << npcData.positionX << ", " << npcData.positionY << ", " << npcData.positionZ << ")" << std::endl; + } + + std::cout << "Total NPCs loaded: " << npcs.size() << std::endl; + return npcs; + } + } // namespace ZL \ No newline at end of file diff --git a/src/items/GameObjectLoader.h b/src/items/GameObjectLoader.h index 282cf1a..454b17e 100644 --- a/src/items/GameObjectLoader.h +++ b/src/items/GameObjectLoader.h @@ -6,12 +6,13 @@ #include "external/nlohmann/json.hpp" #include "TextModel.h" #include "InteractiveObject.h" - +#include "Character.h" namespace ZL { struct Texture; struct VertexRenderStruct; class Renderer; + class Character; struct GameObjectData { std::string name; @@ -32,6 +33,23 @@ namespace ZL { float interactionRadius = 2.0f; }; + struct NpcData { + std::string id; + std::string name; + std::string texturePath; + std::string animationIdlePath; + std::string animationWalkPath; + float positionX = 0.0f; + float positionY = 0.0f; + float positionZ = 0.0f; + float walkSpeed = 1.5f; + float rotationSpeed = 8.0f; + float modelScale = 0.01f; + float modelCorrectionRotX = 0.0f; + float modelCorrectionRotY = 0.0f; + float modelCorrectionRotZ = 0.0f; + }; + struct LoadedGameObject { std::shared_ptr texture; VertexRenderStruct mesh; @@ -53,6 +71,13 @@ namespace ZL { Renderer& renderer, const std::string& zipPath = "" ); + + static std::vector> loadAndCreateNpcs( + const std::string& jsonPath, + const std::string& zipPath = "" + ); + + static std::vector loadNpcsFromJson(const std::string& jsonPath, const std::string& zipPath = ""); }; } // namespace ZL \ No newline at end of file