prendy
Version:
Make games with prerendered backdrops using babylonjs and repond
164 lines (163 loc) • 8.02 kB
JavaScript
import { forEach } from "chootils/dist/loops";
import { getRefs, getState, makeEffects, makeParamEffects, setState } from "repond";
import pointIsInside from "../helpers/babylonjs/pointIsInside";
import { focusSlateOnFocusedDoll } from "../helpers/babylonjs/slate";
import { meta } from "../meta";
import { startParamEffectsGroup, stopParamEffectsGroup } from "repond";
export const characterParamEffects = makeParamEffects({ characterName: "", dollName: "" }, // defaultParams
({ itemEffect, params: { characterName, dollName } }) => ({
whenPositionChanges: itemEffect({
// nameThisRule: `doll_whenWholePlaceFinishesLoading${dollName}_${modelName}`,
run({ itemRefs }) {
const refs = getRefs();
const placesRefs = refs.places;
const { placeInfoByName } = meta.assets;
// TODO
// only update the collider stuff here
// Also listen to dolls positions, and return if not the same dollName (easier than dynamic rules for now)
// if (samePoints(newValue ?? defaultPosition, prevValue ?? defaultPosition)) return;
// NOTE this rule used to use effect instead of itemEffect, which has typescript errors, but was possibly working? or not working and returning early
if (!itemRefs.meshRef)
return;
const { nowPlaceName, loadingOverlayToggled, focusedDoll } = getState().global.main;
const nowPlaceInfo = placeInfoByName[nowPlaceName];
const triggerNames = nowPlaceInfo.triggerNames;
const cameraNames = nowPlaceInfo.cameraNames;
if (loadingOverlayToggled === true)
return;
const characterState = getState().characters[characterName];
// --------------------------
// check cam cubes
// TODO only check for the player character?
// const { playerCharacter } = getState().global.main;
// if (charName !== playerCharacter) return; // NOTE maybe dynamic rule better (since the listener wont run for other characters)
let newAtTheseCamCubes = {};
const currentAtCamCubes = characterState.atCamCubes;
let atCamCubesHasChanged = false;
forEach(cameraNames, (loopedCameraName) => {
const camsRefs = placesRefs[nowPlaceName].camsRefs[loopedCameraName];
let isAtLoopedCamCube = false;
forEach(camsRefs.camCubeMeshes, (loopedMesh) => {
const isAtLoopedCamCubeMesh = !!(itemRefs.meshRef &&
loopedMesh &&
camsRefs.isTriggerable &&
pointIsInside(itemRefs.meshRef.position, loopedMesh));
if (isAtLoopedCamCubeMesh) {
isAtLoopedCamCube = true;
}
});
newAtTheseCamCubes[loopedCameraName] = isAtLoopedCamCube;
if (currentAtCamCubes[loopedCameraName] !== newAtTheseCamCubes[loopedCameraName]) {
atCamCubesHasChanged = true;
}
});
if (atCamCubesHasChanged) {
setState({
characters: {
[characterName]: { atCamCubes: newAtTheseCamCubes },
},
});
}
// --------------------------
// check triggers
let newAtTheseTriggers = {};
const currentAtTriggers = characterState.atTriggers;
let atTriggersHasChanged = false;
forEach(triggerNames, (loopedTriggerName) => {
const loopedMesh = placesRefs[nowPlaceName].triggerMeshes[loopedTriggerName];
const isAtLoopedTrigger = !!(itemRefs.meshRef &&
loopedMesh &&
pointIsInside(itemRefs.meshRef.position, loopedMesh));
newAtTheseTriggers[loopedTriggerName] = isAtLoopedTrigger;
if (currentAtTriggers[loopedTriggerName] !== newAtTheseTriggers[loopedTriggerName]) {
atTriggersHasChanged = true;
}
});
if (atTriggersHasChanged) {
setState({
characters: {
[characterName]: { atTriggers: newAtTheseTriggers },
},
});
}
if (dollName === focusedDoll) {
focusSlateOnFocusedDoll();
}
},
check: { type: "dolls", prop: "position", id: dollName },
atStepEnd: true, // so it only runs once (it sometimes ran twice with "derive" (without the "beforePainting" flow I think))
step: "checkCollisions",
// NOTE "becomes" isn't working for dynamic rules?
// becomes: (position, prevPosition) => {
// console.log("becomes", position, prevPosition);
// return samePoints(position ?? defaultPosition, prevPosition ?? defaultPosition);
// },
}),
// whenInRangeChanges: itemEffect(
// ({
// characterName,
// dollName,
// }: {
// characterName: CharacterName;
// dollName: DollName;
// }) => ({
// // nameThisRule: `doll_whenWholePlaceFinishesLoading${dollName}_${modelName}`,
// run({ newValue: newInRange, itemId }) {
// // console.log(itemId, " in range");
// // console.log(newInRange.cat);
// },
// check: { type: "dolls", prop: "inRange", id: dollName },
// atStepEnd: true,
// })
// ),
}));
// FIXME
// maybe allow repond to run 'addedOrRemoved' rules for initialState?
// NOTE rules can be manually triggered atleast, but the rule might not know an item was added
// TODO add addOrRemovd rules for characters
export function startDynamicCharacterRulesForInitialState() {
const { characterNames } = meta.assets;
forEach(characterNames, (characterName) => {
const { dollName } = getState().characters[characterName];
if (dollName)
startParamEffectsGroup("character", { characterName, dollName });
});
}
export function stopDynamicCharacterRulesForInitialState() {
const { characterNames } = meta.assets;
forEach(characterNames, (characterName) => {
const { dollName } = getState().characters[characterName];
if (dollName)
stopParamEffectsGroup("character", { characterName, dollName });
});
}
export const characterEffects = makeEffects(({ itemEffect }) => ({
whenAtCamCubes: itemEffect({
run({ newValue: newAtCamCubes, prevValue: prevAtCamCubes, itemId: charName }) {
const { placeInfoByName } = meta.assets;
const { playerCharacter } = getState().global.main;
if (charName !== playerCharacter)
return; // NOTE maybe dynamic rule better (since the listener wont run for other characters)
const { nowPlaceName } = getState().global.main;
const { nowCamName } = getState().global.main;
const cameraNames = placeInfoByName[nowPlaceName].cameraNames;
forEach(cameraNames, (loopedCameraName) => {
if (loopedCameraName !== nowCamName && newAtCamCubes[loopedCameraName] && !prevAtCamCubes[loopedCameraName]) {
setState({ global: { main: { goalCamName: loopedCameraName } } });
}
});
},
check: { type: "characters", prop: "atCamCubes" }, // NOTE Maybe change this to current player character with dynamic rule
step: "collisionReaction",
atStepEnd: true,
}),
// NOTE Could be dynamic rule for all characters?
whenPlaceChanges: itemEffect({
run() {
setState((state) => ({
characters: { [state.global.main.playerCharacter]: { hasLeftFirstTrigger: false } },
}));
},
check: { type: "global", prop: "nowPlaceName" },
}),
}));