Add RunSequenceAction and related functionality
This commit is contained in:
@@ -53,6 +53,11 @@ export interface StartDialogueAction {
|
||||
dialogueId: string;
|
||||
}
|
||||
|
||||
export interface RunSequenceAction {
|
||||
type: "runSequence";
|
||||
sequenceId: string;
|
||||
}
|
||||
|
||||
export interface ControlInteractionAction {
|
||||
type: "control";
|
||||
effect: ControlEffect;
|
||||
@@ -66,6 +71,7 @@ export type InteractionAction =
|
||||
| PlaySoundAction
|
||||
| StopSoundAction
|
||||
| StartDialogueAction
|
||||
| RunSequenceAction
|
||||
| ControlInteractionAction;
|
||||
|
||||
export interface InteractionLink {
|
||||
@@ -134,6 +140,13 @@ export interface CreateControlInteractionLinkOptions {
|
||||
effect: ControlEffect;
|
||||
}
|
||||
|
||||
export interface CreateRunSequenceInteractionLinkOptions {
|
||||
id?: string;
|
||||
sourceEntityId: string;
|
||||
trigger?: InteractionTriggerKind;
|
||||
sequenceId: string;
|
||||
}
|
||||
|
||||
function assertNonEmptyString(value: string, label: string) {
|
||||
if (value.trim().length === 0) {
|
||||
throw new Error(`${label} must be non-empty.`);
|
||||
@@ -180,6 +193,11 @@ function cloneAction(action: InteractionAction): InteractionAction {
|
||||
type: "startDialogue",
|
||||
dialogueId: action.dialogueId
|
||||
};
|
||||
case "runSequence":
|
||||
return {
|
||||
type: "runSequence",
|
||||
sequenceId: action.sequenceId
|
||||
};
|
||||
case "control":
|
||||
return {
|
||||
type: "control",
|
||||
@@ -357,6 +375,23 @@ export function createControlInteractionLink(
|
||||
};
|
||||
}
|
||||
|
||||
export function createRunSequenceInteractionLink(
|
||||
options: CreateRunSequenceInteractionLinkOptions
|
||||
): InteractionLink {
|
||||
assertNonEmptyString(options.sourceEntityId, "Interaction source entity id");
|
||||
assertNonEmptyString(options.sequenceId, "Sequence id");
|
||||
|
||||
return {
|
||||
id: options.id ?? createOpaqueId("interaction-link"),
|
||||
sourceEntityId: options.sourceEntityId,
|
||||
trigger: options.trigger ?? "enter",
|
||||
action: {
|
||||
type: "runSequence",
|
||||
sequenceId: options.sequenceId
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function getInteractionActionControlEffect(
|
||||
action: InteractionAction
|
||||
): ControlEffect | null {
|
||||
@@ -384,6 +419,7 @@ export function getInteractionActionControlEffect(
|
||||
target: createSoundEmitterControlTargetRef(action.targetSoundEmitterId)
|
||||
});
|
||||
case "startDialogue":
|
||||
case "runSequence":
|
||||
return null;
|
||||
case "control":
|
||||
return cloneControlEffect(action.effect);
|
||||
@@ -409,6 +445,8 @@ export function getInteractionActionLabel(action: InteractionAction): string {
|
||||
return "Stop Sound";
|
||||
case "startDialogue":
|
||||
return "Start Dialogue";
|
||||
case "runSequence":
|
||||
return "Run Sequence";
|
||||
case "control":
|
||||
return getControlEffectLabel(action.effect);
|
||||
}
|
||||
@@ -479,6 +517,10 @@ export function areInteractionLinksEqual(
|
||||
left.action.dialogueId ===
|
||||
(right.action as StartDialogueAction).dialogueId
|
||||
);
|
||||
case "runSequence":
|
||||
return (
|
||||
left.action.sequenceId === (right.action as RunSequenceAction).sequenceId
|
||||
);
|
||||
case "control":
|
||||
return areControlEffectsEqual(
|
||||
left.action.effect,
|
||||
|
||||
@@ -46,6 +46,7 @@ export interface ProjectScheduleRoutine {
|
||||
title: string;
|
||||
enabled: boolean;
|
||||
target: ControlTargetRef;
|
||||
sequenceId: string | null;
|
||||
days: ProjectScheduleDaySelection;
|
||||
startHour: number;
|
||||
endHour: number;
|
||||
@@ -119,6 +120,22 @@ function normalizeProjectScheduleTitle(title: string | undefined): string {
|
||||
return normalizedTitle;
|
||||
}
|
||||
|
||||
function normalizeProjectScheduleSequenceId(
|
||||
sequenceId: string | null | undefined
|
||||
): string | null {
|
||||
if (sequenceId === undefined || sequenceId === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const normalizedSequenceId = sequenceId.trim();
|
||||
|
||||
if (normalizedSequenceId.length === 0) {
|
||||
throw new Error("Project schedule routine sequence ids must be non-empty.");
|
||||
}
|
||||
|
||||
return normalizedSequenceId;
|
||||
}
|
||||
|
||||
function normalizeProjectScheduleHours(value: number, label: string): number {
|
||||
assertFiniteNumber(value, label);
|
||||
return normalizeTimeOfDayHours(value);
|
||||
@@ -249,7 +266,16 @@ export function createProjectScheduleRoutine(
|
||||
overrides: Partial<
|
||||
Pick<
|
||||
ProjectScheduleRoutine,
|
||||
"id" | "title" | "enabled" | "target" | "days" | "startHour" | "endHour" | "priority" | "effects"
|
||||
| "id"
|
||||
| "title"
|
||||
| "enabled"
|
||||
| "target"
|
||||
| "sequenceId"
|
||||
| "days"
|
||||
| "startHour"
|
||||
| "endHour"
|
||||
| "priority"
|
||||
| "effects"
|
||||
>
|
||||
> & {
|
||||
effect?: ControlEffect;
|
||||
@@ -278,6 +304,7 @@ export function createProjectScheduleRoutine(
|
||||
title: normalizeProjectScheduleTitle(overrides.title),
|
||||
enabled: overrides.enabled ?? true,
|
||||
target,
|
||||
sequenceId: normalizeProjectScheduleSequenceId(overrides.sequenceId),
|
||||
days: normalizeProjectScheduleDays(overrides.days),
|
||||
startHour,
|
||||
endHour,
|
||||
@@ -308,6 +335,7 @@ export function cloneProjectScheduleRoutine(
|
||||
title: routine.title,
|
||||
enabled: routine.enabled,
|
||||
target: cloneControlTargetRef(routine.target),
|
||||
sequenceId: routine.sequenceId,
|
||||
days: cloneProjectScheduleDaySelection(routine.days),
|
||||
startHour: routine.startHour,
|
||||
endHour: routine.endHour,
|
||||
@@ -357,6 +385,7 @@ export function areProjectScheduleRoutinesEqual(
|
||||
left.title === right.title &&
|
||||
left.enabled === right.enabled &&
|
||||
getControlTargetRefKey(left.target) === getControlTargetRefKey(right.target) &&
|
||||
left.sequenceId === right.sequenceId &&
|
||||
areProjectScheduleDaySelectionsEqual(left.days, right.days) &&
|
||||
left.startHour === right.startHour &&
|
||||
left.endHour === right.endHour &&
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
getControlEffectLabel,
|
||||
cloneControlEffect,
|
||||
type ControlEffect
|
||||
} from "../controls/control-surface";
|
||||
@@ -39,6 +40,15 @@ export interface ToggleVisibilitySequenceStep {
|
||||
visible?: boolean;
|
||||
}
|
||||
|
||||
type SequenceDefinitionLike = {
|
||||
id: string;
|
||||
steps: SequenceStep[];
|
||||
};
|
||||
|
||||
type SequenceLibraryLike = {
|
||||
sequences: Record<string, SequenceDefinitionLike>;
|
||||
};
|
||||
|
||||
export type HeldSequenceStep = HeldControlSequenceStep;
|
||||
|
||||
export type ImpulseSequenceStep =
|
||||
@@ -83,8 +93,50 @@ export function cloneSequenceSteps(steps: SequenceStep[]): SequenceStep[] {
|
||||
return steps.map(cloneSequenceStep);
|
||||
}
|
||||
|
||||
export function getSequenceStepLabel(step: SequenceStep): string {
|
||||
switch (step.type) {
|
||||
case "controlEffect":
|
||||
return `${step.stepClass === "held" ? "Held" : "Impulse"}: ${getControlEffectLabel(step.effect)}`;
|
||||
case "startDialogue":
|
||||
return "Impulse: Start Dialogue";
|
||||
case "teleportPlayer":
|
||||
return "Impulse: Teleport Player";
|
||||
case "toggleVisibility":
|
||||
return "Impulse: Toggle Visibility";
|
||||
}
|
||||
}
|
||||
|
||||
export function getHeldSequenceSteps(
|
||||
steps: readonly SequenceStep[]
|
||||
): HeldSequenceStep[] {
|
||||
return steps
|
||||
.filter((step): step is HeldSequenceStep => step.stepClass === "held")
|
||||
.map(cloneSequenceStep) as HeldSequenceStep[];
|
||||
}
|
||||
|
||||
export function getImpulseSequenceSteps(
|
||||
steps: readonly SequenceStep[]
|
||||
): ImpulseSequenceStep[] {
|
||||
return steps
|
||||
.filter((step): step is ImpulseSequenceStep => step.stepClass === "impulse")
|
||||
.map(cloneSequenceStep) as ImpulseSequenceStep[];
|
||||
}
|
||||
|
||||
export function getProjectSequenceHeldSteps(
|
||||
sequence: SequenceDefinitionLike
|
||||
): HeldSequenceStep[] {
|
||||
return getHeldSequenceSteps(sequence.steps);
|
||||
}
|
||||
|
||||
export function getProjectSequenceImpulseSteps(
|
||||
sequence: SequenceDefinitionLike
|
||||
): ImpulseSequenceStep[] {
|
||||
return getImpulseSequenceSteps(sequence.steps);
|
||||
}
|
||||
|
||||
export function getInteractionLinkImpulseSteps(
|
||||
link: InteractionLink
|
||||
link: InteractionLink,
|
||||
sequenceLibrary?: SequenceLibraryLike | null
|
||||
): ImpulseSequenceStep[] {
|
||||
const controlEffect = getInteractionActionControlEffect(link.action);
|
||||
|
||||
@@ -124,6 +176,11 @@ export function getInteractionLinkImpulseSteps(
|
||||
dialogueId: link.action.dialogueId
|
||||
}
|
||||
];
|
||||
case "runSequence": {
|
||||
const sequence =
|
||||
sequenceLibrary?.sequences[link.action.sequenceId] ?? null;
|
||||
return sequence === null ? [] : getProjectSequenceImpulseSteps(sequence);
|
||||
}
|
||||
case "playAnimation":
|
||||
case "stopAnimation":
|
||||
case "playSound":
|
||||
@@ -136,14 +193,24 @@ export function getInteractionLinkImpulseSteps(
|
||||
}
|
||||
|
||||
export function getInteractionLinkSequenceSteps(
|
||||
link: InteractionLink
|
||||
link: InteractionLink,
|
||||
sequenceLibrary?: SequenceLibraryLike | null
|
||||
): SequenceStep[] {
|
||||
return getInteractionLinkImpulseSteps(link);
|
||||
return getInteractionLinkImpulseSteps(link, sequenceLibrary);
|
||||
}
|
||||
|
||||
export function getProjectScheduleRoutineHeldSteps(
|
||||
routine: ProjectScheduleRoutine
|
||||
routine: ProjectScheduleRoutine,
|
||||
sequenceLibrary?: SequenceLibraryLike | null
|
||||
): HeldSequenceStep[] {
|
||||
if (routine.sequenceId !== null) {
|
||||
const sequence = sequenceLibrary?.sequences[routine.sequenceId] ?? null;
|
||||
|
||||
if (sequence !== null) {
|
||||
return getProjectSequenceHeldSteps(sequence);
|
||||
}
|
||||
}
|
||||
|
||||
return routine.effects.map((effect) => ({
|
||||
stepClass: "held" as const,
|
||||
type: "controlEffect" as const,
|
||||
@@ -152,9 +219,10 @@ export function getProjectScheduleRoutineHeldSteps(
|
||||
}
|
||||
|
||||
export function getProjectScheduleRoutineSequenceSteps(
|
||||
routine: ProjectScheduleRoutine
|
||||
routine: ProjectScheduleRoutine,
|
||||
sequenceLibrary?: SequenceLibraryLike | null
|
||||
): SequenceStep[] {
|
||||
return getProjectScheduleRoutineHeldSteps(routine);
|
||||
return getProjectScheduleRoutineHeldSteps(routine, sequenceLibrary);
|
||||
}
|
||||
|
||||
export function getHeldSequenceControlEffects(
|
||||
|
||||
Reference in New Issue
Block a user