PECLRX4APYEWDBPHDM2YDWFF4O6VHK2MJYETSQGKIRKAEBS254YQC 2EGPRWETVM2XPSRMGYM25Y46RXPW7IOCN6MDIV6POCPHNOEGSMHAC 627ZCA4JTONT2DQPA2FU44FRSCXZGA6P3YAGTPL76K3UJVYGMFWAC T46XFRUL5T6SFCNY34NJFKJXDO3UTN6RZFLBVTAQZ4XFUEFBHRLQC KHAKSWQANUZL7B3EYF6GWTDGSUR2WVZGU5BPPI6OKYIPCD3QMWAQC LDXXGEDWISRDMLBW7L2NG6X5757WVFHO7VULU4CINL2FN7MHLEKQC JG36CDUKVUWJT25PJVMWKWI4KFRHM24PFBKPXXRY3D2XVDKUVI7QC M7KYKQQLNUSF3YVPTEJCYGJRW2RS3N7AWSSZ75IDYP3IM2LNRT4QC NOJDNQ5JIUX4ZIAQQL3KKIIUC5W6QZJ3H6RWUIUZFV5GQEJDFLLQC M4DT5MAD3TATSVAT3MFNABHONWXOW443XD5W6DGEVVYHVITXVBDAC NQA6YRMRCF7LIHPYWLC355FPCH7CYA3QKSKQHSNJHA2XHOCWZXCAC FK53AOEMTSFJOB5TCHLDOE2EHAE5W5P2LV3ZNENYSGQGV3UCBWRAC import { Application, Renderer, Point } from "pixi.js";import { Entity } from "./entity";import { Debug } from "./debug";import { HashSet } from "./data_structures/hash";import { TypesafeLoader, AllResourcesType } from "./typesafe_loader";import { CreateGame as ReactMountGame } from "./react/react_root";import { Camera } from "./camera";import { DebugFlagsType } from "./react/debug_flag_buttons";import { CollisionHandler } from "./collision_handler";import { Rect } from "./geometry/rect";// import { CoroutineManager } from "./coroutine_manager";import { BaseGameState } from "./base_state";export let GameReference: BaseGame<any>;export type GameArgs = {scale: number;canvasWidth: number;canvasHeight: number;tileHeight: number;tileWidth: number;backgroundColor: number;debugFlags: DebugFlagsType;state: Omit<IGameState, keyof BaseGameState>;assets: TypesafeLoader<any>;};export const StageName = "Stage";export const FixedStageName = "FixedStage";export const ParallaxStageName = "ParallaxStage";export class BaseGame<TResources extends AllResourcesType = {}> {app: PIXI.Application;state: IGameState;* The root of the display hierarchy for the game. Everything that exists in* the game that isn't fixed as the camera moves should be under this.*/stage: Entity;parallaxStage: Entity;/*** A stage for things in the game that don't move when the camera move and are* instead fixed to the screen. For example, the HUD.*/fixedCameraStage: Entity;private assets: TypesafeLoader<TResources>;renderer: Renderer;camera: Camera;collisionHandler: CollisionHandler;// coroutineManager: CoroutineManager;constructor(props: GameArgs) {GameReference = this;// this.coroutineManager = new CoroutineManager(this);this.state = {...props.state,if (!view) {}this.collisionHandler = new CollisionHandler({canvasWidth: props.canvasWidth / props.scale,canvasHeight: props.canvasHeight / props.scale,tileHeight: props.tileHeight,tileWidth: props.tileWidth,});this.app = new Application({width: props.canvasWidth,height: props.canvasHeight,powerPreference: "low-power",antialias: false,transparent: false,resolution: window.devicePixelRatio,autoDensity: true,view: view as HTMLCanvasElement,});this.app.stage.scale = new Point(props.scale, props.scale);this.parallaxStage = new Entity({ name: ParallaxStageName });this.stage = new Entity({ name: StageName });this.fixedCameraStage = new Entity({ name: FixedStageName });this.state.stage = this.stage;this.app.stage.addChild(this.parallaxStage.sprite);this.app.stage.addChild(this.stage.sprite);this.app.stage.addChild(this.fixedCameraStage.sprite);this.state.renderer = this.app.renderer;this.state.stage = this.stage;this.assets = props.assets;this.assets.onLoadComplete(() => this.startGameLoop());this.assets.onLoadComplete(() => this.initialize());this.renderer = this.app.renderer;this.camera = new Camera({stage: this.stage,state: this.state,canvasWidth: props.canvasWidth,canvasHeight: props.canvasHeight,scale: props.scale,bounds: new Rect({ x: -5000, y: -5000, width: 10000, height: 10000 }),});this.state.camera = this.camera;this.stage.sprite.sortableChildren = true;this.fixedCameraStage.sprite.sortableChildren = true;}/*** Called after resources are finished loading.*/startGameLoop = () => {this.app.ticker.add(() => this.gameLoop());};gameLoop() {Debug.Clear();const { entities } = this.state;if (!this.state.lastCollisionGrid) {const grid = this.collisionHandler.buildCollisionGrid({bounds: new Rect({ x: 0, y: 0, width: 5000, height: 5000 }),entities: this.state.entities,});this.state.lastCollisionGrid = grid;}this.state.tick++;this.state.keys.update();for (const entity of entities.values()) {entity.baseUpdate(this.state);}for (const entity of this.state.toBeDestroyed) {if (entity.sprite.parent) {entity.sprite.parent.removeChild(entity.sprite);}// this.coroutineManager.stopCoroutinesOwnedBy(entity);}this.state.toBeDestroyed = [];const grid = this.collisionHandler.buildCollisionGrid({bounds: this.camera.getBounds(),entities: activeEntities,});this.state.lastCollisionGrid = grid;this.collisionHandler.resolveCollisions({entities: activeEntities,grid: grid,});this.camera.update(this.state);// this.coroutineManager.updateCoroutines(this.state);// let foo = Debug.GetDrawnObjects();// for (const f of Debug.GetDrawnObjects()) {// if (f instanceof AugmentedSprite) {// }// }// }// let foo = Debug.GetDrawn();Debug.ResetDrawCount();}}// if (f.width > 1024) {// f.visible = false;const activeEntities = new HashSet(this.state.entities.values().filter((e) => e.activeModes.includes(this.state.mode)));this.state.entities = new HashSet(entities.values().filter((ent) => !this.state.toBeDestroyed.includes(ent)));initialize() {}// ReactMountGame(this, props.debugFlags);backgroundColor: props.backgroundColor,// throw new Error("I couldn't find an element named #canvas on initialization. Giving up!")const view = document.getElementById("canvas");};...new BaseGameState(),/**import { IGameState } from "Library";
import { Renderer } from "pixi.js";import { KeyboardState } from "./keyboard";import { Entity } from "./entity";import { HashSet } from "./data_structures/hash";import { IGameState } from "Library";import { Mode } from "Library";import { CollisionGrid } from "./collision_grid";import { Camera } from "./camera";export class BaseGameState implements Partial<IGameState> {keys: KeyboardState;entities = new HashSet<Entity>();toBeDestroyed: Entity[] = [];spriteToEntity: { [key: number]: Entity } = {};mode: Mode = "Normal";lastCollisionGrid!: CollisionGrid;constructor() {this.keys = new KeyboardState();}}stage!: Entity;renderer!: Renderer;camera!: Camera;
// 1. Encode font into dataurl// 2. Use dataurl in SVG (otherwise you wouldnt be able to refer to the font in the SVG).// 3. Load the SVG into an image// 4. Render the image to a canvas// 5. Use the canvas as a texture for a Sprite// 6. Waste 30 minutes trying to debug your code only to realize it was because// there was a missing ' in font_data_urlexport const PIXEL_RATIO = (() => {const ctx = document.createElement("canvas").getContext("2d")!,return dpr / bsr;})();export class BaseTextEntity<T extends BaseGameState> extends Entity {protected _html: string;constructor(html: string, width: number, height: number) {super({texture: Texture.WHITE,});this.sprite.height = height;this.buildTextGraphic();}set html(value: string) {if (this._html !== value) {this._html = value;}}// converting woff into dataurl:// https://gist.github.com/viljamis/c4016ff88745a0846b94// https://stackoverflow.com/questions/12652769/rendering-html-elements-to-canvasconst wrappedHtml = `</div>`;<foreignObject width="100%" height="100%"><defs><style type="text/css">@font-face {font-family: FreePixel;}</style></defs></foreignObject></svg>`;const img = new Image();img.onload = () => {ctx.clearRect(0, 0, this.width, this.height);ctx.drawImage(img, x, y);resolve();};img.src = data;});}private htmlToXML(html: string): string {doc.write(html);// the HTML document to a string as opposed to appending it to a// <foreignObject> in the DOM// Get well-formed markupreturn html;}}const can = document.createElement("canvas");can.style.height = h + "px";can.getContext("2d")!.setTransform(ratio, 0, 0, ratio, 0, 0);return can;}protected async buildTextGraphic() {this.sprite.texture = Texture.from(this.canvas);this.sprite.texture.update();}clear() {this.context.clearRect(0, 0, this.width, this.height);this.sprite.texture = Texture.from(this.canvas);this.sprite.texture.update();}}await this.renderHTMLToCanvas(this._html,this.context,0,0,this.width,this.height);can.width = w * ratio;can.height = h * ratio;can.style.width = w + "px";private createHiDPICanvas(w: number,h: number,ratio: number | undefined = undefined) {if (ratio === undefined) {ratio = PIXEL_RATIO;html = new XMLSerializer().serializeToString(doc.body);doc.documentElement.setAttribute("xmlns",doc.documentElement.namespaceURI!);// You must manually set the xmlns if you intend to immediately serializeconst doc = document.implementation.createHTMLDocument("");await new Promise((resolve) => {${this.htmlToXML(wrappedHtml)}src: ${FontDataUrl}const data = `data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}"><div style="width: ${this.width}">${html}private async renderHTMLToCanvas(html: string,ctx: CanvasRenderingContext2D,x: number,y: number,width: number,height: number) {// reference used for this insanity:update() {}this.buildTextGraphic();this._html = html;this.canvas = this.createHiDPICanvas(this.width, this.height);this.context = this.canvas.getContext("2d")!;this.sprite.width = width;name: "BaseTextEntity",canvas: HTMLCanvasElement;context: CanvasRenderingContext2D;dpr = window.devicePixelRatio || 1,bsr =(ctx as any).webkitBackingStorePixelRatio ||(ctx as any).mozBackingStorePixelRatio ||(ctx as any).msBackingStorePixelRatio ||(ctx as any).oBackingStorePixelRatio ||(ctx as any).backingStorePixelRatio ||1;import { Texture } from "pixi.js";import { FontDataUrl } from "./font_data_url";import { Entity } from "./entity";import { BaseGameState } from "./base_state";
import { Vector2, IVector2 } from "./geometry/vector2";import { Entity } from "./entity";import { Rect } from "./geometry/rect";import { Debug } from "./debug";import { IGameState } from "Library";export class Camera {private static LERP_SPEED_X = 0.03;private static LERP_SPEED_Y = 0.4;/*** Top left coordinate of the camera.*/private _position = Vector2.Zero;private _desiredPosition = Vector2.Zero;private _stage: Entity;private _canvasWidth: number;private _canvasHeight: number;private _currentBounds: Rect;constructor(props: {stage: Entity;state: IGameState;canvasWidth: number;canvasHeight: number;scale: number;bounds: Rect;}) {this._stage = props.stage;this._canvasWidth = props.canvasWidth / props.scale;this._canvasHeight = props.canvasHeight / props.scale;this._currentBounds = props.bounds;this._desiredPosition = this._position;}public get center(): Vector2 {return new Vector2({x: this._position.x + this._canvasWidth / 2,});}public setBounds(newBounds: Rect) {this._currentBounds = newBounds;}public getBounds(): Rect {return this._currentBounds;}public cameraFrame(): Rect {return new Rect({x: this.center.x - this._canvasWidth / 2,y: this.center.y - this._canvasHeight / 2,width: this._canvasWidth,height: this._canvasHeight,});}private halfDimensions(): Vector2 {return new Vector2({x: this._canvasWidth / 2,});}private _immediatelyCenterOn = (position: IVector2) => {this._position = new Vector2(position).subtract(this.halfDimensions());};centerOn = (position: IVector2, immediate = false) => {if (immediate) {this._immediatelyCenterOn(position);} else {}};calculateDesiredPosition = (): Vector2 => {let desiredPosition = this._desiredPosition;const currentBounds = this._currentBounds;if (!currentBounds) {console.error("no region for camera!");return desiredPosition;}}// fit the camera rect into the regions rectif (desiredPosition.x < currentBounds.left) {desiredPosition = desiredPosition.withX(currentBounds.left);}if (desiredPosition.x + this.cameraFrame().width > currentBounds.right) {}if (desiredPosition.y < currentBounds.top) {desiredPosition = desiredPosition.withY(currentBounds.top);}if (desiredPosition.y + this.cameraFrame().height > currentBounds.bottom) {}return desiredPosition;};update = (state: IGameState) => {if (Debug.DebugMode) {return;}const desiredPosition = this.calculateDesiredPosition();this._position = new Vector2(Math.floor(this._position.x / 4) * 4,Math.floor(this._position.y / 4) * 4);this._stage.x = Math.floor(-this._position.x);this._stage.y = Math.floor(-this._position.y);};}this._position = this._position.lerp2D(desiredPosition,Camera.LERP_SPEED_X,Camera.LERP_SPEED_Y);desiredPosition = desiredPosition.withY(currentBounds.bottom - this._canvasHeight);desiredPosition = desiredPosition.withX(currentBounds.right - this._canvasWidth);if (currentBounds.width < this._canvasWidth ||currentBounds.height < this._canvasHeight) {throw new Error(`There is a region on the map which is too small for the camera at x: ${currentBounds.x} y: ${currentBounds.y}.`);this._desiredPosition = new Vector2(position).subtract(this.halfDimensions());y: this._canvasHeight / 2,y: this._position.y + this._canvasHeight / 2,this._immediatelyCenterOn(new Vector2({x: this._canvasWidth / 2,y: this._canvasHeight / 2,}));
import { Graphics } from "pixi.js";import { Rect } from "./geometry/rect";import { Entity } from "./entity";import { Vector2 } from "./geometry/vector2";import { DefaultGrid } from "./data_structures/default_grid";import { RectGroup } from "./geometry/rect_group";import { Debug } from "./debug";export type CollisionResultRect = {firstRect: Rect;secondRect: Rect;otherEntity?: Entity;thisEntity?: Entity;overlap: Rect;};type CollisionResultPoint = {firstRect: Rect;secondRect: Rect;firstEntity?: Entity;secondEntity?: Entity;overlap: Vector2;};export class CollisionGrid {private _position: Vector2 = Vector2.Zero;private _width: number;private _height: number;private _cellSize: number;private _numCellsPerRow: number;private _numCellsPerCol: number;private _cells: DefaultGrid<Cell>;private _renderLines: Graphics | null = null;constructor(props: { width: number; height: number; cellSize: number }) {const { width, height, cellSize } = props;this._width = width;this._height = height;this._cellSize = cellSize;this._numCellsPerRow = Math.ceil(width / cellSize);this._numCellsPerCol = Math.ceil(height / cellSize);this._cells = new DefaultGrid<Cell>((x, y) =>new Cell(new Vector2({ x: x * cellSize, y: y * cellSize }), cellSize));}debug() {for (let x = 0; x < 10; x++) {for (let y = 0; y < 10; y++) {// Draw cellDebug.DrawRect(new Rect({x: x * this._cellSize,y: y * this._cellSize,width: this._cellSize,height: this._cellSize,}),0xff0000,true,"fixed");for (const obj of this._cells.get(x, y).colliders) {Debug.DrawRect(obj.rect, 0xff0000, true, "fixed");}}}}public get topLeft() {return this._position;}public get center() {return this._position.add({ x: this._width / 2, y: this._height / 2 });}/*** Checks if the provided rect would collide with anything on the grid. If an* entity is passed in, ignores that entity when checking for collisions.* (Does not add the rect to the grid.)*/getRectCollisions = (rect: Rect,skipEntity?: Entity): CollisionResultRect[] => {const cells: Cell[] = [];const lowX = Math.floor(rect.x / this._cellSize);const highX = Math.ceil((rect.x + rect.width) / this._cellSize);const lowY = Math.floor(rect.y / this._cellSize);const highY = Math.ceil((rect.y + rect.height) / this._cellSize);for (let x = lowX; x < highX; x++) {for (let y = lowY; y < highY; y++) {cells.push(this._cells.get(x, y));}}const collisions: CollisionResultRect[] = [];for (const cell of cells) {for (const { rect: rectInCell, entity: entityInCell } of cell.colliders) {if (entityInCell === skipEntity) {continue;}const overlap = rect.getIntersection(rectInCell);if (overlap) {collisions.push({firstRect: rectInCell,otherEntity: entityInCell,secondRect: rect,thisEntity: skipEntity,overlap,});}}}return collisions;};getRectGroupCollisions = (group: RectGroup,entity?: Entity): CollisionResultRect[] => {let collisions: CollisionResultRect[] = [];for (const rect of group.getRects()) {collisions = [...collisions, ...this.getRectCollisions(rect, entity)];}return collisions;};/*** Same as collidesRect but immediately returns true if there's a collision.*/collidesRectFast = (rect: Rect, entity?: Entity): boolean => {const corners = rect.getCorners();const cells = corners.map((corner) =>this._cells.get(Math.floor(corner.x / this._cellSize),Math.floor(corner.y / this._cellSize)));const uniqueCells: { [key: string]: Cell } = {};for (const cell of cells) {uniqueCells[cell.hash()] = cell;}const values = Object.values(uniqueCells);for (const cell of values) {for (const { rect: rectInCell, entity: entityInCell } of cell.colliders) {if (entityInCell === entity) {continue;}const overlap = rect.intersects(rectInCell);if (overlap) {return true;}}}return false;};collidesPoint = (point: Vector2,takeFirst = false): CollisionResultPoint[] => {const cell = this._cells.get(Math.floor(point.x / this._cellSize),Math.floor(point.y / this._cellSize));const collisions: CollisionResultPoint[] = [];for (const { rect, entity: entityInCell } of cell.colliders) {const overlap = rect.contains(point);if (overlap) {collisions.push({firstRect: rect,firstEntity: entityInCell,secondRect: rect,secondEntity: undefined,overlap: point,});if (takeFirst) {return collisions;}}}return collisions;};/*** Get all collisions on the grid.*/getAllCollisions = (): CollisionResultRect[] => {const result: CollisionResultRect[] = [];for (let cell of this.cells) {const cellRects = cell.colliders;for (let i = 0; i < cellRects.length; i++) {for (let j = i; j < cellRects.length; j++) {if (i === j) continue;const collider1 = cellRects[i];const collider2 = cellRects[j];const intersection = collider1.rect.getIntersection(collider2.rect,false);if (intersection !== undefined) {result.push({firstRect: collider1.rect,secondRect: collider2.rect,otherEntity: collider1.entity,thisEntity: collider2.entity,overlap: intersection,});}}}}return result;};public get cells(): Cell[] {return this._cells.values();}clear = () => {for (const cell of this._cells.values()) {cell.removeAll();}};// Add a rect to the hash grid.// Checks each corner, to handle entities that span multiply grid cells.add = (rect: Rect, associatedEntity?: Entity) => {const startX = Math.floor(rect.x / this._cellSize);const stopX = Math.floor(rect.right / this._cellSize);const startY = Math.floor(rect.y / this._cellSize);const stopY = Math.floor(rect.bottom / this._cellSize);for (let x = startX; x <= stopX; x++) {for (let y = startY; y <= stopY; y++) {this._cells.get(x, y).add(rect, associatedEntity);}}};addRectGroup = (group: RectGroup, associatedEntity?: Entity) => {for (const rect of group.getRects()) {this.add(rect, associatedEntity);}};}type CellItem = {rect: Rect;entity?: Entity;};export class Cell {private _bounds: Rect;private _rects: CellItem[] = [];constructor(topLeft: Vector2, cellSize: number) {this._bounds = Rect.FromPoint(topLeft, cellSize);}public get colliders(): CellItem[] {return this._rects;}add = (rect: Rect, entity?: Entity) => {this._rects.push({ rect, entity });};removeAll = () => {this._rects = [];};hash(): string {return this._bounds.toString();}}
import { Entity } from "./entity";import { Vector2 } from "./geometry/vector2";import { CollisionGrid, CollisionResultRect } from "./collision_grid";import { HashSet } from "./data_structures/hash";import { Rect } from "./geometry/rect";import { RectGroup } from "./geometry/rect_group";export type HitInfo = {hit: boolean;left?: boolean;right?: boolean;up?: boolean;down?: boolean;collisions: CollisionResultRect[];interactions: CollisionResultRect[];};export class CollisionHandler {private _canvasWidth: number;private _canvasHeight: number;private _tileSize: number;constructor(props: {canvasWidth: number;canvasHeight: number;tileWidth: number;tileHeight: number;}) {if (props.tileWidth !== props.tileHeight) {}this._canvasWidth = props.canvasWidth;this._canvasHeight = props.canvasHeight;this._tileSize = props.tileWidth;}buildCollisionGrid = (props: {entities: HashSet<Entity>;bounds: Rect;}): CollisionGrid => {const { entities, bounds } = props;const grid = new CollisionGrid({width: 2 * this._canvasWidth,height: 2 * this._canvasHeight,cellSize: 4 * this._tileSize,});for (const entity of collideableEntities) {if (collisionRect.intersects(bounds)) {const rectOrRectGroup = collisionRect;if (rectOrRectGroup instanceof Rect) {grid.add(rectOrRectGroup, entity);} else {grid.addRectGroup(rectOrRectGroup, entity);}}}return grid;};const xHits =bounds instanceof Rect? grid.getRectCollisions(bounds, entity): grid.getRectGroupCollisions(bounds, entity);return {hits,interactions,};resolveCollisions = (props: {entities: HashSet<Entity>;grid: CollisionGrid;}) => {const { entities, grid } = props;for (const entity of entities.values()) {const hitInfo: HitInfo = {hit: false,collisions: [],interactions: [],};const xVelocity = new Vector2({ x: entity.velocity.x, y: 0 });const yVelocity = new Vector2({ x: 0, y: entity.velocity.y });let delta = Vector2.Zero;// resolve x-axisdelta = delta.add(xVelocity);updatedBounds = updatedBounds.add(xVelocity);if (xHits.length > 0) {hitInfo.hit = true;hitInfo.right = entity.velocity.x > 0;hitInfo.left = entity.velocity.x < 0;hitInfo.collisions = [...hitInfo.collisions, ...xHits];delta = delta.subtract(xVelocity);updatedBounds = updatedBounds.subtract(xVelocity);for (let x = 0; x < xVelocity.x; x++) {updatedBounds = updatedBounds.add(new Vector2(1, 0));delta = delta.add(new Vector2(1, 0));if (newXHits.length > 0) {updatedBounds = updatedBounds.add(new Vector2(-1, 0));delta = delta.add(new Vector2(-1, 0));break;}}}if (xInteractions.length > 0) {hitInfo.interactions = [...hitInfo.interactions, ...xInteractions];}// resolve y-axisdelta = delta.add(yVelocity);updatedBounds = updatedBounds.add(yVelocity);if (yHits.length > 0) {hitInfo.hit = true;hitInfo.up = entity.velocity.y < 0;hitInfo.down = entity.velocity.y > 0;hitInfo.collisions = [...hitInfo.collisions, ...yHits];delta = delta.subtract(yVelocity);updatedBounds = updatedBounds.subtract(yVelocity);for (let y = 0; y < yVelocity.y; y++) {updatedBounds = updatedBounds.add(new Vector2(0, 1));delta = delta.add(new Vector2(0, 1));if (newYHits.length > 0) {updatedBounds = updatedBounds.add(new Vector2(0, -1));delta = delta.add(new Vector2(0, -1));break;}}}if (yInteractions.length > 0) {hitInfo.interactions = [...hitInfo.interactions, ...yInteractions];}entity.hitInfo = hitInfo;hitInfo.hit = hitInfo.collisions.length > 0;entity.x = entity.x + delta.x;entity.y = entity.y + delta.y;}};}const { hits: newYHits } = this.getHitsAt(grid,updatedBounds,entity);const { hits: yHits, interactions: yInteractions } = this.getHitsAt(grid,updatedBounds,entity);const { hits: newXHits } = this.getHitsAt(grid,updatedBounds,entity);const { hits: xHits, interactions: xInteractions } = this.getHitsAt(grid,updatedBounds,entity);let updatedBounds = entity.collisionBounds().add(entity.positionAbsolute());if (entity.velocity.x === 0 && entity.velocity.y === 0) {continue;}};const hits = xHits.filter((x) =>!x.otherEntity || (x.otherEntity && !x.otherEntity.isInteractable()));const interactions = xHits.filter((x) => x.otherEntity && x.otherEntity.isInteractable());getHitsAt = (grid: CollisionGrid,bounds: Rect | RectGroup,entity: Entity): { hits: CollisionResultRect[]; interactions: CollisionResultRect[] } => {const collisionRect = entity.collisionBounds().add(entity.positionAbsolute());const collideableEntities = entities.values().filter((x) => x.isCollideable() || x.isInteractable());throw new Error("Collision handler does not currently support tileWidth != tileHeight");
import { KeyInfoType } from "./keyboard";// import { IGameState } from "Library";// import { Entity } from "./entity";// import { Game } from "../game/game";// import { BaseGame } from "./base_game";// import { IS_DEBUG } from "./environment";// /**// * const state: GameState = yield CoroutineResult;// */// export type GameCoroutine = Generator<CoroutineResult, void, IGameState>// export type CoroutineResult = "next" | { frames: number } | { untilKeyPress: keyof KeyInfoType };// type ActiveCoroutine = {// fn : GameCoroutine;// | { waiting: false }// | { waiting: true; type: "frames" ; frames: number }// | { waiting: true; type: "untilKey"; untilKey: keyof KeyInfoType }// name : string;// owner : Entity | Game;// };// export type CoroutineId = number;// export class CoroutineManager {// private _lastCoroutineId: CoroutineId = -1;// private _activeCoroutines: { [key: number]: ActiveCoroutine } = [];// private _game: BaseGame<any>;// constructor(game: BaseGame<any>) {// this._game = game;// }// startCoroutine(name: string, co: GameCoroutine, owner: Entity | Game): CoroutineId {// for (const activeCo of Object.values(this._activeCoroutines)) {// if (activeCo.name === name) {// if (IS_DEBUG) {// throw new Error(`Two coroutines with the name ${ name }. Tell grant about this!!!`);// } else {// return 0;// }// }// }// this._activeCoroutines[++this._lastCoroutineId] = {// fn : co,// status : { waiting: false },// name : name,// owner : owner,// };// return this._lastCoroutineId;// }// public stopCoroutine(id: CoroutineId): void {// delete this._activeCoroutines[id];// }// public updateCoroutines(state: IGameState): void {// for (const key of Object.keys(this._activeCoroutines)) {// const co = this._activeCoroutines[Number(key)];// if (co.status.waiting) {// if (co.status.type === "frames") {// if (co.status.frames-- < 0) {// co.status = { waiting: false };// } else {// continue;// }// } else if (co.status.type === "untilKey") {// if (state.keys.justDown[co.status.untilKey]) {// co.status = { waiting: false };// } else {// continue;// }// }// }// const { value, done } = co.fn.next(state);// if (done) {// this.stopCoroutine(Number(key));// continue;// }// if (value === "next") {// continue;// if (typeof value === "object") {// if ("frames" in value) {// co.status = { waiting: true, type: 'frames', frames: value.frames };// continue;// } else if ("untilKeyPress" in value) {// co.status = { waiting: true, type: 'untilKey', untilKey: value.untilKeyPress };// continue;// }// }// }// }// stopCoroutinesOwnedBy(entity: Entity) {// const ids = Object.keys(this._activeCoroutines).map(k => Number(k));// for (const id of ids) {// if (this._activeCoroutines[id].owner === entity) {// this.stopCoroutine(id);// }// }// }// }////////// }////////////////////////////// status ://////
export class Pair<T extends { hash(): string }, U extends { hash(): string }> {private _first: T;private _second: U;constructor(first: T, second: U) {this._second = second;}hash(): string {}get first() {return this._first;}get second() {return this._second;}}return `${this._first.hash()}|${this._second.hash()}`;this._first = first;
export class HashSet<K extends { hash(): string }> {private _values: HashMap<K, K>;constructor(initialValues: K[] = []) {this._values = new HashMap<K, K>();for (const value of initialValues) {this.put(value);}}remove(key: K): void {this._values.remove(key);}put(key: K): void {this._values.put(key, key);}get(key: K): boolean {return this._values.get(key) !== undefined;}values(): K[] {return this._values.values();}}export class HashMap<K extends { hash(): string }, V> {private _values: { [key: string]: V } = {};put(key: K, value: V) {this._values[key.hash()] = value;}remove(key: K): void {delete this._values[key.hash()];}get(key: K): V {return this._values[key.hash()];}values(): V[] {}}export class DefaultHashMap<K extends { hash(): string }, V> {private _values: { [key: string]: V } = {};private _makeDefault: () => V;constructor(makeDefaultValue: () => V) {this._makeDefault = makeDefaultValue;}put(key: K, value: V) {this._values[key.hash()] = value;}get(key: K): V {if (this._values[key.hash()] === undefined) {this._values[key.hash()] = this._makeDefault();return this._values[key.hash()];}}}return Object.keys(this._values).map((key) => this._values[key]);
// 2D array that allows for negative indicesexport class Grid<T> {getCount() {let count = 0;for (const key of Object.keys(this._data)) {const inner = this._data[Number(key)];count += Object.keys(inner).length;}return count;}for (const x of Object.keys(this._data)) {const inner = this._data[Number(x)];for (const y of Object.keys(inner)) {y: Number(y),});}}return result;}set(x: number, y: number, value: T) {if (!this._data[x]) {this._data[x] = {};}this._data[x][y] = value;}get(x: number, y: number): T | null {if (!this._data[x]) {return null;}if (this._data[x][y] === undefined) {return null;}return this._data[x][y];}getOrDefault(x: number, y: number, otherwise: T): T {const result = this.get(x, y);if (result === null) {return otherwise;} else {return result;}}}result.push({x: Number(x),keys(): { x: number; y: number }[] {const result: { x: number; y: number }[] = [];private _data: { [key: number]: { [key: number]: T } } = {};
// 2D array that allows for negative indicesexport class DefaultGrid<T> {private _makeDefault: (x: number, y: number) => T;private _count = 0;constructor(makeDefault: (x: number, y: number) => T) {this._makeDefault = makeDefault;}getCount() {return this._count;}for (const x of Object.keys(this._data)) {const inner = this._data[Number(x)];for (const y of Object.keys(inner)) {y: Number(y),});}}return result;}values(): T[] {const result: T[] = [];for (const x of Object.keys(this._data)) {const inner = this._data[Number(x)];for (const y of Object.keys(inner)) {result.push(inner[Number(y)]);}}return result;}set(x: number, y: number, value: T) {if (!this._data[x]) {this._data[x] = {};}if (!this._data[x][y]) {this._count++;}this._data[x][y] = value;}get(x: number, y: number): T {if (!this._data[x]) {this._data[x] = {};}if (this._data[x][y] === undefined) {this._data[x][y] = this._makeDefault(x, y);}return this._data[x][y];}}result.push({x: Number(x),keys(): { x: number; y: number }[] {const result: { x: number; y: number }[] = [];private _data: { [key: number]: { [key: number]: T } } = {};
import { Vector2, IVector2 } from "./geometry/vector2";import { Graphics, Sprite, Container } from "pixi.js";import { Line } from "./geometry/line";import { Entity } from "./entity";import { Rect } from "./geometry/rect";import { RectGroup } from "./geometry/rect_group";import { GameReference } from "./base_game";import { BaseGameState } from "./base_state";import { IS_DEBUG, IS_PRODUCTION } from "./environment";const MAX_DEBUGGING_GRAPHICS_COUNT = 500;export class Debug {public static stageReference: Entity;public static DebugMode = false;public static DebugGraphicStack: Graphics[] = [];public static Clear(): void {for (const debug of Debug.DebugGraphicStack) {debug.parent.removeChild(debug);debug.destroy();}Debug.DebugGraphicStack = [];}* Draw a point on the canvas.* We expect this function to be called every tick in an update() function.* If that's not what you want, pass persistent = true.*/if (IS_PRODUCTION) {}const graphics = new Graphics();new Line({x1: point.x - 40,x2: point.x + 40,y1: point.y - 40,y2: point.y + 40,}).drawOnto(graphics, color);new Line({x1: point.x + 40,x2: point.x - 40,y1: point.y - 40,y2: point.y + 40,}).drawOnto(graphics, color);GameReference.stage.sprite.addChild(graphics);if (!persistent) {this.DebugGraphicStack.push(graphics);if (this.DebugGraphicStack.length > MAX_DEBUGGING_GRAPHICS_COUNT) {const toBeRemoved = this.DebugGraphicStack.shift()!;toBeRemoved.parent.removeChild(toBeRemoved);toBeRemoved.destroy();}}return graphics;}* Draw a line from start to end on the canvas, for debugging.* We expect this function to be called every tick in an update() function.* Debug graphics drawn in the previous tick are removed in the game loop.* If that's not what you want, pass persistent = true.*/if (IS_PRODUCTION) {}return Debug.DrawLine(new Line({ start, end }), color, persistent);}* Draw a line on the canvas, for debugging.* We expect this function to be called every tick in an update() function.* Debug graphics drawn in the previous tick are removed in the game loop.* If that's not what you want, pass persistent = true.*/if (IS_PRODUCTION) {}const graphics = new Graphics();line.drawOnto(graphics, color);if (target === "fixed") {GameReference.fixedCameraStage.sprite.addChild(graphics);} else {GameReference.stage.sprite.addChild(graphics);}if (!persistent) {this.DebugGraphicStack.push(graphics);if (this.DebugGraphicStack.length > MAX_DEBUGGING_GRAPHICS_COUNT) {const toBeRemoved = this.DebugGraphicStack.shift()!;toBeRemoved.parent.removeChild(toBeRemoved);toBeRemoved.destroy();}}return graphics;}* Draw a rectangle from start to end on the canvas, for debugging.* We expect this function to be called every tick in an update() function.* Debug graphics drawn in the previous tick are removed in the game loop.* If that's not what you want, pass persistent = true.*/if (IS_PRODUCTION) {}const lines: Graphics[] = [];for (const line of rect.getLinesFromRect()) {lines.push(Debug.DrawLine(line, color, persistent, target));}return lines;}* Draw the bounds of a game object on the canvas, for debugging.* We expect this function to be called every tick in an update() function.* Debug graphics drawn in the previous tick are removed in the game loop.* If that's not what you want, pass persistent = true.*/public static DrawBounds(persistent = false,target: "stage" | "fixed" = "stage"): Graphics[] {if (IS_PRODUCTION) {}if (entity instanceof Entity) {if (entity instanceof RectGroup) {const results: Graphics[] = [];for (const rect of entity.getRects()) {const lines = Debug.DrawRect(rect, color, persistent, target);for (const line of lines) {results.push(line);}}return results;} else {}}private static profiles: { [key: string]: number[] } = {};/*** Performance test a block of code.*/public static Profile(name: string, cb: () => void): void {Debug.profiles[name] = Debug.profiles[name] || [];const start = window.performance.now();const end = window.performance.now();Debug.profiles[name].push(end - start);if (Debug.profiles[name].length === 60) {const average = Debug.profiles[name].reduce((a, b) => a + b) / 60;const rounded = Math.floor(average * 100) / 100;Debug.profiles[name] = [];}}static ResetDrawCount() {(Sprite as any).drawCount = 0;(Container as any).drawCount = 0;drawn = [];}static GetDrawnObjects() {return drawn;}static GetDrawCount() {}public static DebugStuff(state: BaseGameState) {if (state.keys.justDown.Z) {Debug.DebugMode = true;state.stage.x = 0;state.stage.y = 0;if (state.stage.scale.x === 0.2) {state.stage.scale = new Vector2({ x: 1, y: 1 });} else {state.stage.scale = new Vector2({ x: 0.2, y: 0.2 });}}if (Debug.DebugMode) {if (state.keys.down.W) {state.stage.y += 20;}if (state.keys.down.S) {state.stage.y -= 20;}if (state.keys.down.D) {state.stage.x -= 20;}if (state.keys.down.A) {state.stage.x += 20;}}}public static DebugShowRect(state: BaseGameState, rect: Rect) {state.stage.scale = new Vector2({ x: 0.2, y: 0.2 });state.stage.x = -rect.x * 0.2;state.stage.y = -rect.y * 0.2;}}let drawn: any[] = [];if (IS_DEBUG) {(Sprite as any).drawCount = 0;(Sprite.prototype as any).__render = (Sprite.prototype as any)._render;(Sprite.prototype as any)._render = function (renderer: any) {(Sprite as any).drawCount++;this.__render(renderer);drawn.push(this);};(Sprite.prototype as any).__renderCanvas = (Sprite.prototype as any)._renderCanvas;(Sprite.prototype as any)._renderCanvas = function (renderer: any) {(Sprite as any).drawCount++;this.__renderCanvas(renderer);drawn.push(this);};// PIXI.Container(Container as any).drawCount = 0;(Container.prototype as any).__render = (Container.prototype as any)._render;(Container.prototype as any)._render = function (renderer: any) {(Container as any).drawCount++;this.__render(renderer);drawn.push(this);};(Container.prototype as any).__renderCanvas = (Container.prototype as any)._renderCanvas;(Container.prototype as any)._renderCanvas = function (renderer: any) {(Container as any).drawCount++;this.__renderCanvas(renderer);drawn.push(this);};}return (Sprite as any).drawCount + (Container as any).drawCount;console.log(`${name}: ${rounded}ms`);cb();return Debug.DrawRect(new Rect({x: entity.x,y: entity.y,width: entity.width,height: entity.height,}),color,persistent,target);entity = entity.collisionBounds().add(entity.positionAbsolute());}console.error("SHOULD NOT HAPPEN");entity: Entity | Sprite | Graphics | RectGroup | Container | Rect,color = 0xff0000,**/**console.error("SHOULD NOT HAPPEN");public static DrawRect(rect: Rect,color = 0xff0000,persistent = false,target: "stage" | "fixed" = "fixed"): Graphics[] {**/**console.error("SHOULD NOT HAPPEN");public static DrawLine(line: Line,color = 0xff0000,persistent = false,target: "stage" | "fixed" = "fixed"): Graphics {**/**console.error("SHOULD NOT HAPPEN");public static DrawLineV2(start: Vector2,end: Vector2,color = 0xff0000,persistent = false): Graphics {**/**console.error("SHOULD NOT HAPPEN");public static DrawPoint(point: IVector2,color = 0xff0000,persistent = false): Graphics {* Debug graphics drawn in the previous tick are removed in the game loop.*/**
import { Vector2, IVector2 } from "./geometry/vector2";import { Rect } from "./geometry/rect";import { Sprite, Texture, MaskData, Container } from "pixi.js";import { getUniqueID } from "./util";import { RectGroup } from "./geometry/rect_group";import { BaseGameState } from "./base_state";// import { CoroutineId, GameCoroutine } from "./coroutine_manager";import { IGameState, Mode } from "Library";import { HitInfo } from "./collision_handler";import { serialized } from "./serializer";export enum EntityType {NormalEntity,* The collision information for this entity will be calculated by the main* game loop.*/MovingEntity,}export class AugmentedSprite extends Sprite {entity!: Entity;}// export class ModeEntity extends Entity<GameState> {// shouldUpdate(state: GameState) {// return this.activeModes.includes(state.mode);// }// }// TODO: probably make less of these methods abstract?export class Entity {/*** This is the name that is displayed in the hierarchy.*/public name: string;public activeModes: Mode[] = ["Normal"];public id = getUniqueID();public velocity = Vector2.Zero;/*** The PIXI Sprite that this Entity wraps.*/public sprite: AugmentedSprite;public hitInfo: HitInfo = { hit: false, collisions: [], interactions: [] };protected _collidable: boolean;protected _interactable: boolean;constructor(props: {name: string;collidable?: boolean;texture?: Texture;interactable?: boolean;}) {this.sprite = new AugmentedSprite(props.texture);this.name = props.name;this.sprite.entity = this;this._collidable = props.collidable ?? false;this._interactable = props.interactable ?? false;if (props.interactable && props.collidable) {throw new Error("Cant be both interactable and collideable");}this.startUpdating();this.sprite.sortableChildren = true;this.sprite.anchor.set(0);}addChild(child: Entity, x: number | null = null, y: number | null = null) {this.sprite.addChild(child.sprite);if (x !== null) child.x = x;if (y !== null) child.y = y;}removeChild(child: Entity) {this.sprite.removeChild(child.sprite);}// startCoroutine(name: string, coroutine: GameCoroutine): CoroutineId {// return GameReference.coroutineManager.startCoroutine(name, coroutine, this);// }// stopCoroutine(id: CoroutineId): void {// GameReference.coroutineManager.stopCoroutine(id);// }startUpdating() {GameReference.state.entities.put(this);}stopUpdating() {GameReference.state.entities.remove(this);}shouldUpdate(state: IGameState): boolean {return this.activeModes.includes(state.mode);}setCollideable(isCollideable: boolean) {this._collidable = isCollideable;}setTexture(newTexture: Texture) {this.sprite.texture = newTexture;}* Used for collision detection. (x, y) is relative to the sprite, btw, not* the map or anything else.*/public collisionBounds(): Rect | RectGroup {return new Rect({x: 0,y: 0,width: this.width,}* Returns the position of this Entity relative to the stage (rather than its* parent, like position would).*/public positionAbsolute(): Vector2 {return this.position;}return this.position.add(this.parent?.positionAbsolute() ?? new Vector2());}public get center(): Vector2 {return new Vector2(this.position).add({x: this.width / 2,});}children(): Entity[] {const children = this.sprite.children;const result: Entity[] = [];for (const child of children) {if (child instanceof AugmentedSprite) {result.push(child.entity);}}return result;}destroy(state: BaseGameState) {state.toBeDestroyed.push(this);}hash(): string {return `[Entity ${this.id}]`;}isCollideable(): boolean {return this._collidable;}isInteractable(): boolean {return this._interactable;}dimensions(): Vector2 {const bounds = this.collisionBounds();if (bounds instanceof Rect) {return new Vector2(bounds.x, bounds.y);} else {throw new Error("oh no grant doesnt handle this case!!!");}}// Sprite wrapper stuffpublic get parent(): Entity | null {const parent = this.sprite.parent;if (parent instanceof AugmentedSprite) {const entityParent = parent.entity;if (entityParent) {return entityParent;}}return null;}private queuedUpdates: ((state: IGameState) => void)[] = [];private firstUpdateCalled = false;baseUpdate(state: IGameState): void {if (this.shouldUpdate(state)) {for (const cb of this.queuedUpdates) {cb(state);}if (!this.firstUpdateCalled) {this.firstUpdateCalled = true;this.firstUpdate(state);}this.update(state);}this.queuedUpdates = [];}addOnClick(listener: (state: IGameState) => void) {this.sprite.interactive = true;this.queuedUpdates.push(listener);});}addOnMouseOver(listener: (state: IGameState) => void) {this.sprite.interactive = true;this.queuedUpdates.push(listener);});}addOnMouseOut(listener: (state: IGameState) => void) {this.sprite.interactive = true;this.queuedUpdates.push(listener);});}public set scale(value: Vector2) {this.sprite.scale.x = value.x;this.sprite.scale.y = value.y;}public distance(other: IVector2) {return this.position.distance(other);}}public get scale(): Vector2 {return new Vector2({ x: this.sprite.scale.x, y: this.sprite.scale.y });}public set mask(value: Container | MaskData | null) {this.sprite.mask = value;}public get mask(): Container | MaskData | null {return this.sprite.mask;}public set texture(value: Texture) {this.sprite.texture = value;}public get visible(): boolean {return this.sprite.visible;}public set visible(value: boolean) {this.sprite.visible = value;}public get zIndex(): number {return this.sprite.zIndex;}public set zIndex(value: number) {this.sprite.zIndex = value;this.sprite.parent && this.sprite.parent.sortChildren();}public get position(): Vector2 {return new Vector2({ x: this.x, y: this.y });}public set position(value: Vector2) {this.x = value.x;this.y = value.y;}public get alpha(): number {return this.sprite.alpha;}public set alpha(value: number) {this.sprite.alpha = value;}public get height(): number {return this.sprite.height;}public set height(value: number) {this.sprite.height = value;}public get width(): number {return this.sprite.width;}public set width(value: number) {this.sprite.width = value;}public get y(): number {return this.sprite.y;}public set y(value: number) {this.sprite.y = value;}public get x(): number {return this.sprite.x;}public set x(value: number) {this.sprite.x = value;}this.sprite.on("mouseout", () => {this.sprite.on("mouseover", () => {this.sprite.on("click", () => {y: this.height / 2,if (this.parent &&(this.parent.name === FixedStageName ||this.parent.name === StageName ||this.parent.name === ParallaxStageName)) {/**height: this.height,});/**firstUpdate(state: IGameState): void {}update(state: IGameState): void {}/**import {GameReference,FixedStageName,StageName,ParallaxStageName,} from "./base_game";
export const IS_DEVELOPMENT = !IS_PRODUCTION;export const IS_DEBUG =(IS_DEVELOPMENT || window.location.href.includes("debug=true")) &&!window.location.href.includes("debug=false");export const IS_PRODUCTION =!window.location.href.includes("localhost") ||window.location.href.includes("debug=false");
export const EPSILON = 0.0000001;export const epsEqual = (x: number, y: number) => {return Math.abs(x - y) < EPSILON;export const epsGreaterThan = (x: number, y: number) => {export const epsLessThan = (x: number, y: number) => {return x - EPSILON - y < 0;};return x + EPSILON - y > 0;};};
export const FontDataUrl ="url('data:application/font-woff;charset=utf-8;base64,AAEAAAANAIAAAwBQRFNJRwAAAAEAAPXIAAAACEdERUYC2gAEAAD10AAAACBPUy8yQal3RwAAANwAAABgY21hcAvh29UAAPhIAAAFKGdhc3AAAAADAAD1wAAAAAhnbHlm8tOLKgAAFkQAAM8saGVhZP6TCFsAAPAoAAAANmhoZWELBQIBAAAWAAAAACRobXR4yeTAbwAA8GAAAAVgbG9jYQEk4ygAAOVwAAAKuG1heHAC0gCxAAAWJAAAACBuYW1leSf5mQAA9fAAAAJYcG9zdD0HyCoAAAE8AAAUwwADA/8BkAAFAAAFAAVHAAABAAUABUcAAAIAAIACgAAAAAAFCQAAAAAABAAAAAEAAAAAAAAAAAAAAAAgICAgAEAAIPsCBgD+AAEABwACAGAAAZ/f1wAAA4AFAQAAACAAAQACAAAAAAAA/zgAZAABAAAAAAAAAAAAAAAAAAAAAAAAAq0AAAJyAnMAAwAEAAUABgAHAAgACQAKAAsADAANAA4ADwAQABEAEgATABQAFQAWABcAGAAZABoAGwAcAB0AHgAfACAAIQAiACMAJAAlACYAJwAoACkAKgArACwALQAuAC8AMAAxADIAMwA0ADUANgA3ADgAOQA6ADsAPAA9AD4APwBAAEEAQgBDAEQARQBGAEcASABJAEoASwBMAE0ATgBPAFAAUQBSAFMAVABVAFYAVwBYAFkAWgBbAFwAXQBeAF8AYABhAQIArACjAIQAhQC9AJYA6ACGAI4AiwCdAKkApAEDAIoA2gCDAJMA8gDzAI0AlwCIAQQA3gDxAJ4AqgD1APQA9gCiAK0AyQDHAK4AYgBjAJAAZADLAGUAyADKAM8AzADNAM4A6QBmANMA0ADRAK8AZwDwAJEA1gDUANUAaADrAO0AiQBqAGkAawBtAGwAbgCgAG8AcQBwAHIAcwB1AHQAdgB3AOoAeAB6AHkAewB9AHwAuAChAH8AfgCAAIEA7ADuALoBBQEGAQcBCAEJAQoA/QD+AP8BAAELAQwBDQEBAQ4BDwEQAREBEgETARQBFQD4APkBFgEXARgBGQEaARsA+gDXARwBHQEeAR8BIAEhASIBIwDiAOMBJAElASYBJwEoASkBKgErASwBLQCwALEBLgEvATABMQEyATMBNAE1APsA/ADkAOUBNgE3ATgBOQE6ATsBPAE9AT4BPwFAAUEAuwFCAUMBRAFFAOYA5wCmAUYBRwFIAUkBSgFLAUwBTQFOAU8BUAFRANgA4QDbANwA3QDgAVIA3wFTAVQBVQFWAVcBWAFZAVoBWwFcAV0BXgFfAWABYQFiAWMBZAFlAWYBZwFoAWkBagFrAWwBbQFuAW8BcAFxAXIBcwF0AXUBdgF3AXgBeQF6AXsBfAF9AX4BfwGAAYEBggGDAYQBhQGGAYcBiAGJAYoBiwGMAY0BjgGPAJsBkAGRAZIBkwGUAZUBlgGXAZgBmQGaAZsBnAGdAZ4BnwGgAaEBogGjAaQBpQGmAacBqAGpAaoBqwGsAa0BrgGvAbABsQGyAbMBtAG1AbYBtwG4AbkBugG7AbwBvQG+Ab8BwAHBAcIBwwHEAcUBxgHHAcgByQHKAcsBzAHNAc4BzwHQAdEB0gHTAdQB1QHWAdcB2AHZAdoB2wHcAd0B3gHfAeAB4QHiAeMB5AHlAeYB5wHoAekB6gHrAewB7QHuAe8B8AHxAfIB8wH0AfUB9gH3AfgB+QH6AfsCdAJ1AnYCdwJ4AnkCegJ7AnwCfQJ+An8CgAKBAoIAsgCzAfwB/QC2ALcAxAC0ALUAxQCCAMIAhwCrAMYB/gH/AL4AvwIAAgECAgIDAgQCBQIGAIwCBwIIAgkCCgILAgwCDQCYAKgAmgCZALwAwwClAJICDgIPAhACEQCcAKcAjwISAJQAlQITAhQCFQIWAhcCGAIZAhoCGwIcAh0CHgIfAiACIQIiAiMCJAIlAiYCJwIoAikCKgIrAiwCLQIuAi8CMAIxAjICMwI0AjUCNgI3AjgCOQI6AjsCPAI9Aj4CPwJAAkECQgJDAkQCRQJGAkcCSAJJAkoCSwJMAk0CTgJPAlACUQJSAlMCVAJVAlYCVwJYAlkCWgJbAlwCXQJeAl8CYAC5AmECYgJjAmQCZQJmAmcCaAJpAmoCawJsAm0CbgJvAnACcQKDAoQChQKGAocCiAKJAooCiwKMAo0CjgKPApACkQKSApMClAKVApYClwKYApkCmgKbApwCnQKeAp8CoAKhAqICowKkAqUCpgKnAqgCqQKqAqsCrAKtAq4CrwKwArECsgKzArQCtQpjb250cm9sREVMCXNmdGh5cGhlbgZtaWRkb3QHQW1hY3JvbgdhbWFjcm9uBkFicmV2ZQZhYnJldmUHQW9nb25lawdhb2dvbmVrBkRjYXJvbgZkY2Fyb24GRGNyb2F0B0VtYWNyb24HZW1hY3JvbgRFZG90BGVkb3QHRW9nb25lawdlb2dvbmVrBkVjYXJvbgZlY2Fyb24IR2NlZGlsbGEIZ2NlZGlsbGEHSW1hY3JvbgdpbWFjcm9uB0lvZ29uZWsHaW9nb25lawhLY2VkaWxsYQhrY2VkaWxsYQZMYWN1dGUGbGFjdXRlCExjZWRpbGxhCGxjZWRpbGxhBkxjYXJvbgZsY2Fyb24GTmFjdXRlBm5hY3V0ZQhOY2VkaWxsYQhuY2VkaWxsYQZOY2Fyb24GbmNhcm9uB09tYWNyb24Hb21hY3JvbglPZGJsYWN1dGUJb2RibGFjdXRlBlJhY3V0ZQZyYWN1dGUIUmNlZGlsbGEIcmNlZGlsbGEGUmNhcm9uBnJjYXJvbgZTYWN1dGUGc2FjdXRlCFRjZWRpbGxhCHRjZWRpbGxhBlRjYXJvbgZ0Y2Fyb24HVW1hY3Jvbgd1bWFjcm9uBVVyaW5nBXVyaW5nCVVkYmxhY3V0ZQl1ZGJsYWN1dGUHVW9nb25lawd1b2dvbmVrBlphY3V0ZQZ6YWN1dGUEWmRvdAR6ZG90BU9ob3JuBW9ob3JuBVVob3JuBXVob3JuBmFjYXJvbgZpY2Fyb24Gb2Nhcm9uBnVjYXJvbg91ZGllcmVzaXNtYWNyb24OdWRpZXJlc2lzYWN1dGUOdWRpZXJlc2lzY2Fyb24OdWRpZXJlc2lzZ3JhdmUFdGlsZGUIZ3JhdmVjbWIIYWN1dGVjbWIIdGlsZGVjbWINaG9va2Fib3ZlY29tYgtkb3RiZWxvd2NtYgV0b25vcw5kaWFseXRpa2F0b25vcwpBbHBoYXRvbm9zDEVwc2lsb250b25vcwhFdGF0b25vcwlJb3RhdG9ub3MMT21pY3JvbnRvbm9zDFVwc2lsb250b25vcwpPbWVnYXRvbm9zEWlvdGFkaWVyZXNpc3Rvbm9zBUFscGhhBEJldGEFR2FtbWEKRGVsdGFncmVlawdFcHNpbG9uBFpldGEDRXRhBVRoZXRhBElvdGEFS2FwcGEGTGFtYmRhAk11Ak51AlhpB09taWNyb24CUGkDUmhvBVNpZ21hA1RhdQdVcHNpbG9uA1BoaQNDaGkDUHNpCk9tZWdhZ3JlZWsMSW90YWRpZXJlc2lzD1Vwc2lsb25kaWVyZXNpcwphbHBoYXRvbm9zDGVwc2lsb250b25vcwhldGF0b25vcwlpb3RhdG9ub3MUdXBzaWxvbmRpZXJlc2lzdG9ub3MFYWxwaGEEYmV0YQVnYW1tYQVkZWx0YQdlcHNpbG9uBHpldGEDZXRhBXRoZXRhBGlvdGEFa2FwcGEGbGFtYmRhB211Z3JlZWsCbnUCeGkHb21pY3JvbgNyaG8Gc2lnbWExBXNpZ21hA3RhdQd1cHNpbG9uA3BoaQNjaGkDcHNpBW9tZWdhDGlvdGFkaWVyZXNpcw91cHNpbG9uZGllcmVzaXMMb21pY3JvbnRvbm9zDHVwc2lsb250b25vcwpvbWVnYXRvbm9zCWFmaWkxMDAyMwlhZmlpMTAwNTEJYWZpaTEwMDUyCWFmaWkxMDA1MwlhZmlpMTAwNTQJYWZpaTEwMDU1CWFmaWkxMDA1NglhZmlpMTAwNTcJYWZpaTEwMDU4CWFmaWkxMDA1OQlhZmlpMTAwNjAJYWZpaTEwMDYxCWFmaWkxMDA2MglhZmlpMTAxNDUJQWN5cmlsbGljCWFmaWkxMDAxOAlhZmlpMTAwMTkJYWZpaTEwMDIwCWFmaWkxMDAyMQlhZmlpMTAwMjIJYWZpaTEwMDI0CWFmaWkxMDAyNQlhZmlpMTAwMjYJYWZpaTEwMDI3CWFmaWkxMDAyOAlhZmlpMTAwMjkJYWZpaTEwMDMwCWFmaWkxMDAzMQlhZmlpMTAwMzIJYWZpaTEwMDMzCWFmaWkxMDAzNAlhZmlpMTAwMzUJYWZpaTEwMDM2CWFmaWkxMDAzNwlhZmlpMTAwMzgJYWZpaTEwMDM5CWFmaWkxMDA0MAlhZmlpMTAwNDEJYWZpaTEwMDQyCWFmaWkxMDA0MwlhZmlpMTAwNDQJYWZpaTEwMDQ1CWFmaWkxMDA0NglhZmlpMTAwNDcJYWZpaTEwMDQ4CWFmaWkxMDA0OQlhY3lyaWxsaWMJYWZpaTEwMDY2CWFmaWkxMDA2NwlhZmlpMTAwNjgJYWZpaTEwMDY5CWFmaWkxMDA3MAlhZmlpMTAwNzIJYWZpaTEwMDczCWFmaWkxMDA3NAlhZmlpMTAwNzUJYWZpaTEwMDc2CWFmaWkxMDA3NwlhZmlpMTAwNzgJYWZpaTEwMDc5CWFmaWkxMDA4MAlhZmlpMTAwODEJYWZpaTEwMDgyCWFmaWkxMDA4MwlhZmlpMTAwODQJYWZpaTEwMDg1CWFmaWkxMDA4NglhZmlpMTAwODcJYWZpaTEwMDg4CWFmaWkxMDA4OQlhZmlpMTAwOTAJYWZpaTEwMDkxCWFmaWkxMDA5MglhZmlpMTAwOTMJYWZpaTEwMDk0CWFmaWkxMDA5NQlhZmlpMTAwOTYJYWZpaTEwMDk3CWFmaWkxMDA3MQlhZmlpMTAwOTkJYWZpaTEwMTAwCWFmaWkxMDEwMQlhZmlpMTAxMDIJYWZpaTEwMTAzCWFmaWkxMDEwNAlhZmlpMTAxMDUJYWZpaTEwMTA2CWFmaWkxMDEwNwlhZmlpMTAxMDgJYWZpaTEwMTA5CWFmaWkxMDExMAlhZmlpMTAxOTMJYWZpaTEwMDUwCWFmaWkxMDA5OAlhZmlpMDAyMDgKZGJsbG93bGluZQZtaW51dGUGc2Vjb25kCWV4Y2xhbWRibAhmcmFjdGlvbgluc3VwZXJpb3IGcGVzZXRhBGRvbmcERXVybwlhZmlpNjEzNTIJYXJyb3dsZWZ0B2Fycm93dXAKYXJyb3dyaWdodAlhcnJvd2Rvd24JYXJyb3dib3RoCWFycm93dXBkbgxhcnJvd3VwZG5ic2UKb3J0aG9nb25hbAVhbmdsZQxpbnRlcnNlY3Rpb24FdW5pb24LZXF1aXZhbGVuY2USbG9naWNhbG5vdHJldmVyc2VkC2ludGVncmFsdG9wDmludGVncmFsYm90dG9tCFNGMTAwMDAwB3VuaTI1MDEIU0YxMTAwMDAHdW5pMjUwMwhTRjAxMDAwMAd1bmkyNTBGCFNGMDMwMDAwB3VuaTI1MTMIU0YwMjAwMDAHdW5pMjUxNwhTRjA0MDAwMAd1bmkyNTFCCFNGMDgwMDAwB3VuaTI1MUQHdW5pMjUyMAd1bmkyNTIzCFNGMDkwMDAwB3VuaTI1MjUHdW5pMjUyOAd1bmkyNTJCCFNGMDYwMDAwB3VuaTI1MkYHdW5pMjUzMAd1bmkyNTMzCFNGMDcwMDAwB3VuaTI1MzcHdW5pMjUzOAd1bmkyNTNCCFNGMDUwMDAwB3VuaTI1M0YHdW5pMjU0Mgd1bmkyNTRCCFNGNDMwMDAwCFNGMjQwMDAwCFNGNTEwMDAwCFNGNTIwMDAwCFNGMzkwMDAwCFNGMjIwMDAwCFNGMjEwMDAwCFNGMjUwMDAwCFNGNTAwMDAwCFNGNDkwMDAwCFNGMzgwMDAwCFNGMjgwMDAwCFNGMjcwMDAwCFNGMjYwMDAwCFNGMzYwMDAwCFNGMzcwMDAwCFNGNDIwMDAwCFNGMTkwMDAwCFNGMjAwMDAwCFNGMjMwMDAwCFNGNDcwMDAwCFNGNDgwMDAwCFNGNDEwMDAwCFNGNDUwMDAwCFNGNDYwMDAwCFNGNDAwMDAwCFNGNTQwMDAwCFNGNTMwMDAwCFNGNDQwMDAwB3VwYmxvY2sHZG5ibG9jawVibG9jawdsZmJsb2NrB3J0YmxvY2sHbHRzaGFkZQVzaGFkZQdka3NoYWRlC2JsYWNrc3F1YXJlDmJsYWNrcmVjdGFuZ2xlF2JsYWNrdXBwb2ludGluZ3RyaWFuZ2xlGWJsYWNrcmlnaHRwb2ludGluZ3BvaW50ZXIZYmxhY2tkb3ducG9pbnRpbmd0cmlhbmdsZRhibGFja2xlZnRwb2ludGluZ3BvaW50ZXIGY2lyY2xlDWJ1bGxldGludmVyc2UJaW52Y2lyY2xlCXNtaWxlZmFjZRBibGFja3NtaWxpbmdmYWNlB2NvbXBhc3MGZmVtYWxlBG1hbGUFc3BhZGUEY2x1YgVoZWFydAdkaWFtb25kC211c2ljYWxub3RlEGVpZ2h0aG5vdGViZWFtZWQHdW5pRjhGRgJmaQJmbAZnbHlwaDEGZ2x5cGgyCGdseXBoNDcxCGdseXBoNDcyCGdseXBoNDczCGdseXBoNDc0CGdseXBoNDc1CGdseXBoNDc2CGdseXBoNDc3CGdseXBoNDc4CGdseXBoNDc5CGdseXBoNDgwCGdseXBoNDgxCGdseXBoNDgyCGdseXBoNDgzCGdseXBoNDg0CGdseXBoNDg1CGdseXBoNjM0CGdseXBoNjM1CGdseXBoNjM2CGdseXBoNjM3CGdseXBoNjM4CGdseXBoNjM5CGdseXBoNjQwCGdseXBoNjQxCGdseXBoNjQyCGdseXBoNjQzCGdseXBoNjQ0CGdseXBoNjQ1CGdseXBoNjQ2CGdseXBoNjQ3CGdseXBoNjQ4CGdseXBoNjQ5CGdseXBoNjUwCGdseXBoNjUxCGdseXBoNjUyCGdseXBoNjUzCGdseXBoNjU0CGdseXBoNjU1CGdseXBoNjU2CGdseXBoNjU3CGdseXBoNjU4CGds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AAEBgP4CBAEHAAAHAAABMxEhFSERIwGAgQIA/gCBBwD8AH37fwAAAAACAX/+AQP/BwAAAwAHAAABIREhAzMDIwIBAf7+AoGBAYEDgv6ABP73AQAAAAEBAP4ABAAHAAAHAAABIREhFSERIQEAAYABgP6A/oAHAPwBgfuAAAABAQD+AQQABwAABwAAASERIREhESEBAAGAAYD+gf6ABwD8gv6A+/8AAQAB/gICAQcAAAcAAAEzESMRITUhAYCBgf6BAX8HAPcCBIF9AAAAAAEAAP4AAgEHAAAHAAABMxEjESERIQGAgYH+gAGABwD3AAQCAYAAAAABAAH+AAKABwAABwAAASERIREjNTMBAAGA/oD//wcA9wAEgIEAAAAAAQAB/gACgAcAAAcAAAEhESERIxEzAQABgP6A//8HAPcABAIBgAAAAAEAAP4CBAADAAAHAAARIRUhESMRIQQA/gCB/oEDAH37fwSBAAEAAP3+A/8DgAAHAAARIREhESMRIQP//gKB/oADgP6A+/4EAgAAAAABAAD9/wP/AwAABwAAESEVIREhESMD//6A/oD/AwCB+4AEgAABAAD9/gP/A4AABwAAESERIREhESMD//6A/oD/A4D+gPv+BAIAAAAAAQAAAoMEAAcAAAcAAAEzESEVITUhAX+BAgD8AAF/BwD8AH19AAAAAAEAAAIAA/8G/gAHAAABMxEhESERIQGAgQH+/AEBgAb+/IL+gAGAAAABAAACfwP/Bv8ABwAAEyERIRUhNTP/AYABgPwB/wb//AGBgQABAAACAAP/Bv4ABwAAEyERIREhETP/AYABgPwB/wb+/IL+gAGAAAAAAQAA/gIEAAcAAAsAAAEzESEVIREjESE1IQF/gQIA/gCB/oEBfwcA/AB9+38EgX0AAQAA/f4D/wb+AAsAAAEzESERIREjESERIQGAgQH+/gKB/oABgAb+/IL+gPv+BAIBgAAAAAEAAP3/A/8G/wALAAATIREhFSERIREjNTP/AYABgP6A/oD//wb//AGB+4AEgIEAAAEAAP3+A/8G/gALAAATIREhESERIREjETP/AYABgP6A/oD//wb+/IL+gPv+BAIBgAAAAAACAAACAQQAA4AAAwAHAAARIRUhESEVIQQA/AAEAPwAAoKBAX+BAAAAAgEA/gIChAcAAAMABwAAATMRIwEzESMCA4GB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AACC6AAAgzgAAIN4AACDxAAAg+gAAIQwAACEZAAAhPAAAIVIAACFlAAAhewAAIZkAACGqAAAhvgAAIcoAACHkAAAh7wAAIf4AACILAAAiFQAAIi0AACJBAAAiXgAAImsAACJ4AAAihQAAIpYAACKmAAAiuAAAIsYAACLaAAAi7QAAIwYAACMZAAAjLAAAIz8AACNIAAAjWgAAI24AACONAAAjowAAI7YAACPMAAAj4wAAI/IAACQGAAAkEgAAJCEAACQsAAAkPAAAJEkAACRTAAAkZQAAJHkAACSTAAAkoAAAJK0AACS8AAAkzQAAJN0AACTvAAAk/QAAJQwAACUfAAAlMwAAJUkAACVeAAAlbwAAJX4AACWOAAAlnAAAJa0AACW7AAAl0QAAJeQAACX3AAAmFgAAJi0AACY6AAAmRQAAJlAAACZjAAAmigAAJqkAACa1AAAmxwAAJuEAACb3AAAnEQAAJykAACc4AAAnRwAAJ2AAACdwAAAndwAAJ5MAACeaAAAnoQAAJ6gAACexAAAnvAAAJ8YAACfQAAAn4gAAJ/MAACgDAAAoDgAAKB4AACgpAAAoNQAAKGYAAChvAAAofAAAKI0AACicAAAorAAAKMEAACjNAAAo6QAAKQIAACkkAAApQgAAKVcAAClnAAApdgAAKYgAACmZAAApswAAKcwAACnoAAAqAQAAKhcAAColAAAqOAAAKkYAACpNAAAqZAAAKngAACqBAAAqkgAAKqMAACq0AAAqwAAAKtsAACrrAAAq+AAAKw8AACslAAArLgAAKzgAACtBAAArSAAAK08AACtWAAArXQAAK2YAACtwAAAreQAAK4IAACuLAAArlQAAK54AACunAAArsQAAK7wAACvGAAAr0AAAK9oAACvkAAAr7gAAK/gAACwBAAAsCwAALBQAACweAAAsKAAALDIAACw7AAAsRQAALFEAACxeAAAsagAALHcAACyBAAAsiwAALJYAACyiAAAssAAALLwAACzIAAAs1wAALOIAACztAAAs+wAALQYAAC0RAAAtHwAALSsAAC04AAAtSgAALVYAAC1jAAAtdAAALYEAAC2NAAAtnwAALawAAC24AAAtyQAALdoAAC3rAAAuBAAALgsAAC4SAAAuGQAALiAAAC4nAAAuYQAALs4AAC88AAAvQwAAL0oAAC9XAAAvZgAAL3MAAC+CAAAvmAAAL7IAAC/EAAAv2gAAMAEAADAgAAAwOAAAME0AADBlAAAweAAAMJAAADCiAAAwtQAAMMUAADDfAAAw8QAAMQoAADEcAAAxHAAAMRwAADEcAAAxOgAAMVEAADFRAAAxUQAAMWQAADGIAAAxiAAAMYgAADGIAAAxiAAAMYgAADGIAAAxqgAAMcAAADHLAAAxywAAMdgAADHfAAAx6AAAMe8AADH6AAAyBgAAMgYAADIiAAAyIgAAMiIAADIsAAAyNQAAMlwAADJ7AAAyjQAAMqAAADK4AAAyywAAMtcAADLpAAAzAwAAMyoAADNJAAAzWwAAM3MAADOGAAAzlwAAM7MAADPEAAAzxAAAM8QAADPLAABAAAAAQAAENs2+18PPPUCCwgAAAAAAMy6EogAAAAAzLmr1v///f4EAwcBAAAACAAAAAEAAAAABAAAAAAAAAAEAAAAAAACAAB/AH8AfgCAAH4BfwGBAQAA/wCAAQEAgAGAAH4AfgD/AIAAgACAAIAAgACAAIAAgAGAAQEBAAB/AQAAfwCAAIAAgACAAIAAgACAAIAAgAEAAH8AgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgAEAAIABAACAAAIBfgB/AIAAgACAAIAAgACAAIABAQCAAIAA/wCAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgAGAAQAAgAEAAAACAACCAIAAgACAAgAAgAB/AIAA/wCAAIIAgACAAQABAAD/AP8BAAGAAIAAgAF/AQABAAD/AIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgAEAAQABAAEAAIAAgACAAIAAgACAAIAA/wCAAIAAgACAAIAAgACAAIAAfwB/AH8AfwB/AH8AgACAAIAAgACAAIAA/wEBAP8A/wCAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAfwCAAH8AgAB/AIAAgACAAIAAgP//AH0AgACAAIAAgACAAIAAgACAAIAAgACAAIAAgAEBAQEBAQEBAQEBAQCAAIAAgAD/AIAA/wCAAP8AfwCAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAAAAAAH8BAQCAAIAAgACAAIAAgAEBAQEA/wF/AQABfwB/AIABAQGAAIABfwGBAX8AgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAQEAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIABAQCAAIAAgACAAQEAgACAAIAAgACAAIAAgACAAIABAQCAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAQEAgACAAIAAgACAAIAAgACAAIABAQEBAH8AgACAAIAAgACAAIAAgACAAIAAgAB/AIAAfwCAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACBAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACBAIAAgACAAIAAgACAAIAAfgCAAH8AgACAAIIAgQCBAIIAgACAAIAAgACAAIAAgACAAIABAAEAAIAAgACAAIAAgAB+AIAAgACAAH8AfwB/AQAAfwB+AH8AAAB/AQEAgQEBAQAAgAEBAIAAAAAAAAABfwF/AQEAgACAAIABAQEBAIABAQB/AX8BAQGAAYABAAB/AQEAgACAAIAAAAAAAIABAQCAAQEAAQEBAQEAgACAAIAAgAABAX8AAQCAAIAAgACAAIABAQCAAIABAAEBAQAAggH/AIAAAAAAAYABAAGAAP8AAAABAYABAAAAAAEBgAF/AQABAAABAAAAAQABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAGAAQABAAAAAAAAAAF/AP0A/QAAAAAAAAF/AP0A/QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgD//wAA//8AgACAAIABAQCAAIEBAQB+AH8AfwB/AH8AfwEBAIAAgACAAH8AfwCAAAAAgACAAIAAAAAAAAAAgACAAAAAAACAAIAAAAAAAAAAAAAAAAAAgACAAP8AAACAAX0AAAF9AQABAAAAAIAAAAAAAX8AAQCAAIAAfwCAAIAAgAEBAIAAfwCAAIAAfwCAAIAAgAAAAIAAAAAAAgAAAQAB//8AAgAAAAEAAAAAAAEAAAAOAAAAGAAAAAAAAgABAAECrAABAAQAAAACAAAAAAAOAK4AAQAAAAAAAAA5AAAAAQAAAAAAAQAKADkAAQAAAAAAAgAH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')";
import { EPSILON } from "../epsilon_math";import { Util } from "../util";export interface IVector2 {x: number;y: number;}export class Vector2 {private _x: number;private _y: number;constructor();constructor(x: number, y: number);if (typeof propsOrX === "number") {this._x = propsOrX;this._y = y!;} else {this._x = propsOrX.x;this._y = propsOrX.y;}}public get half(): Vector2 {return new Vector2({ x: this.x / 2, y: this.y / 2 });}public static Zero: Vector2 = new Vector2(0, 0);public static One: Vector2 = new Vector2(1, 1);static IsVector2(x: any): x is Vector2 {return x instanceof Vector2;}static Random(highX: number, highY: number, lowX = 0, lowY = 0) {return new Vector2({x: Util.RandRange(lowX, highX),y: Util.RandRange(lowY, highY),});}hash(): string {return this.toString();}toString(): string {return `[${this.x}, ${this.y}]`;}invert(): Vector2 {return new Vector2({x: -this.x,y: -this.y,});}round(): Vector2 {return new Vector2({x: Math.round(this.x),y: Math.round(this.y),});}floor(): Vector2 {return new Vector2({x: Math.floor(this.x),y: Math.floor(this.y),});}taxicabDistance(p: Vector2): number {return Math.abs(p.x - this.x) + Math.abs(p.y - this.y);}diagonalDistance(p: IVector2): number {return Math.max(Math.abs(p.x - this.x), Math.abs(p.y - this.y));}distance(p: IVector2): number {let dx = Math.abs(p.x - this.x);let dy = Math.abs(p.y - this.y);return Math.sqrt(dx * dx + dy * dy);}return new Vector2({x: this.x + p.x,y: this.y + p.y,});}return new Vector2({x: this.x - p.x,y: this.y - p.y,});}return new Vector2({x: this.x + p.x,y: this.y + p.y,});}addX(x: number): Vector2 {return new Vector2({x: this.x + x,y: this.y,});}addY(y: number): Vector2 {return new Vector2({x: this.x,y: this.y + y,});}clampY(low: number, high: number): Vector2 {let newY = this.y;return new Vector2({x: this.x,y: newY,});}return new Vector2({x: (this.x - about.x) * amount.x + about.x,y: (this.y - about.y) * amount.y + about.y,});}rotate(origin: Vector2, angle: number): Vector2 {angle = angle / (180 / Math.PI);return new Vector2({});}equals(other: Vector2 | undefined): boolean {if (other === undefined) {return false;}return (Math.abs(this.x - other.x) < EPSILON &&Math.abs(this.y - other.y) < EPSILON);}multiply(other: Vector2 | number): Vector2 {if (typeof other === "number") {return new Vector2({x: this.x * other,y: this.y * other,});} else {return new Vector2({x: this.x * other.x,y: this.y * other.y,});}}divide(other: Vector2 | number): Vector2 {if (typeof other === "number") {return new Vector2({x: this.x / other,y: this.y / other,});} else {return new Vector2({x: this.x / other.x,y: this.y / other.y,});}}toJSON(): any {return {__type: "Vector2",x: this.x,y: this.y,}transform(trans: Vector2, scale: number): Vector2 {return new Vector2({x: Math.floor((this.x - trans.x) * scale),y: Math.floor((this.y - trans.y) * scale),});}normalize(): Vector2 {if (this.x === 0 && this.y === 0) {return this;}const length = Math.sqrt(this.x * this.x + this.y * this.y);return new Vector2({x: this.x / length,});}withX(newX: number): Vector2 {return new Vector2({x: newX,y: this.y,});}withY(newY: number): Vector2 {return new Vector2({x: this.x,y: newY,});}invertX(): Vector2 {return new Vector2({x: -this.x,y: this.y,});}lerp(other: Vector2, t: number): Vector2 {if (t === 0) return this;if (t === 1) return other;}lerp2D(other: Vector2, tx: number, ty: number): Vector2 {}coserp(other: Vector2, t: number): Vector2 {t = 0.5 * (1 + Math.cos(2 * t * Math.PI));return this.lerp(other, t);}static Deserialize(obj: any): Vector2 {if (!obj.hasOwnProperty("x") || !obj.hasOwnProperty("y")) {console.error("Failed deserializing point");}return new Vector2({x: obj.x,y: obj.y,});}static Serialize(obj: Vector2): string {return JSON.stringify({ x: obj.x, y: obj.y });}}if (tx > 1 || tx < 0 || ty > 1 || ty < 0) {console.error("Lerp t must be between 0 and 1.");}return this.scale({ x: 0, y: 0 }, { x: 1 - tx, y: 1 - ty }).add(other.scale({ x: 0, y: 0 }, { x: tx, y: ty }));return this.scale({ x: 0, y: 0 }, { x: 1 - t, y: 1 - t }).add(other.scale({ x: 0, y: 0 }, { x: t, y: t }));if (t > 1 || t < 0) {console.error("Lerp t must be between 0 and 1.");}y: this.y / length,};x:Math.cos(angle) * (this.x - origin.x) -Math.sin(angle) * (this.y - origin.y) +origin.x,y:Math.sin(angle) * (this.x - origin.x) +Math.cos(angle) * (this.y - origin.y) +origin.y,scale(about: { x: number; y: number },amount: { x: number; y: number }): Vector2 {if (newY < low) {newY = low;}if (newY > high) {newY = high;}add(p: { x: number; y: number }): Vector2 {subtract(p: { x: number; y: number }): Vector2 {translate(p: { x: number; y: number }): Vector2 {constructor(props: { x: number; y: number });constructor(propsOrX: { x: number; y: number } | number = { x: 0, y: 0 },y?: number) {public get x(): number {return this._x;}public get y(): number {return this._y;}
import { Rect } from "./rect";import { Vector2 } from "./vector2";export class RectGroup {private _rects: Rect[];constructor(rects: Rect[]) {this._rects = rects;}intersects(other: Rect | RectGroup) {if (other instanceof Rect) {for (const rect of this._rects) {if (rect.intersects(other)) {return true;}}return false;}if (other instanceof RectGroup) {for (const r1 of this._rects) {for (const r2 of this._rects) {if (r1.intersects(r2)) {return true;}}}return false;}}getRects(): Rect[] {return this._rects;}add(delta: Vector2): RectGroup {return new RectGroup(newRects);}subtract(delta: Vector2): RectGroup {return new RectGroup(newRects);}}const newRects = this._rects.map((rect) => rect.subtract(delta));const newRects = this._rects.map((rect) => rect.add(delta));
import { Line } from "./line";import { Vector2, IVector2 } from "./vector2";/*** Immutable rectangle class.*/export class Rect {private _x: number;private _y: number;private _width: number;private _height: number;public get x(): number {return this._x;}public get y(): number {return this._y;}public get width(): number {return this._width;}public get height(): number {return this._height;}public get centerX(): number {return this._x + this._width / 2;}public get centerY(): number {return this._y + this._height / 2;}public get right(): number {return this._x + this._width;}public get bottom(): number {return this._y + this._height;}public get top(): number {return this._y;}public get left(): number {return this._x;}public get center(): Vector2 {return new Vector2({x: this.x + this.width / 2,y: this.y + this.height / 2,});}public get dimensions(): Vector2 {return new Vector2({ x: this.width, y: this.height });}public static FromPoint(point: IVector2, size: number): Rect {return new Rect({x: point.x,y: point.y,width: size,height: size,});}public static FromPoints(p1: IVector2, p2: IVector2): Rect {return new Rect({x: Math.min(p1.x, p2.x),y: Math.min(p1.y, p2.y),width: Math.abs(p1.x - p2.x),height: Math.abs(p1.y - p2.y),});}public static FromCenter(props: {center: IVector2;dimensions: IVector2;}): Rect {return new Rect({x: props.center.x - props.dimensions.x / 2,y: props.center.y - props.dimensions.y / 2,width: props.dimensions.x,height: props.dimensions.y,});}public withRight(value: number): Rect {return new Rect({x: this.x,y: this.y,width: value - this.x,height: this.height,});}public withWidth(value: number): Rect {return new Rect({x: this.x,y: this.y,width: value,height: this.height,});}public withHeight(value: number): Rect {return new Rect({x: this.x,y: this.y,width: this.width,height: value,});}public withBottom(value: number): Rect {return new Rect({x: this.x,y: this.y,width: this.width,height: value - this.y,});}public withX(value: number): Rect {return new Rect({x: value,y: this.y,width: this.width,height: this.height,});}public withY(value: number): Rect {return new Rect({x: this.x,y: value,width: this.width,height: this.height,});}public withTop(value: number): Rect {return this.withY(value);}public withLeft(value: number): Rect {return this.withX(value);}/*** bottomRight is held constant.*/public withTopLeft(topLeft: IVector2): Rect {return Rect.FromPoints(topLeft, this.bottomRight);}/*** bottomLeft is held constant.*/public withTopRight(topRight: IVector2): Rect {return Rect.FromPoints(topRight, this.bottomLeft);}/*** topLeft is held constant.*/public withBottomRight(bottomRight: IVector2): Rect {return Rect.FromPoints(bottomRight, this.topLeft);}/*** topRight is held constant.*/public withBottomLeft(bottomLeft: IVector2): Rect {return Rect.FromPoints(bottomLeft, this.topRight);}public withCenter(center: IVector2): Rect {return new Rect({x: center.x - this.width / 2,y: center.y - this.height / 2,width: this.width,height: this.height,});}/*** center is held constant*/public withScale(props: { width?: number; height?: number }): Rect {return new Rect({x: this.centerX - (props.width || this.width) / 2,y: this.centerY - (props.height || this.height) / 2,width: props.width || this.width,height: props.height || this.height,});}public get topLeft(): Vector2 {return new Vector2({x: this.x,y: this.y,});}public get topRight(): Vector2 {return new Vector2({x: this.right,y: this.y,});}public get bottomRight(): Vector2 {return new Vector2({x: this.right,y: this.bottom,});}public get bottomLeft(): Vector2 {return new Vector2({x: this.x,y: this.bottom,});}constructor(props: { x: number; y: number; width: number; height: number }) {this._x = props.x;this._y = props.y;this._width = props.width;this._height = props.height;}static DeserializeRect(s: string): Rect {const [x, y, w, h] = s.split("|").map((x) => Number(x));return new Rect({ x, y, width: w, height: h });}/*** Return the four edges of this Rect as Lines.*/getLinesFromRect(): Line[] {return [new Line({ x1: this.x, y1: this.y, x2: this.x + this.width, y2: this.y }),new Line({x1: this.x,y1: this.y,x2: this.x,y2: this.y + this.height,}),new Line({x1: this.x + this.width,y1: this.y + this.height,x2: this.x + this.width,y2: this.y,}),new Line({x1: this.x + this.width,y1: this.y + this.height,x2: this.x,y2: this.y + this.height,}),];}/*** Return the four corners of this Rect.*/getCorners(): Vector2[] {return [new Vector2({ x: this.x, y: this.y }),new Vector2({ x: this.x + this.width, y: this.y }),new Vector2({ x: this.x, y: this.y + this.height }),new Vector2({ x: this.x + this.width, y: this.y + this.height }),];}serialize(): string {return `${this.x}|${this.y}|${this.width}|${this.height}`;}// consider overlapping edges as intersection, but not overlapping corners.intersects(other: Rect,props: { edgesOnlyIsAnIntersection: boolean } = {edgesOnlyIsAnIntersection: false,}): boolean {const intersection = this.getIntersection(other, true);if (props.edgesOnlyIsAnIntersection) {return (!!intersection && (intersection.width > 0 || intersection.height > 0));} else {return !!intersection && intersection.width * intersection.height > 0;}}completelyContains(smaller: Rect): boolean {return (this.x <= smaller.x &&this.x + this.width >= smaller.x + smaller.width &&this.y <= smaller.y &&this.y + this.height >= smaller.y + smaller.height);}getIntersection(other: Rect,edgesOnlyIsAnIntersection = false): Rect | undefined {const xmin = Math.max(this.x, other.x);const xmax1 = this.x + this.width;const xmax2 = other.x + other.width;const xmax = Math.min(xmax1, xmax2);if (xmax > xmin || (edgesOnlyIsAnIntersection && xmax >= xmin)) {const ymin = Math.max(this.y, other.y);const ymax1 = this.y + this.height;const ymax2 = other.y + other.height;const ymax = Math.min(ymax1, ymax2);if (ymax >= ymin || (edgesOnlyIsAnIntersection && ymax >= ymin)) {return new Rect({x: xmin,y: ymin,width: xmax - xmin,height: ymax - ymin,});}}return undefined;}contains(p: IVector2): boolean {return (p.x >= this.x &&p.x < this.x + this.width &&p.y >= this.y &&p.y < this.y + this.height);}clone(): Rect {return new Rect({x: this.x,y: this.y,width: this.width,height: this.height,});}add(p: IVector2): Rect {return this.translate(p);}subtract(p: IVector2): Rect {return this.translate({ x: -p.x, y: -p.y });}translate(p: IVector2): Rect {return new Rect({x: this.x + p.x,y: this.y + p.y,width: this.width,height: this.height,});}scale(p: Vector2): Rect {return new Rect({x: this.x,y: this.y,width: this.width * p.x,height: this.height * p.y,});}centeredAtOrigin(): Rect {return new Rect({x: -this.width / 2,y: -this.height / 2,width: this.width,height: this.height,});}equals(o: Rect | undefined | null): boolean {if (!o) {return false;}return (this.x === o.x &&this.y === o.y &&this.width === o.width &&this.height === o.height);}toJSON(): any {return {x: this.x,y: this.y,w: this.width,h: this.height,reviver: "Rect",};}/*** Adds amount to both width and height.*/extend(amount: number): Rect {return new Rect({x: this.x,y: this.y,width: this.width + amount,height: this.height + amount,});}shrink(amount: number): Rect {return new Rect({x: this.x + amount,y: this.y + amount,width: Math.max(this.width - amount * 2, 0),height: Math.max(this.height - amount * 2, 0),});}floor(): Rect {return new Rect({x: Math.floor(this.x),y: Math.floor(this.y),width: Math.floor(this.width),height: Math.floor(this.height),});}/*** Grow the Rect by amount in all directions.*/expand(amount: number): Rect {return this.shrink(-amount);}transform(trans: Vector2, scale: number): Rect {const topLeft = this.topLeft.transform(trans, scale);const botRight = this.bottomRight.transform(trans, scale);return new Rect({x: topLeft.x,y: topLeft.y,width: botRight.x - topLeft.x,height: botRight.y - topLeft.y,});}static Deserialize(obj: any): Rect {if (!obj.hasOwnProperty("x") ||!obj.hasOwnProperty("y") ||!obj.hasOwnProperty("w") ||!obj.hasOwnProperty("h")) {console.error("Failed deserializing Rect");}return new Rect({x: obj.x,y: obj.y,width: obj.w,height: obj.h,});}static Serialize(r: Rect): string {return JSON.stringify({x: r.x,y: r.y,w: r.width,h: r.height,});}toString(): string {return `[${this.x}, ${this.y}]`;}}hash(): string {return this.toString();}
import { Graphics } from "pixi.js";import { epsGreaterThan, epsLessThan } from "../epsilon_math";export class Line {private _x1: number;private _x2: number;private _y1: number;private _y2: number;public get angleInDegrees(): number {const cx = this._x1;const cy = this._y1;const ex = this._x2;const ey = this._y2;const dy = ey - cy;const dx = ex - cx;let theta = Math.atan2(dy, dx);theta *= 180 / Math.PI;if (theta < 0) {theta = 360 + theta;}return theta;}public serialized = "";let x1, x2, y1, y2;x1 = props.x1;x2 = props.x2;y1 = props.y1;y2 = props.y2;} else {x1 = props.start.x;x2 = props.end.x;y1 = props.start.y;y2 = props.end.y;}this._x1 = x1;this._y1 = y1;this._x2 = x2;this._y2 = y2;}public get length(): number {return Math.sqrt((this.x2 - this.x1) * (this.x2 - this.x1) +);}public get isDegenerate(): boolean {return this.length === 0;}public rotateAbout(origin: Vector2, angle: number): Line {const start = this.start;const end = this.end;return new Line({start: start.rotate(origin, angle),end: end.rotate(origin, angle),});}public scaleAbout(about: Vector2, amount: Vector2): Line {return new Line({start: this.start.scale(about, amount),end: this.end.scale(about, amount),});}sharesAVertexWith(other: Line): Vector2 | null {return null;}static DeserializeLine(s: string): Line {return new Line({ x1, x2, y1, y2 });}isXAligned(): boolean {return this.x1 === this.x2;}isYAligned(): boolean {return this.y1 === this.y2;}// Must be horizontally/vertically oriented lines// Does not consider intersection, only overlapgetOverlap(other: Line): Line | undefined {const overallLength = new Line({x1: Math.min(this.x1, other.x1),y1: Math.min(this.y1, other.y1),x2: Math.max(this.x2, other.x2),y2: Math.max(this.y2, other.y2),}).length;if (overallLength >= summedLength) {// These lines do not overlap.return undefined;}if (orientedByX) {return new Line({x1: this.x1,x2: this.x2,y1: Math.max(this.y1, other.y1),y2: Math.min(this.y2, other.y2),});return new Line({y1: this.y1,y2: this.y2,x1: Math.max(this.x1, other.x1),x2: Math.min(this.x2, other.x2),});}}// A----B----C----D// AD - BC returns AB and CD.getNonOverlappingSections(other: Line): Line[] | undefined {const overallLength = new Line({x1: Math.min(this.x1, other.x1),y1: Math.min(this.y1, other.y1),x2: Math.max(this.x1, other.x1),y2: Math.max(this.y1, other.y1),}).length;if (overallLength >= summedLength) {// These lines do not overlap.return undefined;}if (orientedByX) {return [return [}}clone(): Line {return new Line({ x1: this.x1, x2: this.x2, y1: this.y1, y2: this.y2 });}translate(p: Vector2): Line {return new Line({x1: this.x1 + p.x,x2: this.x2 + p.x,y1: this.y1 + p.y,y2: this.y2 + p.y,});}transform(trans: Vector2, scale: number): Line {return new Line({start: this.start.transform(trans, scale),end: this.end.transform(trans, scale),});}toJSON(): any {return {reviver: "Line",};}toString(): string {}equals(other: Line | null) {return ();}withNewEnd(newEnd: Vector2): Line {return new Line({x1: this.x1,y1: this.y1,x2: newEnd.x,y2: newEnd.y,});}withNewStart(newStart: Vector2): Line {return new Line({x1: newStart.x,y1: newStart.y,x2: this.x2,y2: this.y2,});}static Deserialize(obj: any): Line {if (!obj.hasOwnProperty("x1") ||!obj.hasOwnProperty("y1") ||!obj.hasOwnProperty("x2") ||console.error("Failed deserializing Rect");}return new Line({x1: obj.x1,y1: obj.y1,x2: obj.x2,y2: obj.y2,});}static Serialize(obj: Line): string {return JSON.stringify({x1: obj.x1,y1: obj.y1,x2: obj.x2,y2: obj.y2,});}drawOnto(graphics: Graphics, color = 0xff0000) {graphics.lineStyle(3, color, 1);graphics.moveTo(this.x1, this.y1);graphics.lineTo(this.x2, this.y2);}* Returns the point where these two lines, if extended arbitrarily, would* intersect.*/lineIntersection(other: Line): Vector2 {const p1 = this.start;const p2 = this.end;const p3 = other.start;const p4 = other.end;const x = p1.x + s * (p2.x - p1.x);const y = p1.y + s * (p2.y - p1.y);return new Vector2({ x, y });}/*** Returns the point where these two segments exist, if there is one.*/segmentIntersection(other: Line): Vector2 | null {const lineIntersection = this.lineIntersection(other);const x = lineIntersection.x;const y = lineIntersection.y;if () {return lineIntersection;}return null;}normalize(): Line {return new Line({start: this.start,end: new Vector2({x: this.start.x + (this.end.x - this.start.x) / mag,y: this.start.x + (this.end.y - this.start.y) / mag,}hash(): string {return this.toString();}add(x: Vector2): Line {start: this.start.add(x),end: this.end.add(x),}}});return new Line({}),});const mag = Math.sqrt((this.x1 - this.x2) ** 2 + (this.y1 - this.y2) ** 2);epsGreaterThan(x, Math.min(other.x1, other.x2)) &&epsLessThan(x, Math.max(other.x1, other.x2)) &&epsGreaterThan(y, Math.min(other.y1, other.y2)) &&epsLessThan(y, Math.max(other.y1, other.y2))epsGreaterThan(x, Math.min(this.x1, this.x2)) &&epsLessThan(x, Math.max(this.x1, this.x2)) &&epsGreaterThan(y, Math.min(this.y1, this.y2)) &&epsLessThan(y, Math.max(this.y1, this.y2)) &&// within other// within usconst s =((p4.x - p3.x) * (p1.y - p3.y) - (p4.y - p3.y) * (p1.x - p3.x)) /((p4.y - p3.y) * (p2.x - p1.x) - (p4.x - p3.x) * (p2.y - p1.y));/**!obj.hasOwnProperty("y2")) {(this.x1 === other.x1 &&this.x2 === other.x2 &&this.y1 === other.y1 &&this.y2 === other.y2) ||(this.x1 === other.x2 &&this.x2 === other.x1 &&this.y1 === other.y2 &&this.y2 === other.y1)if (other === null) {return false;}return `Line: [(${this.x1},${this.y1}) -> (${this.x2},${this.y2})]`;x1: this.x1,x2: this.x2,y1: this.y1,y2: this.y2,new Line({y1: this.y1,y2: this.y2,x1: Math.min(this.x1, other.x1),x2: Math.max(this.x1, other.x1),}),new Line({y1: this.y1,y2: this.y2,x1: Math.min(this.x2, other.x2),x2: Math.max(this.x2, other.x2),}),].filter((l) => !l.isDegenerate);new Line({x1: this.x1,x2: this.x2,y1: Math.min(this.y1, other.y1),y2: Math.max(this.y1, other.y1),}),new Line({x1: this.x1,x2: this.x2,y1: Math.min(this.y2, other.y2),y2: Math.max(this.y2, other.y2),}),].filter((l) => !l.isDegenerate);} /* if (orientedByY) */ else {const summedLength = new Line(this).length + new Line(other).length;if (!orientedByX && !orientedByY) {return undefined;}const orientedByY =this.y1 === this.y2 && this.y1 === other.y1 && this.y1 === other.y2;const orientedByX =this.x1 === this.x2 && this.x1 === other.x1 && this.x1 === other.x2;} /* if (orientedByY) */ else {const summedLength = this.length + other.length;if (!orientedByX && !orientedByY) {return undefined;}const orientedByY =this.y1 === this.y2 && this.y1 === other.y1 && this.y1 === other.y2;const orientedByX =this.x1 === this.x2 && this.x1 === other.x1 && this.x1 === other.x2;const [x1, x2, y1, y2] = s.split("|").map((x) => Number(x));if (this.end.equals(other.start)) {return this.end;}if (this.end.equals(other.end)) {return this.end;}if (this.start.equals(other.start)) {return this.start;}if (this.start.equals(other.end)) {return this.start;}(this.y2 - this.y1) * (this.y2 - this.y1)this.serialized = `${this.x1}|${this.x2}|${this.y1}|${this.y2}`;if ("x1" in props) {constructor(props:| { x1: number; x2: number; y1: number; y2: number }| { start: Vector2; end: Vector2 }) {public get start(): Vector2 {return new Vector2({ x: this.x1, y: this.y1 });}public get end(): Vector2 {return new Vector2({ x: this.x2, y: this.y2 });}public get x1(): number {return this._x1;}public get x2(): number {return this._x2;}public get y1(): number {return this._y1;}public get y2(): number {return this._y2;}import { Vector2 } from "./vector2";
const MAX_SIZE = 500;export class ArbitrarySelection {cover: Rect[] = [];outlinesDirty = true;oldOutlines: Line[][] = [];constructor(cover: Rect[] = []) {this.cover = cover;}public get x(): number {}public get y(): number {}public get w(): number {}public get h(): number {}public get pos(): Vector2 {return new Vector2({ x: this.x, y: this.y });}public get bounds(): Rect {return new Rect({x: this.x,y: this.y,width: this.w,height: this.h,});}public get isEmpty(): boolean {return this.cover.length === 0;}reset(): void {this.cover = [];this.oldOutlines = [];}addRect(rectToAdd: Rect): void {if (subsumingRects.length > 0) {return;}for (const rect of intersectingRects) {this.subtractRect(rect.getIntersection(rectToAdd)!);}this.cover.push(rectToAdd);this.outlinesDirty = true;}subtractRect(rectToSubtract: Rect): void {for (const rect of intersectingRects) {// rectToSubtract completely contains rectif (rectToSubtract.completelyContains(rect)) {continue;}// rectToSubtract partially contains rectconst subrectToRemove = rectToSubtract.getIntersection(rect)!;// rect completely contains subtractedRect// -------------------------// | A |// | |// |-----------------------|// | B | hole | C |// |-----------------------|// | |// | D |// -------------------------const newRects = [this.cover = this.cover.concat(newRects);}for (const rect of intersectingRects) {this.cover.splice(this.cover.indexOf(rect), 1);}this.outlinesDirty = true;if (this.isEmpty) {this.reset();}}// O(n^2) scc algorithm until someone convinces me I need a faster onegetConnectedComponents(): Rect[][] {const components: Rect[][] = [];for (const rect of this.cover) {const component = this.getConnectedComponentFrom(rect);components.push(component);for (const seen of component) {seenRects[seen.serialize()] = true;}}return components;}private getConnectedComponentFrom(start: Rect): Rect[] {let edge = [start];while (edge.length > 0) {let newEdge: Rect[] = [];for (const rect of edge) {component[rect.serialize()] = true;newEdge = newEdge.concat(intersectingRects);}edge = newEdge;}}getOutlines(): Line[][] {if (!this.outlinesDirty) {return this.oldOutlines;}let result: Line[][] = [];const components = this.getConnectedComponents();for (const c of components) {const outline = this.getOutlineFor(c);const outlineComponents = this.getComponentsOfOutline(outline);}this.oldOutlines = result;this.outlinesDirty = false;return result;}private getOutlineFor(comp: Rect[]): Line[] {let allLines: (Line | undefined)[] = [];for (const rect of comp) {allLines.push.apply(allLines, rect.getLinesFromRect());}// Alternate solution if this proves too hard:// Subdivide all lines on intersection points, then remove all// duplicates.// Actually that might even be better heh// The strategy here is to basically remove all overlapping segments. it's// hard because a single line could be overlapping with multiple other// lines.for (let i = 0; i < allLines.length; i++) {const line1 = allLines[i];for (let j = 0; j < allLines.length; j++) {const line2 = allLines[j];const intersection = line1.getOverlap(line2);if (intersection) {allLines[i] = undefined;allLines[j] = undefined;const newLines = line1.getNonOverlappingSections(line2);allLines = allLines.concat(newLines);break;}}}}private getComponentsOfOutline(outline: Line[]): Line[][] {// Store lookup table by start and end vertexlet lookupTable: { [key: number]: Line[] } = [];for (const line of outline) {const idx1 = line.x1 * MAX_SIZE + line.y1;const idx2 = line.x2 * MAX_SIZE + line.y2;lookupTable[idx1].push(line);lookupTable[idx2].push(line);}let result: Line[][] = [];let visited: { [key: string]: boolean } = {};for (const line of outline) {visited[line.serialized] = true;const sequence = [line];while (true) {const current = sequence[sequence.length - 1];visited[next.serialized] = true;sequence.push(next);}result.push(sequence);}return result;}addArbitraryShape(pixels: Vector2[], canvasSize: Vector2): void {this.outlinesDirty = true;const covered: boolean[] = new Array(MAX_SIZE * MAX_SIZE);const rects: Rect[] = [];const ll = pixels.length;for (let i = 0; i < ll; i++) {const p = pixels[i];covered[p.x * MAX_SIZE + p.y] = false;}for (let x = 0; x < canvasSize.x; x++) {for (let y = 0; y < canvasSize.y; y++) {let squareSize = 2;const endSquareX = x + squareSize;const endSquareY = y + squareSize;for (let bottomLineX = x; bottomLineX < endSquareX; bottomLineX++) {squareSize--;break outer;}}for (let bottomLineY = y; bottomLineY < endSquareY; bottomLineY++) {squareSize--;break outer;}}}for (let sx = x; sx < x + squareSize; sx++) {for (let sy = y; sy < y + squareSize; sy++) {covered[sx * MAX_SIZE + sy] = true;}}}}for (const r of rects) {this.addRect(r);}}clone(): ArbitrarySelection {const result = new ArbitrarySelection(this.cover.slice(0));result.outlinesDirty = this.outlinesDirty;result.oldOutlines = this.oldOutlines;return result;}translate(p: Vector2): void {}contains(p: Vector2): boolean {for (const r of this.cover) {}return false;}}if (r.contains(p)) {return true;}if (this.cover.length === 0) {return true;}this.cover = this.cover.map((x) => x.translate(p));this.oldOutlines = this.oldOutlines.map((l) =>l.map((ll) => ll.translate(p)));rects.push(new Rect({x: x,y: y,width: squareSize,height: squareSize,}));if (covered[(x + squareSize - 1) * MAX_SIZE + bottomLineY] ===undefined ||covered[(x + squareSize - 1) * MAX_SIZE + bottomLineY] === true) {if (covered[bottomLineX * MAX_SIZE + (y + squareSize - 1)] ===undefined ||covered[bottomLineX * MAX_SIZE + (y + squareSize - 1)] === true) {outer: for (; squareSize < MAX_SIZE; squareSize++) {if (covered[x * MAX_SIZE + y] !== false) {continue;}if (!next) {break;}const candidates = lookupTable[current.x1 * MAX_SIZE + current.y1].concat(lookupTable[current.x2 * MAX_SIZE + current.y2]);const next = candidates.filter((l) => l !== current && !visited[l.serialized])[0];if (visited[line.serialized]) {continue;}if (!lookupTable[idx1]) {lookupTable[idx1] = [];}if (!lookupTable[idx2]) {lookupTable[idx2] = [];}return allLines.filter((l) => l !== undefined) as Line[];if (!line2) {continue;}if (line1 === line2) {continue;}if (!line1) {continue;}result = result.concat(outlineComponents);return Object.keys(component).map((r) => Rect.DeserializeRect(r));const intersectingRects = this.cover.filter((r) =>r.intersects(rect, { edgesOnlyIsAnIntersection: true }));if (component[rect.serialize()]) {continue;}const component: { [key: string]: boolean } = {};if (seenRects[rect.serialize()]) {continue;}const seenRects: { [key: string]: boolean } = {};{x: rect.x,y: rect.y,width: rect.width,height: subrectToRemove.y - rect.y,}, // A{x: rect.x,y: subrectToRemove.y,width: subrectToRemove.x - rect.x,height: subrectToRemove.height,}, // B{x: subrectToRemove.x + subrectToRemove.width,y: subrectToRemove.y,width:rect.x + rect.width - (subrectToRemove.width + subrectToRemove.x),height: subrectToRemove.height,}, // C{x: rect.x,y: subrectToRemove.y + subrectToRemove.height,width: rect.width,height:rect.y + rect.height - (subrectToRemove.y + subrectToRemove.height),}, // D].filter((r) => r.width > 0 && r.height > 0).map((r) => new Rect(r));const intersectingRects = this.cover.filter((r) =>r.intersects(rectToSubtract, { edgesOnlyIsAnIntersection: false }));const subsumingRects = this.cover.filter((r) =>r.completelyContains(rectToAdd));const intersectingRects = this.cover.filter((r) =>r.intersects(rectToAdd, { edgesOnlyIsAnIntersection: false }));return (Util.MaxBy(this.cover, (r) => r.bottom)!.bottom -Util.MinBy(this.cover, (r) => r.y)!.y);if (this.cover.length === 0) {return 0;}return (Util.MaxBy(this.cover, (r) => r.right)!.right -Util.MinBy(this.cover, (r) => r.x)!.x);if (this.cover.length === 0) {return 0;}return Util.MinBy(this.cover, (r) => r.y)!.y;if (this.cover.length === 0) {return 0;}return Util.MinBy(this.cover, (r) => r.x)!.x;if (this.cover.length === 0) {return 0;}import { Util } from "../util";import { Line } from "./line";import { Rect } from "./rect";import { Vector2 } from "./vector2";
declare module "Library" {export interface ModeList {Normal: never;}export type Mode = keyof ModeList;type HashSet<T> = import("./data_structures/hash").HashSet<T>;type Entity = import("./entity").Entity;type KeyboardState = import("./keyboard").KeyboardState;type CollisionGrid = import("./collision_grid").CollisionGrid;type Camera = import("./camera").Camera;export interface IGameState {camera: Camera;keys: KeyboardState;lastCollisionGrid: CollisionGrid;entities: HashSet<Entity>;spriteToEntity: { [key: number]: Entity };renderer: Renderer;tick: number;toBeDestroyed: Entity[];stage: Entity;mode: Mode;}}
const KeyInfo = () => ({Spacebar: false,});export type KeyInfoType = ReturnType<typeof KeyInfo>;interface QueuedKeyboardEvent {isDown: boolean;}export class KeyboardState {public justDown = KeyInfo();private _queuedEvents: QueuedKeyboardEvent[] = [];constructor() {}public clear() {this._queuedEvents = [];}private keyUp(e: KeyboardEvent): void {// Since events usually happen between two ticks, we queue them up to be// processed on the next tick.this._queuedEvents.push({ event: e, isDown: false });}private keyDown(e: KeyboardEvent): void {this._queuedEvents.push({ event: e, isDown: true });}private eventToKey(event: KeyboardEvent): string {const number = event.keyCode || event.which;let str: string;switch (number) {/* A-Z */}if (str === " ") {return "Spacebar";}if (str.length === 1) {return str.toUpperCase();}return str[0].toUpperCase() + str.slice(1);}update(): void {for (const key of Object.keys(this.justDown)) {this.justDown[key as keyof KeyInfoType] = false;this.justUp[key as keyof KeyInfoType] = false;}for (const queuedEvent of this._queuedEvents) {console.log("got queuedEvent", queuedEvent);const key = this.eventToKey(queuedEvent.event);if (queuedEvent.isDown) {if (!this.down[key as keyof KeyInfoType]) {this.justDown[key as keyof KeyInfoType] = true;}this.down[key as keyof KeyInfoType] = true;} else {if (this.down[key as keyof KeyInfoType]) {this.justUp[key as keyof KeyInfoType] = true;}this.down[key as keyof KeyInfoType] = false;}}this._queuedEvents = [];}}default:str = String.fromCharCode(number);case 13:str = "Enter";break;case 16:str = "Shift";break;case 37:str = "Left";break;case 38:str = "Up";break;case 39:str = "Right";break;case 40:str = "Down";break;this.down = KeyInfo();this.justDown = KeyInfo();this.justUp = KeyInfo();document.addEventListener("keydown", (e) => this.keyDown(e), false);document.addEventListener("keyup", (e) => this.keyUp(e), false);window.addEventListener("blur",() => {this.clear();},false);public justUp = KeyInfo();public down = KeyInfo();event: KeyboardEvent;Enter: false,Q: false,W: false,E: false,R: false,T: false,Y: false,U: false,I: false,O: false,P: false,A: false,S: false,D: false,F: false,G: false,H: false,J: false,K: false,L: false,Z: false,X: false,C: false,V: false,B: false,N: false,M: false,Up: false,Down: false,Left: false,Right: false,Shift: false,
import { Entity, EntityType } from "./entity";import { Vector2 } from "./geometry/vector2";import { Texture } from "pixi.js";import { Rect } from "./geometry/rect";import { BaseGame } from "./base_game";import { BaseGameState } from "./base_state";export class MovingEntity extends Entity {protected _maxSpeed = 50;constructor(props: {game: BaseGame<{}>;texture: Texture;collidable: boolean;}) {super({...props,name: "MovingEntity",});this._collidable = props.collidable;}public get velocity(): Vector2 {return this._velocity;}public set velocity(dir: Vector2) {this._velocity = dir;}public get maxSpeed(): number {return this._maxSpeed;}// Currently just stops moving.collide = (other: Entity, intersection: Rect) => {// if (!this._collidable) return;// this.velocity = Vector2.Zero;};// It's just shyinteract = (other: Entity) => {return;};}public update = (state: BaseGameState) => {};private _velocity = Vector2.Zero;entityType = EntityType.MovingEntity;
type ReactWrapperProps = {game: BaseGame<{}>;debugFlags: DebugFlagsType;};type ReactWrapperState = {selected: Entity | Container | null;moused: Entity | Container | null;};static Instance: GameReactWrapper;mounted = false;constructor(props: ReactWrapperProps) {super(props);this.state = {selected: this.props.game.stage,moused: null,};setInterval(() => this.monitorHierarchyUpdates(), 500);}componentDidMount() {this.mounted = true;GameReactWrapper.Instance = this;}componentWillUnmount() {console.error("This should never happen!!!! very bad?!?");}monitorHierarchyUpdates = () => {if (this.mounted) {this.forceUpdate();}};setSelected = (obj: Entity | Container) => {this.setState({selected: obj,});};setMoused = (obj: Entity | Container | null) => {this.setState({moused: obj,});};renderSelected = () => {const target = this.state.moused || this.state.selected;if (target instanceof Container) {return ();}return (<div><div>x: {target.x}, y: {target.y}</div><div></div><div>width: {target.width}, height: {target.height}</div><div>visible: {target.visible ? "true" : "false"}</div><div></div></div>);};renderHierarchy() {}render() {return (</div><DebugFlagButtons flags={this.props.debugFlags} />{this.renderSelected()}{this.renderHierarchy()}</div></div></div>);}}export const CreateGame = (game: BaseGame<any>, debugFlags: DebugFlagsType) => {ReactDOM.render(<React.StrictMode></React.StrictMode>,);};document.getElementById("root")<GameReactWrapper game={game} debugFlags={debugFlags} />)}<divstyle={{fontWeight: 600,fontFamily: "arial",paddingTop: "8px",paddingBottom: "8px",fontSize: "18px",}}>Debug Hierarchy</div><div>Draw Count: {Debug.GetDrawCount()}</div><divstyle={{fontWeight: 600,fontFamily: "arial",paddingBottom: "8px",fontSize: "18px",}}>Debug Options</div><divstyle={{display: "flex",flexDirection: "row",borderLeft: IS_DEBUG ? "1px solid lightgray" : 0,marginLeft: "16px",paddingLeft: "8px",}}><divstyle={{overflow: "auto",height: "90vh",fontFamily: "arial",fontSize: "14px",}}>{this.props.game && this.props.game.stage && IS_DEBUG && (<div style={{ paddingLeft: "8px" }}><divstyle={{fontFamily: "arial",marginBottom: "8px",fontSize: "14px",width: "300px",padding: "8px",}}>Note: This debugging panel is only shown in development, orproduction with ?debug=true.<HierarchyselectedEntity={this.state.selected}setMoused={this.setMoused}setSelected={this.setSelected}root={this.props.game.fixedCameraStage}gameState={this.props.game.state}/></div>);return (<div><HierarchyselectedEntity={this.state.selected}setMoused={this.setMoused}setSelected={this.setSelected}root={this.props.game.stage}gameState={this.props.game.state}/>{target instanceof TextEntity ? (<div>text: {target.html}</div>) : (<div>hi</div>)}scaleX: {target.scale.x.toFixed(2)} scaleY:{" "}{target.scale.y.toFixed(2)}xAbs: {target.positionAbsolute().x}, yAbs:{" "}{target.positionAbsolute().y}<divstyle={{fontWeight: 600,fontFamily: "arial",paddingTop: "8px",paddingBottom: "8px",fontSize: "18px",}}>{target.name}</div><divstyle={{fontWeight: 600,fontFamily: "arial",paddingTop: "8px",paddingBottom: "8px",fontSize: "18px",}}>Stage</div>if (target === null) {return null;}export class GameReactWrapper extends React.Component<ReactWrapperProps,ReactWrapperState> {import { BaseGame } from "../base_game";import { Hierarchy } from "./hierarchy";import { DebugFlagButtons, DebugFlagsType } from "./debug_flag_buttons";import { IS_DEBUG } from "../environment";import { Entity } from "../entity";import { Container } from "pixi.js";import { TextEntity } from "../text_entity";import { Debug } from "../debug";import React from "react";import ReactDOM from "react-dom";
// import { DebugFlags } from '../../game/debug';type HierarchyProps = {root: Entity | Container;setSelected: (obj: Entity | Container) => void;setMoused: (obj: Entity | Container | null) => void;gameState: IGameState;selectedEntity: Entity | Container | null;};constructor(props: HierarchyProps) {super(props);this.state = {hover: false,collapsed: true,};setInterval(() => {this.updateDebugGraphics();}, 200);}oldTint: { [key: number]: number } = {};hoverGraphics: Graphics[] = [];hoverTarget: Entity | Container | null = null;updateDebugGraphics = () => {// clear debug graphicsfor (const graphic of this.hoverGraphics) {graphic.parent.removeChild(graphic);graphic.destroy();}this.hoverGraphics = [];if (this.hoverTarget !== null) {if (this.props.root instanceof Entity) {const point = Debug.DrawPoint(this.props.root.position, 0xff0000, true);}}if (this.props.selectedEntity === this.props.root) {if (this.props.root instanceof Entity) {}}};mouseOver = () => {if (this.props.root instanceof Entity) {this.oldTint[this.props.root.id] = this.props.root.sprite.tint;this.props.root.sprite.tint = 0x000099;}this.hoverTarget = this.props.root;this.props.setMoused(this.props.root);};mouseOut = () => {if (this.props.root instanceof Entity) {this.props.root.sprite.tint = this.oldTint[this.props.root.id];}this.hoverTarget = null;this.props.setMoused(null);};click = () => {this.props.setSelected(this.props.root);console.log(this.props.root);};renderLeaf(root: any) {}render() {const root = this.props.root;let children = allChildren;let canCollapse = children.length > 20;let didCollapse = false;if (canCollapse) {if (this.state.collapsed) {children = children.slice(0, 20);didCollapse = true;}}if (children)return (<divstyle={{paddingLeft: "10px",}}><divonMouseEnter={this.mouseOver}onMouseLeave={this.mouseOut}onClick={this.click}>{this.renderLeaf(root)}</div></div>});}{children.map((child) => {return (<HierarchyselectedEntity={this.props.selectedEntity}setMoused={this.props.setMoused}setSelected={this.props.setSelected}root={child}gameState={this.props.gameState}/>);})}{canCollapse ? (<divonClick={() =>this.setState({ collapsed: !this.state.collapsed })}style={{ padding: "8px 0" }}>{didCollapse ? (<span>[see {allChildren.length - 20} more]</span>) : (<span>[collapse]</span>)}</div>) : null}fontFamily: "Arial",fontSize: "14px",backgroundColor: this.state.hover ? "darkgray" : "black",let allChildren = root instanceof Entity ? root.children() : [];return (<div>{this.props.selectedEntity === this.props.root ? (<strong>{root.name}</strong>) : (root.name)}{" "}(depth: {root.zIndex}){" "}{root instanceof Entity &&(root.activeModes.includes(this.props.gameState.mode)? "Active": "Inactive")}</div>);this.setState({ hover: false });this.setState({ hover: true });this.hoverGraphics = [...this.hoverGraphics, point];const point = Debug.DrawPoint(this.props.selectedEntity.position,0xff0000,true);this.hoverGraphics = [...this.hoverGraphics,...Debug.DrawBounds(this.props.selectedEntity, 0xff0000, true, "stage"),];this.hoverGraphics = [...this.hoverGraphics, point];this.hoverGraphics = [...Debug.DrawBounds(this.props.root, 0xff0000, true, "stage"),];export class Hierarchy extends React.Component<HierarchyProps,{hover: boolean;collapsed: boolean;}> {import { Container, Graphics } from "pixi.js";import { Entity } from "../entity";import { Debug } from "../debug";import { IGameState } from "Library";import React from "react";
export type DebugFlagsType = {[key: string]: boolean;};const LOCAL_STORAGE_KEY = "debug flags";if (IS_DEBUG) {// delete flags that don't existfor (const flagName of Object.keys(prevStoredFlags)) {if (!defaultFlags.hasOwnProperty(flagName)) {delete prevStoredFlags[flagName];}}return {...defaultFlags,...prevStoredFlags,};} else {return defaultFlags;}const SaveDebugFlagsToLocalStorage = (flags: DebugFlagsType) => {window.localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(flags));};type DebugFlagButtonsProps = {flags: DebugFlagsType;};render() {const flagNames = Object.keys(this.props.flags);return (<div></div>}});SaveDebugFlagsToLocalStorage(this.props.flags);}}/>{" "}{flagName}</div>);})}// The point being that the value gets synced back into the// game with no one being the wiser. MAGIC!this.props.flags[flagName] = !this.props.flags[flagName];// The reason this works at all is because GameReactWrapper// does a forceUpdate() on a setInterval to keep everything// in sync. DebugFlagButtons will be captured in the// setInterval and forced to update.return (<div key={flagName}><inputtype="checkbox"checked={flag}onChange={() => {// NOTE: This is TERRIBLE React code. DO NOT LEARN FROM// THIS. DO NOT IMITATE THIS. IN FACT, RUN FAR AWAY FROM// THIS!!!{flagNames.map((flagName) => {const flag = this.props.flags[flagName];export class DebugFlagButtons extends React.Component<DebugFlagButtonsProps,{}> {};const prevStoredFlags = JSON.parse(window.localStorage.getItem(LOCAL_STORAGE_KEY) || "{}") as DebugFlagsType;export const ReadDebugFlagsFromLocalStorage = <T extends DebugFlagsType>(defaultFlags: T): T => {import React from "react";import { IS_DEBUG } from "../environment";
{"compilerOptions": {/* Basic Options */// "node"// ],// "lib": [], /* Specify library files to be included in the compilation. */// "allowJs": true, /* Allow javascript files to be compiled. */// "checkJs": true, /* Report errors in .js files. */// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */// "declaration": true, /* Generates corresponding '.d.ts' file. */// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */// "sourceMap": true, /* Generates corresponding '.map' file. */// "outFile": "./", /* Concatenate and emit output to single file. */// "outDir": "./", /* Redirect output structure to the directory. */// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */// "composite": true, /* Enable project compilation */// "removeComments": true, /* Do not emit comments to output. */// "noEmit": true, /* Do not emit outputs. */// "importHelpers": true, /* Import emit helpers from 'tslib'. */// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). *//* Strict Type-Checking Options */// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */// "strictNullChecks": true, /* Enable strict null checks. */// "strictFunctionTypes": true, /* Enable strict checking of function types. */// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. *//* Additional Checks */// "noUnusedLocals": true, /* Report errors on unused locals. */// "noUnusedParameters": true, /* Report errors on unused parameters. */// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. *//* Module Resolution Options */// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */// "typeRoots": [], /* List of folders to include type definitions from. */// "types": [], /* Type declaration files to be included in compilation. */// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. *//* Source Map Options */// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. *//* Experimental Options */// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */}}"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */"strict": true /* Enable all strict type-checking options. */,// "types": ["target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */,"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
import { Util } from "../util";const configPath = process.argv[2];const configDirectory = path.dirname(configPath);const assetDirectory = path.join(configDirectory, config.assets.assetsPath);function walkDir(dir: string, callback: (path: string) => void) {fs.readdirSync(dir).forEach((f: string) => {const isDirectory = fs.statSync(dirPath).isDirectory();isDirectory ? walkDir(dirPath, callback) : callback(path.join(dir, f));});function allNestedFiles(dir: string): string[] {let files: string[] = [];files.push(path.slice(dir.length));});return files;}const isPathTiledTileMap = (path: string) => {try {} catch (e) {return false;}};const isPathTiledWorldMap = (path: string) => {try {} catch (e) {return false;}};const buildAssetsFile = () => {const normalFiles: string[] = [];const animationBundles: { [key: string]: string[] } = {};for (const file of allFiles) {const match = /(.+)_(\d+)\.(png|gif)$/.exec(file);if (match !== null) {const [fullString, prefix, frame, extension] = match;animationBundles[prefix][Number(frame)] = file;continue;}const match2 = /(.+) \((\d+)\)\.(png|gif)$/.exec(file);if (match2 !== null) {const [fullString, prefix, frame, extension] = match2;animationBundles[prefix][Number(frame)] = file;continue;}normalFiles.push(file);continue;}let output = `// THIS FILE IS AUTOGENERATED from the parameters in config.json. Do not edit it.// If you want to change something about how it's generated, look at library/asset_builder.ts.import { TypesafeLoader } from "../library/typesafe_loader";export type AssetType =| "Image"| "TileMap"| "TileWorld"| "Audio"| "Spritesheet"| "Animation"export type AssetName = keyof typeof AssetsToLoadexport type AssetPath =export const AssetsToLoad = {if (allFiles.length === 0) {return output;}const longestAssetType = "'TileWorld'".length;for (const file of normalFiles) {if (file.endsWith(".png") || file.endsWith(".gif")) {resourceType = "Image";} else if (file.endsWith(".json")) {if (isPathTiledTileMap(path.join(assetDirectory, file))) {resourceType = "TileMap";} else if (isPathTiledWorldMap(path.join(assetDirectory, file))) {resourceType = "TileWorld";} else {continue;}} else if (file.endsWith(".mp3")) {resourceType = "Audio";}const fileNameWithoutExtension = file.slice(0, file.lastIndexOf("."));}if (Object.keys(animationBundles).length > 0) {for (const animationName of Object.keys(animationBundles)) {output += ` type: "Animation" as const,\n`;output += ` paths: [\n`;for (const frame of animationBundles[animationName]) {}output += ` ],\n`;output += ` },\n`;}}output += "};\n";output += "export const Assets = new TypesafeLoader(AssetsToLoad);\n";return output;function writeAssetsFile() {fs.writeFileSync(assetFilePath, buildAssetsFile());}fs.watch(Util.Debounce(() => {writeAssetsFile();}));writeAssetsFile();assetDirectory,{ recursive: true },console.log(`[${Util.FormatDate(new Date())}] Recompiling...`);};output += "\n";output += ` "${frame}",\n`;if (frame === undefined) {continue;}output += ` "${animationName}": {\n`;output += `\n`;output += ` /* Animations */\n`;output += `\n`;output += ` "${Util.PadString(fileNameWithoutExtension,longestTruncatedFileLength,'"')}: { type: "${Util.PadString(resourceType,longestAssetType,'"')} as const, path: "${Util.PadString(file, longestFileLength, '"')} },\n`;let resourceType = "";const longestTruncatedFileLength = Util.MaxBy(allFiles, (x) =>x.lastIndexOf("."))!.lastIndexOf(".");const longestFileLength = Util.MaxBy(allFiles, (x) => x.length)!.length;output += " // No files found!";output += "}";`;${allKeys.map((key) => ` | "${key}"\n`).join("")}${allKeys.length === 0 ? " | void\n" : ""}const allKeys = normalFiles.concat(Util.FlattenByOne(Object.keys(animationBundles).map((key) => animationBundles[key])));animationBundles[prefix] = animationBundles[prefix] || [];animationBundles[prefix] = animationBundles[prefix] || [];const allFiles = allNestedFiles(assetDirectory).filter((file) =>assetExtensions.find((ext) => file.endsWith(ext)));const assetExtensions = [".png", ".gif", ".mp3", ".json"];return json.maps && json.type === "world";const json = JSON.parse(fs.readFileSync(path, "utf8"));return json.version && json.tilewidth && json.type === "map";const json = JSON.parse(fs.readFileSync(path, "utf8"));walkDir(dir, (path) => {}const dirPath = path.join(dir, f);const assetFilePath = path.join(configDirectory,config.assets.compiledAssetsFile);const config = JSON.parse(fs.readFileSync(configPath, "utf8"));import * as fs from "fs";import * as path from "path";
import { BaseGame } from "./base_game";import { AllResourcesType } from "./typesafe_loader";import { Entity, AugmentedSprite } from "./entity";import { HashSet } from "./data_structures/hash";const serializedClasses: { [key: string]: Function } = {};export function serialized(constructor: Function) {serializedClasses[constructor.name] = constructor;}type GenericJSON = {};type SerializeJSON = {entities: GenericJSON[];stage: number;fixedStage: number;parallaxStage: number;};const run: { [key: string]: boolean } = {};export const once = (fn: () => void) => {if (!run[fn.toString()]) {fn();run[fn.toString()] = true;}export class Serializer<T extends AllResourcesType> {game: BaseGame<T>;constructor(game: BaseGame<T>) {this.game = game;}getAllEntities(thing: HashSet<Entity> | Entity[]): Entity[] {let list: Entity[] = [];if (Array.isArray(thing)) {list = thing;} else {list = thing.values();}let result: Entity[] = [];for (const e of list) {}return result;}public serializeEntity(e: Entity): string {const result: { [key: string]: any } = {};for (const key in this) {const val = this[key];if (val instanceof AugmentedSprite) {result[key] = {__type: "AugmentedSprite",} else if (val instanceof Entity) {result[key] = {__type: "NestedEntity",__subtype: val.constructor.name,__id: val.id,};result[key] = (val as any).toJSON();} else {result[key] = val;}}// .map(([key]) => key)for (const [name, descriptor] of getters) {result["get:" + name] = descriptor.get!.bind(e)();}console.log(result);return JSON.stringify({__type: "Entity",__subtype: this.constructor.name,...result,});}serialize(): string {const idToEntity: { [key: string]: Entity } = {};for (const e of allEntities.values()) idToEntity[e.id] = e;const entities = this.game.state.entities.values();const e = entities[0];once(() => {console.log(allEntities);const result = JSON.stringify(entities, (key, value) => {if (value instanceof Entity) {return this.serializeEntity(value);} else if (typeof value === "number" ||typeof value === "string" ||typeof value === "boolean" ||return JSON.stringify(value);} else {console.log("Unhandled:", value);throw new Error("Unhandled type!");}});console.log(result);});return "";}}typeof value === "undefined") {const allEntities = new HashSet(this.getAllEntities(this.game.state.entities));const getters = Object.entries(Object.getOwnPropertyDescriptors(Object.getPrototypeOf(this))).filter(([key, descriptor]) => typeof descriptor.get === "function");} else if (typeof val === "object" && val !== null && "toJSON" in val) {};result = [...result, e, ...this.getAllEntities(e.children())];};[key: string]:| string| number| boolean| null| undefined| GenericJSON[]| GenericJSON;
export const SetAudioToLoop = (audio: HTMLAudioElement) => {audio.currentTime = 0;audio.play();});return audio;};audio.addEventListener("ended", () => {
import { BaseTextEntity } from "./base_text_entity";import { BaseGameState } from "./base_state";export type TextAlignType = "left" | "right" | "center";export type TextEntityStyle = {color: string;fontSize: number;align?: TextAlignType;export type TextStyles = {[key: string]: TextEntityStyle;export type TextSegment = {text: string;style: TextEntityStyle;export enum TextSegmentState {NormalText,IdText,StyledText,}if (currentState === TextSegmentState.NormalText) {return TextSegmentState.IdText;} else if (currentState === TextSegmentState.IdText) {return TextSegmentState.StyledText;} else if (currentState === TextSegmentState.StyledText) {return TextSegmentState.NormalText;}return undefined as any; // stupid typechecker/*** "%1%This is some red text% normal text %2%blue text!%".*/export class TextEntity extends BaseTextEntity<BaseGameState> {customStyles: TextStyles;defaultStyle: TextEntityStyle;public static StandardStyles: TextStyles = {1: { color: "white", fontSize: 32, align: "left" },2: { color: "cyan", fontSize: 40, align: "left" },};/*** "%1%This is some red text% normal text %2%blue text!%".*/constructor({text,styles = TextEntity.StandardStyles,width = 500,height = 300,color = "white",fontSize = 32,align = "left",super("", width, height);this.defaultStyle = { color, fontSize, align };this.customStyles = styles;this.setText(text);}setText(text: string): void {if (text === "") {this.html = "";return;}const textSegments = this.buildTextSegments(text);style="font-family: FreePixel;this.html = html;}set color(color: string) {}// TODO: This is a hard function to write properly.calculateTextWidth = (text: string) => {const canvas = document.getElementById("canvas2d")! as HTMLCanvasElement;const context = canvas.getContext("2d")!;context.font = `${this.defaultStyle.fontSize}px FreePixel`;const calculatedWidth = context.measureText(text).width;return calculatedWidth;};buildTextSegments(text: string): TextSegment[] {let i = 0;const readChar = () => text[i++];let state = TextSegmentState.NormalText;let id = "";while (i < text.length) {const ch = readChar();if (ch === "%") {if (state === TextSegmentState.NormalText) {id = "";} else if (state === TextSegmentState.IdText) {segments.push({text: "",style: this.customStyles[id],});} else if (state === TextSegmentState.StyledText) {segments.push({text: "",style: this.defaultStyle,});}state = AdvanceState(state);continue;} else {if (state === TextSegmentState.NormalText) {segments[segments.length - 1].text += ch;} else if (state === TextSegmentState.IdText) {id += ch;} else if (state === TextSegmentState.StyledText) {segments[segments.length - 1].text += ch;}}}}// public set width(value: number) {// this.sprite.width = value;// // this.buildTextGraphic();// }// public set height(value: number) {// this.sprite.width = value;// // this.buildTextGraphic();// }}return segments.filter((segment) => segment.text.trim() !== "");const segments: TextSegment[] = [{text: "",style: this.defaultStyle,},];// This only works after the CSS has loadedthis.defaultStyle = { ...this.defaultStyle, color: color };text-align: ${segment.style.align || "left"};font-size: ${segment.style.fontSize}px;">${segment.text}</span>`;}).join("").replace(/\n/g, "");color: ${segment.style.color};const html = textSegments.map((segment) => {return `<span}: {text: string;styles?: TextStyles;width?: number;height?: number;color?: string;fontSize?: number;align?: TextAlignType;}) {* Format:** Format:*};export const AdvanceState = (currentState: TextSegmentState): TextSegmentState => {};};};
import { Rectangle, Texture } from "pixi.js";// import { AssetName } from '../game/assets';import { Tile } from "./tilemap/tilemap_types";import { TypesafeLoader } from "./typesafe_loader";// import { C } from '../game/constants';export class TextureCache {static Cache: { [key: string]: Texture } = {};public static GetTextureFromSpritesheet({resourceName: textureName,x,y,tilewidth,tileheight,assets,}: {resourceName: any; // AssetName;x: number;y: number;tilewidth: number;tileheight: number;assets: TypesafeLoader<{}>;}): Texture {const key = `${textureName}-${x}-${y}`;if (!TextureCache.Cache[key]) {const texture = (assets.getResource(textureName) as Texture).clone();texture.frame = new Rectangle(x * tilewidth,y * tileheight,tilewidth,tileheight);this.Cache[key] = texture;}return this.Cache[key];}public static GetTextureForTile({assets,tile,}: {assets: TypesafeLoader<{}>;tile: Tile;}): Texture {const {tile: { imageUrlRelativeToGame, spritesheetx, spritesheety },} = tile;return TextureCache.GetTextureFromSpritesheet({// TODO: Is there any way to improve this cast?// Once I add a loader for tilemaps, probably yes!resourceName: imageUrlRelativeToGame.slice(0,imageUrlRelativeToGame.lastIndexOf(".")) as any, // as AssetNamex: spritesheetx,y: spritesheety,tilewidth: tile.tile.tilewidth,tileheight: tile.tile.tileheight,assets: assets,});}}
export interface TiledTileLayerChunkJSON {data: number[];height: number;width: number;x: number;y: number;}export interface TiledTileLayerJSON {chunks: TiledTileLayerChunkJSON[];height: number;id: number;name: string;opacity: number;startx: number;starty: number;offsetx: number;offsety: number;type: "tilelayer";visible: boolean;width: number;x: number;y: number;}export interface TiledGroupLayerJSON {id: number;layers: TiledLayerTypes[];name: string;opacity: string;type: "group";visible: boolean;x: number;y: number;}[];export interface TiledObjectJSON {gid?: number;properties?: any;propertytypes?: { [key: string]: "int" | "string" };height: number;id: number;name: string;rotation: number;type: any;visible: boolean;width: number;x: number;y: number;}export interface TiledObjectLayerJSON {draworder: "topdown" | "index";type: "objectgroup";}export interface TilesetTilesJSON {objectgroup?: TiledObjectLayerJSON;value: string;}[];}export interface TilesetJSON {imageheight: number;}| TiledTileLayerJSON| TiledObjectLayerJSONexport interface TiledJSON {height: number;nextobjectid: number;orientation: "orthogonal";renderorder: "right-down";tileheight: number;tilewidth: number;version: number;layers: TiledLayerTypes[];tilesets: TilesetJSON[];}export interface Tile {}export interface Tileset {gidStart: number;gidEnd: number;name: string;imageUrlRelativeToTilemap: string;imageUrlRelativeToGame: string;imageheight: number;}export interface TiledObject {tile: SpritesheetTile;height: number;width: number;x: number;y: number;}export interface SpritesheetTile {imageUrlRelativeToGame: string;spritesheetx: number;spritesheety: number;tilewidth: number;tileProperties: TilesetTilesJSON[] | undefined;}tileheight: number;properties?: { [key: string]: string };tilewidth: number;tileheight: number;tiles: TilesetTilesJSON[] | undefined;imagewidth: number;x: number;y: number;gid: number;tile: SpritesheetTile;isCollider: boolean;tileProperties: { [key: string]: unknown };width: number;| TiledGroupLayerJSON;export type TiledLayerTypes =tiles?: TilesetTilesJSON[];imagewidth: number;margin: number;name: string;spacing: number;tilecount: number;tileheight: number;tilewidth: number;columns: number;firstgid: number;image: string;properties?: {name: string;type: "string"; // TODO: There are probably others. And yes, the literal string "string".id: number;height: number;name: string;objects: TiledObjectJSON[];opacity: number;visible: boolean;width: number;x: number;y: number;export type TiledPropertiesType = {name: string;type: string;value: string;}
import { Entity } from "../entity";import { Rect } from "../geometry/rect";import { TiledObjectLayerJSON, Tile } from "./tilemap_types";import { TextureCache } from "../texture_cache";import { Grid } from "../data_structures/grid";import { Texture } from "pixi.js";import { TiledTilemap, MapLayer } from "./tilemap";import { TilemapRegion } from "./tilemap_data";import { TypesafeLoader } from "../typesafe_loader";export type GetInstanceTypeProps = {type TilemapCustomObjectSingle = {};type TilemapCustomObjectGroup = {};type TilemapCustomObjectRect = {layerName: string;};| TilemapCustomObjectGroup| TilemapCustomObjectSingleexport type ObjectInfo = { entity: Entity; layerName: string };export class TiledTilemapObjects {private _layers: TiledObjectLayerJSON[];private _customObjects: TilemapCustomObjects[];private _map: TiledTilemap;/*** Every custom object in the game.*/private _allObjects: ObjectInfo[] = [];private _assets: TypesafeLoader<any>;constructor(props: {customObjects: TilemapCustomObjects[];}) {const { layers, customObjects, map } = props;this._customObjects = customObjects;for (const layer of this._layers) {const objectsInLayer = this.loadLayer(layer);this._allObjects = [...this._allObjects, ...objectsInLayer];}this.turnOffAllObjects();}turnOffAllObjects() {for (const customObject of this._allObjects) {customObject.entity.stopUpdating();}}loadObjectLayers(): MapLayer[] {this.turnOffAllObjects();let result: MapLayer[] = [];for (const layer of this._layers) {result.push({objectLayer: true,});}for (const object of this._allObjects) {associatedLayer.entity.addChild(object.entity);object.entity.startUpdating();}return result;}private loadLayer(layer: TiledObjectLayerJSON): ObjectInfo[] {const results: ObjectInfo[] = [];type ObjectInGroup = {gridX: number;gridY: number;};const objectsToGroup: ObjectInGroup[] = [];// Add all single objectsif (!obj.gid) {// this is probably a region, so see if we have one of those.for (const customObject of this._customObjects) {customObject.process({rect: new Rect({properties: TiledTilemap.ParseTiledProperties(obj.properties),});continue processNextObject;}}}const { spritesheet, tileProperties } = this._map._data.gidInfo(obj.gid);const objProperties: { [key: string]: unknown } = {};tileProperties[name] = value;}const allProperties = {...tileProperties,...objProperties,};let newObj: Entity | null = null;let x = obj.x;const tile = {tileProperties: allProperties,};const tileName = allProperties.name as string;if (tileName === undefined) {throw new Error("Custom object needs a tile type");}if (obj.type === "single") {return obj.name === tileName;}if (obj.type === "group") {return obj.names.includes(tileName);}return false;});if (associatedObject === undefined) {}if (associatedObject.type === "single") {if (associatedObject.name === tileName) {newObj = associatedObject.getInstanceType(spriteTex, allProperties, {layerName: layer.name,x: tile.x,y: tile.y,});}} else if (associatedObject.type === "group") {// add to the list of grouped objects, which we will process later.if (associatedObject.names.includes(tileName)) {objectsToGroup.push({name: tileName,tile: tile,// TODO: We're making an assumption that the size of the objects// are all the same. I think this is safe tho?gridX: tile.x / obj.width,gridY: tile.y / obj.height,});}}if (newObj) {newObj.x = tile.x;newObj.y = tile.y;results.push({layerName: layer.name,});}}// Find all groups and add them// Step 1: Load all objects into gridfor (const objectToGroup of objectsToGroup) {grid.set(objectToGroup.gridX, objectToGroup.gridY, {grouped: false,});}// Step 2: BFS from each object to find all neighbors which are part of the// group.for (const obj of objectsToGroup) {const result = grid.get(obj.gridX, obj.gridY);const { grouped } = result;if (grouped) {continue;}// Step 2a: Find all names of objects in that grouplet customObject: TilemapCustomObjectGroup | null = null;for (const candidate of this._customObjects) {if (candidate.type === "group") {if (candidate.names.includes(obj.name)) {customObject = candidate;break;}}}if (customObject === null) {throw new Error("HUH!?!?");}// Step 2: Actually run BFSconst group: ObjectInGroup[] = [obj];const groupEdge: ObjectInGroup[] = [obj];while (groupEdge.length > 0) {const current = groupEdge.pop()!;const dxdy = [];for (const [dx, dy] of dxdy) {const result = grid.get(current.gridX + dx, current.gridY + dy);const { obj: neighbor, grouped } = result;if (customObject.names.includes(neighbor.name)) {group.push(neighbor);groupEdge.push(neighbor);}}}// BFS complete; `group` contains entire group.for (const obj of group) {grid.get(obj.gridX, obj.gridY)!.grouped = true;}// Find (x, y) of grouplet minTileX = Number.POSITIVE_INFINITY;let minTileY = Number.POSITIVE_INFINITY;for (const obj of group) {minTileX = Math.min(minTileX, obj.tile.x);minTileY = Math.min(minTileY, obj.tile.y);}const groupEntity = customObject.getGroupInstanceType({layerName: layer.name,});groupEntity.x = minTileX;groupEntity.y = minTileY;for (const obj of group) {const objEntity = customObject.getInstanceType(spriteTex);groupEntity.addChild(objEntity);objEntity.x = obj.tile.x - groupEntity.x;objEntity.y = obj.tile.y - groupEntity.y;}results.push({layerName: layer.name,});}return results;}getAllObjects(): ObjectInfo[] {return this._allObjects;}}entity: groupEntity,const spriteTex = TextureCache.GetTextureForTile({assets: this._assets,tile: obj.tile,});x: minTileX,y: minTileY,if (grouped) {continue;}if (group.includes(neighbor)) {continue;}if (!result) {continue;}[1, 0],[-1, 0],[0, 1],[0, -1],if (!result) {throw new Error("Wat");}obj: objectToGroup,const grid = new Grid<{ obj: ObjectInGroup; grouped: boolean }>();entity: newObj,const spriteTex = TextureCache.GetTextureForTile({assets: this._assets,tile,});throw new Error(`Unhandled tile type: ${tileName}`);const associatedObject = this._customObjects.find((obj) => {x: x,y: y,tile: spritesheet,isCollider: this._map._data._gidHasCollision[obj.gid] || false,gid: obj.gid,let y = obj.y - spritesheet.tileheight; // Tiled pivot point is (0, 1) so we need to subtract by tile height.for (const { name, value } of obj.properties || []) {throw new Error(`on layer ${layer.name} at position x: ${obj.x} and y: ${obj.y} you have a rect region that's not being processed`);x: obj.x,y: obj.y,width: obj.width,height: obj.height,}),if (customObject.type === "rect" &&customObject.layerName === layer.name) {processNextObject: for (const obj of layer.objects) {// Step 0:name: string;tile: Tile;const associatedLayer = result.find((obj) => obj.layerName === object.layerName)!;entity: new Entity({ name: layer.name }),layerName: layer.name,this._map = map;this._assets = props.assets;this._layers = layers;map: TiledTilemap;assets: TypesafeLoader<any>;layers: TiledObjectLayerJSON[];| TilemapCustomObjectRect;export type TilemapCustomObjects =process: (rect: TilemapRegion) => void;type: "rect";type: "group";names: string[];getInstanceType: (tex: Texture) => Entity;getGroupInstanceType: (props: GetInstanceTypeProps) => Entity;type: "single";name: string;getInstanceType: (tex: Texture,tileProperties: { [key: string]: unknown },props: GetInstanceTypeProps) => Entity | null;layerName: string;x: number;y: number;};
import { Grid } from "../data_structures/grid";import { Rect } from "../geometry/rect";import { RectGroup } from "../geometry/rect_group";import { Vector2 } from "../geometry/vector2";import { TiledTilemap } from "./tilemap";import { Util } from "../util";export type TilemapRegion = {properties: { [key: string]: string };| {export class TilemapData {private _tileHeight: number;// (should be private, but cant be for organization reasons)_gidHasCollision: { [id: number]: boolean } = {};const { data, pathToTilemap } = props;this._data = data;}isGidCollider(gid: number): boolean {return this._gidHasCollision[gid] || false;}getTileWidth(): number {return this._tileWidth;}getTileHeight(): number {return this._tileHeight;}getTilesets(): Tileset[] {return this._tilesets;}private loadTilesets(pathToTilemap: string, json: TiledJSON): Tileset[] {const tilesets: Tileset[] = [];tilesets.push({name,imageUrlRelativeToTilemap,imageUrlRelativeToGame,imagewidth,imageheight,tilewidth,tileheight,tiles,gidStart: firstgid,});}return tilesets;}private buildCollisionInfoForTiles(): { [key: number]: boolean } {// Build a dumb (for now) object of collision ids by just checking if the// tile literally has any collision object at all and takes that to mean the// entire thing is covered.// We could improve this if we want!const gidHasCollision: { [id: number]: boolean } = {};for (const tileset of this._data.tilesets) {if (tileset.tiles) {for (const tileAndCollisionObjects of tileset.tiles) {if (!tileAndCollisionObjects.objectgroup) {continue;}if (tileAndCollisionObjects.objectgroup.objects.length > 0) {gidHasCollision[tileAndCollisionObjects.id + tileset.firstgid] = true;}}}}return gidHasCollision;}getLayerNames(): string[] {return Object.keys(this._layers);}private getAllLayers(): (TiledTileLayerJSON | TiledObjectLayerJSON)[] {return this._getAllLayersHelper(this._data.layers);}getLayer(layerName: string) {return this._layers[layerName];}/*** Returns all layers as a flat array - most notably flattens* layer groups, which are nested.*/let result: (TiledTileLayerJSON | TiledObjectLayerJSON)[] = [];for (const layer of root) {if (layer.type === "group") {result = [...result, ...this._getAllLayersHelper(layer.layers)];} else {result.push(layer);}}return result;}getAllObjectLayers(): TiledObjectLayerJSON[] {const allLayers = this.getAllLayers();const objectLayers: TiledObjectLayerJSON[] = [];for (const layer of allLayers) {if (layer.type === "objectgroup") {objectLayers.push(layer);}}return objectLayers;}private loadTileLayers(): { [layerName: string]: TilemapLayer } {const result: { [layerName: string]: TilemapLayer } = {};const layers = this.getAllLayers();for (const layer of layers) {if (layer.type === "tilelayer") {const grid = this.loadTiles(layer);type: "tiles",grid,offset: new Vector2(layer.offsetx, layer.offsety),};} else if (layer.type === "objectgroup") {result[layer.name] = this.loadRectLayer(layer);}}return result;}loadRectLayer(layer: TiledObjectLayerJSON): TilemapLayer {const objects = layer.objects;const rects: TilemapRegion[] = [];for (const obj of objects) {if (!obj.gid) {rects.push({rect: new Rect({x: obj.x,y: obj.y,width: obj.width,height: obj.height,}),properties: TiledTilemap.ParseTiledProperties(obj.properties) || {},});}}return {rects: rects,offset: Vector2.Zero,};}private loadTiles(layer: TiledTileLayerJSON): Grid<Tile> {const result = new Grid<Tile>();const { chunks } = layer;// TODO: If the world gets very large, loading in all chunks like this might// not be the best idea - lazy loading could be better.for (const chunk of chunks) {for (let i = 0; i < chunk.data.length; i++) {const gid = chunk.data[i];const relTileY = Math.floor(i / chunk.width);if (isNaN(layer.offsetx)) layer.offsetx = 0; // TODO this is indicative of a tmx tileset embed, which we dont support yetif (isNaN(layer.offsety)) layer.offsety = 0;const offsetX = layer.offsetx / this._tileWidth;const offsetY = layer.offsety / this._tileHeight;throw new Error("AAAAAAAAAAAAAAAAAAAAAAAAA");}const absTileX = relTileX + chunk.x + offsetX;const absTileY = relTileY + chunk.y + offsetY;const { spritesheet, tileProperties } = this.gidInfo(gid);// TODO: Merge instance properties and tileset properties...result.set(absTileX, absTileY, {tileProperties: tileProperties,});}}return result;}tileProperties: { [key: string]: unknown };} {if (gid >= gidStart && gid < gidEnd) {const normalizedGid = gid - gidStart;const y = Math.floor(normalizedGid / tilesWide);const spritesheet = {imageUrlRelativeToGame,spritesheetx: x,spritesheety: y,tilewidth,tileheight,tileProperties: tiles,};let tileProperties: { [key: string]: unknown } = {};if (tiles) {if (matchedTileInfo && matchedTileInfo.properties) {for (const { name, value } of matchedTileInfo.properties) {tileProperties[name] = value;}}}return {spritesheet,tileProperties,};}}throw new Error("gid out of range. ask gabby what to do?!?");}public getTilesAtAbsolutePosition(x: number, y: number): Tile[] {return this.getLayerNames()}const tileHeight = this._tileHeight;const layer = this._layers[layerName];if (layer.type === "tiles") {return layer.grid.get(Math.floor(x / tileWidth),Math.floor(y / tileHeight));}return null;}getCollidersInRegion(region: Rect): Rect[] {}getCollidersInRegionForLayer(region: Rect, layerName: string): RectGroup {const lowX = Math.floor(region.x / this._tileWidth);const lowY = Math.floor(region.y / this._tileHeight);const highY = Math.ceil(region.bottom / this._tileHeight);let colliders: Rect[] = [];for (let x = lowX; x <= highX; x++) {for (let y = lowY; y <= highY; y++) {const tile = this.getTileAtAbsolutePositionForLayer(y * this._tileHeight,layerName);if (tile && tile.isCollider) {}}}return new RectGroup(colliders);}}colliders.push(new Rect({x: x * this._tileWidth,y: y * this._tileHeight,width: this._tileWidth,height: this._tileHeight,}));x * this._tileWidth,const highX = Math.ceil(region.right / this._tileWidth);return Util.FlattenByOne(this.getLayerNames().map((layerName) =>this.getCollidersInRegionForLayer(region, layerName).getRects()));public getTileAtAbsolutePositionForLayer(x: number,y: number,layerName: string): Tile | null {const tileWidth = this._tileWidth;.map((layerName) =>this.getTileAtAbsolutePositionForLayer(x, y, layerName)).filter((x) => x) as Tile[];const matchedTileInfo = tiles.find((tile) => gid === gidStart + tile.id);const x = normalizedGid % tilesWide;const tilesWide = imagewidth / tilewidth;for (const {gidStart,gidEnd,imageUrlRelativeToGame,imagewidth,tilewidth,tileheight,tiles,} of this._tilesets) {gidInfo(gid: number): {spritesheet: SpritesheetTile;gid: gid,x: absTileX * this._tileWidth + layer.offsetx,y: absTileY * this._tileHeight + layer.offsety,tile: spritesheet,isCollider: this.isGidCollider(gid),if (offsetX !== Math.floor(offsetX) ||offsetY !== Math.floor(offsetY)) {const relTileX = i % chunk.width;if (gid === 0) {continue;} // emptyif (gid > 200000) {throw new Error("???");} // tiled bug? (TODO: does this actually happen?)type: "rects",result[layer.name] = {private _getAllLayersHelper(root: TiledLayerTypes[]): (TiledTileLayerJSON | TiledObjectLayerJSON)[] {gidEnd: firstgid + tileCountInTileset,for (const {image: imageUrlRelativeToTilemap,name,firstgid,imageheight,imagewidth,tileheight,tilewidth,tiles,} of json.tilesets) {const tileCountInTileset =(imageheight * imagewidth) / (tileheight * tilewidth);const imageUrlRelativeToGame = new URL(pathToTilemap + "/" + imageUrlRelativeToTilemap,"http://a").href.slice("http://a".length + 1); // slice off the initial / toothis._tileWidth = this._data.tilewidth;this._tileHeight = this._data.tileheight;this._gidHasCollision = this.buildCollisionInfoForTiles();this._tilesets = this.loadTilesets(pathToTilemap, this._data);this._layers = this.loadTileLayers();constructor(props: { data: TiledJSON; pathToTilemap: string }) {private _layers: { [tilesetName: string]: TilemapLayer };private _tilesets: Tileset[];private _data: TiledJSON;private _tileWidth: number;type: "rects";rects: TilemapRegion[];offset: Vector2;};export type TilemapLayer =| {type: "tiles";grid: Grid<Tile>;offset: Vector2;}};rect: Rect;import {TiledJSON,Tileset,Tile,TiledLayerTypes,TiledTileLayerJSON,TiledObjectLayerJSON,SpritesheetTile,} from "./tilemap_types";
// import { Assets } from '../../game/assets';export type MapLayer = {layerName: string;entity: Entity;objectLayer: boolean;};// TODO: Handle the weird new file format where tilesets link to ANOTHER json fileexport class TiledTilemap {private _tileWidth: number;private _tileHeight: number;private _renderer: Renderer;private _objects: TiledTilemapObjects;private _assets: TypesafeLoader<any>;_data: TilemapData;// this is required to calculate the relative paths of the tileset images.json: TiledJSON;renderer: Renderer;pathToTilemap: string;customObjects: TilemapCustomObjects[];assets: TypesafeLoader<any>;}) {this._data = new TilemapData({ data, pathToTilemap });this._renderer = renderer;this._tileWidth = this._data.getTileWidth();this._tileHeight = this._data.getTileHeight();this._assets = assets;this._objects = new TiledTilemapObjects({layers: this._data.getAllObjectLayers(),customObjects: customObjects,map: this,assets: null as any,});}/*** Load all the regions on a specified layer.*/loadRegionLayer(layerName: string): TilemapRegion[] {const layer = this._data.getLayer(layerName);if (layer.type === "rects") {return layer.rects;}throw new Error("Not a rect layer");}private cache: { [key: string]: MapLayer[] } = {};public loadLayersInRectCached(region: Rect): MapLayer[] {// for (const k of Object.keys(this.cache)) {// const obj = this.cache[k]// for (const l of obj) {// if (l.entity.texture) {// l.entity.texture.destroy();// }// l.entity.parent?.removeChild(l.entity);// }// }// this.cache = {};const hash = region.toString();if (!this.cache[hash]) {this.cache[hash] = this.loadLayersInRect(region);}return this.cache[hash];}private loadLayersInRect(region: Rect): MapLayer[] {let tileLayers: MapLayer[] = [];// Load tile layersfor (const layerName of this._data.getLayerNames()) {const layer = this._data.getLayer(layerName);const renderTexture = RenderTexture.create({width: Math.ceil(region.width),height: Math.ceil(region.height),});const tileWidth = this._tileWidth;const tileHeight = this._tileHeight;const iStart = region.x / tileWidth;const jStart = region.y / tileHeight;if (iStart !== Math.floor(iStart) || jStart !== Math.floor(jStart)) {}for (let i = region.x / tileWidth; i < region.right / tileWidth; i++) {const tile = layer.grid.get(i, j);const sprite = new Sprite(tex);// We have to offset here because we'd be drawing outside of the// bounds of the RenderTexture otherwise.this._renderer.render(sprite, renderTexture, false);}}const layerEntity = new Entity({texture: renderTexture,name: layerName,});layerEntity.x = region.x;layerEntity.y = region.y;layerEntity.width = region.width;layerEntity.height = region.height;tileLayers.push({entity: layerEntity,layerName,objectLayer: false,});}// Load object layers// TODO: only load objects in this region - not the entire layer!!!const objectLayers = this._objects.loadObjectLayers();for (const objectLayer of objectLayers) {objectLayer.entity.zIndex = 5; // TODO}for (const tileLayer of tileLayers) {tileLayer.entity.zIndex = 0;}tileLayers = [...tileLayers, ...objectLayers];return tileLayers;}turnOffAllObjects() {this._objects.turnOffAllObjects();}getAllObjects(): ObjectInfo[] {return this._objects.getAllObjects();}const result: { [key: string]: string } = {};if (properties === undefined) {return {};}for (const obj of properties) {result[obj.name] = obj.value;}return result;}}public static ParseTiledProperties(properties: { name: string; type: string; value: string }[] | undefined): { [key: string]: string } {sprite.x = tile.x - region.x - layer.offset.x;sprite.y = tile.y - region.y - layer.offset.y;const tex = TextureCache.GetTextureForTile({assets: this._assets,tile,});if (!tile) {continue;}for (let j = region.y / tileHeight;j < region.bottom / tileHeight;j++) {throw new Error("x and y of passed in region aren't divisible by tileWidth/height");if (layer.type !== "tiles") {continue;}constructor({json: data,renderer,pathToTilemap,customObjects,assets,}: {import { TypesafeLoader } from "../typesafe_loader";import { Sprite, Renderer, RenderTexture } from "pixi.js";import { Rect } from "../geometry/rect";import { TiledJSON } from "./tilemap_types";import { TextureCache } from "../texture_cache";import { Entity } from "../entity";import {TiledTilemapObjects,TilemapCustomObjects,ObjectInfo,} from "./tilemap_objects";import { TilemapData, TilemapRegion } from "./tilemap_data";
// import { AssetsToLoad } from '../game/assets';const AssetsToLoad = {} as { never: any };type AnimationResource = {paths: string[];};type NormalResource = {type: "Image" | "Audio" | "TileMap" | "TileWorld" | "Spritesheet";path: string;};type IndividualResourceObj = AnimationResource | NormalResource;* TypeSafe loader is intended to be a wrapper around PIXI.Loader which gives a* type-checked getResource() check.*/export class TypesafeLoader<Resources extends AllResourcesType> {loader: Loader;loadComplete: boolean;loadCompleteCallbacks: (() => void)[];constructor(resourceNames: Resources) {this.loadCompleteCallbacks = [];this.loader = new Loader();this.loadComplete = false;this.startStageOneLoading(resourceNames);}// Stage 1: Load all assets in resources.tsprivate startStageOneLoading = (resources: Resources) => {for (const key of Object.keys(resources)) {const resource = resources[key];if (resource.type === "Animation") {for (const path of resource.paths) {this.loader.add(path);}} else {this.loader.add(resource.path);}}this.loader.load(this.startStageTwoLoading);// Stage 2: Load all assets required by tilemaps - mostly tilesets, I hope!.private startStageTwoLoading = () => {let allTilemapDependencyPaths: string[] = [];for (const resource of Object.keys(AssetsToLoad)) {const castedResource = resource as keyof typeof AssetsToLoad;if (AssetsToLoad[castedResource].type === "TileMap") {});allTilemapDependencyPaths = allTilemapDependencyPaths.concat();}}for (const tilemapDependencyPath of allTilemapDependencyPaths) {if (!this.loader.resources[tilemapDependencyPath]) {this.loader.add(tilemapDependencyPath);}}this.loader.load(this.finishLoading);const resource = AssetsToLoad[resourceName] as IndividualResourceObj;if (resource.type === "Audio") {return new Audio(resource.path) as any;} else if (resource.type === "Animation") {} else if (resource.type === "Image") {return this.loader.resources[resource.path].texture as any;} else if (resource.type === "Spritesheet") {throw new Error("Unhandled");} else if (resource.type === "TileMap") {return this.loader.resources[resource.path].data;} else if (resource.type === "TileWorld") {return this.loader.resources[resource.path].data;}throw new Error("AAAAAA");}private finishLoading = () => {this.loadComplete = true;for (const callback of this.loadCompleteCallbacks) {callback();}this.loadCompleteCallbacks = [];onLoadComplete(callback: () => void) {if (this.loadComplete) {setTimeout(() => {callback();}, 0);} else {this.loadCompleteCallbacks.push(callback);}}}};return resource.paths.map((path) => this.loader.resources[path].texture) as any;getResource<T extends keyof typeof AssetsToLoad>(resourceName: T): ResourceReturn<typeof AssetsToLoad[T]["type"]> {};tilemapData.getTilesets().map((tileset) => tileset.imageUrlRelativeToGame)const tilemapData = new TilemapData({data: this.getResource(castedResource) as TiledJSON,pathToTilemap,const pathToTilemap = resource.substring(0, resource.lastIndexOf("/"));};/**export type AllResourcesType = { [key: string]: IndividualResourceObj };type ResourceReturn<T extends string> = T extends "Image"? Texture: T extends "Audio"? HTMLAudioElement: T extends "TileMap"? TiledJSON: T extends "TileWorld"? object: T extends "Spritesheet"? unknown: T extends "Animation"? Texture[]: never;type: "Animation";import { TilemapData } from "./tilemap/tilemap_data";import { TiledJSON } from "./tilemap/tilemap_types";import { Loader, Texture } from "pixi.js";
let lastUsedId = 0;export const getUniqueID = () => {return lastUsedId++;};export class Util {static MinBy<T>(list: T[], fn: (T: T) => number): T | undefined {let lowestValue: number | undefined = undefined;for (const item of list) {const value = fn(item);if (lowestValue === undefined || value < lowestValue) {lowestT = item;lowestValue = value;}}return lowestT;}static MaxBy<T>(list: T[], fn: (T: T) => number): T | undefined {let highestValue: number | undefined = undefined;for (const item of list) {const value = fn(item);if (highestValue === undefined || value > highestValue) {highestT = item;highestValue = value;}}return highestT;}static RandRange(low: number, high: number): number {return Math.floor(Math.random() * (high - low) + low);}public static SortByKey<T>(array: T[], key: (x: T) => number): T[] {return array.sort((a, b) => {});}public static ReplaceAll(mapObj: { [key: string]: string }): string {});}public static Debounce<F extends (...args: any[]) => void>(func: F,waitMilliseconds = 50,options = {isImmediate: false,}): F {let timeoutId: any; // types are different on node vs client, so we have to use any.const result = (...args: any[]) => {const doLater = () => {timeoutId = undefined;if (!options.isImmediate) {func.apply(this, args);}const shouldCallNow = options.isImmediate && timeoutId === undefined;if (timeoutId !== undefined) {clearTimeout(timeoutId);}timeoutId = setTimeout(doLater, waitMilliseconds);if (shouldCallNow) {func.apply(this, args);}return result as any;}public static FormatDate(d: Date): string {const monthName = [}public static FlattenByOne<T>(arr: T[][]): T[] {for (const obj of arr) {}}return string + intersperse + character.repeat(length - string.length);}}public static PadString(string: string,length: number,intersperse = "",character = " ") {return result;result = result.concat(obj);let result: T[] = [];return `${monthName} ${d.getDate()}, ${("00" + d.getHours()).substr(-2)}:${("00" + d.getMinutes()).substr(-2)}:${("00" + d.getSeconds()).substr(-2)}`;"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",][d.getMonth()];};};return str.replace(re, (matched) => {return mapObj[matched.toLowerCase()];const re = new RegExp(Object.keys(mapObj).join("|"), "gi");str: string,return key(a) - key(b);let highestT: T | undefined = undefined;let lowestT: T | undefined = undefined;