Working on cutscenes and pathfinding

This commit is contained in:
Vladislav Khorev 2026-06-14 22:31:11 +03:00
parent 80825e3220
commit 020f31e1e4
17 changed files with 192 additions and 73 deletions

View File

@ -1,12 +1,11 @@
export const AVAILABLE_IMAGES: string[] = [
'resources/w/cutscenes/cutscene1/cutscene1_wall_x.png',
'resources/w/cutscenes/cutscene1/cutscene1_aida1_x.png',
'resources/w/cutscenes/cutscene1/cutscene1_aida2_x.png',
'resources/w/cutscenes/cutscene1/cutscene1_aida3_x.png',
'resources/w/cutscenes/cutscene1/cutscene1_heads_x.png',
'resources/w/cutscenes/cutscene2/scr1.png',
'resources/w/cutscenes/cutscene2/scr2.png',
'resources/w/cutscenes/cutscene2/scr3.png',
'resources/w/cutscenes/cutscene2/scr4.png',
'resources/w/cutscenes/cutscene2/cs2_background.png',
'resources/w/cutscenes/cutscene2/cs2_books.png',
'resources/w/cutscenes/cutscene2/cs2_chair.png',
'resources/w/cutscenes/cutscene2/cs2_gg001.png',
'resources/w/cutscenes/cutscene2/cs2_gg002.png',
'resources/w/cutscenes/cutscene2/cs2_gg003.png',
'resources/w/cutscenes/cutscene2/cs2_gg004.png',
'resources/w/cutscenes/cutscene2/cs2_foreground.png',
'resources/black.png',
];

View File

@ -414,19 +414,19 @@
{
"id": "computer_cutscene001",
"skippable": true,
"durationMs": 9000,
"durationMs": 12000,
"fadeOutMs": 500,
"fadeInMs": 500,
"endFadeOutMs": 0,
"endFadeInMs": 500,
"onFadeInCallback": "on_sleep_cutscene",
"onFadeInCallback": "",
"imageSegments": [
{
"path": "resources/black.png",
"width": 1280,
"height": 720,
"startMs": 0,
"endMs": 9000,
"endMs": 13000,
"fadeInMs": 0,
"fadeOutMs": 0,
"easing": "Linear",
@ -442,82 +442,162 @@
}
},
{
"path": "resources/w/cutscenes/cutscene2/scr1.png",
"path": "resources/w/cutscenes/cutscene2/cs2_background.png",
"width": 1280,
"height": 720,
"startMs": 0,
"endMs": 2510,
"fadeInMs": 500,
"fadeOutMs": 0,
"endMs": 11984,
"fadeInMs": 0,
"fadeOutMs": 1000,
"easing": "Linear",
"from": {
"centerX": 0.5,
"centerY": 0.55,
"centerY": 0.5,
"scale": 1
},
"to": {
"centerX": 0.5,
"centerY": 0.55,
"centerY": 0.5,
"scale": 1
}
},
{
"path": "resources/w/cutscenes/cutscene2/cs2_books.png",
"width": 1280,
"height": 720,
"startMs": 0,
"endMs": 11550,
"fadeInMs": 0,
"fadeOutMs": 1000,
"easing": "Linear",
"from": {
"centerX": 0.46,
"centerY": 0.5,
"scale": 1.1
},
"to": {
"centerX": 0.58,
"centerY": 0.5,
"scale": 1.2
}
},
{
"path": "resources/w/cutscenes/cutscene2/cs2_chair.png",
"width": 1280,
"height": 720,
"startMs": 17,
"endMs": 10767,
"fadeInMs": 0,
"fadeOutMs": 300,
"easing": "Linear",
"from": {
"centerX": 0.47,
"centerY": 0.5,
"scale": 1.1
},
"to": {
"centerX": 0.57,
"centerY": 0.5,
"scale": 1.3
}
},
{
"path": "resources/w/cutscenes/cutscene2/cs2_gg001.png",
"width": 1280,
"height": 720,
"startMs": 0,
"endMs": 3083,
"fadeInMs": 0,
"fadeOutMs": 1000,
"easing": "Linear",
"from": {
"centerX": 0.47,
"centerY": 0.5,
"scale": 1.1
},
"to": {
"centerX": 0.5,
"centerY": 0.5,
"scale": 1.15
}
},
{
"path": "resources/w/cutscenes/cutscene2/cs2_gg002.png",
"width": 1280,
"height": 720,
"startMs": 1850,
"endMs": 4883,
"fadeInMs": 1000,
"fadeOutMs": 1000,
"easing": "Linear",
"from": {
"centerX": 0.5,
"centerY": 0.5,
"scale": 1.15
},
"to": {
"centerX": 0.53,
"centerY": 0.5,
"scale": 1.2
}
},
{
"path": "resources/w/cutscenes/cutscene2/cs2_gg003.png",
"width": 1280,
"height": 720,
"startMs": 3950,
"endMs": 7083,
"fadeInMs": 1000,
"fadeOutMs": 1000,
"easing": "Linear",
"from": {
"centerX": 0.49,
"centerY": 0.5,
"scale": 1.05
},
"to": {
"centerX": 0.55,
"centerY": 0.5,
"scale": 1.1
}
},
{
"path": "resources/w/cutscenes/cutscene2/scr2.png",
"path": "resources/w/cutscenes/cutscene2/cs2_gg004.png",
"width": 1280,
"height": 720,
"startMs": 956,
"endMs": 3522,
"fadeInMs": 1500,
"fadeOutMs": 500,
"startMs": 6050,
"endMs": 10100,
"fadeInMs": 1000,
"fadeOutMs": 1000,
"easing": "Linear",
"from": {
"centerX": 0.5,
"centerY": 0.6,
"scale": 1.3
"centerX": 0.47,
"centerY": 0.5,
"scale": 1.1
},
"to": {
"centerX": 0.5,
"centerY": 0.6,
"scale": 1.5
"centerX": 0.53,
"centerY": 0.5,
"scale": 1.2
}
},
{
"path": "resources/w/cutscenes/cutscene2/scr4.png",
"path": "resources/w/cutscenes/cutscene2/cs2_foreground.png",
"width": 1280,
"height": 720,
"startMs": 4849,
"endMs": 6964,
"startMs": 0,
"endMs": 10100,
"fadeInMs": 0,
"fadeOutMs": 500,
"fadeOutMs": 3000,
"easing": "Linear",
"from": {
"centerX": 0.5,
"centerY": 0.35,
"centerY": 0.5,
"scale": 1
},
"to": {
"centerX": 0.5,
"centerY": 0.35,
"scale": 1
}
},
{
"path": "resources/w/cutscenes/cutscene2/scr3.png",
"width": 1280,
"height": 720,
"startMs": 3594,
"endMs": 6478,
"fadeInMs": 500,
"fadeOutMs": 1500,
"easing": "Linear",
"from": {
"centerX": 0.5,
"centerY": 0.6,
"scale": 1
},
"to": {
"centerX": 0.5,
"centerY": 0.6,
"centerY": 0.5,
"scale": 1
}
}
@ -525,21 +605,21 @@
"lines": [
{
"speaker": "",
"text": начал делать презентацию по книге.",
"text": сел за компьютер и начал писать эссе по книге",
"durationMs": 3000,
"waitForConfirm": false,
"luaCallback": ""
},
{
"speaker": "",
"text": "Книга была такая скучная что я уснул.",
"durationMs": 3000,
"text": "Книга была очень скучной. Как будто кто-то наложил на книгу чары сна.",
"durationMs": 4000,
"waitForConfirm": false,
"luaCallback": ""
},
{
"speaker": "",
"text": "И я проснулся уже ночью...",
"text": "Неудивительно что я стал понемногу засыпать...",
"durationMs": 3000,
"waitForConfirm": false,
"luaCallback": ""

BIN
resources/w/cutscenes/cutscene2/cs2_background.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
resources/w/cutscenes/cutscene2/cs2_books.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
resources/w/cutscenes/cutscene2/cs2_chair.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
resources/w/cutscenes/cutscene2/cs2_foreground.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
resources/w/cutscenes/cutscene2/cs2_gg001.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
resources/w/cutscenes/cutscene2/cs2_gg002.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
resources/w/cutscenes/cutscene2/cs2_gg003.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
resources/w/cutscenes/cutscene2/cs2_gg004.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
resources/w/cutscenes/cutscene2/scr1.png (Stored with Git LFS)

Binary file not shown.

BIN
resources/w/cutscenes/cutscene2/scr2.png (Stored with Git LFS)

Binary file not shown.

BIN
resources/w/cutscenes/cutscene2/scr3.png (Stored with Git LFS)

Binary file not shown.

BIN
resources/w/cutscenes/cutscene2/scr4.png (Stored with Git LFS)

Binary file not shown.

View File

@ -1004,9 +1004,10 @@ namespace ZL
case SDLK_f:
currentLocation->dialogueSystem.startDialogue("phone_night_aiperi001");
break;
case SDLK_e:
//currentLocation->dialogueSystem.startCutscene("lection_cutscene001"); //.startDialogue("test_cutscene_pan_dialogue");
currentLocation->dialogueSystem.startCutscene("computer_cutscene001"); //.startDialogue("test_cutscene_pan_dialogue");
break;

View File

@ -10,6 +10,7 @@
#include <memory>
#include <cfloat>
#include <limits>
#include <random>
#include "GameConstants.h"
#include "Character.h"
#include "external/nlohmann/json.hpp"
@ -1325,6 +1326,23 @@ namespace ZL
static constexpr int64_t kReplanCooldownMs = 500;
static constexpr float kStuckMovementThreshold = 0.02f;
static constexpr int kStuckFrameThreshold = 4;
static constexpr float kStuckPauseMin = 0.5f;
static constexpr float kStuckPauseMax = 1.0f;
const float deltaS = static_cast<float>(deltaMs) / 1000.f;
// Tick down pause timers; replan NPCs when their pause expires.
for (auto it = stuckPauseRemainingS.begin(); it != stuckPauseRemainingS.end();) {
it->second -= deltaS;
if (it->second <= 0.f) {
if (!it->first->isPlayer) {
it->first->forceReplan();
}
it = stuckPauseRemainingS.erase(it);
} else {
++it;
}
}
for (auto it = replanCooldownRemainingMs.begin(); it != replanCooldownRemainingMs.end();) {
it->second -= deltaMs;
@ -1362,8 +1380,16 @@ namespace ZL
if (c->isMoving()) {
if (moved < kStuckMovementThreshold) {
if (++stuckFrameCounts[c] >= kStuckFrameThreshold) {
c->forceReplan();
c->stopInPlace();
stuckFrameCounts[c] = 0;
if (stuckPauseRemainingS.find(c) == stuckPauseRemainingS.end()) {
if (!c->isPlayer) {
static std::mt19937 rng(std::random_device{}());
std::uniform_real_distribution<float> pauseDist(kStuckPauseMin, kStuckPauseMax);
stuckPauseRemainingS[c] = pauseDist(rng);
}
// Player just stops; replan is not scheduled.
}
}
} else {
stuckFrameCounts[c] = 0;

View File

@ -183,6 +183,7 @@ namespace ZL
std::unordered_map<Character*, Eigen::Vector3f> lastCharacterPositions;
std::unordered_map<Character*, int64_t> replanCooldownRemainingMs;
std::unordered_map<Character*, int> stuckFrameCounts;
std::unordered_map<Character*, float> stuckPauseRemainingS;
static constexpr float NPC_BUMP_CALLBACK_COOLDOWN = 5.0f;
std::unordered_map<int, float> npcBumpedByPlayerCooldown;