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, PointNodeComponentProps } from "./PointNodeComponent"; import { UpdaterGeneratorType2 } from "../../lib/util/updaterGenerator"; import { Vector2 } from "../../lib/util/geometry/vector2"; import { PixiPointFrom } from "../../lib/pixi/pixify"; import { engageLifecycle, LifecycleHandlerBase } from "./LifecycleHandler"; import { RootComponentState } from "./RootComponent"; 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 SPACING_PX: number = 36; public static CHUNK_SPACING_PX: number = (ChunkGenConstants.CHUNK_DIM + 0.0) * RenderedChunkConstants.SPACING_PX; public static NODE_SIZE_PX: number = 22; public static NODE_HITAREA_PX: number = RenderedChunkConstants.NODE_SIZE_PX + 4; public static NODE_ROUNDED_PX: number = 4; } type Props = { delta: number, args: { pointNodeTexture: Pixi.Texture, markForceUpdate: (childInstance: any) => void, }, selfChunkRef: ChunkRef, updaters: UpdaterGeneratorType2<GameState>, tooltipUpdaters: UpdaterGeneratorType2<RootComponentState>['tooltip'], position: Vector2, chunkGen: ChunkGen, selectedPointNode: PointNodeRef | undefined, allocatedPointNodeSubset: HashSet<PointNodeRef>, } type State = {} class ChunkComponent2 extends LifecycleHandlerBase<Props, State> { public container: Pixi.Container; public state: State; public children: KeyedHashMap<PointNodeRef, PointNodeComponent>; constructor(props: Props) { super(props); this.state = {}; this.container = new Pixi.Container(); this.children = new KeyedHashMap(); this.resyncChildren(props); } protected renderSelf(props: Props) { this.container.position = PixiPointFrom(props.position); } protected shouldUpdate(prevProps: Props, prevState: State, props: Props, state: State): boolean { // return true; for (let key of (Object.keys(prevProps) as (keyof Props)[])) { if (key === 'delta' || key === 'args' || key === 'updaters') { continue; } if (key === 'position') { if (!prevProps[key].equals(props[key])) { console.log(`chunk shouldUpdate differed in ${key}, returning true`); return true; } else { continue; } } if (key === 'selectedPointNode') { if (prevProps[key]?.hash() !== props[key]?.hash()) { console.log(`chunk shouldUpdate differed in ${key}, returning true`); return true; } else { continue; } } if (key === 'selfChunkRef') { if (prevProps[key]?.hash() !== props[key]?.hash()) { console.log(`chunk shouldUpdate differed in ${key}, returning true`); return true; } else { continue; } } if (key === 'allocatedPointNodeSubset') { // subsets could be different objects but have the same contents if (!prevProps[key].equals(props[key])) { // console.log(`prevProps: ${JSON.stringify(prevProps[key])}`); // console.log(`props: ${JSON.stringify(props[key])}`); console.log(`chunk shouldUpdate differed in ${key}, returning true`); return true; } else { continue; } } if (prevProps[key] !== props[key]) { console.log(`chunk shouldUpdate differed in ${key}, returning true`); return true; } } return false; } private resyncChildren(props: Props) { let childrenToDelete = this.children.clone(); // track which children need to be destroyed according to new props console.log(`chunk component upsert children got here`); // console.log(`chunk component upsert children has ${this.children.size()} children`); for (let [pointNodeCoord, pointNodeGen] of props.chunkGen.pointNodes.entries()) { const pointNodeRef = new PointNodeRef({ z: props.selfChunkRef.z, chunkCoord: props.selfChunkRef.chunkCoord, pointNodeCoord: pointNodeCoord, pointNodeId: pointNodeGen.id }) let childPropsFactory = (props: Props, state: State) : PointNodeComponentProps => { return { delta: props.delta, args: { pointNodeTexture: props.args.pointNodeTexture, markForceUpdate: this.markForceUpdate, }, selfPointNodeRef: pointNodeRef, updaters: props.updaters, tooltipUpdaters: props.tooltipUpdaters, position: pointNodeRef.pointNodeCoord.multiply(RenderedChunkConstants.SPACING_PX), pointNodeGen, isSelected: props.selectedPointNode?.pointNodeId === pointNodeRef.pointNodeId, isAllocated: props.allocatedPointNodeSubset.contains(pointNodeRef), }; } const childKey = pointNodeRef; let childComponent = this.children.get(childKey); if (childComponent) { // childComponent.update(childPropsFactory(props, this.state)); childrenToDelete.remove(childKey); } else { childComponent = new PointNodeComponent(childPropsFactory(props, this.state)); this.children.put(pointNodeRef, childComponent); // this.container.addChild(childComponent.container); this.addChild({ childClass: PointNodeComponent, instance: childComponent, propsFactory: childPropsFactory }); } } console.log(`chunk component to delete has ${childrenToDelete.size()} children`); for (let [childKey, childComponent] of childrenToDelete.entries()) { // childComponent.willUnmount(); this.children.remove(childKey); // this.container.removeChild(childComponent.container); this.removeChild(childComponent); } } protected updateChildren(props: Props) { this.resyncChildren(props); } protected didForceUpdateChild(instance: LifecycleHandlerBase<any, any>) { // IMPORTANT! this is intended to raise the child that asked for a force update to the top so it isn't covered // by other sibling pixi containers. however this code doesnt work well during the update call, for some reason (not sure why) this.container.removeChild(instance.container); this.container.addChild(instance.container); } } const wrapped = engageLifecycle(ChunkComponent2); // eslint-disable-next-line type wrapped = ChunkComponent2; export { wrapped as ChunkComponent }; export type { Props as ChunkComponentProps };