Working on teleports
This commit is contained in:
1 parent
7e94009217
commit
8214342248
15 files changed
+298
-131
No files matched your search
+98
-64
@@ -12,6 +12,7 @@
|
||||
#include <cfloat>
|
||||
#include "GameConstants.h"
|
||||
#include "Character.h"
|
||||
#include "external/nlohmann/json.hpp"
|
||||
|
||||
|
||||
namespace ZL
|
||||
@@ -133,38 +134,7 @@ namespace ZL
|
||||
}
|
||||
}
|
||||
|
||||
auto sparkTexture2 = renderer.textureManager.LoadFromPng("resources/w/star.png", CONST_ZIP_FILE);
|
||||
|
||||
// Teleport zone visuals: an upward fountain of sparks parked at the
|
||||
// teleport position, so the player can spot the zone from a distance.
|
||||
teleportPosition = params.teleportPosition;
|
||||
teleportRadius = params.teleportRadius;
|
||||
if (teleportRadius > 0.0f) {
|
||||
teleportSparks = std::make_unique<SparkEmitter>();
|
||||
std::vector<Vector3f> teleportEmitPoints;
|
||||
teleportEmitPoints.push_back(Vector3f{ teleportPosition.x(), teleportPosition.y(), teleportPosition.z() });
|
||||
teleportSparks->setEmissionPoints(teleportEmitPoints);
|
||||
teleportSparks->setTexture(sparkTexture2);
|
||||
teleportSparks->setEmissionRate(50.0f);
|
||||
teleportSparks->setMaxParticles(80);
|
||||
teleportSparks->setParticleSize(0.05f);
|
||||
teleportSparks->setBiasX(0.0f);
|
||||
teleportSparks->setEmissionDirection(Vector3f{ 0.0f, 1.0f, 0.0f });
|
||||
teleportSparks->setEmissionRadius(teleportRadius);
|
||||
teleportSparks->setSpeedRange(0.5f, 1.0f);
|
||||
teleportSparks->setZSpeedRange(0.5f, 1.5f);
|
||||
teleportSparks->setScaleRange(0.5f, 1.0f);
|
||||
teleportSparks->setLifeTimeRange(1500.0f, 2500.0f);
|
||||
teleportSparks->setUseWorldSpace(true);
|
||||
teleportSparks->markConfigured();
|
||||
|
||||
|
||||
// If the player happens to spawn already inside the zone, treat them
|
||||
// as in-zone so they don't immediately teleport on the first update.
|
||||
if (player && (player->position - teleportPosition).norm() <= teleportRadius) {
|
||||
playerInTeleportZone = true;
|
||||
}
|
||||
}
|
||||
loadTeleportZones(params.teleportsJsonPath, CONST_ZIP_FILE);
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
// Create shadow map (2048x2048, ortho size 40, near 0.1, far 100)
|
||||
@@ -198,6 +168,65 @@ namespace ZL
|
||||
|
||||
}
|
||||
|
||||
void Location::loadTeleportZones(const std::string& jsonPath, const char* zipFile)
|
||||
{
|
||||
if (jsonPath.empty()) return;
|
||||
|
||||
std::string content;
|
||||
try {
|
||||
if (!zipFile || zipFile[0] == '\0') {
|
||||
content = readTextFile(jsonPath);
|
||||
} else {
|
||||
auto buf = readFileFromZIP(jsonPath, zipFile);
|
||||
if (buf.empty()) {
|
||||
std::cerr << "[TELEPORT] Failed to read " << jsonPath << " from zip" << std::endl;
|
||||
return;
|
||||
}
|
||||
content.assign(buf.begin(), buf.end());
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "[TELEPORT] Failed to open " << jsonPath << ": " << e.what() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
using json = nlohmann::json;
|
||||
json j;
|
||||
try {
|
||||
j = json::parse(content);
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "[TELEPORT] JSON parse error in " << jsonPath << ": " << e.what() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!j.contains("teleports") || !j["teleports"].is_array()) return;
|
||||
|
||||
auto activeTex = renderer.textureManager.LoadFromPng("resources/w/star.png", zipFile ? zipFile : "");
|
||||
auto inactiveTex = renderer.textureManager.LoadFromPng("resources/w/star_red.png", zipFile ? zipFile : "");
|
||||
|
||||
for (const auto& item : j["teleports"]) {
|
||||
TeleportZone tz;
|
||||
tz.id = item.value("id", "");
|
||||
tz.active = item.value("active", false);
|
||||
tz.position = Eigen::Vector3f(
|
||||
item.value("positionX", 0.0f),
|
||||
item.value("positionY", 0.0f),
|
||||
item.value("positionZ", 0.0f)
|
||||
);
|
||||
tz.radius = item.value("radius", 0.0f);
|
||||
tz.destinationLocation = item.value("destinationLocation", "");
|
||||
tz.destinationPosition = Eigen::Vector3f(
|
||||
item.value("destinationPositionX", 0.0f),
|
||||
item.value("destinationPositionY", 0.0f),
|
||||
item.value("destinationPositionZ", 0.0f)
|
||||
);
|
||||
tz.destinationRotationY = item.value("destinationRotationY", 0.0f);
|
||||
tz.initSparks(activeTex, inactiveTex);
|
||||
teleportZones.push_back(std::move(tz));
|
||||
}
|
||||
|
||||
std::cout << "[TELEPORT] Loaded " << teleportZones.size() << " teleport(s) from " << jsonPath << std::endl;
|
||||
}
|
||||
|
||||
void Location::setupNavigation(const std::string& navigationJsonPath)
|
||||
{
|
||||
// Static navigation blockers are defined in the navigation JSON as polygons.
|
||||
@@ -447,14 +476,12 @@ namespace ZL
|
||||
const Eigen::Matrix4f currentView = renderer.GetCurrentModelViewMatrix();
|
||||
if (player) player->prepareHitSparksForDraw(currentView);
|
||||
for (auto& npc : npcs) npc->prepareHitSparksForDraw(currentView);
|
||||
if (teleportSparks) teleportSparks->prepareForDraw(currentView);
|
||||
for (auto& tz : teleportZones) tz.prepareForDraw(currentView);
|
||||
|
||||
if (player) player->draw(renderer);
|
||||
for (auto& npc : npcs) npc->draw(renderer);
|
||||
|
||||
if (teleportSparks) {
|
||||
teleportSparks->draw(renderer, Environment::zoom, Environment::width, Environment::height);
|
||||
}
|
||||
for (auto& tz : teleportZones) tz.draw(renderer, Environment::zoom, Environment::width, Environment::height);
|
||||
|
||||
#ifdef SHOW_PATH
|
||||
drawDebugNavigation();
|
||||
@@ -641,16 +668,14 @@ namespace ZL
|
||||
|
||||
if (player) player->prepareHitSparksForDraw(cameraViewMatrix);
|
||||
for (auto& npc : npcs) npc->prepareHitSparksForDraw(cameraViewMatrix);
|
||||
if (teleportSparks) teleportSparks->prepareForDraw(cameraViewMatrix);
|
||||
for (auto& tz : teleportZones) tz.prepareForDraw(cameraViewMatrix);
|
||||
|
||||
if (player) player->drawWithShadow(renderer, lightFromCamera, shadowMap->getDepthTexture(), lightDirCamera);
|
||||
CheckGlError(__FILE__, __LINE__);
|
||||
|
||||
for (auto& npc : npcs) npc->drawWithShadow(renderer, lightFromCamera, shadowMap->getDepthTexture(), lightDirCamera);
|
||||
|
||||
if (teleportSparks) {
|
||||
teleportSparks->draw(renderer, Environment::zoom, Environment::width, Environment::height);
|
||||
}
|
||||
for (auto& tz : teleportZones) tz.draw(renderer, Environment::zoom, Environment::width, Environment::height);
|
||||
#endif
|
||||
CheckGlError(__FILE__, __LINE__);
|
||||
|
||||
@@ -889,26 +914,15 @@ namespace ZL
|
||||
}
|
||||
}
|
||||
|
||||
// Drive teleport spark animation regardless of whether the zone is armed.
|
||||
if (teleportSparks) {
|
||||
teleportSparks->update(static_cast<float>(delta));
|
||||
}
|
||||
for (auto& tz : teleportZones) tz.update(static_cast<float>(delta));
|
||||
|
||||
// Teleport zone: rising-edge trigger only. The destination location's
|
||||
// callback is responsible for placing the player back inside its own
|
||||
// zone and setting playerInTeleportZone = true so we don't bounce.
|
||||
if (player && onTeleport && teleportRadius > 0.0f) {
|
||||
float distToZone = (player->position - teleportPosition).norm();
|
||||
if (distToZone <= teleportRadius) {
|
||||
if (!playerInTeleportZone) {
|
||||
playerInTeleportZone = true;
|
||||
std::cout << "[TELEPORT] Player entered teleport zone" << std::endl;
|
||||
onTeleport();
|
||||
return; // active location may have changed
|
||||
}
|
||||
}
|
||||
else {
|
||||
playerInTeleportZone = false;
|
||||
if (targetTeleportZone && player) {
|
||||
float dist = (player->position - targetTeleportZone->position).norm();
|
||||
if (dist <= targetTeleportZone->radius) {
|
||||
std::cout << "[TELEPORT] Player reached teleport zone '" << targetTeleportZone->id << "'" << std::endl;
|
||||
if (onTeleport) onTeleport(targetTeleportZone->destinationLocation, targetTeleportZone->destinationPosition, targetTeleportZone->destinationRotationY);
|
||||
targetTeleportZone = nullptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -955,6 +969,7 @@ namespace ZL
|
||||
targetInteractiveObject = clickedObject;
|
||||
targetInteractNpc = nullptr;
|
||||
targetInteractNpcIndex = -1;
|
||||
targetTeleportZone = nullptr;
|
||||
player->setTarget(clickedObject->position);
|
||||
player->attackTarget = nullptr;
|
||||
std::cout << "[CLICK] Player moving to object..." << std::endl;
|
||||
@@ -974,6 +989,7 @@ namespace ZL
|
||||
}
|
||||
if (npcIndex != -1) {
|
||||
targetInteractiveObject = nullptr;
|
||||
targetTeleportZone = nullptr;
|
||||
|
||||
if (clickedNpc->canAttack) {
|
||||
// Hostile NPC: combat logic walks the player in via attackTarget;
|
||||
@@ -1012,15 +1028,33 @@ namespace ZL
|
||||
|
||||
}
|
||||
else if (rayDir.y() < -0.001f && player) {
|
||||
// Otherwise, unproject click to ground plane for Viola's walk target
|
||||
// Unproject click to ground plane for walk target / teleport detection
|
||||
float t = -camPos.y() / rayDir.y();
|
||||
Eigen::Vector3f hit = camPos + rayDir * t;
|
||||
std::cout << "[CLICK] Clicked on ground at: (" << hit.x() << ", " << hit.z() << ")" << std::endl;
|
||||
|
||||
player->setTarget(Eigen::Vector3f(hit.x(), 0.f, hit.z()));
|
||||
player->attackTarget = nullptr;
|
||||
targetInteractNpc = nullptr;
|
||||
targetInteractNpcIndex = -1;
|
||||
TeleportZone* clickedTeleport = nullptr;
|
||||
for (auto& tz : teleportZones) {
|
||||
if (!tz.active) continue;
|
||||
Eigen::Vector2f d(hit.x() - tz.position.x(), hit.z() - tz.position.z());
|
||||
if (d.norm() <= tz.radius) { clickedTeleport = &tz; break; }
|
||||
}
|
||||
|
||||
if (clickedTeleport) {
|
||||
std::cout << "[CLICK] Clicked teleport zone '" << clickedTeleport->id << "'" << std::endl;
|
||||
targetTeleportZone = clickedTeleport;
|
||||
targetInteractiveObject = nullptr;
|
||||
targetInteractNpc = nullptr;
|
||||
targetInteractNpcIndex = -1;
|
||||
player->setTarget(clickedTeleport->position);
|
||||
player->attackTarget = nullptr;
|
||||
} else {
|
||||
player->setTarget(Eigen::Vector3f(hit.x(), 0.f, hit.z()));
|
||||
player->attackTarget = nullptr;
|
||||
targetInteractNpc = nullptr;
|
||||
targetInteractNpcIndex = -1;
|
||||
targetTeleportZone = nullptr;
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::cout << "[CLICK] No valid target found" << std::endl;
|
||||
|
||||
Reference in new issue
Block a user