-
Notifications
You must be signed in to change notification settings - Fork 372
Expand file tree
/
Copy pathcybergrindHandler.ts
More file actions
126 lines (103 loc) · 4.98 KB
/
Copy pathcybergrindHandler.ts
File metadata and controls
126 lines (103 loc) · 4.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import type { FileData, FileFormat, FormatHandler } from "../FormatHandler.ts";
import CommonFormats, { Category } from "src/CommonFormats.ts";
import { BadMagicError, EOFError, InitializationError } from "src/errors.ts";
class cybergrindHandler implements FormatHandler {
public name: string = "cybergrind";
public supportedFormats?: FileFormat[];
public ready: boolean = false;
#canvas?: HTMLCanvasElement;
#ctx?: CanvasRenderingContext2D;
async init () {
this.supportedFormats = [
CommonFormats.PNG.supported("png", true, false),
{
name: "ULTRAKILL CyberGrind Pattern",
format: "cgp",
extension: "cgp",
mime: "text/plain",
category: Category.DATA,
from: false,
to: true,
internal: "cgp",
lossless: false,
}
];
this.#canvas = document.createElement("canvas");
this.#ctx = this.#canvas.getContext("2d") || undefined;
this.ready = true;
}
async doConvert (
inputFiles: FileData[],
inputFormat: FileFormat,
outputFormat: FileFormat
): Promise<FileData[]> {
const encoder = new TextEncoder();
const outputFiles: FileData[] = [];
if (inputFormat.internal !== "png" || outputFormat.internal !== "cgp") {
throw new TypeError(`Unsupported output format: ${outputFormat.internal}`);
}
if (!this.#canvas || !this.#ctx) {
throw new InitializationError("Handler not initialized.");
}
for (const file of inputFiles) {
// take img and load
const blob = new Blob([file.bytes as BlobPart], { type: inputFormat.mime });
const image = new Image();
await new Promise((resolve, reject) => {
image.addEventListener("load", resolve);
image.addEventListener("error", reject);
image.src = URL.createObjectURL(blob);
});
// make canvas with 16x16
this.#canvas.width = 16;
this.#canvas.height = 16;
this.#ctx.drawImage(image, 0, 0, 16, 16);
const pixels = this.#ctx.getImageData(0, 0, 16, 16);
// mcmap's canvas logic used as a base!
// make the heights array and brightest of each pixel array
let heights = [];
let reds: { index: number, value: number}[] = [];
let greens: { index: number, value: number}[] = [];
let blues: { index: number, value: number}[] = [];
for (let i = 0; i < pixels.data.length; i += 4) {
const r = pixels.data[i];
const g = pixels.data[i + 1];
const b = pixels.data[i + 2];
const grayscale = 0.299 * r + 0.587 * g + 0.114 * b;
const height = Math.round((grayscale / 255) * 10); // map to 0-10 and round
heights.push(height);
reds.push({index: i / 4, value: r});
greens.push({index: i / 4, value: g});
blues.push({index: i / 4, value: b});
}
// take the 5 brightest pixels of each color, and have no duplicates
reds.sort((a, b) => b.value - a.value);
reds = reds.slice(0, 5);
const usedIndices = new Set(reds.map(p => p.index));
greens.sort((a, b) => b.value - a.value)
greens = greens.filter(p => !usedIndices.has(p.index)).slice(0, 5);
greens.forEach(p => usedIndices.add(p.index));
blues.sort((a, b) => b.value - a.value)
blues = blues.filter(p => !usedIndices.has(p.index)).slice(0, 5);
let cyberHeights: string = ``;
let enemyThing: string = ``;
for (let index = 0; index < heights.length; index++) {
if (index > 0 && index % 16 == 0)
{
cyberHeights += "\n";
enemyThing += "\n";
}
const height = heights[index];
cyberHeights += `${height >= 10 ? `(${height})` : `${height}`}`;
// H is Hideous mass, n is melee, p is ranged
// H is last so it will be less likely to exist
enemyThing += blues.some(x => x.index === index) ? 'H' : greens.some(x => x.index === index) ? 'n' : reds.some(x => x.index === index) ? 'H' : '0';
}
const outputBytes = encoder.encode(cyberHeights + "\n\n" + enemyThing);
const newName = file.name.replace(/\.[^/.]+$/, "") + ".cgp"; // name renaming stolen from textToPy.ts
outputFiles.push({ bytes: outputBytes, name: newName });
}
return outputFiles;
}
}
export default cybergrindHandler;