minor fixing, adapting for mobile web
This commit is contained in:
1 parent
44cc0fba67
commit
eac914e073
6 files changed
+393
-129
No files matched your search
+108
-22
@@ -19,6 +19,9 @@ namespace ZL
|
||||
extern const char* CONST_ZIP_FILE;
|
||||
|
||||
static constexpr float CAMERA_FOV_Y = 1.0f / 1.5f;
|
||||
// How close the player needs to be to a peaceful NPC before the
|
||||
// on_npc_interact callback fires and the conversation begins.
|
||||
static constexpr float NPC_TALK_DISTANCE = 1.25f;
|
||||
|
||||
Location::Location(Renderer& iRenderer, Inventory& iInventory)
|
||||
: renderer(iRenderer)
|
||||
@@ -293,24 +296,65 @@ namespace ZL
|
||||
}
|
||||
|
||||
Character* Location::raycastNpcs(const Eigen::Vector3f& rayOrigin, const Eigen::Vector3f& rayDir, float maxDistance) {
|
||||
// Every NPC is treated as a vertical cylinder: radius 1.0m, height 1.85m,
|
||||
// base at npc->position (the model's foot). Intersection = circle hit in the
|
||||
// XZ plane, then clip the entry/exit t against the [yFoot, yFoot+height] slab.
|
||||
static constexpr float NPC_CLICK_RADIUS = 1.0f;
|
||||
static constexpr float NPC_CLICK_HEIGHT = 1.85f;
|
||||
|
||||
Character* closestNpc = nullptr;
|
||||
float closestDist = maxDistance;
|
||||
|
||||
std::cout << "[RAYCAST_NPC] Starting raycast with " << npcs.size() << " npcs" << std::endl;
|
||||
|
||||
for (auto& npc : npcs) {
|
||||
Eigen::Vector3f toNpc = npc->position - rayOrigin;
|
||||
float distAlongRay = toNpc.dot(rayDir);
|
||||
if (distAlongRay < 0.1f) continue;
|
||||
const float dx = rayOrigin.x() - npc->position.x();
|
||||
const float dz = rayOrigin.z() - npc->position.z();
|
||||
const float a = rayDir.x() * rayDir.x() + rayDir.z() * rayDir.z();
|
||||
if (a < 1e-6f) continue; // purely-vertical ray; not a real click case
|
||||
|
||||
Eigen::Vector3f closestPoint = rayOrigin + rayDir * distAlongRay;
|
||||
float distToNpc = (closestPoint - npc->position).norm();
|
||||
const float b = 2.0f * (dx * rayDir.x() + dz * rayDir.z());
|
||||
const float c = dx * dx + dz * dz - NPC_CLICK_RADIUS * NPC_CLICK_RADIUS;
|
||||
const float disc = b * b - 4.0f * a * c;
|
||||
if (disc < 0.0f) continue; // ray misses the infinite cylinder
|
||||
|
||||
float radius = npc->modelScale * 50.0f;
|
||||
const float sqrtDisc = std::sqrt(disc);
|
||||
const float tNear = (-b - sqrtDisc) / (2.0f * a);
|
||||
const float tFar = (-b + sqrtDisc) / (2.0f * a);
|
||||
|
||||
if (distToNpc <= radius && distAlongRay < closestDist) {
|
||||
closestDist = distAlongRay;
|
||||
float entryT = max(tNear, 0.1f);
|
||||
float exitT = tFar;
|
||||
|
||||
// Clip against the cylinder's vertical extent.
|
||||
const float yMin = npc->position.y();
|
||||
const float yMax = yMin + NPC_CLICK_HEIGHT;
|
||||
if (std::abs(rayDir.y()) > 1e-6f) {
|
||||
const float tYa = (yMin - rayOrigin.y()) / rayDir.y();
|
||||
const float tYb = (yMax - rayOrigin.y()) / rayDir.y();
|
||||
entryT = max(entryT, min(tYa, tYb));
|
||||
exitT = min(exitT, max(tYa, tYb));
|
||||
}
|
||||
else if (rayOrigin.y() < yMin || rayOrigin.y() > yMax) {
|
||||
continue; // horizontal ray passing above or below the cylinder
|
||||
}
|
||||
|
||||
if (entryT > exitT) continue;
|
||||
|
||||
std::cout << "[RAYCAST_NPC] " << npc->npcId << " hit at t=" << entryT << std::endl;
|
||||
|
||||
if (entryT < closestDist) {
|
||||
closestDist = entryT;
|
||||
closestNpc = npc.get();
|
||||
}
|
||||
}
|
||||
|
||||
if (closestNpc) {
|
||||
std::cout << "[RAYCAST_NPC] HIT: " << closestNpc->npcId << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cout << "[RAYCAST_NPC] No NPC hit" << std::endl;
|
||||
}
|
||||
|
||||
return closestNpc;
|
||||
}
|
||||
|
||||
@@ -611,6 +655,26 @@ namespace ZL
|
||||
targetInteractiveObject = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if player reached target NPC for interaction.
|
||||
if (targetInteractNpc && targetInteractNpcIndex >= 0 && player) {
|
||||
float distToNpc = (player->position - targetInteractNpc->position).norm();
|
||||
if (distToNpc <= NPC_TALK_DISTANCE) {
|
||||
std::cout << "[NPC] Player reached NPC index " << targetInteractNpcIndex
|
||||
<< " (distance " << distToNpc << "); firing on_npc_interact" << std::endl;
|
||||
// Stop the player at the talk distance and have the NPC turn to face them.
|
||||
player->setTarget(player->position);
|
||||
targetInteractNpc->faceTarget = player.get();
|
||||
try {
|
||||
scriptEngine.callNpcInteractCallback(targetInteractNpcIndex);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "[NPC] callback error: " << e.what() << std::endl;
|
||||
}
|
||||
targetInteractNpc = nullptr;
|
||||
targetInteractNpcIndex = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Location::handleDown(int64_t fingerId, int eventX, int eventY, int mx, int my)
|
||||
@@ -653,6 +717,8 @@ namespace ZL
|
||||
<< player->position.y() << ", " << player->position.z() << ")" << std::endl;
|
||||
|
||||
targetInteractiveObject = clickedObject;
|
||||
targetInteractNpc = nullptr;
|
||||
targetInteractNpcIndex = -1;
|
||||
player->setTarget(clickedObject->position);
|
||||
player->attackTarget = nullptr;
|
||||
std::cout << "[CLICK] Player moving to object..." << std::endl;
|
||||
@@ -671,22 +737,40 @@ namespace ZL
|
||||
}
|
||||
}
|
||||
if (npcIndex != -1) {
|
||||
if (distance <= clickedNpc->interactionRadius) {
|
||||
std::cout << "[CLICK] *** SUCCESS: Clicked on NPC index: " << npcIndex << " ***" << std::endl;
|
||||
scriptEngine.callNpcInteractCallback(npcIndex);
|
||||
targetInteractiveObject = nullptr;
|
||||
|
||||
if (clickedNpc->canAttack) {
|
||||
// Hostile NPC: combat logic walks the player in via attackTarget;
|
||||
// don't queue a Lua interaction during a fight.
|
||||
player->attackTarget = clickedNpc;
|
||||
targetInteractNpc = nullptr;
|
||||
targetInteractNpcIndex = -1;
|
||||
|
||||
if (distance <= clickedNpc->interactionRadius) {
|
||||
std::cout << "[CLICK] Hostile NPC " << npcIndex << " in range; firing on_npc_interact" << std::endl;
|
||||
scriptEngine.callNpcInteractCallback(npcIndex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::cout << "[CLICK] Too far from NPC (distance " << distance
|
||||
<< " > " << clickedNpc->interactionRadius << ")" << std::endl;
|
||||
}
|
||||
|
||||
if (clickedNpc->canAttack)
|
||||
{
|
||||
player->attackTarget = clickedNpc;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Peaceful NPC: walk to them, fire on_npc_interact when within talk distance.
|
||||
player->attackTarget = nullptr;
|
||||
|
||||
if (distance <= NPC_TALK_DISTANCE) {
|
||||
// Already in talk range — fire immediately, stop, and face the player.
|
||||
std::cout << "[CLICK] *** SUCCESS: Clicked on NPC index: " << npcIndex << " (in range) ***" << std::endl;
|
||||
player->setTarget(player->position);
|
||||
clickedNpc->faceTarget = player.get();
|
||||
scriptEngine.callNpcInteractCallback(npcIndex);
|
||||
targetInteractNpc = nullptr;
|
||||
targetInteractNpcIndex = -1;
|
||||
}
|
||||
else {
|
||||
std::cout << "[CLICK] NPC " << npcIndex << " out of talk range (distance " << distance
|
||||
<< " > " << NPC_TALK_DISTANCE << "); walking to NPC..." << std::endl;
|
||||
player->setTarget(clickedNpc->position);
|
||||
targetInteractNpc = clickedNpc;
|
||||
targetInteractNpcIndex = npcIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -699,6 +783,8 @@ namespace ZL
|
||||
|
||||
player->setTarget(Eigen::Vector3f(hit.x(), 0.f, hit.z()));
|
||||
player->attackTarget = nullptr;
|
||||
targetInteractNpc = nullptr;
|
||||
targetInteractNpcIndex = -1;
|
||||
}
|
||||
else {
|
||||
std::cout << "[CLICK] No valid target found" << std::endl;
|
||||
@@ -719,7 +805,7 @@ namespace ZL
|
||||
);
|
||||
}
|
||||
|
||||
if (rightMouseDown) {
|
||||
if (cameraDragging) {
|
||||
int dx = eventX - lastMouseX;
|
||||
int dy = eventY - lastMouseY;
|
||||
lastMouseX = eventX;
|
||||
|
||||
Reference in new issue
Block a user