From a340aabe1456906e8a4bcda4f2ada859c08dc951 Mon Sep 17 00:00:00 2001 From: cramerL Date: Fri, 19 Jul 2024 19:42:44 +0200 Subject: [PATCH 01/30] updated install script to use `npm ci` to avoid issues with dependencies --- install_all.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install_all.sh b/install_all.sh index cf718cd1..df02d47b 100644 --- a/install_all.sh +++ b/install_all.sh @@ -1,9 +1,9 @@ #!/bin/bash cd common -npm install +npm ci npm run build cd ../backend -npm install +npm ci cd ../webapp -npm install \ No newline at end of file +npm ci From 0dc1c92148634124cc61a9c9f10bed1d57546568 Mon Sep 17 00:00:00 2001 From: cramerL Date: Fri, 19 Jul 2024 20:03:45 +0200 Subject: [PATCH 02/30] show selected tile in "Tile selector" when copying tiles on the map (only adjacent tiles) --- .../tile-selector/tile-selector.scene.ts | 42 +++++++++++++++---- .../services/phaser/tilemap/tile-drawer.ts | 15 +++---- 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.scene.ts b/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.scene.ts index 4589a4c2..6761986b 100644 --- a/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.scene.ts +++ b/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.scene.ts @@ -15,7 +15,7 @@ export class TileSelectorScene extends Phaser.Scene { private tileMap?: Phaser.Tilemaps.Tilemap; private selecting = false; private rect?: Phaser.GameObjects.Rectangle; - private sub?: Subscription; + private subs: Subscription[] = []; private tilesetRendered = false; @@ -38,11 +38,35 @@ export class TileSelectorScene extends Phaser.Scene { e.preventDefault(); }; - this.sub = Globals.mapLoaderService.selectedLayer.subscribe(layer => { + this.subs.push(Globals.mapLoaderService.selectedLayer.subscribe(layer => { if (layer) { this.drawTileset(layer); } - }); + })); + + this.subs.push(Globals.phaserEventsService.changeSelectedTiles.subscribe(tiles => { + this.drawRect(0, 0); + if (tiles.length === 0) { + return; + } + const baseTile = tiles[0]; + + let width = 0; + let height = 0; + + for (const tile of tiles) { + const id = tile.id - tile.offset.x - tile.offset.y * this.tilesetSize.x; + if (baseTile.id !== id) { + return; + } + width = Math.max(width, tile.offset.x); + height = Math.max(height, tile.offset.y); + } + + const start = Helper.indexToPoint(baseTile.id, this.tilesetSize.x); + this.drawRect(width + 1, height + 1, start.x, start.y); + + })); const pan = new MapPan(this, 'mapPan'); this.add.existing(pan); @@ -98,7 +122,7 @@ export class TileSelectorScene extends Phaser.Scene { // cancel current selection when out of bounds if (!this.rightClickStart || !this.rightClickEnd) { - this.drawRect(1, 1); + this.drawRect(0, 0); return; } @@ -129,8 +153,6 @@ export class TileSelectorScene extends Phaser.Scene { }); }); - this.drawRect(width, height, smaller.x, smaller.y); - this.rightClickStart = undefined; this.rightClickEnd = undefined; @@ -138,9 +160,10 @@ export class TileSelectorScene extends Phaser.Scene { } destroy() { - if (this.sub) { - this.sub.unsubscribe(); + for (const sub of this.subs) { + sub.unsubscribe(); } + this.subs = []; this.keyBindings.forEach(binding => { this.input.removeListener(binding.event, binding.fun); }); @@ -235,6 +258,9 @@ export class TileSelectorScene extends Phaser.Scene { if (!this.tilesetRendered) { return; } + if (width === 0 || height === 0) { + return; + } this.rect = this.add.rectangle(x * Globals.TILE_SIZE, y * Globals.TILE_SIZE, width * Globals.TILE_SIZE, height * Globals.TILE_SIZE); this.rect.setOrigin(0, 0); if (Globals.settingsService.getSettings().selectionBoxDark) { diff --git a/webapp/src/app/services/phaser/tilemap/tile-drawer.ts b/webapp/src/app/services/phaser/tilemap/tile-drawer.ts index b11dee12..e99d8580 100644 --- a/webapp/src/app/services/phaser/tilemap/tile-drawer.ts +++ b/webapp/src/app/services/phaser/tilemap/tile-drawer.ts @@ -402,17 +402,12 @@ export class TileDrawer extends BaseObject { const tilesWithin = phaserLayer.getTilesWithin(smaller.x, smaller.y, width, height); - tilesWithin.forEach((tile: Phaser.Tilemaps.Tile) => { - this.selectedTiles.push({ - id: tile.index, - offset: Vec2.sub(tile, smaller, true) - }); - }); - - this.renderPreview(); - - this.drawRect(width, height); + const tiles: SelectedTile[] = tilesWithin.map(tile => ({ + id: tile.index, + offset: Vec2.sub(tile, smaller, true) + })); + Globals.phaserEventsService.changeSelectedTiles.next(tiles); this.rightClickStart = undefined; this.rightClickEnd = undefined; From 1fbfe1186035ff67a90e256adcbc17bea878208e Mon Sep 17 00:00:00 2001 From: cramerL Date: Fri, 19 Jul 2024 23:12:32 +0200 Subject: [PATCH 03/30] refactored tile drawer and tile-selector to remove duplicates --- .../tile-selector/tile-selector.scene.ts | 194 ++--------- .../src/app/services/phaser/BaseTileDrawer.ts | 308 ++++++++++++++++++ .../services/phaser/tilemap/cc-map-layer.ts | 20 ++ .../services/phaser/tilemap/tile-drawer.ts | 278 +--------------- 4 files changed, 369 insertions(+), 431 deletions(-) create mode 100644 webapp/src/app/services/phaser/BaseTileDrawer.ts diff --git a/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.scene.ts b/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.scene.ts index 6761986b..06ed619f 100644 --- a/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.scene.ts +++ b/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.scene.ts @@ -1,61 +1,57 @@ import * as Phaser from 'phaser'; import { Subscription } from 'rxjs'; - -import { Point } from '../../../../models/cross-code-map'; -import { SelectedTile } from '../../../../models/tile-selector'; import { Globals } from '../../../../services/globals'; import { Helper } from '../../../../services/phaser/helper'; import { MapPan } from '../../../../services/phaser/map-pan'; import { CCMapLayer } from '../../../../services/phaser/tilemap/cc-map-layer'; -import { Vec2 } from '../../../../services/phaser/vec2'; import { customPutTilesAt } from '../../../../services/phaser/tilemap/layer-helper'; +import { BaseTileDrawer } from '../../../../services/phaser/BaseTileDrawer'; export class TileSelectorScene extends Phaser.Scene { private tileMap?: Phaser.Tilemaps.Tilemap; - private selecting = false; - private rect?: Phaser.GameObjects.Rectangle; private subs: Subscription[] = []; - private tilesetRendered = false; - - // TODO: copypaste - same is in tileDrawer, move somewhere else - private selectedTiles: SelectedTile[] = []; - private rightClickStart?: Point; - private rightClickEnd?: Point; + private baseDrawer!: BaseTileDrawer; + private tilesetWidth = 0; - private keyBindings: { event: string, fun: Function }[] = []; - private tilesetSize: Point = {x: 0, y: 0}; + private tilemapLayer?: CCMapLayer; constructor() { super({key: 'main'}); } create() { + + this.baseDrawer = new BaseTileDrawer(this, true); + this.baseDrawer.resetSelectedTiles(); + this.add.existing(this.baseDrawer); + this.cameras.main.setBackgroundColor('#616161'); this.game.canvas.oncontextmenu = function (e) { e.preventDefault(); }; - this.subs.push(Globals.mapLoaderService.selectedLayer.subscribe(layer => { - if (layer) { - this.drawTileset(layer); - } + this.subs.push(Globals.mapLoaderService.selectedLayer.subscribe(async layer => { + await this.drawTileset(layer); })); this.subs.push(Globals.phaserEventsService.changeSelectedTiles.subscribe(tiles => { - this.drawRect(0, 0); + this.baseDrawer.drawRect(0, 0); if (tiles.length === 0) { return; } const baseTile = tiles[0]; + if (baseTile.id === 0) { + return; + } let width = 0; let height = 0; for (const tile of tiles) { - const id = tile.id - tile.offset.x - tile.offset.y * this.tilesetSize.x; + const id = tile.id - tile.offset.x - tile.offset.y * this.tilesetWidth; if (baseTile.id !== id) { return; } @@ -63,9 +59,8 @@ export class TileSelectorScene extends Phaser.Scene { height = Math.max(height, tile.offset.y); } - const start = Helper.indexToPoint(baseTile.id, this.tilesetSize.x); - this.drawRect(width + 1, height + 1, start.x, start.y); - + const start = Helper.indexToPoint(baseTile.id, this.tilesetWidth); + this.baseDrawer.drawRect(width + 1, height + 1, start.x * Globals.TILE_SIZE, start.y * Globals.TILE_SIZE); })); const pan = new MapPan(this, 'mapPan'); @@ -73,40 +68,6 @@ export class TileSelectorScene extends Phaser.Scene { this.tileMap = this.add.tilemap(undefined, Globals.TILE_SIZE, Globals.TILE_SIZE); - this.keyBindings = []; - const pointerDown = (pointer: Phaser.Input.Pointer) => { - if (pointer.rightButtonDown() || pointer.leftButtonDown()) { - this.onMouseDown(); - } - }; - this.keyBindings.push({event: 'pointerdown', fun: pointerDown}); - - const pointerUp = (pointer: Phaser.Input.Pointer) => { - if (pointer.rightButtonReleased() || pointer.leftButtonReleased()) { - this.onMouseUp(); - } - }; - this.keyBindings.push({event: 'pointerup', fun: pointerUp}); - this.keyBindings.push({event: 'pointerupoutside', fun: pointerUp}); - - this.keyBindings.forEach(binding => { - this.input.addListener(binding.event, binding.fun); - }); - } - - private onMouseDown() { - if (!this.tilesetRendered) { - return; - } - - // only start tile copy when cursor in bounds - const pointer = this.input.activePointer; - const p = Helper.worldToTile(pointer.worldX, pointer.worldY); - if (!Helper.isInBoundsP(this.tilesetSize, p)) { - return; - } - - this.rightClickStart = p; } public resize() { @@ -114,103 +75,15 @@ export class TileSelectorScene extends Phaser.Scene { this.game.scale.resize(size.width, size.height); } - private onMouseUp() { - if (!this.tilesetRendered) { - return; - } - this.selectedTiles = []; - - // cancel current selection when out of bounds - if (!this.rightClickStart || !this.rightClickEnd) { - this.drawRect(0, 0); - return; - } - - // select tiles - const start = this.rightClickStart; - const end = this.rightClickEnd; - - const smaller = { - x: Math.min(start.x, end.x), - y: Math.min(start.y, end.y) - }; - - const bigger = { - x: Math.max(start.x, end.x), - y: Math.max(start.y, end.y) - }; - - const width = bigger.x - smaller.x + 1; - const height = bigger.y - smaller.y + 1; - - - const tilesWithin = this.tileMap!.getTilesWithin(smaller.x, smaller.y, width, height) ?? []; - - tilesWithin.forEach((tile: Phaser.Tilemaps.Tile) => { - this.selectedTiles.push({ - id: tile.index, - offset: Vec2.sub(tile, smaller, true) - }); - }); - - this.rightClickStart = undefined; - this.rightClickEnd = undefined; - - Globals.phaserEventsService.changeSelectedTiles.next(this.selectedTiles); - } - destroy() { for (const sub of this.subs) { sub.unsubscribe(); } this.subs = []; - this.keyBindings.forEach(binding => { - this.input.removeListener(binding.event, binding.fun); - }); - this.keyBindings = []; } - - override update(time: number, delta: number): void { - const pointer = this.input.activePointer; - const p = Helper.worldToTile(pointer.worldX, pointer.worldY); - - // render selection border - if (this.rightClickStart) { - p.x = Helper.clamp(p.x, 0, this.tilesetSize.x - 1); - p.y = Helper.clamp(p.y, 0, this.tilesetSize.y - 1); - - if (this.rightClickEnd && this.rightClickEnd.x === p.x && this.rightClickEnd.y === p.y) { - // shortcut to avoid redrawing rectangle every frame - return; - } - - this.rightClickEnd = p; - const diff = Vec2.sub(p, this.rightClickStart, true); - const start = {x: this.rightClickStart.x, y: this.rightClickStart.y}; - if (diff.x >= 0) { - diff.x++; - } else { - start.x += 1; - diff.x--; - } - if (diff.y >= 0) { - diff.y++; - } else { - start.y += 1; - diff.y--; - } - - this.drawRect(diff.x, diff.y, start.x, start.y); - return; - } - } - - private async drawTileset(selectedLayer: CCMapLayer) { - this.tilesetRendered = false; - this.drawRect(0, 0); - - if (!selectedLayer.details.tilesetName) { + private async drawTileset(selectedLayer?: CCMapLayer) { + if (!selectedLayer?.details.tilesetName) { if (this.tileMap) { this.tileMap.removeAllLayers(); } @@ -227,7 +100,7 @@ export class TileSelectorScene extends Phaser.Scene { } const tilesetSize = Helper.getTilesetSize(this, selectedLayer.details.tilesetName); - this.tilesetSize = tilesetSize; + this.tilesetWidth = tilesetSize.x; this.tileMap.removeAllLayers(); const tileset = this.tileMap.addTilesetImage('tileset', selectedLayer.details.tilesetName, Globals.TILE_SIZE, Globals.TILE_SIZE); if (!tileset) { @@ -248,25 +121,10 @@ export class TileSelectorScene extends Phaser.Scene { customPutTilesAt(data, layer); - this.tilesetRendered = true; - } - - private drawRect(width: number, height: number, x = 0, y = 0) { - if (this.rect) { - this.rect.destroy(); - } - if (!this.tilesetRendered) { - return; - } - if (width === 0 || height === 0) { - return; - } - this.rect = this.add.rectangle(x * Globals.TILE_SIZE, y * Globals.TILE_SIZE, width * Globals.TILE_SIZE, height * Globals.TILE_SIZE); - this.rect.setOrigin(0, 0); - if (Globals.settingsService.getSettings().selectionBoxDark) { - this.rect.setStrokeStyle(2, 0x333333, 0.9); - } else { - this.rect.setStrokeStyle(2, 0xffffff, 0.6); - } + this.tilemapLayer?.destroy(); + this.tilemapLayer = new CCMapLayer(this.tileMap); + await this.tilemapLayer.initFromPhaser(layer); + + await this.baseDrawer.setLayer(this.tilemapLayer); } } diff --git a/webapp/src/app/services/phaser/BaseTileDrawer.ts b/webapp/src/app/services/phaser/BaseTileDrawer.ts new file mode 100644 index 00000000..6ca5725d --- /dev/null +++ b/webapp/src/app/services/phaser/BaseTileDrawer.ts @@ -0,0 +1,308 @@ +import { SelectedTile } from '../../models/tile-selector'; +import { Point } from '../../models/cross-code-map'; +import { Helper } from './helper'; +import { BaseObject } from './base-object'; +import * as Phaser from 'phaser'; +import { CCMapLayer } from './tilemap/cc-map-layer'; +import { Globals } from '../globals'; +import { Vec2 } from './vec2'; +import { customPutTileAt } from './tilemap/layer-helper'; + +export class BaseTileDrawer extends BaseObject { + private selectedTiles: SelectedTile[] = []; + private rightPointerDown = false; + private rightClickStart?: Point; + private rightClickEnd?: Point; + + private selection?: Phaser.GameObjects.Container; + + private previewTileMap!: Phaser.Tilemaps.Tilemap; + private previewLayer?: Phaser.Tilemaps.TilemapLayer; + + private layer?: CCMapLayer; + + /** + * @param scene + * @param leftClick + * @param container container is used to follow mouse movements and render preview + */ + constructor( + scene: Phaser.Scene, + private leftClick?: boolean, + private container?: Phaser.GameObjects.Container + ) { + super(scene, 'baseTileDrawer'); + } + + protected override init(): void { + this.previewTileMap = this.scene.add.tilemap(undefined, Globals.TILE_SIZE, Globals.TILE_SIZE); + } + + preUpdate(): void { + if (!this.layer) { + return; + } + const pointer = this.scene.input.activePointer; + const p = Helper.worldToTile(pointer.worldX - this.layer.x, pointer.worldY - this.layer.y); + + // render selection border + if (this.rightClickStart) { + Helper.clampToBounds(this.layer, p); + + if (this.rightClickEnd && this.rightClickEnd.x === p.x && this.rightClickEnd.y === p.y) { + // shortcut to avoid redrawing rectangle every frame + return; + } + + this.rightClickEnd = p; + const diff = Vec2.sub(p, this.rightClickStart, true); + const start = {x: 0, y: 0}; + if (diff.x >= 0) { + diff.x++; + } else { + start.x = 1; + diff.x--; + } + if (diff.y >= 0) { + diff.y++; + } else { + start.y = 1; + diff.y--; + } + + if (!this.container) { + Vec2.add(start, this.rightClickStart); + } + this.drawRect(diff.x, diff.y, start.x * Globals.TILE_SIZE, start.y * Globals.TILE_SIZE, true); + return; + } + + + // position tile drawer border to cursor + if (this.container) { + const container = this.container; + container.x = pointer.worldX; + container.y = pointer.worldY; + + if (container.x < this.layer.x) { + container.x -= Globals.TILE_SIZE; + } + if (container.y < this.layer.y) { + container.y -= Globals.TILE_SIZE; + } + + container.x -= (container.x - this.layer.x) % Globals.TILE_SIZE; + container.y -= (container.y - this.layer.y) % Globals.TILE_SIZE; + + if (this.previewLayer) { + Vec2.assign(this.previewLayer, container); + } + } + } + + protected override activate(): void { + this.selection?.setVisible(true); + this.previewLayer?.setVisible(true); + const pointerDown = (pointer: Phaser.Input.Pointer) => { + if (pointer.rightButtonDown() || this.leftClick && pointer.leftButtonDown()) { + this.onMouseRightDown(); + } + }; + + const pointerUp = (pointer: Phaser.Input.Pointer) => { + if ((pointer.rightButtonReleased() || this.leftClick && pointer.leftButtonReleased()) && this.rightPointerDown) { + this.onMouseRightUp(); + } + }; + this.addKeybinding({event: 'pointerdown', fun: pointerDown, emitter: this.scene.input}); + this.addKeybinding({event: 'pointerup', fun: pointerUp, emitter: this.scene.input}); + this.addKeybinding({event: 'pointerupoutside', fun: pointerUp, emitter: this.scene.input}); + + this.addSubscription(Globals.phaserEventsService.changeSelectedTiles.subscribe(tiles => this.updateSelectedTiles(tiles))); + } + + protected override deactivate(): void { + this.selection?.setVisible(false); + this.previewLayer?.setVisible(false); + } + + public async setLayer(layer?: CCMapLayer) { + this.layer = layer; + if (!layer) { + return; + } + + const exists = await Helper.loadTexture(layer.details.tilesetName, this.scene); + if (!exists) { + return; + } + + const tileset = this.previewTileMap.addTilesetImage('only', layer.details.tilesetName); + if (tileset) { + tileset.firstgid = 1; + } + + } + + public resetSelectedTiles() { + Globals.phaserEventsService.changeSelectedTiles.next([{id: 0, offset: {x: 0, y: 0}}]); + } + + + private onMouseRightDown() { + this.rightPointerDown = true; + if (!this.layer) { + return; + } + + // only start tile copy when cursor in bounds + const pointer = this.scene.input.activePointer; + const p = Helper.worldToTile(pointer.worldX - this.layer.x, pointer.worldY - this.layer.y); + if (!Helper.isInBounds(this.layer, p)) { + return; + } + + this.resetSelectedTiles(); + this.rightClickStart = p; + } + + private onMouseRightUp() { + this.rightPointerDown = false; + if (!this.layer) { + return; + } + + // cancel current selection when out of bounds + const phaserLayer = this.layer.getPhaserLayer(); + if (!this.rightClickStart || !this.rightClickEnd || !phaserLayer) { + this.resetSelectedTiles(); + return; + } + + // select tiles + const start = this.rightClickStart; + const end = this.rightClickEnd; + + const smaller = { + x: Math.min(start.x, end.x), + y: Math.min(start.y, end.y) + }; + + const bigger = { + x: Math.max(start.x, end.x), + y: Math.max(start.y, end.y) + }; + + const width = bigger.x - smaller.x + 1; + const height = bigger.y - smaller.y + 1; + + const tilesWithin = phaserLayer.getTilesWithin(smaller.x, smaller.y, width, height); + + const tiles: SelectedTile[] = tilesWithin.map(tile => ({ + id: tile.index, + offset: Vec2.sub(tile, smaller, true) + })); + Globals.phaserEventsService.changeSelectedTiles.next(tiles); + + this.rightClickStart = undefined; + this.rightClickEnd = undefined; + } + + private updateSelectedTiles(selected: SelectedTile[]) { + this.selectedTiles = selected; + this.renderPreview(); + + let x = 0; + let y = 0; + selected.forEach(tile => { + const o = tile.offset; + if (o.x > x) { + x = o.x; + } + if (o.y > y) { + y = o.y; + } + }); + + if (this.container) { + this.drawRect(x + 1, y + 1, 0, 0); + return; + } + } + + public drawRect(width: number, height: number, x = 0, y = 0, renderSize = false) { + if (this.selection) { + this.selection.destroy(); + } + + let textColor = 'rgba(0,0,0,0.6)'; + let backgroundColor = 0xffffff; + if (Globals.settingsService.getSettings().selectionBoxDark) { + textColor = 'rgba(255,255,255,0.9)'; + backgroundColor = 0x333333; + } + + this.selection = this.scene.add.container(x, y); + + const rect = this.scene.add.rectangle(0, 0, width * Globals.TILE_SIZE, height * Globals.TILE_SIZE); + rect.setOrigin(0, 0); + rect.setStrokeStyle(1, backgroundColor, this.container ? 0.6 : 0.9); + + this.selection.add(rect); + this.container?.add(this.selection); + + if (!renderSize) { + Globals.globalEventsService.updateTileSelectionSize.next(undefined); + return; + } + + const makeText = (pos: Point, val: number) => { + const text = this.scene.add.text(pos.x, pos.y, Math.abs(val) + '', { + font: '400 10px Roboto', + color: textColor, + resolution: window.devicePixelRatio * 3, + }); + text.setOrigin(0.5, 0); + const background = this.scene.add.rectangle(pos.x, pos.y + 2, 14, 10, backgroundColor, 0.6); + background.setOrigin(0.5, 0); + + this.selection?.add(background); + this.selection?.add(text); + }; + + if (Math.abs(width) >= 3) { + makeText({ + x: width * Globals.TILE_SIZE / 2, + y: (height > 0 ? 0 : height * Globals.TILE_SIZE) - 1 + }, width); + } + + if (Math.abs(height) >= 3) { + makeText({ + x: Globals.TILE_SIZE / 2 + (width > 0 ? 0 : width * Globals.TILE_SIZE), + y: (height - 1) * Globals.TILE_SIZE / 2, + }, height); + } + + Globals.globalEventsService.updateTileSelectionSize.next({ + x: Math.abs(width), + y: Math.abs(height) + }); + } + + private renderPreview() { + if (!this.container) { + return; + } + this.previewTileMap.removeAllLayers(); + const layer = this.previewTileMap.createBlankLayer('layer', 'only', 0, 0, 40, 40)!; + + this.selectedTiles.forEach(tile => { + customPutTileAt(tile.id, tile.offset.x, tile.offset.y, layer.layer); + }); + + this.previewLayer = layer; + this.previewLayer.depth = this.container.depth - 1; + this.previewLayer.alpha = 0.6; + } +} diff --git a/webapp/src/app/services/phaser/tilemap/cc-map-layer.ts b/webapp/src/app/services/phaser/tilemap/cc-map-layer.ts index ba3dcea3..fee138ae 100644 --- a/webapp/src/app/services/phaser/tilemap/cc-map-layer.ts +++ b/webapp/src/app/services/phaser/tilemap/cc-map-layer.ts @@ -47,6 +47,26 @@ export class CCMapLayer { }); } + public async initFromPhaser(layer: Phaser.Tilemaps.TilemapLayer) { + const details: MapLayer = { + type: 'Background', + name: 'fromPhaser', + level: 0, + width: layer.width, + height: layer.height, + visible: 1, + tilesetName: layer.tileset[0]?.image?.key ?? '', + repeat: false, + distance: 0, + tilesize: Globals.TILE_SIZE, + moveSpeed: {x: 0, y: 0}, + data: [], + }; + this.layer = layer; + this.extractLayerData(details); + await this.init(details); + } + get visible(): boolean { return this.layer.visible; } diff --git a/webapp/src/app/services/phaser/tilemap/tile-drawer.ts b/webapp/src/app/services/phaser/tilemap/tile-drawer.ts index e99d8580..f53e5b60 100644 --- a/webapp/src/app/services/phaser/tilemap/tile-drawer.ts +++ b/webapp/src/app/services/phaser/tilemap/tile-drawer.ts @@ -9,22 +9,16 @@ import { CCMapLayer } from './cc-map-layer'; import { Filler } from './fill'; import { pointsInLine } from './points-in-line'; import { customPutTileAt } from './layer-helper'; +import { BaseTileDrawer } from '../BaseTileDrawer'; export class TileDrawer extends BaseObject { - private layer?: CCMapLayer; private selectedTiles: SelectedTile[] = []; + private layer?: CCMapLayer; - private selection?: Phaser.GameObjects.Container; - - private previewTileMap!: Phaser.Tilemaps.Tilemap; - private previewLayer?: Phaser.Tilemaps.TilemapLayer; + private baseDrawer!: BaseTileDrawer; - private rightClickStart?: Point; - private rightClickEnd?: Point; private renderLayersTransparent = false; - private rightPointerDown = false; - private container!: Phaser.GameObjects.Container; @@ -40,7 +34,9 @@ export class TileDrawer extends BaseObject { super(scene, 'tileDrawer'); } - protected init() { + + protected override init() { + this.fillKey = this.scene.input.keyboard!.addKey(Phaser.Input.Keyboard.KeyCodes.F, false); this.transparentKey = this.scene.input.keyboard!.addKey(Phaser.Input.Keyboard.KeyCodes.R, false); this.visibilityKey = this.scene.input.keyboard!.addKey(Phaser.Input.Keyboard.KeyCodes.V, false); @@ -49,15 +45,14 @@ export class TileDrawer extends BaseObject { this.container = this.scene.add.container(0, 0); this.container.depth = 10000; - this.drawRect(1, 1); - - this.resetSelectedTiles(); - - this.previewTileMap = this.scene.add.tilemap(undefined, Globals.TILE_SIZE, Globals.TILE_SIZE); + this.baseDrawer = new BaseTileDrawer(this.scene, false, this.container); + this.baseDrawer.resetSelectedTiles(); + this.scene.add.existing(this.baseDrawer); } - private selectLayer(selectedLayer?: CCMapLayer) { + private async selectLayer(selectedLayer?: CCMapLayer) { this.layer = selectedLayer; + await this.baseDrawer.setLayer(selectedLayer); this.setLayerAlpha(); @@ -66,32 +61,8 @@ export class TileDrawer extends BaseObject { return; } this.container.visible = true; - const tileset = this.previewTileMap.addTilesetImage('only', selectedLayer.details.tilesetName); - if (tileset) { - tileset.firstgid = 1; - } - } - - private updateSelectedTiles(selected: SelectedTile[]) { - this.selectedTiles = selected; - this.renderPreview(); - - let x = 0; - let y = 0; - selected.forEach(tile => { - const o = tile.offset; - if (o.x > x) { - x = o.x; - } - if (o.y > y) { - y = o.y; - } - }); - - this.drawRect(x + 1, y + 1, 0, 0); } - preUpdate(): void { // hide cursor when no map loaded if (!this.layer) { @@ -101,54 +72,6 @@ export class TileDrawer extends BaseObject { const pointer = this.scene.input.activePointer; const p = Helper.worldToTile(pointer.worldX - this.layer.x, pointer.worldY - this.layer.y); - // render selection border - if (this.rightClickStart) { - Helper.clampToBounds(this.layer, p); - - if (this.rightClickEnd && this.rightClickEnd.x === p.x && this.rightClickEnd.y === p.y) { - // shortcut to avoid redrawing rectangle every frame - return; - } - - this.rightClickEnd = p; - const diff = Vec2.sub(p, this.rightClickStart, true); - const start = {x: 0, y: 0}; - if (diff.x >= 0) { - diff.x++; - } else { - start.x = Globals.TILE_SIZE; - diff.x--; - } - if (diff.y >= 0) { - diff.y++; - } else { - start.y = Globals.TILE_SIZE; - diff.y--; - } - - this.drawRect(diff.x, diff.y, start.x, start.y, true); - return; - } - - // position tile drawer border to cursor - const container = this.container; - container.x = pointer.worldX; - container.y = pointer.worldY; - - if (container.x < this.layer.x) { - container.x -= Globals.TILE_SIZE; - } - if (container.y < this.layer.y) { - container.y -= Globals.TILE_SIZE; - } - - container.x -= (container.x - this.layer.x) % Globals.TILE_SIZE; - container.y -= (container.y - this.layer.y) % Globals.TILE_SIZE; - - if (this.previewLayer) { - Vec2.assign(this.previewLayer, container); - } - // draw tiles // trigger only when mouse is over canvas element (the renderer), avoids triggering when interacting with ui if (pointer.leftButtonDown() && pointer.downElement?.nodeName === 'CANVAS' && this.layer) { @@ -197,36 +120,13 @@ export class TileDrawer extends BaseObject { protected deactivate() { this.container.visible = false; - if (this.previewLayer) { - this.previewLayer.visible = false; - } + this.baseDrawer.setActive(false); } protected activate() { - if (this.previewLayer) { - this.previewLayer.visible = true; - } - const sub = Globals.mapLoaderService.selectedLayer.subscribe(layer => this.selectLayer(layer)); - this.addSubscription(sub); - - const sub2 = Globals.phaserEventsService.changeSelectedTiles.subscribe(tiles => this.updateSelectedTiles(tiles)); - this.addSubscription(sub2); - - const pointerDown = (pointer: Phaser.Input.Pointer) => { - if (pointer.rightButtonDown()) { - this.onMouseRightDown(); - } - }; - this.addKeybinding({event: 'pointerdown', fun: pointerDown, emitter: this.scene.input}); - - const pointerUp = (pointer: Phaser.Input.Pointer) => { - if (pointer.rightButtonReleased() && this.rightPointerDown) { - this.onMouseRightUp(); - } - }; - this.addKeybinding({event: 'pointerup', fun: pointerUp, emitter: this.scene.input}); - this.addKeybinding({event: 'pointerupoutside', fun: pointerUp, emitter: this.scene.input}); - + this.addSubscription(Globals.mapLoaderService.selectedLayer.subscribe(layer => this.selectLayer(layer))); + this.addSubscription(Globals.phaserEventsService.changeSelectedTiles.subscribe(tiles => this.selectedTiles = tiles)); + this.baseDrawer.setActive(true); const fill = () => { if (!Helper.isInputFocused()) { @@ -288,154 +188,6 @@ export class TileDrawer extends BaseObject { } } - private onMouseRightDown() { - this.rightPointerDown = true; - if (!this.layer) { - return; - } - - - // only start tile copy when cursor in bounds - const pointer = this.scene.input.activePointer; - const p = Helper.worldToTile(pointer.worldX - this.layer.x, pointer.worldY - this.layer.y); - if (!Helper.isInBounds(this.layer, p)) { - return; - } - - this.resetSelectedTiles(); - this.renderPreview(); - this.rightClickStart = p; - } - - private drawRect(width: number, height: number, x = 0, y = 0, renderSize = false) { - if (this.selection) { - this.selection.destroy(); - } - - let textColor = 'rgba(0,0,0,0.6)'; - let backgroundColor = 0xffffff; - if (Globals.settingsService.getSettings().selectionBoxDark) { - textColor = 'rgba(255,255,255,0.9)'; - backgroundColor = 0x333333; - } - - this.selection = this.scene.add.container(x, y); - - const rect = this.scene.add.rectangle(0, 0, width * Globals.TILE_SIZE, height * Globals.TILE_SIZE); - rect.setOrigin(0, 0); - rect.setStrokeStyle(1, backgroundColor, 0.6); - - this.selection.add(rect); - this.container.add(this.selection); - - if (!renderSize) { - Globals.globalEventsService.updateTileSelectionSize.next(undefined); - return; - } - - const makeText = (pos: Point, val: number) => { - const text = this.scene.add.text(pos.x, pos.y, Math.abs(val) + '', { - font: '400 10px Roboto', - color: textColor, - resolution: window.devicePixelRatio * 3, - }); - text.setOrigin(0.5, 0); - const background = this.scene.add.rectangle(pos.x, pos.y + 2, 14, 10, backgroundColor, 0.6); - background.setOrigin(0.5, 0); - - this.selection?.add(background); - this.selection?.add(text); - }; - - if (Math.abs(width) >= 3) { - makeText({ - x: width * Globals.TILE_SIZE / 2, - y: (height > 0 ? 0 : height * Globals.TILE_SIZE) - 1 - }, width); - } - - if (Math.abs(height) >= 3) { - makeText({ - x: Globals.TILE_SIZE / 2 + (width > 0 ? 0 : width * Globals.TILE_SIZE), - y: (height - 1) * Globals.TILE_SIZE / 2, - }, height); - } - - Globals.globalEventsService.updateTileSelectionSize.next({ - x: Math.abs(width), - y: Math.abs(height) - }); - } - - private onMouseRightUp() { - this.rightPointerDown = false; - if (!this.layer) { - return; - } - this.selectedTiles = []; - - // cancel current selection when out of bounds - const phaserLayer = this.layer.getPhaserLayer(); - if (!this.rightClickStart || !this.rightClickEnd || !phaserLayer) { - this.drawRect(1, 1); - this.resetSelectedTiles(); - this.renderPreview(); - return; - } - - // select tiles - const start = this.rightClickStart; - const end = this.rightClickEnd; - - const smaller = { - x: Math.min(start.x, end.x), - y: Math.min(start.y, end.y) - }; - - const bigger = { - x: Math.max(start.x, end.x), - y: Math.max(start.y, end.y) - }; - - const width = bigger.x - smaller.x + 1; - const height = bigger.y - smaller.y + 1; - - const tilesWithin = phaserLayer.getTilesWithin(smaller.x, smaller.y, width, height); - - const tiles: SelectedTile[] = tilesWithin.map(tile => ({ - id: tile.index, - offset: Vec2.sub(tile, smaller, true) - })); - - Globals.phaserEventsService.changeSelectedTiles.next(tiles); - - this.rightClickStart = undefined; - this.rightClickEnd = undefined; - } - - private renderPreview() { - - // reset last draw when selected tiles change - this.lastDraw.x = -1; - this.previewTileMap.removeAllLayers(); - const layer = this.previewTileMap.createBlankLayer('layer', 'only', 0, 0, 40, 40)!; - - this.selectedTiles.forEach(tile => { - customPutTileAt(tile.id, tile.offset.x, tile.offset.y, layer.layer); - }); - - this.previewLayer = layer; - this.previewLayer.depth = this.container.depth - 1; - this.previewLayer.alpha = 0.6; - - // TODO: phaser bug fix, see https://github.com/photonstorm/phaser/issues/4642 - this.previewTileMap.layers = [this.previewLayer.layer]; - } - - private resetSelectedTiles() { - this.selectedTiles = [{id: 0, offset: {x: 0, y: 0}}]; - } - private fill() { if (!this.layer) { return; From b75ccf898acf8e01b849017796b95900bb6ca2a1 Mon Sep 17 00:00:00 2001 From: cramerL Date: Fri, 19 Jul 2024 23:58:59 +0200 Subject: [PATCH 04/30] improved performance when rendering tile selector --- .../tile-selector/tile-selector.scene.ts | 33 +++++++++------ .../services/phaser/tilemap/cc-map-layer.ts | 41 ++++++++----------- 2 files changed, 38 insertions(+), 36 deletions(-) diff --git a/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.scene.ts b/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.scene.ts index 06ed619f..b35f4273 100644 --- a/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.scene.ts +++ b/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.scene.ts @@ -9,19 +9,19 @@ import { BaseTileDrawer } from '../../../../services/phaser/BaseTileDrawer'; export class TileSelectorScene extends Phaser.Scene { - private tileMap?: Phaser.Tilemaps.Tilemap; + private tileMap!: Phaser.Tilemaps.Tilemap; private subs: Subscription[] = []; private baseDrawer!: BaseTileDrawer; private tilesetWidth = 0; - private tilemapLayer?: CCMapLayer; + private tilemapLayer!: CCMapLayer; constructor() { super({key: 'main'}); } - create() { + async create() { this.baseDrawer = new BaseTileDrawer(this, true); this.baseDrawer.resetSelectedTiles(); @@ -67,7 +67,22 @@ export class TileSelectorScene extends Phaser.Scene { this.add.existing(pan); this.tileMap = this.add.tilemap(undefined, Globals.TILE_SIZE, Globals.TILE_SIZE); + this.tilemapLayer = new CCMapLayer(this.tileMap); + await this.tilemapLayer.init({ + type: 'Background', + name: 'fromPhaser', + level: 0, + width: 1, + height: 1, + visible: 1, + tilesetName: '', + repeat: false, + distance: 0, + tilesize: Globals.TILE_SIZE, + moveSpeed: {x: 0, y: 0}, + data: [] + }); } public resize() { @@ -90,10 +105,6 @@ export class TileSelectorScene extends Phaser.Scene { return; } - if (!this.tileMap) { - return; - } - const exists = await Helper.loadTexture(selectedLayer.details.tilesetName, this); if (!exists) { return; @@ -102,7 +113,7 @@ export class TileSelectorScene extends Phaser.Scene { const tilesetSize = Helper.getTilesetSize(this, selectedLayer.details.tilesetName); this.tilesetWidth = tilesetSize.x; this.tileMap.removeAllLayers(); - const tileset = this.tileMap.addTilesetImage('tileset', selectedLayer.details.tilesetName, Globals.TILE_SIZE, Globals.TILE_SIZE); + const tileset = this.tileMap.addTilesetImage(selectedLayer.details.tilesetName); if (!tileset) { return; } @@ -120,11 +131,7 @@ export class TileSelectorScene extends Phaser.Scene { } customPutTilesAt(data, layer); - - this.tilemapLayer?.destroy(); - this.tilemapLayer = new CCMapLayer(this.tileMap); - await this.tilemapLayer.initFromPhaser(layer); - + this.tilemapLayer.setPhaserLayer(layer); await this.baseDrawer.setLayer(this.tilemapLayer); } } diff --git a/webapp/src/app/services/phaser/tilemap/cc-map-layer.ts b/webapp/src/app/services/phaser/tilemap/cc-map-layer.ts index fee138ae..1b885e6b 100644 --- a/webapp/src/app/services/phaser/tilemap/cc-map-layer.ts +++ b/webapp/src/app/services/phaser/tilemap/cc-map-layer.ts @@ -33,7 +33,7 @@ export class CCMapLayer { this.container.depth = 999; this.makeLayer('stub'); this.updateBorder(); - if (details.data) { + if (details.data && details.data.length > 0) { customPutTilesAt(details.data, this.layer); } await this.updateTileset(details.tilesetName!); @@ -47,26 +47,6 @@ export class CCMapLayer { }); } - public async initFromPhaser(layer: Phaser.Tilemaps.TilemapLayer) { - const details: MapLayer = { - type: 'Background', - name: 'fromPhaser', - level: 0, - width: layer.width, - height: layer.height, - visible: 1, - tilesetName: layer.tileset[0]?.image?.key ?? '', - repeat: false, - distance: 0, - tilesize: Globals.TILE_SIZE, - moveSpeed: {x: 0, y: 0}, - data: [], - }; - this.layer = layer; - this.extractLayerData(details); - await this.init(details); - } - get visible(): boolean { return this.layer.visible; } @@ -264,11 +244,26 @@ export class CCMapLayer { } private extractLayerData(layer: MapLayer): void { - this.layer.getTilesWithin().forEach(tile => { + for (const tile of this.layer.getTilesWithin()) { if (!layer.data[tile.y]) { layer.data[tile.y] = []; } layer.data[tile.y][tile.x] = tile.index; - }); + } + } + + setPhaserLayer(layer: Phaser.Tilemaps.TilemapLayer) { + + const oldLayer = this.layer as typeof this.layer | undefined; + + this.layer = layer; + this.layer.alpha = oldLayer?.alpha ?? 1; + this.details.width = this.layer.width / Globals.TILE_SIZE; + this.details.height = this.layer.height / Globals.TILE_SIZE; + this.setOffset(this.container.x, this.container.y); + this.updateLevel(); + if (oldLayer) { + oldLayer.destroy(true); + } } } From 8f84b6906c76bdb5cd5576310218a4ba56773cfb Mon Sep 17 00:00:00 2001 From: cramerL Date: Sat, 20 Jul 2024 01:18:09 +0200 Subject: [PATCH 05/30] refactored autotile naming and added 4x4 type --- .../services/autotile/autotile.constants.ts | 111 +++++++++++------- .../app/services/autotile/autotile.service.ts | 67 +++++++---- .../src/app/services/autotile/gfx-mapper.ts | 35 +++--- webapp/src/app/services/phaser/helper.ts | 4 + 4 files changed, 138 insertions(+), 79 deletions(-) diff --git a/webapp/src/app/services/autotile/autotile.constants.ts b/webapp/src/app/services/autotile/autotile.constants.ts index 0753f94f..94e8af64 100644 --- a/webapp/src/app/services/autotile/autotile.constants.ts +++ b/webapp/src/app/services/autotile/autotile.constants.ts @@ -1,13 +1,8 @@ import { Point } from '../../models/cross-code-map'; +import { Helper } from '../phaser/helper'; -/** value is width, height is always 2 */ -export enum AutotileType { - SMALL = 4, - DEFAULT = 8, - LARGE = 10, - SUPER_LARGE = 12, - MEGA_LARGE = 14 -} +/** Known autotile sizes */ +export type AutotileType = '4x1' | '4x4' | '8x2' | '10x2' | '12x2' | '14x2'; export interface AutotileConfig { tileCountX: number; @@ -23,6 +18,12 @@ export interface AutotileConfig { * X means tile, O means border. * * E.g. XXXX is the filled tile, OXXO is the left border + * + * XX OX + * XX OX + * + * + * 4x4 uses a different layout: top, right, bottom, left. */ export interface FillType { XXXX: Point[]; @@ -43,10 +44,6 @@ export interface FillType { XOOO: Point[]; } -export const FILL_TYPE: { - [key in AutotileType]: FillType -} = {}; - const empty: FillType = { XXXX: [], OXXX: [], @@ -67,7 +64,7 @@ const empty: FillType = { }; -const fillTypeDefault: FillType = { +const fillType8x2: FillType = { XXXX: [{x: 0, y: 0}], OXXX: [{x: 1, y: 0}], XOXX: [{x: 2, y: 0}], @@ -85,31 +82,27 @@ const fillTypeDefault: FillType = { OXOO: [{x: 6, y: 1}], XOOO: [{x: 7, y: 1}], }; -FILL_TYPE[AutotileType.DEFAULT] = fillTypeDefault; - -const fillTypeLarge: FillType = JSON.parse(JSON.stringify(fillTypeDefault)); -fillTypeLarge.OOXX.push({x: 8, y: 0}); -fillTypeLarge.OXXO.push({x: 9, y: 0}); -fillTypeLarge.XXOO.push({x: 8, y: 1}); -fillTypeLarge.XOOX.push({x: 9, y: 1}); -FILL_TYPE[AutotileType.LARGE] = fillTypeLarge; - -const fillTypeSuperLarge: FillType = JSON.parse(JSON.stringify(fillTypeLarge)); -fillTypeSuperLarge.OOXO.push({x: 10, y: 0}); -fillTypeSuperLarge.OOOX.push({x: 11, y: 0}); -fillTypeSuperLarge.OXOO.push({x: 10, y: 1}); -fillTypeSuperLarge.XOOO.push({x: 11, y: 1}); -FILL_TYPE[AutotileType.SUPER_LARGE] = fillTypeSuperLarge; - -const fillTypeMegaLarge: FillType = JSON.parse(JSON.stringify(fillTypeSuperLarge)); -fillTypeMegaLarge.OOXX.push({x: 12, y: 0}); -fillTypeMegaLarge.OXXO.push({x: 13, y: 0}); -fillTypeMegaLarge.XXOO.push({x: 12, y: 1}); -fillTypeMegaLarge.XOOX.push({x: 13, y: 1}); -FILL_TYPE[AutotileType.MEGA_LARGE] = fillTypeMegaLarge; - -const fillTypeSmall: FillType = JSON.parse(JSON.stringify(empty)); -for (const value of Object.values(fillTypeSmall) as Point[][]) { + +const fillType10x2 = Helper.copy(fillType8x2); +fillType10x2.OOXX.push({x: 8, y: 0}); +fillType10x2.OXXO.push({x: 9, y: 0}); +fillType10x2.XXOO.push({x: 8, y: 1}); +fillType10x2.XOOX.push({x: 9, y: 1}); + +const fillType12x2 = Helper.copy(fillType10x2); +fillType12x2.OOXO.push({x: 10, y: 0}); +fillType12x2.OOOX.push({x: 11, y: 0}); +fillType12x2.OXOO.push({x: 10, y: 1}); +fillType12x2.XOOO.push({x: 11, y: 1}); + +const fillType14x2 = Helper.copy(fillType12x2); +fillType14x2.OOXX.push({x: 12, y: 0}); +fillType14x2.OXXO.push({x: 13, y: 0}); +fillType14x2.XXOO.push({x: 12, y: 1}); +fillType14x2.XOOX.push({x: 13, y: 1}); + +const fillType4x1 = Helper.copy(empty); +for (const value of Object.values(fillType4x1) as Point[][]) { value.push({x: 0, y: 0}); value.push({x: 0, y: 0}); value.push({x: 0, y: 0}); @@ -118,10 +111,44 @@ for (const value of Object.values(fillTypeSmall) as Point[][]) { value.push({x: 3, y: 0}); } -FILL_TYPE[AutotileType.SMALL] = fillTypeSmall; +const fillType4x4: FillType = { + // order is important for same offset. Last one is used for reverse mapping + OOOO: [{x: 2, y: 2}], + XXXX: [{x: 2, y: 2}], + + OXXX: [{x: 2, y: 1}], + XOXX: [{x: 3, y: 2}], + XXOX: [{x: 2, y: 3}], + XXXO: [{x: 1, y: 2}], + + XXOO: [{x: 1, y: 3}], + OXXO: [{x: 1, y: 1}], + OOXX: [{x: 3, y: 1}], + XOOX: [{x: 3, y: 3}], + + OXOX: [{x: 1, y: 0}], + XOXO: [{x: 0, y: 2}], + + XOOO: [{x: 0, y: 3}], + OXOO: [{x: 0, y: 0}], + OOXO: [{x: 0, y: 1}], + OOOX: [{x: 2, y: 0}], +}; + + +export const FILL_TYPE: { + [key in AutotileType]: FillType +} = { + '4x1': fillType4x1, + '4x4': fillType4x4, + '8x2': fillType8x2, + '10x2': fillType10x2, + '12x2': fillType12x2, + '14x2': fillType14x2 +}; -export const FILL_TYPE_CLIFF_BORDER: FillType = JSON.parse(JSON.stringify(empty)); +export const FILL_TYPE_CLIFF_BORDER = Helper.copy(empty); FILL_TYPE_CLIFF_BORDER.XXXX = [ {x: 1, y: 0}, @@ -182,7 +209,7 @@ FILL_TYPE_CLIFF_BORDER.XOOX = [ {x: 3, y: 6} ]; -export const FILL_TYPE_CLIFF: FillType = JSON.parse(JSON.stringify(empty)); +export const FILL_TYPE_CLIFF: FillType = Helper.copy(empty); FILL_TYPE_CLIFF.XXXX = [ {x: 0, y: 3}, @@ -191,7 +218,7 @@ FILL_TYPE_CLIFF.XXXX = [ {x: 5, y: 3}, ]; -export const FILL_TYPE_CLIFF_ALT: FillType = JSON.parse(JSON.stringify(empty)); +export const FILL_TYPE_CLIFF_ALT: FillType = Helper.copy(empty); FILL_TYPE_CLIFF_ALT.XXXX = [ {x: 0, y: 0}, diff --git a/webapp/src/app/services/autotile/autotile.service.ts b/webapp/src/app/services/autotile/autotile.service.ts index 21a23da0..bd522465 100644 --- a/webapp/src/app/services/autotile/autotile.service.ts +++ b/webapp/src/app/services/autotile/autotile.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { Point } from '../../models/cross-code-map'; -import { CheckDir, CHECK_DIR, CHECK_ITERATE } from '../height-map/heightmap.constants'; +import { CHECK_DIR, CHECK_ITERATE, CheckDir } from '../height-map/heightmap.constants'; import { CCMapLayer } from '../phaser/tilemap/cc-map-layer'; import { AutotileConfig, FillType } from './autotile.constants'; import { GfxMapper } from './gfx-mapper'; @@ -67,29 +67,54 @@ export class AutotileService { let fillType = ''; - if (this.checkAt(w, 1) && this.checkAt(nw, 2) && this.checkAt(n, 3)) { - fillType += 'X'; - } else { - fillType += 'O'; - } - - if (this.checkAt(n, 2) && this.checkAt(ne, 3) && this.checkAt(e, 0)) { - fillType += 'X'; - } else { - fillType += 'O'; - } - - if (this.checkAt(e, 3) && this.checkAt(se, 0) && this.checkAt(s, 1)) { - fillType += 'X'; + // 4x4 needs special handling, corners are ignored + if (config.type === '4x4') { + if (this.checkAt(n, 2)) { + fillType += 'X'; + } else { + fillType += 'O'; + } + if (this.checkAt(e, 3)) { + fillType += 'X'; + } else { + fillType += 'O'; + } + if (this.checkAt(s, 0)) { + fillType += 'X'; + } else { + fillType += 'O'; + } + if (this.checkAt(w, 1)) { + fillType += 'X'; + } else { + fillType += 'O'; + } } else { - fillType += 'O'; + if (this.checkAt(w, 1) && this.checkAt(nw, 2) && this.checkAt(n, 3)) { + fillType += 'X'; + } else { + fillType += 'O'; + } + + if (this.checkAt(n, 2) && this.checkAt(ne, 3) && this.checkAt(e, 0)) { + fillType += 'X'; + } else { + fillType += 'O'; + } + + if (this.checkAt(e, 3) && this.checkAt(se, 0) && this.checkAt(s, 1)) { + fillType += 'X'; + } else { + fillType += 'O'; + } + + if (this.checkAt(s, 0) && this.checkAt(sw, 1) && this.checkAt(w, 2)) { + fillType += 'X'; + } else { + fillType += 'O'; + } } - if (this.checkAt(s, 0) && this.checkAt(sw, 1) && this.checkAt(w, 2)) { - fillType += 'X'; - } else { - fillType += 'O'; - } tile.fill = fillType as keyof FillType; this.drawSingleTile(layer, config, tile); } diff --git a/webapp/src/app/services/autotile/gfx-mapper.ts b/webapp/src/app/services/autotile/gfx-mapper.ts index d4b429f3..b8c1f181 100644 --- a/webapp/src/app/services/autotile/gfx-mapper.ts +++ b/webapp/src/app/services/autotile/gfx-mapper.ts @@ -2,13 +2,7 @@ import { Point } from '../../models/cross-code-map'; import { ChipsetConfig } from '../height-map/gfx-mapper/gfx-mapper.constants'; import { Helper } from '../phaser/helper'; import { Vec2 } from '../phaser/vec2'; -import { - AutotileConfig, - AutotileType, FillType, FILL_TYPE, - FILL_TYPE_CLIFF, - FILL_TYPE_CLIFF_ALT, - FILL_TYPE_CLIFF_BORDER -} from './autotile.constants'; +import { AutotileConfig, AutotileType, FILL_TYPE, FILL_TYPE_CLIFF, FILL_TYPE_CLIFF_ALT, FILL_TYPE_CLIFF_BORDER, FillType } from './autotile.constants'; import autotilesJson from '../../../assets/autotiles.json'; @@ -20,10 +14,9 @@ interface JsonType { map: string; tileCountX: number; autotiles: { - type: keyof typeof AutotileType; + size: Point; mergeWithEmpty?: boolean; base: Point; - cliff: Point; }[]; } @@ -41,9 +34,8 @@ export class GfxMapper { constructor() { this.generateAutotileConfig(); - const enumVals = Object.values(AutotileType).filter(v => !isNaN(Number(v))); - for (const type of enumVals as AutotileType[]) { + for (const type of Helper.typedKeys(FILL_TYPE)) { const map = new Map(); this.mapping[type] = map; this.generateMapping(map, FILL_TYPE[type]); @@ -56,20 +48,31 @@ export class GfxMapper { } private generateAutotileConfig() { - console.log(autotilesJson); for (const config of autotilesJson as JsonType[]) { const arr: AutotileConfig[] = []; this.AUTOTILE_CONFIG[config.map] = arr; for (const autotile of config.autotiles) { - const type = AutotileType[autotile.type]; + const generatedType: AutotileType = `${autotile.size.x}x${autotile.size.y}` as AutotileType; + + const tileset = TILESET_CONFIG[config.map]; + const terrains = tileset.terrains ?? []; + terrains.push(tileset.base); + let cliff: Point | undefined; + for (const terrain of terrains) { + if (terrain.ground.x === autotile.base.x && terrain.ground.y === autotile.base.y) { + cliff = terrain.cliff; + break; + } + } + const newConfig: AutotileConfig = { key: config.map, tileCountX: config.tileCountX, - type: type, + type: generatedType, mergeWithEmpty: autotile.mergeWithEmpty === undefined ? true : autotile.mergeWithEmpty, base: autotile.base, - cliff: autotile.cliff + cliff: cliff }; arr.push(newConfig); @@ -146,7 +149,7 @@ export class GfxMapper { } private getMappingKey(p: Point) { - return p.y * 2000 + p.x; + return p.y * 1000 + p.x; } } diff --git a/webapp/src/app/services/phaser/helper.ts b/webapp/src/app/services/phaser/helper.ts index 1b51d43f..564b4b64 100644 --- a/webapp/src/app/services/phaser/helper.ts +++ b/webapp/src/app/services/phaser/helper.ts @@ -72,6 +72,10 @@ export class Helper { return JSON.parse(JSON.stringify(obj)); } + public static typedKeys(obj: T): (keyof T)[] { + return Object.keys(obj) as (keyof T)[]; + } + public static getJson(key: string, callback: (json: any) => void) { const scene = Globals.scene; From 13e0225fa93c768eba73bdad05d3924625b6e511 Mon Sep 17 00:00:00 2001 From: cramerL Date: Sat, 20 Jul 2024 23:58:49 +0200 Subject: [PATCH 06/30] show the user if an autotile is currently selected --- .../captions/captions.component.html | 10 +++---- .../captions/captions.component.scss | 19 +++++++++---- .../components/captions/captions.component.ts | 10 ++++++- .../app/services/autotile/autotile.service.ts | 28 ++++++++++++++++++- .../src/app/services/global-events.service.ts | 1 + 5 files changed, 56 insertions(+), 12 deletions(-) diff --git a/webapp/src/app/components/captions/captions.component.html b/webapp/src/app/components/captions/captions.component.html index 0b8ff06f..d67c55aa 100644 --- a/webapp/src/app/components/captions/captions.component.html +++ b/webapp/src/app/components/captions/captions.component.html @@ -1,8 +1,8 @@ -

{{ version }}

-

+

{{ version }}

+

- - {{el.text}} - + + {{ el.text }} +

diff --git a/webapp/src/app/components/captions/captions.component.scss b/webapp/src/app/components/captions/captions.component.scss index 8c76c985..873e0c79 100644 --- a/webapp/src/app/components/captions/captions.component.scss +++ b/webapp/src/app/components/captions/captions.component.scss @@ -1,12 +1,16 @@ @use '@angular/material' as mat; -.caption { +.bottom-container { bottom: 0; - padding: 5px; - color: white; + z-index: 9999; position: absolute; - pointer-events: none; + //pointer-events: none; +} + +.caption { + padding: 5px; + color: white; } .version { @@ -14,9 +18,14 @@ } .bottom-elements { - background-color: #0005; + display: flex; + gap: 8px; &:empty { display: none; } + + & .caption { + background-color: #0005; + } } diff --git a/webapp/src/app/components/captions/captions.component.ts b/webapp/src/app/components/captions/captions.component.ts index 39f901aa..28787bc5 100644 --- a/webapp/src/app/components/captions/captions.component.ts +++ b/webapp/src/app/components/captions/captions.component.ts @@ -16,10 +16,14 @@ export class CaptionsComponent implements OnInit { version = environment.version; coords: BottomUiElement = {}; selectionSize: BottomUiElement = {}; + autotile: BottomUiElement = { + text: 'Autotile' + }; uiElements: BottomUiElement[] = [ this.coords, - this.selectionSize + this.selectionSize, + this.autotile, ]; ngOnInit(): void { @@ -32,5 +36,9 @@ export class CaptionsComponent implements OnInit { this.selectionSize.text = `${size?.x}x${size?.y}`; this.selectionSize.active = !!size; }); + + Globals.globalEventsService.isAutotile.subscribe(show => { + this.autotile.active = show; + }); } } diff --git a/webapp/src/app/services/autotile/autotile.service.ts b/webapp/src/app/services/autotile/autotile.service.ts index bd522465..ec512eb0 100644 --- a/webapp/src/app/services/autotile/autotile.service.ts +++ b/webapp/src/app/services/autotile/autotile.service.ts @@ -5,6 +5,10 @@ import { CCMapLayer } from '../phaser/tilemap/cc-map-layer'; import { AutotileConfig, FillType } from './autotile.constants'; import { GfxMapper } from './gfx-mapper'; import { customPutTileAt } from '../phaser/tilemap/layer-helper'; +import { PhaserEventsService } from '../phaser/phaser-events.service'; +import { combineLatest } from 'rxjs'; +import { MapLoaderService } from '../map-loader.service'; +import { GlobalEventsService } from '../global-events.service'; interface TileData { pos: Point; @@ -19,7 +23,29 @@ export class AutotileService { private gfxMapper = new GfxMapper(); - constructor() { + constructor( + phaserEvents: PhaserEventsService, + mapLoader: MapLoaderService, + events: GlobalEventsService + ) { + combineLatest([ + phaserEvents.changeSelectedTiles.asObservable(), + mapLoader.selectedLayer.asObservable() + ]).subscribe(([tiles, layer]) => { + if (!layer) { + events.isAutotile.next(false); + return; + } + let autotile = false; + for (const tile of tiles) { + const config = this.gfxMapper.getAutotileConfig(layer.details.tilesetName, tile.id, false); + if (config) { + autotile = true; + break; + } + } + events.isAutotile.next(autotile); + }); } public drawTile(layer: CCMapLayer, x: number, y: number, tile: number, checkCliff = true) { diff --git a/webapp/src/app/services/global-events.service.ts b/webapp/src/app/services/global-events.service.ts index 4c9814ac..de06baf3 100644 --- a/webapp/src/app/services/global-events.service.ts +++ b/webapp/src/app/services/global-events.service.ts @@ -25,6 +25,7 @@ export class GlobalEventsService { updateCoords = new Subject(); updateTileSelectionSize = new Subject(); + isAutotile = new BehaviorSubject(false); showIngamePreview = new BehaviorSubject(false); hasUnsavedChanges = new BehaviorSubject(false); gridSettings = new BehaviorSubject(Globals.gridSettings()); From 27a6944d6bd67193198665b95fcf0f3110b638dc Mon Sep 17 00:00:00 2001 From: cramerL Date: Sun, 21 Jul 2024 01:32:59 +0200 Subject: [PATCH 07/30] added custom json loader to include json's from mods --- backend/src/server.ts | 1 + common/src/controllers/api.ts | 81 +++++++++++++------ webapp/src/app/app.component.ts | 10 +-- .../app/services/autotile/autotile.service.ts | 7 +- .../src/app/services/autotile/gfx-mapper.ts | 42 ++++++---- .../src/app/services/http-client.service.ts | 6 +- .../src/app/services/json-loader.service.ts | 56 +++++++++++++ 7 files changed, 153 insertions(+), 50 deletions(-) create mode 100644 webapp/src/app/services/json-loader.service.ts diff --git a/backend/src/server.ts b/backend/src/server.ts index cea88214..ff871986 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -27,6 +27,7 @@ app.get('/api/allTilesets', async (_, res) => res.json(await api.getAllTilesets( app.get('/api/allMaps', async (req, res) => res.json(await api.getAllMaps(config.pathToCrosscode, req.query['includeVanillaMaps'] == 'true'))); app.get('/api/allFilesInFolder', async (req, res) => res.json(await api.getAllFilesInFolder(config.pathToCrosscode, req.query['folder'] as string, req.query['extension'] as string))); app.get('/api/allMods', async (_, res) => res.json(await api.getAllMods(config.pathToCrosscode))); +app.get('/api/allModMapEditorConfigs', async (_, res) => res.json(await api.getAllModMapEditorConfigs(config.pathToCrosscode))); app.post('/api/get', async (req, res) => { res.json(await api.get(config.pathToCrosscode, req.body.path)); }); diff --git a/common/src/controllers/api.ts b/common/src/controllers/api.ts index f40eaafc..7c6710cd 100644 --- a/common/src/controllers/api.ts +++ b/common/src/controllers/api.ts @@ -1,6 +1,13 @@ import { fsPromise, pathPromise } from '../require.js'; import { saveFile as save } from './saveFile.js'; + +export interface ConfigExtension { + filename: string; + mod: string; + file: T; +} + const mods: string[] = []; let packagesCache: Record }>; @@ -8,7 +15,7 @@ async function listAllFiles(dir: string, filelist: string[], ending: string, roo if (root === undefined) { root = dir; } - + const files = await tryReadDir(dir); const promises: Promise[] = []; for (const file of files) { @@ -48,7 +55,7 @@ async function searchFile(file: string, dir: string, filelist: string[], ending: .resolve(dir, file) .split(path.normalize(root))[1] .replace(/\\/g, '/'); - + const result = normalized.startsWith('/') ? normalized.substr(1) : normalized; if (!filelist.includes(result)) { filelist.push(result); @@ -84,12 +91,12 @@ function selectMod(name: string, packages: Record path.basename(path.dirname(file)))); - + const promises: Promise<[string, Buffer]>[] = []; for (const file of files) { const folderName = path.basename(path.dirname(file)); @@ -146,7 +153,7 @@ async function readMods(dir: string) { } const rawPackages = await Promise.all(promises); const packages: Record }> = {}; - + for (const [name, pkg] of rawPackages) { try { const parsed = JSON.parse(pkg as unknown as string); @@ -159,13 +166,13 @@ async function readMods(dir: string) { console.error('Invalid json data in package.json of mod: ' + name, err); } } - + const promisesCCMod: Promise<[string, Buffer]>[] = []; for (const file of filesCCMod) { promisesCCMod.push((async (): Promise<[string, Buffer]> => [path.basename(path.dirname(file)), await fs.promises.readFile(file)])()); } const rawCCMods = await Promise.all(promisesCCMod); - + for (const [name, pkg] of rawCCMods) { try { const parsed = JSON.parse(pkg as unknown as string); @@ -178,7 +185,7 @@ async function readMods(dir: string) { console.error('Invalid json data in ccmod.json of mod: ' + name, err); } } - + packagesCache = packages; return packages; } @@ -187,35 +194,35 @@ export async function getAllFiles(dir: string) { const path = await pathPromise; const images = await listAllFiles(path.resolve(dir, 'media/'), [], 'png', path.resolve(dir)); const data = await listAllFiles(path.resolve(dir, 'data/'), [], 'json', path.resolve(dir)); - + for (const mod of mods) { const modDir = path.join(dir, 'mods', mod, 'assets'); await listAllFiles(path.resolve(modDir, 'media/'), images, 'png', path.resolve(modDir)); await listAllFiles(path.resolve(modDir, 'data/'), data, 'json', path.resolve(modDir)); } - + images.sort(); data.sort(); - - return { images, data }; + + return {images, data}; } export async function getAllTilesets(dir: string) { const path = await pathPromise; const result = await listAllFiles(path.resolve(dir, 'media/map/'), [], 'png', path.resolve(dir)); - + for (const mod of mods) { const modDir = path.join(dir, 'mods', mod, 'assets'); await listAllFiles(path.resolve(modDir, 'media/map/'), result, 'png', path.resolve(modDir)); } - + return result.sort(); } export async function getAllMaps(dir: string, includeVanillaMaps: boolean) { const path = await pathPromise; const paths: string[] = []; - + if (mods.length === 0 || includeVanillaMaps) { await listAllFiles(path.resolve(dir, 'data/maps/'), paths, 'json', path.resolve(dir)); } @@ -223,7 +230,7 @@ export async function getAllMaps(dir: string, includeVanillaMaps: boolean) { const modDir = path.join(dir, 'mods', mods[0], 'assets'); await listAllFiles(path.resolve(modDir, 'data/maps/'), paths, 'json', path.resolve(modDir)); } - + return paths .sort() .map(p => p.substring('data/maps/'.length, p.length - '.json'.length)) @@ -233,12 +240,12 @@ export async function getAllMaps(dir: string, includeVanillaMaps: boolean) { export async function getAllFilesInFolder(dir: string, folder: string, extension: string) { const path = await pathPromise; const result = await listAllFiles(path.resolve(dir, folder), [], extension, path.resolve(dir)); - + for (const mod of mods) { const modDir = path.join(dir, 'mods', mod, 'assets'); await listAllFiles(path.resolve(modDir, folder), result, extension, path.resolve(modDir)); } - + return result.sort() .map(p => p.substring(folder.length, p.length - `.${extension}`.length)); } @@ -246,10 +253,34 @@ export async function getAllFilesInFolder(dir: string, folder: string, extension export async function getAllMods(dir: string) { const packages = await readMods(dir); return Object.entries(packages) - .map(([id, pkg]) => ({ id, displayName: pkg.displayName as string })) + .map(([id, pkg]) => ({id, displayName: pkg.displayName as string})) .sort((a, b) => a.displayName.localeCompare(b.displayName)); } +export async function getAllModMapEditorConfigs(dir: string): Promise[]> { + const packages = await readMods(dir); + + const fs = await fsPromise; + const path = await pathPromise; + + const configs: ConfigExtension[] = []; + + for (const mod of Object.values(packages)) { + const modName = mod.folderName; + const mapEditorPath = path.join(dir, 'mods', modName, 'map-editor'); + const files = await tryReadDir(mapEditorPath); + for (const filename of files) { + const file = await fs.promises.readFile(path.join(mapEditorPath, filename), 'utf-8'); + configs.push({ + filename: filename, + mod: modName, + file: file + }); + } + } + return configs; +} + export async function selectedMod(dir: string, modName: string) { const packages = await readMods(dir); mods.splice(0); // Clear array @@ -264,7 +295,7 @@ export async function get(dir: string, file: string): Promise { promises.push(getAsync(modFile)); } promises.push(getAsync(path.join(dir, file))); - + const results = await Promise.all(promises); for (const result of results) { if (result) { @@ -282,7 +313,7 @@ export async function resolve(dir: string, file: string): Promise { promises.push(resolveAsync(modFile)); } promises.push(resolveAsync(path.join(dir, file))); - + const results = await Promise.all(promises); for (const result of results) { if (result) { diff --git a/webapp/src/app/app.component.ts b/webapp/src/app/app.component.ts index fc5a9237..1c6fa16c 100644 --- a/webapp/src/app/app.component.ts +++ b/webapp/src/app/app.component.ts @@ -13,16 +13,12 @@ export class AppComponent { constructor( private readonly eventsService: GlobalEventsService, private readonly overlayService: OverlayService, - private readonly router: Router - ) { - this.router.events.subscribe(event => { - console.log(event.constructor.name, event); - }); + ) { } - + @HostListener('window:beforeunload', ['$event']) onUnload($event: any) { - if(this.eventsService.hasUnsavedChanges.getValue()) { + if (this.eventsService.hasUnsavedChanges.getValue()) { $event.returnValue = 'Are you sure you want to discard your changes?'; const dialogRef = this.overlayService.open(ConfirmCloseComponent, { diff --git a/webapp/src/app/services/autotile/autotile.service.ts b/webapp/src/app/services/autotile/autotile.service.ts index ec512eb0..7b410c74 100644 --- a/webapp/src/app/services/autotile/autotile.service.ts +++ b/webapp/src/app/services/autotile/autotile.service.ts @@ -9,6 +9,7 @@ import { PhaserEventsService } from '../phaser/phaser-events.service'; import { combineLatest } from 'rxjs'; import { MapLoaderService } from '../map-loader.service'; import { GlobalEventsService } from '../global-events.service'; +import { JsonLoaderService } from '../json-loader.service'; interface TileData { pos: Point; @@ -21,13 +22,15 @@ interface TileData { }) export class AutotileService { - private gfxMapper = new GfxMapper(); + private gfxMapper: GfxMapper; constructor( phaserEvents: PhaserEventsService, mapLoader: MapLoaderService, - events: GlobalEventsService + events: GlobalEventsService, + jsonLoader: JsonLoaderService, ) { + this.gfxMapper = new GfxMapper(jsonLoader); combineLatest([ phaserEvents.changeSelectedTiles.asObservable(), mapLoader.selectedLayer.asObservable() diff --git a/webapp/src/app/services/autotile/gfx-mapper.ts b/webapp/src/app/services/autotile/gfx-mapper.ts index b8c1f181..cc85de30 100644 --- a/webapp/src/app/services/autotile/gfx-mapper.ts +++ b/webapp/src/app/services/autotile/gfx-mapper.ts @@ -4,9 +4,8 @@ import { Helper } from '../phaser/helper'; import { Vec2 } from '../phaser/vec2'; import { AutotileConfig, AutotileType, FILL_TYPE, FILL_TYPE_CLIFF, FILL_TYPE_CLIFF_ALT, FILL_TYPE_CLIFF_BORDER, FillType } from './autotile.constants'; -import autotilesJson from '../../../assets/autotiles.json'; - import tilesets from '../../../assets/tilesets.json'; +import { JsonLoaderService } from '../json-loader.service'; const TILESET_CONFIG: { [key: string]: ChipsetConfig } = tilesets; @@ -15,6 +14,7 @@ interface JsonType { tileCountX: number; autotiles: { size: Point; + cliff?: Point | null | false; mergeWithEmpty?: boolean; base: Point; }[]; @@ -31,9 +31,14 @@ export class GfxMapper { private cliffMapping = new Map(); private cliffAltMapping = new Map(); - constructor() { - this.generateAutotileConfig(); - + constructor( + private jsonLoader: JsonLoaderService + ) { + this.init(); + } + + private async init() { + await this.generateAutotileConfig(); for (const type of Helper.typedKeys(FILL_TYPE)) { const map = new Map(); @@ -44,12 +49,17 @@ export class GfxMapper { this.generateMapping(this.cliffBorderMapping, FILL_TYPE_CLIFF_BORDER); this.generateMapping(this.cliffMapping, FILL_TYPE_CLIFF); this.generateMapping(this.cliffAltMapping, FILL_TYPE_CLIFF_ALT); - } - private generateAutotileConfig() { - for (const config of autotilesJson as JsonType[]) { - const arr: AutotileConfig[] = []; + private async generateAutotileConfig() { + const jsons = await this.jsonLoader.loadJson('autotiles.json'); + const autotilesJson = jsons.flat(); + for (const config of autotilesJson) { + let arr: AutotileConfig[] = []; + const prevArr = this.AUTOTILE_CONFIG[config.map]; + if (prevArr) { + arr = prevArr; + } this.AUTOTILE_CONFIG[config.map] = arr; for (const autotile of config.autotiles) { @@ -58,11 +68,13 @@ export class GfxMapper { const tileset = TILESET_CONFIG[config.map]; const terrains = tileset.terrains ?? []; terrains.push(tileset.base); - let cliff: Point | undefined; - for (const terrain of terrains) { - if (terrain.ground.x === autotile.base.x && terrain.ground.y === autotile.base.y) { - cliff = terrain.cliff; - break; + let cliff = autotile.cliff; + if (cliff === undefined) { + for (const terrain of terrains) { + if (terrain.ground.x === autotile.base.x && terrain.ground.y === autotile.base.y) { + cliff = terrain.cliff; + break; + } } } @@ -72,7 +84,7 @@ export class GfxMapper { type: generatedType, mergeWithEmpty: autotile.mergeWithEmpty === undefined ? true : autotile.mergeWithEmpty, base: autotile.base, - cliff: cliff + cliff: cliff ? cliff : undefined }; arr.push(newConfig); diff --git a/webapp/src/app/services/http-client.service.ts b/webapp/src/app/services/http-client.service.ts index 22d7142b..c063bfc3 100644 --- a/webapp/src/app/services/http-client.service.ts +++ b/webapp/src/app/services/http-client.service.ts @@ -1,7 +1,7 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { api } from 'cc-map-editor-common'; -import { Observable } from 'rxjs'; +import { lastValueFrom, Observable } from 'rxjs'; import { FileInfos } from '../models/file-infos'; import { ElectronService } from './electron.service'; import { Globals } from './globals'; @@ -61,6 +61,10 @@ export class HttpClientService { getMods(): Observable<{ id: string, displayName: string }[]> { return this.request('api/allMods', api.getAllMods); } + + getModMapEditorConfigs() { + return lastValueFrom(this.request('api/allModMapEditorConfigs', api.getAllModMapEditorConfigs)); + } getAssetsFile(path: string): Observable { if (!Globals.isElectron) { diff --git a/webapp/src/app/services/json-loader.service.ts b/webapp/src/app/services/json-loader.service.ts new file mode 100644 index 00000000..8f417143 --- /dev/null +++ b/webapp/src/app/services/json-loader.service.ts @@ -0,0 +1,56 @@ +import { Injectable } from '@angular/core'; +import { HttpClientService } from './http-client.service'; +import { HttpClient } from '@angular/common/http'; +import { lastValueFrom } from 'rxjs'; +import { ConfigExtension } from 'cc-map-editor-common/dist/controllers/api'; +import { MatSnackBar } from '@angular/material/snack-bar'; + +@Injectable({ + providedIn: 'root' +}) +export class JsonLoaderService { + + private readonly initialized?: Promise; + private configs = new Map[]>; + + constructor( + private http: HttpClientService, + private angularHttp: HttpClient, + private snackbar: MatSnackBar, + ) { + this.initialized = this.init(); + } + + async init() { + const configs = await this.http.getModMapEditorConfigs(); + + for (const config of configs) { + const newConfig: ConfigExtension = { + filename: config.filename, + mod: config.mod, + file: {}, + }; + try { + newConfig.file = JSON.parse(config.file); + } catch (e) { + console.error(e); + this.snackbar.open(`Failed to parse Mod config: ${config.mod} -> ${config.filename}`, undefined); + continue; + } + const configs = this.configs.get(config.filename) ?? []; + this.configs.set(config.filename, configs); + configs.push(newConfig); + } + } + + async loadJson(file: string): Promise { + await this.initialized; + const json = await lastValueFrom(this.angularHttp.get(`./assets/${file}`)); + const modJson = (this.configs.get(file) ?? []).map(v => v.file) as T[]; + + return [ + json as T, + ...modJson + ]; + } +} From 967b1f329e359fd06bcf2b2b28b938bd53fd5e57 Mon Sep 17 00:00:00 2001 From: cramerL Date: Sun, 21 Jul 2024 01:45:44 +0200 Subject: [PATCH 08/30] tilsets.json now loaded through json-loader --- webapp/src/app/services/autotile/gfx-mapper.ts | 11 ++++++----- webapp/src/app/services/json-loader.service.ts | 9 ++++++++- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/webapp/src/app/services/autotile/gfx-mapper.ts b/webapp/src/app/services/autotile/gfx-mapper.ts index cc85de30..97352382 100644 --- a/webapp/src/app/services/autotile/gfx-mapper.ts +++ b/webapp/src/app/services/autotile/gfx-mapper.ts @@ -4,11 +4,8 @@ import { Helper } from '../phaser/helper'; import { Vec2 } from '../phaser/vec2'; import { AutotileConfig, AutotileType, FILL_TYPE, FILL_TYPE_CLIFF, FILL_TYPE_CLIFF_ALT, FILL_TYPE_CLIFF_BORDER, FillType } from './autotile.constants'; -import tilesets from '../../../assets/tilesets.json'; import { JsonLoaderService } from '../json-loader.service'; -const TILESET_CONFIG: { [key: string]: ChipsetConfig } = tilesets; - interface JsonType { map: string; tileCountX: number; @@ -26,6 +23,9 @@ export class GfxMapper { [key: string]: AutotileConfig[] | undefined; } = {}; + + private TILESET_CONFIG: { [key: string]: ChipsetConfig } = {}; + private mapping: { [key in AutotileType]: Map } = {}; private cliffBorderMapping = new Map(); private cliffMapping = new Map(); @@ -38,6 +38,7 @@ export class GfxMapper { } private async init() { + this.TILESET_CONFIG = await this.jsonLoader.loadJsonMerged('tilesets.json'); await this.generateAutotileConfig(); for (const type of Helper.typedKeys(FILL_TYPE)) { @@ -65,7 +66,7 @@ export class GfxMapper { for (const autotile of config.autotiles) { const generatedType: AutotileType = `${autotile.size.x}x${autotile.size.y}` as AutotileType; - const tileset = TILESET_CONFIG[config.map]; + const tileset = this.TILESET_CONFIG[config.map]; const terrains = tileset.terrains ?? []; terrains.push(tileset.base); let cliff = autotile.cliff; @@ -123,7 +124,7 @@ export class GfxMapper { if (!cliff) { return this.getFill(pos, config.base, this.mapping[config.type]); } - const tilesetConfig = TILESET_CONFIG[config.key]; + const tilesetConfig = this.TILESET_CONFIG[config.key]; let tilesetBase; if (tilesetConfig && tilesetConfig.base) { tilesetBase = tilesetConfig.base; diff --git a/webapp/src/app/services/json-loader.service.ts b/webapp/src/app/services/json-loader.service.ts index 8f417143..3ddfbad8 100644 --- a/webapp/src/app/services/json-loader.service.ts +++ b/webapp/src/app/services/json-loader.service.ts @@ -34,7 +34,7 @@ export class JsonLoaderService { newConfig.file = JSON.parse(config.file); } catch (e) { console.error(e); - this.snackbar.open(`Failed to parse Mod config: ${config.mod} -> ${config.filename}`, undefined); + this.snackbar.open(`Failed to parse mod config: ${config.mod}/map-editor/${config.filename}`, 'close'); continue; } const configs = this.configs.get(config.filename) ?? []; @@ -53,4 +53,11 @@ export class JsonLoaderService { ...modJson ]; } + + async loadJsonMerged(file: string): Promise { + const jsons = await this.loadJson(file); + const base = {} as T; + Object.assign(base as any, ...jsons); + return base; + } } From 621009c546ad7fcb667771eebeb9b0c93b24e81c Mon Sep 17 00:00:00 2001 From: cramerL Date: Sun, 21 Jul 2024 01:56:37 +0200 Subject: [PATCH 09/30] added simple cache to json loader --- webapp/src/app/services/json-loader.service.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/webapp/src/app/services/json-loader.service.ts b/webapp/src/app/services/json-loader.service.ts index 3ddfbad8..037bcad5 100644 --- a/webapp/src/app/services/json-loader.service.ts +++ b/webapp/src/app/services/json-loader.service.ts @@ -13,6 +13,8 @@ export class JsonLoaderService { private readonly initialized?: Promise; private configs = new Map[]>; + private cache: Record = {}; + constructor( private http: HttpClientService, private angularHttp: HttpClient, @@ -55,9 +57,14 @@ export class JsonLoaderService { } async loadJsonMerged(file: string): Promise { + const cached = this.cache[file] as T | undefined; + if (cached) { + return cached; + } const jsons = await this.loadJson(file); const base = {} as T; Object.assign(base as any, ...jsons); + this.cache[file] = base; return base; } } From f7024408c24cb6a02d533df0ca093f7deb473427 Mon Sep 17 00:00:00 2001 From: cramerL Date: Sun, 21 Jul 2024 01:56:46 +0200 Subject: [PATCH 10/30] abstract-faces.json now loaded through json-loader --- .../custom-expression-widget.component.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/webapp/src/app/components/widgets/person-expression-widget/custom-expression-widget/custom-expression-widget.component.ts b/webapp/src/app/components/widgets/person-expression-widget/custom-expression-widget/custom-expression-widget.component.ts index d5de39ef..258cff2a 100644 --- a/webapp/src/app/components/widgets/person-expression-widget/custom-expression-widget/custom-expression-widget.component.ts +++ b/webapp/src/app/components/widgets/person-expression-widget/custom-expression-widget/custom-expression-widget.component.ts @@ -9,10 +9,10 @@ import { Helper } from '../../../../services/phaser/helper'; import { CharacterSettings, Face } from '../../../../services/phaser/entities/registry/npc'; import { Person } from '../../../../models/events'; import { prepareSheet } from '../../../../services/phaser/sheet-parser'; -import AbstractFaces from '../../../../../assets/abstract-faces.json'; import { getNPCTemplates } from '../../../../services/phaser/entities/registry/npc-templates'; import { ExpressionRendererEntity, ExpressionRendererSettings } from './expression-renderer-entity'; import { Globals } from '../../../../services/globals'; +import { JsonLoaderService } from '../../../../services/json-loader.service'; @Component({ selector: 'app-custom-expression-widget', @@ -27,6 +27,7 @@ export class CustomExpressionWidgetComponent extends OverlayWidget imple constructor( private http: HttpClientService, private changeDetectorRef: ChangeDetectorRef, + private jsonLoader: JsonLoaderService, overlayService: OverlayService, overlay: Overlay, ) { @@ -109,7 +110,8 @@ export class CustomExpressionWidgetComponent extends OverlayWidget imple let face: Face = sheet.face ?? {}; if (typeof sheet.face === 'object' && sheet.face?.ABSTRACT) { - face = AbstractFaces[sheet.face.ABSTRACT as string]; + const abstractFaces = await this.jsonLoader.loadJsonMerged>('abstract-faces.json'); + face = abstractFaces[sheet.face.ABSTRACT as string]; } return face; From c5f71a671ae2e8ccb5979f92adc47f9835678a35 Mon Sep 17 00:00:00 2001 From: cramerL Date: Mon, 22 Jul 2024 00:27:03 +0200 Subject: [PATCH 11/30] changed default for "mergeWithEmpty" to false. Can't find anything where that's useful --- webapp/src/app/services/autotile/gfx-mapper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/src/app/services/autotile/gfx-mapper.ts b/webapp/src/app/services/autotile/gfx-mapper.ts index 97352382..df3824c7 100644 --- a/webapp/src/app/services/autotile/gfx-mapper.ts +++ b/webapp/src/app/services/autotile/gfx-mapper.ts @@ -83,7 +83,7 @@ export class GfxMapper { key: config.map, tileCountX: config.tileCountX, type: generatedType, - mergeWithEmpty: autotile.mergeWithEmpty === undefined ? true : autotile.mergeWithEmpty, + mergeWithEmpty: !!autotile.mergeWithEmpty, base: autotile.base, cliff: cliff ? cliff : undefined }; From 075b50d91915ba17623ccd455095bbbba7cc8493 Mon Sep 17 00:00:00 2001 From: cramerL Date: Mon, 22 Jul 2024 00:27:45 +0200 Subject: [PATCH 12/30] updated autotiles for some tilesets --- webapp/src/assets/autotiles.json | 418 +++++++++++++++---------------- 1 file changed, 204 insertions(+), 214 deletions(-) diff --git a/webapp/src/assets/autotiles.json b/webapp/src/assets/autotiles.json index fe3b0343..4aadd502 100644 --- a/webapp/src/assets/autotiles.json +++ b/webapp/src/assets/autotiles.json @@ -4,392 +4,382 @@ "tileCountX": 32, "autotiles": [ { - "type": "MEGA_LARGE", "base": { "x": 0, "y": 1 - } - } - ] - }, - { - "map": "media/map/bergen-trail.png", - "tileCountX": 32, - "autotiles": [ + }, + "size": { + "x": 14, + "y": 2 + }, + "cliff": false + }, { - "type": "LARGE", "base": { - "x": 1, - "y": 0 + "x": 12, + "y": 16 }, - "cliff": { - "x": 6, + "size": { + "x": 4, "y": 4 } }, { - "type": "DEFAULT", "base": { - "x": 1, + "x": 8, + "y": 34 + }, + "size": { + "x": 8, "y": 2 + } + }, + { + "base": { + "x": 8, + "y": 36 }, - "cliff": { - "x": 12, - "y": 4 + "size": { + "x": 8, + "y": 2 } } ] }, { - "map": "media/map/jungle.png", + "map": "media/map/bergen-trail.png", "tileCountX": 32, "autotiles": [ { - "type": "SMALL", "base": { - "x": 0, + "x": 1, "y": 0 + }, + "size": { + "x": 10, + "y": 2 } }, { - "type": "SMALL", "base": { - "x": 0, - "y": 1 + "x": 1, + "y": 2 + }, + "size": { + "x": 8, + "y": 2 } }, { - "type": "MEGA_LARGE", "base": { - "x": 6, + "x": 11, "y": 0 }, - "cliff": { - "x": 18, - "y": 4 + "size": { + "x": 8, + "y": 2 } }, { - "type": "SUPER_LARGE", "base": { - "x": 20, + "x": 22, "y": 0 }, - "cliff": { - "x": 18, - "y": 4 + "size": { + "x": 10, + "y": 2 } }, { - "type": "SUPER_LARGE", "base": { - "x": 20, - "y": 11 + "x": 23, + "y": 5 }, - "mergeWithEmpty": false + "size": { + "x": 8, + "y": 2 + } }, { - "type": "SUPER_LARGE", "base": { - "x": 20, - "y": 13 + "x": 23, + "y": 7 }, - "mergeWithEmpty": false + "size": { + "x": 8, + "y": 2 + } }, { - "type": "SUPER_LARGE", "base": { - "x": 20, - "y": 15 + "x": 22, + "y": 9 }, - "mergeWithEmpty": false + "size": { + "x": 10, + "y": 2 + } }, { - "type": "SUPER_LARGE", "base": { - "x": 20, - "y": 17 + "x": 13, + "y": 13 }, - "mergeWithEmpty": false + "size": { + "x": 4, + "y": 4 + } }, { - "type": "SUPER_LARGE", "base": { - "x": 20, - "y": 19 + "x": 0, + "y": 40 }, - "mergeWithEmpty": false + "size": { + "x": 8, + "y": 2 + } }, { - "type": "SUPER_LARGE", "base": { - "x": 20, - "y": 21 + "x": 0, + "y": 42 }, - "mergeWithEmpty": false + "size": { + "x": 8, + "y": 2 + } }, { - "type": "SUPER_LARGE", "base": { - "x": 20, - "y": 23 + "x": 18, + "y": 36 }, - "mergeWithEmpty": false + "size": { + "x": 12, + "y": 2 + } }, { - "type": "SUPER_LARGE", "base": { - "x": 20, - "y": 25 - }, - "mergeWithEmpty": false - } - ] - }, - { - "map": "media/map/arid.png", - "tileCountX": 32, - "autotiles": [ - { - "type": "LARGE", - "base": { - "x": 1, - "y": 0 + "x": 18, + "y": 38 }, - "cliff": { - "x": 6, - "y": 4 + "size": { + "x": 12, + "y": 2 } }, { - "type": "DEFAULT", "base": { - "x": 11, - "y": 0 + "x": 0, + "y": 56 + }, + "size": { + "x": 10, + "y": 2 } } ] }, { - "map": "media/map/arid-interior.png", + "map": "media/map/jungle.png", "tileCountX": 32, "autotiles": [ { - "type": "LARGE", "base": { - "x": 1, + "x": 6, "y": 0 + }, + "size": { + "x": 14, + "y": 2 } }, { - "type": "LARGE", "base": { - "x": 11, + "x": 20, "y": 0 - } - } - ] - }, - { - "map": "media/map/cave.png", - "tileCountX": 32, - "autotiles": [ - { - "type": "DEFAULT", - "base": { - "x": 0, - "y": 1 + }, + "size": { + "x": 10, + "y": 2 } }, { - "type": "LARGE", "base": { - "x": 8, - "y": 1 + "x": 20, + "y": 11 }, - "cliff": { - "x": 18, - "y": 4 + "size": { + "x": 12, + "y": 2 } }, { - "type": "LARGE", "base": { - "x": 18, - "y": 1 + "x": 20, + "y": 13 + }, + "size": { + "x": 12, + "y": 2 } - } - ] - }, - { - "map": "media/map/forest.png", - "tileCountX": 32, - "autotiles": [ + }, { - "type": "LARGE", "base": { - "x": 1, - "y": 0 + "x": 20, + "y": 15 + }, + "size": { + "x": 12, + "y": 2 } }, { - "type": "LARGE", "base": { - "x": 11, - "y": 0 + "x": 20, + "y": 17 + }, + "size": { + "x": 12, + "y": 2 } }, { - "type": "DEFAULT", "base": { - "x": 21, - "y": 0 + "x": 20, + "y": 19 + }, + "size": { + "x": 12, + "y": 2 } }, { - "type": "LARGE", "base": { - "x": 9, + "x": 20, + "y": 21 + }, + "size": { + "x": 12, "y": 2 } - } - ] - }, - { - "map": "media/map/heat-area.png", - "tileCountX": 32, - "autotiles": [ + }, { - "type": "LARGE", "base": { - "x": 1, - "y": 0 + "x": 20, + "y": 23 }, - "cliff": { + "size": { "x": 12, - "y": 4 + "y": 2 } }, { - "type": "DEFAULT", "base": { - "x": 1, - "y": 2 + "x": 20, + "y": 25 }, - "cliff": { - "x": 18, - "y": 4 + "size": { + "x": 12, + "y": 2 } }, { - "type": "LARGE", "base": { - "x": 11, - "y": 0 + "x": 7, + "y": 25 }, - "cliff": { - "x": 6, + "size": { + "x": 4, "y": 4 } }, { - "type": "DEFAULT", "base": { "x": 11, - "y": 2 + "y": 32 }, - "cliff": { - "x": 24, - "y": 4 + "size": { + "x": 8, + "y": 2 } }, { - "type": "LARGE", "base": { - "x": 21, - "y": 0 + "x": 16, + "y": 47 + }, + "size": { + "x": 8, + "y": 2 } }, { - "type": "LARGE", "base": { - "x": 21, + "x": 24, + "y": 47 + }, + "size": { + "x": 8, "y": 2 } - } - ] - }, - { - "map": "media/map/heat-dng.png", - "tileCountX": 32, - "autotiles": [ + }, { - "type": "DEFAULT", "base": { - "x": 1, - "y": 0 + "x": 9, + "y": 49 + }, + "size": { + "x": 8, + "y": 2 } }, { - "type": "LARGE", "base": { - "x": 1, + "x": 24, + "y": 49 + }, + "size": { + "x": 8, "y": 2 } }, { - "type": "DEFAULT", "base": { - "x": 9, - "y": 0 + "x": 24, + "y": 49 + }, + "size": { + "x": 8, + "y": 2 } }, { - "type": "DEFAULT", "base": { - "x": 17, - "y": 0 + "x": 28, + "y": 51 + }, + "size": { + "x": 4, + "y": 4 } } ] }, { - "map": "media/map/cold-dng.png", + "map": "media/map/arid-interior.png", "tileCountX": 32, "autotiles": [ { - "type": "DEFAULT", - "base": { - "x": 0, - "y": 1 - } - }, - { - "type": "DEFAULT", - "base": { - "x": 12, - "y": 0 - } - }, - { - "type": "DEFAULT", - "base": { - "x": 12, - "y": 2 - } - }, - { - "type": "DEFAULT", "base": { - "x": 20, - "y": 0 - } - }, - { - "type": "DEFAULT", - "base": { - "x": 20, - "y": 2 + "x": 24, + "y": 19 + }, + "size": { + "x": 4, + "y": 4 } } ] From 971fda3cdd4ee2da92baf1017eefd95c3ef53680 Mon Sep 17 00:00:00 2001 From: cramerL Date: Fri, 26 Jul 2024 01:46:45 +0200 Subject: [PATCH 13/30] updated autotiles for most maps --- webapp/src/assets/autotiles.json | 2428 ++++++++++++++++++++++++++++-- 1 file changed, 2319 insertions(+), 109 deletions(-) diff --git a/webapp/src/assets/autotiles.json b/webapp/src/assets/autotiles.json index 4aadd502..d4b2f7ee 100644 --- a/webapp/src/assets/autotiles.json +++ b/webapp/src/assets/autotiles.json @@ -1,4 +1,132 @@ [ + { + "map": "media/map/arid.png", + "tileCountX": 32, + "autotiles": [ + { + "base": { + "x": 1, + "y": 0 + }, + "size": { + "x": 10, + "y": 2 + } + }, + { + "base": { + "x": 11, + "y": 0 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 13, + "y": 13 + }, + "size": { + "x": 4, + "y": 4 + } + } + ] + }, + { + "map": "media/map/arid-interior.png", + "tileCountX": 32, + "autotiles": [ + { + "base": { + "x": 1, + "y": 0 + }, + "size": { + "x": 10, + "y": 2 + } + }, + { + "base": { + "x": 11, + "y": 0 + }, + "size": { + "x": 10, + "y": 2 + } + }, + { + "base": { + "x": 24, + "y": 19 + }, + "size": { + "x": 4, + "y": 4 + } + }, + { + "base": { + "x": 24, + "y": 27 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 24, + "y": 29 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 24, + "y": 31 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 24, + "y": 33 + }, + "size": { + "x": 8, + "y": 2 + } + } + ] + }, + { + "map": "media/map/arid-shadow.png", + "tileCountX": 16, + "autotiles": [ + { + "base": { + "x": 0, + "y": 0 + }, + "size": { + "x": 14, + "y": 2 + } + } + ] + }, { "map": "media/map/autumn-outside.png", "tileCountX": 32, @@ -9,25 +137,2039 @@ "y": 1 }, "size": { - "x": 14, + "x": 14, + "y": 2 + }, + "cliff": false + }, + { + "base": { + "x": 12, + "y": 16 + }, + "size": { + "x": 4, + "y": 4 + } + }, + { + "base": { + "x": 8, + "y": 34 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 8, + "y": 36 + }, + "size": { + "x": 8, + "y": 2 + } + } + ] + }, + { + "map": "media/map/beach.png", + "tileCountX": 32, + "autotiles": [ + { + "base": { + "x": 0, + "y": 0 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 12, + "y": 0 + }, + "size": { + "x": 10, + "y": 2 + } + }, + { + "base": { + "x": 10, + "y": 2 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 0, + "y": 2 + }, + "size": { + "x": 10, + "y": 2 + } + }, + { + "base": { + "x": 4, + "y": 17 + }, + "size": { + "x": 10, + "y": 2 + } + }, + { + "base": { + "x": 2, + "y": 19 + }, + "size": { + "x": 10, + "y": 2 + } + }, + { + "base": { + "x": 2, + "y": 21 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 1, + "y": 25 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 6, + "y": 31 + }, + "size": { + "x": 4, + "y": 4 + } + }, + { + "base": { + "x": 0, + "y": 27 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 0, + "y": 29 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 8, + "y": 29 + }, + "size": { + "x": 8, + "y": 2 + } + } + ] + }, + { + "map": "media/map/bergen-trail.png", + "tileCountX": 32, + "autotiles": [ + { + "base": { + "x": 1, + "y": 0 + }, + "size": { + "x": 10, + "y": 2 + } + }, + { + "base": { + "x": 1, + "y": 2 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 11, + "y": 0 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 22, + "y": 0 + }, + "size": { + "x": 10, + "y": 2 + } + }, + { + "base": { + "x": 23, + "y": 5 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 23, + "y": 7 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 22, + "y": 9 + }, + "size": { + "x": 10, + "y": 2 + } + }, + { + "base": { + "x": 13, + "y": 13 + }, + "size": { + "x": 4, + "y": 4 + } + }, + { + "base": { + "x": 0, + "y": 40 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 0, + "y": 42 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 18, + "y": 36 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 18, + "y": 38 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 0, + "y": 56 + }, + "size": { + "x": 10, + "y": 2 + } + } + ] + }, + { + "map": "media/map/cave.png", + "tileCountX": 32, + "autotiles": [ + { + "base": { + "x": 0, + "y": 1 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 8, + "y": 1 + }, + "size": { + "x": 10, + "y": 2 + } + }, + { + "base": { + "x": 18, + "y": 1 + }, + "size": { + "x": 10, + "y": 2 + } + }, + { + "base": { + "x": 16, + "y": 19 + }, + "size": { + "x": 4, + "y": 4 + } + }, + { + "base": { + "x": 0, + "y": 32 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 0, + "y": 34 + }, + "size": { + "x": 8, + "y": 2 + } + } + ] + }, + { + "map": "media/map/cold-dng.png", + "tileCountX": 32, + "autotiles": [ + { + "base": { + "x": 0, + "y": 1 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 12, + "y": 0 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 12, + "y": 2 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 20, + "y": 0 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 20, + "y": 2 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 17, + "y": 19 + }, + "size": { + "x": 4, + "y": 4 + } + }, + { + "base": { + "x": 17, + "y": 23 + }, + "size": { + "x": 4, + "y": 4 + } + }, + { + "base": { + "x": 20, + "y": 27 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 21, + "y": 40 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 24, + "y": 47 + }, + "size": { + "x": 8, + "y": 2 + } + } + ] + }, + { + "map": "media/map/cold-dng-shadow.png", + "tileCountX": 16, + "autotiles": [ + { + "base": { + "x": 3, + "y": 8 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 3, + "y": 10 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 3, + "y": 12 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 3, + "y": 14 + }, + "size": { + "x": 8, + "y": 2 + } + } + ] + }, + { + "map": "media/map/dungeon-shadow.png", + "tileCountX": 16, + "autotiles": [ + { + "base": { + "x": 3, + "y": 8 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 3, + "y": 10 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 3, + "y": 12 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 3, + "y": 14 + }, + "size": { + "x": 8, + "y": 2 + } + } + ] + }, + { + "map": "media/map/evo-village.png", + "tileCountX": 32, + "autotiles": [ + { + "base": { + "x": 0, + "y": 1 + }, + "size": { + "x": 14, + "y": 2 + } + }, + { + "base": { + "x": 0, + "y": 3 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 18, + "y": 3 + }, + "size": { + "x": 4, + "y": 4 + } + }, + { + "base": { + "x": 0, + "y": 16 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 0, + "y": 22 + }, + "size": { + "x": 8, + "y": 2 + } + } + ] + }, + { + "map": "media/map/evo-village-interior.png", + "tileCountX": 32, + "autotiles": [ + { + "base": { + "x": 0, + "y": 5 + }, + "size": { + "x": 4, + "y": 4 + } + } + ] + }, + { + "map": "media/map/final-dungeon-inner.png", + "tileCountX": 32, + "autotiles": [ + { + "base": { + "x": 1, + "y": 0 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 9, + "y": 0 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 10, + "y": 11 + }, + "size": { + "x": 4, + "y": 4 + } + }, + { + "base": { + "x": 15, + "y": 11 + }, + "size": { + "x": 4, + "y": 4 + } + } + ] + }, + { + "map": "media/map/final-dungeon-outer.png", + "tileCountX": 32, + "autotiles": [ + { + "base": { + "x": 1, + "y": 0 + }, + "size": { + "x": 10, + "y": 2 + } + }, + { + "base": { + "x": 1, + "y": 2 + }, + "size": { + "x": 10, + "y": 2 + } + }, + { + "base": { + "x": 12, + "y": 0 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 12, + "y": 2 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 20, + "y": 0 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 20, + "y": 2 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 20, + "y": 4 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 12, + "y": 4 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 16, + "y": 9 + }, + "size": { + "x": 4, + "y": 4 + } + }, + { + "base": { + "x": 17, + "y": 26 + }, + "size": { + "x": 12, + "y": 2 + } + } + ] + }, + { + "map": "media/map/forest.png", + "tileCountX": 32, + "autotiles": [ + { + "base": { + "x": 1, + "y": 0 + }, + "size": { + "x": 10, + "y": 2 + } + }, + { + "base": { + "x": 11, + "y": 0 + }, + "size": { + "x": 10, + "y": 2 + } + }, + { + "base": { + "x": 21, + "y": 0 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 9, + "y": 2 + }, + "size": { + "x": 10, + "y": 2 + } + }, + { + "base": { + "x": 0, + "y": 50 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 0, + "y": 52 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 4, + "y": 54 + }, + "size": { + "x": 4, + "y": 4 + } + }, + { + "base": { + "x": 0, + "y": 62 + }, + "size": { + "x": 8, + "y": 2 + } + } + ] + }, + { + "map": "media/map/forest-dng.png", + "tileCountX": 32, + "autotiles": [ + { + "base": { + "x": 10, + "y": 11 + }, + "size": { + "x": 4, + "y": 4 + } + }, + { + "base": { + "x": 15, + "y": 11 + }, + "size": { + "x": 4, + "y": 4 + } + }, + { + "base": { + "x": 1, + "y": 0 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 9, + "y": 0 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 3, + "y": 2 + }, + "size": { + "x": 8, + "y": 2 + } + } + ] + }, + { + "map": "media/map/glass-parallax.png", + "tileCountX": 32, + "autotiles": [ + { + "base": { + "x": 20, + "y": 4 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 20, + "y": 6 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 20, + "y": 8 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 20, + "y": 10 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 0, + "y": 8 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 0, + "y": 10 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 0, + "y": 12 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 0, + "y": 14 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 12, + "y": 12 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 12, + "y": 14 + }, + "size": { + "x": 12, + "y": 2 + } + } + ] + }, + { + "map": "media/map/heat-area.png", + "tileCountX": 32, + "autotiles": [ + { + "base": { + "x": 1, + "y": 0 + }, + "size": { + "x": 10, + "y": 2 + } + }, + { + "base": { + "x": 1, + "y": 2 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 11, + "y": 0 + }, + "size": { + "x": 10, + "y": 2 + } + }, + { + "base": { + "x": 11, + "y": 2 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 21, + "y": 0 + }, + "size": { + "x": 10, + "y": 2 + } + }, + { + "base": { + "x": 21, + "y": 2 + }, + "size": { + "x": 10, + "y": 2 + } + }, + { + "base": { + "x": 24, + "y": 8 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 24, + "y": 36 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 24, + "y": 38 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 6, + "y": 38 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 6, + "y": 40 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 6, + "y": 42 + }, + "size": { + "x": 4, + "y": 4 + } + }, + { + "base": { + "x": 2, + "y": 46 + }, + "size": { + "x": 10, + "y": 2 + } + }, + { + "base": { + "x": 0, + "y": 50 + }, + "size": { + "x": 8, + "y": 2 + } + } + ] + }, + { + "map": "media/map/heat-area-hax.png", + "tileCountX": 32, + "autotiles": [ + { + "base": { + "x": 0, + "y": 3 + }, + "size": { + "x": 8, + "y": 2 + } + } + ] + }, + { + "map": "media/map/heat-dng.png", + "tileCountX": 32, + "autotiles": [ + { + "base": { + "x": 1, + "y": 0 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 17, + "y": 0 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 1, + "y": 2 + }, + "size": { + "x": 10, + "y": 2 + } + }, + { + "base": { + "x": 9, + "y": 0 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 11, + "y": 2 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 19, + "y": 2 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 10, + "y": 11 + }, + "size": { + "x": 4, + "y": 4 + } + }, + { + "base": { + "x": 15, + "y": 11 + }, + "size": { + "x": 4, + "y": 4 + } + }, + { + "base": { + "x": 6, + "y": 30 + }, + "size": { + "x": 10, + "y": 2 + } + }, + { + "base": { + "x": 0, + "y": 40 + }, + "size": { + "x": 8, + "y": 2 + } + } + ] + }, + { + "map": "media/map/heat-interior.png", + "tileCountX": 32, + "autotiles": [ + { + "base": { + "x": 11, + "y": 4 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 6, + "y": 5 + }, + "size": { + "x": 4, + "y": 4 + } + } + ] + }, + { + "map": "media/map/heat-interior-shadow.png", + "tileCountX": 16, + "autotiles": [ + { + "base": { + "x": 3, + "y": 8 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 3, + "y": 10 + }, + "size": { + "x": 8, + "y": 2 + } + } + ] + }, + { + "map": "media/map/jungle.png", + "tileCountX": 32, + "autotiles": [ + { + "base": { + "x": 6, + "y": 0 + }, + "size": { + "x": 14, + "y": 2 + } + }, + { + "base": { + "x": 20, + "y": 0 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 20, + "y": 11 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 20, + "y": 13 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 20, + "y": 15 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 20, + "y": 17 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 20, + "y": 19 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 20, + "y": 21 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 20, + "y": 23 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 20, + "y": 25 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 7, + "y": 25 + }, + "size": { + "x": 4, + "y": 4 + } + }, + { + "base": { + "x": 11, + "y": 32 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 16, + "y": 47 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 24, + "y": 47 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 9, + "y": 49 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 24, + "y": 49 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 24, + "y": 49 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 28, + "y": 51 + }, + "size": { + "x": 4, + "y": 4 + } + } + ] + }, + { + "map": "media/map/jungle-boss.png", + "tileCountX": 32, + "autotiles": [ + { + "base": { + "x": 0, + "y": 0 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 0, + "y": 2 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 0, + "y": 4 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 0, + "y": 6 + }, + "size": { + "x": 12, + "y": 2 + } + } + ] + }, + { + "map": "media/map/jungle-interior.png", + "tileCountX": 32, + "autotiles": [ + { + "base": { + "x": 3, + "y": 5 + }, + "size": { + "x": 8, + "y": 2 + } + } + ] + }, + { + "map": "media/map/jungle-interior-shadow.png", + "tileCountX": 16, + "autotiles": [ + { + "base": { + "x": 3, + "y": 8 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 3, + "y": 10 + }, + "size": { + "x": 8, + "y": 2 + } + } + ] + }, + { + "map": "media/map/jungle-shadow.png", + "tileCountX": 16, + "autotiles": [ + { + "base": { + "x": 0, + "y": 0 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 0, + "y": 4 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 0, + "y": 6 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 0, + "y": 8 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 0, + "y": 10 + }, + "size": { + "x": 8, + "y": 2 + } + } + ] + }, + { + "map": "media/map/lab.png", + "tileCountX": 32, + "autotiles": [ + { + "base": { + "x": 0, + "y": 1 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 12, + "y": 0 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 12, + "y": 2 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 20, + "y": 0 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 20, + "y": 2 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 0, + "y": 17 + }, + "size": { + "x": 4, + "y": 4 + } + }, + { + "base": { + "x": 11, + "y": 23 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 11, + "y": 25 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 11, + "y": 27 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 0, + "y": 34 + }, + "size": { + "x": 14, + "y": 2 + } + }, + { + "base": { + "x": 20, + "y": 45 + }, + "size": { + "x": 12, + "y": 2 + } + } + ] + }, + { + "map": "media/map/observatory-inner.png", + "tileCountX": 32, + "autotiles": [ + { + "base": { + "x": 1, + "y": 0 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 9, + "y": 0 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 17, + "y": 0 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 1, + "y": 2 + }, + "size": { + "x": 10, + "y": 2 + } + }, + { + "base": { + "x": 11, + "y": 2 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 19, + "y": 2 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 10, + "y": 11 + }, + "size": { + "x": 4, + "y": 4 + } + }, + { + "base": { + "x": 15, + "y": 11 + }, + "size": { + "x": 4, + "y": 4 + } + }, + { + "base": { + "x": 6, + "y": 30 + }, + "size": { + "x": 10, + "y": 2 + } + }, + { + "base": { + "x": 0, + "y": 40 + }, + "size": { + "x": 8, + "y": 2 + } + } + ] + }, + { + "map": "media/map/old-hideout.png", + "tileCountX": 32, + "autotiles": [ + { + "base": { + "x": 1, + "y": 0 + }, + "size": { + "x": 10, + "y": 2 + } + }, + { + "base": { + "x": 11, + "y": 0 + }, + "size": { + "x": 10, + "y": 2 + } + }, + { + "base": { + "x": 21, + "y": 0 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 9, + "y": 2 + }, + "size": { + "x": 10, + "y": 2 + } + } + ] + }, + { + "map": "media/map/old-hideout-shadows.png", + "tileCountX": 16, + "autotiles": [ + { + "base": { + "x": 3, + "y": 8 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 3, + "y": 10 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 3, + "y": 12 + }, + "size": { + "x": 8, + "y": 2 + } + }, + { + "base": { + "x": 3, + "y": 14 + }, + "size": { + "x": 8, + "y": 2 + } + } + ] + }, + { + "map": "media/map/old-hideout-shadows2.png", + "tileCountX": 16, + "autotiles": [ + { + "base": { + "x": 3, + "y": 8 + }, + "size": { + "x": 8, "y": 2 - }, - "cliff": false + } }, { "base": { - "x": 12, - "y": 16 + "x": 3, + "y": 10 }, "size": { - "x": 4, - "y": 4 + "x": 8, + "y": 2 } - }, + } + ] + }, + { + "map": "media/map/raid-boss.png", + "tileCountX": 32, + "autotiles": [ { "base": { - "x": 8, - "y": 34 + "x": 5, + "y": 5 }, "size": { "x": 8, @@ -36,8 +2178,8 @@ }, { "base": { - "x": 8, - "y": 36 + "x": 5, + "y": 7 }, "size": { "x": 8, @@ -47,33 +2189,39 @@ ] }, { - "map": "media/map/bergen-trail.png", + "map": "media/map/rh-interior.png", "tileCountX": 32, "autotiles": [ { "base": { - "x": 1, - "y": 0 + "x": 6, + "y": 5 }, "size": { - "x": 10, - "y": 2 + "x": 4, + "y": 4 } }, { "base": { - "x": 1, - "y": 2 + "x": 11, + "y": 4 }, "size": { "x": 8, "y": 2 } - }, + } + ] + }, + { + "map": "media/map/rh-interior-shadow.png", + "tileCountX": 16, + "autotiles": [ { "base": { - "x": 11, - "y": 0 + "x": 3, + "y": 8 }, "size": { "x": 8, @@ -82,18 +2230,24 @@ }, { "base": { - "x": 22, - "y": 0 + "x": 3, + "y": 10 }, "size": { - "x": 10, + "x": 8, "y": 2 } - }, + } + ] + }, + { + "map": "media/map/rhombus-dungeon.png", + "tileCountX": 32, + "autotiles": [ { "base": { - "x": 23, - "y": 5 + "x": 17, + "y": 0 }, "size": { "x": 8, @@ -102,28 +2256,34 @@ }, { "base": { - "x": 23, - "y": 7 + "x": 17, + "y": 2 }, "size": { "x": 8, "y": 2 } - }, + } + ] + }, + { + "map": "media/map/rhombus-outside.png", + "tileCountX": 32, + "autotiles": [ { "base": { - "x": 22, - "y": 9 + "x": 0, + "y": 0 }, "size": { - "x": 10, + "x": 12, "y": 2 } }, { "base": { "x": 13, - "y": 13 + "y": 15 }, "size": { "x": 4, @@ -133,17 +2293,17 @@ { "base": { "x": 0, - "y": 40 + "y": 19 }, "size": { - "x": 8, + "x": 10, "y": 2 } }, { "base": { - "x": 0, - "y": 42 + "x": 6, + "y": 32 }, "size": { "x": 8, @@ -152,28 +2312,24 @@ }, { "base": { - "x": 18, - "y": 36 - }, - "size": { - "x": 12, - "y": 2 - } - }, - { - "base": { - "x": 18, - "y": 38 + "x": 0, + "y": 40 }, "size": { - "x": 12, + "x": 8, "y": 2 } - }, + } + ] + }, + { + "map": "media/map/rombus-shadows.png", + "tileCountX": 16, + "autotiles": [ { "base": { "x": 0, - "y": 56 + "y": 0 }, "size": { "x": 10, @@ -183,113 +2339,119 @@ ] }, { - "map": "media/map/jungle.png", + "map": "media/map/rookie-harbor.png", "tileCountX": 32, "autotiles": [ { "base": { - "x": 6, + "x": 1, "y": 0 }, "size": { - "x": 14, + "x": 12, "y": 2 } }, { "base": { - "x": 20, + "x": 13, "y": 0 }, "size": { - "x": 10, - "y": 2 + "x": 4, + "y": 4 } }, { "base": { - "x": 20, - "y": 11 + "x": 17, + "y": 0 }, "size": { - "x": 12, - "y": 2 + "x": 4, + "y": 4 } }, { "base": { - "x": 20, - "y": 13 + "x": 1, + "y": 2 }, "size": { - "x": 12, + "x": 8, "y": 2 } }, { "base": { - "x": 20, - "y": 15 + "x": 14, + "y": 18 }, "size": { - "x": 12, - "y": 2 + "x": 4, + "y": 4 } }, { "base": { - "x": 20, - "y": 17 + "x": 11, + "y": 45 }, "size": { "x": 12, "y": 2 } - }, + } + ] + }, + { + "map": "media/map/shockwave-dng.png", + "tileCountX": 32, + "autotiles": [ { "base": { - "x": 20, - "y": 19 + "x": 1, + "y": 0 }, "size": { - "x": 12, + "x": 8, "y": 2 } }, { "base": { - "x": 20, - "y": 21 + "x": 9, + "y": 0 }, "size": { - "x": 12, + "x": 8, "y": 2 } }, { "base": { - "x": 20, - "y": 23 + "x": 3, + "y": 2 }, "size": { - "x": 12, + "x": 8, "y": 2 } }, { "base": { - "x": 20, - "y": 25 + "x": 10, + "y": 11 }, "size": { - "x": 12, - "y": 2 + "x": 4, + "y": 4 } }, { "base": { - "x": 7, - "y": 25 + "x": 15, + "y": 11 }, "size": { "x": 4, @@ -298,28 +2460,40 @@ }, { "base": { - "x": 11, - "y": 32 + "x": 10, + "y": 42 }, "size": { "x": 8, "y": 2 } - }, + } + ] + }, + { + "map": "media/map/tree-expo-space.png", + "tileCountX": 32, + "autotiles": [ { "base": { - "x": 16, - "y": 47 + "x": 0, + "y": 0 }, "size": { "x": 8, "y": 2 } - }, + } + ] + }, + { + "map": "media/map/tree-inner.png", + "tileCountX": 32, + "autotiles": [ { "base": { - "x": 24, - "y": 47 + "x": 1, + "y": 0 }, "size": { "x": 8, @@ -329,7 +2503,7 @@ { "base": { "x": 9, - "y": 49 + "y": 0 }, "size": { "x": 8, @@ -338,8 +2512,8 @@ }, { "base": { - "x": 24, - "y": 49 + "x": 3, + "y": 2 }, "size": { "x": 8, @@ -348,18 +2522,18 @@ }, { "base": { - "x": 24, - "y": 49 + "x": 10, + "y": 11 }, "size": { - "x": 8, - "y": 2 + "x": 4, + "y": 4 } }, { "base": { - "x": 28, - "y": 51 + "x": 15, + "y": 11 }, "size": { "x": 4, @@ -369,13 +2543,49 @@ ] }, { - "map": "media/map/arid-interior.png", + "map": "media/map/underwater.png", "tileCountX": 32, "autotiles": [ { "base": { - "x": 24, - "y": 19 + "x": 0, + "y": 10 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 0, + "y": 12 + }, + "size": { + "x": 12, + "y": 2 + } + }, + { + "base": { + "x": 0, + "y": 14 + }, + "size": { + "x": 12, + "y": 2 + } + } + ] + }, + { + "map": "media/map/unknown-interior.png", + "tileCountX": 32, + "autotiles": [ + { + "base": { + "x": 0, + "y": 12 }, "size": { "x": 4, From e537a01bda75a03d9471319dc7bc5540e3f1cf10 Mon Sep 17 00:00:00 2001 From: cramerL Date: Fri, 26 Jul 2024 01:48:51 +0200 Subject: [PATCH 14/30] fixed autotile error if tileset config doesn't exist for specific tileset --- .gitignore | 2 +- .../src/app/services/autotile/autotile.constants.ts | 13 +------------ webapp/src/app/services/autotile/gfx-mapper.ts | 8 +++++--- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 1e31f171..b163494a 100644 --- a/.gitignore +++ b/.gitignore @@ -44,4 +44,4 @@ yarn-error.log Thumbs.db # scripts -*.bat \ No newline at end of file +*.bat diff --git a/webapp/src/app/services/autotile/autotile.constants.ts b/webapp/src/app/services/autotile/autotile.constants.ts index 94e8af64..30248d57 100644 --- a/webapp/src/app/services/autotile/autotile.constants.ts +++ b/webapp/src/app/services/autotile/autotile.constants.ts @@ -2,7 +2,7 @@ import { Point } from '../../models/cross-code-map'; import { Helper } from '../phaser/helper'; /** Known autotile sizes */ -export type AutotileType = '4x1' | '4x4' | '8x2' | '10x2' | '12x2' | '14x2'; +export type AutotileType = '4x4' | '8x2' | '10x2' | '12x2' | '14x2'; export interface AutotileConfig { tileCountX: number; @@ -101,16 +101,6 @@ fillType14x2.OXXO.push({x: 13, y: 0}); fillType14x2.XXOO.push({x: 12, y: 1}); fillType14x2.XOOX.push({x: 13, y: 1}); -const fillType4x1 = Helper.copy(empty); -for (const value of Object.values(fillType4x1) as Point[][]) { - value.push({x: 0, y: 0}); - value.push({x: 0, y: 0}); - value.push({x: 0, y: 0}); - value.push({x: 1, y: 0}); - value.push({x: 2, y: 0}); - value.push({x: 3, y: 0}); -} - const fillType4x4: FillType = { // order is important for same offset. Last one is used for reverse mapping OOOO: [{x: 2, y: 2}], @@ -139,7 +129,6 @@ const fillType4x4: FillType = { export const FILL_TYPE: { [key in AutotileType]: FillType } = { - '4x1': fillType4x1, '4x4': fillType4x4, '8x2': fillType8x2, '10x2': fillType10x2, diff --git a/webapp/src/app/services/autotile/gfx-mapper.ts b/webapp/src/app/services/autotile/gfx-mapper.ts index df3824c7..a9608e2a 100644 --- a/webapp/src/app/services/autotile/gfx-mapper.ts +++ b/webapp/src/app/services/autotile/gfx-mapper.ts @@ -24,7 +24,7 @@ export class GfxMapper { } = {}; - private TILESET_CONFIG: { [key: string]: ChipsetConfig } = {}; + private TILESET_CONFIG: Record = {}; private mapping: { [key in AutotileType]: Map } = {}; private cliffBorderMapping = new Map(); @@ -67,8 +67,10 @@ export class GfxMapper { const generatedType: AutotileType = `${autotile.size.x}x${autotile.size.y}` as AutotileType; const tileset = this.TILESET_CONFIG[config.map]; - const terrains = tileset.terrains ?? []; - terrains.push(tileset.base); + const terrains = tileset?.terrains ?? []; + if (tileset) { + terrains.push(tileset.base); + } let cliff = autotile.cliff; if (cliff === undefined) { for (const terrain of terrains) { From ffa902b12eda5962e189df587cfcce3f77847815 Mon Sep 17 00:00:00 2001 From: cramerL Date: Fri, 26 Jul 2024 02:35:25 +0200 Subject: [PATCH 15/30] actions.json and events.json now loaded through json-loader --- .../app/components/editor/editor.component.ts | 11 +++++++- .../event-editor/add/add-event.service.ts | 27 +++++++++++-------- .../event-editor/event-helper.service.ts | 6 +++-- .../event-registry/default-event.ts | 18 +++++++++---- .../event-registry/event-registry.service.ts | 8 +++++- .../src/app/services/json-loader.service.ts | 14 +++++++++- webapp/src/app/services/map-loader.service.ts | 3 ++- .../phaser/entities/entity-manager.ts | 5 +++- webapp/src/app/services/save.service.ts | 2 +- webapp/src/styles.scss | 14 ++++++++++ 10 files changed, 84 insertions(+), 24 deletions(-) diff --git a/webapp/src/app/components/editor/editor.component.ts b/webapp/src/app/components/editor/editor.component.ts index 8f6377a6..01bfb5d3 100644 --- a/webapp/src/app/components/editor/editor.component.ts +++ b/webapp/src/app/components/editor/editor.component.ts @@ -3,6 +3,7 @@ import { MatSidenav } from '@angular/material/sidenav'; import { AddEntityMenuService } from '../../services/add-entity-menu.service'; import { LoadMapComponent } from '../dialogs/load-map/load-map.component'; +import { JsonLoaderService } from '../../services/json-loader.service'; @Component({ selector: 'app-editor', @@ -16,8 +17,16 @@ export class EditorComponent { @ViewChild('sidenavLoadMap', {static: true}) sidenavLoadMap!: MatSidenav; - constructor(addEntity: AddEntityMenuService) { + constructor( + addEntity: AddEntityMenuService, + jsonLoader: JsonLoaderService + ) { addEntity.init(); + + // makes sure they are synchronously available + jsonLoader.loadJsonMerged('actions.json'); + jsonLoader.loadJsonMerged('events.json'); + } loadMapClicked() { diff --git a/webapp/src/app/components/widgets/event-widget/event-editor/add/add-event.service.ts b/webapp/src/app/components/widgets/event-widget/event-editor/add/add-event.service.ts index 994d0624..3da44509 100644 --- a/webapp/src/app/components/widgets/event-widget/event-editor/add/add-event.service.ts +++ b/webapp/src/app/components/widgets/event-widget/event-editor/add/add-event.service.ts @@ -3,15 +3,13 @@ import { Injectable } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; import { Observable, Subject } from 'rxjs'; -import actions from '../../../../../../assets/actions.json'; -import events from '../../../../../../assets/events.json'; -import { - ListSearchOverlayComponent -} from '../../../../dialogs/list-search-overlay/list-search-overlay.component'; +import { ListSearchOverlayComponent } from '../../../../dialogs/list-search-overlay/list-search-overlay.component'; import { OverlayRefControl } from '../../../../dialogs/overlay/overlay-ref-control'; import { OverlayService } from '../../../../dialogs/overlay/overlay.service'; import { AbstractEvent } from '../../event-registry/abstract-event'; import { EventRegistryService } from '../../event-registry/event-registry.service'; +import { JsonLoaderService } from '../../../../../services/json-loader.service'; +import { ActionsJson, EventsJson } from '../../event-registry/default-event'; @Injectable({ providedIn: 'root' @@ -20,8 +18,8 @@ export class AddEventService { private selectedEvent = new Subject>(); private actionStep = false; - events: string[]; - actions: string[]; + events: string[] = []; + actions: string[] = []; private ref?: OverlayRefControl; @@ -29,11 +27,18 @@ export class AddEventService { private eventRegistry: EventRegistryService, private overlayService: OverlayService, private overlay: Overlay, - private domSanitizer: DomSanitizer + private domSanitizer: DomSanitizer, + private jsonLoader: JsonLoaderService, ) { - const registry = Object.keys(this.eventRegistry.getAll()); - const eventNames = Object.keys(events); + this.init(); + } + + private async init() { + const events = await this.jsonLoader.loadJsonMerged('events.json'); + const actions = await this.jsonLoader.loadJsonMerged('actions.json'); + const eventNames = Object.keys(events); + const registry = Object.keys(this.eventRegistry.getAll()); const eventSet = new Set([...registry, ...eventNames]); this.events = Array.from(eventSet); @@ -77,7 +82,7 @@ export class AddEventService { select(event: string) { const clss = this.eventRegistry.getEvent(event); - const instance: AbstractEvent = new clss(this.domSanitizer, {type: event}, this.actionStep); + const instance: AbstractEvent = new clss(this.domSanitizer, {type: event}, this.actionStep, this.jsonLoader); instance.generateNewData(); instance.update(); this.selectedEvent.next(instance); diff --git a/webapp/src/app/components/widgets/event-widget/event-editor/event-helper.service.ts b/webapp/src/app/components/widgets/event-widget/event-editor/event-helper.service.ts index 8133437c..b9aad6cf 100644 --- a/webapp/src/app/components/widgets/event-widget/event-editor/event-helper.service.ts +++ b/webapp/src/app/components/widgets/event-widget/event-editor/event-helper.service.ts @@ -3,6 +3,7 @@ import { DomSanitizer } from '@angular/platform-browser'; import { BehaviorSubject } from 'rxjs'; import { AbstractEvent, EventType } from '../event-registry/abstract-event'; import { EventRegistryService } from '../event-registry/event-registry.service'; +import { JsonLoaderService } from '../../../../services/json-loader.service'; @Injectable({ providedIn: 'root' @@ -13,13 +14,14 @@ export class EventHelperService { constructor( private eventRegistry: EventRegistryService, - private domSanitizer: DomSanitizer + private domSanitizer: DomSanitizer, + private jsonLoader: JsonLoaderService ) { } public getEventFromType(val: EventType, actionStep: boolean): AbstractEvent { const eventClass = this.eventRegistry.getEvent(val.type); - const instance: AbstractEvent = new eventClass(this.domSanitizer, val, actionStep); + const instance: AbstractEvent = new eventClass(this.domSanitizer, val, actionStep, this.jsonLoader); if (val.type === 'IF') { const valIf = val as any; diff --git a/webapp/src/app/components/widgets/event-widget/event-registry/default-event.ts b/webapp/src/app/components/widgets/event-widget/event-registry/default-event.ts index dc17298e..83117949 100644 --- a/webapp/src/app/components/widgets/event-widget/event-registry/default-event.ts +++ b/webapp/src/app/components/widgets/event-widget/event-registry/default-event.ts @@ -1,8 +1,15 @@ import { DomSanitizer } from '@angular/platform-browser'; -import actions from '../../../../../assets/actions.json'; -import events from '../../../../../assets/events.json'; import { AttributeValue, EntityAttributes } from '../../../../services/phaser/entities/cc-entity'; import { AbstractEvent, EventType } from './abstract-event'; +import { JsonLoaderService } from '../../../../services/json-loader.service'; + +export interface ActionsJson { + [key: string]: JsonEventType; +} + +export interface EventsJson { + [key: string]: JsonEventType; +} interface DefaultEventData extends EventType { [key: string]: any; @@ -20,13 +27,14 @@ export class DefaultEvent extends Abstra constructor( domSanitizer: DomSanitizer, data: T, - actionStep = false + actionStep = false, + jsonLoader: JsonLoaderService ) { super(domSanitizer, data, actionStep); if (actionStep) { - this.type = actions[this.data.type]; + this.type = jsonLoader.loadJsonMergedSync('actions.json')[this.data.type]; } else { - this.type = events[this.data.type]; + this.type = jsonLoader.loadJsonMergedSync('events.json')[this.data.type]; } } diff --git a/webapp/src/app/components/widgets/event-widget/event-registry/event-registry.service.ts b/webapp/src/app/components/widgets/event-widget/event-registry/event-registry.service.ts index 0a244c28..5f96b124 100644 --- a/webapp/src/app/components/widgets/event-widget/event-registry/event-registry.service.ts +++ b/webapp/src/app/components/widgets/event-widget/event-registry/event-registry.service.ts @@ -23,8 +23,14 @@ import { Wait } from './wait'; import { ShowSideMsg } from './show-side-msg'; import { ShowModalChoice } from './show-modal-choice'; import { SetMsgExpression } from './set-msg-expression'; +import { JsonLoaderService } from '../../../../services/json-loader.service'; -type EventConstructor = new (domSanitizer: DomSanitizer, data: T, actionStep: boolean) => AbstractEvent; +type EventConstructor = new ( + domSanitizer: DomSanitizer, + data: T, + actionStep: boolean, + jsonLoader: JsonLoaderService +) => AbstractEvent; @Injectable({ providedIn: 'root' diff --git a/webapp/src/app/services/json-loader.service.ts b/webapp/src/app/services/json-loader.service.ts index 037bcad5..e5fe339a 100644 --- a/webapp/src/app/services/json-loader.service.ts +++ b/webapp/src/app/services/json-loader.service.ts @@ -36,7 +36,11 @@ export class JsonLoaderService { newConfig.file = JSON.parse(config.file); } catch (e) { console.error(e); - this.snackbar.open(`Failed to parse mod config: ${config.mod}/map-editor/${config.filename}`, 'close'); + this.snackbar.open( + `Failed to parse mod config: ${config.mod}/map-editor/${config.filename}`, + 'close', + {panelClass: 'snackbar-error'} + ); continue; } const configs = this.configs.get(config.filename) ?? []; @@ -67,4 +71,12 @@ export class JsonLoaderService { this.cache[file] = base; return base; } + + loadJsonMergedSync(file: string): T { + const cached = this.cache[file] as T | undefined; + if (cached) { + return cached; + } + throw new Error('Tried to get json synchronous, but its not loaded'); + } } diff --git a/webapp/src/app/services/map-loader.service.ts b/webapp/src/app/services/map-loader.service.ts index 1e9f8c9b..989face2 100644 --- a/webapp/src/app/services/map-loader.service.ts +++ b/webapp/src/app/services/map-loader.service.ts @@ -49,7 +49,8 @@ export class MapLoaderService { } catch (e: any) { console.error(e); this.snackBar.open('Error: ' + e.message, undefined, { - duration: 2500 + duration: 2500, + panelClass: 'snackbar-error' }); return; } diff --git a/webapp/src/app/services/phaser/entities/entity-manager.ts b/webapp/src/app/services/phaser/entities/entity-manager.ts index d3d3384d..4ac7d0de 100644 --- a/webapp/src/app/services/phaser/entities/entity-manager.ts +++ b/webapp/src/app/services/phaser/entities/entity-manager.ts @@ -400,7 +400,10 @@ export class EntityManager extends BaseObject { } entities = (parsed as any[]).filter(v => this.isMapEntity(v)); } catch (e) { - Globals.snackbar.open('could not parse entities from clipboard', undefined, {duration: 2000}); + Globals.snackbar.open('could not parse entities from clipboard', undefined, { + duration: 2000, + panelClass: 'snackbar-error' + }); return; } if (entities.length === 0) { diff --git a/webapp/src/app/services/save.service.ts b/webapp/src/app/services/save.service.ts index e376f73e..c8dbd03b 100644 --- a/webapp/src/app/services/save.service.ts +++ b/webapp/src/app/services/save.service.ts @@ -51,7 +51,7 @@ export class SaveService { this.snackbar.open('successfully saved map', 'ok', { duration: 3000 }); }, error: err => { console.error(err); - this.snackbar.open('failed to save map', 'ok'); + this.snackbar.open('failed to save map', 'ok', {panelClass: 'snackbar-error'}); } }); } diff --git a/webapp/src/styles.scss b/webapp/src/styles.scss index 94b9a875..dc526705 100644 --- a/webapp/src/styles.scss +++ b/webapp/src/styles.scss @@ -192,3 +192,17 @@ body { .newline-tooltip { white-space: pre-line; } + +.snackbar-error { + > * { + background: mat.get-color-from-palette(mat.$red-palette, 700) !important; + } + + button { + color: white !important; + } + + .mat-mdc-snack-bar-label { + color: white !important; + } +} From d744068415eda3ed2f0714a6cea485ff6e05e21e Mon Sep 17 00:00:00 2001 From: cramerL Date: Fri, 26 Jul 2024 15:39:07 +0200 Subject: [PATCH 16/30] entities.json now loaded through json-loader --- .../app/components/phaser/phaser.component.ts | 5 ++++- .../event-editor/add/add-event.service.ts | 2 +- .../event-editor/event-helper.service.ts | 2 +- .../event-widget/event-registry/default-event.ts | 4 ++-- .../event-registry/event-registry.service.ts | 4 +--- .../src/app/services/add-entity-menu.service.ts | 9 ++++++--- webapp/src/app/services/autotile/gfx-mapper.ts | 1 - webapp/src/app/services/globals.ts | 3 ++- .../phaser/entities/registry/default-entity.ts | 15 +++++++++++++-- webapp/src/assets/autotiles.json | 16 ---------------- 10 files changed, 30 insertions(+), 31 deletions(-) diff --git a/webapp/src/app/components/phaser/phaser.component.ts b/webapp/src/app/components/phaser/phaser.component.ts index b98a69ef..9faa1f1d 100644 --- a/webapp/src/app/components/phaser/phaser.component.ts +++ b/webapp/src/app/components/phaser/phaser.component.ts @@ -13,6 +13,7 @@ import { MainScene } from '../../services/phaser/main-scene'; import { PhaserEventsService } from '../../services/phaser/phaser-events.service'; import { SettingsService } from '../../services/settings.service'; import { StateHistoryService } from '../dialogs/floating-window/history/state-history.service'; +import { JsonLoaderService } from '../../services/json-loader.service'; @Component({ selector: 'app-phaser', @@ -34,7 +35,8 @@ export class PhaserComponent implements AfterViewInit { snackbar: MatSnackBar, registry: EntityRegistryService, autotile: AutotileService, - settingsService: SettingsService + settingsService: SettingsService, + jsonLoader: JsonLoaderService, ) { Globals.stateHistoryService = stateHistory; Globals.mapLoaderService = mapLoader; @@ -45,6 +47,7 @@ export class PhaserComponent implements AfterViewInit { Globals.httpService = http; Globals.snackbar = snackbar; Globals.settingsService = settingsService; + Globals.jsonLoader = jsonLoader; } diff --git a/webapp/src/app/components/widgets/event-widget/event-editor/add/add-event.service.ts b/webapp/src/app/components/widgets/event-widget/event-editor/add/add-event.service.ts index 3da44509..1a0d1f60 100644 --- a/webapp/src/app/components/widgets/event-widget/event-editor/add/add-event.service.ts +++ b/webapp/src/app/components/widgets/event-widget/event-editor/add/add-event.service.ts @@ -82,7 +82,7 @@ export class AddEventService { select(event: string) { const clss = this.eventRegistry.getEvent(event); - const instance: AbstractEvent = new clss(this.domSanitizer, {type: event}, this.actionStep, this.jsonLoader); + const instance: AbstractEvent = new clss(this.domSanitizer, {type: event}, this.actionStep); instance.generateNewData(); instance.update(); this.selectedEvent.next(instance); diff --git a/webapp/src/app/components/widgets/event-widget/event-editor/event-helper.service.ts b/webapp/src/app/components/widgets/event-widget/event-editor/event-helper.service.ts index b9aad6cf..03646eb1 100644 --- a/webapp/src/app/components/widgets/event-widget/event-editor/event-helper.service.ts +++ b/webapp/src/app/components/widgets/event-widget/event-editor/event-helper.service.ts @@ -21,7 +21,7 @@ export class EventHelperService { public getEventFromType(val: EventType, actionStep: boolean): AbstractEvent { const eventClass = this.eventRegistry.getEvent(val.type); - const instance: AbstractEvent = new eventClass(this.domSanitizer, val, actionStep, this.jsonLoader); + const instance: AbstractEvent = new eventClass(this.domSanitizer, val, actionStep); if (val.type === 'IF') { const valIf = val as any; diff --git a/webapp/src/app/components/widgets/event-widget/event-registry/default-event.ts b/webapp/src/app/components/widgets/event-widget/event-registry/default-event.ts index 83117949..c504c7a2 100644 --- a/webapp/src/app/components/widgets/event-widget/event-registry/default-event.ts +++ b/webapp/src/app/components/widgets/event-widget/event-registry/default-event.ts @@ -1,7 +1,7 @@ import { DomSanitizer } from '@angular/platform-browser'; import { AttributeValue, EntityAttributes } from '../../../../services/phaser/entities/cc-entity'; import { AbstractEvent, EventType } from './abstract-event'; -import { JsonLoaderService } from '../../../../services/json-loader.service'; +import { Globals } from '../../../../services/globals'; export interface ActionsJson { [key: string]: JsonEventType; @@ -28,9 +28,9 @@ export class DefaultEvent extends Abstra domSanitizer: DomSanitizer, data: T, actionStep = false, - jsonLoader: JsonLoaderService ) { super(domSanitizer, data, actionStep); + const jsonLoader = Globals.jsonLoader; if (actionStep) { this.type = jsonLoader.loadJsonMergedSync('actions.json')[this.data.type]; } else { diff --git a/webapp/src/app/components/widgets/event-widget/event-registry/event-registry.service.ts b/webapp/src/app/components/widgets/event-widget/event-registry/event-registry.service.ts index 5f96b124..49a816af 100644 --- a/webapp/src/app/components/widgets/event-widget/event-registry/event-registry.service.ts +++ b/webapp/src/app/components/widgets/event-widget/event-registry/event-registry.service.ts @@ -23,13 +23,11 @@ import { Wait } from './wait'; import { ShowSideMsg } from './show-side-msg'; import { ShowModalChoice } from './show-modal-choice'; import { SetMsgExpression } from './set-msg-expression'; -import { JsonLoaderService } from '../../../../services/json-loader.service'; type EventConstructor = new ( domSanitizer: DomSanitizer, data: T, - actionStep: boolean, - jsonLoader: JsonLoaderService + actionStep: boolean ) => AbstractEvent; @Injectable({ diff --git a/webapp/src/app/services/add-entity-menu.service.ts b/webapp/src/app/services/add-entity-menu.service.ts index 63f46129..4da63465 100644 --- a/webapp/src/app/services/add-entity-menu.service.ts +++ b/webapp/src/app/services/add-entity-menu.service.ts @@ -1,7 +1,6 @@ import { Overlay } from '@angular/cdk/overlay'; import { Injectable } from '@angular/core'; -import entities from '../../assets/entities.json'; import { ListSearchOverlayComponent } from '../components/dialogs/list-search-overlay/list-search-overlay.component'; import { OverlayRefControl } from '../components/dialogs/overlay/overlay-ref-control'; import { OverlayService } from '../components/dialogs/overlay/overlay.service'; @@ -9,6 +8,8 @@ import { MapEntity, Point } from '../models/cross-code-map'; import { GlobalEventsService } from './global-events.service'; import { EntityRegistryService } from './phaser/entities/registry/entity-registry.service'; import { Vec2 } from './phaser/vec2'; +import { JsonLoaderService } from './json-loader.service'; +import { EntitiesJson } from './phaser/entities/registry/default-entity'; @Injectable({ providedIn: 'root' @@ -26,11 +27,13 @@ export class AddEntityMenuService { private events: GlobalEventsService, private overlayService: OverlayService, private overlay: Overlay, - private entityRegistry: EntityRegistryService + private entityRegistry: EntityRegistryService, + private jsonLoader: JsonLoaderService, ) { } - public init() { + public async init() { + const entities = await this.jsonLoader.loadJsonMerged('entities.json'); const registry = Object.keys(this.entityRegistry.getAll()); const entityNames = Object.keys(entities); diff --git a/webapp/src/app/services/autotile/gfx-mapper.ts b/webapp/src/app/services/autotile/gfx-mapper.ts index a9608e2a..ca30a971 100644 --- a/webapp/src/app/services/autotile/gfx-mapper.ts +++ b/webapp/src/app/services/autotile/gfx-mapper.ts @@ -3,7 +3,6 @@ import { ChipsetConfig } from '../height-map/gfx-mapper/gfx-mapper.constants'; import { Helper } from '../phaser/helper'; import { Vec2 } from '../phaser/vec2'; import { AutotileConfig, AutotileType, FILL_TYPE, FILL_TYPE_CLIFF, FILL_TYPE_CLIFF_ALT, FILL_TYPE_CLIFF_BORDER, FillType } from './autotile.constants'; - import { JsonLoaderService } from '../json-loader.service'; interface JsonType { diff --git a/webapp/src/app/services/globals.ts b/webapp/src/app/services/globals.ts index 73393a92..bbaa9dd0 100644 --- a/webapp/src/app/services/globals.ts +++ b/webapp/src/app/services/globals.ts @@ -10,6 +10,7 @@ import { MatSnackBar } from '@angular/material/snack-bar'; import { SettingsService } from './settings.service'; import { signal } from '@angular/core'; import { GridSettings } from '../components/toolbar/grid-menu/grid-menu.component'; +import { JsonLoaderService } from './json-loader.service'; export class Globals { static isElectron = false; @@ -27,7 +28,6 @@ export class Globals { }); static disablePhaserInput = new Set(); - // TODO: remove them from global state static stateHistoryService: StateHistoryService; static mapLoaderService: MapLoaderService; static globalEventsService: GlobalEventsService; @@ -37,4 +37,5 @@ export class Globals { static httpService: HttpClientService; static settingsService: SettingsService; static snackbar: MatSnackBar; + static jsonLoader: JsonLoaderService; } diff --git a/webapp/src/app/services/phaser/entities/registry/default-entity.ts b/webapp/src/app/services/phaser/entities/registry/default-entity.ts index 70a0b3e7..3a3f597d 100644 --- a/webapp/src/app/services/phaser/entities/registry/default-entity.ts +++ b/webapp/src/app/services/phaser/entities/registry/default-entity.ts @@ -1,6 +1,10 @@ -import entities from '../../../../../assets/entities.json'; import { CCMap } from '../../tilemap/cc-map'; import { CCEntity, EntityAttributes, ScaleSettings } from '../cc-entity'; +import { Globals } from '../../../globals'; + +export interface EntitiesJson { + [key: string]: JsonEntityType; +} interface JsonEntityType { attributes: EntityAttributes; @@ -37,8 +41,15 @@ export class DefaultEntity extends CCEntity { } }; - constructor(scene: Phaser.Scene, map: CCMap, x: number, y: number, private typeName: string) { + constructor( + scene: Phaser.Scene, + map: CCMap, + x: number, + y: number, + private typeName: string + ) { super(scene, map, x, y, typeName); + const entities = Globals.jsonLoader.loadJsonMergedSync('entities.json'); this.typeDef = entities[typeName]; } diff --git a/webapp/src/assets/autotiles.json b/webapp/src/assets/autotiles.json index d4b2f7ee..37e56c31 100644 --- a/webapp/src/assets/autotiles.json +++ b/webapp/src/assets/autotiles.json @@ -111,22 +111,6 @@ } ] }, - { - "map": "media/map/arid-shadow.png", - "tileCountX": 16, - "autotiles": [ - { - "base": { - "x": 0, - "y": 0 - }, - "size": { - "x": 14, - "y": 2 - } - } - ] - }, { "map": "media/map/autumn-outside.png", "tileCountX": 32, From d3df072b87ef2d566a6f8b5e772fdd81ae431e63 Mon Sep 17 00:00:00 2001 From: cramerL Date: Fri, 26 Jul 2024 16:23:03 +0200 Subject: [PATCH 17/30] map-styles.json now loaded through json-loader --- .../map-settings/map-settings.component.ts | 2 ++ .../src/app/components/editor/editor.component.ts | 1 + webapp/src/app/models/map-styles.ts | 7 +++---- webapp/src/app/services/global-events.service.ts | 1 + .../services/phaser/entities/entity-manager.ts | 15 +++++++++++++++ webapp/src/app/services/phaser/helper.ts | 4 ++-- 6 files changed, 24 insertions(+), 6 deletions(-) diff --git a/webapp/src/app/components/dialogs/map-settings/map-settings.component.ts b/webapp/src/app/components/dialogs/map-settings/map-settings.component.ts index 1e669b64..829db54d 100644 --- a/webapp/src/app/components/dialogs/map-settings/map-settings.component.ts +++ b/webapp/src/app/components/dialogs/map-settings/map-settings.component.ts @@ -63,6 +63,8 @@ export class MapSettingsComponent { y: settings.mapHeight }); + this.events.updateEntities.next(true); + this.ref.close(); } } diff --git a/webapp/src/app/components/editor/editor.component.ts b/webapp/src/app/components/editor/editor.component.ts index 01bfb5d3..524854b2 100644 --- a/webapp/src/app/components/editor/editor.component.ts +++ b/webapp/src/app/components/editor/editor.component.ts @@ -26,6 +26,7 @@ export class EditorComponent { // makes sure they are synchronously available jsonLoader.loadJsonMerged('actions.json'); jsonLoader.loadJsonMerged('events.json'); + jsonLoader.loadJsonMerged('map-styles.json'); } diff --git a/webapp/src/app/models/map-styles.ts b/webapp/src/app/models/map-styles.ts index 6bbfd0fc..765b2211 100644 --- a/webapp/src/app/models/map-styles.ts +++ b/webapp/src/app/models/map-styles.ts @@ -1,12 +1,11 @@ -export type MapStyles = { - walls: WallColors; -} & { +export interface MapStyles { [key: string]: MapStyle; -}; +} export type MapStyle = { sheet: string; hasDoorMat?: boolean; + walls?: WallColors; } & { [key: string]: any; }; diff --git a/webapp/src/app/services/global-events.service.ts b/webapp/src/app/services/global-events.service.ts index de06baf3..704cbcdf 100644 --- a/webapp/src/app/services/global-events.service.ts +++ b/webapp/src/app/services/global-events.service.ts @@ -22,6 +22,7 @@ export class GlobalEventsService { offsetEntities = new Subject(); toggleVisibility = new Subject(); showAddEntityMenu = new Subject(); + updateEntities = new Subject(); updateCoords = new Subject(); updateTileSelectionSize = new Subject(); diff --git a/webapp/src/app/services/phaser/entities/entity-manager.ts b/webapp/src/app/services/phaser/entities/entity-manager.ts index 4ac7d0de..56108751 100644 --- a/webapp/src/app/services/phaser/entities/entity-manager.ts +++ b/webapp/src/app/services/phaser/entities/entity-manager.ts @@ -83,6 +83,8 @@ export class EntityManager extends BaseObject { this.visibilityKey = keyboard!.addKey(keyCodes.R, false); this.selectionBox = new SelectionBox(this.scene); + + Globals.globalEventsService.updateEntities.subscribe(() => this.resetEntities()); } @@ -432,6 +434,19 @@ export class EntityManager extends BaseObject { } + private async resetEntities() { + if (!this.map) { + return; + } + const exportedMap = this.map.exportMap(); + await this.initialize(exportedMap, this.map); + if (this.active) { + for (const entity of this.entities) { + entity.setActive(true); + } + } + } + deleteSelectedEntities() { this.skipEdit = true; const saveHistory = this.selectedEntities.length > 0; diff --git a/webapp/src/app/services/phaser/helper.ts b/webapp/src/app/services/phaser/helper.ts index 564b4b64..3d854905 100644 --- a/webapp/src/app/services/phaser/helper.ts +++ b/webapp/src/app/services/phaser/helper.ts @@ -1,4 +1,3 @@ -import mapStyles from '../../../../../webapp/src/assets/map-styles.json'; import { Point } from '../../models/cross-code-map'; import { MapStyles } from '../../models/map-styles'; import { Globals } from '../globals'; @@ -151,12 +150,13 @@ export class Helper { } public static getMapStyle(map: CCMap, type: string): MapStyles { + const mapStyles = Globals.jsonLoader.loadJsonMergedSync('map-styles.json'); const mapStyleName = map.attributes.mapStyle || 'default'; const mapStyle = mapStyles[mapStyleName]; if (mapStyle && mapStyle[type]) { return mapStyle[type]; } - return mapStyles.default[type]; + return mapStyles['default'][type]; } public static async asyncFilter(arr: T[], predicate: (v: T) => Promise) { From 059c96a2cb7447d3e765ca9b67f6252af448257e Mon Sep 17 00:00:00 2001 From: cramerL Date: Fri, 26 Jul 2024 16:31:48 +0200 Subject: [PATCH 18/30] tilesets.json now loaded through json-loader --- .../services/height-map/height-map.service.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/webapp/src/app/services/height-map/height-map.service.ts b/webapp/src/app/services/height-map/height-map.service.ts index 8cec2161..4b198b6b 100644 --- a/webapp/src/app/services/height-map/height-map.service.ts +++ b/webapp/src/app/services/height-map/height-map.service.ts @@ -1,7 +1,6 @@ import { Injectable } from '@angular/core'; import { EventManager } from '@angular/platform-browser'; -import tilesets from '../../../assets/tilesets.json'; import { StateHistoryService } from '../../components/dialogs/floating-window/history/state-history.service'; import { AutotileService } from '../autotile/autotile.service'; import { GlobalEventsService } from '../global-events.service'; @@ -29,8 +28,11 @@ import { WallLink } from './heightmap.constants'; import { customPutTileAt } from '../phaser/tilemap/layer-helper'; +import { JsonLoaderService } from '../json-loader.service'; -const TILESET_CONFIG: { [key: string]: ChipsetConfig } = tilesets; +interface TilesetJson { + [key: string]: ChipsetConfig; +} interface TileData { level: number; @@ -53,6 +55,7 @@ export class HeightMapService { private maxLevel = 0; private width = 0; private height = 0; + private tilesetConfig: TilesetJson = {}; private c_wallProps = {start: 0, end: 0}; @@ -61,6 +64,7 @@ export class HeightMapService { private mapLoader: MapLoaderService, private stateHistory: StateHistoryService, private autotile: AutotileService, + private jsonLoader: JsonLoaderService, eventManager: EventManager ) { @@ -75,10 +79,12 @@ export class HeightMapService { }); } - public init() { + public async init() { this.events.generateHeights.subscribe(forceAll => this.generateHeights(forceAll)); this.mapLoader.tileMap.subscribe(map => this.onMapLoad(map)); + this.tilesetConfig = await this.jsonLoader.loadJsonMerged('tilesets.json'); + // TODO: add shortcuts for generation } @@ -202,7 +208,7 @@ export class HeightMapService { if (details.distance !== 1) { continue; } - if (details.type === 'Background' && TILESET_CONFIG[details.tilesetName] && lastLevel !== details.level) { + if (details.type === 'Background' && this.tilesetConfig[details.tilesetName] && lastLevel !== details.level) { lastLevel = details.level; this.applyOnBackground(layer, forceAll); } else if (details.type === 'Collision') { @@ -229,7 +235,7 @@ export class HeightMapService { } private applyOnBackground(layer: CCMapLayer, forceAll: boolean) { - const config = TILESET_CONFIG[layer.details.tilesetName]; + const config = this.tilesetConfig[layer.details.tilesetName]; if (!config) { return; } From 0c27fc817c826ed036359efedadd811784a47390 Mon Sep 17 00:00:00 2001 From: cramerL Date: Fri, 26 Jul 2024 16:45:23 +0200 Subject: [PATCH 19/30] destructibles.json and destructible-types.json now loaded through json-loader --- .../custom-des-type-widget.component.ts | 2 +- .../app/services/phaser/entities/registry/destructible.ts | 2 +- .../app/services/phaser/entities/registry/item-destruct.ts | 5 +++-- webapp/src/app/services/phaser/main-scene.ts | 2 -- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/webapp/src/app/components/widgets/custom-des-type-widget/custom-des-type-widget.component.ts b/webapp/src/app/components/widgets/custom-des-type-widget/custom-des-type-widget.component.ts index c34296a4..3baae6e8 100644 --- a/webapp/src/app/components/widgets/custom-des-type-widget/custom-des-type-widget.component.ts +++ b/webapp/src/app/components/widgets/custom-des-type-widget/custom-des-type-widget.component.ts @@ -85,7 +85,7 @@ export class CustomDesTypeWidgetComponent extends OverlayWidget('destructibles.json'); destructibles = Object.keys(json).map(v => ({ name: v, desType: v diff --git a/webapp/src/app/services/phaser/entities/registry/destructible.ts b/webapp/src/app/services/phaser/entities/registry/destructible.ts index 8c2defc5..93d8f014 100644 --- a/webapp/src/app/services/phaser/entities/registry/destructible.ts +++ b/webapp/src/app/services/phaser/entities/registry/destructible.ts @@ -47,7 +47,7 @@ export class Destructible extends CCEntity { } protected async setupType(settings: any): Promise { - const types = this.scene.cache.json.get('destructible-types.json') as DestructibleTypes; + const types = await Globals.jsonLoader.loadJsonMerged('destructible-types.json'); this.attributes['desType'].options = {}; for (const name of Object.keys(types)) { diff --git a/webapp/src/app/services/phaser/entities/registry/item-destruct.ts b/webapp/src/app/services/phaser/entities/registry/item-destruct.ts index 9aeff56a..0c52b2d7 100644 --- a/webapp/src/app/services/phaser/entities/registry/item-destruct.ts +++ b/webapp/src/app/services/phaser/entities/registry/item-destruct.ts @@ -2,10 +2,11 @@ import { Helper } from '../../helper'; import { Anims, AnimSheet } from '../../sheet-parser'; import { DefaultEntity } from './default-entity'; import { Point3 } from '../../../../models/cross-code-map'; -import { AttributeValue, EntityAttributes } from '../cc-entity'; +import { EntityAttributes } from '../cc-entity'; import { EnemyInfo } from './enemy'; import { SheetReference } from './destructible'; import { GlobalSettings } from '../../global-settings'; +import { Globals } from '../../../globals'; export interface ItemDestructTypes { [name: string]: ItemDestructType; @@ -58,7 +59,7 @@ export class ItemDestruct extends DefaultEntity { desType = config.desType; } } - const destructibles = this.scene.cache.json.get('destructibles.json') as ItemDestructTypes; + const destructibles = await Globals.jsonLoader.loadJsonMerged('destructibles.json'); const type = destructibles[desType]; if (!type) { this.generateNoImageType(0xFF0000, 1); diff --git a/webapp/src/app/services/phaser/main-scene.ts b/webapp/src/app/services/phaser/main-scene.ts index 106db6ca..a703ac2b 100644 --- a/webapp/src/app/services/phaser/main-scene.ts +++ b/webapp/src/app/services/phaser/main-scene.ts @@ -24,8 +24,6 @@ export class MainScene extends Phaser.Scene { this.load.image('pixel', 'assets/pixel.png'); this.load.image('ingame', 'assets/ingame.png'); - this.load.json('destructibles.json', 'assets/destructibles.json'); - this.load.json('destructible-types.json', 'assets/destructible-types.json'); this.load.crossOrigin = 'anonymous'; // this.load.on('progress', (val: number) => console.log(val)); From 8b8576d3077d35c8355c0ceee2eaec8d6b26f98c Mon Sep 17 00:00:00 2001 From: cramerL Date: Fri, 26 Jul 2024 17:16:43 +0200 Subject: [PATCH 20/30] updated changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 826a7208..6454d2cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] +### Added +- Added overrideable JSON configs, see [#327](https://github.com/CCDirectLink/crosscode-map-editor/pull/327) for more details +- Added most definitions to autotiles.json +- Added new autotile type 4x4 + ## [1.7.1] 2024-07-21 ### Fixed - Fixed Event editor not working From 911789815298520366ee60133ded77534477c750 Mon Sep 17 00:00:00 2001 From: cramerL Date: Sun, 28 Jul 2024 22:14:25 +0200 Subject: [PATCH 21/30] simplified interface ConfigExtension --- common/src/controllers/api.ts | 8 ++++---- webapp/src/app/services/json-loader.service.ts | 15 +++++---------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/common/src/controllers/api.ts b/common/src/controllers/api.ts index 7c6710cd..90095a99 100644 --- a/common/src/controllers/api.ts +++ b/common/src/controllers/api.ts @@ -2,10 +2,10 @@ import { fsPromise, pathPromise } from '../require.js'; import { saveFile as save } from './saveFile.js'; -export interface ConfigExtension { +export interface ConfigExtension { filename: string; mod: string; - file: T; + file: string; } const mods: string[] = []; @@ -257,13 +257,13 @@ export async function getAllMods(dir: string) { .sort((a, b) => a.displayName.localeCompare(b.displayName)); } -export async function getAllModMapEditorConfigs(dir: string): Promise[]> { +export async function getAllModMapEditorConfigs(dir: string): Promise { const packages = await readMods(dir); const fs = await fsPromise; const path = await pathPromise; - const configs: ConfigExtension[] = []; + const configs: ConfigExtension[] = []; for (const mod of Object.values(packages)) { const modName = mod.folderName; diff --git a/webapp/src/app/services/json-loader.service.ts b/webapp/src/app/services/json-loader.service.ts index e5fe339a..05335ac4 100644 --- a/webapp/src/app/services/json-loader.service.ts +++ b/webapp/src/app/services/json-loader.service.ts @@ -2,7 +2,6 @@ import { Injectable } from '@angular/core'; import { HttpClientService } from './http-client.service'; import { HttpClient } from '@angular/common/http'; import { lastValueFrom } from 'rxjs'; -import { ConfigExtension } from 'cc-map-editor-common/dist/controllers/api'; import { MatSnackBar } from '@angular/material/snack-bar'; @Injectable({ @@ -11,7 +10,7 @@ import { MatSnackBar } from '@angular/material/snack-bar'; export class JsonLoaderService { private readonly initialized?: Promise; - private configs = new Map[]>; + private configs = new Map; private cache: Record = {}; @@ -27,13 +26,9 @@ export class JsonLoaderService { const configs = await this.http.getModMapEditorConfigs(); for (const config of configs) { - const newConfig: ConfigExtension = { - filename: config.filename, - mod: config.mod, - file: {}, - }; + let parsedFile: unknown; try { - newConfig.file = JSON.parse(config.file); + parsedFile = JSON.parse(config.file); } catch (e) { console.error(e); this.snackbar.open( @@ -45,14 +40,14 @@ export class JsonLoaderService { } const configs = this.configs.get(config.filename) ?? []; this.configs.set(config.filename, configs); - configs.push(newConfig); + configs.push(parsedFile); } } async loadJson(file: string): Promise { await this.initialized; const json = await lastValueFrom(this.angularHttp.get(`./assets/${file}`)); - const modJson = (this.configs.get(file) ?? []).map(v => v.file) as T[]; + const modJson = (this.configs.get(file) ?? []) as T[]; return [ json as T, From d30247da652dcf5723d79a22f750cef06b3b733c Mon Sep 17 00:00:00 2001 From: cramerL Date: Sun, 28 Jul 2024 22:35:20 +0200 Subject: [PATCH 22/30] added support for directories inside mod folder "map-editor" --- common/src/controllers/api.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/src/controllers/api.ts b/common/src/controllers/api.ts index 90095a99..e37ceee4 100644 --- a/common/src/controllers/api.ts +++ b/common/src/controllers/api.ts @@ -268,11 +268,11 @@ export async function getAllModMapEditorConfigs(dir: string): Promise Date: Sun, 28 Jul 2024 22:36:39 +0200 Subject: [PATCH 23/30] renamed ConfigExtension interface --- common/src/controllers/api.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/common/src/controllers/api.ts b/common/src/controllers/api.ts index e37ceee4..969ce831 100644 --- a/common/src/controllers/api.ts +++ b/common/src/controllers/api.ts @@ -1,8 +1,7 @@ import { fsPromise, pathPromise } from '../require.js'; import { saveFile as save } from './saveFile.js'; - -export interface ConfigExtension { +export interface ModEditorConfig { filename: string; mod: string; file: string; @@ -257,13 +256,13 @@ export async function getAllMods(dir: string) { .sort((a, b) => a.displayName.localeCompare(b.displayName)); } -export async function getAllModMapEditorConfigs(dir: string): Promise { +export async function getAllModMapEditorConfigs(dir: string): Promise { const packages = await readMods(dir); const fs = await fsPromise; const path = await pathPromise; - const configs: ConfigExtension[] = []; + const configs: ModEditorConfig[] = []; for (const mod of Object.values(packages)) { const modName = mod.folderName; From e5b9580fcbcc26ea77b232343ec4dd9df29f1de5 Mon Sep 17 00:00:00 2001 From: cramerL Date: Sun, 28 Jul 2024 23:19:04 +0200 Subject: [PATCH 24/30] fixed autotile issue --- webapp/src/assets/autotiles.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/src/assets/autotiles.json b/webapp/src/assets/autotiles.json index 37e56c31..5a589257 100644 --- a/webapp/src/assets/autotiles.json +++ b/webapp/src/assets/autotiles.json @@ -2332,7 +2332,7 @@ "y": 0 }, "size": { - "x": 12, + "x": 8, "y": 2 } }, From ca654f301984ac285d8ca7ca376584fe67144561 Mon Sep 17 00:00:00 2001 From: cramerL Date: Mon, 29 Jul 2024 00:15:53 +0200 Subject: [PATCH 25/30] fixed BaseTileDrawer having issues when deleting layers --- .../tile-selector/tile-selector.scene.ts | 19 +++++++++++-------- .../src/app/services/phaser/BaseTileDrawer.ts | 15 +++++++++++---- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.scene.ts b/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.scene.ts index b35f4273..81ea50a5 100644 --- a/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.scene.ts +++ b/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.scene.ts @@ -34,7 +34,11 @@ export class TileSelectorScene extends Phaser.Scene { }; this.subs.push(Globals.mapLoaderService.selectedLayer.subscribe(async layer => { - await this.drawTileset(layer); + const success = await this.drawTileset(layer); + if (!success){ + this.tileMap.removeAllLayers(); + await this.baseDrawer.setLayer(); + } })); this.subs.push(Globals.phaserEventsService.changeSelectedTiles.subscribe(tiles => { @@ -97,17 +101,14 @@ export class TileSelectorScene extends Phaser.Scene { this.subs = []; } - private async drawTileset(selectedLayer?: CCMapLayer) { + private async drawTileset(selectedLayer?: CCMapLayer): Promise { if (!selectedLayer?.details.tilesetName) { - if (this.tileMap) { - this.tileMap.removeAllLayers(); - } - return; + return false; } const exists = await Helper.loadTexture(selectedLayer.details.tilesetName, this); if (!exists) { - return; + return false; } const tilesetSize = Helper.getTilesetSize(this, selectedLayer.details.tilesetName); @@ -115,7 +116,7 @@ export class TileSelectorScene extends Phaser.Scene { this.tileMap.removeAllLayers(); const tileset = this.tileMap.addTilesetImage(selectedLayer.details.tilesetName); if (!tileset) { - return; + return false; } tileset.firstgid = 1; const layer = this.tileMap.createBlankLayer('first', tileset, 0, 0, tilesetSize.x, tilesetSize.y)!; @@ -133,5 +134,7 @@ export class TileSelectorScene extends Phaser.Scene { customPutTilesAt(data, layer); this.tilemapLayer.setPhaserLayer(layer); await this.baseDrawer.setLayer(this.tilemapLayer); + + return true; } } diff --git a/webapp/src/app/services/phaser/BaseTileDrawer.ts b/webapp/src/app/services/phaser/BaseTileDrawer.ts index 6ca5725d..703f3312 100644 --- a/webapp/src/app/services/phaser/BaseTileDrawer.ts +++ b/webapp/src/app/services/phaser/BaseTileDrawer.ts @@ -101,8 +101,7 @@ export class BaseTileDrawer extends BaseObject { } protected override activate(): void { - this.selection?.setVisible(true); - this.previewLayer?.setVisible(true); + this.setVisibility(true); const pointerDown = (pointer: Phaser.Input.Pointer) => { if (pointer.rightButtonDown() || this.leftClick && pointer.leftButtonDown()) { this.onMouseRightDown(); @@ -122,20 +121,28 @@ export class BaseTileDrawer extends BaseObject { } protected override deactivate(): void { - this.selection?.setVisible(false); - this.previewLayer?.setVisible(false); + this.setVisibility(false); + } + + private setVisibility(visible: boolean) { + visible = visible && !!this.layer; + this.selection?.setVisible(visible); + this.previewLayer?.setVisible(visible); } public async setLayer(layer?: CCMapLayer) { this.layer = layer; if (!layer) { + this.setVisibility(false); return; } const exists = await Helper.loadTexture(layer.details.tilesetName, this.scene); if (!exists) { + this.setVisibility(false); return; } + this.setVisibility(true); const tileset = this.previewTileMap.addTilesetImage('only', layer.details.tilesetName); if (tileset) { From 8ec14166804ff8b7bcf37230268161d46ef16ab4 Mon Sep 17 00:00:00 2001 From: cramerL Date: Mon, 29 Jul 2024 00:28:42 +0200 Subject: [PATCH 26/30] added comment to explain tile selector scene highlight logic --- .../dialogs/floating-window/tile-selector/tile-selector.scene.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.scene.ts b/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.scene.ts index 81ea50a5..f2594be0 100644 --- a/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.scene.ts +++ b/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.scene.ts @@ -54,6 +54,7 @@ export class TileSelectorScene extends Phaser.Scene { let width = 0; let height = 0; + // If the selection is a continuous rectangle in the tile selector, highlight it for (const tile of tiles) { const id = tile.id - tile.offset.x - tile.offset.y * this.tilesetWidth; if (baseTile.id !== id) { From ea2c3f41269ba24d8003a886ca3a3ffcfd3939d4 Mon Sep 17 00:00:00 2001 From: cramerL Date: Mon, 29 Jul 2024 00:54:38 +0200 Subject: [PATCH 27/30] removed unused jsonLoader --- .../widgets/event-widget/event-editor/event-helper.service.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/webapp/src/app/components/widgets/event-widget/event-editor/event-helper.service.ts b/webapp/src/app/components/widgets/event-widget/event-editor/event-helper.service.ts index 03646eb1..c36bab1f 100644 --- a/webapp/src/app/components/widgets/event-widget/event-editor/event-helper.service.ts +++ b/webapp/src/app/components/widgets/event-widget/event-editor/event-helper.service.ts @@ -3,19 +3,17 @@ import { DomSanitizer } from '@angular/platform-browser'; import { BehaviorSubject } from 'rxjs'; import { AbstractEvent, EventType } from '../event-registry/abstract-event'; import { EventRegistryService } from '../event-registry/event-registry.service'; -import { JsonLoaderService } from '../../../../services/json-loader.service'; @Injectable({ providedIn: 'root' }) export class EventHelperService { - selectedEvent: BehaviorSubject | null> = new BehaviorSubject | null> (null); + selectedEvent: BehaviorSubject | null> = new BehaviorSubject | null>(null); constructor( private eventRegistry: EventRegistryService, private domSanitizer: DomSanitizer, - private jsonLoader: JsonLoaderService ) { } From 0a7ae46a50e9201869e870b67a6dc3a251b57a27 Mon Sep 17 00:00:00 2001 From: cramerL Date: Mon, 29 Jul 2024 00:56:36 +0200 Subject: [PATCH 28/30] improved map styles definitions --- webapp/src/app/models/map-styles.ts | 23 +++++++------------ .../phaser/entities/registry/destructible.ts | 2 +- webapp/src/app/services/phaser/helper.ts | 9 +++----- 3 files changed, 12 insertions(+), 22 deletions(-) diff --git a/webapp/src/app/models/map-styles.ts b/webapp/src/app/models/map-styles.ts index 765b2211..b48a1d30 100644 --- a/webapp/src/app/models/map-styles.ts +++ b/webapp/src/app/models/map-styles.ts @@ -1,20 +1,13 @@ export interface MapStyles { - [key: string]: MapStyle; + default: MapStyleType; + + [key: string]: MapStyleType | undefined; } -export type MapStyle = { - sheet: string; - hasDoorMat?: boolean; - walls?: WallColors; -} & { - [key: string]: any; -}; +export interface MapStyleType { + [key: string]: MapStyle | undefined; +} -export interface WallColors { - blockFront: string; - blockTop: string; - pBlockFront: string; - pBlockTop: string; - npBlockFront: string; - npBlockTop: string; +export interface MapStyle { + sheet?: string; } diff --git a/webapp/src/app/services/phaser/entities/registry/destructible.ts b/webapp/src/app/services/phaser/entities/registry/destructible.ts index 93d8f014..600551de 100644 --- a/webapp/src/app/services/phaser/entities/registry/destructible.ts +++ b/webapp/src/app/services/phaser/entities/registry/destructible.ts @@ -105,7 +105,7 @@ export class Destructible extends CCEntity { const mapStyle = Helper.getMapStyle(Globals.map, 'destruct'); for (const sheet of sheets) { if (!sheet.gfx) { - sheet.gfx = mapStyle['sheet']; + sheet.gfx = mapStyle?.sheet; } const exists = await Helper.loadTexture(sheet.gfx, this.scene); if (!exists) { diff --git a/webapp/src/app/services/phaser/helper.ts b/webapp/src/app/services/phaser/helper.ts index 3d854905..4de08ad6 100644 --- a/webapp/src/app/services/phaser/helper.ts +++ b/webapp/src/app/services/phaser/helper.ts @@ -1,5 +1,5 @@ import { Point } from '../../models/cross-code-map'; -import { MapStyles } from '../../models/map-styles'; +import { MapStyle, MapStyles } from '../../models/map-styles'; import { Globals } from '../globals'; import { CCMap } from './tilemap/cc-map'; import { CCMapLayer } from './tilemap/cc-map-layer'; @@ -149,14 +149,11 @@ export class Helper { return tag === 'input' || tag === 'textarea'; } - public static getMapStyle(map: CCMap, type: string): MapStyles { + public static getMapStyle(map: CCMap, type: keyof MapStyles): MapStyle | undefined { const mapStyles = Globals.jsonLoader.loadJsonMergedSync('map-styles.json'); const mapStyleName = map.attributes.mapStyle || 'default'; const mapStyle = mapStyles[mapStyleName]; - if (mapStyle && mapStyle[type]) { - return mapStyle[type]; - } - return mapStyles['default'][type]; + return mapStyle?.[type] ?? mapStyles.default[type]; } public static async asyncFilter(arr: T[], predicate: (v: T) => Promise) { From f6244453df3f548c7336fd2ea6b763b7b946cd43 Mon Sep 17 00:00:00 2001 From: cramerL Date: Mon, 29 Jul 2024 01:20:04 +0200 Subject: [PATCH 29/30] fixed selection not showing in tile selector when changing layers --- .../tile-selector/tile-selector.scene.ts | 2 +- webapp/src/app/services/phaser/BaseTileDrawer.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.scene.ts b/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.scene.ts index f2594be0..3de8984b 100644 --- a/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.scene.ts +++ b/webapp/src/app/components/dialogs/floating-window/tile-selector/tile-selector.scene.ts @@ -35,7 +35,7 @@ export class TileSelectorScene extends Phaser.Scene { this.subs.push(Globals.mapLoaderService.selectedLayer.subscribe(async layer => { const success = await this.drawTileset(layer); - if (!success){ + if (!success) { this.tileMap.removeAllLayers(); await this.baseDrawer.setLayer(); } diff --git a/webapp/src/app/services/phaser/BaseTileDrawer.ts b/webapp/src/app/services/phaser/BaseTileDrawer.ts index 703f3312..c4fa8429 100644 --- a/webapp/src/app/services/phaser/BaseTileDrawer.ts +++ b/webapp/src/app/services/phaser/BaseTileDrawer.ts @@ -124,10 +124,9 @@ export class BaseTileDrawer extends BaseObject { this.setVisibility(false); } - private setVisibility(visible: boolean) { - visible = visible && !!this.layer; - this.selection?.setVisible(visible); - this.previewLayer?.setVisible(visible); + private setVisibility(selection: boolean, preview = selection) { + this.selection?.setVisible(selection && !!this.layer); + this.previewLayer?.setVisible(preview && !!this.layer); } public async setLayer(layer?: CCMapLayer) { @@ -139,7 +138,7 @@ export class BaseTileDrawer extends BaseObject { const exists = await Helper.loadTexture(layer.details.tilesetName, this.scene); if (!exists) { - this.setVisibility(false); + this.setVisibility(true, false); return; } this.setVisibility(true); @@ -250,6 +249,7 @@ export class BaseTileDrawer extends BaseObject { } this.selection = this.scene.add.container(x, y); + this.selection.setDepth(10); const rect = this.scene.add.rectangle(0, 0, width * Globals.TILE_SIZE, height * Globals.TILE_SIZE); rect.setOrigin(0, 0); From 48a8364b23c4e4b8136aaa8407857b637926f88b Mon Sep 17 00:00:00 2001 From: cramerL Date: Wed, 31 Jul 2024 17:56:27 +0200 Subject: [PATCH 30/30] set z-index on floating window to fix some ordering issues --- .../dialogs/floating-window/floating-window.component.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/webapp/src/app/components/dialogs/floating-window/floating-window.component.scss b/webapp/src/app/components/dialogs/floating-window/floating-window.component.scss index 34d97a38..c62dfc8f 100644 --- a/webapp/src/app/components/dialogs/floating-window/floating-window.component.scss +++ b/webapp/src/app/components/dialogs/floating-window/floating-window.component.scss @@ -10,6 +10,7 @@ $toolbarHeight: 32px; .container { min-width: 100px; position: absolute; + z-index: 100; } .ng-draggable {