prendy
Version:
Make games with prerendered backdrops using babylonjs and repond
211 lines (210 loc) • 9.73 kB
JavaScript
import { meta } from "../meta";
import { getState, onNextTick, setState, stopNewEffect } from "repond";
import { addSubEvents, makeEventTypes, II } from "repond-events";
import { getCharDollStuff } from "../helpers/prendyUtils/characters";
import { getGlobalState, setGlobalState } from "../helpers/prendyUtils/global";
import { doWhenNowCamChanges, doWhenNowSegmentChanges, getSegmentFromSegmentRules } from "../helpers/prendyUtils/scene";
export const sceneEvents = makeEventTypes(({ event }) => ({
changeSegmentAtLoop: event({
run: async ({ place, segment }, { runMode, liveId }) => {
const segmentChangeEffectId = "changeSegmentAtLoop" + "_" + liveId;
const stopSegmentChangeEffect = () => stopNewEffect(segmentChangeEffectId);
const endEvent = () => {
stopSegmentChangeEffect();
setState({ liveEvents: { [liveId]: { goalEndTime: 0 } } });
};
if (runMode === "start") {
// NOTE WARNING This will probably break if goalSegmentNameAtLoop changes from somewhere else!!!
// to fix: could listen to changes to goalSegmentNameAtLoop
// might be fixed now that doWhenNowSegmentChanges listens to any change, instead of waiting for the expected segment name
// FIXME this can not work? (the async resolve part)
onNextTick(() => {
setGlobalState((state) => {
const { goalSegmentNameAtLoop } = state;
if (goalSegmentNameAtLoop) {
// TEMP resolve straight away if there's already a goalSegmentNameAtLoop
console.error("there was already a goalSegmentNameAtLoop when running changeSegmentAtLoop");
endEvent();
return {};
}
// End this event once the segment changes
doWhenNowSegmentChanges(segment, () => endEvent(), segmentChangeEffectId);
// Set the goal segment name for when the backdrop video loops
return { goalSegmentNameAtLoop: segment };
});
});
}
else {
stopSegmentChangeEffect();
}
},
params: { place: "", segment: "" },
duration: Infinity,
}),
changeCameraAtLoop: event({
run: async ({ place, cam }, { runMode, liveId }) => {
const camChangeEffectId = "changeCameraAtLoop" + "_" + liveId;
const stopCamChangeEffect = () => stopNewEffect(camChangeEffectId);
const endEvent = () => {
stopCamChangeEffect();
setState({ liveEvents: { [liveId]: { goalEndTime: 0 } } });
};
if (runMode === "start") {
setState((state) => {
const { goalCamNameAtLoop } = state.global.main;
if (goalCamNameAtLoop) {
// TEMP resolve straight away if there's already a goalCamNameAtLoop
console.error("there was already a goalSegmentNameAtLoop when running changeSegmentAtLoopAsync");
endEvent();
return {};
}
doWhenNowCamChanges(cam, () => endEvent(), camChangeEffectId);
return {
// AnyCameraName needed if there's only 1 place
global: { main: { goalCamNameAtLoop: cam } },
};
});
}
else {
stopCamChangeEffect();
}
},
params: { place: "", cam: "" },
duration: Infinity,
}),
hideWall: event({
run: async ({ place, wall, unhide }, { runMode }) => {
if (runMode !== "start")
return;
let wallShouldShow = false;
if (unhide)
wallShouldShow = true;
setState((state) => ({
places: {
[place]: { toggledWalls: { ...state.places[place].toggledWalls, [wall]: wallShouldShow } },
},
}));
},
params: {
place: "",
wall: "",
unhide: undefined,
},
}),
showStoryView: event({
run: async ({ hide = false }, { runMode, liveId, elapsedTime }) => {
if (runMode !== "start")
return;
const GUESSED_FADE_TIME = 1000; // note could listen to something like isFullyFaded and return here, but maybe a set time is okay
setGlobalState({ storyOverlayToggled: hide });
// await delay(GUESSED_FADE_TIME);
setState({ liveEvents: { [liveId]: { goalEndTime: elapsedTime + GUESSED_FADE_TIME } } });
},
params: { hide: undefined },
duration: Infinity,
}),
setSegment: event({
run: async ({ place, segment }, { runMode, isFirstAdd, liveId }) => {
if (isFirstAdd)
addSubEvents(liveId, [II("scene", "changeSegmentAtLoop", { place, segment })]);
},
params: { place: "", segment: "" },
}),
setCamera: event({
run: async ({ place, cam, whenToRun = "now" }, { runMode, isFirstAdd, liveId }) => {
if (whenToRun === "at loop") {
if (isFirstAdd)
addSubEvents(liveId, [II("scene", "changeCameraAtLoop", { place, cam })]);
}
else {
if (runMode === "start") {
const endEvent = () => setState({ liveEvents: { [liveId]: { goalEndTime: 0 } } });
const { nowPlaceName } = getState().global.main;
const { nowCamName } = getState().global.main;
// already on that camera
if (nowCamName === cam) {
endEvent();
return;
}
// AnyCameraName needed if there's only 1 place
setState({ global: { main: { goalCamName: cam } } }, () => endEvent());
}
}
},
params: {
place: "",
cam: "",
whenToRun: undefined,
},
}),
canTriggerCamera: event({
run: async ({ place, cam, whenToRun = "now" }, { runMode, isFirstAdd, liveId }) => {
if (whenToRun === "at loop") {
if (isFirstAdd)
addSubEvents(liveId, [II("scene", "changeCameraAtLoop", { place, cam })]);
}
else {
if (runMode === "start") {
const endEvent = () => setState({ liveEvents: { [liveId]: { goalEndTime: 0 } } });
const { nowPlaceName } = getState().global.main;
const { nowCamName } = getState().global.main;
// already on that camera
if (nowCamName === cam) {
endEvent();
return;
}
// AnyCameraName needed if there's only 1 place
setState({ global: { main: { goalCamName: cam } } }, () => endEvent());
}
}
},
params: {
place: "",
cam: "",
whenToRun: undefined,
},
}),
goToNewPlace: event({
run: async ({ toOption, who }, { runMode, liveId }) => {
if (runMode !== "start")
return;
const playerCharacter = getGlobalState().playerCharacter;
const character = who || playerCharacter;
const { placeInfoByName } = meta.assets;
// NOTE could include waitForPlaceFullyLoaded here so it can be awaited
let { toSpot, toPlace, toPositon, toCam, toSegment } = toOption;
const { dollName } = getCharDollStuff(character) ?? {};
if (!dollName)
return;
onNextTick(() => {
setState((state) => {
const newPlaceDefaultCamName = placeInfoByName[toPlace].cameraNames[0];
const nowSegmentName = state.global.main.nowSegmentName;
const placeInfo = placeInfoByName[toPlace];
// toSpot = toSpot ?? (placeInfo.spotNames[0] as SpotNameByPlace[T_PlaceName]);
toSpot = toSpot; // ?? (placeInfo.spotNames[0] as SpotNameByPlace[T_PlaceName]);
toPositon = toPositon;
toCam = toCam ?? placeInfo.cameraNames[0]; // types as a cam for the chosen place
toSegment = toSegment ?? placeInfo.segmentNames[0];
const foundRuleSegmentName = getSegmentFromSegmentRules(toPlace, toCam);
if (foundRuleSegmentName)
toSegment = foundRuleSegmentName;
return {
global: {
main: {
goalPlaceName: toPlace,
goalSegmentWhenGoalPlaceLoads: toSegment || nowSegmentName,
goalCamWhenNextPlaceLoads: toCam || newPlaceDefaultCamName,
},
},
dolls: { [dollName]: { goalPositionAtNewPlace: toPositon, goalSpotNameAtNewPlace: toSpot } },
};
});
});
},
params: {
toOption: { toPlace: "" },
who: undefined,
},
}),
}));