diff --git a/resources/config2/gameobjects.json b/resources/config2/gameobjects.json index 6b5b95e..d45a8ac 100644 --- a/resources/config2/gameobjects.json +++ b/resources/config2/gameobjects.json @@ -38,27 +38,6 @@ "positionZ": -7.9, "scale": 3.0, "interactive": false - }, - { - "name": "medik", - "texturePath": "resources/w/red.png", - "meshPath": "resources/w/firebox.txt", - "rotationX": 0.0, - "rotationY": -1.5707963267948966, - "rotationZ": 0.0, - "positionX": 0.0, - "positionY": 0.0, - "positionZ": 4.0, - "scale": 1.0, - "interactive": true, - "item": { - "id": "health_potion", - "name": "Health Potion", - "description": "Restores 50 HP", - "icon": "resources/w/red.png", - "radius": 2.0 - }, - "activateFunction" : "on_health_pickup" } ] } \ No newline at end of file diff --git a/resources/config2/npcs2.json b/resources/config2/npcs2.json index 2ba775f..dafe518 100644 --- a/resources/config2/npcs2.json +++ b/resources/config2/npcs2.json @@ -19,6 +19,7 @@ "positionX": 2.95, "positionY": 0.0, "positionZ": 16.65, + "facingAngle" : 3.141592, "walkSpeed": 1.5, "rotationSpeed": 8.0, "modelScale": 0.0001, @@ -50,6 +51,7 @@ "positionX": 19.5, "positionY": 0.0, "positionZ": 32.0, + "facingAngle" : 3.141592, "walkSpeed": 1.5, "rotationSpeed": 8.0, "modelScale": 0.0001, @@ -76,6 +78,7 @@ "positionX": -1.94774, "positionY": 0.0, "positionZ": 16.2712, + "facingAngle" : 3.141592, "walkSpeed": 1.5, "rotationSpeed": 8.0, "modelScale": 0.001, diff --git a/resources/w/gg/avatar_unknown.png b/resources/w/gg/avatar_unknown.png new file mode 100644 index 0000000..b349f03 --- /dev/null +++ b/resources/w/gg/avatar_unknown.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2cc6f23e3f2c56cbc8a48e57fefb1494bb93b5f1679e680b85d1979d055b75a0 +size 9048 diff --git a/src/Game.cpp b/src/Game.cpp index 22cd7d2..36bf80a 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -169,6 +169,8 @@ namespace ZL params1.scriptPath = "resources/start.lua"; params1.playerPosition = Eigen::Vector3f::Zero(); params1.ghostPosition = Eigen::Vector3f(0.f, 0.f, -20.f); + params1.teleportPosition = Eigen::Vector3f(-2.03001f, 0.f, -4.95618f); + params1.teleportRadius = 1.5f; location1 = std::make_shared(renderer, inventory); location1->setup(params1); @@ -184,11 +186,35 @@ namespace ZL params2.npcsJsonPath = "resources/config2/npcs2.json"; params2.ghostPosition = Eigen::Vector3f(700.f, 0.f, 700.f); - + params2.teleportPosition = Eigen::Vector3f(-6.45, 0.0, 7.82); + params2.teleportRadius = 1.5f; location2 = std::make_shared(renderer, inventory); location2->setup(params2); - currentLocation = location1; + // Teleport callbacks: stepping into a location's zone hands the player + // to the other location at *its* teleport position. We pre-arm the + // destination's playerInTeleportZone so the player must walk out and + // back in before they can teleport again. + location1->onTeleport = [this]() { + std::cout << "[TELEPORT] location 1 -> location 2" << std::endl; + currentLocation = location2; + if (currentLocation->player) { + currentLocation->player->position = currentLocation->teleportPosition; + currentLocation->player->setTarget(currentLocation->teleportPosition); + } + currentLocation->playerInTeleportZone = true; + }; + location2->onTeleport = [this]() { + std::cout << "[TELEPORT] location 2 -> location 1" << std::endl; + currentLocation = location1; + if (currentLocation->player) { + currentLocation->player->position = currentLocation->teleportPosition; + currentLocation->player->setTarget(currentLocation->teleportPosition); + } + currentLocation->playerInTeleportZone = true; + }; + + currentLocation = location2; std::cout << "Load resurces step 5" << std::endl; diff --git a/src/Location.cpp b/src/Location.cpp index 1ca4ee5..43b9c44 100644 --- a/src/Location.cpp +++ b/src/Location.cpp @@ -141,6 +141,35 @@ namespace ZL npcs.push_back(std::move(npc02)); + // 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(); + std::vector teleportEmitPoints; + teleportEmitPoints.push_back(Vector3f{ teleportPosition.x(), teleportPosition.y(), teleportPosition.z() }); + teleportSparks->setEmissionPoints(teleportEmitPoints); + teleportSparks->setTexture(sparkTexture); + teleportSparks->setEmissionRate(50.0f); + teleportSparks->setMaxParticles(80); + teleportSparks->setParticleSize(0.15f); + teleportSparks->setBiasX(0.0f); + teleportSparks->setEmissionDirection(Vector3f{ 0.0f, 1.0f, 0.0f }); + 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; + } + } + // Create shadow map (2048x2048, ortho size 40, near 0.1, far 100) shadowMap = std::make_unique(2048, 40.0f, 0.1f, 100.0f); shadowMap->setLightDirection(Eigen::Vector3f(-0.5f, -1.0f, -0.3f)); @@ -403,10 +432,15 @@ 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); if (player) player->draw(renderer); for (auto& npc : npcs) npc->draw(renderer); + if (teleportSparks) { + teleportSparks->draw(renderer, Environment::zoom, Environment::width, Environment::height); + } + #ifdef SHOW_PATH drawDebugNavigation(); #endif @@ -592,11 +626,16 @@ namespace ZL if (player) player->prepareHitSparksForDraw(cameraViewMatrix); for (auto& npc : npcs) npc->prepareHitSparksForDraw(cameraViewMatrix); + if (teleportSparks) teleportSparks->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); + } #endif CheckGlError(__FILE__, __LINE__); @@ -681,6 +720,29 @@ namespace ZL targetInteractNpcIndex = -1; } } + + // Drive teleport spark animation regardless of whether the zone is armed. + if (teleportSparks) { + teleportSparks->update(static_cast(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; + } + } } void Location::handleDown(int64_t fingerId, int eventX, int eventY, int mx, int my) @@ -742,7 +804,7 @@ namespace ZL break; } } - if (npcIndex != -1) { + if (npcIndex != -1 && clickedNpc->hp > 0) { targetInteractiveObject = nullptr; if (clickedNpc->canAttack) { diff --git a/src/Location.h b/src/Location.h index ff5341f..ee7a7c0 100644 --- a/src/Location.h +++ b/src/Location.h @@ -11,6 +11,8 @@ #include "ScriptEngine.h" #include "dialogue/DialogueSystem.h" +#include "SparkEmitter.h" +#include namespace ZL { @@ -25,6 +27,8 @@ namespace ZL std::string scriptPath; Eigen::Vector3f playerPosition = Eigen::Vector3f::Zero(); Eigen::Vector3f ghostPosition = Eigen::Vector3f::Zero(); + Eigen::Vector3f teleportPosition = Eigen::Vector3f::Zero(); + float teleportRadius = 0.0f; }; class Location @@ -64,6 +68,15 @@ namespace ZL ScriptEngine scriptEngine; Dialogue::DialogueSystem dialogueSystem; + // Teleport zone: when the player crosses into the radius of teleportPosition, + // onTeleport() is invoked once. Re-triggering requires leaving the zone first + // (rising-edge detection via playerInTeleportZone). + Eigen::Vector3f teleportPosition = Eigen::Vector3f::Zero(); + float teleportRadius = 0.0f; + bool playerInTeleportZone = false; + std::function onTeleport; + std::unique_ptr teleportSparks; + #ifdef SHOW_PATH std::vector debugNavMeshes; void buildDebugNavMeshes(); diff --git a/src/dialogue/DialogueOverlay.cpp b/src/dialogue/DialogueOverlay.cpp index a74a1c8..fbc6135 100644 --- a/src/dialogue/DialogueOverlay.cpp +++ b/src/dialogue/DialogueOverlay.cpp @@ -112,7 +112,7 @@ void DialogueOverlay::drawDialogue(Renderer& renderer, const PresentationModel& // const float H = Environment::projectionHeight; const UiRect portraitRect{ 24.0f, 24.0f, 182.0f, 182.0f }; - const UiRect textboxRect{ 220.0f, 24.0f, max(200.0f, W - 244.0f), 182.0f }; + const UiRect textboxRect{ 220.0f, 24.0f, max(300.0f, W - 244.0f), 182.0f }; lastDialogueAdvanceRect = { portraitRect.x, portraitRect.y, textboxRect.x + textboxRect.w - portraitRect.x, textboxRect.h }; lastCutsceneAdvanceRect = {}; cutsceneAdvanceEnabled = false; diff --git a/src/items/GameObjectLoader.cpp b/src/items/GameObjectLoader.cpp index f766a9f..2bf4b4a 100644 --- a/src/items/GameObjectLoader.cpp +++ b/src/items/GameObjectLoader.cpp @@ -368,6 +368,8 @@ namespace ZL { data.modelCorrectionRotY = item.value("modelCorrectionRotY", 0.0f) * static_cast(M_PI) / 180.0f; data.modelCorrectionRotZ = item.value("modelCorrectionRotZ", 0.0f) * static_cast(M_PI) / 180.0f; + data.facingAngle = item.value("facingAngle", 0.0f); + // Load gift data if available if (item.contains("gift") && item["gift"].is_object()) { const auto& giftData = item["gift"]; @@ -471,6 +473,7 @@ namespace ZL { npc->rotationSpeed = npcData.rotationSpeed; npc->modelScale = npcData.modelScale; npc->position = Eigen::Vector3f(npcData.positionX, npcData.positionY, npcData.positionZ); + npc->facingAngle = npcData.facingAngle; npc->interactionRadius = npcData.interactionRadius; // Set NPC metadata diff --git a/src/items/GameObjectLoader.h b/src/items/GameObjectLoader.h index 02aae87..6b8dea5 100644 --- a/src/items/GameObjectLoader.h +++ b/src/items/GameObjectLoader.h @@ -60,6 +60,7 @@ namespace ZL { float modelCorrectionRotX = 0.0f; float modelCorrectionRotY = 0.0f; float modelCorrectionRotZ = 0.0f; + float facingAngle = 0.0f; GiftData gift; float interactionRadius = 0.0f; };