Working on teleports

This commit is contained in:
Vladislav Khorev 2026-06-09 14:43:04 +03:00
parent b94f02bd77
commit 0ed7021e90
9 changed files with 60 additions and 3 deletions

View File

@ -20,6 +20,8 @@
"positionZ": -9.65, "positionZ": -9.65,
"radius": 1.5, "radius": 1.5,
"active": true, "active": true,
"automatic": true,
"automaticRadius": 1.4,
"destinationLocation": "uni_interior", "destinationLocation": "uni_interior",
"destinationPositionX": 1.05971, "destinationPositionX": 1.05971,
"destinationPositionY": 0.0, "destinationPositionY": 0.0,

View File

@ -7,6 +7,8 @@
"positionZ": -10.1046, "positionZ": -10.1046,
"radius": 1.8, "radius": 1.8,
"active": true, "active": true,
"automatic": true,
"automaticRadius": 1.7,
"destinationLocation": "uni_exterior", "destinationLocation": "uni_exterior",
"destinationPositionX": 8.9718, "destinationPositionX": 8.9718,
"destinationPositionY": 0.0, "destinationPositionY": 0.0,

View File

@ -359,6 +359,16 @@ namespace ZL
menuManager.tutorialShowTaxiHint(); menuManager.tutorialShowTaxiHint();
}; };
locations["location_dorm"]->tutorialInteractiveObjectsLocked = true;
menuManager.tutorialUnlockInteractiveObjectsFunc = [this]()
{
if (locations["location_dorm"])
{
locations["location_dorm"]->tutorialInteractiveObjectsLocked = false;
}
};
menuManager.callTaxiFunc = [this]() menuManager.callTaxiFunc = [this]()
{ {
if (locations["location_dorm"]) if (locations["location_dorm"])
@ -386,6 +396,7 @@ namespace ZL
if (currentLocation->player) { if (currentLocation->player) {
currentLocation->player->position = destPos; currentLocation->player->position = destPos;
currentLocation->player->setTarget(destPos); currentLocation->player->setTarget(destPos);
currentLocation->player->stopInPlace();
currentLocation->player->facingAngle = destRotY; currentLocation->player->facingAngle = destRotY;
currentLocation->player->targetFacingAngle = destRotY; currentLocation->player->targetFacingAngle = destRotY;
} }

View File

@ -213,6 +213,8 @@ namespace ZL
item.value("positionZ", 0.0f) item.value("positionZ", 0.0f)
); );
tz.radius = item.value("radius", 0.0f); tz.radius = item.value("radius", 0.0f);
tz.automatic = item.value("automatic", false);
tz.automaticRadius = item.value("automaticRadius", tz.radius);
tz.destinationLocation = item.value("destinationLocation", ""); tz.destinationLocation = item.value("destinationLocation", "");
tz.destinationPosition = Eigen::Vector3f( tz.destinationPosition = Eigen::Vector3f(
item.value("destinationPositionX", 0.0f), item.value("destinationPositionX", 0.0f),
@ -1406,7 +1408,7 @@ namespace ZL
for (auto& [idx, t] : npcBumpsPlayerCooldown) t -= deltaS; for (auto& [idx, t] : npcBumpsPlayerCooldown) t -= deltaS;
// Check if player reached target interactive object // Check if player reached target interactive object
if (targetInteractiveObject && player && !targetInteractiveObject->isAnimating) { if (!tutorialInteractiveObjectsLocked && targetInteractiveObject && player && !targetInteractiveObject->isAnimating) {
const Eigen::Vector3f& approachTarget = targetInteractiveObject->hasInteractionPosition const Eigen::Vector3f& approachTarget = targetInteractiveObject->hasInteractionPosition
? targetInteractiveObject->interactionPosition ? targetInteractiveObject->interactionPosition
: targetInteractiveObject->position; : targetInteractiveObject->position;
@ -1467,6 +1469,32 @@ namespace ZL
return; return;
} }
} }
if (player) {
for (auto& tz : teleportZones) {
if (!tz.active || !tz.automatic) continue;
const float autoR = tz.automaticRadius > 0.0f ? tz.automaticRadius : tz.radius;
const float dist = (player->position - tz.position).norm();
if (!tz.automaticInitialized) {
// If the player starts inside the zone (e.g. just arrived here via teleport),
// mark them as already inside so they must leave before it can fire.
tz.automaticPlayerInside = (dist <= autoR);
tz.automaticInitialized = true;
continue;
}
const bool isInside = (dist <= autoR);
if (isInside && !tz.automaticPlayerInside) {
tz.automaticPlayerInside = true;
std::cout << "[TELEPORT] Auto-teleport triggered by zone '" << tz.id << "'" << std::endl;
if (onTeleport) onTeleport(tz.destinationLocation, tz.destinationPosition, tz.destinationRotationY);
return;
} else if (!isInside) {
tz.automaticPlayerInside = false;
}
}
}
} }
void Location::handleDown(int64_t fingerId, int eventX, int eventY, int mx, int my) void Location::handleDown(int64_t fingerId, int eventX, int eventY, int mx, int my)

View File

@ -102,6 +102,8 @@ namespace ZL
bool isNight = false; bool isNight = false;
bool isDawn = false; bool isDawn = false;
bool tutorialInteractiveObjectsLocked = false;
// Called when the player successfully taps the ground and a floor walk target is set. // Called when the player successfully taps the ground and a floor walk target is set.
// Used by the tutorial system to detect the "tap to walk" gesture. // Used by the tutorial system to detect the "tap to walk" gesture.
std::function<void()> onPlayerFloorWalk; std::function<void()> onPlayerFloorWalk;

View File

@ -727,6 +727,11 @@ namespace ZL {
case TutorialStep::Step3: case TutorialStep::Step3:
tutorialStep = TutorialStep::Step4; tutorialStep = TutorialStep::Step4;
nextRoot = hudStep4Root; nextRoot = hudStep4Root;
if (tutorialUnlockInteractiveObjectsFunc)
{
tutorialUnlockInteractiveObjectsFunc();
tutorialUnlockInteractiveObjectsFunc = nullptr;
}
break; break;
default: default:
return; // Step4/Step5 transitions are driven by onItemPickedUp return; // Step4/Step5 transitions are driven by onItemPickedUp

View File

@ -81,6 +81,7 @@ namespace ZL {
std::function<void(int)> chatOpenCallback; std::function<void(int)> chatOpenCallback;
std::function<void()> skipCutsceneFunc; std::function<void()> skipCutsceneFunc;
std::function<void()> callTaxiFunc; std::function<void()> callTaxiFunc;
std::function<void()> tutorialUnlockInteractiveObjectsFunc;
// Called when a chat message bubble should be shown (text + direction) // Called when a chat message bubble should be shown (text + direction)
void onChatBubbleReady(const std::string& text, bool incoming); void onChatBubbleReady(const std::string& text, bool incoming);

View File

@ -14,8 +14,8 @@ void TeleportZone::initSparks(std::shared_ptr<Texture> activeTex, std::shared_pt
sparks->setEmissionPoints(emitPoints); sparks->setEmissionPoints(emitPoints);
sparks->setTexture(active ? activeTex : inactiveTex); sparks->setTexture(active ? activeTex : inactiveTex);
sparks->setEmissionRate(50.0f); sparks->setEmissionRate(50.0f);
sparks->setMaxParticles(80); sparks->setMaxParticles(160);
sparks->setParticleSize(0.05f); sparks->setParticleSize(0.10f);
sparks->setBiasX(0.0f); sparks->setBiasX(0.0f);
sparks->setEmissionDirection(Vector3f{ 0.0f, 1.0f, 0.0f }); sparks->setEmissionDirection(Vector3f{ 0.0f, 1.0f, 0.0f });
sparks->setEmissionRadius(radius); sparks->setEmissionRadius(radius);

View File

@ -20,6 +20,12 @@ struct TeleportZone {
std::shared_ptr<Texture> activeTexture; std::shared_ptr<Texture> activeTexture;
std::shared_ptr<Texture> inactiveTexture; std::shared_ptr<Texture> inactiveTexture;
bool automatic = false;
float automaticRadius = 0.0f;
// Runtime tracking — prevents re-firing immediately on arrival at destination.
bool automaticPlayerInside = false;
bool automaticInitialized = false;
void initSparks(std::shared_ptr<Texture> activeTex, std::shared_ptr<Texture> inactiveTex); void initSparks(std::shared_ptr<Texture> activeTex, std::shared_ptr<Texture> inactiveTex);
void setActive(bool isActive); void setActive(bool isActive);
void update(float deltaMs); void update(float deltaMs);