Skip to content

Instantly share code, notes, and snippets.

@floriancargoet
Last active October 18, 2022 20:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save floriancargoet/653bdb165dece659ea36e2b4ac6642d1 to your computer and use it in GitHub Desktop.
Save floriancargoet/653bdb165dece659ea36e2b4ac6642d1 to your computer and use it in GitHub Desktop.

Usage

Copy the code of binksi_import_map.js in the console, then execute:

importMap(url);

Or to select a file on your computer:

importMap();
function U32ColorToRGBA(n) {
const u32a = new Uint32Array([n]);
const u8a = new Uint8ClampedArray(u32a.buffer);
return Array.from(u8a);
}
function U32ColorToHex(n) {
const [r, g, b] = U32ColorToRGBA(n);
const h = ("000000" + (r * 256 * 256 + g * 256 + b).toString(16)).slice(-6);
return "#" + h;
}
class ImportedMap {
tileIndex = 1;
tiles = {};
rooms = [];
mapColors = new Set();
constructor(img) {
this.createTilesFromMapImage(img);
}
get transparentColor() {
const [first] = this.mapColors;
return first;
}
createTilesFromMapImage(img) {
// Check the dimensions match a whole number of rooms.
const roomSize = 16 * TILE_PX;
if (img.height % roomSize !== 0 || img.width % roomSize !== 0) {
throw new Error(
`The dimensions of the map must be a multiple of ${roomSize}.`
);
}
// Create rooms and tiles.
const roomCols = img.width / roomSize;
const roomRows = img.height / roomSize;
// Canvas so we can read pixels for color detection.
const tileCtx = createRendering2D(TILE_PX, TILE_PX);
for (let row = 0; row < roomRows; row++) {
for (let col = 0; col < roomCols; col++) {
const room = [];
this.rooms.push(room);
// Iterate over tiles in room
for (let yRoom = 0; yRoom < 16; yRoom++) {
const roomLine = [];
room.push(roomLine);
for (let xRoom = 0; xRoom < 16; xRoom++) {
const yMap = row * 16 + yRoom;
const xMap = col * 16 + xRoom;
fillRendering2D(tileCtx);
tileCtx.drawImage(
img,
xMap * TILE_PX,
yMap * TILE_PX,
TILE_PX,
TILE_PX,
0,
0,
TILE_PX,
TILE_PX
);
const tile = this.getOrCreateTile(tileCtx);
roomLine.push(tile);
}
}
}
}
if (this.mapColors.size > 7) {
// FIXME: excluding transparency
throw new Error("A map can have at most 7 colors.");
}
}
getOrCreateTile(ctx) {
const imageData = ctx.getImageData(0, 0, TILE_PX, TILE_PX);
const uid = ctx.canvas.toDataURL("image/png");
if (!this.tiles[uid]) {
this.tiles[uid] = this.createTile(uid, imageData);
}
return this.tiles[uid];
}
createTile(uid, imageData) {
const d = new Uint32Array(imageData.data.buffer); // endianness dependant!
const colors = new Set();
// const counts = {};
for (let i = 0; i < d.length; i++) {
const color = d[i];
colors.add(color);
// counts[color] ??= 0;
// counts[color]++;
}
if (colors.size > 2) {
throw new Error("A tile can have at most 2 colors.");
}
for (const c of colors) {
this.mapColors.add(c);
}
return {
uid,
index: this.tileIndex++,
imageData,
colors: [...colors],
};
}
async drawTileset(ctx) {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
let x = 0,
y = 0;
for (const tile of Object.values(this.tiles)) {
ctx.putImageData(tile.imageData, x, y);
x += TILE_PX;
if (x === 16 * TILE_PX) {
x = 0;
y += TILE_PX;
}
}
// Ensure the tileset is white/transparent
const transparentColor = this.transparentColor;
withPixels(ctx, (pixels) => {
for (let i = 0; i < pixels.length; i++) {
if (pixels[i] === transparentColor) {
pixels[i] = 0;
} else {
pixels[i] = 0xffffffff;
}
}
});
}
}
async function importMap(url) {
if (typeof url !== "string") {
const [file] = await maker.pickFiles("image/png");
url = URL.createObjectURL(file);
}
const map = new ImportedMap(await loadImage(url));
// Create bipsi rooms & tiles
EDITOR.stateManager.makeChange(async (data) => {
// New tileset
const tileset = await EDITOR.forkTileset();
// Reset tiles & create new ones
const tiles = Object.values(map.tiles);
data.tiles = [];
for (let i = 0; i < tiles.length; i++) {
data.tiles.push({ id: tiles[i].index, frames: [i] });
}
resizeTileset(tileset, data.tiles);
EDITOR.tileBrowser.selectedTileIndex = 0;
// Draw tiles on new tileset
map.drawTileset(tileset);
// Palette
const colors = [...map.mapColors];
const palette = makeBlankPalette(0);
data.palettes = [palette];
for (let i = 0; i < colors.length; i++) {
palette.colors[i + 1] = U32ColorToHex(colors[i]);
}
// Rooms
const transparentColor = map.transparentColor;
map.rooms.forEach((mapRoom, i) => {
const overwrittenRoom = data.rooms[i];
const bipsiRoom = makeBlankRoom(i + 1, data.palettes[0].id);
if (overwrittenRoom) {
bipsiRoom.events = overwrittenRoom.events;
bipsiRoom.wallmap = overwrittenRoom.wallmap;
}
bipsiRoom.tilemap = mapRoom.map((line) => line.map((tile) => tile.index));
bipsiRoom.foremap = mapRoom.map((line) =>
line.map((tile) => {
// find which one is not the transparent color
let index;
if (tile.colors[0] !== transparentColor) {
index = colors.indexOf(tile.colors[0]);
} else {
index = colors.indexOf(tile.colors[1]);
}
return index + 1;
})
);
data.rooms[i] = bipsiRoom;
});
EDITOR.roomSelectWindow.select.selectedIndex = 0;
EDITOR.requestRedraw();
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment