added npc's gift
This commit is contained in:
parent
bc2997d86f
commit
34759326b9
@ -14,7 +14,13 @@
|
||||
"modelScale": 0.01,
|
||||
"modelCorrectionRotX": 0.0,
|
||||
"modelCorrectionRotY": 180.0,
|
||||
"modelCorrectionRotZ": 0.0
|
||||
"modelCorrectionRotZ": 0.0,
|
||||
"gift": {
|
||||
"id": "guard_token",
|
||||
"name": "Guard's Token",
|
||||
"description": "A token from the Guard - sign of respect",
|
||||
"icon": "resources/fire2.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "npc_02_ghost",
|
||||
@ -30,7 +36,13 @@
|
||||
"modelScale": 0.01,
|
||||
"modelCorrectionRotX": 0.0,
|
||||
"modelCorrectionRotY": 180.0,
|
||||
"modelCorrectionRotZ": 0.0
|
||||
"modelCorrectionRotZ": 0.0,
|
||||
"gift": {
|
||||
"id": "ghost_essence",
|
||||
"name": "Ghost's Essence",
|
||||
"description": "A mysterious essence from the Ghost realm",
|
||||
"icon": "resources/fire2.png"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -26,3 +26,13 @@ function on_health_pickup()
|
||||
|
||||
end
|
||||
|
||||
-- ============================================
|
||||
-- NPC INTERACTION HANDLER
|
||||
-- ============================================
|
||||
|
||||
function on_npc_interact(npc_index)
|
||||
print("[Lua] NPC interaction! Index: " .. tostring(npc_index))
|
||||
game_api.receive_npc_gift(npc_index)
|
||||
end
|
||||
|
||||
print("Lua script loaded successfully!")
|
||||
@ -2,6 +2,7 @@
|
||||
#include "BoneAnimatedModel.h"
|
||||
#include "render/Renderer.h"
|
||||
#include "render/TextureManager.h"
|
||||
#include "items/Item.h"
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
@ -40,6 +41,12 @@ public:
|
||||
// Applied after scale, fixes model-space orientation (e.g. Blender Z-up exports)
|
||||
Eigen::Quaternionf modelCorrectionRotation = Eigen::Quaternionf::Identity();
|
||||
|
||||
// NPC Gift
|
||||
Item giftItem;
|
||||
std::string npcId;
|
||||
std::string npcName;
|
||||
bool giftReceived = false;
|
||||
|
||||
private:
|
||||
struct AnimationData {
|
||||
BoneSystem model;
|
||||
|
||||
39
src/Game.cpp
39
src/Game.cpp
@ -306,6 +306,28 @@ namespace ZL
|
||||
return closestObject;
|
||||
}
|
||||
|
||||
Character* Game::raycastNpcs(const Eigen::Vector3f& rayOrigin, const Eigen::Vector3f& rayDir, float maxDistance) {
|
||||
Character* closestNpc = nullptr;
|
||||
float closestDist = maxDistance;
|
||||
|
||||
for (auto& npc : npcs) {
|
||||
Eigen::Vector3f toNpc = npc->position - rayOrigin;
|
||||
float distAlongRay = toNpc.dot(rayDir);
|
||||
if (distAlongRay < 0.1f) continue;
|
||||
|
||||
Eigen::Vector3f closestPoint = rayOrigin + rayDir * distAlongRay;
|
||||
float distToNpc = (closestPoint - npc->position).norm();
|
||||
|
||||
float radius = npc->modelScale * 50.0f;
|
||||
|
||||
if (distToNpc <= radius && distAlongRay < closestDist) {
|
||||
closestDist = distAlongRay;
|
||||
closestNpc = npc.get();
|
||||
}
|
||||
}
|
||||
return closestNpc;
|
||||
}
|
||||
|
||||
void Game::drawUI()
|
||||
{
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
@ -594,6 +616,22 @@ namespace ZL
|
||||
player->setTarget(clickedObject->position);
|
||||
std::cout << "[CLICK] Player moving to object..." << std::endl;
|
||||
}
|
||||
else {
|
||||
// Check if we clicked on an NPC
|
||||
Character* clickedNpc = raycastNpcs(camPos, rayDir);
|
||||
if (clickedNpc && player) {
|
||||
int npcIndex = -1;
|
||||
for (size_t i = 0; i < npcs.size(); ++i) {
|
||||
if (npcs[i].get() == clickedNpc) {
|
||||
npcIndex = static_cast<int>(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (npcIndex != -1) {
|
||||
std::cout << "[CLICK] *** SUCCESS: Clicked on NPC index: " << npcIndex << " ***" << std::endl;
|
||||
scriptEngine.callNpcInteractCallback(npcIndex);
|
||||
}
|
||||
}
|
||||
else if (rayDir.y() < -0.001f && player) {
|
||||
// Otherwise, unproject click to ground plane for Viola's walk target
|
||||
float t = -camPos.y() / rayDir.y();
|
||||
@ -604,6 +642,7 @@ namespace ZL
|
||||
else {
|
||||
std::cout << "[CLICK] No valid target found" << std::endl;
|
||||
}
|
||||
}
|
||||
std::cout << "========================================\n" << std::endl;
|
||||
} else {
|
||||
handleUp(ZL::UiManager::MOUSE_FINGER_ID, mx, my);
|
||||
|
||||
@ -98,6 +98,7 @@ namespace ZL {
|
||||
void handleUp(int64_t fingerId, int mx, int my);
|
||||
void handleMotion(int64_t fingerId, int mx, int my);
|
||||
InteractiveObject* raycastInteractiveObjects(const Eigen::Vector3f& rayOrigin, const Eigen::Vector3f& rayDir);
|
||||
Character* raycastNpcs(const Eigen::Vector3f& rayOrigin, const Eigen::Vector3f& rayDir, float maxDistance = 100.0f);
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
static Game* s_instance;
|
||||
|
||||
@ -138,9 +138,64 @@ namespace ZL {
|
||||
return game->inventory.hasItem(id);
|
||||
});
|
||||
|
||||
// receive_npc_gift(npc_index)
|
||||
api.set_function("receive_npc_gift", [game](int npcIndex) {
|
||||
std::cout << "[script] receive_npc_gift: npc index " << npcIndex << std::endl;
|
||||
|
||||
auto& npcs = game->npcs;
|
||||
if (npcIndex < 0 || npcIndex >= static_cast<int>(npcs.size())) {
|
||||
std::cerr << "[script] receive_npc_gift: index out of range\n";
|
||||
return;
|
||||
}
|
||||
|
||||
auto& npc = npcs[npcIndex];
|
||||
if (!npc) {
|
||||
std::cerr << "[script] receive_npc_gift: npc is null\n";
|
||||
return;
|
||||
}
|
||||
|
||||
if (npc->giftReceived) {
|
||||
std::cout << "[script] NPC " << npc->npcName << " already gave gift\n";
|
||||
return;
|
||||
}
|
||||
|
||||
if (npc->giftItem.id.empty()) {
|
||||
std::cerr << "[script] receive_npc_gift: NPC has no gift\n";
|
||||
return;
|
||||
}
|
||||
|
||||
game->inventory.addItem(npc->giftItem);
|
||||
npc->giftReceived = true;
|
||||
|
||||
std::cout << "[script] Received gift from " << npc->npcName << ": "
|
||||
<< npc->giftItem.name << std::endl;
|
||||
});
|
||||
|
||||
lua.script_file("resources/start.lua");
|
||||
}
|
||||
|
||||
void ScriptEngine::callNpcInteractCallback(int npcIndex) {
|
||||
if (!impl) {
|
||||
std::cerr << "[SCRIPT] Engine not initialized!" << std::endl;
|
||||
return;
|
||||
}
|
||||
sol::state& lua = impl->lua;
|
||||
sol::function fn = lua["on_npc_interact"];
|
||||
if (fn.valid()) {
|
||||
auto result = fn(npcIndex);
|
||||
if (!result.valid()) {
|
||||
sol::error err = result;
|
||||
std::cerr << "[SCRIPT] on_npc_interact error: " << err.what() << "\n";
|
||||
}
|
||||
else {
|
||||
std::cout << "[SCRIPT] on_npc_interact called with index " << npcIndex << std::endl;
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::cerr << "[SCRIPT] Lua function 'on_npc_interact' not found!" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEngine::runScript(const std::string& path) {
|
||||
auto result = impl->lua.safe_script_file(path, sol::script_pass_on_error);
|
||||
if (!result.valid()) {
|
||||
|
||||
@ -22,6 +22,7 @@ public:
|
||||
void callActivateFunction(const std::string& functionName);
|
||||
|
||||
void showInventory(Game* game);
|
||||
void callNpcInteractCallback(int npcIndex);
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
|
||||
@ -296,6 +296,15 @@ namespace ZL {
|
||||
data.modelCorrectionRotY = item.value("modelCorrectionRotY", 0.0f) * static_cast<float>(M_PI) / 180.0f;
|
||||
data.modelCorrectionRotZ = item.value("modelCorrectionRotZ", 0.0f) * static_cast<float>(M_PI) / 180.0f;
|
||||
|
||||
// Load gift data if available
|
||||
if (item.contains("gift") && item["gift"].is_object()) {
|
||||
const auto& giftData = item["gift"];
|
||||
data.gift.id = giftData.value("id", "");
|
||||
data.gift.name = giftData.value("name", "Gift");
|
||||
data.gift.description = giftData.value("description", "A gift from an NPC");
|
||||
data.gift.icon = giftData.value("icon", "");
|
||||
}
|
||||
|
||||
npcs.push_back(data);
|
||||
}
|
||||
|
||||
@ -356,7 +365,16 @@ namespace ZL {
|
||||
npc->modelScale = npcData.modelScale;
|
||||
npc->position = Eigen::Vector3f(npcData.positionX, npcData.positionY, npcData.positionZ);
|
||||
|
||||
// Set model correction rotation
|
||||
// Set NPC metadata
|
||||
npc->npcId = npcData.id;
|
||||
npc->npcName = npcData.name;
|
||||
|
||||
// Set gift
|
||||
if (!npcData.gift.id.empty()) {
|
||||
npc->giftItem = Item(npcData.gift.id, npcData.gift.name, npcData.gift.description, npcData.gift.icon);
|
||||
std::cout << " Gift: " << npcData.gift.name << std::endl;
|
||||
}
|
||||
|
||||
Eigen::Quaternionf corrRotQuat = Eigen::Quaternionf::Identity();
|
||||
if (npcData.modelCorrectionRotX != 0.0f) {
|
||||
corrRotQuat = Eigen::Quaternionf(Eigen::AngleAxisf(npcData.modelCorrectionRotX, Eigen::Vector3f::UnitX())) * corrRotQuat;
|
||||
|
||||
@ -34,6 +34,13 @@ namespace ZL {
|
||||
std::string activateFunctionName;
|
||||
};
|
||||
|
||||
struct GiftData {
|
||||
std::string id;
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::string icon;
|
||||
};
|
||||
|
||||
struct NpcData {
|
||||
std::string id;
|
||||
std::string name;
|
||||
@ -49,6 +56,7 @@ namespace ZL {
|
||||
float modelCorrectionRotX = 0.0f;
|
||||
float modelCorrectionRotY = 0.0f;
|
||||
float modelCorrectionRotZ = 0.0f;
|
||||
GiftData gift;
|
||||
};
|
||||
|
||||
struct LoadedGameObject {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user