Skip to content

Commit

Permalink
Merge pull request #327 from CCDirectLink/autotiles
Browse files Browse the repository at this point in the history
Added custom json configs and improved autotiles
  • Loading branch information
Vegita2 authored Aug 1, 2024
2 parents ec37f1d + 48a8364 commit c68d2ac
Show file tree
Hide file tree
Showing 43 changed files with 3,329 additions and 873 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,4 @@ yarn-error.log
Thumbs.db

# scripts
*.bat
*.bat
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions backend/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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));
});
Expand Down
80 changes: 55 additions & 25 deletions common/src/controllers/api.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import { fsPromise, pathPromise } from '../require.js';
import { saveFile as save } from './saveFile.js';

export interface ModEditorConfig {
filename: string;
mod: string;
file: string;
}

const mods: string[] = [];
let packagesCache: Record<string, { folderName: string, displayName: string, ccmodDependencies?: Map<string, string> }>;

async function listAllFiles(dir: string, filelist: string[], ending: string, root?: string): Promise<string[]> {
if (root === undefined) {
root = dir;
}

const files = await tryReadDir(dir);
const promises: Promise<void>[] = [];
for (const file of files) {
Expand Down Expand Up @@ -48,7 +54,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);
Expand Down Expand Up @@ -84,12 +90,12 @@ function selectMod(name: string, packages: Record<string, { folderName: string,
if (!pkg) {
return;
}

result.push(pkg.folderName);
if (!pkg.ccmodDependencies) {
return;
}

for (const depName of Object.keys(pkg.ccmodDependencies)) {
selectMod(depName, packages, result);
}
Expand Down Expand Up @@ -125,16 +131,16 @@ async function readMods(dir: string) {
if (packagesCache) {
return packagesCache;
}

const fs = await fsPromise;
const path = await pathPromise;

const modFolder = path.join(dir, 'mods/');
const files = await searchSubFolder(modFolder, 'package.json');
const filesCCMod = await searchSubFolder(modFolder, 'ccmod.json');

const ccmodFolderNames = new Set(filesCCMod.map(file => path.basename(path.dirname(file))));

const promises: Promise<[string, Buffer]>[] = [];
for (const file of files) {
const folderName = path.basename(path.dirname(file));
Expand All @@ -146,7 +152,7 @@ async function readMods(dir: string) {
}
const rawPackages = await Promise.all(promises);
const packages: Record<string, { folderName: string, displayName: string, ccmodDependencies?: Map<string, string> }> = {};

for (const [name, pkg] of rawPackages) {
try {
const parsed = JSON.parse(pkg as unknown as string);
Expand All @@ -159,13 +165,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);
Expand All @@ -178,7 +184,7 @@ async function readMods(dir: string) {
console.error('Invalid json data in ccmod.json of mod: ' + name, err);
}
}

packagesCache = packages;
return packages;
}
Expand All @@ -187,43 +193,43 @@ 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));
}
if (mods.length > 0) {
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))
Expand All @@ -233,23 +239,47 @@ 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));
}

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<ModEditorConfig[]> {
const packages = await readMods(dir);

const fs = await fsPromise;
const path = await pathPromise;

const configs: ModEditorConfig[] = [];

for (const mod of Object.values(packages)) {
const modName = mod.folderName;
const mapEditorPath = path.join(dir, 'mods', modName, 'map-editor');
const files = await listAllFiles(mapEditorPath, [], 'json', path.resolve(dir));
for (const filename of files) {
const file = await fs.promises.readFile(path.join(dir, filename), 'utf-8');
configs.push({
filename: path.basename(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
Expand All @@ -264,7 +294,7 @@ export async function get<T>(dir: string, file: string): Promise<T> {
promises.push(getAsync(modFile));
}
promises.push(getAsync(path.join(dir, file)));

const results = await Promise.all(promises);
for (const result of results) {
if (result) {
Expand All @@ -282,7 +312,7 @@ export async function resolve(dir: string, file: string): Promise<string> {
promises.push(resolveAsync(modFile));
}
promises.push(resolveAsync(path.join(dir, file)));

const results = await Promise.all(promises);
for (const result of results) {
if (result) {
Expand Down
6 changes: 3 additions & 3 deletions install_all.sh
Original file line number Diff line number Diff line change
@@ -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
npm ci
10 changes: 3 additions & 7 deletions webapp/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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, {
Expand Down
10 changes: 5 additions & 5 deletions webapp/src/app/components/captions/captions.component.html
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<p class="version caption">{{ version }}</p>
<p class="bottom-elements caption">
<p class="bottom-container version caption">{{ version }}</p>
<p class="bottom-container bottom-elements">
<ng-container *ngFor="let el of uiElements">
<ng-container *ngIf="el.active">
{{el.text}}
</ng-container>
<span class="caption" *ngIf="el.active">
{{ el.text }}
</span>
</ng-container>
</p>
19 changes: 14 additions & 5 deletions webapp/src/app/components/captions/captions.component.scss
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
@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 {
right: 0;
}

.bottom-elements {
background-color: #0005;
display: flex;
gap: 8px;

&:empty {
display: none;
}

& .caption {
background-color: #0005;
}
}
10 changes: 9 additions & 1 deletion webapp/src/app/components/captions/captions.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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;
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ $toolbarHeight: 32px;
.container {
min-width: 100px;
position: absolute;
z-index: 100;
}

.ng-draggable {
Expand Down
Loading

0 comments on commit c68d2ac

Please sign in to comment.