From 336453062107f9af42199c1d9ba0020b8dc75c3f Mon Sep 17 00:00:00 2001 From: duckietm Date: Tue, 17 Feb 2026 17:30:26 +0100 Subject: [PATCH] :up: New floorplan --- .../src/common/floorplan/ActionSettings.ts | 39 ++ .../room/src/common/floorplan/Constants.ts | 44 ++ .../common/floorplan/ConvertMapToString.ts | 1 + .../src/common/floorplan/FloorplanEditor.ts | 502 ++++++++++++++++++ .../src/common/floorplan/FloorplanResource.ts | 227 ++++++++ .../common/floorplan/IFloorplanSettings.ts | 8 + .../floorplan/IVisualizationSettings.ts | 7 + packages/room/src/common/floorplan/Tile.ts | 31 ++ packages/room/src/common/floorplan/Utils.ts | 53 ++ packages/room/src/common/floorplan/index.ts | 9 + packages/room/src/common/index.ts | 1 + packages/room/src/index.ts | 1 + 12 files changed, 923 insertions(+) create mode 100644 packages/room/src/common/floorplan/ActionSettings.ts create mode 100644 packages/room/src/common/floorplan/Constants.ts create mode 100644 packages/room/src/common/floorplan/ConvertMapToString.ts create mode 100644 packages/room/src/common/floorplan/FloorplanEditor.ts create mode 100644 packages/room/src/common/floorplan/FloorplanResource.ts create mode 100644 packages/room/src/common/floorplan/IFloorplanSettings.ts create mode 100644 packages/room/src/common/floorplan/IVisualizationSettings.ts create mode 100644 packages/room/src/common/floorplan/Tile.ts create mode 100644 packages/room/src/common/floorplan/Utils.ts create mode 100644 packages/room/src/common/floorplan/index.ts create mode 100644 packages/room/src/common/index.ts diff --git a/packages/room/src/common/floorplan/ActionSettings.ts b/packages/room/src/common/floorplan/ActionSettings.ts new file mode 100644 index 0000000..8ac3825 --- /dev/null +++ b/packages/room/src/common/floorplan/ActionSettings.ts @@ -0,0 +1,39 @@ +import { FloorAction, HEIGHT_SCHEME } from './Constants'; + +export class ActionSettings +{ + private _currentAction: number; + private _currentHeight: string; + + constructor() + { + this._currentAction = FloorAction.SET; + this._currentHeight = HEIGHT_SCHEME[1]; + } + + public get currentAction(): number + { + return this._currentAction; + } + + public set currentAction(value: number) + { + this._currentAction = value; + } + + public get currentHeight(): string + { + return this._currentHeight; + } + + public set currentHeight(value: string) + { + this._currentHeight = value; + } + + public clear(): void + { + this._currentAction = FloorAction.SET; + this._currentHeight = HEIGHT_SCHEME[1]; + } +} diff --git a/packages/room/src/common/floorplan/Constants.ts b/packages/room/src/common/floorplan/Constants.ts new file mode 100644 index 0000000..da66d51 --- /dev/null +++ b/packages/room/src/common/floorplan/Constants.ts @@ -0,0 +1,44 @@ +export const TILE_SIZE = 32; +export const MAX_NUM_TILE_PER_AXIS = 64; + +export const HEIGHT_SCHEME: string = 'x0123456789abcdefghijklmnopq'; + +export class FloorAction +{ + public static readonly DOOR = 0; + public static readonly UP = 1; + public static readonly DOWN = 2; + public static readonly SET = 3; + public static readonly UNSET = 4; +} + +export const COLORMAP: object = { + 'x': '101010', + '0': '0065ff', + '1': '0091ff', + '2': '00bcff', + '3': '00e8ff', + '4': '00ffea', + '5': '00ffbf', + '6': '00ff93', + '7': '00ff68', + '8': '00ff3d', + '9': '19ff00', + 'a': '44ff00', + 'b': '70ff00', + 'c': '9bff00', + 'd': 'f2ff00', + 'e': 'ffe000', + 'f': 'ffb500', + 'g': 'ff8900', + 'h': 'ff5e00', + 'i': 'ff3200', + 'j': 'ff0700', + 'k': 'ff0023', + 'l': 'ff007a', + 'm': 'ff00a5', + 'n': 'ff00d1', + 'o': 'ff00fc', + 'p': 'd600ff', + 'q': 'aa00ff' +}; diff --git a/packages/room/src/common/floorplan/ConvertMapToString.ts b/packages/room/src/common/floorplan/ConvertMapToString.ts new file mode 100644 index 0000000..1579158 --- /dev/null +++ b/packages/room/src/common/floorplan/ConvertMapToString.ts @@ -0,0 +1 @@ +export const ConvertTileMapToString = (map: string) => map.replace(/\r\n|\r|\n/g, '\n').toLowerCase(); diff --git a/packages/room/src/common/floorplan/FloorplanEditor.ts b/packages/room/src/common/floorplan/FloorplanEditor.ts new file mode 100644 index 0000000..e0a61bb --- /dev/null +++ b/packages/room/src/common/floorplan/FloorplanEditor.ts @@ -0,0 +1,502 @@ +import { ActionSettings } from './ActionSettings'; +import { FloorAction, HEIGHT_SCHEME, MAX_NUM_TILE_PER_AXIS, TILE_SIZE } from './Constants'; +import { imageBase64, spritesheet } from './FloorplanResource'; +import { Tile } from './Tile'; +import { getScreenPositionForTile } from './Utils'; + +export class FloorplanEditor +{ + private static _INSTANCE: FloorplanEditor = null; + + public static readonly TILE_BLOCKED = 'r_blocked'; + public static readonly TILE_DOOR = 'r_door'; + + private _tilemap: Tile[][]; + private _width: number; + private _height: number; + private _isPointerDown: boolean; + private _doorLocation: { x: number, y: number }; + private _lastUsedTile: { x: number, y: number }; + private _isSquareSelectMode: boolean; + private _squareSelectStart: { x: number, y: number }; + private _squareSelectEnd: { x: number, y: number }; + private _renderer: CanvasRenderingContext2D; + private _actionSettings: ActionSettings; + + private _image: HTMLImageElement; + + constructor() + { + const width = TILE_SIZE * MAX_NUM_TILE_PER_AXIS + 20; + const height = (TILE_SIZE * MAX_NUM_TILE_PER_AXIS) / 2 + 100; + + const canvas = document.createElement('canvas'); + + canvas.height = height; + canvas.width = width; + + canvas.style.touchAction = 'none'; + + this._renderer = canvas.getContext('2d'); + + this._image = new Image(); + + this._image.src = imageBase64; + + this._tilemap = []; + this._doorLocation = { x: 0, y: 0 }; + this._width = 0; + this._height = 0; + this._isPointerDown = false; + this._lastUsedTile = { x: -1, y: -1 }; + this._isSquareSelectMode = false; + this._squareSelectStart = null; + this._squareSelectEnd = null; + this._actionSettings = new ActionSettings(); + } + + public onPointerRelease(event: PointerEvent = null): void + { + if(this._isSquareSelectMode && this._isPointerDown && this._squareSelectStart) + { + if(event) + { + const tile = this.getTileAtPosition({ x: event.offsetX, y: event.offsetY }); + + if(tile) this._squareSelectEnd = tile; + } + + if(this._squareSelectEnd) this.fillSquareSelection(); + } + + this._isPointerDown = false; + this._squareSelectStart = null; + this._squareSelectEnd = null; + } + + public onPointerDown(event: PointerEvent): void + { + if(event.button === 2) return; + + const location = { x: event.offsetX, y: event.offsetY }; + + this._isPointerDown = true; + + if(this._isSquareSelectMode) + { + const tile = this.getTileAtPosition(location); + + this._squareSelectStart = tile; + this._squareSelectEnd = tile; + + return; + } + + this.tileHitDetection(location, true); + } + + public onPointerMove(event: PointerEvent): void + { + if(!this._isPointerDown) return; + + const location = { x: event.offsetX, y: event.offsetY }; + + if(this._isSquareSelectMode) + { + const tile = this.getTileAtPosition(location); + + if(tile) this._squareSelectEnd = tile; + + return; + } + + this.tileHitDetection(location, false); + } + + private getTileAtPosition(tempPoint: { x: number, y: number }): { x: number, y: number } + { + const mousePositionX = Math.floor(tempPoint.x); + const mousePositionY = Math.floor(tempPoint.y); + + const width = TILE_SIZE; + const height = TILE_SIZE / 2; + + for(let y = 0; y < this._tilemap.length; y++) + { + for(let x = 0; x < this.tilemap[y].length; x++) + { + const [ tileStartX, tileStartY ] = getScreenPositionForTile(x, y); + + const centreX = tileStartX + (width / 2); + const centreY = tileStartY + (height / 2); + + const dx = Math.abs(mousePositionX - centreX); + const dy = Math.abs(mousePositionY - centreY); + + const solution = (dx / (width * 0.5) + dy / (height * 0.5) <= 1); + + if(solution) return { x, y }; + } + } + + return null; + } + + private fillSquareSelection(): void + { + const startX = Math.min(this._squareSelectStart.x, this._squareSelectEnd.x); + const endX = Math.max(this._squareSelectStart.x, this._squareSelectEnd.x); + const startY = Math.min(this._squareSelectStart.y, this._squareSelectEnd.y); + const endY = Math.max(this._squareSelectStart.y, this._squareSelectEnd.y); + + for(let y = startY; y <= endY; y++) + { + for(let x = startX; x <= endX; x++) + { + this.onClick(x, y, false); + } + } + + this.renderTiles(); + } + + private tileHitDetection(tempPoint: { x: number, y: number }, isClick: boolean = false): boolean + { + const mousePositionX = Math.floor(tempPoint.x); + const mousePositionY = Math.floor(tempPoint.y); + + const width = TILE_SIZE; + const height = TILE_SIZE / 2; + + for(let y = 0; y < this._tilemap.length; y++) + { + for(let x = 0; x < this.tilemap[y].length; x++) + { + const [ tileStartX, tileStartY ] = getScreenPositionForTile(x, y); + + const centreX = tileStartX + (width / 2); + const centreY = tileStartY + (height / 2); + + const dx = Math.abs(mousePositionX - centreX); + const dy = Math.abs(mousePositionY - centreY); + + const solution = (dx / (width * 0.5) + dy / (height * 0.5) <= 1);//todo: improve this + + if(solution) + { + if(this._isPointerDown) + { + if(isClick) + { + this.onClick(x, y); + } + + else if(this._lastUsedTile.x !== x || this._lastUsedTile.y !== y) + { + this._lastUsedTile.x = x; + this._lastUsedTile.y = y; + this.onClick(x, y); + } + + } + return true; + } + } + } + return false; + } + + private onClick(x: number, y: number, render: boolean = true): void + { + const tile = (this._tilemap[y] && this._tilemap[y][x]); + + if(!tile) return; + + const heightIndex = HEIGHT_SCHEME.indexOf(tile.height); + + let futureHeightIndex = 0; + + switch(this._actionSettings.currentAction) + { + case FloorAction.DOOR: + + if(tile.height !== 'x') + { + this._doorLocation.x = x; + this._doorLocation.y = y; + if(render) this.renderTiles(); + } + return; + case FloorAction.UP: + if(tile.height === 'x') return; + futureHeightIndex = heightIndex + 1; + break; + case FloorAction.DOWN: + if(tile.height === 'x' || (heightIndex <= 1)) return; + futureHeightIndex = heightIndex - 1; + break; + case FloorAction.SET: + futureHeightIndex = HEIGHT_SCHEME.indexOf(this._actionSettings.currentHeight); + break; + case FloorAction.UNSET: + futureHeightIndex = 0; + break; + } + + if(futureHeightIndex === -1) return; + + if(heightIndex === futureHeightIndex) return; + + if(futureHeightIndex > 0) + { + if((x + 1) > this._width) this._width = x + 1; + + if((y + 1) > this._height) this._height = y + 1; + } + + const newHeight = HEIGHT_SCHEME[futureHeightIndex]; + + if(!newHeight) return; + + if(tile.isBlocked) return; + + this._tilemap[y][x].height = newHeight; + + if(render) this.renderTiles(); + } + + public renderTiles(): void + { + this.clearCanvas(); + + for(let y = 0; y < this._tilemap.length; y++) + { + for(let x = 0; x < this.tilemap[y].length; x++) + { + const tile = this.tilemap[y][x]; + let assetName = tile.height; + + if(this._doorLocation.x === x && this._doorLocation.y === y) + assetName = FloorplanEditor.TILE_DOOR; + + if(tile.isBlocked) assetName = FloorplanEditor.TILE_BLOCKED; + + //if((tile.height === 'x') || tile.height === 'X') continue; + const [ positionX, positionY ] = getScreenPositionForTile(x, y); + + const asset = spritesheet.frames[assetName]; + + this.renderer.drawImage(this._image, asset.frame.x, asset.frame.y, asset.frame.w, asset.frame.h, positionX, positionY, asset.frame.w, asset.frame.h); + } + } + } + + public setTilemap(map: string, blockedTiles: boolean[][]): void + { + this._tilemap = []; + const roomMapStringSplit = map.split('\r'); + + let width = 0; + let height = roomMapStringSplit.length; + + // find the map width, height + for(let y = 0; y < height; y++) + { + const originalRow = roomMapStringSplit[y]; + + if(originalRow.length === 0) + { + roomMapStringSplit.splice(y, 1); + height = roomMapStringSplit.length; + y--; + continue; + } + + if(originalRow.length > width) + { + width = originalRow.length; + } + } + // fill map with room heightmap tiles + for(let y = 0; y < height; y++) + { + this._tilemap[y] = []; + const rowString = roomMapStringSplit[y]; + + for(let x = 0; x < width; x++) + { + const blocked = (blockedTiles[y] && blockedTiles[y][x]) || false; + + const char = rowString[x]; + if(((!(char === 'x')) && (!(char === 'X')) && char)) + { + this._tilemap[y][x] = new Tile(char, blocked); + } + else + { + this._tilemap[y][x] = new Tile('x', blocked); + } + } + + for(let x = width; x < MAX_NUM_TILE_PER_AXIS; x++) + { + this.tilemap[y][x] = new Tile('x', false); + } + } + + // fill remaining map with empty tiles + for(let y = height; y < MAX_NUM_TILE_PER_AXIS; y++) + { + if(!this.tilemap[y]) this.tilemap[y] = []; + for(let x = 0; x < MAX_NUM_TILE_PER_AXIS; x++) + { + this.tilemap[y][x] = new Tile('x', false); + } + } + + this._width = width; + this._height = height; + } + + public getCurrentTilemapString(): string + { + const highestTile = this._tilemap[this._height - 1][this._width - 1]; + + if(highestTile.height === 'x') + { + this._width = -1; + this._height = -1; + + for(let y = MAX_NUM_TILE_PER_AXIS - 1; y >= 0; y--) + { + if(!this._tilemap[y]) continue; + + for(let x = MAX_NUM_TILE_PER_AXIS - 1; x >= 0; x--) + { + if(!this._tilemap[y][x]) continue; + + const tile = this._tilemap[y][x]; + + if(tile.height !== 'x') + { + if((x + 1) > this._width) + this._width = x + 1; + + if((y + 1) > this._height) + this._height = y + 1; + } + } + } + } + + + const rows = []; + + for(let y = 0; y < this._height; y++) + { + const row = []; + + for(let x = 0; x < this._width; x++) + { + const tile = this._tilemap[y][x]; + + row[x] = tile.height; + } + + rows[y] = row.join(''); + } + + return rows.join('\r'); + } + + public clear(): void + { + this._tilemap = []; + this._doorLocation = { x: -1, y: -1 }; + this._width = 0; + this._height = 0; + this._isPointerDown = false; + this._lastUsedTile = { x: -1, y: -1 }; + this._actionSettings.clear(); + this._isSquareSelectMode = false; + this._squareSelectStart = null; + this._squareSelectEnd = null; + this.clearCanvas(); + } + + + public toggleSelectAll(): void + { + const shouldUnset = (this._actionSettings.currentAction === FloorAction.UNSET); + + for(let y = 0; y < this._tilemap.length; y++) + { + for(let x = 0; x < this.tilemap[y].length; x++) + { + const tile = this._tilemap[y][x]; + + if(!tile || tile.isBlocked) continue; + + tile.height = shouldUnset ? 'x' : this._actionSettings.currentHeight; + } + } + + this.renderTiles(); + } + + public toggleSquareSelectMode(): boolean + { + this._isSquareSelectMode = !this._isSquareSelectMode; + + this._squareSelectStart = null; + this._squareSelectEnd = null; + + return this._isSquareSelectMode; + } + + public get isSquareSelectMode(): boolean + { + return this._isSquareSelectMode; + } + + public clearCanvas(): void + { + this.renderer.fillStyle = '0x000000'; + this.renderer.fillRect(0, 0, this._renderer.canvas.width, this._renderer.canvas.height); + } + + public get renderer(): CanvasRenderingContext2D + { + return this._renderer; + } + + public get tilemap(): Tile[][] + { + return this._tilemap; + } + + public get doorLocation(): { x: number, y: number } + { + return this._doorLocation; + } + + public set doorLocation(value: { x: number, y: number }) + { + this._doorLocation = value; + } + + public get actionSettings(): ActionSettings + { + return this._actionSettings; + } + + public static get instance(): FloorplanEditor + { + if(!FloorplanEditor._INSTANCE) + { + FloorplanEditor._INSTANCE = new FloorplanEditor(); + } + + return FloorplanEditor._INSTANCE; + } +} diff --git a/packages/room/src/common/floorplan/FloorplanResource.ts b/packages/room/src/common/floorplan/FloorplanResource.ts new file mode 100644 index 0000000..8faf30b --- /dev/null +++ b/packages/room/src/common/floorplan/FloorplanResource.ts @@ -0,0 +1,227 @@ +export const imageBase64 = + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGwAAAC+CAMAAADnThrbAAAAYFBMVEUAAAAiIiIAZf8A6P8A/5MZ/wCb/wD/tQD/MgD/AHr/APxDXocAkf8A/+oA/2hE/wDy/wD/iQD/BwD/AKXWAP////8AvP8A/78A/z1w/wD/4AD/XgD/ACP/ANGqAP8QEBBSz3qJAAAAAXRSTlMAQObYZgAAAAlwSFlzAAALEwAACxMBAJqcGAAABJxJREFUeNrt1tuOszoMBeDpD7QFBloKDGf6/m+51UTj1PUhN1uWOup3vSQLkSzn6+vV4fAV9X9lLIcdDv/+xWKHw/d3PPPzE8k8Rj1osceoBz3z4yiZ31HauN9R2rjfUcq451HSuOdR0rjnUcK411HcuNdR3LjXUcw4btTrOG7U6zhu1Ms4adTzOGnU8zhpVBh30Eb5cQ/fKp/5UR1+51l9mfU/Mz6NxvfMuEGMu9G49f/y8vzLz4Ikif/8qopnhiF6FhNHP9aVo2cGR71lCZAvbAXkzADE/kgQvooqhM8MCNuMCUFLtiJoZiBI5ycsvD4qFs4MLLTNElFYjJUoZAYRPAsSlV/5lcpnBhU8C6y+zPqfGZ9G43tm3CDG3Wjc+p9N/Z7PgjSN//y6jmfGMXoWU0c/1rWjZ0ZHvWUpkC9sDeTMCMT+SBG+imqEz4wI24wpQUu2JmhmJEjnpyy8PmoWzowstM1SUViMtShkRhE8C1KVX/m1ymdGFTwLrL7M+p8Zn0bje2bcIMbdaNz6n039ns+CLIv//Mslnpmm6FnMHP1YXxw9MznqLcuAfGEvQM5MQOyPDOGr6ILwmQlhmzEjaMleCJqZCNL5GQuvjwsLZyYW2maZKCzGiyhkJhE8CzKVX/kXlc9MKngWWH2Z9T8zPo3G98y4QYy70bj1P5v6PZ8Fx2P851+v8cw8R8/i0dGP9dXRM7Oj3rIjkC/sFciZGYj9cUT4KroifGZG2GY8ErRkrwTNzATp/CMLr48rC2dmFtpmR1FYjFdRyMwieBYcVX7lX1U+M6vgWWD1Zdb/zPg0Gt8z4wYx7kbj1v9s6vd8FpxO8Z/fNPHMskTP4snRj3Xj6JnFUW/ZCcgXtgFyZgFif5wQvooahM8sCNuMJ4KWbEPQzEKQzj+x8PpoWDizsNA2O4nCYmxEIbOI4FlwUvmV36h8ZlHBs8Dqy6z/mfFpNL5nxg1i3I3Grf/Z1O/5LDif4z//dotn1jV6Fs+Ofqxvjp5ZHfWWnYF8YW9AzqxA7I8zwlfRDeEzK8I245mgJXsjaGYlSOefWXh93Fg4s7LQNjuLwmK8iUJmFcGz4KzyK/+m8plVBc8Cqy+z/mfGp9H4nhk3iHE3Grf+Z1O/57Mgz+M/v23jmW2LnsXc0Y916+iZzVFvWQ7kC9sCObMBsT9yhK+iFuEzG8I2Y07Qkm0JmtkI0vk5C6+PloUzGwtts1wUFmMrCplNBM+CXOVXfqvymU0FzwKrL7P+Z8an0fieGTeIcTcat/5nU7/ns6Ao4j+/6+KZfY+excLRj3Xn6JndUW9ZAeQL2wE5swOxPwqEr6IO4TM7wjZjQdCS7Qia2QnS+QULr4+OhTM7C22zQhQWYycKmV0Ez4JC5Vd+p/KZXQXPAqsvs/5nxqfR+J4ZN4hxNxq3/mdTv+ezoCzjP7/v45n7PXoWS0c/1r2jZ+6OestKIF/YHsiZOxD7o0T4KuoRPnNH2GYsCVqyPUEzd4J0fsnC66Nn4cydhbZZKQqLsReFzF0Ez4JS5Vd+r/KZuwqeBVZfZv3PjE+j8T0zbhDjbjRu/b+0PP8DZwi9QurvbfwAAAAASUVORK5CYII='; + +export const spritesheet = { + frames: { + '0': { + frame: { x: 1, y: 1, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + '1': { + frame: { x: 37, y: 1, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + '2': { + frame: { x: 73, y: 1, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + '3': { + frame: { x: 1, y: 20, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + '4': { + frame: { x: 37, y: 20, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + '5': { + frame: { x: 73, y: 20, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + '6': { + frame: { x: 1, y: 39, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + '7': { + frame: { x: 37, y: 39, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + '8': { + frame: { x: 73, y: 39, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + '9': { + frame: { x: 1, y: 58, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + 'a': { + frame: { x: 37, y: 58, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + 'b': { + frame: { x: 73, y: 58, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + 'c': { + frame: { x: 1, y: 77, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + 'd': { + frame: { x: 37, y: 77, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + 'e': { + frame: { x: 73, y: 77, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + 'f': { + frame: { x: 1, y: 96, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + 'g': { + frame: { x: 37, y: 96, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + 'h': { + frame: { x: 73, y: 96, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + 'i': { + frame: { x: 1, y: 115, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + 'j': { + frame: { x: 37, y: 115, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + 'k': { + frame: { x: 73, y: 115, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + 'l': { + frame: { x: 1, y: 134, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + 'm': { + frame: { x: 37, y: 134, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + 'n': { + frame: { x: 73, y: 134, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + 'o': { + frame: { x: 1, y: 153, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + 'p': { + frame: { x: 37, y: 153, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + 'q': { + frame: { x: 73, y: 153, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + 'r_blocked': { + frame: { x: 1, y: 172, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + 'r_door': { + frame: { x: 37, y: 172, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + 'x': { + frame: { x: 73, y: 172, w: 34, h: 17 }, + rotated: false, + trimmed: false, + spriteSourceSize: { x: 0, y: 0, w: 34, h: 17 }, + sourceSize: { w: 34, h: 17 }, + }, + }, + meta: { + app: 'https://www.codeandweb.com/texturepacker', + version: '1.0', + image: 'tiles.png', + format: 'RGBA8888', + size: { w: 108, h: 190 }, + scale: '1', + smartupdate: + '$TexturePacker:SmartUpdate:6d0f8373580629749f786a0b0f6c6bb9:96dff9df69bdc6938cf02f254bbe028b:accbe1e7e294ded8391337fc1c446319$', + }, +}; diff --git a/packages/room/src/common/floorplan/IFloorplanSettings.ts b/packages/room/src/common/floorplan/IFloorplanSettings.ts new file mode 100644 index 0000000..5159527 --- /dev/null +++ b/packages/room/src/common/floorplan/IFloorplanSettings.ts @@ -0,0 +1,8 @@ +import { IVisualizationSettings } from './IVisualizationSettings'; + +export interface IFloorplanSettings extends IVisualizationSettings +{ + tilemap: string; + reservedTiles: boolean[][]; + entryPoint: [ number, number ]; +} diff --git a/packages/room/src/common/floorplan/IVisualizationSettings.ts b/packages/room/src/common/floorplan/IVisualizationSettings.ts new file mode 100644 index 0000000..97d271c --- /dev/null +++ b/packages/room/src/common/floorplan/IVisualizationSettings.ts @@ -0,0 +1,7 @@ +export interface IVisualizationSettings +{ + entryPointDir: number; + wallHeight: number; + thicknessWall: number; + thicknessFloor: number; +} diff --git a/packages/room/src/common/floorplan/Tile.ts b/packages/room/src/common/floorplan/Tile.ts new file mode 100644 index 0000000..fd9c059 --- /dev/null +++ b/packages/room/src/common/floorplan/Tile.ts @@ -0,0 +1,31 @@ +export class Tile +{ + private _height: string; + private _isBlocked: boolean; + + constructor(height: string, isBlocked: boolean) + { + this._height = height; + this._isBlocked = isBlocked; + } + + public get height(): string + { + return this._height; + } + + public set height(height: string) + { + this._height = height; + } + + public get isBlocked(): boolean + { + return this._isBlocked; + } + + public set isBlocked(val: boolean) + { + this._isBlocked = val; + } +} diff --git a/packages/room/src/common/floorplan/Utils.ts b/packages/room/src/common/floorplan/Utils.ts new file mode 100644 index 0000000..841b193 --- /dev/null +++ b/packages/room/src/common/floorplan/Utils.ts @@ -0,0 +1,53 @@ +import { TILE_SIZE } from './Constants'; + +export const getScreenPositionForTile = (x: number, y: number): [number , number] => +{ + let positionX = (x * TILE_SIZE / 2) - (y * TILE_SIZE / 2); + const positionY = (x * TILE_SIZE / 4) + (y * TILE_SIZE / 4); + + positionX = positionX + 1024; // center the map in the canvas + + return [ positionX, positionY ]; +} + +export const getTileFromScreenPosition = (x: number, y: number): [number, number] => +{ + const translatedX = x - 1024; // after centering translation + + const realX = ((translatedX /(TILE_SIZE / 2)) + (y / (TILE_SIZE / 4))) / 2; + const realY = ((y /(TILE_SIZE / 4)) - (translatedX / (TILE_SIZE / 2))) / 2; + + return [ realX, realY ]; +} + +export const convertNumbersForSaving = (value: number): number => +{ + value = parseInt(value.toString()); + switch(value) + { + case 0: + return -2; + case 1: + return -1; + case 3: + return 1; + default: + return 0; + + } +} + +export const convertSettingToNumber = (value: number): number => +{ + switch(value) + { + case 0.25: + return 0; + case 0.5: + return 1; + case 2: + return 3; + default: + return 2; + } +} diff --git a/packages/room/src/common/floorplan/index.ts b/packages/room/src/common/floorplan/index.ts new file mode 100644 index 0000000..873d2c8 --- /dev/null +++ b/packages/room/src/common/floorplan/index.ts @@ -0,0 +1,9 @@ +export * from './ActionSettings'; +export * from './Constants'; +export * from './ConvertMapToString'; +export * from './FloorplanEditor'; +export * from './FloorplanResource'; +export * from './IFloorplanSettings'; +export * from './IVisualizationSettings'; +export * from './Tile'; +export * from './Utils'; diff --git a/packages/room/src/common/index.ts b/packages/room/src/common/index.ts new file mode 100644 index 0000000..ebc0b88 --- /dev/null +++ b/packages/room/src/common/index.ts @@ -0,0 +1 @@ +export * from './floorplan'; diff --git a/packages/room/src/index.ts b/packages/room/src/index.ts index 0764754..2982146 100644 --- a/packages/room/src/index.ts +++ b/packages/room/src/index.ts @@ -1,3 +1,4 @@ +export * from './common'; export * from './GetRoomContentLoader'; export * from './GetRoomEngine'; export * from './GetRoomManager';