import * as Pixi from "pixi.js";
import { RenderedChunkConstants } from "./ChunkComponent";
import { UpdaterGeneratorType2 } from "../../lib/util/updaterGenerator";
import { GameState, PointNodeGen, PointNodeRef, ResourceModifier, ResourceType } from "../../data/GameState";
import { Vector2 } from "../../lib/util/geometry/vector2";
import { PixiPointFrom } from "../../lib/pixi/pixify";
import { multiplyColor } from "../../lib/util/misc";
import { TooltippableAreaComponent } from "./TooltippableAreaComponent";
import { engageLifecycle, LifecycleHandlerBase } from "./LifecycleHandler";
import { selectOrReselectNode } from "../../game/OnSelectOrReselectNode";
import { RootComponentState } from "./RootComponent";
type Props = {
delta: number,
args: {
pointNodeTexture: Pixi.Texture,
markForceUpdate: (childInstance: any) => void,
},
selfPointNodeRef: PointNodeRef,
updaters: UpdaterGeneratorType2<GameState>,
tooltipUpdaters: UpdaterGeneratorType2<RootComponentState>['tooltip'],
position: Vector2,
pointNodeGen: PointNodeGen,
isSelected: boolean,
isAllocated: boolean
};
type State = {
numClicks: number // debug
descriptionText: string;
}
class PointNodeComponent extends LifecycleHandlerBase<Props, State> {
public container: Pixi.Container;
public state: State;
public sprite: Pixi.Sprite
public halfwayCenterSprite: Pixi.Sprite;
public centerSprite: Pixi.Sprite;
public hitArea: Pixi.IHitArea;
public tooltippableArea?: TooltippableAreaComponent
constructor(props: Props) {
super(props);
this.state = {
numClicks: 0,
descriptionText: '',
};
this.updateSelf(props); // initialize the description text properly
this.container = new Pixi.Container();
this.container.sortableChildren = true;
this.sprite = new Pixi.Sprite(props.args.pointNodeTexture);
this.sprite.anchor.x = 0.5;
this.sprite.anchor.y = 0.5;
this.sprite.zIndex = -1;
this.container.addChild(this.sprite);
this.centerSprite = new Pixi.Sprite(props.args.pointNodeTexture);
this.centerSprite.anchor.x = 0.5;
this.centerSprite.anchor.y = 0.5;
this.centerSprite.scale = PixiPointFrom(new Vector2(0.5, 0.5));
this.centerSprite.zIndex = 1;
this.centerSprite.alpha = 0; // TESTING
// this.container.addChild(this.centerSprite);
this.halfwayCenterSprite = new Pixi.Sprite(props.args.pointNodeTexture);
this.halfwayCenterSprite.anchor.x = 0.5;
this.halfwayCenterSprite.anchor.y = 0.5;
this.halfwayCenterSprite.scale = PixiPointFrom(new Vector2(0.75, 0.75));
this.halfwayCenterSprite.zIndex = 0;
// disable this sprite for now - causes a fairly significant fps hit, until we get around to holding less nodes on the page at once
this.halfwayCenterSprite.alpha = 0;
// this.container.addChild(this.halfwayCenterSprite);
this.container.interactive = true;
// NOTE(bowei): ive tested, the following 2 settings don't significantly affect FPS
this.container.buttonMode = true;
this.hitArea = new Pixi.Rectangle(
- RenderedChunkConstants.NODE_HITAREA_PX / 2,
- RenderedChunkConstants.NODE_HITAREA_PX / 2,
RenderedChunkConstants.NODE_HITAREA_PX,
RenderedChunkConstants.NODE_HITAREA_PX,
);
// note: hitarea breaks child onhover: https://github.com/pixijs/pixi.js/issues/5837
this.container.hitArea = this.hitArea;
// const tooltippableAreaPropsFactory = (p: Props, s: State) => {
// let nodeDescription: string = "Nothing (empty node)";
// if (p.pointNodeGen.resourceType !== ResourceType.Nothing) {
// nodeDescription = `${p.pointNodeGen.resourceAmount} ${p.pointNodeGen.resourceModifier} ${p.pointNodeGen.resourceType}`;
// }
// return {
// args: {
// markForceUpdate: this.markForceUpdate,
// },
// text: nodeDescription,
// hitArea: this.hitArea, // TODO(bowei): move into state???
// }
// }
// this.tooltippableArea = new TooltippableAreaComponent(tooltippableAreaPropsFactory(props, this.state));
// this.container.addChild(this.tooltippableArea.container);
// this.addChild({
// childClass: TooltippableAreaComponent,
// instance: this.tooltippableArea,
// propsFactory: tooltippableAreaPropsFactory,
// });
}
protected updateSelf(props: Props) {
let nodeDescription: string = "Nothing (empty node)";
if (props.pointNodeGen.resourceType !== ResourceType.Nothing) {
nodeDescription = `${props.pointNodeGen.resourceAmount} ${props.pointNodeGen.resourceModifier} ${props.pointNodeGen.resourceType}`;
}
this.state.descriptionText = nodeDescription;
}
protected renderSelf(props: Props) {
this.container.position = PixiPointFrom(props.position);
let tint: number;
let centerTint: number;
if (props.isSelected) {
tint = 0xBBBBFF;
centerTint = 0xBBBBFF;
} else {
tint = 0xFFFFFF;
centerTint = 0xFFFFFF;
}
if (props.isAllocated) {
tint = 0x444444;
} else {
}
let baseColor: number = 0;
if (props.pointNodeGen.resourceType === ResourceType.Nothing) {
baseColor = 0x99bbff; // blue that mixes in with bg
} else if (props.pointNodeGen.resourceType === ResourceType.Mana0) {
if (props.pointNodeGen.resourceModifier === ResourceModifier.Flat) {
baseColor = 0xeeaaaa; // pink
} else if (props.pointNodeGen.resourceModifier === ResourceModifier.Increased0) {
baseColor = 0xcc88ee; // lavender?
}
}
// switch (props.pointNodeGen.resourceType) {
// case ResourceType.Nothing:
// baseColor = 0x99bbff; // blue that mixes in with bg
// break;
// case ResourceType.Mana0:
// baseColor = 0xeeaaaa; // red
// break;
// case ResourceType.Mana1:
// baseColor = 0xbb7733; // brown?
// break;
// case ResourceType.Mana2:
// baseColor = 0x44aa44; // green
// break;
// }
this.sprite.tint = multiplyColor(baseColor, tint);
this.centerSprite.tint = multiplyColor(baseColor, centerTint);
// NOTE(bowei): careful, we dont want to set the scale of our tooltip to be bigger
if (props.selfPointNodeRef.pointNodeCoord.equals(Vector2.Zero)) {
this.container.scale = PixiPointFrom(new Vector2(1.25, 1.25));
}
}
protected shouldUpdate(staleProps: Props, staleState: State, props: Props, state: State): boolean {
for (let key of (Object.keys(staleProps) as (keyof Props)[])) {
if (key === 'delta' || key === 'args' || key === 'updaters') { continue; }
if (staleProps[key] !== props[key]) {
return true;
}
if (key === 'position') {
if (!staleProps[key].equals(props[key])) {
console.log(`chunk shouldUpdate differed in ${key}, returning true`);
return true;
} else {
continue;
}
}
if (key === 'selfPointNodeRef') {
if (staleProps[key]?.hash() !== props[key]?.hash()) {
console.log(`chunk shouldUpdate differed in ${key}, returning true`);
return true;
} else {
continue;
}
}
}
return false;
}
protected didMount() {
const { updaters } = this._staleProps; // we assume this will never change
// this.container.addListener('pointerover', (event: Pixi.InteractionEvent) => {
// this.state.pointerover = event;
// })
// this.container.addListener('pointerout', () => {
// this.state.pointerover = undefined;
// })
//
this.container.addListener("pointerdown", (event: Pixi.InteractionEvent) => {
this._staleProps.args.markForceUpdate(this);
this.state.numClicks++;
selectOrReselectNode(updaters, this._staleProps.selfPointNodeRef);
// event.stopPropagation();
});
this.container.addListener('pointerover', (event: Pixi.InteractionEvent) => {
// this._staleProps.args.markForceUpdate(this);
// source: https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/tooltip.js
const localPosition = event.data.getLocalPosition(this.container);
const position = new Vector2(this.container.worldTransform.tx, this.container.worldTransform.ty);
// const position = new Vector2(this.container.worldTransform.tx, this.container.worldTransform.ty);
this._staleProps.tooltipUpdaters.enqueueUpdate((prev) => {
const next = { ...prev, visible: true, text: this.state.descriptionText, position: position.add(localPosition) };
// console.log({ next });
return next;
})
});
this.container.addListener('pointerout', (event: Pixi.InteractionEvent) => {
// this._staleProps.args.markForceUpdate(this);
this._staleProps.tooltipUpdaters.enqueueUpdate((prev) => {
const next = { ...prev, visible: false, text: '' };
return next;
})
});
this.container.addListener('pointermove', (event: Pixi.InteractionEvent) => {
// this._staleProps.args.markForceUpdate(this);
// source: https://www.iwm-tuebingen.de/iwmbrowser/lib/pixi/tooltip.js
const localPosition = event.data.getLocalPosition(this.container);
const position = new Vector2(this.container.worldTransform.tx, this.container.worldTransform.ty);
this._staleProps.tooltipUpdaters.position.enqueueUpdate(position.add(localPosition));
})
}
public toString(): string {
return "PointNodeCompoent " + this._staleProps.selfPointNodeRef.toString();
}
}
const wrapped = engageLifecycle(PointNodeComponent);
// eslint-disable-next-line
type wrapped = PointNodeComponent;
export { wrapped as PointNodeComponent };
export type { Props as PointNodeComponentProps };