ZGS4FTTFBXTF2SHYTPAJJBWEUVWVYXPSJVEFI5NYJWTW273B4NHAC MYAKTHOSHICOSCWJPA33Z4VLNB2G27GRO4RUS2764HHIXO5OFKJAC HUH4SI4HXIP72KQSJP2I4ELHX5KUQZM7FFGKZZGJ33DF7E3JHMYQC Z3E7XJOW6NSBDYRDKSGTOAEJSPATAUX4JUFCL4DIL3372GL4K52QC TQ57VE45BHV7MOZ6GKTYZEAMAOTXLPQ3ROCWJ2FUCITQWOYVMUIAC NJ3CLHJNB5S7K7XHXLMDGD3RDVIFIHYNDMFQJF3CUVMAK6E436YQC ED3IWKTSVYYD52TWW5TOC4H63YZ7FTO3E53YO5NELPG5I3RLRAYAC ZHOSSPNKGFIKSFPDXCGLMSYMMX2J433VU2BUUWBKUH7TOLQUBSPQC DLEEYV4V7X337ZJJM775DPARMCMXMLOBXGSBCWDMZBHYKSQTGZCQC }export class ChunkGenConstants {public static CHUNK_DIM = 9; // each chunk is a DIM x DIM grid of nodes, centered on a single nodepublic static CHUNK_HALF_DIM = (ChunkGenConstants.CHUNK_DIM - 1) / 2;public static DROP_NODES_CHANCE = 0.3; // before generating edges, how many of the nodes to throw out
? (UpdaterGeneratorType<T[k]> & { getUpdater: () => UpdaterFn<T[k]> }): { getUpdater: () => UpdaterFn<T[k]> }} & { getUpdater: () => UpdaterFn<T> }
? (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>,}
export type DeepReadonly<T> = T extends Function ? T : {readonly [P in keyof T]: T[P] extends { [k: string]: any } ? DeepReadonly<T[P]> : T[P];}
import { Line } from "../lib/util/geometry/line";import { Vector2 } from "../lib/util/geometry/vector2";import { INTMAX32, squirrel3 } from "../lib/util/random";import * as Pixi from "pixi.js";import { HashMap, HashSet } from "../lib/util/data_structures/hash";import { ChunkGenConstants, PointNodeRef } from "../data/GameState";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;}export class RenderedChunk {// public chunk!: Chunk;public container: Pixi.Container;public renderedNodes: HashMap<Vector2, Pixi.Graphics | Pixi.Sprite> = new HashMap();// constructor(chunk: Chunk, onNodeFocus: (selection: PointNodeRef) => void, texture?: Pixi.Texture) {constructor(args: { onNodeFocus: (selection: PointNodeRef) => void, nodeTexture: Pixi.Texture }) {// render the thingthis.container = new Pixi.Container();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): 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}`);// 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);}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();}}
import { Line } from "../lib/util/geometry/line";import { Vector2 } from "../lib/util/geometry/vector2";import { INTMAX32, squirrel3 } from "../lib/util/random";import * as Pixi from "pixi.js";import { HashMap, HashSet } from "../lib/util/data_structures/hash";import { ChunkGenConstants, GameState, PointNodeRef } from "../data/GameState";import { RenderedChunkConstants } from "./RenderedChunk";import { DeepReadonly, updaterGenerator, UpdaterGeneratorType } from "../lib/util/misc";
public sprite: Pixi.Sprite;public selfPointNodeRef: PointNodeRef; // which node we are// local statepublic justClicked: boolean = false;constructor(args: { texture: Pixi.Texture, selfPointNodeRef: PointNodeRef }) {this.selfPointNodeRef = args.selfPointNodeRef;this.sprite = new Pixi.Sprite(args.texture);this.sprite.anchor.x = 0.5;this.sprite.anchor.y = 0.5;// this.sprite.x = node.x * RenderedChunkConstants.SPACING_PX;// this.sprite.y = node.y * RenderedChunkConstants.SPACING_PX;this.sprite.interactive = true;this.sprite.hitArea = new Pixi.Rectangle(- RenderedChunkConstants.NODE_HITAREA_PX / 2,- RenderedChunkConstants.NODE_HITAREA_PX / 2,RenderedChunkConstants.NODE_HITAREA_PX,RenderedChunkConstants.NODE_HITAREA_PX,);this.sprite.addListener("pointerdown", () => {this.justClicked = true;});}private setTint(args: { isSelected: boolean, isAllocated: boolean }) {if (args.isAllocated) {this.sprite.tint = 0x00AAFF;} else {if (args.isSelected) {this.sprite.tint = 0xBBBBBB;} else {this.sprite.tint = 0xFFFFFF;}}}
private isSelected(gameState: DeepReadonly<GameState>): boolean {return gameState.playerUI.selectedPointNode?.pointNodeId == this.selfPointNodeRef.pointNodeId;}private isAllocated(gameState: DeepReadonly<GameState>): boolean {return gameState.playerSave.allocatedPointNodeSet.get(this.selfPointNodeRef)}public update(args: {gameState: DeepReadonly<GameState>, gameStateUpdater: UpdaterGeneratorType<GameState>,renderedNodeMap: DeepReadonly<HashMap<PointNodeRef, RenderedPointNode>>}) {let { gameState, gameStateUpdater } = args;// sync ourselves with statelet isSelected = this.isSelected(gameState);let isAllocated = this.isAllocated(gameState);if (this.justClicked) {if (!gameState.playerUI.selectedPointNode) {// if nothing is is selected, select ourselves;isSelected = true;this.setTint({ isSelected, isAllocated });gameStateUpdater.playerUI.selectedPointNode.set(this.selfPointNodeRef);} else if (gameState.playerUI.selectedPointNode.pointNodeId == this.selfPointNodeRef.pointNodeId) {// if we were already selected, try to allocate ourselvesif (!isAllocated) {isAllocated = true;// save our allocation to state// TODO(bowei): this code block should be somewhere else????gameStateUpdater.playerSave.allocatedPointNodeSet.update(set => {set.put(this.selfPointNodeRef);return set;})gameStateUpdater.playerSave.allocatedPointNodeHistory.update(history => {history.push(this.selfPointNodeRef);return history;})}this.setTint({ isSelected, isAllocated });} else {// if something other than ourselves is selected, unselect it and select ourselves;isSelected = true;let otherNode = args.renderedNodeMap.get(gameState.playerUI.selectedPointNode);gameStateUpdater.playerUI.selectedPointNode.set(this.selfPointNodeRef);otherNode.setTint({isSelected: false,isAllocated: otherNode.isAllocated(gameState)});this.setTint({ isSelected: true, isAllocated})}}// don't forget to reset state!this.justClicked = false;}