Add support for TW large tilesets, real MS tilesets, better tileset detection, and an attempted fix for CC1 thin wall tiles

This commit is contained in:
Eevee (Evelyn Woods) committed 2021-06-03 02:15:45 -06:00
1 parent 3e7390ffc0
commit 51bc3dfe83
6 files changed
+743 -206

No files matched your search

+6 -38
View File
@@ -1,13 +1,14 @@
import { readFile, stat } from 'fs/promises';
import { performance } from 'perf_hooks';
import { argv, exit, stderr, stdout } from 'process';
import { compat_flags_for_ruleset } from '../defs.js';
import { Level } from '../game.js';
import * as format_c2g from '../format-c2g.js';
import * as format_dat from '../format-dat.js';
import * as format_tws from '../format-tws.js';
import * as util from '../util.js';
import { argv, exit, stderr, stdout } from 'process';
import { opendir, readFile, stat } from 'fs/promises';
import { performance } from 'perf_hooks';
import { LocalDirectorySource } from './lib.js';
// TODO arguments:
// - custom pack to test, possibly its solutions, possibly its ruleset (or default to steam-strict/lynx)
@@ -17,39 +18,6 @@ import { performance } from 'perf_hooks';
// - support for xfails somehow?
// TODO use this for a test suite
export class LocalDirectorySource extends util.FileSource {
constructor(root) {
super();
this.root = root;
this.files = {};
this._loaded_promise = this._scan_dir('/');
}
async _scan_dir(path) {
let dir = await opendir(this.root + path);
for await (let dirent of dir) {
if (dirent.isDirectory()) {
await this._scan_dir(path + dirent.name + '/');
}
else {
let filepath = path + dirent.name;
this.files[filepath.toLowerCase()] = filepath;
if (this.files.size > 2000)
throw `way, way too many files in local directory source ${this.root}`;
}
}
}
async get(path) {
let realpath = this.files[path.toLowerCase()];
if (realpath) {
return (await readFile(this.root + realpath)).buffer;
}
else {
throw new Error(`No such file: ${path}`);
}
}
}
function pad(s, n) {
return s.substring(0, n).padEnd(n, " ");
@@ -330,7 +298,7 @@ may be run with different compat modes.
don't support built-in replays, this must be a TWS file
-l level range to play back; either 'all' or a string like '1-4,10'
-f force the next argument to be interpreted as a file path, if for
some perverse reason you have a level file named '-c'
some perverse reason you have a level file named '-c'
-h, --help ignore other arguments and show this message
Supports the same filetypes as Lexy's Labyrinth: DAT/CCL, C2M, or a directory
+48
View File
@@ -0,0 +1,48 @@
import { opendir, readFile } from 'fs/promises';
import canvas from 'canvas';
import CanvasRenderer from '../renderer-canvas.js';
import * as util from '../util.js';
export class NodeCanvasRenderer extends CanvasRenderer {
static make_canvas(w, h) {
return canvas.createCanvas(w, h);
}
}
export class LocalDirectorySource extends util.FileSource {
constructor(root) {
super();
this.root = root;
this.files = {};
this._loaded_promise = this._scan_dir('/');
}
async _scan_dir(path) {
let dir = await opendir(this.root + path);
for await (let dirent of dir) {
if (dirent.isDirectory()) {
await this._scan_dir(path + dirent.name + '/');
}
else {
let filepath = path + dirent.name;
this.files[filepath.toLowerCase()] = filepath;
if (this.files.size > 2000)
throw `way, way too many files in local directory source ${this.root}`;
}
}
}
async get(path) {
let realpath = this.files[path.toLowerCase()];
if (realpath) {
return (await readFile(this.root + realpath)).buffer;
}
else {
throw new Error(`No such file: ${path}`);
}
}
}
+70
View File
@@ -0,0 +1,70 @@
import { readFile, writeFile } from 'fs/promises';
import * as process from 'process';
import canvas from 'canvas';
import minimist from 'minimist';
import * as format_c2g from '../format-c2g.js';
import { infer_tileset_from_image } from '../tileset.js';
import { NodeCanvasRenderer } from './lib.js';
const USAGE = `\
Usage: render.mjs [OPTION]... LEVELFILE OUTFILE
Renders the level contained in LEVELFILE to a PNG and saves it to OUTFILE.
Arguments:
-t FILE path to a tileset to use
-e render in editor mode: use the revealed forms of tiles and
show facing directions
-l NUM choose the level number to render, if LEVELFILE is a pack
[default: 1]
-r REGION specify the region to render; see below
REGION may be one of:
initial an area the size of the level's viewport, centered on the
player's initial position
all the entire level
WxH an area W by H, centered on the player's initial position
...etc...
`;
async function main() {
let args = minimist(process.argv.slice(2), {
alias: {
tileset: ['t'],
},
});
// assert _.length is 2
let [pack_path, dest_path] = args._;
// TODO i need a more consistent and coherent way to turn a path into a level pack, currently
// this is only a single c2m
let pack_data = await readFile(pack_path);
let stored_level = format_c2g.parse_level(pack_data.buffer);
let img = await canvas.loadImage(args.tileset ?? 'tileset-lexy.png');
let tileset = infer_tileset_from_image(img);
let renderer = new NodeCanvasRenderer(tileset);
renderer.set_level(stored_level);
let i = stored_level.linear_cells.findIndex(cell => cell.some(tile => tile && tile.type.is_real_player));
if (i < 0) {
console.log("???");
process.stderr.write("error: no players in this level\n");
process.exit(1);
}
let [x, y] = stored_level.scalar_to_coords(i);
let w = stored_level.viewport_size;
let h = w;
// TODO this is probably duplicated from the renderer, and could also be reused in the editor
// TODO handle a map smaller than the viewport
let x0 = Math.max(0, x - w / 2);
let y0 = Math.max(0, y - h / 2);
renderer.draw_static_region(x0, y0, x0 + w, y0 + h);
await writeFile(dest_path, renderer.canvas.toBuffer());
}
main();