Profectus-Demo/src/game/layers.ts

159 lines
4.5 KiB
TypeScript
Raw Normal View History

import {
2022-01-14 04:25:47 +00:00
CoercableComponent,
persistent,
PersistentRef,
Replace,
setDefault,
StyleValue
} from "@/features/feature";
import { Link } from "@/features/links";
2022-01-25 04:23:30 +00:00
import Decimal from "@/util/bignum";
import {
2022-01-14 04:25:47 +00:00
Computable,
GetComputableType,
GetComputableTypeWithDefault,
processComputable,
ProcessedComputable
} from "@/util/computed";
import { createProxy } from "@/util/proxies";
import { createNanoEvents, Emitter } from "nanoevents";
2022-01-25 04:23:30 +00:00
import { globalBus } from "./events";
2022-01-14 04:25:47 +00:00
import player from "./player";
2022-01-25 04:23:30 +00:00
export interface LayerEvents {
// Generation
preUpdate: (diff: Decimal) => void;
// Actions (e.g. automation)
update: (diff: Decimal) => void;
// Effects (e.g. milestones)
postUpdate: (diff: Decimal) => void;
}
export const layers: Record<string, Readonly<GenericLayer> | undefined> = {};
window.layers = layers;
2022-01-14 04:25:47 +00:00
export interface Position {
x: number;
y: number;
}
2022-01-14 04:25:47 +00:00
export interface LayerOptions {
id: string;
color?: Computable<string>;
display: Computable<CoercableComponent>;
classes?: Computable<Record<string, boolean>>;
style?: Computable<StyleValue>;
name?: Computable<string>;
minimizable?: Computable<boolean>;
forceHideGoBack?: Computable<boolean>;
minWidth?: Computable<number>;
links?: Computable<Link[]>;
}
2022-01-14 04:25:47 +00:00
export interface BaseLayer {
minimized: PersistentRef<boolean>;
emitter: Emitter<LayerEvents>;
on: OmitThisParameter<Emitter<LayerEvents>["on"]>;
emit: <K extends keyof LayerEvents>(event: K, ...args: Parameters<LayerEvents[K]>) => void;
}
2022-01-14 04:25:47 +00:00
export type Layer<T extends LayerOptions> = Replace<
T & BaseLayer,
{
color: GetComputableType<T["color"]>;
display: GetComputableType<T["display"]>;
classes: GetComputableType<T["classes"]>;
style: GetComputableType<T["style"]>;
name: GetComputableTypeWithDefault<T["name"], T["id"]>;
minWidth: GetComputableTypeWithDefault<T["minWidth"], 600>;
minimizable: GetComputableTypeWithDefault<T["minimizable"], true>;
forceHideGoBack: GetComputableType<T["forceHideGoBack"]>;
links: GetComputableType<T["links"]>;
}
>;
export type GenericLayer = Replace<
Layer<LayerOptions>,
{
name: ProcessedComputable<string>;
minWidth: ProcessedComputable<number>;
minimizable: ProcessedComputable<boolean>;
}
>;
export function createLayer<T extends LayerOptions>(options: T): Layer<T> {
const layer: T & Partial<BaseLayer> = options;
const emitter = (layer.emitter = createNanoEvents<LayerEvents>());
2022-01-25 04:23:30 +00:00
layer.on = emitter.on.bind(emitter);
layer.emit = emitter.emit.bind(emitter);
2022-01-14 04:25:47 +00:00
layer.minimized = persistent(false);
processComputable(layer as T, "color");
processComputable(layer as T, "display");
processComputable(layer as T, "name");
setDefault(layer, "name", options.id);
processComputable(layer as T, "minWidth");
setDefault(layer, "minWidth", 600);
processComputable(layer as T, "minimizable");
setDefault(layer, "minimizable", true);
processComputable(layer as T, "links");
2022-01-25 04:23:30 +00:00
const proxy = createProxy(layer as unknown as Layer<T>);
2022-01-14 04:25:47 +00:00
return proxy;
}
2022-01-14 04:25:47 +00:00
export function addLayer(
layer: GenericLayer,
player: { layers?: Record<string, Record<string, unknown>> }
): void {
2022-01-25 04:23:30 +00:00
console.info("Adding layer", layer.id);
if (layers[layer.id]) {
2022-01-14 04:25:47 +00:00
console.error(
"Attempted to add layer with same ID as existing layer",
2021-08-18 05:18:23 +00:00
layer.id,
2022-01-14 04:25:47 +00:00
layers[layer.id]
);
2022-01-14 04:25:47 +00:00
return;
}
2022-01-14 04:25:47 +00:00
setDefault(player, "layers", {});
if (player.layers[layer.id] == null) {
player.layers[layer.id] = {};
}
2022-01-14 04:25:47 +00:00
layers[layer.id] = layer;
2022-01-14 04:25:47 +00:00
globalBus.emit("addLayer", layer, player.layers[layer.id]);
}
2022-01-14 04:25:47 +00:00
export function getLayer<T extends GenericLayer>(layerID: string): () => T {
return () => layers[layerID] as T;
}
2022-01-14 04:25:47 +00:00
export function removeLayer(layer: GenericLayer): void {
2022-01-25 04:23:30 +00:00
console.info("Removing layer", layer.id);
2022-01-14 04:25:47 +00:00
globalBus.emit("removeLayer", layer);
2022-01-25 04:23:30 +00:00
layers[layer.id] = undefined;
}
2022-01-14 04:25:47 +00:00
export function reloadLayer(layer: GenericLayer): void {
removeLayer(layer);
// Re-create layer
addLayer(layer, player);
}
2022-01-25 04:23:30 +00:00
globalBus.on("update", function updateLayers(diff) {
Object.values(layers).forEach(layer => {
layer?.emit("preUpdate", diff);
});
Object.values(layers).forEach(layer => {
layer?.emit("update", diff);
});
Object.values(layers).forEach(layer => {
layer?.emit("postUpdate", diff);
});
});