Add functions for cone and torus geometry calculations in whitebox-topology.ts, update dialogue handling in runtime-host.ts
This commit is contained in:
@@ -196,11 +196,14 @@ function getRadialPrismFaceVertexIds(
|
|||||||
faceId: RadialPrismFaceId
|
faceId: RadialPrismFaceId
|
||||||
): WhiteboxVertexId[] {
|
): WhiteboxVertexId[] {
|
||||||
if (faceId === "top") {
|
if (faceId === "top") {
|
||||||
return Array.from({ length: brush.sideCount }, (_, index) => `top-${brush.sideCount - 1 - index}` as const);
|
return Array.from({ length: brush.sideCount }, (_, index) => `top-${index}` as const);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (faceId === "bottom") {
|
if (faceId === "bottom") {
|
||||||
return Array.from({ length: brush.sideCount }, (_, index) => `bottom-${index}` as const);
|
return Array.from(
|
||||||
|
{ length: brush.sideCount },
|
||||||
|
(_, index) => `bottom-${brush.sideCount - 1 - index}` as const
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const sideIndex = Number(faceId.slice(5));
|
const sideIndex = Number(faceId.slice(5));
|
||||||
@@ -234,6 +237,72 @@ function getRadialPrismEdgeVertexIds(
|
|||||||
return [`bottom-${index}`, `bottom-${nextIndex}`];
|
return [`bottom-${index}`, `bottom-${nextIndex}`];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getConeFaceVertexIds(
|
||||||
|
brush: ConeBrush,
|
||||||
|
faceId: ConeFaceId
|
||||||
|
): WhiteboxVertexId[] {
|
||||||
|
if (faceId === "bottom") {
|
||||||
|
return Array.from(
|
||||||
|
{ length: brush.sideCount },
|
||||||
|
(_, index) => `bottom-${brush.sideCount - 1 - index}` as const
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sideIndex = Number(faceId.slice(5));
|
||||||
|
const nextIndex = (sideIndex + 1) % brush.sideCount;
|
||||||
|
return [`bottom-${sideIndex}`, `bottom-${nextIndex}`, "apex"];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getConeEdgeVertexIds(
|
||||||
|
edgeId: ConeEdgeId,
|
||||||
|
sideCount: number
|
||||||
|
): [ConeVertexId, ConeVertexId] {
|
||||||
|
if (edgeId.startsWith("bottom-")) {
|
||||||
|
const index = Number(edgeId.slice(7));
|
||||||
|
const nextIndex = (index + 1) % sideCount;
|
||||||
|
return [`bottom-${index}`, `bottom-${nextIndex}`];
|
||||||
|
}
|
||||||
|
|
||||||
|
const index = Number(edgeId.slice(5));
|
||||||
|
return [`bottom-${index}`, "apex"];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTorusFaceVertexIds(
|
||||||
|
brush: TorusBrush,
|
||||||
|
faceId: TorusFaceId
|
||||||
|
): WhiteboxVertexId[] {
|
||||||
|
const [majorIndex, tubeIndex] = parseIndexedWhiteboxId(faceId, "face-");
|
||||||
|
const nextMajorIndex = (majorIndex + 1) % brush.majorSegmentCount;
|
||||||
|
const nextTubeIndex = (tubeIndex + 1) % brush.tubeSegmentCount;
|
||||||
|
return [
|
||||||
|
`vertex-${majorIndex}-${tubeIndex}`,
|
||||||
|
`vertex-${majorIndex}-${nextTubeIndex}`,
|
||||||
|
`vertex-${nextMajorIndex}-${nextTubeIndex}`,
|
||||||
|
`vertex-${nextMajorIndex}-${tubeIndex}`
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTorusEdgeVertexIds(
|
||||||
|
brush: TorusBrush,
|
||||||
|
edgeId: TorusEdgeId
|
||||||
|
): [TorusVertexId, TorusVertexId] {
|
||||||
|
if (edgeId.startsWith("major-")) {
|
||||||
|
const [majorIndex, tubeIndex] = parseIndexedWhiteboxId(edgeId, "major-");
|
||||||
|
const nextMajorIndex = (majorIndex + 1) % brush.majorSegmentCount;
|
||||||
|
return [
|
||||||
|
`vertex-${majorIndex}-${tubeIndex}`,
|
||||||
|
`vertex-${nextMajorIndex}-${tubeIndex}`
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const [majorIndex, tubeIndex] = parseIndexedWhiteboxId(edgeId, "tube-");
|
||||||
|
const nextTubeIndex = (tubeIndex + 1) % brush.tubeSegmentCount;
|
||||||
|
return [
|
||||||
|
`vertex-${majorIndex}-${tubeIndex}`,
|
||||||
|
`vertex-${majorIndex}-${nextTubeIndex}`
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
export function getBrushFaceIds(brush: Brush): WhiteboxFaceId[] {
|
export function getBrushFaceIds(brush: Brush): WhiteboxFaceId[] {
|
||||||
switch (brush.kind) {
|
switch (brush.kind) {
|
||||||
case "box":
|
case "box":
|
||||||
|
|||||||
@@ -263,6 +263,7 @@ export interface RuntimeSceneTransitionRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface RuntimeDialogueState {
|
export interface RuntimeDialogueState {
|
||||||
|
npcEntityId: string;
|
||||||
dialogueId: string;
|
dialogueId: string;
|
||||||
title: string;
|
title: string;
|
||||||
lineId: string;
|
lineId: string;
|
||||||
@@ -712,9 +713,14 @@ export class RuntimeHost {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const npc =
|
||||||
|
this.runtimeScene.entities.npcs.find(
|
||||||
|
(candidate) => candidate.entityId === this.currentDialogue?.npcEntityId
|
||||||
|
) ?? null;
|
||||||
const dialogue =
|
const dialogue =
|
||||||
this.runtimeScene.dialogues.dialogues[this.currentDialogue.dialogueId] ??
|
npc?.dialogues.find(
|
||||||
null;
|
(candidate) => candidate.id === this.currentDialogue?.dialogueId
|
||||||
|
) ?? null;
|
||||||
|
|
||||||
if (dialogue === null) {
|
if (dialogue === null) {
|
||||||
this.setRuntimeDialogue(null);
|
this.setRuntimeDialogue(null);
|
||||||
@@ -729,7 +735,8 @@ export class RuntimeHost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.setRuntimeDialogue(
|
this.setRuntimeDialogue(
|
||||||
this.createRuntimeDialogueState(
|
this.createRuntimeNpcDialogueState(
|
||||||
|
this.currentDialogue.npcEntityId,
|
||||||
dialogue.id,
|
dialogue.id,
|
||||||
nextLineIndex,
|
nextLineIndex,
|
||||||
this.currentDialogue.source
|
this.currentDialogue.source
|
||||||
@@ -3287,6 +3294,9 @@ export class RuntimeHost {
|
|||||||
stopSound: (soundEmitterId) => {
|
stopSound: (soundEmitterId) => {
|
||||||
this.audioSystem.stopSound(soundEmitterId);
|
this.audioSystem.stopSound(soundEmitterId);
|
||||||
},
|
},
|
||||||
|
startNpcDialogue: (npcEntityId, dialogueId, source) => {
|
||||||
|
this.openRuntimeNpcDialogue(npcEntityId, dialogueId, source);
|
||||||
|
},
|
||||||
startDialogue: (dialogueId, source) => {
|
startDialogue: (dialogueId, source) => {
|
||||||
this.openRuntimeDialogue(dialogueId, source);
|
this.openRuntimeDialogue(dialogueId, source);
|
||||||
},
|
},
|
||||||
@@ -3311,6 +3321,50 @@ export class RuntimeHost {
|
|||||||
this.interactionPromptHandler?.(prompt);
|
this.interactionPromptHandler?.(prompt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private createRuntimeNpcDialogueState(
|
||||||
|
npcEntityId: string,
|
||||||
|
dialogueId: string,
|
||||||
|
lineIndex: number,
|
||||||
|
source: RuntimeDialogueStartSource
|
||||||
|
): RuntimeDialogueState | null {
|
||||||
|
if (this.runtimeScene === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const npc =
|
||||||
|
this.runtimeScene.entities.npcs.find(
|
||||||
|
(candidate) => candidate.entityId === npcEntityId
|
||||||
|
) ?? null;
|
||||||
|
|
||||||
|
if (npc === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dialogue = npc.dialogues.find((candidate) => candidate.id === dialogueId);
|
||||||
|
|
||||||
|
if (dialogue === undefined) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const line = dialogue.lines[lineIndex];
|
||||||
|
|
||||||
|
if (line === undefined) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
npcEntityId,
|
||||||
|
dialogueId,
|
||||||
|
title: dialogue.title,
|
||||||
|
lineId: line.id,
|
||||||
|
lineIndex,
|
||||||
|
lineCount: dialogue.lines.length,
|
||||||
|
speakerName: line.speakerName,
|
||||||
|
text: line.text,
|
||||||
|
source
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private createRuntimeDialogueState(
|
private createRuntimeDialogueState(
|
||||||
dialogueId: string,
|
dialogueId: string,
|
||||||
lineIndex: number,
|
lineIndex: number,
|
||||||
@@ -3333,6 +3387,7 @@ export class RuntimeHost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
npcEntityId: source.sourceEntityId ?? "legacy-dialogue-source",
|
||||||
dialogueId,
|
dialogueId,
|
||||||
title: dialogue.title,
|
title: dialogue.title,
|
||||||
lineId: line.id,
|
lineId: line.id,
|
||||||
@@ -3346,6 +3401,7 @@ export class RuntimeHost {
|
|||||||
|
|
||||||
private setRuntimeDialogue(dialogue: RuntimeDialogueState | null) {
|
private setRuntimeDialogue(dialogue: RuntimeDialogueState | null) {
|
||||||
if (
|
if (
|
||||||
|
this.currentDialogue?.npcEntityId === dialogue?.npcEntityId &&
|
||||||
this.currentDialogue?.dialogueId === dialogue?.dialogueId &&
|
this.currentDialogue?.dialogueId === dialogue?.dialogueId &&
|
||||||
this.currentDialogue?.lineId === dialogue?.lineId &&
|
this.currentDialogue?.lineId === dialogue?.lineId &&
|
||||||
this.currentDialogue?.lineIndex === dialogue?.lineIndex &&
|
this.currentDialogue?.lineIndex === dialogue?.lineIndex &&
|
||||||
@@ -3389,6 +3445,65 @@ export class RuntimeHost {
|
|||||||
this.setRuntimeDialogue(dialogue);
|
this.setRuntimeDialogue(dialogue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private openRuntimeNpcDialogue(
|
||||||
|
npcEntityId: string,
|
||||||
|
dialogueId: string | null,
|
||||||
|
source: RuntimeDialogueStartSource = {
|
||||||
|
kind: "direct",
|
||||||
|
sourceEntityId: null,
|
||||||
|
linkId: null,
|
||||||
|
trigger: null
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
if (this.runtimeScene === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const npc =
|
||||||
|
this.runtimeScene.entities.npcs.find(
|
||||||
|
(candidate) => candidate.entityId === npcEntityId
|
||||||
|
) ?? null;
|
||||||
|
|
||||||
|
if (npc === null) {
|
||||||
|
console.warn(`dialogue: missing npc ${npcEntityId}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolvedDialogueId =
|
||||||
|
dialogueId ??
|
||||||
|
npc.defaultDialogueId ??
|
||||||
|
npc.dialogues[0]?.id ??
|
||||||
|
null;
|
||||||
|
|
||||||
|
if (resolvedDialogueId === null) {
|
||||||
|
console.warn(`dialogue: npc ${npcEntityId} has no dialogue to speak`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.currentDialogue?.npcEntityId === npcEntityId &&
|
||||||
|
this.currentDialogue?.dialogueId === resolvedDialogueId
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dialogue = this.createRuntimeNpcDialogueState(
|
||||||
|
npcEntityId,
|
||||||
|
resolvedDialogueId,
|
||||||
|
0,
|
||||||
|
source
|
||||||
|
);
|
||||||
|
|
||||||
|
if (dialogue === null) {
|
||||||
|
console.warn(
|
||||||
|
`dialogue: npc ${npcEntityId} is missing dialogue ${resolvedDialogueId}`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setRuntimeDialogue(dialogue);
|
||||||
|
}
|
||||||
|
|
||||||
private resolveInteractionPrompt(): RuntimeInteractionPrompt | null {
|
private resolveInteractionPrompt(): RuntimeInteractionPrompt | null {
|
||||||
if (
|
if (
|
||||||
this.runtimeScene === null ||
|
this.runtimeScene === null ||
|
||||||
|
|||||||
Reference in New Issue
Block a user