diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3b772862..259c0906 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,12 @@ 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
+- Render dimensions when selecting multiple tiles [#310](https://github.com/CCDirectLink/crosscode-map-editor/issues/310)
+
+### Changed
+- Increased font resolution for entity names
+
## [1.2.0] 2024-01-30
### Added
- Toggle in settings that also shows the vanilla maps in the map selection menu
diff --git a/webapp/src/app/components/captions/captions.component.html b/webapp/src/app/components/captions/captions.component.html
index 88819bf9..0b8ff06f 100644
--- a/webapp/src/app/components/captions/captions.component.html
+++ b/webapp/src/app/components/captions/captions.component.html
@@ -1,2 +1,8 @@
{{ version }}
-{{ coords }}
+
+
+
+ {{el.text}}
+
+
+
diff --git a/webapp/src/app/components/captions/captions.component.scss b/webapp/src/app/components/captions/captions.component.scss
index 3e845772..8c76c985 100644
--- a/webapp/src/app/components/captions/captions.component.scss
+++ b/webapp/src/app/components/captions/captions.component.scss
@@ -13,10 +13,10 @@
right: 0;
}
-.coords {
+.bottom-elements {
background-color: #0005;
- &.inactive {
+ &:empty {
display: none;
}
}
diff --git a/webapp/src/app/components/captions/captions.component.ts b/webapp/src/app/components/captions/captions.component.ts
index 6ec461cf..39f901aa 100644
--- a/webapp/src/app/components/captions/captions.component.ts
+++ b/webapp/src/app/components/captions/captions.component.ts
@@ -2,6 +2,11 @@ import { Component, OnInit } from '@angular/core';
import { environment } from '../../../environments/environment';
import { Globals } from '../../services/globals';
+export interface BottomUiElement {
+ text?: string;
+ active?: boolean;
+}
+
@Component({
selector: 'app-captions',
templateUrl: './captions.component.html',
@@ -9,16 +14,23 @@ import { Globals } from '../../services/globals';
})
export class CaptionsComponent implements OnInit {
version = environment.version;
- coords = '';
- coordsClass = 'inactive';
-
+ coords: BottomUiElement = {};
+ selectionSize: BottomUiElement = {};
+
+ uiElements: BottomUiElement[] = [
+ this.coords,
+ this.selectionSize
+ ];
+
ngOnInit(): void {
- Globals.globalEventsService.updateCoords.subscribe((coords) => {
- this.coords = !coords
- ? ''
- : `(${coords.x}, ${coords.y}, ${coords.z})`;
-
- this.coordsClass = coords ? '' : 'inactive';
+ Globals.globalEventsService.updateCoords.subscribe(coords => {
+ this.coords.text = `(${coords?.x}, ${coords?.y}, ${coords?.z})`;
+ this.coords.active = !!coords;
+ });
+
+ Globals.globalEventsService.updateTileSelectionSize.subscribe(size => {
+ this.selectionSize.text = `${size?.x}x${size?.y}`;
+ this.selectionSize.active = !!size;
});
}
}
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 5339b75b..4589a4c2 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
@@ -237,6 +237,10 @@ export class TileSelectorScene extends Phaser.Scene {
}
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);
- this.rect.setStrokeStyle(1, 0xffffff, 0.6);
+ if (Globals.settingsService.getSettings().selectionBoxDark) {
+ this.rect.setStrokeStyle(2, 0x333333, 0.9);
+ } else {
+ this.rect.setStrokeStyle(2, 0xffffff, 0.6);
+ }
}
}
diff --git a/webapp/src/app/components/dialogs/settings/settings.component.html b/webapp/src/app/components/dialogs/settings/settings.component.html
index 81219f6c..1d861f24 100644
--- a/webapp/src/app/components/dialogs/settings/settings.component.html
+++ b/webapp/src/app/components/dialogs/settings/settings.component.html
@@ -15,7 +15,7 @@
Select
Check
- {{icon}}
+ {{ icon }}
@@ -24,21 +24,43 @@
Mod
None
- {{mod}}
+ {{ mod }}
Maps will be stored and loaded from the selected mod
-
+
Include vanilla maps
-
+
Wrap event editor lines
+
+
Selection Box Style
+
+
diff --git a/webapp/src/app/components/dialogs/settings/settings.component.ts b/webapp/src/app/components/dialogs/settings/settings.component.ts
index 4305f06d..babb79ee 100644
--- a/webapp/src/app/components/dialogs/settings/settings.component.ts
+++ b/webapp/src/app/components/dialogs/settings/settings.component.ts
@@ -6,9 +6,10 @@ import { BrowserService } from '../../../services/browser.service';
import { ElectronService } from '../../../services/electron.service';
import { Globals } from '../../../services/globals';
import { HttpClientService } from '../../../services/http-client.service';
-import { SettingsService } from '../../../services/settings.service';
+import { AppSettings, SettingsService } from '../../../services/settings.service';
import { SharedService } from '../../../services/shared-service';
import { OverlayRefControl } from '../overlay/overlay-ref-control';
+import { PropListCard } from '../../widgets/shared/image-select-overlay/image-select-card/image-select-card.component';
@Component({
selector: 'app-settings',
@@ -16,19 +17,28 @@ import { OverlayRefControl } from '../overlay/overlay-ref-control';
styleUrls: ['./settings.component.scss']
})
export class SettingsComponent implements OnInit {
-
+
isElectron = Globals.isElectron;
folderFormControl = new FormControl();
icon = 'help_outline';
iconCss = 'icon-undefined';
mods: string[] = [];
mod = '';
- wrapEventEditorLines: boolean;
- includeVanillaMaps: boolean;
+ settings: AppSettings;
isIncludeVanillaMapsDisabled: boolean;
-
+
+ cardLight: PropListCard = {
+ name: 'Light',
+ imgSrc: 'assets/selection-light.png',
+ };
+
+ cardDark: PropListCard = {
+ name: 'Dark',
+ imgSrc: 'assets/selection-dark.png',
+ };
+
private readonly sharedService: SharedService;
-
+
constructor(
private ref: OverlayRefControl,
private electron: ElectronService,
@@ -42,28 +52,27 @@ export class SettingsComponent implements OnInit {
} else {
this.sharedService = browser;
}
-
+
http.getMods().subscribe(mods => this.mods = mods);
this.mod = this.sharedService.getSelectedMod();
this.isIncludeVanillaMapsDisabled = !this.mod;
- this.wrapEventEditorLines = this.settingsService.wrapEventEditorLines;
- this.includeVanillaMaps = this.settingsService.includeVanillaMaps;
+ this.settings = JSON.parse(JSON.stringify(this.settingsService.getSettings()));
}
-
+
ngOnInit() {
if (this.isElectron) {
this.folderFormControl.setValue(this.electron.getAssetsPath());
this.folderFormControl.valueChanges.subscribe(() => this.resetIcon());
}
-
+
this.check();
}
-
+
private resetIcon() {
this.icon = 'help_outline';
this.iconCss = 'icon-undefined';
}
-
+
private setIcon(valid: boolean) {
if (valid) {
this.icon = 'check';
@@ -73,14 +82,14 @@ export class SettingsComponent implements OnInit {
this.iconCss = 'icon-invalid';
}
}
-
+
select() {
const path = this.electron.selectCcFolder();
if (path) {
this.folderFormControl.setValue(path);
}
}
-
+
check() {
const valid = this.electron.checkAssetsPath(this.folderFormControl.value);
this.setIcon(valid);
@@ -92,28 +101,27 @@ export class SettingsComponent implements OnInit {
});
}
}
-
+
modSelectEvent(selectedMod: string) {
this.isIncludeVanillaMapsDisabled = !selectedMod;
}
-
+
save() {
if (this.isElectron) {
this.electron.saveAssetsPath(this.folderFormControl.value);
}
this.sharedService.saveModSelect(this.mod);
- this.settingsService.wrapEventEditorLines = this.wrapEventEditorLines;
- this.settingsService.includeVanillaMaps = this.includeVanillaMaps;
+ this.settingsService.updateSettings(this.settings);
this.close();
const ref = this.snackBar.open('Changing the path requires to restart the editor', 'Restart', {
duration: 6000
});
-
+
ref.onAction().subscribe(() => this.sharedService.relaunch());
}
-
+
close() {
this.ref.close();
}
-
+
}
diff --git a/webapp/src/app/components/phaser/phaser.component.ts b/webapp/src/app/components/phaser/phaser.component.ts
index 2043365e..62cb566c 100644
--- a/webapp/src/app/components/phaser/phaser.component.ts
+++ b/webapp/src/app/components/phaser/phaser.component.ts
@@ -12,6 +12,7 @@ import { MainScene } from '../../services/phaser/main-scene';
import { PhaserEventsService } from '../../services/phaser/phaser-events.service';
import { StateHistoryService } from '../dialogs/floating-window/history/state-history.service';
import { MatSnackBar } from '@angular/material/snack-bar';
+import { SettingsService } from '../../services/settings.service';
@Component({
selector: 'app-phaser',
@@ -32,7 +33,8 @@ export class PhaserComponent implements AfterViewInit {
private http: HttpClientService,
snackbar: MatSnackBar,
registry: EntityRegistryService,
- autotile: AutotileService
+ autotile: AutotileService,
+ settingsService: SettingsService
) {
Globals.stateHistoryService = stateHistory;
Globals.mapLoaderService = mapLoader;
@@ -42,6 +44,7 @@ export class PhaserComponent implements AfterViewInit {
Globals.entityRegistry = registry;
Globals.httpService = http;
Globals.snackbar = snackbar;
+ Globals.settingsService = settingsService;
}
diff --git a/webapp/src/app/components/widgets/event-widget/event-editor/editor/event-editor.component.ts b/webapp/src/app/components/widgets/event-widget/event-editor/editor/event-editor.component.ts
index d3a8ae69..932a4956 100644
--- a/webapp/src/app/components/widgets/event-widget/event-editor/editor/event-editor.component.ts
+++ b/webapp/src/app/components/widgets/event-widget/event-editor/editor/event-editor.component.ts
@@ -65,7 +65,7 @@ export class EventEditorComponent implements OnChanges, OnInit {
}
ngOnInit() {
- this.wrapText = this.settingsService.wrapEventEditorLines;
+ this.wrapText = this.settingsService.getSettings().wrapEventEditorLines;
}
ngOnChanges() {
diff --git a/webapp/src/app/services/global-events.service.ts b/webapp/src/app/services/global-events.service.ts
index 0226dcf6..edc62133 100644
--- a/webapp/src/app/services/global-events.service.ts
+++ b/webapp/src/app/services/global-events.service.ts
@@ -21,6 +21,7 @@ export class GlobalEventsService {
showAddEntityMenu = new Subject();
updateCoords = new Subject();
+ updateTileSelectionSize = new Subject();
showIngamePreview = new BehaviorSubject(false);
hasUnsavedChanges = new BehaviorSubject(false);
diff --git a/webapp/src/app/services/globals.ts b/webapp/src/app/services/globals.ts
index 2111ed55..c0a20497 100644
--- a/webapp/src/app/services/globals.ts
+++ b/webapp/src/app/services/globals.ts
@@ -7,6 +7,7 @@ import { EntityRegistryService } from './phaser/entities/registry/entity-registr
import { PhaserEventsService } from './phaser/phaser-events.service';
import { CCMap } from './phaser/tilemap/cc-map';
import { MatSnackBar } from '@angular/material/snack-bar';
+import { SettingsService } from './settings.service';
export class Globals {
static isElectron = false;
@@ -30,5 +31,6 @@ export class Globals {
static autotileService: AutotileService;
static entityRegistry: EntityRegistryService;
static httpService: HttpClientService;
+ static settingsService: SettingsService;
static snackbar: MatSnackBar;
}
diff --git a/webapp/src/app/services/http-client.service.ts b/webapp/src/app/services/http-client.service.ts
index 88832b7e..f4a3de0a 100644
--- a/webapp/src/app/services/http-client.service.ts
+++ b/webapp/src/app/services/http-client.service.ts
@@ -30,7 +30,7 @@ export class HttpClientService {
}
getMaps(): Observable {
- const includeVanillaMaps: boolean = this.settingsService.includeVanillaMaps;
+ const includeVanillaMaps = this.settingsService.getSettings().includeVanillaMaps;
return this.request(`api/allMaps?includeVanillaMaps=${includeVanillaMaps}`, api.getAllMaps, includeVanillaMaps);
}
diff --git a/webapp/src/app/services/phaser/entities/cc-entity.ts b/webapp/src/app/services/phaser/entities/cc-entity.ts
index 7e044e08..2cbef319 100644
--- a/webapp/src/app/services/phaser/entities/cc-entity.ts
+++ b/webapp/src/app/services/phaser/entities/cc-entity.ts
@@ -649,6 +649,7 @@ export abstract class CCEntity extends BaseObject {
this.text = this.scene.add.text(0, 0, '', {
font: '400 18pt Roboto',
color: 'white',
+ resolution: window.devicePixelRatio * 3
});
this.text.setOrigin(0.5, 0.5);
this.text.setScale(0.3);
diff --git a/webapp/src/app/services/phaser/tilemap/tile-drawer.ts b/webapp/src/app/services/phaser/tilemap/tile-drawer.ts
index 47ccd924..b694b5f0 100644
--- a/webapp/src/app/services/phaser/tilemap/tile-drawer.ts
+++ b/webapp/src/app/services/phaser/tilemap/tile-drawer.ts
@@ -15,7 +15,7 @@ export class TileDrawer extends BaseObject {
private layer?: CCMapLayer;
private selectedTiles: SelectedTile[] = [];
- private rect?: Phaser.GameObjects.Rectangle;
+ private selection?: Phaser.GameObjects.Container;
private previewTileMap!: Phaser.Tilemaps.Tilemap;
private previewLayer?: Phaser.Tilemaps.TilemapLayer;
@@ -126,7 +126,7 @@ export class TileDrawer extends BaseObject {
diff.y--;
}
- this.drawRect(diff.x, diff.y, start.x, start.y);
+ this.drawRect(diff.x, diff.y, start.x, start.y, true);
return;
}
@@ -307,15 +307,64 @@ export class TileDrawer extends BaseObject {
this.rightClickStart = p;
}
- private drawRect(width: number, height: number, x = 0, y = 0) {
- if (this.rect) {
- this.rect.destroy();
+ private drawRect(width: number, height: number, x = 0, y = 0, renderSize = false) {
+ if (this.selection) {
+ this.selection.destroy();
}
- this.rect = this.scene.add.rectangle(x, y, width * Globals.TILE_SIZE, height * Globals.TILE_SIZE);
- this.rect.setOrigin(0, 0);
- this.rect.setStrokeStyle(1, 0xffffff, 0.6);
- this.container.add(this.rect);
+ 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() {
diff --git a/webapp/src/app/services/settings.service.ts b/webapp/src/app/services/settings.service.ts
index 884feaf8..36fa95a7 100644
--- a/webapp/src/app/services/settings.service.ts
+++ b/webapp/src/app/services/settings.service.ts
@@ -1,28 +1,38 @@
import { Injectable } from '@angular/core';
+export interface AppSettings {
+ wrapEventEditorLines: boolean;
+ includeVanillaMaps: boolean;
+ selectionBoxDark: boolean;
+}
+
@Injectable({
providedIn: 'root'
})
export class SettingsService {
- private static readonly wrapSettingName = 'wrapEventEditorLines';
- private static readonly includeVanillaMapsSettingName = 'includeVanillaMaps';
-
- private static loadBooleanOrDefault(key: string, defaultValue: boolean): boolean {
+
+ private settings: AppSettings = {
+ wrapEventEditorLines: this.loadBooleanOrDefault('wrapEventEditorLines', true),
+ includeVanillaMaps: this.loadBooleanOrDefault('includeVanillaMaps', false),
+ selectionBoxDark: this.loadBooleanOrDefault('selectionBoxDark', true),
+ };
+
+ getSettings(): Readonly {
+ return this.settings;
+ }
+
+ private loadBooleanOrDefault(key: keyof AppSettings, defaultValue: boolean): boolean {
const loadedValue = localStorage.getItem(key);
return loadedValue === null ? defaultValue : (loadedValue === 'true');
}
-
- get wrapEventEditorLines() {
- return SettingsService.loadBooleanOrDefault(SettingsService.wrapSettingName, true);
- }
- set wrapEventEditorLines(value: boolean) {
- localStorage.setItem(SettingsService.wrapSettingName, value.toString());
- }
-
- get includeVanillaMaps() {
- return SettingsService.loadBooleanOrDefault(SettingsService.includeVanillaMapsSettingName, false);
- }
- set includeVanillaMaps(value: boolean) {
- localStorage.setItem(SettingsService.includeVanillaMapsSettingName, value.toString());
+
+ public updateSettings(newSettings: Partial) {
+ this.settings = {
+ ...this.settings,
+ ...newSettings
+ };
+ for (const [key, value] of Object.entries(this.settings)) {
+ localStorage.setItem(key, (value as boolean).toString());
+ }
}
}
diff --git a/webapp/src/assets/selection-dark.png b/webapp/src/assets/selection-dark.png
new file mode 100644
index 00000000..62e75bcf
Binary files /dev/null and b/webapp/src/assets/selection-dark.png differ
diff --git a/webapp/src/assets/selection-light.png b/webapp/src/assets/selection-light.png
new file mode 100644
index 00000000..b260e360
Binary files /dev/null and b/webapp/src/assets/selection-light.png differ