Rework story engine flow and reorganize project docs
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
2026-04-06 23:19:00 +08:00
parent d678929064
commit ddcb5d5c8c
241 changed files with 19805 additions and 2478 deletions

View File

@@ -13,6 +13,7 @@ import {
getCharacterBottomOffsetPx,
getMonsterWorldLeft,
getPlayerWorldLeft,
HOSTILE_NPC_SCENE_INSET_PX,
SCENE_TRANSITION_LOWER_COMPANION_DELAY_S,
SCENE_TRANSITION_SPEED_PX_PER_S,
SCENE_TRANSITION_SPRITE_CLEARANCE_PX,
@@ -26,7 +27,6 @@ export function GameCanvasRuntime({
currentScenePreset,
worldType,
sceneHostileNpcs,
sceneMonsters,
playerX,
playerOffsetY,
playerFacing,
@@ -58,12 +58,8 @@ export function GameCanvasRuntime({
const stageLiftPx = 68;
const playerGroundOffset = playerCharacter?.groundOffsetY ?? 22;
const cameraAnchorX = scrollWorld ? playerX : PLAYER_BASE_X_METERS;
const resolvedSceneHostileNpcs =
sceneMonsters && sceneMonsters.length > 0
? sceneMonsters
: (sceneHostileNpcs ?? []);
const closestHostileNpcDistance = resolvedSceneHostileNpcs.length > 0
? Math.min(...resolvedSceneHostileNpcs.map(hostileNpc => Math.abs(hostileNpc.xMeters - playerX)))
const closestHostileNpcDistance = sceneHostileNpcs.length > 0
? Math.min(...sceneHostileNpcs.map(hostileNpc => Math.abs(hostileNpc.xMeters - playerX)))
: Infinity;
const escapeLead = scrollWorld ? Math.max(0, Math.min(1, (closestHostileNpcDistance - 1.2) / 3.4)) : 0;
const sideAnchor = '15%';
@@ -78,10 +74,13 @@ export function GameCanvasRuntime({
? playerMeleeLeft
: playerWorldLeft;
const monsterAnchorMeters = 3.2;
const getHostileNpcOuterLeft = (hostileNpc: (typeof resolvedSceneHostileNpcs)[number]) =>
hostileNpc.animation === 'attack' && hostileNpc.combatMode !== 'ranged' && !scrollWorld
? monsterMeleeLeft
: getMonsterWorldLeft(sideAnchor, hostileNpc.xMeters, cameraAnchorX, monsterAnchorMeters);
const getHostileNpcOuterLeft = (hostileNpc: (typeof sceneHostileNpcs)[number]) => {
const baseLeft =
hostileNpc.animation === 'attack' && hostileNpc.combatMode !== 'ranged' && !scrollWorld
? monsterMeleeLeft
: getMonsterWorldLeft(sideAnchor, hostileNpc.xMeters, cameraAnchorX, monsterAnchorMeters);
return `calc(${baseLeft} - ${HOSTILE_NPC_SCENE_INSET_PX}px)`;
};
const getPlayerEffectLeft = (effectX: number, offsetPx = 0) => {
const base = playerActionMode === 'melee' && !scrollWorld
? playerMeleeLeft
@@ -89,7 +88,7 @@ export function GameCanvasRuntime({
return `calc(${base} + 3.5rem + ${offsetPx}px)`;
};
const getHostileNpcEffectLeft = (effectX: number, hostileNpcId?: string, offsetPx = 0) => {
const effectHostileNpc = hostileNpcId ? resolvedSceneHostileNpcs.find(hostileNpc => hostileNpc.id === hostileNpcId) : null;
const effectHostileNpc = hostileNpcId ? sceneHostileNpcs.find(hostileNpc => hostileNpc.id === hostileNpcId) : null;
const base = effectHostileNpc
? getHostileNpcOuterLeft(effectHostileNpc)
: getMonsterWorldLeft(sideAnchor, effectX, cameraAnchorX, monsterAnchorMeters);
@@ -183,7 +182,7 @@ export function GameCanvasRuntime({
effectivePlayerAnimationState={effectivePlayerAnimationState}
shouldShowPlayerDialogueIcon={shouldShowPlayerDialogueIcon}
dialogueIndicator={dialogueIndicator}
sceneHostileNpcs={resolvedSceneHostileNpcs}
sceneCombatants={sceneHostileNpcs}
monsters={monsters}
getHostileNpcOuterLeft={getHostileNpcOuterLeft}
groundBottom={groundBottom}
@@ -198,7 +197,7 @@ export function GameCanvasRuntime({
activeCombatEffects={activeCombatEffects}
getPlayerEffectLeft={getPlayerEffectLeft}
getHostileNpcEffectLeft={getHostileNpcEffectLeft}
sceneHostileNpcs={resolvedSceneHostileNpcs}
sceneCombatants={sceneHostileNpcs}
playerCharacter={playerCharacter}
groundBottom={groundBottom}
stageLiftPx={stageLiftPx}