DFFMZSJOCLTBA3IGRYS2ZIJNO2MC3VLBBU42QL5DFY2SMCNFXPIAC OL5CSNTOYADQ6NOJS5YRWY4QIAWEQRGCJGBHNSRSMJD2I43COJ7AC Z3E7XJOW6NSBDYRDKSGTOAEJSPATAUX4JUFCL4DIL3372GL4K52QC HUH4SI4HXIP72KQSJP2I4ELHX5KUQZM7FFGKZZGJ33DF7E3JHMYQC ZGS4FTTFBXTF2SHYTPAJJBWEUVWVYXPSJVEFI5NYJWTW273B4NHAC TQ57VE45BHV7MOZ6GKTYZEAMAOTXLPQ3ROCWJ2FUCITQWOYVMUIAC DLEEYV4V7X337ZJJM775DPARMCMXMLOBXGSBCWDMZBHYKSQTGZCQC ZHOSSPNKGFIKSFPDXCGLMSYMMX2J433VU2BUUWBKUH7TOLQUBSPQC return Object.keys(this._values).map(key => this._values[key]);}// hashes only the keys - use HashableHashMap if you know that the value type here is also hashablehashKeyset(): string {const hashes: number[] = Object.keys(this._values).map(s => hashCode(s));let code: number = hashes.reduce((pv, cv) => pv + cv);return code.toString();}}// Hash a string to a number. source: https://gist.github.com/hyamamoto/fd435505d29ebfa3d9716fd2be8d42f0function hashCode(s: string): number {let h = 0;for (let i = 0; i < s.length; i++) {h = Math.imul(31, h) + s.charCodeAt(i) | 0;}return h;}export class HashableHashMap<K extends { hash(): string }, V extends { hash(): string }> extends HashMap<K, V> {hash(): string {const hashes: number[] = Object.entries(this._values).map(([s, v]) => hashCode(s) + hashCode(v.hash()));let code: number = hashes.reduce((pv, cv) => pv + cv);return code.toString();}}/*** Same as HashMap, but actually stores the keys used to key the hashmap, instead of just their hashes.* Allows iteration over the full key-value pair set.*/export class KeyedHashMap<K extends { hash(): string }, V>{private _values: { [key: string]: [K, V] } = {};put(key: K, value: V) {this._values[key.hash()] = [key, value];}remove(key: K): void {delete this._values[key.hash()];}get(key: K): V {return this._values[key.hash()][1];}contains(key: K): boolean {// V may be an undefined typereturn this.get(key) !== undefined && key.hash() in this._values;}keys(): K[] {return Object.keys(this._values).map(key => this._values[key][0]);}entries(): ([K, V])[] {
import { HashMap, HashSet } from "../lib/util/data_structures/hash";import { ChunkGenConstants, PointNodeRef } from "../data/GameState";
import { HashMap, HashSet, KeyedHashMap } from "../lib/util/data_structures/hash";import { ChunkGen, ChunkGenConstants, ChunkRef, PointNodeRef } from "../data/GameState";import { RenderedPointNode } from "./RenderedPointNode";
// constructor(chunk: Chunk, onNodeFocus: (selection: PointNodeRef) => void, texture?: Pixi.Texture) {constructor(args: { onNodeFocus: (selection: PointNodeRef) => void, nodeTexture: Pixi.Texture }) {
public renderedPointNodes: KeyedHashMap<PointNodeRef, RenderedPointNode>;// public renderedNodes: HashMap<Vector2, Pixi.Graphics | Pixi.Sprite> = new HashMap();
// render the thing
// args here will never change, and changing this will NOT force a rerenderconstructor(args: {pointNodeTexture: Pixi.Texture,selfChunkRef: ChunkRef, // where am i in parentchunkGen: ChunkGen, // what is in mestateUpdaterQueue: [Function],ticker: Pixi.Ticker}) {
for (let node of chunk.nodes) {let g: Pixi.Sprite = new Pixi.Sprite(args.nodeTexture);g.anchor.x = 0.5;g.anchor.y = 0.5;g.x = node.x * RenderedChunkConstants.SPACING_PX;g.y = node.y * RenderedChunkConstants.SPACING_PX;g.hitArea = new Pixi.Rectangle(- RenderedChunkConstants.NODE_HITAREA_PX / 2,- RenderedChunkConstants.NODE_HITAREA_PX / 2,RenderedChunkConstants.NODE_HITAREA_PX,RenderedChunkConstants.NODE_HITAREA_PX,)this.renderedNodes.put(node, g);g.interactive = true;
this.selfChunkRef = args.selfChunkRef;this.renderedPointNodes = new KeyedHashMap();
if (this.chunk.allocatedNodes.get(node)) {g.tint = 0x00aaff;} else if (this.chunk.selectedNodes.get(node)) {g.tint = 0xBBBBBB;}g.addListener("pointerdown", () => {onNodeFocus(new PointNodeRef({z: 0, // TODO(bowei): fixchunkCoord: this.chunk.location,pointNodeCoord: node,pointNodeId: 0, // TODO(bowei): fix}));console.log(`clicked chunk ${this.chunk.location.x} ${this.chunk.location.y} node ${node.x}, ${node.y}`);
// args.chunkGen.pointNodesfor (let [pointNodeCoord, pointNodeGen] of args.chunkGen.pointNodes.entries()) {const pointNodeRef = new PointNodeRef({z: this.selfChunkRef.z,chunkCoord: this.selfChunkRef.chunkCoord,pointNodeCoord: pointNodeCoord,pointNodeId: pointNodeGen.id})
// if nothing is selectedif (this.chunk.selectedNodes.values().length == 0) {// select itthis.chunk.selectedNodes.put(node);g.tint = 0xBBBBBB;// g.alpha = 0.5;} else if (this.chunk.selectedNodes.get(node)) {// i was already selected, let's allocate itthis.chunk.selectedNodes.remove(node);// try to allocate, only allow if we are connected to something already allocatedlet neighbors = [node.addX(1), node.addY(1), node.addY(-1), node.addX(-1)];let allowed = false;for (let neighbor of neighbors) {if (this.chunk.allocatedNodes.get(neighbor)) {allowed = true;break;}}if (allowed) {this.chunk.allocatedNodes.put(node);g.tint = 0x00aaff;} else {g.tint = 0xFFFFFF;window.alert('not allowed to allocate that one!');}// g.alpha = 0.5;} else {// unselect what was previously selectedfor (let selected of this.chunk.selectedNodes.values()) {this.renderedNodes.get(selected).tint = 0xFFFFFF;this.chunk.selectedNodes.remove(selected);}this.chunk.selectedNodes.put(node);g.tint = 0xBBBBBB;}});this.container.addChild(g);
let renderedPointNode = new RenderedPointNode({selfPointNodeRef: pointNodeRef,...args})this.renderedPointNodes.put(pointNodeRef, renderedPointNode);this.container.addChild(renderedPointNode.sprite);// renderedPointNode.setCoord(pointNodeRef); renderedPointNode.setCoord();renderedPointNode.sprite.x = pointNodeCoord.x * RenderedChunkConstants.SPACING_PX;renderedPointNode.sprite.y = pointNodeCoord.y * RenderedChunkConstants.SPACING_PX;
// this.container.x = this.chunk.location.x * RenderedChunk.CHUNK_SPACING_PX;// this.container.y = this.chunk.location.y * RenderedChunk.CHUNK_SPACING_PX;}public setLocation(chunk: ChunkRef = this.selfChunkRef): this {this.container.x = chunk.chunkCoord.x * RenderedChunkConstants.CHUNK_SPACING_PX;this.container.y = chunk.chunkCoord.y * RenderedChunkConstants.CHUNK_SPACING_PX;return this}
public hash(): string {return this.chunk.hash();
public rerender(props: {selectedPointNode: PointNodeRef | undefined,allocatedPointNodeSubset: HashSet<PointNodeRef>,}) {for (let child of this.renderedPointNodes.values()) {child.rerender({isSelected: props.selectedPointNode?.pointNodeId == child.selfPointNodeRef.pointNodeId,isAllocated: props.allocatedPointNodeSubset.contains(child.selfPointNodeRef),})}
// for (let node of chunk.nodes) {// let g: Pixi.Sprite = new Pixi.Sprite(args.nodeTexture);// g.anchor.x = 0.5;// g.anchor.y = 0.5;// g.x = node.x * RenderedChunkConstants.SPACING_PX;// g.y = node.y * RenderedChunkConstants.SPACING_PX;// g.hitArea = new Pixi.Rectangle(// - RenderedChunkConstants.NODE_HITAREA_PX / 2,// - RenderedChunkConstants.NODE_HITAREA_PX / 2,// RenderedChunkConstants.NODE_HITAREA_PX,// RenderedChunkConstants.NODE_HITAREA_PX,// )// this.renderedNodes.put(node, g);// g.interactive = true;//// if (this.chunk.allocatedNodes.get(node)) {// g.tint = 0x00aaff;// } else if (this.chunk.selectedNodes.get(node)) {// g.tint = 0xBBBBBB;// }// g.addListener("pointerdown", () => {// onNodeFocus(new PointNodeRef({// z: 0, // TODO(bowei): fix// chunkCoord: this.chunk.location,// pointNodeCoord: node,// pointNodeId: 0, // TODO(bowei): fix// }));// console.log(`clicked chunk ${this.chunk.location.x} ${this.chunk.location.y} node ${node.x}, ${node.y}`);//// // if nothing is selected// if (this.chunk.selectedNodes.values().length == 0) {// // select it// this.chunk.selectedNodes.put(node);// g.tint = 0xBBBBBB;// // g.alpha = 0.5;// } else if (this.chunk.selectedNodes.get(node)) {// // i was already selected, let's allocate it// this.chunk.selectedNodes.remove(node);// // try to allocate, only allow if we are connected to something already allocated// let neighbors = [node.addX(1), node.addY(1), node.addY(-1), node.addX(-1)];// let allowed = false;// for (let neighbor of neighbors) {// if (this.chunk.allocatedNodes.get(neighbor)) {// allowed = true;// break;// }// }// if (allowed) {// this.chunk.allocatedNodes.put(node);// g.tint = 0x00aaff;// } else {// g.tint = 0xFFFFFF;// window.alert('not allowed to allocate that one!');// }// // g.alpha = 0.5;// } else {// // unselect what was previously selected// for (let selected of this.chunk.selectedNodes.values()) {// this.renderedNodes.get(selected).tint = 0xFFFFFF;// this.chunk.selectedNodes.remove(selected);// }// this.chunk.selectedNodes.put(node);// g.tint = 0xBBBBBB;// }// });// this.container.addChild(g);// }//// this.container.x = this.chunk.location.x * RenderedChunk.CHUNK_SPACING_PX;// this.container.y = this.chunk.location.y * RenderedChunk.CHUNK_SPACING_PX;// }//// public hash(): string {// return this.chunk.hash();// }
public animate(delta: number) {
public setLocation(node: PointNodeRef = this.selfPointNodeRef) : this {this.sprite.x = node.pointNodeCoord.x * RenderedChunkConstants.SPACING_PX;this.sprite.y = node.pointNodeCoord.y * RenderedChunkConstants.SPACING_PX;return this;}public animate(delta: number) : this {
// public isSelected(gameState: DeepReadonly<GameState>): boolean {// return gameState.playerUI.selectedPointNode?.pointNodeId == this.selfPointNodeRef.pointNodeId;// }//// public isAllocated(gameState: DeepReadonly<GameState>): boolean {// return gameState.playerSave.allocatedPointNodeSet.get(this.selfPointNodeRef)// }
public isSelected(gameState: DeepReadonly<GameState>): boolean {return gameState.playerUI.selectedPointNode?.pointNodeId == this.selfPointNodeRef.pointNodeId;}public isAllocated(gameState: DeepReadonly<GameState>): boolean {return gameState.playerSave.allocatedPointNodeSet.get(this.selfPointNodeRef)}
import * as Pixi from "pixi.js";import { ChunkRef, PointNodeRef, ZLevelGen } from "../data/GameState";import { KeyedHashMap, HashSet } from "../lib/util/data_structures/hash";import { Vector2 } from "../lib/util/geometry/vector2";import { squirrel3 } from "../lib/util/random";import { RenderedChunk, RenderedChunkConstants } from "./RenderedChunk";export class RenderedZLevel {public container: Pixi.Container;public z: number;public renderedChunks: KeyedHashMap<ChunkRef, RenderedChunk> = new KeyedHashMap();constructor(args: {pointNodeTexture: Pixi.Texture,z: number,zLevelGen: ZLevelGen, // what is in mestateUpdaterQueue: [Function],ticker: Pixi.Ticker}) {// constructor(zLevel: ZLevel, onNodeFocus: (selection: PointNodeRef) => void, texture?: Pixi.Texture) {this.z = args.z;this.container = new Pixi.Container();for (let [chunkCoord, chunkGen] of args.zLevelGen.chunks.entries()) {const chunkRef = new ChunkRef({z: this.z,chunkCoord,chunkId: chunkGen.id,});const renderedChunk = new RenderedChunk({selfChunkRef: chunkRef,chunkGen,...args});this.renderedChunks.put(chunkRef, renderedChunk);this.container.addChild(renderedChunk.container);renderedChunk.container.x = chunkCoord.x * RenderedChunkConstants.CHUNK_SPACING_PX;renderedChunk.container.y = chunkCoord.y * RenderedChunkConstants.CHUNK_SPACING_PX;}}public animate(delta: number): this {return this;}public rerender(props: {selectedPointNode: PointNodeRef | undefined,allocatedPointNodeSubset: HashSet<PointNodeRef>,}) {for (let [chunkRef, child] of this.renderedChunks.entries()) {let relevantToChunk = new HashSet(props.allocatedPointNodeSubset.values().filter((pointNodeRef) => {return pointNodeRef.chunkCoord.x === chunkRef.chunkCoord.x &&pointNodeRef.chunkCoord.y === chunkRef.chunkCoord.y;}));child.rerender({selectedPointNode: props.selectedPointNode,allocatedPointNodeSubset: relevantToChunk})}}}