minor fixing, adapting for mobile web

This commit is contained in:
Vladislav Khorev committed 2026-04-25 14:39:27 +03:00
1 parent 44cc0fba67
commit eac914e073
6 files changed
+393 -129

No files matched your search

+108 -22
View File
@@ -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;