silphscope 1.4.21 → 1.4.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +185 -83
- package/docs/node/renderAllBalls.md +0 -0
- package/docs/node/renderAllGraphics.md +0 -0
- package/docs/node/renderAllIcons.md +0 -0
- package/docs/node/renderAllMons.md +0 -0
- package/docs/node/renderAllMoves.md +0 -0
- package/docs/node/renderAllTrainers.md +0 -0
- package/docs/node/renderAllX-options.md +0 -0
- package/item-data/itemData.json +376 -1128
- package/main.js +149 -5
- package/mon-data/monData.json +440 -4373
- package/move-data/moveData.json +0 -609
- package/package.json +3 -2
- package/silphscope.d.ts +579 -502
- package/src/graphics/balls/render-ball-particle.js +48 -26
- package/src/graphics/balls/render-balls.js +45 -22
- package/src/graphics/graphics-extractor-main.js +155 -294
- package/src/graphics/icons/render-icons.js +14 -4
- package/src/graphics/mons/render-mon-foot.js +17 -5
- package/src/graphics/mons/render-mon-icon.js +27 -6
- package/src/graphics/mons/render-mons.js +16 -7
- package/src/graphics/moves/render-moves.js +44 -29
- package/src/graphics/trainers/render-trainer-back-pics.js +52 -10
- package/src/graphics/trainers/render-trainers.js +16 -19
- package/src/is-valid-filter-type.js +15 -0
- package/src/run-with-concurrency.js +12 -0
- package/src/validate-render-options.js +28 -0
- package/trainer-data/trainerData.json +148 -444
|
@@ -27,7 +27,8 @@ export async function renderMonIcon(monName, mons, reader, rom, options = {}) {
|
|
|
27
27
|
throw new TypeError("renderMonIcon(..., rom) requires a ROM Buffer/Uint8Array");
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
let
|
|
30
|
+
let fullFileCount = 0;
|
|
31
|
+
const results = returnFileBuffer? [] : null;
|
|
31
32
|
const mon = mons[monName];
|
|
32
33
|
if (!mon) {
|
|
33
34
|
throw new Error(`Missing mon entry for ${monName}`);
|
|
@@ -71,12 +72,32 @@ export async function renderMonIcon(monName, mons, reader, rom, options = {}) {
|
|
|
71
72
|
await fs.promises.mkdir(dir, { recursive: true });
|
|
72
73
|
await fs.promises.writeFile(`${dir}/icon_frame1.png`, buffer1);
|
|
73
74
|
await fs.promises.writeFile(`${dir}/icon_frame2.png`, buffer2);
|
|
74
|
-
|
|
75
|
+
fullFileCount += 2;
|
|
76
|
+
}
|
|
77
|
+
if (returnFileBuffer) {
|
|
78
|
+
results.push({
|
|
79
|
+
name: `${monName}-icon-frame1`,
|
|
80
|
+
category: "mon",
|
|
81
|
+
asset: "icon",
|
|
82
|
+
path: `out/mons/${monName}/icon_frame1`,
|
|
83
|
+
buffer: buffer1,
|
|
84
|
+
meta: {
|
|
85
|
+
frame: 1,
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
results.push({
|
|
89
|
+
name: `${monName}-icon-frame2`,
|
|
90
|
+
category: "mon",
|
|
91
|
+
asset: "icon",
|
|
92
|
+
path: `out/mons/${monName}/icon_frame2`,
|
|
93
|
+
buffer: buffer2,
|
|
94
|
+
meta: {
|
|
95
|
+
frame: 2,
|
|
96
|
+
},
|
|
97
|
+
});
|
|
75
98
|
}
|
|
76
|
-
|
|
77
99
|
return {
|
|
78
|
-
...(returnFileBuffer && {
|
|
79
|
-
|
|
80
|
-
fileCount,
|
|
100
|
+
...(returnFileBuffer && { results }),
|
|
101
|
+
fullFileCount,
|
|
81
102
|
};
|
|
82
103
|
}
|
|
@@ -45,10 +45,9 @@ export async function renderMon(monName, mons, reader, rom, options = {}) {
|
|
|
45
45
|
returnFileBuffer,
|
|
46
46
|
outputDir,
|
|
47
47
|
});
|
|
48
|
-
fullFileCount += monIconData.
|
|
48
|
+
fullFileCount += monIconData.fullFileCount;
|
|
49
49
|
if (returnFileBuffer) {
|
|
50
|
-
results.push(monIconData.
|
|
51
|
-
results.push(monIconData.frame2);
|
|
50
|
+
results.push(...monIconData.results);
|
|
52
51
|
}
|
|
53
52
|
}
|
|
54
53
|
if (footprint === true) {
|
|
@@ -58,9 +57,9 @@ export async function renderMon(monName, mons, reader, rom, options = {}) {
|
|
|
58
57
|
returnFileBuffer,
|
|
59
58
|
outputDir,
|
|
60
59
|
});
|
|
61
|
-
fullFileCount += monFootData.
|
|
60
|
+
fullFileCount += monFootData.fullFileCount;
|
|
62
61
|
if (returnFileBuffer) {
|
|
63
|
-
results.push(monFootData.
|
|
62
|
+
results.push(...monFootData.results);
|
|
64
63
|
}
|
|
65
64
|
}
|
|
66
65
|
|
|
@@ -108,12 +107,22 @@ export async function renderMon(monName, mons, reader, rom, options = {}) {
|
|
|
108
107
|
const dir = `${outputDir}/${monName}`;
|
|
109
108
|
const fileName = `${dir}/${side}${variant === "shiny" ? "_shiny" : ""}.png`;
|
|
110
109
|
await fs.promises.writeFile(fileName, pngBuffer);
|
|
110
|
+
fullFileCount += 1;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
if (returnFileBuffer) {
|
|
114
|
-
results.push(
|
|
114
|
+
results.push({
|
|
115
|
+
name: `${monName}-${variant === "shiny"? "shiny" : "normal"}-${side}-sprite`,
|
|
116
|
+
category: "mon",
|
|
117
|
+
asset: "sprite",
|
|
118
|
+
path: `out/mons/${monName}/${side}_${variant === "shiny"? "shiny" : "normal"}`,
|
|
119
|
+
buffer: pngBuffer,
|
|
120
|
+
meta: {
|
|
121
|
+
side: side,
|
|
122
|
+
variant: variant,
|
|
123
|
+
},
|
|
124
|
+
});
|
|
115
125
|
}
|
|
116
|
-
fullFileCount += 1;
|
|
117
126
|
}
|
|
118
127
|
}
|
|
119
128
|
|
|
@@ -49,7 +49,7 @@ export async function renderMove(moveName, moves, reader, rom, options = {}) {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
let fullFileCount = 0;
|
|
52
|
-
const results = returnFileBuffer? [] : null;
|
|
52
|
+
const results = returnFileBuffer ? [] : null;
|
|
53
53
|
const move = moves[moveName];
|
|
54
54
|
if (!move) {
|
|
55
55
|
throw new Error(`Missing move: ${moveName}`)
|
|
@@ -75,48 +75,63 @@ export async function renderMove(moveName, moves, reader, rom, options = {}) {
|
|
|
75
75
|
|
|
76
76
|
const png = new PNG({ width, height });
|
|
77
77
|
png.data = image;
|
|
78
|
-
const pngBuffer = PNG.sync.write(png, {
|
|
78
|
+
const pngBuffer = PNG.sync.write(png, {
|
|
79
79
|
filterType: pngFilterType,
|
|
80
|
-
deflateLevel: pngCompressionLevel,
|
|
80
|
+
deflateLevel: pngCompressionLevel,
|
|
81
81
|
});
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (
|
|
88
|
-
const png = new PNG({ width, height });
|
|
89
|
-
png.data = image;
|
|
90
|
-
const pngBuffer = PNG.sync.write(png, {
|
|
91
|
-
filterType: pngFilterType,
|
|
92
|
-
deflateLevel: pngCompressionLevel,
|
|
93
|
-
});
|
|
83
|
+
const rootDir = (sortUnused && move?.unused === true) ? `${outputDir}/unused` : `${outputDir}` // yes this could become null if outputDir is null (who would have thought :p) but since nothing uses this unless outputDir is true then it isn't really an issue... although it is a bit messy...
|
|
84
|
+
const dir = `${rootDir}/${moveName}`;
|
|
85
|
+
|
|
86
|
+
if (renderMasterImage) {
|
|
87
|
+
if (outputDir) {
|
|
94
88
|
await fs.promises.writeFile(`${dir}/master.png`, pngBuffer);
|
|
95
89
|
fullFileCount += 1;
|
|
96
|
-
if (returnFileBuffer) {
|
|
97
|
-
results.push(pngBuffer);
|
|
98
|
-
}
|
|
99
90
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
deflateLevel: pngCompressionLevel,
|
|
91
|
+
if (returnFileBuffer) {
|
|
92
|
+
results.push({
|
|
93
|
+
name: `${moveName}-full-sprite`,
|
|
94
|
+
category: "move",
|
|
95
|
+
asset: "sprite",
|
|
96
|
+
path: `out/moves/${(sortUnused && move?.unused) === true? `unused/${moveName}` : `${moveName}`}/master`,
|
|
97
|
+
buffer: pngBuffer,
|
|
98
|
+
meta: { },
|
|
109
99
|
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
110
102
|
|
|
103
|
+
if (outputDir) { // I will update this later but in theory it should also work... eventually though it will need a split inside to handle full image generation :p
|
|
104
|
+
await fs.promises.mkdir(dir, { recursive: true });
|
|
105
|
+
}
|
|
106
|
+
for (let i = 0; i < move.frames.length; i++) {
|
|
107
|
+
const frame = move.frames[i];
|
|
108
|
+
const frameImageData = extractFrameFromImage(image, width, frame);
|
|
109
|
+
|
|
110
|
+
const png = new PNG({ width: frame.width, height: frame.height });
|
|
111
|
+
png.data = frameImageData;
|
|
112
|
+
const pngBuffer = PNG.sync.write(png, {
|
|
113
|
+
filterType: pngFilterType,
|
|
114
|
+
deflateLevel: pngCompressionLevel,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
if (outputDir) {
|
|
111
118
|
const fileName = `${dir}/frame-${i}.png`;
|
|
112
119
|
await fs.promises.writeFile(fileName, pngBuffer);
|
|
113
120
|
fullFileCount += 1;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
121
|
+
}
|
|
122
|
+
if (returnFileBuffer) {
|
|
123
|
+
results.push({
|
|
124
|
+
name: `${moveName}-frame${i}`,
|
|
125
|
+
category: "move",
|
|
126
|
+
asset: "frame",
|
|
127
|
+
path: `out/moves/${(sortUnused && move?.unused) === true ? `unused/${moveName}` : `${moveName}`}/frame-${i}`,
|
|
128
|
+
buffer: pngBuffer,
|
|
129
|
+
meta: { },
|
|
130
|
+
});
|
|
117
131
|
}
|
|
118
132
|
}
|
|
119
133
|
|
|
134
|
+
|
|
120
135
|
return {
|
|
121
136
|
...(returnFileBuffer && { results }),
|
|
122
137
|
fullFileCount,
|
|
@@ -27,7 +27,8 @@ export async function renderTrainerBackPic(trainerName, trainers, reader, rom, o
|
|
|
27
27
|
throw new TypeError("renderTrainerBackPic(..., rom) requires a ROM Buffer/Uint8Array");
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
let
|
|
30
|
+
let fullFileCount = 0;
|
|
31
|
+
const results = returnFileBuffer? [] : null;
|
|
31
32
|
const trainer = trainers[trainerName];
|
|
32
33
|
if (!trainer) {
|
|
33
34
|
throw new Error(`Missing trainer entry for ${trainerName}`);
|
|
@@ -98,7 +99,7 @@ export async function renderTrainerBackPic(trainerName, trainers, reader, rom, o
|
|
|
98
99
|
await fs.promises.writeFile(`${dir}/trainer_back_frame_2.png`, buffer2);
|
|
99
100
|
await fs.promises.writeFile(`${dir}/trainer_back_frame_3.png`, buffer3);
|
|
100
101
|
await fs.promises.writeFile(`${dir}/trainer_back_frame_4.png`, buffer4);
|
|
101
|
-
|
|
102
|
+
fullFileCount += 4;
|
|
102
103
|
if (frame5) {
|
|
103
104
|
const pngFrame5 = new PNG({ width, height: frameHeight });
|
|
104
105
|
pngFrame5.data = frame5;
|
|
@@ -106,17 +107,58 @@ export async function renderTrainerBackPic(trainerName, trainers, reader, rom, o
|
|
|
106
107
|
filterType: pngFilterType,
|
|
107
108
|
deflateLevel: pngCompressionLevel,
|
|
108
109
|
});
|
|
109
|
-
fs.
|
|
110
|
-
|
|
110
|
+
await fs.promises.writeFile(`${dir}/trainer_back_frame_5.png`, buffer5);
|
|
111
|
+
fullFileCount += 1;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (returnFileBuffer) {
|
|
116
|
+
results.push({
|
|
117
|
+
name: `${trainerName}-back-frame1`,
|
|
118
|
+
category: "trainer",
|
|
119
|
+
asset: "frame",
|
|
120
|
+
path: `out/trainers/${trainerName}/back_frame_1`,
|
|
121
|
+
buffer: buffer1,
|
|
122
|
+
meta: {},
|
|
123
|
+
});
|
|
124
|
+
results.push({
|
|
125
|
+
name: `${trainerName}-back-frame2`,
|
|
126
|
+
category: "trainer",
|
|
127
|
+
asset: "frame",
|
|
128
|
+
path: `out/trainers/${trainerName}/back_frame_2`,
|
|
129
|
+
buffer: buffer2,
|
|
130
|
+
meta: {},
|
|
131
|
+
});
|
|
132
|
+
results.push({
|
|
133
|
+
name: `${trainerName}-back-frame3`,
|
|
134
|
+
category: "trainer",
|
|
135
|
+
asset: "frame",
|
|
136
|
+
path: `out/trainers/${trainerName}/back_frame_3`,
|
|
137
|
+
buffer: buffer3,
|
|
138
|
+
meta: {},
|
|
139
|
+
});
|
|
140
|
+
results.push({
|
|
141
|
+
name: `${trainerName}-back-frame4`,
|
|
142
|
+
category: "trainer",
|
|
143
|
+
asset: "frame",
|
|
144
|
+
path: `out/trainers/${trainerName}/back_frame_4`,
|
|
145
|
+
buffer: buffer4,
|
|
146
|
+
meta: {},
|
|
147
|
+
});
|
|
148
|
+
if (frame5) {
|
|
149
|
+
results.push({
|
|
150
|
+
name: `${trainerName}-back-frame5`,
|
|
151
|
+
category: "trainer",
|
|
152
|
+
asset: "frame",
|
|
153
|
+
path: `out/trainers/${trainerName}/back_frame_5`,
|
|
154
|
+
buffer: buffer5,
|
|
155
|
+
meta: {},
|
|
156
|
+
});
|
|
111
157
|
}
|
|
112
158
|
}
|
|
113
159
|
|
|
114
160
|
return {
|
|
115
|
-
...(returnFileBuffer && {
|
|
116
|
-
|
|
117
|
-
...(returnFileBuffer && { frame3: buffer3 }),
|
|
118
|
-
...(returnFileBuffer && { frame4: buffer4 }),
|
|
119
|
-
...(returnFileBuffer && { frame5: buffer5 }),
|
|
120
|
-
fileCount,
|
|
161
|
+
...(returnFileBuffer && { results }),
|
|
162
|
+
fullFileCount,
|
|
121
163
|
};
|
|
122
164
|
}
|
|
@@ -45,15 +45,9 @@ export async function renderTrainer(trainerName, trainers, backTrainers, reader,
|
|
|
45
45
|
returnFileBuffer,
|
|
46
46
|
outputDir,
|
|
47
47
|
});
|
|
48
|
-
fullFileCount += trainerBackPicData.
|
|
48
|
+
fullFileCount += trainerBackPicData.fullFileCount;
|
|
49
49
|
if (returnFileBuffer) {
|
|
50
|
-
results.push(trainerBackPicData.
|
|
51
|
-
results.push(trainerBackPicData.frame2);
|
|
52
|
-
results.push(trainerBackPicData.frame3);
|
|
53
|
-
results.push(trainerBackPicData.frame4);
|
|
54
|
-
if (trainerBackPicData?.frame5) {
|
|
55
|
-
results.push(trainerBackPicData.frame5);
|
|
56
|
-
}
|
|
50
|
+
results.push(...trainerBackPicData.results);
|
|
57
51
|
}
|
|
58
52
|
}
|
|
59
53
|
else if (backTrainerName === false && trainerName === "PAINTER") {
|
|
@@ -63,12 +57,9 @@ export async function renderTrainer(trainerName, trainers, backTrainers, reader,
|
|
|
63
57
|
returnFileBuffer,
|
|
64
58
|
outputDir,
|
|
65
59
|
});
|
|
66
|
-
fullFileCount += trainerBackPicData.
|
|
60
|
+
fullFileCount += trainerBackPicData.fullFileCount;
|
|
67
61
|
if (returnFileBuffer) {
|
|
68
|
-
results.push(trainerBackPicData.
|
|
69
|
-
results.push(trainerBackPicData.frame2);
|
|
70
|
-
results.push(trainerBackPicData.frame3);
|
|
71
|
-
results.push(trainerBackPicData.frame4);
|
|
62
|
+
results.push(...trainerBackPicData.results);
|
|
72
63
|
}
|
|
73
64
|
const trainerBackPicData2 = await renderTrainerBackPic("POKEDUDE", backTrainers, reader, rom, {
|
|
74
65
|
pngFilterType,
|
|
@@ -76,12 +67,9 @@ export async function renderTrainer(trainerName, trainers, backTrainers, reader,
|
|
|
76
67
|
returnFileBuffer,
|
|
77
68
|
outputDir,
|
|
78
69
|
});
|
|
79
|
-
fullFileCount += trainerBackPicData2.
|
|
70
|
+
fullFileCount += trainerBackPicData2.fullFileCount;
|
|
80
71
|
if (returnFileBuffer) {
|
|
81
|
-
results.push(
|
|
82
|
-
results.push(trainerBackPicData.frame2);
|
|
83
|
-
results.push(trainerBackPicData.frame3);
|
|
84
|
-
results.push(trainerBackPicData.frame4);
|
|
72
|
+
results.push(...trainerBackPicData2.results);
|
|
85
73
|
}
|
|
86
74
|
}
|
|
87
75
|
}
|
|
@@ -112,7 +100,16 @@ export async function renderTrainer(trainerName, trainers, backTrainers, reader,
|
|
|
112
100
|
});
|
|
113
101
|
|
|
114
102
|
if (returnFileBuffer) {
|
|
115
|
-
results.push(
|
|
103
|
+
results.push({
|
|
104
|
+
name: `${trainerName}-front-sprite`,
|
|
105
|
+
category: "trainer",
|
|
106
|
+
asset: "sprite",
|
|
107
|
+
path: `out/trainers/${trainerName}/front`,
|
|
108
|
+
buffer: pngBuffer,
|
|
109
|
+
meta: {
|
|
110
|
+
side: "front" // now some of you may be wondering why I added this... well it's because eventually I want you to be able to make a master image of the back sprites so we need to be able to differentiate the buffers here... anyway that was a long explanation :p
|
|
111
|
+
},
|
|
112
|
+
});
|
|
116
113
|
}
|
|
117
114
|
|
|
118
115
|
if (outputDir) {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Copyright (c) 2026 chickenPoo
|
|
2
|
+
// Licensed under the MIT License. See LICENSE file in project root.
|
|
3
|
+
|
|
4
|
+
export function isValidFilterType(filterTypeValue) {
|
|
5
|
+
if (Number.isInteger(filterTypeValue)) {
|
|
6
|
+
return filterTypeValue >= -1 && filterTypeValue <= 4;
|
|
7
|
+
}
|
|
8
|
+
if (Array.isArray(filterTypeValue)) {
|
|
9
|
+
return filterTypeValue.length > 0 && filterTypeValue.every(entry =>
|
|
10
|
+
Number.isInteger(entry) && entry >= 0 && entry <= 4
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { mapLimit } from "./map-limit.js";
|
|
2
|
+
|
|
3
|
+
export async function runWithConcurrency(items, concurrency, callback) { // this seems simple... but I have no idea if this will do what I am thinking it will do... (don't be a dum dum like me who decided to code kids!)
|
|
4
|
+
if (concurrency <= 1) {
|
|
5
|
+
for (const item of items) {
|
|
6
|
+
await callback(item);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
await mapLimit(items, concurrency, callback);
|
|
12
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Copyright (c) 2026 chickenPoo
|
|
2
|
+
// Licensed under the MIT License. See LICENSE file in project root.
|
|
3
|
+
|
|
4
|
+
import { isValidFilterType } from "./is-valid-filter-type.js";
|
|
5
|
+
|
|
6
|
+
export function validateRenderOptions(functionName, options = {}) {
|
|
7
|
+
const {
|
|
8
|
+
concurrency = null,
|
|
9
|
+
filterType = null,
|
|
10
|
+
compressionLevel = null,
|
|
11
|
+
} = options;
|
|
12
|
+
|
|
13
|
+
if (concurrency) {
|
|
14
|
+
if (!Number.isInteger(concurrency) || concurrency < 1) {
|
|
15
|
+
throw new TypeError(`${functionName}(rom, options = { concurrency, ... }) requires concurrency to be a integer greater than 0 (recieved ${concurrency})`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (filterType) {
|
|
19
|
+
if (!isValidFilterType(filterType)) {
|
|
20
|
+
throw new TypeError(`${functionName}renderAllMons(rom, options = { pngFilterType, ... }) requires pngFilterType to be a integer between -1 and 4 (received ${pngFilterType})`)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
if (compressionLevel) {
|
|
24
|
+
if (!Number.isInteger(compressionLevel) || compressionLevel < 0 || compressionLevel > 9) {
|
|
25
|
+
throw new TypeError(`${functionName}renderAllMons(rom, options = { pngCompressionLevel, ... }) requires pngCompressionLevel to be a integer between 0 and 9 (received ${pngCompressionLevel})`)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|