Implement scene-based chapter quest progression
Some checks failed
CI / verify (push) Has been cancelled
Some checks failed
CI / verify (push) Has been cancelled
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { getCharacterAnimationDurationMs } from '../data/characterCombat';
|
||||
import { getCharacterById } from '../data/characterPresets';
|
||||
@@ -12,6 +12,9 @@ type CompanionRecruitPresentation = {
|
||||
recruitToken: number;
|
||||
};
|
||||
|
||||
type CompanionPresentationMap = Record<string, CompanionRecruitPresentation>;
|
||||
type CompanionObserveFacingMap = Record<string, 'left' | 'right'>;
|
||||
|
||||
const RECRUIT_ENTRY_OFFSET_X = 148;
|
||||
const RECRUIT_ENTRY_OFFSET_Y = 10;
|
||||
const MIN_RECRUIT_PHASE_MS = 180;
|
||||
@@ -26,6 +29,53 @@ function randomObservePause() {
|
||||
return Math.round(OBSERVE_MIN_PAUSE_MS + Math.random() * (OBSERVE_MAX_PAUSE_MS - OBSERVE_MIN_PAUSE_MS));
|
||||
}
|
||||
|
||||
export function buildCompanionRenderStatesForGameState(params: {
|
||||
gameState: GameState;
|
||||
presentationByNpcId?: CompanionPresentationMap;
|
||||
observeFacingByNpcId?: CompanionObserveFacingMap;
|
||||
}) {
|
||||
const {
|
||||
gameState,
|
||||
presentationByNpcId = {},
|
||||
observeFacingByNpcId = {},
|
||||
} = params;
|
||||
|
||||
return (gameState.companions ?? [])
|
||||
.map((companion, index) => {
|
||||
const character = getCharacterById(companion.characterId);
|
||||
if (!character) return null;
|
||||
|
||||
const presentation = presentationByNpcId[companion.npcId];
|
||||
|
||||
return {
|
||||
npcId: companion.npcId,
|
||||
character,
|
||||
hp: companion.hp,
|
||||
maxHp: companion.maxHp,
|
||||
mana: companion.mana,
|
||||
maxMana: companion.maxMana,
|
||||
skillCooldowns: companion.skillCooldowns,
|
||||
animationState: presentation?.animationState ?? (
|
||||
companion.hp <= 0
|
||||
? companion.animationState ?? AnimationState.DIE
|
||||
: gameState.scrollWorld
|
||||
? AnimationState.RUN
|
||||
: gameState.inBattle
|
||||
? companion.animationState ?? AnimationState.IDLE
|
||||
: gameState.animationState
|
||||
),
|
||||
actionMode: companion.actionMode ?? 'idle',
|
||||
slot: index % 2 === 0 ? 'upper' : 'lower',
|
||||
facing: observeFacingByNpcId[companion.npcId] ?? gameState.playerFacing,
|
||||
entryOffsetX: (presentation?.entryOffsetX ?? 0) + (companion.offsetX ?? 0),
|
||||
entryOffsetY: (presentation?.entryOffsetY ?? 0) + (companion.offsetY ?? 0),
|
||||
transitionMs: presentation?.transitionMs ?? companion.transitionMs ?? 0,
|
||||
recruitToken: presentation?.recruitToken,
|
||||
} satisfies CompanionRenderState;
|
||||
})
|
||||
.filter(Boolean) as CompanionRenderState[];
|
||||
}
|
||||
|
||||
export function useNpcInteractionFlow(gameState: GameState) {
|
||||
const [presentationByNpcId, setPresentationByNpcId] = useState<Record<string, CompanionRecruitPresentation>>({});
|
||||
const [observeFacingByNpcId, setObserveFacingByNpcId] = useState<Record<string, 'left' | 'right'>>({});
|
||||
@@ -216,42 +266,16 @@ export function useNpcInteractionFlow(gameState: GameState) {
|
||||
});
|
||||
}, [gameState.ambientIdleMode, gameState.companions]);
|
||||
|
||||
const companionRenderStates: CompanionRenderState[] = (gameState.companions ?? [])
|
||||
.map((companion, index) => {
|
||||
const character = getCharacterById(companion.characterId);
|
||||
if (!character) return null;
|
||||
const buildCompanionRenderStates = useCallback((state: GameState) => buildCompanionRenderStatesForGameState({
|
||||
gameState: state,
|
||||
presentationByNpcId,
|
||||
observeFacingByNpcId,
|
||||
}), [observeFacingByNpcId, presentationByNpcId]);
|
||||
|
||||
const presentation = presentationByNpcId[companion.npcId];
|
||||
|
||||
return {
|
||||
npcId: companion.npcId,
|
||||
character,
|
||||
hp: companion.hp,
|
||||
maxHp: companion.maxHp,
|
||||
mana: companion.mana,
|
||||
maxMana: companion.maxMana,
|
||||
skillCooldowns: companion.skillCooldowns,
|
||||
animationState: presentation?.animationState ?? (
|
||||
companion.hp <= 0
|
||||
? companion.animationState ?? AnimationState.DIE
|
||||
: gameState.scrollWorld
|
||||
? AnimationState.RUN
|
||||
: gameState.inBattle
|
||||
? companion.animationState ?? AnimationState.IDLE
|
||||
: gameState.animationState
|
||||
),
|
||||
actionMode: companion.actionMode ?? 'idle',
|
||||
slot: index % 2 === 0 ? 'upper' : 'lower',
|
||||
facing: observeFacingByNpcId[companion.npcId] ?? gameState.playerFacing,
|
||||
entryOffsetX: (presentation?.entryOffsetX ?? 0) + (companion.offsetX ?? 0),
|
||||
entryOffsetY: (presentation?.entryOffsetY ?? 0) + (companion.offsetY ?? 0),
|
||||
transitionMs: presentation?.transitionMs ?? companion.transitionMs ?? 0,
|
||||
recruitToken: presentation?.recruitToken,
|
||||
};
|
||||
})
|
||||
.filter(Boolean) as CompanionRenderState[];
|
||||
const companionRenderStates = buildCompanionRenderStates(gameState);
|
||||
|
||||
return {
|
||||
companionRenderStates,
|
||||
buildCompanionRenderStates,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user