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 @@
+
+
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