Files
webeditor3d/src/runtime-three/runtime-project-scheduler.ts

176 lines
4.9 KiB
TypeScript
Raw Normal View History

import {
applyControlEffectToResolvedState,
cloneControlEffect,
cloneRuntimeResolvedControlState,
createActorControlTargetRef,
createDefaultResolvedControlSource,
createSchedulerResolvedControlSource,
createSetActorPresenceControlEffect,
type RuntimeResolvedControlState,
type SetActorPresenceControlEffect
} from "../controls/control-surface";
import {
cloneProjectScheduler,
compareProjectScheduleRoutinePriority,
isProjectScheduleRoutineActiveAt,
type ProjectScheduler,
type ProjectScheduleRoutine
} from "../scheduler/project-scheduler";
export interface RuntimeResolvedActorScheduleState {
actorId: string;
hasRules: boolean;
active: boolean;
activeRoutineId: string | null;
activeRoutineTitle: string | null;
effect: SetActorPresenceControlEffect | null;
}
export interface RuntimeResolvedProjectScheduleState {
actors: RuntimeResolvedActorScheduleState[];
}
export interface RuntimeProjectSchedulerState {
document: ProjectScheduler;
resolved: RuntimeResolvedProjectScheduleState;
}
function cloneRuntimeResolvedActorScheduleState(
state: RuntimeResolvedActorScheduleState
): RuntimeResolvedActorScheduleState {
return {
actorId: state.actorId,
hasRules: state.hasRules,
active: state.active,
activeRoutineId: state.activeRoutineId,
activeRoutineTitle: state.activeRoutineTitle,
effect: state.effect === null ? null : cloneControlEffect(state.effect)
};
}
export function cloneRuntimeResolvedProjectScheduleState(
state: RuntimeResolvedProjectScheduleState
): RuntimeResolvedProjectScheduleState {
return {
actors: state.actors.map(cloneRuntimeResolvedActorScheduleState)
};
}
export function createEmptyRuntimeResolvedProjectScheduleState(): RuntimeResolvedProjectScheduleState {
return {
actors: []
};
}
export function createRuntimeProjectSchedulerState(options: {
document: ProjectScheduler;
resolved?: RuntimeResolvedProjectScheduleState;
}): RuntimeProjectSchedulerState {
return {
document: cloneProjectScheduler(options.document),
resolved: cloneRuntimeResolvedProjectScheduleState(
options.resolved ?? createEmptyRuntimeResolvedProjectScheduleState()
)
};
}
function resolveActorScheduleRules(
scheduler: ProjectScheduler,
actorId: string
): ProjectScheduleRoutine[] {
return Object.values(scheduler.routines)
.filter(
(routine) =>
routine.enabled &&
routine.target.kind === "actor" &&
routine.target.actorId === actorId &&
routine.effect.type === "setActorPresence"
)
.sort(compareProjectScheduleRoutinePriority);
}
export function resolveRuntimeActorScheduleState(options: {
scheduler: ProjectScheduler;
actorId: string;
dayNumber: number;
timeOfDayHours: number;
}): RuntimeResolvedActorScheduleState {
const actorRules = resolveActorScheduleRules(options.scheduler, options.actorId);
const activeRoutine =
actorRules.find((routine) =>
isProjectScheduleRoutineActiveAt(
routine,
options.dayNumber,
options.timeOfDayHours
)
) ?? null;
if (activeRoutine === null) {
return {
actorId: options.actorId,
hasRules: actorRules.length > 0,
active: actorRules.length === 0,
activeRoutineId: null,
activeRoutineTitle: null,
effect: null
};
}
return {
actorId: options.actorId,
hasRules: true,
active: activeRoutine.effect.active,
activeRoutineId: activeRoutine.id,
activeRoutineTitle: activeRoutine.title,
effect: cloneControlEffect(activeRoutine.effect)
};
}
export function resolveRuntimeProjectScheduleState(options: {
scheduler: ProjectScheduler;
actorIds: string[];
dayNumber: number;
timeOfDayHours: number;
}): RuntimeResolvedProjectScheduleState {
const actorIds = [...new Set(options.actorIds)];
return {
actors: actorIds.map((actorId) =>
resolveRuntimeActorScheduleState({
scheduler: options.scheduler,
actorId,
dayNumber: options.dayNumber,
timeOfDayHours: options.timeOfDayHours
})
)
};
}
export function applyRuntimeProjectScheduleToControlState(
resolved: RuntimeResolvedControlState,
schedule: RuntimeResolvedProjectScheduleState
): RuntimeResolvedControlState {
let nextResolved = cloneRuntimeResolvedControlState(resolved);
nextResolved.discrete = nextResolved.discrete.filter(
(state) => state.type !== "actorPresence"
);
for (const actorState of schedule.actors) {
const effect =
actorState.effect ??
createSetActorPresenceControlEffect({
target: createActorControlTargetRef(actorState.actorId),
active: actorState.active
});
nextResolved = applyControlEffectToResolvedState(
nextResolved,
effect,
actorState.activeRoutineId === null
? createDefaultResolvedControlSource()
: createSchedulerResolvedControlSource(actorState.activeRoutineId)
);
}
return nextResolved;
}