diff --git a/CHANGELOG.md b/CHANGELOG.md index 147d9c54..50f11df0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,15 @@ 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 support for parallax layers (`distance != 1`) +- The size of every individual layer can now be changed instead of using the map size +- Added preview to see how the parallax effect would look ingame + +### Fixed +- Layers with different sizes are now saved properly [#272](https://github.com/CCDirectLink/crosscode-map-editor/issues/272) + ## [1.0.0] 2023-08-04 ### Added diff --git a/webapp/src/app/components/layers/layers.component.html b/webapp/src/app/components/layers/layers.component.html index df6f8573..ed1a4080 100644 --- a/webapp/src/app/components/layers/layers.component.html +++ b/webapp/src/app/components/layers/layers.component.html @@ -34,6 +34,41 @@
+
+
+ Size: + +
+ +
+ + X: + + + + + Y: + + +
+
+
Type: @@ -59,7 +94,8 @@ Level: + [ngModel]="selectedLayer?.details?.level" + (selectionChange)="updateLevel($event.value)"> {{i}} @@ -68,16 +104,24 @@
Distance: - +
Tileset: - - {{getTilesetName(tileset)}} + + {{getTilesetName(tileset)}}
diff --git a/webapp/src/app/components/layers/layers.component.scss b/webapp/src/app/components/layers/layers.component.scss index be07dd60..365c7248 100644 --- a/webapp/src/app/components/layers/layers.component.scss +++ b/webapp/src/app/components/layers/layers.component.scss @@ -67,12 +67,13 @@ mat-nav-list { .small-list { min-height: 80px !important; + & mat-list-item, .mat-list-item-content { height: 36px !important; } } -.layers-tab-container{ +.layers-tab-container { height: 100%; } @@ -86,6 +87,7 @@ mat-nav-list { .label { flex: 0 0 80px; + @include mat.icon-button-density(-3); } .list-item { @@ -93,6 +95,10 @@ mat-nav-list { width: 100%; } +.size-prefix { + margin-left: 6px; +} + .simple-input { padding-bottom: 5px !important; box-shadow: inset 0px -1px 0px 0px rgb(255, 255, 255); diff --git a/webapp/src/app/components/layers/layers.component.ts b/webapp/src/app/components/layers/layers.component.ts index a326ec06..f1323ba7 100644 --- a/webapp/src/app/components/layers/layers.component.ts +++ b/webapp/src/app/components/layers/layers.component.ts @@ -24,10 +24,13 @@ export class LayersComponent implements OnInit { newLayerName = ''; tilesets: string[] = []; //Angular view data + width = 0; + height = 0; + constructor(private mapLoader: MapLoaderService, - private stateHistory: StateHistoryService, - private http: HttpClientService, - events: GlobalEventsService) { + private stateHistory: StateHistoryService, + private http: HttpClientService, + events: GlobalEventsService) { events.toggleVisibility.subscribe(() => { if (this.selectedLayer) { this.toggleVisibility({ @@ -36,12 +39,22 @@ export class LayersComponent implements OnInit { } as Event, this.selectedLayer); } }); - + this.loadTilesets(); } ngOnInit() { - this.mapLoader.selectedLayer.subscribe(layer => this.selectedLayer = layer); + this.mapLoader.selectedLayer.subscribe(layer => { + this.selectedLayer = layer; + for (const layer of (this.map?.layers ?? [])) { + layer.select(false); + } + if (layer){ + layer.select(true); + this.width = layer.details.width; + this.height = layer.details.height; + } + }); this.mapLoader.tileMap.subscribe(tilemap => this.map = tilemap); } @@ -117,9 +130,6 @@ export class LayersComponent implements OnInit { } selectLayer(layer?: CCMapLayer) { - if (layer) { - layer.visible = true; - } this.mapLoader.selectedLayer.next(layer); } @@ -130,17 +140,17 @@ export class LayersComponent implements OnInit { this.selectedLayer.updateTileset(name); this.mapLoader.selectedLayer.next(this.selectedLayer); } - + getTilesetName(path: string): string { return path.substring('media/map/'.length, path.length - '.png'.length); } - + private async loadTilesets() { if (LayersComponent.tilesets.length > 0) { this.tilesets = LayersComponent.tilesets; return; } - + LayersComponent.tilesets = await firstValueFrom(this.http.getAllTilesets()); this.tilesets = LayersComponent.tilesets; } @@ -152,6 +162,21 @@ export class LayersComponent implements OnInit { this.selectedLayer.updateLevel(level); } + updateSize() { + this.selectedLayer?.resize(this.width, this.height); + this.stateHistory.saveState({ + name: 'Layer resized', + icon: 'resize' + }); + } + + updateDistance() { + this.stateHistory.saveState({ + name: 'Distance changed', + icon: 'fit_screen' + }); + } + drop(event: CdkDragDrop) { if (event.previousIndex === event.currentIndex) { return; @@ -165,5 +190,4 @@ export class LayersComponent implements OnInit { icon: 'open_with', }, true); } - } diff --git a/webapp/src/app/components/phaser/phaser.component.ts b/webapp/src/app/components/phaser/phaser.component.ts index bc14fd53..2043365e 100644 --- a/webapp/src/app/components/phaser/phaser.component.ts +++ b/webapp/src/app/components/phaser/phaser.component.ts @@ -1,4 +1,4 @@ -import { Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core'; +import { AfterViewInit, Component, ElementRef, HostListener, ViewChild } from '@angular/core'; import * as Phaser from 'phaser'; import { AutotileService } from '../../services/autotile/autotile.service'; @@ -18,7 +18,7 @@ import { MatSnackBar } from '@angular/material/snack-bar'; templateUrl: './phaser.component.html', styleUrls: ['./phaser.component.scss'] }) -export class PhaserComponent implements OnInit { +export class PhaserComponent implements AfterViewInit { @ViewChild('content', {static: true}) content!: ElementRef; @@ -45,7 +45,7 @@ export class PhaserComponent implements OnInit { } - ngOnInit() { + ngAfterViewInit() { this.heightMap.init(); const scene = new MainScene(); const scale = this.getScale(); @@ -83,8 +83,8 @@ export class PhaserComponent implements OnInit { private getScale() { const rect = this.content.nativeElement.getBoundingClientRect(); return { - width: (rect.width + 5) * window.devicePixelRatio, - height: (rect.height + 5) * window.devicePixelRatio + width: rect.width * window.devicePixelRatio, + height: rect.height * window.devicePixelRatio }; } } diff --git a/webapp/src/app/components/sidenav/sidenav.component.ts b/webapp/src/app/components/sidenav/sidenav.component.ts index 31421e3b..eb010d72 100644 --- a/webapp/src/app/components/sidenav/sidenav.component.ts +++ b/webapp/src/app/components/sidenav/sidenav.component.ts @@ -15,9 +15,7 @@ import { CCMapLayer } from '../../services/phaser/tilemap/cc-map-layer'; export class SidenavComponent implements OnInit { activeTab = EditorView.Layers; - selectedLayer?: CCMapLayer; tilemap?: CCMap; - editorViewEnum = EditorView; disableLayersTab = false; constructor( @@ -27,11 +25,6 @@ export class SidenavComponent implements OnInit { } ngOnInit() { - this.mapLoader.selectedLayer.subscribe(layer => { - if (layer) { - this.selectedLayer = layer; - } - }); this.mapLoader.tileMap.subscribe(tilemap => { this.tilemap = tilemap; const currentView = this.globalEvents.currentView; diff --git a/webapp/src/app/components/toolbar/toolbar.component.html b/webapp/src/app/components/toolbar/toolbar.component.html index 1cb7cbbd..9baa59cf 100644 --- a/webapp/src/app/components/toolbar/toolbar.component.html +++ b/webapp/src/app/components/toolbar/toolbar.component.html @@ -29,9 +29,28 @@ -
- 3D! - +
+
+ + 3D! + +
+ +
+ + Ingame Preview + +
diff --git a/webapp/src/app/components/toolbar/toolbar.component.scss b/webapp/src/app/components/toolbar/toolbar.component.scss index 63c1100b..ef38b909 100644 --- a/webapp/src/app/components/toolbar/toolbar.component.scss +++ b/webapp/src/app/components/toolbar/toolbar.component.scss @@ -5,10 +5,6 @@ z-index: 2; } -.toggle-3d { - margin-left: 16px; -} - mat-spinner { height: 40px; } diff --git a/webapp/src/app/components/toolbar/toolbar.component.ts b/webapp/src/app/components/toolbar/toolbar.component.ts index b112d3f9..c9011b7f 100644 --- a/webapp/src/app/components/toolbar/toolbar.component.ts +++ b/webapp/src/app/components/toolbar/toolbar.component.ts @@ -32,7 +32,7 @@ export class ToolbarComponent implements OnInit { public loadMapClicked = new EventEmitter(false); constructor(private mapLoader: MapLoaderService, - private events: GlobalEventsService, + public events: GlobalEventsService, private dialog: MatDialog, private overlayService: OverlayService, private overlay: Overlay, @@ -123,8 +123,12 @@ export class ToolbarComponent implements OnInit { }); } - changeTo3d(event: MatSlideToggleChange) { - this.is3d = event.checked; - this.router.navigate([event.checked ? '3d' : '']); + changeTo3d(checked: boolean) { + this.is3d = checked; + this.router.navigate([checked ? '3d' : '']); + } + + toggleIngamePreview(checked: boolean) { + this.events.showIngamePreview.next(checked); } } diff --git a/webapp/src/app/services/3d/layer-generation/texture-generator.ts b/webapp/src/app/services/3d/layer-generation/texture-generator.ts index a0221969..97bbf411 100644 --- a/webapp/src/app/services/3d/layer-generation/texture-generator.ts +++ b/webapp/src/app/services/3d/layer-generation/texture-generator.ts @@ -26,11 +26,9 @@ export class TextureGenerator { .filter(l => l.details.type.toLowerCase() === 'background') .sort((a, b) => a.details.level - b.details.level); - Globals.phaserEventsService.showMapBorder.next(false); } destroy() { - Globals.phaserEventsService.showMapBorder.next(true); } /** diff --git a/webapp/src/app/services/global-events.service.ts b/webapp/src/app/services/global-events.service.ts index 1adc93f9..fe140de8 100644 --- a/webapp/src/app/services/global-events.service.ts +++ b/webapp/src/app/services/global-events.service.ts @@ -20,6 +20,7 @@ export class GlobalEventsService { showAddEntityMenu = new Subject(); updateCoords = new Subject(); + showIngamePreview = new BehaviorSubject(false); babylonLoading = new BehaviorSubject(false); is3D = new BehaviorSubject(false); diff --git a/webapp/src/app/services/phaser/base-object.ts b/webapp/src/app/services/phaser/base-object.ts index a0ed77ac..991f88f7 100644 --- a/webapp/src/app/services/phaser/base-object.ts +++ b/webapp/src/app/services/phaser/base-object.ts @@ -1,5 +1,6 @@ import * as Phaser from 'phaser'; import { Subscription } from 'rxjs'; +import { PreUpdate } from './pre-update'; export interface KeyBinding { event: string; @@ -7,7 +8,7 @@ export interface KeyBinding { emitter: Phaser.Events.EventEmitter; } -export abstract class BaseObject extends Phaser.GameObjects.GameObject { +export abstract class BaseObject extends Phaser.GameObjects.GameObject implements PreUpdate { private subs: Subscription[] = []; private keyBindings: KeyBinding[] = []; diff --git a/webapp/src/app/services/phaser/ingame-preview.ts b/webapp/src/app/services/phaser/ingame-preview.ts new file mode 100644 index 00000000..77ecbcf1 --- /dev/null +++ b/webapp/src/app/services/phaser/ingame-preview.ts @@ -0,0 +1,36 @@ +import { PreUpdate } from './pre-update'; +import { Globals } from '../globals'; +import { Subscription } from 'rxjs'; + +export class IngamePreview extends Phaser.GameObjects.Image implements PreUpdate { + + private sub: Subscription; + + constructor(scene: Phaser.Scene) { + super(scene, 0, 0, 'ingame'); + this.depth = 99999; + this.sub = Globals.globalEventsService.showIngamePreview.subscribe(v => this.visible = v); + } + + override destroy(fromScene?: boolean) { + super.destroy(fromScene); + this.sub.unsubscribe(); + } + + preUpdate(time: number, delta: number): void { + const cam = this.scene.cameras.main; + + const midX = cam.scrollX + cam.width * 0.5; + const midY = cam.scrollY + cam.height * 0.5; + + const displayWidth = cam.width / cam.zoomX; + const displayHeight = cam.height / cam.zoomY; + + const centerX = midX - displayWidth * 0.1; + const centerY = midY - displayHeight * 0.06; + + this.x = centerX; + this.y = centerY; + } + +} diff --git a/webapp/src/app/services/phaser/layer-parallax.ts b/webapp/src/app/services/phaser/layer-parallax.ts new file mode 100644 index 00000000..1d432a2d --- /dev/null +++ b/webapp/src/app/services/phaser/layer-parallax.ts @@ -0,0 +1,63 @@ +import { BaseObject } from './base-object'; +import { Globals } from '../globals'; +import { CCMap } from './tilemap/cc-map'; +import { IngamePreview } from './ingame-preview'; + +export class LayerParallax extends BaseObject { + + private map?: CCMap; + + constructor( + scene: Phaser.Scene, + private preview: IngamePreview + ) { + super(scene, LayerParallax.name, true); + } + + protected activate(): void { + this.addSubscription(Globals.mapLoaderService.tileMap.subscribe(map => this.map = map)); + } + + protected deactivate(): void { + if (!this.map) { + return; + } + for (const layer of this.map.layers) { + layer.setOffset(0, 0); + } + } + + protected init(): void { + } + + preUpdate(time: number, delta: number) { + if (!this.map) { + return; + } + + const layers = this.map.layers; + + const centerX = this.preview.x; + const centerY = this.preview.y; + + // half of game resolution + const offX = centerX - 284; + const offY = centerY - 160; + + for (const layer of layers) { + if (!layer.visible) { + continue; + } + let x = offX / (layer.details?.distance ?? 1); + let y = offY / (layer.details?.distance ?? 1); + + if (layer.details.distance < 1) { + x += 16; + y += 16; + } + + layer.setOffset(offX - x, offY - y); + } + } + +} diff --git a/webapp/src/app/services/phaser/main-scene.ts b/webapp/src/app/services/phaser/main-scene.ts index 9b39bb4a..b853df94 100644 --- a/webapp/src/app/services/phaser/main-scene.ts +++ b/webapp/src/app/services/phaser/main-scene.ts @@ -7,14 +7,12 @@ import { EntityManager } from './entities/entity-manager'; import { MapPan } from './map-pan'; import { CCMap } from './tilemap/cc-map'; import { TileDrawer } from './tilemap/tile-drawer'; +import { LayerParallax } from './layer-parallax'; +import { IngamePreview } from './ingame-preview'; export class MainScene extends Phaser.Scene { - private readonly borderSize = 1; - - private border?: Phaser.GameObjects.Rectangle; private sub?: Subscription; - private borderVisible = true; constructor() { super({key: 'main'}); @@ -23,6 +21,7 @@ export class MainScene extends Phaser.Scene { preload() { 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'); @@ -52,7 +51,6 @@ export class MainScene extends Phaser.Scene { this.sub = Globals.mapLoaderService.map.subscribe((map) => { if (map) { tileMap.loadMap(map); - this.rescaleBorder(); // reset camera position on map load const cam = this.cameras.main; @@ -64,13 +62,6 @@ export class MainScene extends Phaser.Scene { cam.centerOn(map.mapWidth * s / 2 + offset, map.mapHeight * s / 2); } }); - Globals.phaserEventsService.updateMapBorder.subscribe(() => this.rescaleBorder()); - Globals.phaserEventsService.showMapBorder.subscribe(visible => { - if (this.border) { - this.border.visible = visible; - } - this.borderVisible = visible; - }); const pan = new MapPan(this, 'mapPan'); this.add.existing(pan); @@ -79,6 +70,10 @@ export class MainScene extends Phaser.Scene { this.add.existing(tileDrawer); this.add.existing(entityManager); + + const preview = new IngamePreview(this); + this.add.existing(preview); + this.add.existing(new LayerParallax(this, preview)); const coordsReporter = new CoordsReporter(this); this.add.existing(coordsReporter); @@ -105,18 +100,4 @@ export class MainScene extends Phaser.Scene { this.sub.unsubscribe(); } } - - private rescaleBorder() { - const s = Globals.TILE_SIZE; - - if (this.border) { - this.border.destroy(); - } - const map = Globals.map; - - this.border = this.add.rectangle(-this.borderSize, -this.borderSize, map.mapWidth * s + this.borderSize * 2, map.mapHeight * s + this.borderSize * 2); - this.border.setStrokeStyle(this.borderSize * 2, 0xfc4445, 1); - this.border.setOrigin(0, 0); - this.border.visible = this.borderVisible; - } } diff --git a/webapp/src/app/services/phaser/map-pan.ts b/webapp/src/app/services/phaser/map-pan.ts index 17f60ba3..f7f3c909 100644 --- a/webapp/src/app/services/phaser/map-pan.ts +++ b/webapp/src/app/services/phaser/map-pan.ts @@ -1,8 +1,9 @@ import { Point } from '../../models/cross-code-map'; import { Globals } from '../globals'; import { Vec2 } from './vec2'; +import { PreUpdate } from './pre-update'; -export class MapPan extends Phaser.GameObjects.GameObject { +export class MapPan extends Phaser.GameObjects.GameObject implements PreUpdate{ private isScrolling = false; private startMouse: Point = {x: 0, y: 0}; private startCam: Point = {x: 0, y: 0}; @@ -77,7 +78,6 @@ export class MapPan extends Phaser.GameObjects.GameObject { cam.scrollX += oldX - mouse.x; cam.scrollY += oldY - mouse.y; } - Globals.phaserEventsService.updateMapBorder.next(true); } preUpdate() { diff --git a/webapp/src/app/services/phaser/phaser-events.service.ts b/webapp/src/app/services/phaser/phaser-events.service.ts index 5be64f3a..991249c4 100644 --- a/webapp/src/app/services/phaser/phaser-events.service.ts +++ b/webapp/src/app/services/phaser/phaser-events.service.ts @@ -8,6 +8,4 @@ import { SelectedTile } from '../../models/tile-selector'; export class PhaserEventsService { changeSelectedTiles = new Subject(); - updateMapBorder = new Subject(); - showMapBorder = new Subject(); } diff --git a/webapp/src/app/services/phaser/pre-update.ts b/webapp/src/app/services/phaser/pre-update.ts new file mode 100644 index 00000000..290f8a7a --- /dev/null +++ b/webapp/src/app/services/phaser/pre-update.ts @@ -0,0 +1,3 @@ +export interface PreUpdate { + preUpdate(time: number, delta: number): void; +} 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 acf4f936..6d9195f5 100644 --- a/webapp/src/app/services/phaser/tilemap/cc-map-layer.ts +++ b/webapp/src/app/services/phaser/tilemap/cc-map-layer.ts @@ -3,6 +3,7 @@ import { BlendModes } from 'phaser'; import { MapLayer, Point } from '../../../models/cross-code-map'; import { Helper } from '../helper'; import { customPutTilesAt } from './layer-helper'; +import { Globals } from '../../globals'; import Tile = Phaser.Tilemaps.Tile; export class CCMapLayer { @@ -10,8 +11,12 @@ export class CCMapLayer { public details!: MapLayer; private layer!: Phaser.Tilemaps.TilemapLayer; + private border!: Phaser.GameObjects.Rectangle; + private container!: Phaser.GameObjects.Container; - constructor(private tilemap: Phaser.Tilemaps.Tilemap) { + constructor( + private tilemap: Phaser.Tilemaps.Tilemap + ) { } public async init(details: MapLayer) { @@ -41,7 +46,12 @@ export class CCMapLayer { details.distance = parseFloat(details.distance); } this.details = details; - this.layer = this.tilemap.createBlankLayer(details.name + Math.random(), 'stub')!; + this.border = this.tilemap.scene.add.rectangle(); + this.border.visible = false; + this.container = this.tilemap.scene.add.container(0, 0, this.border); + this.container.depth = 999; + this.makeLayer('stub'); + this.updateBorder(); if (details.data) { customPutTilesAt(details.data, this.layer); } @@ -61,6 +71,8 @@ export class CCMapLayer { } set visible(val: boolean) { + this.container.visible = val; + this.container.active = val; this.layer.visible = val; this.layer.active = val; } @@ -73,8 +85,24 @@ export class CCMapLayer { this.layer.alpha = val; } + get x(): number { + return this.container.x; + } + + get y(): number { + return this.container.y; + } + destroy() { - this.layer.destroy(); + this.container.destroy(true); + this.layer?.destroy(true); + } + + select(val: boolean) { + if (val) { + this.visible = true; + } + this.border.visible = val; } offsetLayer(offset: Point, borderTiles = false) { @@ -114,12 +142,9 @@ export class CCMapLayer { newData[y][x] = old[x]?.index ?? 0; } } - const tilesetName = this.layer.tileset[0].name; const visible = this.layer.visible; - this.layer.destroy(); - - this.layer = this.tilemap.createBlankLayer(this.details.name + Math.random(), tilesetName, 0, 0, width, height)!; - customPutTilesAt(newData, this.layer); + this.makeLayer(undefined, newData); + this.updateBorder(); this.visible = visible; } @@ -128,14 +153,10 @@ export class CCMapLayer { const details = this.details; details.tilesetName = tilesetname; - const oldLayer = this.layer; await Helper.loadTexture(tilesetname, this.tilemap.scene); const newTileset = this.tilemap.addTilesetImage(tilesetname, undefined, undefined, undefined, undefined, undefined, 1); - this.layer = this.tilemap.createBlankLayer(details.name + Math.random(), newTileset ?? [], 0, 0, details.width, details.height)!; - customPutTilesAt(oldLayer.layer.data, this.layer); - - oldLayer.destroy(); + this.makeLayer(newTileset ?? []); this.updateLevel(this.details.level); this.updateLighter(!!this.details.lighter); @@ -150,6 +171,13 @@ export class CCMapLayer { this.layer.depth = this.details.level * 10; } + setOffset(x: number, y: number) { + this.container.x = x; + this.container.y = y; + this.layer.x = x; + this.layer.y = y; + } + updateLighter(lighter: boolean) { this.details.lighter = lighter; const blendMode = lighter ? BlendModes.ADD : BlendModes.NORMAL; @@ -160,6 +188,41 @@ export class CCMapLayer { return this.layer; } + private makeLayer(tileset?: string | string[] | Phaser.Tilemaps.Tileset, tiles?: Tile[][] | number[][]) { + const oldLayer = this.layer as typeof this.layer | undefined; + + if (!tileset) { + tileset = oldLayer?.tileset[0]?.name ?? []; + } + if (!tiles) { + tiles = oldLayer?.layer?.data; + } + this.layer = this.tilemap.createBlankLayer(this.details.name + Math.random(), tileset, 0, 0, this.details.width, this.details.height)!; + if (tiles) { + customPutTilesAt(tiles, this.layer); + } + this.layer.alpha = oldLayer?.alpha ?? 1; + this.setOffset(this.container.x, this.container.y); + this.updateLevel(this.details.level); + if (oldLayer) { + oldLayer.destroy(true); + } + } + + private updateBorder() { + const s = Globals.TILE_SIZE; + + const borderSize = 2; + + this.border.setPosition(-borderSize * 0.5, -borderSize * 0.5); + this.border.setSize( + this.details.width * s + borderSize, + this.details.height * s + borderSize, + ); + this.border.setStrokeStyle(borderSize, 0xfc4445, 1); + this.border.setOrigin(0, 0); + } + exportLayer(): MapLayer { const out: MapLayer = Object.assign({}, this.details); if (out.levelName) { diff --git a/webapp/src/app/services/phaser/tilemap/cc-map.ts b/webapp/src/app/services/phaser/tilemap/cc-map.ts index 8bc1a4d5..118c6b1f 100644 --- a/webapp/src/app/services/phaser/tilemap/cc-map.ts +++ b/webapp/src/app/services/phaser/tilemap/cc-map.ts @@ -135,8 +135,13 @@ export class CCMap { this.mapWidth = width; this.mapHeight = height; - this.layers.forEach(layer => layer.resize(width, height)); - Globals.phaserEventsService.updateMapBorder.next(true); + this.layers.forEach(layer => { + // only update layers with distance: 1 + // Parallax Layers shouldn't be updated because they usually have different dimensions than the map + if (layer.details.distance === 1) { + layer.resize(width, height); + } + }); } offsetMap(offset: Point, borderTiles = false) { diff --git a/webapp/src/app/services/phaser/tilemap/tile-drawer.ts b/webapp/src/app/services/phaser/tilemap/tile-drawer.ts index 75a6f65d..47ccd924 100644 --- a/webapp/src/app/services/phaser/tilemap/tile-drawer.ts +++ b/webapp/src/app/services/phaser/tilemap/tile-drawer.ts @@ -99,7 +99,7 @@ export class TileDrawer extends BaseObject { return; } const pointer = this.scene.input.activePointer; - const p = Helper.worldToTile(pointer.worldX, pointer.worldY); + const p = Helper.worldToTile(pointer.worldX - this.layer.x, pointer.worldY - this.layer.y); // render selection border if (this.rightClickStart) { @@ -135,15 +135,15 @@ export class TileDrawer extends BaseObject { container.x = pointer.worldX; container.y = pointer.worldY; - if (container.x < 0) { + if (container.x < this.layer.x) { container.x -= Globals.TILE_SIZE; } - if (container.y < 0) { + if (container.y < this.layer.y) { container.y -= Globals.TILE_SIZE; } - container.x -= container.x % Globals.TILE_SIZE; - container.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); @@ -151,7 +151,7 @@ export class TileDrawer extends BaseObject { // 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) { + if (pointer.leftButtonDown() && pointer.downElement?.nodeName === 'CANVAS' && this.layer) { const finalPos = {x: 0, y: 0}; const startPos = {x: 0, y: 0}; @@ -297,7 +297,7 @@ export class TileDrawer extends BaseObject { // only start tile copy when cursor in bounds const pointer = this.scene.input.activePointer; - const p = Helper.worldToTile(pointer.worldX, pointer.worldY); + const p = Helper.worldToTile(pointer.worldX - this.layer.x, pointer.worldY - this.layer.y); if (!Helper.isInBounds(this.layer, p)) { return; } @@ -398,10 +398,14 @@ export class TileDrawer extends BaseObject { } const pointer = this.scene.input.activePointer; - const p = Helper.worldToTile(pointer.worldX, pointer.worldY); + const p = Helper.worldToTile(pointer.worldX - this.layer.x, pointer.worldY - this.layer.y); if (this.selectedTiles.length > 0) { Filler.fill(this.layer, this.selectedTiles[0].id, p); + Globals.stateHistoryService.saveState({ + name: 'fill', + icon: 'format_color_fill' + }); } } diff --git a/webapp/src/assets/ingame.png b/webapp/src/assets/ingame.png new file mode 100644 index 00000000..c208eb53 Binary files /dev/null and b/webapp/src/assets/ingame.png differ