From ce8bc2d14203f04b15b0a82057ad4974c12e9848 Mon Sep 17 00:00:00 2001 From: Vladislav Khorev Date: Sun, 19 Apr 2026 22:03:56 +0300 Subject: [PATCH] Added bandit chase, change dialogs --- resources/bandit.png | 3 + resources/cashier.png | 3 + resources/dialogue/sample_dialogues.json | 372 +++++++++++++---------- resources/e/car_bandit001.png | 3 + resources/girlfriend.png | 3 + resources/police.png | 3 + src/Game.cpp | 5 +- src/Location.cpp | 279 ++++++++++++----- src/Location.h | 14 +- 9 files changed, 441 insertions(+), 244 deletions(-) create mode 100644 resources/bandit.png create mode 100644 resources/cashier.png create mode 100644 resources/e/car_bandit001.png create mode 100644 resources/girlfriend.png create mode 100644 resources/police.png diff --git a/resources/bandit.png b/resources/bandit.png new file mode 100644 index 0000000..bb0af40 --- /dev/null +++ b/resources/bandit.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c78d7129280b471d7dc98b5dea61b3967863e6d8d6660fbe21f61de036285541 +size 49953 diff --git a/resources/cashier.png b/resources/cashier.png new file mode 100644 index 0000000..e4400a8 --- /dev/null +++ b/resources/cashier.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:de5d8aa2c022c4d2bc4bedbda2287e685e02308c767f2749d6812e754aa5c5ac +size 43383 diff --git a/resources/dialogue/sample_dialogues.json b/resources/dialogue/sample_dialogues.json index 5f512a3..2d5fe1c 100644 --- a/resources/dialogue/sample_dialogues.json +++ b/resources/dialogue/sample_dialogues.json @@ -8,7 +8,7 @@ "id": "line_1", "type": "Line", "speaker": "Милиция", - "portrait": "resources/ghost_avatar.png", + "portrait": "resources/police.png", "text": "Белый седан 256 остановитесь немедленно!", "next": "end_1" }, @@ -18,6 +18,40 @@ } ] }, + { + "id": "dialogue_bandit1", + "start": "line_1", + "nodes": [ + { + "id": "line_1", + "type": "Line", + "speaker": "Бандит", + "portrait": "resources/bandit.png", + "text": "Думали, что сможете убежать от нас?", + "next": "line_2" + }, + { + "id": "line_2", + "type": "Line", + "speaker": "Бандит", + "portrait": "resources/bandit.png", + "text": "Никто никогда не убежит от Нурланбая!", + "next": "line_3" + }, + { + "id": "line_3", + "type": "Line", + "speaker": "Бандит", + "portrait": "resources/bandit.png", + "text": "Ваша песенка спета!", + "next": "end_1" + }, + { + "id": "end_1", + "type": "End" + } + ] + }, { "id": "dialogue_gas1", "start": "line_1", @@ -25,7 +59,7 @@ { "id": "line_1", "type": "Line", - "speaker": "Игрок", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "Первая колонка, полный бак пожалуйста.", "next": "line_2" @@ -34,14 +68,14 @@ "id": "line_2", "type": "Line", "speaker": "Кассир", - "portrait": "resources/ghost_avatar.png", + "portrait": "resources/cashier.png", "text": "[Смотрит подозрительно]", "next": "line_3" }, { "id": "line_3", "type": "Line", - "speaker": "Игрок", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "Что вы на меня так смотрите, со мной что-то не так?", "next": "line_4" @@ -50,7 +84,7 @@ "id": "line_4", "type": "Line", "speaker": "Кассир", - "portrait": "resources/ghost_avatar.png", + "portrait": "resources/cashier.png", "text": "Нет-нет, все хорошо.", "next": "line_5" }, @@ -58,14 +92,14 @@ "id": "line_5", "type": "Line", "speaker": "Кассир", - "portrait": "resources/ghost_avatar.png", + "portrait": "resources/cashier.png", "text": "Говорите, первая колонка, да?", "next": "line_6" }, { "id": "line_6", "type": "Line", - "speaker": "Игрок", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "Да.", "next": "line_7" @@ -74,7 +108,7 @@ "id": "line_7", "type": "Line", "speaker": "Кассир", - "portrait": "resources/ghost_avatar.png", + "portrait": "resources/cashier.png", "text": "Вот, пожалуйста. Приходите к нам еще!", "next": "end_1" }, @@ -92,14 +126,14 @@ "id": "line_2", "type": "Line", "speaker": "Кассир", - "portrait": "resources/ghost_avatar.png", + "portrait": "resources/cashier.png", "text": "[Смотрит подозрительно]", "next": "line_3" }, { "id": "line_3", "type": "Line", - "speaker": "Игрок", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "Что-то случилось?", "next": "line_4" @@ -108,7 +142,7 @@ "id": "line_4", "type": "Line", "speaker": "Кассир", - "portrait": "resources/ghost_avatar.png", + "portrait": "resources/cashier.png", "text": "Поставьте машину чуть ближе к колонке пожалуйста.", "next": "end_1" }, @@ -126,14 +160,14 @@ "id": "line_2", "type": "Line", "speaker": "Кассир", - "portrait": "resources/ghost_avatar.png", + "portrait": "resources/cashier.png", "text": "[Смотрит подозрительно]", "next": "line_3" }, { "id": "line_3", "type": "Line", - "speaker": "Игрок", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "Что-то случилось?", "next": "line_4" @@ -142,7 +176,7 @@ "id": "line_4", "type": "Line", "speaker": "Кассир", - "portrait": "resources/ghost_avatar.png", + "portrait": "resources/cashier.png", "text": "У вас уже заправлен полный бак.", "next": "end_1" }, @@ -159,7 +193,7 @@ { "id": "line_1", "type": "Line", - "speaker": "Игрок", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "Ну капец, мы застряли!", "next": "line_2" @@ -167,7 +201,7 @@ { "id": "line_2", "type": "Line", - "speaker": "Игрок", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "Теперь только пешком.", "next": "end_1" @@ -185,7 +219,7 @@ { "id": "line_1", "type": "Line", - "speaker": "Игрок", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "Все, приехали! Машина в хлам!", "next": "end_1" @@ -203,7 +237,7 @@ { "id": "line_1", "type": "Line", - "speaker": "Игрок", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "Все, приехали! Бензин закончился!", "next": "end_1" @@ -221,8 +255,8 @@ { "id": "line_1", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Алексей", + "portrait": "resources/girlfriend.png", "text": "До Таласской области осталось ехать примерно 7 километров.", "next": "end_1" }, @@ -239,8 +273,8 @@ { "id": "line_1", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Алексей", + "portrait": "resources/girlfriend.png", "text": "До Таласской области осталось ехать примерно 5 километров.", "next": "end_1" }, @@ -257,8 +291,8 @@ { "id": "line_1", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Алтынай", + "portrait": "resources/girlfriend.png", "text": "До Таласской области осталось ехать примерно 2 километра.", "next": "end_1" }, @@ -275,31 +309,31 @@ { "id": "line_1", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Алтынай", + "portrait": "resources/girlfriend.png", "text": "Все, мы в Таласской области.", "next": "line_2" }, { "id": "line_2", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Алтынай", + "portrait": "resources/girlfriend.png", "text": "Здесь должен быть сигнал, дай мне телефон.", "next": "line_3" }, { "id": "line_3", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Алтынай", + "portrait": "resources/girlfriend.png", "text": "Я позвоню дяде.", "next": "line_4" }, { "id": "line_4", "type": "Line", - "speaker": "Игрок", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "Держи.", "next": "line_5" @@ -307,47 +341,47 @@ { "id": "line_5", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Алтынай", + "portrait": "resources/girlfriend.png", "text": "Ало, Акыл байке! Это Алтынай.", "next": "line_6" },{ "id": "line_6", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Алтынай", + "portrait": "resources/girlfriend.png", "text": "У меня проблемы, меня преследуют бандиты.", "next": "line_7" }, { "id": "line_7", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Алтынай", + "portrait": "resources/girlfriend.png", "text": "Я сейчас еду с другом в сторону Таласа.", "next": "line_8" }, { "id": "line_8", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Алтынай", + "portrait": "resources/girlfriend.png", "text": "Пришли пожалуйста патруль мне на встречу.", "next": "line_9" }, { "id": "line_9", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Алтынай", + "portrait": "resources/girlfriend.png", "text": "Белый седан, номер 256.", "next": "line_10" }, { "id": "line_10", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Алтынай", + "portrait": "resources/girlfriend.png", "text": "Спасибо! Пока!", "next": "end_1" }, @@ -364,7 +398,7 @@ { "id": "line_1", "type": "Line", - "speaker": "Hero", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "У нас бензин кончается.", "next": "line_2" @@ -372,7 +406,7 @@ { "id": "line_2", "type": "Line", - "speaker": "Hero", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "Надо заправиться.", "next": "line_3" @@ -380,16 +414,16 @@ { "id": "line_3", "type": "Line", - "speaker": "Ghost", - "portrait": "resources/ghost_avatar.png", + "speaker": "Алтынай", + "portrait": "resources/girlfriend.png", "text": "Хорошо, только давай быстро.", "next": "line_4" }, { "id": "line_4", "type": "Line", - "speaker": "Ghost", - "portrait": "resources/ghost_avatar.png", + "speaker": "Алтынай", + "portrait": "resources/girlfriend.png", "text": "Мне как-то не по себе.", "next": "end_1" }, @@ -406,15 +440,15 @@ { "id": "line_1", "type": "Line", - "speaker": "Ghost", - "portrait": "resources/ghost_avatar.png", + "speaker": "Алтынай", + "portrait": "resources/girlfriend.png", "text": "Ты видел? Он так на меня смотрел, как будто узнал меня.", "next": "line_2" }, { "id": "line_2", "type": "Line", - "speaker": "Hero", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "Что-то мне стремно, давай поедем отсюда быстрее", "next": "end_1" @@ -432,15 +466,15 @@ { "id": "line_1", "type": "Line", - "speaker": "Hero", - "portrait": "resources/hero.png", + "speaker": "Телефон", + "portrait": "resources/phone.png", "text": "[Телефон звонит]", "next": "line_2" }, { "id": "line_2", "type": "Line", - "speaker": "Hero", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "Да, слушаю.", "next": "line_3" @@ -448,88 +482,88 @@ { "id": "line_3", "type": "Line", - "speaker": "Ghost", - "portrait": "resources/ghost_avatar.png", + "speaker": "Телефон", + "portrait": "resources/phone.png", "text": "Алексей, это Нурланбай на связи.", "next": "line_4" }, { "id": "line_4", "type": "Line", - "speaker": "Ghost", - "portrait": "resources/ghost_avatar.png", + "speaker": "Телефон", + "portrait": "resources/phone.png", "text": "Мои ребята сообщили, что Алтынай видели на заправке с тобой.", "next": "line_5" }, { "id": "line_5", "type": "Line", - "speaker": "Ghost", - "portrait": "resources/ghost_avatar.png", + "speaker": "Алексей", + "portrait": "resources/hero.png", "text": "Как вы узнали мой номер?", "next": "line_6" }, { "id": "line_6", "type": "Line", - "speaker": "Ghost", - "portrait": "resources/ghost_avatar.png", + "speaker": "Телефон", + "portrait": "resources/phone.png", "text": "О, это было нетрудно. У тебя слишком заметная машина.", "next": "line_7" }, { "id": "line_7", "type": "Line", - "speaker": "Ghost", - "portrait": "resources/ghost_avatar.png", + "speaker": "Телефон", + "portrait": "resources/phone.png", "text": "Мои ребята уже поехали за тобой.", "next": "line_8" }, { "id": "line_8", "type": "Line", - "speaker": "Ghost", - "portrait": "resources/ghost_avatar.png", + "speaker": "Телефон", + "portrait": "resources/phone.png", "text": "Предлагаю тебе не усложнять ничего.", "next": "line_9" }, { "id": "line_9", "type": "Line", - "speaker": "Ghost", - "portrait": "resources/ghost_avatar.png", + "speaker": "Телефон", + "portrait": "resources/phone.png", "text": "Остановись на трассе, отдай нам Алтынай.", "next": "line_10" }, { "id": "line_10", "type": "Line", - "speaker": "Ghost", - "portrait": "resources/ghost_avatar.png", + "speaker": "Телефон", + "portrait": "resources/phone.png", "text": "И можешь ехать дальше спокойно.", "next": "line_11" }, { "id": "line_11", "type": "Line", - "speaker": "Ghost", - "portrait": "resources/ghost_avatar.png", + "speaker": "Алексей", + "portrait": "resources/hero.png", "text": "Никогда!", "next": "line_12" },{ "id": "line_12", "type": "Line", - "speaker": "Ghost", - "portrait": "resources/ghost_avatar.png", + "speaker": "Телефон", + "portrait": "resources/phone.png", "text": "Какой ты смелый парень.", "next": "line_13" },{ "id": "line_13", "type": "Line", - "speaker": "Ghost", - "portrait": "resources/ghost_avatar.png", + "speaker": "Телефон", + "portrait": "resources/phone.png", "text": "Ну ничего, скоро увидимся. Давай, бывай.", "next": "line_14" }, { "id": "line_14", "type": "Line", - "speaker": "Ghost", - "portrait": "resources/ghost_avatar.png", + "speaker": "Телефон", + "portrait": "resources/phone.png", "text": "[Гудки]", "next": "end_1" }, @@ -546,23 +580,23 @@ { "id": "line_1", "type": "Line", - "speaker": "Ghost", - "portrait": "resources/ghost_avatar.png", + "speaker": "Милиция", + "portrait": "resources/police.png", "text": "Старший лейтенант Каримов, отдел милиции Чуйской области.", "next": "line_2" }, { "id": "line_2", "type": "Line", - "speaker": "Hero", - "portrait": "resources/hero.png", + "speaker": "Милиция", + "portrait": "resources/police.png", "text": "Предъявите ваши права.", "next": "line_3" }, { "id": "line_3", "type": "Line", - "speaker": "Hero", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "Вот, пожалуйста.", "next": "line_4" @@ -570,23 +604,23 @@ { "id": "line_4", "type": "Line", - "speaker": "Hero", - "portrait": "resources/hero.png", + "speaker": "Милиция", + "portrait": "resources/police.png", "text": "Алексей Смирнов, да?", "next": "line_5" }, { "id": "line_5", "type": "Line", - "speaker": "Hero", - "portrait": "resources/hero.png", + "speaker": "Милиция", + "portrait": "resources/police.png", "text": "Когда в последний раз проходили техосмотр?", "next": "line_6" }, { "id": "line_6", "type": "Line", - "speaker": "Hero", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "В прошлом году.", "next": "line_7" @@ -594,15 +628,15 @@ { "id": "line_7", "type": "Line", - "speaker": "Hero", - "portrait": "resources/hero.png", + "speaker": "Милиция", + "portrait": "resources/police.png", "text": "Страховка у вас есть?", "next": "line_8" }, { "id": "line_8", "type": "Line", - "speaker": "Hero", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "Да, вот, до ноября.", "next": "line_9" @@ -610,15 +644,15 @@ { "id": "line_9", "type": "Line", - "speaker": "Hero", - "portrait": "resources/hero.png", + "speaker": "Милиция", + "portrait": "resources/police.png", "text": "А огнетушитель в машине есть?", "next": "line_10" }, { "id": "line_10", "type": "Line", - "speaker": "Hero", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "*Да он просто тянет время!*", "next": "line_11" @@ -626,7 +660,7 @@ { "id": "line_11", "type": "Line", - "speaker": "Hero", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "Давайте напрямую, я задержан или нет?", "next": "line_12" @@ -634,22 +668,22 @@ { "id": "line_12", "type": "Line", - "speaker": "Hero", - "portrait": "resources/hero.png", + "speaker": "Милиция", + "portrait": "resources/police.png", "text": "Нет...", "next": "line_13" },{ "id": "line_13", "type": "Line", - "speaker": "Hero", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "Если не отпустите меня сейчас, я позвоню в прокуратуру.", "next": "line_14" },{ "id": "line_14", "type": "Line", - "speaker": "Hero", - "portrait": "resources/hero.png", + "speaker": "Милиция", + "portrait": "resources/police.png", "text": ".. Счастливого пути!", "next": "end_1" }, @@ -666,8 +700,8 @@ { "id": "line_1", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Бандит", + "portrait": "resources/bandit.png", "text": "Попался красавчик.", "next": "end_1" }, @@ -684,9 +718,9 @@ { "id": "line_1", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", - "text": "Эй, парень! Остановись и выйди!", + "speaker": "Бандит", + "portrait": "resources/bandit.png", + "text": "Эй, парень! Остановись!", "next": "end_1" }, { @@ -702,8 +736,8 @@ { "id": "line_1", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Бандит", + "portrait": "resources/bandit.png", "text": "Попались воробушки.", "next": "end_1" }, @@ -720,8 +754,8 @@ { "id": "line_1", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Бандит", + "portrait": "resources/bandit.png", "text": "Ну все, вам конец!", "next": "end_1" }, @@ -738,7 +772,7 @@ { "id": "line_1", "type": "Line", - "speaker": "Игрок", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "Алтынай ты тут?", "next": "line_2" @@ -746,48 +780,48 @@ { "id": "line_2", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Алтынай", + "portrait": "resources/girlfriend.png", "text": "Да, я тут!", "next": "line_3" }, { "id": "line_3", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Алтынай", + "portrait": "resources/girlfriend.png", "text": "Где твоя машина припаркована?", "next": "line_4" }, { "id": "line_4", "type": "Line", - "speaker": "Игрок", + "speaker": "Алексей", "portrait": "resources/hero.png", - "text": "Прямо тут, недалеко.", + "text": "Здесь рядом.", "next": "line_5" }, { "id": "line_5", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Алтынай", + "portrait": "resources/girlfriend.png", "text": "Давай отведи меня в машину, только тихо!", "next": "line_6" }, { "id": "line_6", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Алтынай", + "portrait": "resources/girlfriend.png", "text": "Постарайся не попадаться на глаза бандитам!", "next": "line_7" }, { "id": "line_7", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Алтынай", + "portrait": "resources/girlfriend.png", "text": "Я прыгаю!", "next": "end_1" }, @@ -804,7 +838,7 @@ { "id": "line_1", "type": "Line", - "speaker": "Игрок", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "*Моя подруга Алтынай пропала сегодня.*", "next": "line_2" @@ -812,7 +846,7 @@ { "id": "line_2", "type": "Line", - "speaker": "Игрок", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "*А потом я получил голосовое сообщение от нее.*", "next": "line_3" @@ -820,63 +854,63 @@ { "id": "line_3", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Телефон", + "portrait": "resources/phone.png", "text": "Если ты это слушаешь, то случилась беда.", "next": "line_4" }, { "id": "line_4", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Телефон", + "portrait": "resources/phone.png", "text": "Ко мне сватался Нурланбай, местный бизнесмен и авторитет.", "next": "line_5" }, { "id": "line_5", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Телефон", + "portrait": "resources/phone.png", "text": "Он мне обещал, что похитит меня, и сегодня он сделал это.", "next": "line_6" }, { "id": "line_6", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Телефон", + "portrait": "resources/phone.png", "text": "Я отправила тебе координаты дома, куда могут меня спрятать.", "next": "line_7" }, { "id": "line_7", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Телефон", + "portrait": "resources/phone.png", "text": "Не обращайся в милицию, у него есть связи в МВД Чуйской области.", "next": "line_8" }, { "id": "line_8", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Телефон", + "portrait": "resources/phone.png", "text": "Приезжай на своей машине и помоги мне сбежать оттуда!", "next": "line_9" }, { "id": "line_9", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Телефон", + "portrait": "resources/phone.png", "text": "Мне больше не к кому обращаться. Я жду тебя!", "next": "line_10" }, { "id": "line_10", "type": "Line", - "speaker": "Игрок", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "*Село где держат Алтынай, находится впереди по трассе.*", "next": "line_11" @@ -884,7 +918,7 @@ { "id": "line_11", "type": "Line", - "speaker": "Игрок", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "*Пора садится в машину и отправляться в путь!*", "next": "end_1" @@ -902,7 +936,7 @@ { "id": "line_1", "type": "Line", - "speaker": "Игрок", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "*Вот этот дом, с охраной!*", "next": "line_2" @@ -910,7 +944,7 @@ { "id": "line_2", "type": "Line", - "speaker": "Игрок", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "*Нужно держаться от этого бандита подальше!*", "next": "line_3" @@ -918,7 +952,7 @@ { "id": "line_3", "type": "Line", - "speaker": "Игрок", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "*Если этот бандит меня заметит, он меня убьет!*", "next": "line_4" @@ -926,7 +960,7 @@ { "id": "line_4", "type": "Line", - "speaker": "Игрок", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "*Может стоит прократься мимо него и осмотреть дом сзади?*", "next": "end_1" @@ -944,23 +978,23 @@ { "id": "line_1", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Алтынай", + "portrait": "resources/girlfriend.png", "text": "Что сидишь, поехали!", "next": "line_2" }, { "id": "line_2", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Алтынай", + "portrait": "resources/girlfriend.png", "text": "Дави по газам быстрее пока они нас не заметили!", "next": "line_3" }, { "id": "line_3", "type": "Line", - "speaker": "Игрок", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "Конечно, сейчас...", "next": "line_4" @@ -968,39 +1002,39 @@ { "id": "line_4", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Алтынай", + "portrait": "resources/girlfriend.png", "text": "Спасибо что помог мне!", "next": "line_5" }, { "id": "line_5", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Алтынай", + "portrait": "resources/girlfriend.png", "text": "Этот Нурланбай сумасшедший!", "next": "line_6" }, { "id": "line_6", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Алтынай", + "portrait": "resources/girlfriend.png", "text": "Он владелец сети заправок, а еще свою банду собрал.", "next": "line_7" },{ "id": "line_7", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Алтынай", + "portrait": "resources/girlfriend.png", "text": "У него друг начальник милиции Чуйской области.", "next": "line_8" }, { "id": "line_8", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Алтынай", + "portrait": "resources/girlfriend.png", "text": "Но я не выйду за него, пусть он сдохнет!", "next": "end_1" }, @@ -1017,7 +1051,7 @@ { "id": "line_1", "type": "Line", - "speaker": "Игрок", + "speaker": "Алексей", "portrait": "resources/hero.png", "text": "А куда мы поедем?", "next": "line_2" @@ -1025,32 +1059,32 @@ { "id": "line_2", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Алтынай", + "portrait": "resources/girlfriend.png", "text": "Давай в Талас.", "next": "line_3" }, { "id": "line_3", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Алтынай", + "portrait": "resources/girlfriend.png", "text": "Там у меня есть дядя в администрации области.", "next": "line_4" }, { "id": "line_4", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Алтынай", + "portrait": "resources/girlfriend.png", "text": "Я у него спрячусь, и никакой Нурланбай нас там не достанет.", "next": "line_5" }, { "id": "line_5", "type": "Line", - "speaker": "Игрок", - "portrait": "resources/hero.png", + "speaker": "Алтынай", + "portrait": "resources/girlfriend.png", "text": "Надо только доехать хотя бы до границы области.", "next": "end_1" }, diff --git a/resources/e/car_bandit001.png b/resources/e/car_bandit001.png new file mode 100644 index 0000000..6fd5f06 --- /dev/null +++ b/resources/e/car_bandit001.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cf4f87cbe76478c9136b65cd1aca837b625e439f6251a1962c44b014575888d9 +size 70164 diff --git a/resources/girlfriend.png b/resources/girlfriend.png new file mode 100644 index 0000000..6478166 --- /dev/null +++ b/resources/girlfriend.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3534b70ffc4fe15e9f2019a9e4a477e5e1a67a1be30e6ea2c51a56fdfd70b1da +size 49579 diff --git a/resources/police.png b/resources/police.png new file mode 100644 index 0000000..2343753 --- /dev/null +++ b/resources/police.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ed85f19811176e16f356054f59ab2498c04b0fc32ba3b6f79f7b2ae41074b27f +size 50782 diff --git a/src/Game.cpp b/src/Game.cpp index 0eac095..d1529fb 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -153,14 +153,15 @@ namespace ZL }; + defaultLocation = std::make_shared(renderer, inventory, "default"); defaultLocation->setup(); defaultLocation->onLocationChangeRequest = [this](const std::string& locId) { this->changeLocation(locId); }; - currentLocation = defaultLocation; - //currentLocation = forestLocation; + //currentLocation = defaultLocation; + currentLocation = forestLocation; std::cout << "Load resurces step 5" << std::endl; diff --git a/src/Location.cpp b/src/Location.cpp index ba7bb55..ec19d40 100644 --- a/src/Location.cpp +++ b/src/Location.cpp @@ -165,11 +165,34 @@ void Location::setup() 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)); + auto banditTexture1 = std::make_shared(CreateTextureDataFromPng("resources/e/bandit_packed1_diffuse.png", CONST_ZIP_FILE)); + auto banditTexture2 = std::make_shared(CreateTextureDataFromPng("resources/e/bandit_packed2_diffuse.png", CONST_ZIP_FILE)); + + bandit = std::make_unique(); + bandit->loadBinaryAnimation(AnimationState::STAND, "resources/e/bandit_stand_idle003.anim2", CONST_ZIP_FILE); + bandit->loadBinaryAnimation(AnimationState::WALK, "resources/e/bandit_run003.anim2", CONST_ZIP_FILE); + bandit->setTexture("Body", banditTexture0); + bandit->setTexture("Bottoms", banditTexture2); + bandit->setTexture("Eyelashes", banditTexture0); + bandit->setTexture("Eyes", banditTexture0); + bandit->setTexture("Gloves", banditTexture1); + bandit->setTexture("Masks", banditTexture1); + bandit->setTexture("Shoes", banditTexture1); + bandit->setTexture("Tops", banditTexture1); + bandit->walkSpeed = 3.6f; + bandit->rotationSpeed = 8.0f; + bandit->modelScale = 0.01f; + bandit->modelCorrectionRotation = Eigen::Quaternionf(Eigen::AngleAxisf(M_PI, Eigen::Vector3f::UnitY())); + // Parked far away — only spawned on screen via npcBanditCarSpawned. + bandit->position = Vector3f{ 1000, 0, 1000 }; + bandit->setTarget(bandit->position); std::cout << "[LOCATION] Setting up FOREST location (custom models only)" << std::endl; carPosition ={ 7, 0, -7 + 300 }; npcCar.position = Vector3f(9, 0, -335) + Vector3f(1000,0,0); + npcBanditCar.position = Vector3f(9, 0, 0) + Vector3f(2000, 0, 0); girlfriend->position = Vector3f{ 5, 0, 0.9 + 300 }; girlfriend->setTarget(girlfriend->position); @@ -402,6 +425,17 @@ void Location::setup() //npcCar.position = carPosition + Eigen::Vector3f(0, 0.f, 14.f);//Eigen::Vector3f(-12.f, 0.f, 8.f); npcCar.rotation = 0.f; npcCar.mode = NpcCar::Mode::NONE; + + // Bandit car: shares the same mesh as the player/police car but uses its own + // texture, is a touch faster, and tails the player at half the follow distance. + npcBanditCar.texture = std::make_shared(CreateTextureDataFromPng("resources/e/car_bandit001.png", CONST_ZIP_FILE)); + npcBanditCar.rotation = 0.f; + npcBanditCar.mode = NpcCar::Mode::NONE; + npcBanditCar.maxSpeed = 26.0f; + npcBanditCar.maxReverseSpeed = 8.0f; + npcBanditCar.followMinDistance = 10.5f; + npcBanditCar.followMaxDistance = 12.5f; + npcBanditCar.position = Vector3f(-1000, 0, -1000); /*npcCar.mode = NpcCar::Mode::FOLLOW_WAYPOINTS; npcCar.waypoints = { Eigen::Vector3f(-12.f, 0.f, 8.f), @@ -727,7 +761,8 @@ void Location::setup() renderer.PopMatrix(); - drawNpcCar(); + drawNpcCar(npcCar); + drawNpcCar(npcBanditCar); // Don't draw the player mesh when in first-person mode if (player && !inCar && !firstPersonMode) player->draw(renderer); @@ -1016,9 +1051,9 @@ void Location::setup() return std::abs(alongForward) <= carLength * 0.5f && std::abs(alongRight) <= carWidth * 0.5f; } - void Location::pushOutOfNpcCarFootprint(Eigen::Vector3f& position) const + void Location::pushOutOfNpcCarFootprint(const NpcCar& car, Eigen::Vector3f& position) const { - if (!npcCar.texture) return; + if (!car.texture) return; constexpr float carLength = 7.3f; constexpr float carWidth = 2.8f; @@ -1026,10 +1061,10 @@ void Location::setup() constexpr float halfWidth = carWidth * 0.5f; constexpr float skin = 0.05f; - const Eigen::Vector3f forward(-std::sin(npcCar.rotation), 0.f, -std::cos(npcCar.rotation)); - const Eigen::Vector3f right(std::cos(npcCar.rotation), 0.f, -std::sin(npcCar.rotation)); + const Eigen::Vector3f forward(-std::sin(car.rotation), 0.f, -std::cos(car.rotation)); + const Eigen::Vector3f right(std::cos(car.rotation), 0.f, -std::sin(car.rotation)); - Eigen::Vector3f delta = position - npcCar.position; + Eigen::Vector3f delta = position - car.position; delta.y() = 0.f; float alongForward = delta.dot(forward); float alongRight = delta.dot(right); @@ -1050,20 +1085,20 @@ void Location::setup() } const float originalY = position.y(); - Eigen::Vector3f resolved = npcCar.position + forward * alongForward + right * alongRight; + Eigen::Vector3f resolved = car.position + forward * alongForward + right * alongRight; resolved.y() = originalY; position = resolved; } - bool Location::doesPlayerCarCollideWithNpcCar(const Eigen::Vector3f& center, float rotation) const + bool Location::doesPlayerCarCollideWithNpcCar(const NpcCar& car, const Eigen::Vector3f& center, float rotation) const { - if (!npcCar.texture) return false; + if (!car.texture) return false; constexpr float carLength = 7.3f; constexpr float carWidth = 2.8f; constexpr float sampleSpacing = 0.5f; - const Eigen::Vector3f delta = center - npcCar.position; + const Eigen::Vector3f delta = center - car.position; const float approxRadius = std::sqrt(carLength * carLength + carWidth * carWidth); if (delta.squaredNorm() > approxRadius * approxRadius) { return false; @@ -1083,7 +1118,7 @@ void Location::setup() for (int j = 0; j <= widthSteps; ++j) { const float fw = (static_cast(j) / static_cast(widthSteps)) * 2.0f - 1.0f; const Eigen::Vector3f sample = center + lineOffset + right * (fw * halfWidth); - if (isPointInCarFootprint(sample, npcCar.position, npcCar.rotation)) { + if (isPointInCarFootprint(sample, car.position, car.rotation)) { return true; } } @@ -1202,14 +1237,19 @@ void Location::setup() updateGirlfriendFollow(); girlfriend->update(delta); if (!girlfriendInCar) { - pushOutOfNpcCarFootprint(girlfriend->position); + pushOutOfNpcCarFootprint(npcCar, girlfriend->position); + pushOutOfNpcCarFootprint(npcBanditCar, girlfriend->position); } } - if (salesperson) pushOutOfNpcCarFootprint(salesperson->position); + if (salesperson) { + pushOutOfNpcCarFootprint(npcCar, salesperson->position); + pushOutOfNpcCarFootprint(npcBanditCar, salesperson->position); + } if (police) { police->update(delta); - pushOutOfNpcCarFootprint(police->position); + pushOutOfNpcCarFootprint(npcCar, police->position); + pushOutOfNpcCarFootprint(npcBanditCar, police->position); } if (bandit) { @@ -1217,12 +1257,14 @@ void Location::setup() updateBanditFollow(); } bandit->update(delta); - pushOutOfNpcCarFootprint(bandit->position); + pushOutOfNpcCarFootprint(npcCar, bandit->position); + pushOutOfNpcCarFootprint(npcBanditCar, bandit->position); } for (auto& npc : npcs) { npc->update(delta); - pushOutOfNpcCarFootprint(npc->position); + pushOutOfNpcCarFootprint(npcCar, npc->position); + pushOutOfNpcCarFootprint(npcBanditCar, npc->position); } if (inCar) { @@ -1273,7 +1315,8 @@ void Location::setup() if (navigation.isReady() && !isCarFootprintWalkable(carPosition, carRotation)) { carRotation = oldRotation; } - if (doesPlayerCarCollideWithNpcCar(carPosition, carRotation)) { + if (doesPlayerCarCollideWithNpcCar(npcCar, carPosition, carRotation) + || doesPlayerCarCollideWithNpcCar(npcBanditCar, carPosition, carRotation)) { carRotation = oldRotation; } @@ -1294,7 +1337,8 @@ void Location::setup() carVelocity = 0.f; break; } - if (doesPlayerCarCollideWithNpcCar(candidate, carRotation)) { + if (doesPlayerCarCollideWithNpcCar(npcCar, candidate, carRotation) + || doesPlayerCarCollideWithNpcCar(npcBanditCar, candidate, carRotation)) { carVelocity = 0.f; if (!dialoguePlayedCrash && !dialogueSystem.isActive()) { if (dialogueSystem.startDialogue("driving_dialogue_crash")) { @@ -1377,6 +1421,7 @@ void Location::setup() if (navigation.isReady() && !navigation.isWalkable(p)) return true; if (isPointInCarFootprint(p, carPosition, carRotation)) return true; if (isPointInCarFootprint(p, npcCar.position, npcCar.rotation)) return true; + if (npcBanditCar.texture && isPointInCarFootprint(p, npcBanditCar.position, npcBanditCar.rotation)) return true; return false; }; if (blocked(player->position)) { @@ -1394,7 +1439,8 @@ void Location::setup() } } - updateNpcCar(delta); + updateNpcCar(npcCar, delta); + updateNpcCar(npcBanditCar, delta); if (locationId == "forest") { @@ -1454,6 +1500,10 @@ void Location::setup() if (npcCar.mode == NpcCar::Mode::FOLLOW_PLAYER || npcCar.mode == NpcCar::Mode::NONE_STAY) { npcCar.position += shift; } + if (npcBanditCarSpawned && + (npcBanditCar.mode == NpcCar::Mode::FOLLOW_PLAYER || npcBanditCar.mode == NpcCar::Mode::NONE_STAY)) { + npcBanditCar.position += shift; + } } } @@ -1580,6 +1630,89 @@ void Location::setup() npcCarSpawnedAfterPhone = true; } } + + // --- Bandit NPC car quest (forest only) --- + + // Track how long the player has been on foot. Resets the moment they get + // back into the car so brief stops don't count toward the spawn timer. + if (!inCar) { + playerOnFootSeconds += static_cast(delta) / 1000.0f; + } + else { + playerOnFootSeconds = 0.f; + } + + if (!npcBanditCarSpawned && bandit && player && npcBanditCar.texture) { + bool shouldSpawn = false; + + if (dialoguePlayedDrivingGasOut) { + shouldSpawn = true; + } + if (dialoguePlayedOffroad) { + shouldSpawn = true; + } + // Cases 1 & 2: phone dialogue done and player has been on foot >30s + // (the 60s case from the spec collapses into the same trigger — once + // the car is spawned the second threshold is moot). + if (dialoguePlayedPhone1 && !inCar && playerOnFootSeconds > 30.0f) { + shouldSpawn = true; + } + + // Case 3: still driving, police chase wrapped up, and the goal + // distance is closing in. + if (inCar + && policeEncounterStage == PoliceEncounterStage::Done + && distanceRemaining < 1800.0f) { + shouldSpawn = true; + } + + if (shouldSpawn) { + const Eigen::Vector3f anchor = inCar ? carPosition : player->position; + // 150 m behind the player along +Z (player drives toward -Z), + // snapped onto the road's right lane so the bandit appears on the + // road rather than the grass. + npcBanditCar.position = Eigen::Vector3f(9.0f, 0.0f, anchor.z() + 150.0f); + npcBanditCar.rotation = static_cast(M_PI); // facing -Z, toward player + npcBanditCar.velocity = 0.f; + npcBanditCar.steeringAngle = 0.f; + npcBanditCar.mode = NpcCar::Mode::FOLLOW_PLAYER; + npcBanditCarSpawned = true; + } + } + + // Once the bandit car is alongside the player and the player is on foot, + // the bandit hops out and the dialog plays. Fires exactly once. + if (npcBanditCarSpawned && !banditExitedNpcBanditCar && bandit && player && !inCar) { + Eigen::Vector3f toCar = npcBanditCar.position - player->position; + toCar.y() = 0.f; + const float toCarDist = toCar.norm(); + + if (toCarDist < 10.0f && !dialogueSystem.isActive()) { + // Park the bandit car and place the bandit on its left side, the + // same staging the police encounter uses. + npcBanditCar.mode = NpcCar::Mode::NONE_STAY; + npcBanditCar.velocity = 0.f; + + const Eigen::Vector3f carLeft(-std::cos(npcBanditCar.rotation), 0.f, std::sin(npcBanditCar.rotation)); + constexpr float carHalfWidth = 2.8f * 0.5f; + bandit->position = npcBanditCar.position + carLeft * (carHalfWidth + 1.5f); + bandit->position.y() = 0.f; + bandit->clearPath(); + + Eigen::Vector3f approachTarget = player->position; + if (toCarDist > 1e-4f) { + approachTarget = player->position + (toCar / toCarDist) * 1.5f; + } + approachTarget.y() = 0.f; + bandit->setTarget(approachTarget); + + if (dialogueSystem.startDialogue("dialogue_bandit1")) { + dialoguePlayedBandit1 = true; + banditExitedNpcBanditCar = true; + playerFrozen = true; + } + } + } } if (locationId == "default") @@ -1693,42 +1826,44 @@ void Location::setup() } } - void Location::updateNpcCar(int64_t deltaMs) + void Location::updateNpcCar(NpcCar& car, int64_t deltaMs) { const float dt = static_cast(deltaMs) / 1000.0f; - Eigen::Vector3f target = npcCar.position; + Eigen::Vector3f target = car.position; float throttle = 0.f; bool hasTarget = false; - if (npcCar.mode == NpcCar::Mode::FOLLOW_WAYPOINTS) { - if (!npcCar.waypoints.empty()) { - if (npcCar.currentWaypoint >= npcCar.waypoints.size()) { - npcCar.currentWaypoint = 0; + if (car.mode == NpcCar::Mode::FOLLOW_WAYPOINTS) { + if (!car.waypoints.empty()) { + if (car.currentWaypoint >= car.waypoints.size()) { + car.currentWaypoint = 0; } - Eigen::Vector3f toTarget = npcCar.waypoints[npcCar.currentWaypoint] - npcCar.position; + Eigen::Vector3f toTarget = car.waypoints[car.currentWaypoint] - car.position; toTarget.y() = 0.f; - if (toTarget.norm() < npcCar.waypointReachRadius) { - npcCar.currentWaypoint = (npcCar.currentWaypoint + 1) % npcCar.waypoints.size(); + if (toTarget.norm() < car.waypointReachRadius) { + car.currentWaypoint = (car.currentWaypoint + 1) % car.waypoints.size(); } - target = npcCar.waypoints[npcCar.currentWaypoint]; + target = car.waypoints[car.currentWaypoint]; throttle = 1.0f; hasTarget = true; } } - else if (npcCar.mode == NpcCar::Mode::FOLLOW_PLAYER) { - + else if (car.mode == NpcCar::Mode::FOLLOW_PLAYER) { + //if (x > 0.5) { - target = carPosition; - Eigen::Vector3f toTarget = target - npcCar.position; + // Chase the actual player when they're on foot — otherwise an + // abandoned car would keep the chaser frozen at its parking spot. + target = (inCar || !player) ? carPosition : player->position; + Eigen::Vector3f toTarget = target - car.position; toTarget.y() = 0.f; const float dist = toTarget.norm(); - const float targetDist = npcCar.followMinDistance; + const float targetDist = car.followMinDistance; const float coastBuffer = 1.5f; - if (dist > npcCar.followMaxDistance) { + if (dist > car.followMaxDistance) { throttle = 1.0f; } else if (dist > targetDist + coastBuffer) { @@ -1750,69 +1885,69 @@ void Location::setup() throttle = 0.f; } - float targetHeading = npcCar.rotation; + float targetHeading = car.rotation; if (hasTarget) { - Eigen::Vector3f toTarget = target - npcCar.position; + Eigen::Vector3f toTarget = target - car.position; toTarget.y() = 0.f; if (toTarget.squaredNorm() > 1e-4f) { targetHeading = std::atan2(-toTarget.x(), -toTarget.z()); } } - float angleDiff = targetHeading - npcCar.rotation; + float angleDiff = targetHeading - car.rotation; while (angleDiff > M_PI) angleDiff -= 2.0f * static_cast(M_PI); while (angleDiff < -M_PI) angleDiff += 2.0f * static_cast(M_PI); const float turnIntent = max(-1.0f, min(1.0f, angleDiff * 2.0f)); - const float targetSteer = turnIntent * npcCar.maxSteerAngle; + const float targetSteer = turnIntent * car.maxSteerAngle; const float steerLerp = min(1.f, dt * 8.f); - npcCar.steeringAngle += (targetSteer - npcCar.steeringAngle) * steerLerp; + car.steeringAngle += (targetSteer - car.steeringAngle) * steerLerp; if (throttle > 0.f) { - npcCar.velocity += npcCar.acceleration * dt * throttle; + car.velocity += car.acceleration * dt * throttle; } else if (throttle < 0.f) { - const float brakeForce = npcCar.acceleration * dt * (-throttle); - if (npcCar.velocity > 0.f) { - npcCar.velocity = max(0.f, npcCar.velocity - brakeForce); + const float brakeForce = car.acceleration * dt * (-throttle); + if (car.velocity > 0.f) { + car.velocity = max(0.f, car.velocity - brakeForce); } else { - npcCar.velocity = min(0.f, npcCar.velocity + brakeForce); + car.velocity = min(0.f, car.velocity + brakeForce); } } else { - const float resistance = npcCar.friction * dt; - if (npcCar.velocity > 0.f) { - npcCar.velocity = max(0.f, npcCar.velocity - resistance); + const float resistance = car.friction * dt; + if (car.velocity > 0.f) { + car.velocity = max(0.f, car.velocity - resistance); } - else if (npcCar.velocity < 0.f) { - npcCar.velocity = min(0.f, npcCar.velocity + resistance); + else if (car.velocity < 0.f) { + car.velocity = min(0.f, car.velocity + resistance); } } - npcCar.velocity = max(-npcCar.maxReverseSpeed, min(npcCar.maxSpeed, npcCar.velocity)); + car.velocity = max(-car.maxReverseSpeed, min(car.maxSpeed, car.velocity)); - const float oldRotation = npcCar.rotation; - if (std::abs(npcCar.velocity) > 0.01f) { - const float speedFactor = npcCar.velocity / npcCar.maxSpeed; - npcCar.rotation += turnIntent * npcCar.turnRate * dt * speedFactor; + const float oldRotation = car.rotation; + if (std::abs(car.velocity) > 0.01f) { + const float speedFactor = car.velocity / car.maxSpeed; + car.rotation += turnIntent * car.turnRate * dt * speedFactor; } - if (navigation.isReady() && !isCarFootprintWalkable(npcCar.position, npcCar.rotation)) { - npcCar.rotation = oldRotation; + if (navigation.isReady() && !isCarFootprintWalkable(car.position, car.rotation)) { + car.rotation = oldRotation; } - const Eigen::Vector3f forward(-std::sin(npcCar.rotation), 0.f, -std::cos(npcCar.rotation)); - const Eigen::Vector3f totalDelta = forward * npcCar.velocity * dt; + const Eigen::Vector3f forward(-std::sin(car.rotation), 0.f, -std::cos(car.rotation)); + const Eigen::Vector3f totalDelta = forward * car.velocity * dt; const float totalDist = totalDelta.norm(); const float maxSubstep = 0.2f; const int substeps = max(1, static_cast(std::ceil(totalDist / maxSubstep))); const Eigen::Vector3f stepDelta = totalDelta / static_cast(substeps); for (int s = 0; s < substeps; ++s) { - Eigen::Vector3f candidate = npcCar.position + stepDelta; - if (navigation.isReady() && !isCarFootprintWalkable(candidate, npcCar.rotation)) { - npcCar.velocity = 0.f; + Eigen::Vector3f candidate = car.position + stepDelta; + if (navigation.isReady() && !isCarFootprintWalkable(candidate, car.rotation)) { + car.velocity = 0.f; break; } - npcCar.position = candidate; + car.position = candidate; } } @@ -1925,15 +2060,15 @@ void Location::setup() } } - void Location::drawNpcCar() + void Location::drawNpcCar(const NpcCar& car) { - if (!npcCar.texture) return; + if (!car.texture) return; renderer.PushMatrix(); - renderer.TranslateMatrix(npcCar.position); + renderer.TranslateMatrix(car.position); renderer.TranslateMatrix({ 0, 0.7f, 0 }); - renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(npcCar.rotation, Eigen::Vector3f::UnitY())).toRotationMatrix()); - glBindTexture(GL_TEXTURE_2D, npcCar.texture->getTexID()); + renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(car.rotation, Eigen::Vector3f::UnitY())).toRotationMatrix()); + glBindTexture(GL_TEXTURE_2D, car.texture->getTexID()); renderer.DrawVertexRenderStruct(carMesh); if (carWheelTexture) { @@ -1953,7 +2088,7 @@ void Location::setup() renderer.PushMatrix(); renderer.TranslateMatrix(wheelPositions[i]); if (isFront[i]) { - renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(npcCar.steeringAngle, Eigen::Vector3f::UnitY())).toRotationMatrix()); + renderer.RotateMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(car.steeringAngle, Eigen::Vector3f::UnitY())).toRotationMatrix()); } renderer.DrawVertexRenderStruct(carWheelMesh); renderer.PopMatrix(); @@ -2152,6 +2287,7 @@ void Location::setup() roomTexture.reset(); npcCar.texture.reset(); + npcBanditCar.texture.reset(); //dialogueSystem.dialogueDatabase.clear(); @@ -2168,6 +2304,11 @@ void Location::setup() dialoguePlayedOffroad = false; dialoguePlayedCrash = false; + playerOnFootSeconds = 0.f; + npcBanditCarSpawned = false; + dialoguePlayedBandit1 = false; + banditExitedNpcBanditCar = false; + std::cout << "[LOCATION] Cleanup complete" << std::endl; } diff --git a/src/Location.h b/src/Location.h index bf297b5..1c857a3 100644 --- a/src/Location.h +++ b/src/Location.h @@ -98,6 +98,7 @@ namespace ZL float carMaxSteerAngle = 0.6f; NpcCar npcCar; + NpcCar npcBanditCar; bool keyForward = false; bool keyBackward = false; @@ -144,6 +145,11 @@ namespace ZL Eigen::Vector3f banditLastFollowTarget = Eigen::Vector3f::Zero(); bool banditLastFollowTargetValid = false; + float playerOnFootSeconds = 0.f; + bool npcBanditCarSpawned = false; + bool dialoguePlayedBandit1 = false; + bool banditExitedNpcBanditCar = false; + bool dialoguePlayedVillageRescue1 = false; bool dialoguePlayedVillageIntro1 = false; bool dialogueVillageIntro1Finished = false; @@ -185,10 +191,10 @@ namespace ZL bool setNavigationAreaAvailable(const std::string& areaName, bool available); void update(int64_t deltaMs); - void updateNpcCar(int64_t deltaMs); + void updateNpcCar(NpcCar& car, int64_t deltaMs); void updateGirlfriendFollow(); void updateBanditFollow(); - void drawNpcCar(); + void drawNpcCar(const NpcCar& car); void handleDown(int64_t fingerId, int eventX, int eventY, int mx, int my); void handleUp(int64_t fingerId, int mx, int my); @@ -219,8 +225,8 @@ namespace ZL bool isCarFootprintWalkable(const Eigen::Vector3f& center, float rotation) const; bool isPointInCarFootprint(const Eigen::Vector3f& point, const Eigen::Vector3f& center, float rotation) const; - bool doesPlayerCarCollideWithNpcCar(const Eigen::Vector3f& center, float rotation) const; - void pushOutOfNpcCarFootprint(Eigen::Vector3f& position) const; + bool doesPlayerCarCollideWithNpcCar(const NpcCar& car, const Eigen::Vector3f& center, float rotation) const; + void pushOutOfNpcCarFootprint(const NpcCar& car, Eigen::Vector3f& position) const; // --- FIRST PERSON BUILDING ZONE --- bool firstPersonMode = false;