3UFL673QX2JG7KHTAM7DFH4BBYVHDNMXBS2RW45G3JBNFZFLSRRQC FEYZALRNZQFN7QWCJGGTHHAYUN2OSSWD7SITQMOZIGQFUMWRFFCQC TQ57VE45BHV7MOZ6GKTYZEAMAOTXLPQ3ROCWJ2FUCITQWOYVMUIAC DFFMZSJOCLTBA3IGRYS2ZIJNO2MC3VLBBU42QL5DFY2SMCNFXPIAC ZHOSSPNKGFIKSFPDXCGLMSYMMX2J433VU2BUUWBKUH7TOLQUBSPQC QUT2VGNOS2XSDLSYCSMILCKF343M56MRPRSUWN4GXWPGCK6APK2QC HXHNGFB2VCXB6YXDND35HJI22GBJC3QTSUR2TK3M3LXGJHVNXVHAC TQAGEMW7FKIQCPQOJN44XVUZJQFQYNGSYRVIYSXFYF36HU7YDMZQC ZWX5PV44JLFYTQXGXNQG64CKTJJC6I2TVMNRII3EAEMUXMV5XNYQC return Object.keys(this._values).map(key => this._values[key]);
return Object.values(this._values);// return Object.keys(this._values).map(key => this._values[key]); // why grant???}*[Symbol.iterator]() {// construct a new iterator. note that as usualfor (let key of Object.keys(this._values)) {yield key;}
export type UpdaterFnParam2<T, W> = T | ((prev: T, prevWhole: W) => T);export type UpdaterFn2<T, W> = (arg: UpdaterFnParam2<T, W>) => void;export type UpdaterGeneratorType2<T, W = T> = {[k in keyof T]: ((T[k] extends { [kkt: string]: any } ? UpdaterGeneratorType2<T[k], W> : {}) & {getUpdater: () => UpdaterFn2<T[k], W>,set: UpdaterFn2<T[k], W>,update: UpdaterFn2<T[k], W>,})} & {getUpdater: () => UpdaterFn2<T, W>,set: UpdaterFn2<T, W>,update: UpdaterFn2<T, W>,}function isObject(o: any): o is { [x: string]: any } {return (typeof o === "object");}export function updaterGenerator2Helper<T, W>(_dataObject: T, dataUpdater: UpdaterFn2<T, W>, wholeDataObject: W): UpdaterGeneratorType2<T, W> {const updaters: UpdaterGeneratorType2<T, W> = {} as any;updaters.getUpdater = () => dataUpdater;updaters.set = dataUpdater;updaters.update = dataUpdater;if (typeof _dataObject !== "object") return updaters;else {const dataObject: T = _dataObject;const keys: (keyof T)[] = Object.keys(dataObject) as any;keys.forEach((key: (keyof T)) => {if (key === "set" || key === "getUpdater" || key === "update") {throw Error(`Invalid key in updaterGenerator: ${key} conflicts with reserved keywords set, update, getUpdater.`);}function keyUpdater(newValueOrCallback: UpdaterFnParam2<T[typeof key], W>) {if (typeof newValueOrCallback === "function") {dataUpdater((oldData) => {const newData = {...oldData,[key]: (newValueOrCallback as ((prev: T[typeof key], whole: W) => T[typeof key]))(oldData[key], wholeDataObject),};return newData;});} else {dataUpdater((oldData) => ({ ...oldData, [key]: newValueOrCallback }));}}updaters[key] = (updaterGenerator2Helper<T[typeof key], W>(dataObject[key], keyUpdater, wholeDataObject) as unknown as (typeof updaters)[typeof key]);});return updaters;}}export function updaterGenerator2<T>(dataObject: T, dataUpdater: UpdaterFn<T>): UpdaterGeneratorType2<T> {const dataUpdater2 = (arg: UpdaterFnParam2<T, T>) => {if (typeof arg === 'function') {dataUpdater((prev) => {// if T is a function type already, typescript correctly notifies us that this will failreturn (arg as ((prev: T, prevWhole: T) => T))(prev, prev);})} else {dataUpdater(arg);}};return updaterGenerator2Helper<T, T>(dataObject, dataUpdater2, dataObject);}type UpdaterFnParam<T> = T | ((prev: T) => T);// type UpdaterFnParam<T> = ((prev: T) => T);export type UpdaterFn<T> = (arg: UpdaterFnParam<T>) => void;export type UpdaterGeneratorType<T> = {[k in keyof T]: T[k] extends { [kkt: string]: any }? (UpdaterGeneratorType<T[k]> & {getUpdater: () => UpdaterFn<T[k]>,set: UpdaterFn<T[k]>,update: UpdaterFn<T[k]>,}): {getUpdater: () => UpdaterFn<T[k]>,set: UpdaterFn<T[k]>,update: UpdaterFn<T[k]>,}} & {getUpdater: () => UpdaterFn<T>,set: UpdaterFn<T>,update: UpdaterFn<T>,}/*** Convenience method for generating setState<FancyObject.sub.component>() from setState<FancyObject> callbacks.* If used in react, recommended that this be memoized.** @generic T should be a data-only object - nested objects are allowed but arrays, sets not supported* @param dataObject ANY instance of T, used only for its keys. MUST have all keys present* @param dataUpdater an updater function, which can be called as: dataUpdater(newT) or* dataUpdater((oldT) => { return newTFromOldT(oldT) }) ; e.g. react setState() function.* @return a deep object that has the same keys as T, except each key also has a getUpdater()/set/update member;* the getUpdater() on a subobject of T acts similarly to the dataUpdater<T> but to the subobject rather than the whole object.* e.g. :* let gameStateUpdater = updaterGenerator(skeletonObject, setGameState);* let setName = gameStateUpdater.player.name.getUpdater();* gameStateUpdater.player.name.set(newName);* gameStateUpdater.player.name.update(oldName => oldName + " ");**/export function updaterGenerator<T>(dataObject: T, dataUpdater: UpdaterFn<T>): UpdaterGeneratorType<T> {const updaters: UpdaterGeneratorType<T> = {} as any;updaters.getUpdater = () => dataUpdater;updaters.set = dataUpdater;updaters.update = dataUpdater;if (typeof dataObject !== "object") return updaters;const keys : (keyof T)[] = Object.keys(dataObject) as any as (keyof T)[];keys.forEach((key: (keyof T)) => {if (key === "set" || key === "getUpdater" || key === "update") {throw Error(`Invalid key in updaterGenerator: ${key} conflicts with reserved keywords set, update, getUpdater.`);}function keyUpdater(newValueOrCallback: UpdaterFnParam<T[typeof key]>) {if (typeof newValueOrCallback === "function") {dataUpdater((oldData) => {const newData = {...oldData,[key]: (newValueOrCallback as Function)(oldData[key]),};return newData;});} else {dataUpdater((oldData) => ({ ...oldData, [key]: newValueOrCallback }));}}updaters[key] = updaterGenerator(dataObject[key], keyUpdater) as any;});return updaters;}
import * as Pixi from "pixi.js";import { HashSet, KeyedHashMap } from "../../lib/util/data_structures/hash";import { ChunkGen, ChunkGenConstants, ChunkRef, GameState, PointNodeRef } from "../../data/GameState";import { PointNodeComponent } from "./PointNodeComponent";import { UpdaterGeneratorType2 } from "../../lib/util/updaterGenerator";import { Vector2 } from "../../lib/util/geometry/vector2";import { PixiPointFrom } from "../../lib/pixi/pixify";export class RenderedChunkConstants {public static SPACING_PX: number = 24;public static CHUNK_SPACING_PX: number = (ChunkGenConstants.CHUNK_DIM + 0.5) * RenderedChunkConstants.SPACING_PX;public static NODE_SIZE_PX: number = 14;public static NODE_HITAREA_PX: number = 18;public static NODE_ROUNDED_PX: number = 4;}type Props = {delta: number,args: {pointNodeTexture: Pixi.Texture,selfChunkRef: ChunkRef,},updaters: UpdaterGeneratorType2<GameState>,position: Vector2,chunkGen: ChunkGen,selectedPointNode: PointNodeRef | undefined,allocatedPointNodeSubset: HashSet<PointNodeRef>,}export class ChunkComponent {public container: Pixi.Container;staleProps!: Props;state!: {};public children: KeyedHashMap<PointNodeRef, PointNodeComponent>;constructor(props: Props) {this.staleProps = props;this.state = {};this.container = new Pixi.Container();this.children = new KeyedHashMap();for (let [pointNodeCoord, pointNodeGen] of props.chunkGen.pointNodes.entries()) {const pointNodeRef = new PointNodeRef({z: props.args.selfChunkRef.z,chunkCoord: props.args.selfChunkRef.chunkCoord,pointNodeCoord: pointNodeCoord,pointNodeId: pointNodeGen.id})let childProps = {delta: props.delta,args: {pointNodeTexture: props.args.pointNodeTexture,selfPointNodeRef: pointNodeRef,},updaters: props.updaters,position: pointNodeRef.pointNodeCoord.multiply(RenderedChunkConstants.SPACING_PX),isSelected: props.selectedPointNode?.pointNodeId == pointNodeRef.pointNodeId,isAllocated: props.allocatedPointNodeSubset.contains(pointNodeRef),};let childComponent = new PointNodeComponent(childProps);this.children.put(pointNodeRef, childComponent);this.container.addChild(childComponent.container);}}renderSelf(props: Props) {this.container.position = PixiPointFrom(props.position);}updateSelf(props: Props) { }public update(props: Props) {this.updateSelf(props);for (let [pointNodeCoord, pointNodeGen] of props.chunkGen.pointNodes.entries()) {const pointNodeRef = new PointNodeRef({z: props.args.selfChunkRef.z,chunkCoord: props.args.selfChunkRef.chunkCoord,pointNodeCoord: pointNodeCoord,pointNodeId: pointNodeGen.id})let childProps = {delta: props.delta,args: {pointNodeTexture: props.args.pointNodeTexture,selfPointNodeRef: pointNodeRef,},updaters: props.updaters,position: pointNodeRef.pointNodeCoord.multiply(RenderedChunkConstants.SPACING_PX),isSelected: props.selectedPointNode?.pointNodeId == pointNodeRef.pointNodeId,isAllocated: props.allocatedPointNodeSubset.contains(pointNodeRef),};let childComponent = this.children.get(pointNodeRef);childComponent.update(childProps);}this.renderSelf(props);}}
import * as Pixi from "pixi.js";import { RenderedChunkConstants } from "./ChunkComponent";import { UpdaterGeneratorType2 } from "../../lib/util/updaterGenerator";import { GameState, PointNodeRef } from "../../data/GameState";import { Vector2 } from "../../lib/util/geometry/vector2";import { PixiPointFrom } from "../../lib/pixi/pixify";/*** Usage:* class RenderedChunk {* constructor(stateUpdaterQueue) {* this.nodes = 0...10.map(i => new RenderedPointNode({texture, new NodeRef(i), stateUpdaterQueue}))* // this.nodes[0].render({ some, stuff })* this.nodes[0] should listen to gameState.playerUI.selectedPointNode and allocatedPointNodes, and* updating gameState.playerUI.selectedPointNode or gameState.playerSave.allocatedPointNodes or their* parents should trigger queueing of the rerender* or rather, rerendering* }* }*/type Props = {delta: number,args: {pointNodeTexture: Pixi.Texture,selfPointNodeRef: PointNodeRef,},updaters: UpdaterGeneratorType2<GameState>,position: Vector2,isSelected: boolean,isAllocated: boolean};export class PointNodeComponent {public container: Pixi.Sprite;staleProps!: Props;state!: {};constructor(props: Props) {this.staleProps = props;this.state = {};this.container = new Pixi.Sprite(props.args.pointNodeTexture);this.container.anchor.x = 0.5;this.container.anchor.y = 0.5;// this.container.x = node.x * RenderedChunkConstants.SPACING_PX;// this.container.y = node.y * RenderedChunkConstants.SPACING_PX;this.container.interactive = true;this.container.buttonMode = true;this.container.hitArea = new Pixi.Rectangle(- RenderedChunkConstants.NODE_HITAREA_PX / 2,- RenderedChunkConstants.NODE_HITAREA_PX / 2,RenderedChunkConstants.NODE_HITAREA_PX,RenderedChunkConstants.NODE_HITAREA_PX,);this.renderSelf(props);this.didMount();}renderSelf(props: Props) {this.container.position = PixiPointFrom(props.position);if (props.isAllocated) {this.container.tint = 0x00AAFF;} else {if (props.isSelected) {this.container.tint = 0xBBBBBB;} else {this.container.tint = 0xFFFFFF;}}}updateSelf(props: Props) { }public update(props: Props) {this.updateSelf(props);this.renderSelf(props);}didMount() {const { args, updaters } = this.staleProps; // we assume this will never changethis.container.addListener("pointerdown", () => {updaters.playerSave.allocatedPointNodeSet.update((prev, prevGameState) => {// if we were already selected, allocate usif (prevGameState.playerUI.selectedPointNode?.pointNodeId == args.selfPointNodeRef.pointNodeId) {prev.put(args.selfPointNodeRef);return prev.clone();}return prev;})updaters.playerSave.allocatedPointNodeHistory.update((prev, prevGameState) => {// if we were already selected, allocate us and add to the history (maybe this should be managed elsewhere??)if (prevGameState.playerUI.selectedPointNode?.pointNodeId == args.selfPointNodeRef.pointNodeId) {prev.push(args.selfPointNodeRef);return [...prev];}return prev;})updaters.playerUI.selectedPointNode.update((prev, gameState) => {return args.selfPointNodeRef;})});}}
import * as Pixi from "pixi.js";import { ChunkRef, GameState, PointNodeRef, ZLevelGen } from "../../data/GameState";import { PixiPointFrom } from "../../lib/pixi/pixify";import { HashSet, KeyedHashMap } from "../../lib/util/data_structures/hash";import { Vector2 } from "../../lib/util/geometry/vector2";import { UpdaterGeneratorType2 } from "../../lib/util/updaterGenerator";import { RenderedChunkConstants } from "../RenderedChunk";import { ChunkComponent } from "./ChunkComponent";type Props = {delta: number,args: {pointNodeTexture: Pixi.Texture,z: number,},updaters: UpdaterGeneratorType2<GameState>,position: Vector2,zLevelGen: ZLevelGen,selectedPointNode: PointNodeRef | undefined,allocatedPointNodeSubset: HashSet<PointNodeRef>,}export class RenderedZLevel {public container: Pixi.Container;staleProps!: Props;state!: {};public children: KeyedHashMap<ChunkRef, ChunkComponent> = new KeyedHashMap();constructor(props: Props) {this.staleProps = props;this.state = {};this.container = new Pixi.Container();for (let [chunkCoord, chunkGen] of props.zLevelGen.chunks.entries()) {const chunkRef = new ChunkRef({z: props.args.z,chunkCoord,chunkId: chunkGen.id,});let allocatedPointNodeSubset = new HashSet(props.allocatedPointNodeSubset.values().filter((pointNodeRef) => {return pointNodeRef.chunkCoord.x === chunkRef.chunkCoord.x &&pointNodeRef.chunkCoord.y === chunkRef.chunkCoord.y;}));let childProps = {delta: props.delta,args: {pointNodeTexture: props.args.pointNodeTexture,selfChunkRef: chunkRef,},updaters: props.updaters,position: chunkRef.chunkCoord.multiply(RenderedChunkConstants.CHUNK_SPACING_PX),chunkGen: chunkGen,selectedPointNode: props.selectedPointNode,allocatedPointNodeSubset,}const childComponent = new ChunkComponent(childProps);this.children.put(chunkRef, childComponent);this.container.addChild(childComponent.container);}}renderSelf(props: Props) {this.container.position = PixiPointFrom(props.position);}updateSelf(props: Props) { }public update(props: Props) {this.updateSelf(props);for (let [chunkCoord, chunkGen] of props.zLevelGen.chunks.entries()) {const chunkRef = new ChunkRef({z: props.args.z,chunkCoord,chunkId: chunkGen.id,});let allocatedPointNodeSubset = new HashSet(props.allocatedPointNodeSubset.values().filter((pointNodeRef) => {return pointNodeRef.chunkCoord.x === chunkRef.chunkCoord.x &&pointNodeRef.chunkCoord.y === chunkRef.chunkCoord.y;}));let childProps = {delta: props.delta,args: {pointNodeTexture: props.args.pointNodeTexture,selfChunkRef: chunkRef,},updaters: props.updaters,position: chunkRef.chunkCoord.multiply(RenderedChunkConstants.CHUNK_SPACING_PX),chunkGen: chunkGen,selectedPointNode: props.selectedPointNode,allocatedPointNodeSubset,}this.children.get(chunkRef).update(childProps);}this.renderSelf(props);}}