From 60a1422232390591b10f047f716e3a9688d1b93c Mon Sep 17 00:00:00 2001 From: Vladislav Khorev Date: Sun, 19 Apr 2026 09:28:28 +0300 Subject: [PATCH] Adding quests --- resources/dialogue/sample_dialogues.json | 161 +++++++++++++++++++++++ src/Location.cpp | 67 +++++++++- src/Location.h | 12 +- 3 files changed, 232 insertions(+), 8 deletions(-) diff --git a/resources/dialogue/sample_dialogues.json b/resources/dialogue/sample_dialogues.json index 1c36bf8..a748054 100644 --- a/resources/dialogue/sample_dialogues.json +++ b/resources/dialogue/sample_dialogues.json @@ -196,6 +196,167 @@ } ] }, + { + "id": "driving_dialogue_gas_out", + "start": "line_1", + "nodes": [ + { + "id": "line_1", + "type": "Line", + "speaker": "Игрок", + "portrait": "resources/w/gg/gg2_s_podsvetkoy5.png", + "text": "Все, приехали! Бензин закончился!", + "next": "end_1" + }, + { + "id": "end_1", + "type": "End" + } + ] + }, + { + "id": "driving_dialogue_distance7000", + "start": "line_1", + "nodes": [ + { + "id": "line_1", + "type": "Line", + "speaker": "Игрок", + "portrait": "resources/w/gg/gg2_s_podsvetkoy5.png", + "text": "До Таласской области осталось ехать примерно 7 километров.", + "next": "end_1" + }, + { + "id": "end_1", + "type": "End" + } + ] + }, + { + "id": "driving_dialogue_distance5000", + "start": "line_1", + "nodes": [ + { + "id": "line_1", + "type": "Line", + "speaker": "Игрок", + "portrait": "resources/w/gg/gg2_s_podsvetkoy5.png", + "text": "До Таласской области осталось ехать примерно 5 километров.", + "next": "end_1" + }, + { + "id": "end_1", + "type": "End" + } + ] + }, + { + "id": "driving_dialogue_distance2000", + "start": "line_1", + "nodes": [ + { + "id": "line_1", + "type": "Line", + "speaker": "Игрок", + "portrait": "resources/w/gg/gg2_s_podsvetkoy5.png", + "text": "До Таласской области осталось ехать примерно 2 километра.", + "next": "end_1" + }, + { + "id": "end_1", + "type": "End" + } + ] + }, + { + "id": "driving_dialogue_distance0", + "start": "line_1", + "nodes": [ + { + "id": "line_1", + "type": "Line", + "speaker": "Игрок", + "portrait": "resources/w/gg/gg2_s_podsvetkoy5.png", + "text": "Все, мы в Таласской области.", + "next": "line_2" + }, + { + "id": "line_2", + "type": "Line", + "speaker": "Игрок", + "portrait": "resources/w/gg/gg2_s_podsvetkoy5.png", + "text": "Здесь должен быть сигнал, дай мне телефон.", + "next": "line_3" + }, + { + "id": "line_3", + "type": "Line", + "speaker": "Игрок", + "portrait": "resources/w/gg/gg2_s_podsvetkoy5.png", + "text": "Я позвоню дяде.", + "next": "line_4" + }, + { + "id": "line_4", + "type": "Line", + "speaker": "Игрок", + "portrait": "resources/w/gg/gg2_s_podsvetkoy5.png", + "text": "Держи.", + "next": "line_5" + }, + { + "id": "line_5", + "type": "Line", + "speaker": "Игрок", + "portrait": "resources/w/gg/gg2_s_podsvetkoy5.png", + "text": "Ало, Акыл байке! Это Алтынай.", + "next": "line_6" + },{ + "id": "line_6", + "type": "Line", + "speaker": "Игрок", + "portrait": "resources/w/gg/gg2_s_podsvetkoy5.png", + "text": "У меня проблемы, меня преследуют бандиты.", + "next": "line_7" + }, + { + "id": "line_7", + "type": "Line", + "speaker": "Игрок", + "portrait": "resources/w/gg/gg2_s_podsvetkoy5.png", + "text": "Я сейчас еду с другом в сторону Таласа.", + "next": "line_8" + }, + { + "id": "line_8", + "type": "Line", + "speaker": "Игрок", + "portrait": "resources/w/gg/gg2_s_podsvetkoy5.png", + "text": "Пришли пожалуйста патруль мне на встречу.", + "next": "line_9" + }, + { + "id": "line_9", + "type": "Line", + "speaker": "Игрок", + "portrait": "resources/w/gg/gg2_s_podsvetkoy5.png", + "text": "Белый седан, номер 256.", + "next": "line_10" + }, + { + "id": "line_10", + "type": "Line", + "speaker": "Игрок", + "portrait": "resources/w/gg/gg2_s_podsvetkoy5.png", + "text": "Спасибо! Пока!", + "next": "end_1" + }, + { + "id": "end_1", + "type": "End" + } + ] + }, { "id": "driving_dialogue_gas1", "start": "line_1", diff --git a/src/Location.cpp b/src/Location.cpp index 7f15a05..85b5298 100644 --- a/src/Location.cpp +++ b/src/Location.cpp @@ -128,7 +128,7 @@ void Location::setup() police->rotationSpeed = 8.0f; police->modelScale = 0.01f; police->modelCorrectionRotation = Eigen::Quaternionf(Eigen::AngleAxisf(M_PI, Eigen::Vector3f::UnitY())); - police->position = Vector3f{ 10, -0, 10 }; + police->position = Vector3f{ 1000, -0, 10 }; police->setTarget(police->position); auto banditTexture0 = std::make_shared(CreateTextureDataFromPng("resources/e/bandit_packed0_diffuse.png", CONST_ZIP_FILE)); @@ -166,7 +166,7 @@ void Location::setup() std::cout << "[LOCATION] Setting up FOREST location (custom models only)" << std::endl; carPosition ={ 7, 0, -7 + 200 }; - npcCar.position = Vector3f(9, 0, -235); + npcCar.position = Vector3f(9, 0, -335) + Vector3f(1000,0,0); girlfriend->position = Vector3f{ 5, 0, 0.9 + 200 }; girlfriend->setTarget(girlfriend->position); @@ -1074,7 +1074,7 @@ void Location::setup() constexpr float offRoadFrictionMultiplier = 4.0f; const bool offRoad = std::abs(carPosition.x()) > roadHalfWidth; - if (!offRoad) { + if (!offRoad && !carOutOfGas) { if (keyForward) { carVelocity += carAcceleration * dt; } @@ -1164,7 +1164,22 @@ void Location::setup() } } - if (!policeFollow && player->position.z() < -240) + // Ran out of gas: far south without having heard the girlfriend's gas + // reminder. Latches the stall, then plays the out-of-gas line once. + if (!carOutOfGas && !dialoguePlayedGas1 && player->position.z() < -300.f) { + carOutOfGas = true; + } + if (carOutOfGas && !dialoguePlayedDrivingGasOut && !dialogueSystem.isActive()) { + if (dialogueSystem.startDialogue("driving_dialogue_gas_out")) { + dialoguePlayedDrivingGasOut = true; + } + } + + if (!policeFollow && + player->position.z() - npcCar.position.z() > -10.f + && player->position.z() < npcCar.position.z() + && npcCar.position.x()<900 + && policeEncounterStage != PoliceEncounterStage::Done) { policeFollow = true; npcCar.mode = NpcCar::Mode::FOLLOW_PLAYER; @@ -1208,6 +1223,33 @@ void Location::setup() updateNpcCar(delta); + // Track remaining driving distance toward -Z. Computed from this frame's + // real movement (captured before the tile-shift below), so the teleport + // never contributes to the count. + if (player) { + distanceRemaining += player->position.z() - playerPosBefore.z(); + } + if (!dialoguePlayedDistance7000 && distanceRemaining < 7000.f/5.f && !dialogueSystem.isActive()) { + if (dialogueSystem.startDialogue("driving_dialogue_distance7000")) { + dialoguePlayedDistance7000 = true; + } + } + if (!dialoguePlayedDistance5000 && distanceRemaining < 5000.f/5.f && !dialogueSystem.isActive()) { + if (dialogueSystem.startDialogue("driving_dialogue_distance5000")) { + dialoguePlayedDistance5000 = true; + } + } + if (!dialoguePlayedDistance2000 && distanceRemaining < 2000.f/5.f && !dialogueSystem.isActive()) { + if (dialogueSystem.startDialogue("driving_dialogue_distance2000")) { + dialoguePlayedDistance2000 = true; + } + } + if (!dialoguePlayedDistance0 && distanceRemaining < 0.f && !dialogueSystem.isActive()) { + if (dialogueSystem.startDialogue("driving_dialogue_distance0")) { + dialoguePlayedDistance0 = true; + } + } + { constexpr float tileSize = 100.0f; constexpr float teleportThreshold = tileSize * 3.5f; @@ -1232,7 +1274,7 @@ void Location::setup() player->setTarget(player->position); player->clearPath(); } - if (npcCar.mode == NpcCar::Mode::FOLLOW_PLAYER) { + if (npcCar.mode == NpcCar::Mode::FOLLOW_PLAYER || npcCar.mode == NpcCar::Mode::NONE_STAY) { npcCar.position += shift; } } @@ -1307,7 +1349,7 @@ void Location::setup() } if (!police) { policeFollow = false; - npcCar.mode = NpcCar::Mode::NONE; + npcCar.mode = NpcCar::Mode::NONE_STAY; policeEncounterStage = PoliceEncounterStage::Done; } } @@ -1320,11 +1362,22 @@ void Location::setup() const float dx = carPosition.x() - gasStationPos.x(); const float dz = carPosition.z() - gasStationPos.z(); if (std::hypot(dx, dz) > minDistance) { - if (dialogueSystem.startDialogue("dialogue_phone1")) { + if (dialogueSystem.startDialogue("dialogue_phone1", [this]() { + dialoguePhone1Finished = true; + })) { dialoguePlayedPhone1 = true; } } } + + // After the phone dialogue, once the player's car enters the ambush zone, + // spawn the NPC (police) car at its staged position. Fires once. + if (dialoguePhone1Finished && !npcCarSpawnedAfterPhone) { + if (carPosition.z() > -350.f && carPosition.z() < -340.f) { + npcCar.position = Eigen::Vector3f(9.f, 0.f, -325.f); + npcCarSpawnedAfterPhone = true; + } + } } void Location::updateNpcCar(int64_t deltaMs) diff --git a/src/Location.h b/src/Location.h index 3e2923d..d1efa55 100644 --- a/src/Location.h +++ b/src/Location.h @@ -15,7 +15,7 @@ namespace ZL struct NpcCar { - enum class Mode { FOLLOW_WAYPOINTS, FOLLOW_PLAYER, NONE }; + enum class Mode { FOLLOW_WAYPOINTS, FOLLOW_PLAYER, NONE, NONE_STAY }; Eigen::Vector3f position = Eigen::Vector3f::Zero(); float rotation = 0.f; @@ -113,6 +113,16 @@ namespace ZL bool dialoguePlayedGas1 = false; bool dialoguePlayedGirlfriend1 = false; bool dialoguePlayedPhone1 = false; + bool dialoguePhone1Finished = false; + bool npcCarSpawnedAfterPhone = false; + bool carOutOfGas = false; + bool dialoguePlayedDrivingGasOut = false; + + float distanceRemaining = 2000.f;//10000.0f; + bool dialoguePlayedDistance7000 = false; + bool dialoguePlayedDistance5000 = false; + bool dialoguePlayedDistance2000 = false; + bool dialoguePlayedDistance0 = false; bool policeFollow = false;