mirror of
https://github.com/lscharen/iigs-game-engine.git
synced 2025-02-05 14:31:13 +00:00
Update tiled export to create code to start timer scripts for animated tiles
This commit is contained in:
parent
2ef67e0a1c
commit
7d7a54a731
@ -2,15 +2,14 @@
|
||||
* Read an exported Tiled project in JSON format and produce Merlin32 output files with
|
||||
* GTE-compatible setup code wrapped around it.
|
||||
*/
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { abort } = require('process');
|
||||
const process = require('process');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const process = require('process');
|
||||
const StringBuilder = require('string-builder');
|
||||
const parser = require('xml2json');
|
||||
const png2iigs = require('./png2iigs');
|
||||
const parser = require('xml2json');
|
||||
const png2iigs = require('./png2iigs');
|
||||
|
||||
main(process.argv.slice(2)).then(
|
||||
main(process.argv.slice(2)).then(
|
||||
() => process.exit(0),
|
||||
(e) => {
|
||||
console.error(e);
|
||||
@ -87,11 +86,100 @@ function getArg(argv, arg, fn, defaultValue) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
function writeTileAnimations(filename, animations) {
|
||||
const init = new StringBuilder();
|
||||
const scripts = new StringBuilder();
|
||||
|
||||
// First step is some initialization code that copies the first animated
|
||||
// tile data into the dynamic tile space
|
||||
const initLabel = 'TileAnimInit';
|
||||
init.appendLine(`${initLabel} ENT`);
|
||||
init.appendLine();
|
||||
for (const animation of animations) {
|
||||
// Get the first tile of the animation
|
||||
const firstTileId = animation.frames[0].tileId;
|
||||
|
||||
// Create code to copy it into the dynamic tile index location
|
||||
init.appendLine(' ldx #' + firstTileId);
|
||||
init.appendLine(' ldy #' + animation.dynTileId);
|
||||
init.appendLine(' jsr CopyTileToDyn');
|
||||
}
|
||||
|
||||
// Next, create the scripts to change the tile data based on the configured ticks delays.
|
||||
for (const animation of animations) {
|
||||
// Get the animation frames
|
||||
const frames = animation.frames;
|
||||
|
||||
// Look at the frames and get the number of ticks. We only support a uniform animation period.
|
||||
const numTicks = frames.map(f => f.ticks).reduce((x, y) => Math.min(x, y),Infinity);
|
||||
if (frames.some(f => f.ticks !== numTicks)) {
|
||||
console.warn(`Animated tiles must have a uniform animation delay. Setting ticks to ${numTicks}`);
|
||||
}
|
||||
|
||||
const label = `TileAnim_${animation.tileId}`;
|
||||
init.appendLine(` lda #${label}`);
|
||||
init.appendLine(` ldx #^${label}`);
|
||||
init.appendLine(` ldy #${numTicks}`);
|
||||
init.appendLine(` jsl StartScript`);
|
||||
|
||||
scripts.appendLine(label);
|
||||
for (let i = 0; i < frames.length ; i += 1) {
|
||||
const last = (i === (frames.length - 1));
|
||||
const command = 'YIELD+SET_DYN_TILE' + (last ? '+JUMP' : '');
|
||||
const jump = last ? `,-${frames.length - 1}` : '';
|
||||
|
||||
scripts.appendLine(` dw ${command},${frames[i].tileId},${animation.dynTileId}${jump}`);
|
||||
}
|
||||
}
|
||||
|
||||
init.appendLine(' rts');
|
||||
|
||||
fs.writeFileSync(filename, init.toString() + scripts.toString());
|
||||
}
|
||||
|
||||
function writeTiles(filename, tiles) {
|
||||
const tileSource = png2iigs.buildMerlinCodeForTiles(tiles);
|
||||
fs.writeFileSync(filename, tileSource);
|
||||
}
|
||||
|
||||
function findAnimatedTiles(tileset) {
|
||||
const animations = [];
|
||||
let dynTileId = 0;
|
||||
|
||||
if (tileset.tile) {
|
||||
for (const tile of tileset.tile) {
|
||||
if (!tile.animation) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const tileId = parseInt(tile.id, 10);
|
||||
const frames = tile.animation.frame.map(f => {
|
||||
const millis = parseInt(f.duration, 10);
|
||||
const ticksPerMillis = 60. / 1000.;
|
||||
return {
|
||||
tileId: parseInt(f.tileid, 10),
|
||||
ticks: Math.round(millis * ticksPerMillis),
|
||||
millis
|
||||
};
|
||||
});
|
||||
|
||||
animations.push({
|
||||
tileId,
|
||||
dynTileId,
|
||||
frames
|
||||
});
|
||||
|
||||
dynTileId += 1;
|
||||
if (dynTileId > 31) {
|
||||
console.warn('Only 32 animated tiles are supported');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return animations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Command line arguments
|
||||
*
|
||||
@ -145,6 +233,8 @@ async function main(argv) {
|
||||
let bg0TileSet = null;
|
||||
|
||||
for (const record of tileSets) {
|
||||
console.log('Looking for animated tiles...');
|
||||
const animations = findAnimatedTiles(record.tileset);
|
||||
console.log(`Importing tileset "${record.tileset.name}"`);
|
||||
const tiles = await readTileSet(workdir, record.tileset);
|
||||
|
||||
@ -152,7 +242,22 @@ async function main(argv) {
|
||||
console.log(`Writing tiles to ${outputFilename}`);
|
||||
writeTiles(outputFilename, tiles);
|
||||
console.log(`Writing complete`);
|
||||
|
||||
// Look for tiles with animation sequences. If found, this information need to be propagated
|
||||
// to the tilemap export to mark those tile IDs as Dynamic Tiles.
|
||||
//
|
||||
// Exporting the "animations" actually created two code stubs; one to copy the first
|
||||
// tile of the animation into the dynamic tile space during initialization and a second
|
||||
// that created the timer callbacks that replace the tile data based on the time animation
|
||||
// rate. We only have a VBL timer, so the animation time is rounded to the nearest
|
||||
// 1/60 of a second.
|
||||
if (animations.length > 0) {
|
||||
console.log('Writing tile animation ');
|
||||
const animationFilename = path.resolve(path.join(outdir, record.tileset.name + 'Anim.s'));
|
||||
writeTileAnimations(animationFilename, animations);
|
||||
console.log(`Writing complete`);
|
||||
|
||||
}
|
||||
bg0TileSet = tiles;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user