UNPKG

prendy

Version:

Make games with prerendered backdrops using babylonjs and repond

141 lines (140 loc) 6.49 kB
import delay from "delay"; import { getState, onNextTick, setState } from "repond"; import { meta } from "../../meta"; import { get2DAngleFromCharacterToSpot, getCharDollStuff } from "../prendyUtils/characters"; import { setGlobalState } from "../prendyUtils/global"; import { doWhenNowCamChanges, doWhenNowSegmentChanges, getSegmentFromSegmentRules } from "../prendyUtils/scene"; async function changeSegmentAtLoop(_place, newSegmentName) { // 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) return new Promise((resolve, _reject) => { 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 changeSegmentAtLoopAsync"); resolve(); return {}; } doWhenNowSegmentChanges(newSegmentName, () => resolve()); return { goalSegmentNameAtLoop: newSegmentName }; }); }); }); } async function changeCameraAtLoop(_place, newCamName) { return new Promise((resolve, _reject) => { 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"); resolve(); return {}; } doWhenNowCamChanges(newCamName, () => resolve()); return { // AnyCameraName needed if there's only 1 place global: { main: { goalCamNameAtLoop: newCamName } }, }; }); }); } export function lookAtSpot(place, spot, character) { const { playerCharacter } = getState().global.main; const editedCharacter = character ?? playerCharacter; const charDollStuff = getCharDollStuff(editedCharacter); const { dollName } = charDollStuff; const angle = get2DAngleFromCharacterToSpot(editedCharacter, place, spot); setState({ dolls: { [dollName]: { rotationYGoal: angle } } }); } export function hideWallIf(placeName, wallName, isDisabled) { // NOTE could update to set properties in a loop to avoid spreading setState((state) => ({ places: { [placeName]: { toggledWalls: { ...state.places[placeName].toggledWalls, [wallName]: !isDisabled } }, }, })); } export async function showStoryView(isVisible = true) { const GUESSED_FADE_TIME = 1000; // note could listen to something like isFullyFaded and return here, but maybe a set time is okay setGlobalState({ storyOverlayToggled: !isVisible }); await delay(GUESSED_FADE_TIME); } export function setSegment(_placeName, segmentName // whenToRun: "now" | "at loop" = "at loop" ) { return new Promise((resolve, _reject) => { // always sets segment at loop sicne it probably shouldn't change halfway through // if (whenToRun === "now") { // const { nowSegmentName } = getState().global.main; // if (nowSegmentName === segmentName) { // console.warn("already on that segment"); // resolve(); // return; // } // setGlobalState({ goalSegmentName: segmentName }, () => resolve()); // } else if (whenToRun === "at loop") { changeSegmentAtLoop(_placeName, segmentName).finally(() => resolve()); // } }); } export function setCamera(_placeName, cameraName, whenToRun = "now") { return new Promise((resolve, _reject) => { if (whenToRun === "now") { const { nowPlaceName } = getState().global.main; const { nowCamName } = getState().global.main; // already on that camera if (nowCamName === cameraName) { resolve(); return; } // AnyCameraName needed if there's only 1 place setState({ global: { main: { goalCamName: cameraName } } }, () => resolve()); } else if (whenToRun === "at loop") { changeCameraAtLoop(_placeName, cameraName).finally(() => resolve()); } }); } export function goToNewPlace(toOption, charNameParam) { const charName = charNameParam || meta.assets.characterNames[0]; const { placeInfoByName } = meta.assets; // NOTE could include waitForPlaceFullyLoaded here so it can be awaited let { toSpot, toPlace, toPositon, toCam, toSegment } = toOption; const { dollName } = getCharDollStuff(charName) ?? {}; 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 } }, }; }); }); } // ideas // disable/enable camCubes ? // start/stop characterFollowinglayer ? // }