diff --git a/app/handlers/map.ts b/app/handlers/map.ts
index 6bc390a..7ab7134 100644
--- a/app/handlers/map.ts
+++ b/app/handlers/map.ts
@@ -1,9 +1,103 @@
-export function ensureMap(name: string, mapName: string) {}
+import * as childProcess from 'child_process';
+import * as fs from 'fs-extra';
+import { baseUrl } from '../helpers';
-export function newMap(name: string, creator: string) {}
+export const fixTiledMapPaths = (map: any) => {
+ map.tilesets.forEach((tileset: any) => {
+ tileset.image = `../../__assets/spritesheets/${tileset.name.toLowerCase()}.png`;
+ });
+};
-export function renameMap(oldName: string, newName: string) {}
+export function ensureMap(mapName: string, mapData: any) {
+ const path = `${baseUrl}/resources/maps/src/content/maps/custom/${mapName}.json`;
+ fs.writeFileSync(path, JSON.stringify(mapData));
+}
-export function editMap(name: string) {}
+export function newMap(mapName: string, mapAuthor: string) {
+ const fileName = mapName.replace(/[^a-zA-Z-]/g, '');
+ const templatePath = `${baseUrl}/resources/maps/src/content/maps/custom/Template.json`;
+ const path = `${baseUrl}/resources/maps/src/content/maps/custom/${fileName}.json`;
-export function editMapSpawnerNames(oldName: string, newName: string) {}
+ if (!fs.existsSync(templatePath)) {
+ throw new Error('Template is gone.');
+ }
+
+ if (fs.existsSync(path)) {
+ throw new Error('Map already exists');
+ }
+
+ const json = fs.readJSONSync(templatePath);
+ json.properties.creator = mapAuthor;
+ json.propertytypes.creator = 'string';
+
+ fs.writeJSONSync(path, json);
+
+ editMap(fileName);
+
+ return json;
+}
+
+export function copyMap(mapName: string) {
+ const oldPath = `${baseUrl}/resources/maps/src/content/maps/custom/${mapName}.json`;
+ const newPath = `${baseUrl}/resources/maps/src/content/maps/custom/${mapName} (copy).json`;
+
+ if (fs.existsSync(newPath)) {
+ throw new Error('A map by that name already exists.');
+ }
+
+ fs.copySync(oldPath, newPath);
+}
+
+export function renameMap(oldName: string, newName: string) {
+ const oldPath = `${baseUrl}/resources/maps/src/content/maps/custom/${oldName}.json`;
+ const newPath = `${baseUrl}/resources/maps/src/content/maps/custom/${newName}.json`;
+
+ if (fs.existsSync(newPath)) {
+ throw new Error('A map by that name already exists.');
+ }
+
+ fs.moveSync(oldPath, newPath);
+}
+
+export function removeMap(mapName: string) {
+ const oldPath = `${baseUrl}/resources/maps/src/content/maps/custom/${mapName}.json`;
+ const newPath = `${baseUrl}/resources/maps/src/content/maps/custom/${mapName}.bak.json`;
+
+ fs.moveSync(oldPath, newPath, { overwrite: true });
+}
+
+export function editMap(mapName: string) {
+ if (!fs.existsSync(`${baseUrl}/resources/Tiled`)) {
+ throw new Error('Tiled is not installed.');
+ }
+
+ const path = `${baseUrl}/resources/maps/src/content/maps/custom/${mapName}.json`;
+
+ const map = fs.readJsonSync(path);
+ fixTiledMapPaths(map);
+ fs.writeJsonSync(path, map);
+
+ childProcess.exec(`${baseUrl}/resources/Tiled/tiled.exe "${path}"`);
+}
+
+export function editMapSpawnerNames(oldName: string, newName: string) {
+ fs.readdirSync(`${baseUrl}/resources/maps/src/content/maps/custom`).forEach(
+ (file) => {
+ const path = `${baseUrl}/resources/maps/src/content/maps/custom/${file}`;
+ const json = fs.readJSONSync(path);
+
+ let didWrite = false;
+
+ json.layers[10].objects.forEach((spawner: any) => {
+ if (spawner.tag !== oldName) return;
+
+ spawner.tag = newName;
+ didWrite = true;
+ });
+
+ if (didWrite) {
+ fs.writeJSONSync(path, json);
+ }
+ }
+ );
+}
diff --git a/app/ipc.ts b/app/ipc.ts
index 61ea1dd..1a2f510 100644
--- a/app/ipc.ts
+++ b/app/ipc.ts
@@ -73,8 +73,38 @@ export function setupIPC(sendToUI: SendToUI) {
});
ipcMain.on('RENAME_MAP', async (e: any, data: any) => {
- handlers.renameMap(data.oldName, data.newName);
- sendToUI('renamemap', data);
+ try {
+ handlers.renameMap(data.oldName, data.newName);
+ sendToUI('renamemap', data);
+ } catch (e) {
+ sendToUI('notify', {
+ type: 'error',
+ text: 'A map by that name already exists.',
+ });
+ }
+ });
+
+ ipcMain.on('REMOVE_MAP', async (e: any, data: any) => {
+ try {
+ handlers.removeMap(data.mapName);
+ } catch (e) {
+ sendToUI('notify', {
+ type: 'error',
+ text: 'Could not fully delete map for some reason.',
+ });
+ }
+ });
+
+ ipcMain.on('COPY_MAP', async (e: any, data: any) => {
+ try {
+ handlers.copyMap(data.mapName);
+ sendToUI('copymap', data);
+ } catch (e) {
+ sendToUI('notify', {
+ type: 'error',
+ text: 'A map by that name already exists.',
+ });
+ }
});
ipcMain.on('EDIT_MAP', async (e: any, data: any) => {
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 534f726..3383e8d 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -13,6 +13,11 @@ import { HomeModule } from './home/home.module';
import { provideHotToastConfig } from '@ngxpert/hot-toast';
import { SweetAlert2Module } from '@sweetalert2/ngx-sweetalert2';
+import {
+ NgxFloatUiModule,
+ NgxFloatUiPlacements,
+ NgxFloatUiTriggers,
+} from 'ngx-float-ui';
import {
provideNgxWebstorage,
withLocalStorage,
@@ -29,6 +34,12 @@ import { AppComponent } from './app.component';
SharedModule,
HomeModule,
AppRoutingModule,
+ NgxFloatUiModule.forRoot({
+ trigger: NgxFloatUiTriggers.hover,
+ showDelay: 500,
+ placement: NgxFloatUiPlacements.TOPEND,
+ appendTo: 'body',
+ }),
SweetAlert2Module.forRoot({
provideSwal: () => import('sweetalert2/dist/sweetalert2.js'),
}),
diff --git a/src/app/home/home.module.ts b/src/app/home/home.module.ts
index 06dabc8..00231d9 100644
--- a/src/app/home/home.module.ts
+++ b/src/app/home/home.module.ts
@@ -5,11 +5,7 @@ import { HomeRoutingModule } from './home-routing.module';
import { SweetAlert2Module } from '@sweetalert2/ngx-sweetalert2';
import { AgGridModule } from 'ag-grid-angular';
-import {
- NgxFloatUiModule,
- NgxFloatUiPlacements,
- NgxFloatUiTriggers,
-} from 'ngx-float-ui';
+import { NgxFloatUiModule } from 'ngx-float-ui';
import { SharedModule } from '../shared/shared.module';
import { DialogsComponent } from '../tabs/dialogs/dialogs.component';
import { DroptablesComponent } from '../tabs/droptables/droptables.component';
@@ -41,11 +37,7 @@ import { HomeComponent } from './home.component';
HomeRoutingModule,
SweetAlert2Module,
AgGridModule,
- NgxFloatUiModule.forRoot({
- trigger: NgxFloatUiTriggers.hover,
- showDelay: 500,
- placement: NgxFloatUiPlacements.TOPEND,
- }),
+ NgxFloatUiModule,
],
})
export class HomeModule {}
diff --git a/src/app/services/electron.service.ts b/src/app/services/electron.service.ts
index 14ad37f..bb79cbb 100644
--- a/src/app/services/electron.service.ts
+++ b/src/app/services/electron.service.ts
@@ -44,7 +44,7 @@ export class ElectronService {
});
window.api.receive('newmap', (mapData) => {
- this.modService.addMap(mapData);
+ this.modService.addMap(mapData as { name: string; map: any });
});
window.api.receive('renamemap', (nameData) => {
@@ -54,6 +54,10 @@ export class ElectronService {
);
});
+ window.api.receive('copymap', (nameData) => {
+ this.modService.copyMap(nameData.mapName as string);
+ });
+
window.api.receive('json', (jsonData) => {
this.modService.setJSON(jsonData.name as string, jsonData.data);
});
diff --git a/src/app/services/mod.service.ts b/src/app/services/mod.service.ts
index 016938c..8cbb8d9 100644
--- a/src/app/services/mod.service.ts
+++ b/src/app/services/mod.service.ts
@@ -44,6 +44,8 @@ export class ModService {
const newModData = this.mod();
this.localStorage.store('mod', newModData);
});
+
+ this.ensureMapsExist();
}
// mod functions
@@ -93,11 +95,43 @@ export class ModService {
}
// map functions
- public addMap(map: any) {
- if (map.name === 'Template') return;
+ private ensureMapsExist() {
+ this.mod().maps.forEach((map) => {
+ window.api.send('ENSURE_MAP', { ...map });
+ });
+ }
+
+ public importMap(incomingMap: { name: string; map: any }) {
+ this.addMap(incomingMap);
+ window.api.send('ENSURE_MAP', { ...incomingMap });
+ }
+
+ public addMap(incomingMap: { name: string; map: any }) {
+ if (incomingMap.name === 'Template') return;
const mod = this.mod();
- mod.maps.push(map);
+ if (!mod.meta.name) mod.meta.name = incomingMap.name;
+
+ const existingMap = mod.maps.findIndex((x) => x.name === incomingMap.name);
+ if (existingMap !== -1) {
+ mod.maps.splice(existingMap, 1, incomingMap);
+ } else {
+ mod.maps.push(incomingMap);
+ }
+
+ this.updateMod(mod);
+ }
+
+ public copyMap(mapName: string) {
+ const mod = this.mod();
+
+ const existingMap = mod.maps.find((x) => x.name === mapName);
+ if (!existingMap) return;
+
+ const newMap = structuredClone(existingMap);
+ newMap.name = `${mapName} (copy)`;
+
+ mod.maps.push(newMap);
this.updateMod(mod);
}
@@ -111,6 +145,28 @@ export class ModService {
mapRef.name = newName;
+ this.updateMapNameAcrossMod(oldName, newName);
+ this.updateMod(mod);
+ }
+
+ private updateMapNameAcrossMod(oldName: string, newName: string) {
+ const mod = this.mod();
+ mod.drops.forEach((droptable) => {
+ if (droptable.mapName !== oldName) return;
+
+ droptable.mapName = newName;
+ });
+
+ this.updateMod(mod);
+ }
+
+ public removeMap(removeMap: { name: string; map: any }) {
+ const mod = this.mod();
+ const existingMap = mod.maps.findIndex((x) => x.name === removeMap.name);
+ if (existingMap !== -1) {
+ mod.maps.splice(existingMap, 1);
+ }
+
this.updateMod(mod);
}
diff --git a/src/app/shared/components/cell-buttons/cell-buttons.component.html b/src/app/shared/components/cell-buttons/cell-buttons.component.html
index 65ddde2..323a816 100644
--- a/src/app/shared/components/cell-buttons/cell-buttons.component.html
+++ b/src/app/shared/components/cell-buttons/cell-buttons.component.html
@@ -1,6 +1,6 @@
@if(params.showCopyButton) {
-