refactor: clean map gameplay architecture
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { DEFAULT_CATEGORY_VOLUMES } from "@/data/audioConfig";
|
||||
import { logger } from "@/utils/core/Logger";
|
||||
|
||||
export type AudioCategory = "music" | "sfx" | "dialogue";
|
||||
@@ -7,12 +8,6 @@ interface AudioContextWindow extends Window {
|
||||
webkitAudioContext?: typeof AudioContext;
|
||||
}
|
||||
|
||||
const DEFAULT_CATEGORY_VOLUMES: Record<AudioCategory, number> = {
|
||||
music: 1,
|
||||
sfx: 1,
|
||||
dialogue: 1,
|
||||
};
|
||||
|
||||
interface PlaySoundOptions {
|
||||
category?: OneShotAudioCategory;
|
||||
pan?: number;
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import { create } from "zustand";
|
||||
import { isGameStep, isMainGameState } from "@/data/game/gameStateConfig";
|
||||
import {
|
||||
isGameStep,
|
||||
isMainGameState,
|
||||
type GameStep,
|
||||
type MainGameState,
|
||||
} from "@/types/game";
|
||||
import {
|
||||
isRepairMissionId,
|
||||
isMissionStep,
|
||||
getNextMissionStep,
|
||||
getPreviousMissionStep,
|
||||
isMissionStep,
|
||||
isRepairMissionId,
|
||||
} from "@/data/gameplay/repairMissionState";
|
||||
import type { GameStep, MainGameState } from "@/types/game";
|
||||
import {
|
||||
type MissionStep,
|
||||
type RepairMissionId,
|
||||
} from "@/types/gameplay/repairMission";
|
||||
@@ -49,10 +47,10 @@ export interface GameState {
|
||||
ebike: MissionState & {
|
||||
isRepaired: boolean;
|
||||
};
|
||||
pylone: MissionState & {
|
||||
pylon: MissionState & {
|
||||
isPowered: boolean;
|
||||
};
|
||||
ferme: MissionState & {
|
||||
farm: MissionState & {
|
||||
irrigationFixed: boolean;
|
||||
};
|
||||
outro: {
|
||||
@@ -71,14 +69,14 @@ interface GameActions {
|
||||
setIntroState: (intro: Partial<IntroState>) => void;
|
||||
setPlayerName: (playerName: string) => void;
|
||||
setEbikeState: (ebike: Partial<GameState["ebike"]>) => void;
|
||||
setPyloneState: (pylone: Partial<GameState["pylone"]>) => void;
|
||||
setFermeState: (ferme: Partial<GameState["ferme"]>) => void;
|
||||
setPylonState: (pylon: Partial<GameState["pylon"]>) => void;
|
||||
setFarmState: (farm: Partial<GameState["farm"]>) => void;
|
||||
setOutroState: (outro: Partial<GameState["outro"]>) => void;
|
||||
setMissionStep: (mission: RepairMissionId, step: MissionStep) => void;
|
||||
completeIntro: () => void;
|
||||
completeEbike: () => void;
|
||||
completePylone: () => void;
|
||||
completeFerme: () => void;
|
||||
completePylon: () => void;
|
||||
completeFarm: () => void;
|
||||
completeMission: (mission: RepairMissionId) => void;
|
||||
startOutro: () => void;
|
||||
advanceGameState: () => void;
|
||||
@@ -110,6 +108,10 @@ function completeIntroState(state: GameState): GameStateUpdate {
|
||||
hasCompleted: true,
|
||||
isEbikeUnlocked: true,
|
||||
},
|
||||
missionFlow: {
|
||||
...state.missionFlow,
|
||||
canMove: true,
|
||||
},
|
||||
ebike: {
|
||||
...state.ebike,
|
||||
currentStep: "locked",
|
||||
@@ -119,39 +121,39 @@ function completeIntroState(state: GameState): GameStateUpdate {
|
||||
|
||||
function completeEbikeState(state: GameState): GameStateUpdate {
|
||||
return {
|
||||
mainState: "pylone",
|
||||
mainState: "pylon",
|
||||
ebike: {
|
||||
...state.ebike,
|
||||
currentStep: "done",
|
||||
isRepaired: true,
|
||||
},
|
||||
pylone: {
|
||||
...state.pylone,
|
||||
pylon: {
|
||||
...state.pylon,
|
||||
currentStep: "waiting",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function completePyloneState(state: GameState): GameStateUpdate {
|
||||
function completePylonState(state: GameState): GameStateUpdate {
|
||||
return {
|
||||
mainState: "ferme",
|
||||
pylone: {
|
||||
...state.pylone,
|
||||
mainState: "farm",
|
||||
pylon: {
|
||||
...state.pylon,
|
||||
currentStep: "done",
|
||||
isPowered: true,
|
||||
},
|
||||
ferme: {
|
||||
...state.ferme,
|
||||
farm: {
|
||||
...state.farm,
|
||||
currentStep: "waiting",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function completeFermeState(state: GameState): GameStateUpdate {
|
||||
function completeFarmState(state: GameState): GameStateUpdate {
|
||||
return {
|
||||
mainState: "outro",
|
||||
ferme: {
|
||||
...state.ferme,
|
||||
farm: {
|
||||
...state.farm,
|
||||
currentStep: "done",
|
||||
irrigationFixed: true,
|
||||
},
|
||||
@@ -182,10 +184,10 @@ function completeMissionState(
|
||||
switch (mission) {
|
||||
case "ebike":
|
||||
return completeEbikeState(state);
|
||||
case "pylone":
|
||||
return completePyloneState(state);
|
||||
case "ferme":
|
||||
return completeFermeState(state);
|
||||
case "pylon":
|
||||
return completePylonState(state);
|
||||
case "farm":
|
||||
return completeFarmState(state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,12 +245,12 @@ function createInitialGameState(): GameState {
|
||||
dialogueAudio: null,
|
||||
isRepaired: false,
|
||||
},
|
||||
pylone: {
|
||||
pylon: {
|
||||
currentStep: "locked",
|
||||
dialogueAudio: null,
|
||||
isPowered: false,
|
||||
},
|
||||
ferme: {
|
||||
farm: {
|
||||
currentStep: "locked",
|
||||
dialogueAudio: null,
|
||||
irrigationFixed: false,
|
||||
@@ -321,8 +323,8 @@ function hydrateDebugGameState(initial: GameState, value: unknown): GameState {
|
||||
if (!isRecord(value)) return initial;
|
||||
|
||||
const ebike = hydrateMissionState(initial.ebike, value.ebike);
|
||||
const pylone = hydrateMissionState(initial.pylone, value.pylone);
|
||||
const ferme = hydrateMissionState(initial.ferme, value.ferme);
|
||||
const pylon = hydrateMissionState(initial.pylon, value.pylon);
|
||||
const farm = hydrateMissionState(initial.farm, value.farm);
|
||||
const outro = isRecord(value.outro) ? value.outro : null;
|
||||
|
||||
return {
|
||||
@@ -344,19 +346,19 @@ function hydrateDebugGameState(initial: GameState, value: unknown): GameState {
|
||||
? value.ebike.isRepaired
|
||||
: initial.ebike.isRepaired,
|
||||
},
|
||||
pylone: {
|
||||
...pylone,
|
||||
pylon: {
|
||||
...pylon,
|
||||
isPowered:
|
||||
isRecord(value.pylone) && isBoolean(value.pylone.isPowered)
|
||||
? value.pylone.isPowered
|
||||
: initial.pylone.isPowered,
|
||||
isRecord(value.pylon) && isBoolean(value.pylon.isPowered)
|
||||
? value.pylon.isPowered
|
||||
: initial.pylon.isPowered,
|
||||
},
|
||||
ferme: {
|
||||
...ferme,
|
||||
farm: {
|
||||
...farm,
|
||||
irrigationFixed:
|
||||
isRecord(value.ferme) && isBoolean(value.ferme.irrigationFixed)
|
||||
? value.ferme.irrigationFixed
|
||||
: initial.ferme.irrigationFixed,
|
||||
isRecord(value.farm) && isBoolean(value.farm.irrigationFixed)
|
||||
? value.farm.irrigationFixed
|
||||
: initial.farm.irrigationFixed,
|
||||
},
|
||||
outro: {
|
||||
dialogueAudio:
|
||||
@@ -385,8 +387,8 @@ function pickGameState(state: GameStore): GameState {
|
||||
missionFlow: state.missionFlow,
|
||||
intro: state.intro,
|
||||
ebike: state.ebike,
|
||||
pylone: state.pylone,
|
||||
ferme: state.ferme,
|
||||
pylon: state.pylon,
|
||||
farm: state.farm,
|
||||
outro: state.outro,
|
||||
};
|
||||
}
|
||||
@@ -417,18 +419,18 @@ export const useGameStore = create<GameStore>()((set) => ({
|
||||
})),
|
||||
setEbikeState: (ebike) =>
|
||||
set((state) => ({ ebike: { ...state.ebike, ...ebike } })),
|
||||
setPyloneState: (pylone) =>
|
||||
set((state) => ({ pylone: { ...state.pylone, ...pylone } })),
|
||||
setFermeState: (ferme) =>
|
||||
set((state) => ({ ferme: { ...state.ferme, ...ferme } })),
|
||||
setPylonState: (pylon) =>
|
||||
set((state) => ({ pylon: { ...state.pylon, ...pylon } })),
|
||||
setFarmState: (farm) =>
|
||||
set((state) => ({ farm: { ...state.farm, ...farm } })),
|
||||
setOutroState: (outro) =>
|
||||
set((state) => ({ outro: { ...state.outro, ...outro } })),
|
||||
setMissionStep: (mission, step) =>
|
||||
set((state) => setMissionStepState(state, mission, step)),
|
||||
completeIntro: () => set(completeIntroState),
|
||||
completeEbike: () => set((state) => completeMissionState(state, "ebike")),
|
||||
completePylone: () => set((state) => completeMissionState(state, "pylone")),
|
||||
completeFerme: () => set((state) => completeMissionState(state, "ferme")),
|
||||
completePylon: () => set((state) => completeMissionState(state, "pylon")),
|
||||
completeFarm: () => set((state) => completeMissionState(state, "farm")),
|
||||
completeMission: (mission) =>
|
||||
set((state) => completeMissionState(state, mission)),
|
||||
startOutro: () => set(startOutroState),
|
||||
|
||||
@@ -1,38 +1,18 @@
|
||||
import { create } from "zustand";
|
||||
import {
|
||||
MAP_PERFORMANCE_GROUP_NAMES,
|
||||
MAP_PERFORMANCE_MODEL_GROUPS,
|
||||
MAP_PERFORMANCE_MODEL_NAMES,
|
||||
type MapPerformanceGroupName,
|
||||
type MapPerformanceModelName,
|
||||
} from "@/data/world/mapPerformanceConfig";
|
||||
|
||||
export type MapPerformanceGroupName =
|
||||
| "vegetation"
|
||||
| "crops"
|
||||
| "trees"
|
||||
| "buildings"
|
||||
| "landmarks"
|
||||
| "props"
|
||||
| "terrain"
|
||||
| "sky";
|
||||
|
||||
export type MapPerformanceModelName =
|
||||
| "buisson"
|
||||
| "arbre"
|
||||
| "sapin"
|
||||
| "champdeble"
|
||||
| "champdesoja"
|
||||
| "champsdetournesol"
|
||||
| "ecole"
|
||||
| "generateur"
|
||||
| "fermeverticale"
|
||||
| "lafabrik"
|
||||
| "immeuble1"
|
||||
| "eolienne"
|
||||
| "pylone"
|
||||
| "boiteauxlettres"
|
||||
| "maison1"
|
||||
| "panneauaffichage"
|
||||
| "panneauclassique"
|
||||
| "panneaufleche"
|
||||
| "panneausolaire"
|
||||
| "parcebike"
|
||||
| "terrain"
|
||||
| "sky";
|
||||
export {
|
||||
MAP_PERFORMANCE_GROUP_NAMES,
|
||||
MAP_PERFORMANCE_MODEL_NAMES,
|
||||
type MapPerformanceGroupName,
|
||||
type MapPerformanceModelName,
|
||||
};
|
||||
|
||||
export interface MapPerformanceVisibility {
|
||||
groups: Record<MapPerformanceGroupName, boolean>;
|
||||
@@ -47,70 +27,6 @@ interface MapPerformanceActions {
|
||||
|
||||
type MapPerformanceStore = MapPerformanceVisibility & MapPerformanceActions;
|
||||
|
||||
export const MAP_PERFORMANCE_GROUP_NAMES: readonly MapPerformanceGroupName[] = [
|
||||
"vegetation",
|
||||
"crops",
|
||||
"trees",
|
||||
"buildings",
|
||||
"landmarks",
|
||||
"props",
|
||||
"terrain",
|
||||
"sky",
|
||||
];
|
||||
|
||||
export const MAP_PERFORMANCE_MODEL_NAMES: readonly MapPerformanceModelName[] = [
|
||||
"buisson",
|
||||
"arbre",
|
||||
"sapin",
|
||||
"champdeble",
|
||||
"champdesoja",
|
||||
"champsdetournesol",
|
||||
"ecole",
|
||||
"generateur",
|
||||
"fermeverticale",
|
||||
"lafabrik",
|
||||
"immeuble1",
|
||||
"eolienne",
|
||||
"pylone",
|
||||
"boiteauxlettres",
|
||||
"maison1",
|
||||
"panneauaffichage",
|
||||
"panneauclassique",
|
||||
"panneaufleche",
|
||||
"panneausolaire",
|
||||
"parcebike",
|
||||
"terrain",
|
||||
"sky",
|
||||
];
|
||||
|
||||
const MODEL_GROUPS: Record<
|
||||
MapPerformanceModelName,
|
||||
readonly MapPerformanceGroupName[]
|
||||
> = {
|
||||
buisson: ["vegetation"],
|
||||
arbre: ["vegetation", "trees"],
|
||||
sapin: ["vegetation", "trees"],
|
||||
champdeble: ["vegetation", "crops"],
|
||||
champdesoja: ["vegetation", "crops"],
|
||||
champsdetournesol: ["vegetation", "crops"],
|
||||
ecole: ["buildings", "landmarks"],
|
||||
generateur: ["landmarks"],
|
||||
fermeverticale: ["buildings", "landmarks"],
|
||||
lafabrik: ["buildings", "landmarks"],
|
||||
immeuble1: ["buildings"],
|
||||
eolienne: ["props"],
|
||||
pylone: ["props"],
|
||||
boiteauxlettres: ["props"],
|
||||
maison1: ["buildings"],
|
||||
panneauaffichage: ["props"],
|
||||
panneauclassique: ["props"],
|
||||
panneaufleche: ["props"],
|
||||
panneausolaire: ["props"],
|
||||
parcebike: ["props"],
|
||||
terrain: ["terrain"],
|
||||
sky: ["sky"],
|
||||
};
|
||||
|
||||
function createVisibleRecord<T extends string>(
|
||||
keys: readonly T[],
|
||||
): Record<T, boolean> {
|
||||
@@ -140,7 +56,9 @@ export function isMapModelVisible(
|
||||
if (!isMapPerformanceModelName(name)) return true;
|
||||
if (!visibility.models[name]) return false;
|
||||
|
||||
return MODEL_GROUPS[name].every((group) => visibility.groups[group]);
|
||||
return MAP_PERFORMANCE_MODEL_GROUPS[name].every(
|
||||
(group) => visibility.groups[group],
|
||||
);
|
||||
}
|
||||
|
||||
export const useMapPerformanceStore = create<MapPerformanceStore>()((set) => ({
|
||||
|
||||
Reference in New Issue
Block a user