@nodecraft/rendermite 1.1.4 → 1.2.1
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 +27 -2
- package/assets/builtin/renderchest/models/block/banner_standing.json +225 -0
- package/assets/builtin/renderchest/models/block/banner_wall.json +155 -0
- package/assets/builtin/renderchest/models/block/bed_foot.json +44 -0
- package/assets/builtin/renderchest/models/block/bed_head.json +44 -0
- package/assets/builtin/renderchest/models/block/chest_left.json +182 -0
- package/assets/builtin/renderchest/models/block/chest_right.json +182 -0
- package/assets/builtin/renderchest/models/block/copper_golem_statue.json +709 -0
- package/assets/builtin/renderchest/models/block/fluid.json +83 -0
- package/assets/builtin/renderchest/models/block/fluid_tinted.json +89 -0
- package/assets/builtin/renderchest/models/block/hanging_sign.json +54 -0
- package/assets/builtin/renderchest/models/block/standing_sign.json +32 -0
- package/assets/builtin/renderchest/models/block/wall_sign.json +19 -0
- package/dist/atlas/registry.js +1 -1
- package/dist/atlas/sources.js +1 -1
- package/dist/atlas-pack-CoOih7By.js +130 -0
- package/dist/atlas-pack-DRTrWcSU.d.ts +41 -0
- package/dist/block-entities-xv3bQpEX.js +490 -0
- package/dist/blocks/atlas-pack.d.ts +2 -0
- package/dist/blocks/atlas-pack.js +2 -0
- package/dist/blocks/block-entities.d.ts +93 -0
- package/dist/blocks/block-entities.js +2 -0
- package/dist/blocks/blockstate-eval.d.ts +2 -0
- package/dist/blocks/blockstate-eval.js +2 -0
- package/dist/blocks/cli.d.ts +9 -0
- package/dist/blocks/cli.js +2 -0
- package/dist/blocks/export.d.ts +2 -0
- package/dist/blocks/export.js +2 -0
- package/dist/blocks/obj-mesh.d.ts +2 -0
- package/dist/blocks/obj-mesh.js +2 -0
- package/dist/blocks/tints.d.ts +2 -0
- package/dist/blocks/tints.js +2 -0
- package/dist/{blockstate-CdstnW2-.js → blockstate-DVb8Mhxc.js} +1 -1
- package/dist/blockstate-eval-BRQ3NWY2.js +264 -0
- package/dist/blockstate-eval-CObtPWzJ.d.ts +122 -0
- package/dist/cli-CWUKXFbx.js +161 -0
- package/dist/cli.js +9 -7
- package/dist/{descriptor-DWebtkI1.js → descriptor-BVIoEpPO.js} +3 -3
- package/dist/{discovery-B9aqV_Rz.js → discovery-CsZu08re.js} +1 -1
- package/dist/dynamic/generators.js +1 -1
- package/dist/{entity-fallback-Cu22uEIK.js → entity-fallback-BTkM8j0V.js} +3 -155
- package/dist/entity-textures-DFRIVdF1.js +209 -0
- package/dist/export-BIpqNU3H.d.ts +44 -0
- package/dist/export-DY5sGwt0.js +790 -0
- package/dist/{face-cQ-pVHoe.d.ts → face-BKwkeTps.d.ts} +1 -1
- package/dist/{fluid-bucket-BzoLofB_.js → fluid-bucket-B-I4ZYHl.js} +1 -1
- package/dist/{generators-BlG4KmtP.js → generators-BCZVlWig.js} +1 -1
- package/dist/{geo-w9HHIrb5.js → geo-BguldlU-.js} +5 -5
- package/dist/{geometry-w0yQbzDZ.d.ts → geometry-Bkur58Vi.d.ts} +1 -1
- package/dist/index.d.ts +10 -5
- package/dist/index.js +21 -16
- package/dist/{item-styles-upgEO1yP.js → item-styles-DjYRdyHi.js} +1 -1
- package/dist/items/blockstate.js +1 -1
- package/dist/items/discovery.js +1 -1
- package/dist/{jar-D9z-X7tA.js → jar-5aLDI0nT.js} +2 -2
- package/dist/{legacy-reshape-C05SfAPR.js → legacy-reshape-CcdstKyI.js} +2 -2
- package/dist/{legacy-tints-B09J0Rxh.js → legacy-tints-l-541wgI.js} +1 -1
- package/dist/{loader-CWsTZgrk.js → loader-C-NuJaEi.js} +119 -9
- package/dist/{loader-Lb0ZeILz.js → loader-C18GpurU.js} +1 -1
- package/dist/{loader-DD83d80u.d.ts → loader-C_1_amsq.d.ts} +18 -1
- package/dist/{locator-ycZzh_6z.js → locator-COMEtEn9.js} +23 -1
- package/dist/{manager-CkletR5K.js → manager-CYbAkWuT.js} +2 -2
- package/dist/models/loader.d.ts +1 -1
- package/dist/models/loader.js +1 -1
- package/dist/models/types.d.ts +1 -1
- package/dist/{models-BKxGkPS7.js → models-CQwcTZcg.js} +1 -1
- package/dist/mods/models.js +1 -1
- package/dist/mods/prepare.js +1 -1
- package/dist/mods/synthesize.js +1 -1
- package/dist/mods/tints.js +1 -1
- package/dist/{renderer-DB1Oe7xs.js → obj-bvs3gwex.js} +234 -234
- package/dist/obj-mesh-C6Sp4Ysc.d.ts +41 -0
- package/dist/obj-mesh-rnYbSbpv.js +97 -0
- package/dist/output/item-styles.js +1 -1
- package/dist/{pipeline-Bp6amYz_.d.ts → pipeline-BDvXcw20.d.ts} +1 -1
- package/dist/{pipeline-CgJaVpoL.js → pipeline-DeGRR0CP.js} +14 -14
- package/dist/{prepare-D56Z-9cg.js → prepare-B8oPdGYz.js} +5 -5
- package/dist/{prepare-Cc8QZ3j0.js → prepare-C95vjt_o.js} +5 -5
- package/dist/{registry-DS5PZ57l.js → registry-DlduyjtM.js} +2 -2
- package/dist/render/entity-fallback.d.ts +3 -3
- package/dist/render/entity-fallback.js +1 -1
- package/dist/render/face.d.ts +1 -1
- package/dist/render/face.js +1 -1
- package/dist/render/fluid-bucket.js +1 -1
- package/dist/render/geo.d.ts +2 -2
- package/dist/render/geo.js +1 -1
- package/dist/render/geometry.d.ts +1 -1
- package/dist/render/legacy-tints.d.ts +1 -1
- package/dist/render/legacy-tints.js +1 -1
- package/dist/render/obj.d.ts +10 -3
- package/dist/render/obj.js +2 -2
- package/dist/render/pipeline.d.ts +1 -1
- package/dist/render/pipeline.js +1 -1
- package/dist/render/renderer.d.ts +1 -1
- package/dist/render/renderer.js +1 -1
- package/dist/render/tints.d.ts +1 -1
- package/dist/render/tints.js +1 -1
- package/dist/{renderer-C79EQDSn.d.ts → renderer-U20hnhbi.d.ts} +2 -2
- package/dist/resources/descriptor.js +1 -1
- package/dist/resources/entity-textures.d.ts +51 -0
- package/dist/resources/entity-textures.js +2 -0
- package/dist/resources/jar.js +1 -1
- package/dist/resources/locator.js +1 -1
- package/dist/resources/manager.js +1 -1
- package/dist/resources/synthetic.js +1 -1
- package/dist/{run-uq8_hUx9.d.ts → run-CvIqiZ6T.d.ts} +1 -1
- package/dist/{run-U9pM9wKU.js → run-Diql5y56.js} +7 -7
- package/dist/run.d.ts +1 -1
- package/dist/run.js +1 -1
- package/dist/{sources-Bxk-WOzH.js → sources-BlUriDCs.js} +1 -1
- package/dist/{synthesize-3Q0aaOnK.js → synthesize-jTjVjPAt.js} +2 -2
- package/dist/{synthetic-ZAUjXT7E.js → synthetic-rc9BPT4P.js} +0 -0
- package/dist/textures/loader.js +1 -1
- package/dist/tints-2k3ZhkY3.d.ts +41 -0
- package/dist/{tints-3VI_2SNy.js → tints-B3WQcPtx.js} +1 -1
- package/dist/{tints-Do9Sh3dg.d.ts → tints-DqhgFi8a.d.ts} +1 -1
- package/dist/tints-a9FWZLma.js +176 -0
- package/dist/{tints-CebmvjmP.js → tints-nxtGrqv5.js} +3 -3
- package/dist/{types-DpRSF9Vj.d.ts → types-qlp3I8_-.d.ts} +11 -0
- package/dist/versions/download.js +1 -1
- package/dist/versions/legacy-reshape.js +1 -1
- package/dist/versions/prepare.js +1 -1
- package/dist/workers/render-worker.d.ts +1 -1
- package/dist/workers/render-worker.js +2 -2
- package/package.json +1 -1
- package/dist/{download-D9aARBFp.js → download-1wAfrGKx.js} +1 -1
- package/dist/{face-CMJ3b3Ae.js → face-CKHboPfh.js} +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { n as ResourceLocator } from "./locator-
|
|
2
|
-
import { i as
|
|
1
|
+
import { n as ResourceLocator } from "./locator-COMEtEn9.js";
|
|
2
|
+
import { a as findChestTexture, i as findBedTexture, l as findMobTexture, n as HEAD_LAYOUTS, o as findEntityTexture, r as coversSheet, t as DYE_COLORS, u as tokens } from "./entity-textures-DFRIVdF1.js";
|
|
3
|
+
import { c as renderModel } from "./obj-bvs3gwex.js";
|
|
3
4
|
//#region src/render/entity-fallback.ts
|
|
4
5
|
/**
|
|
5
6
|
* Heuristic rendering for mod items that parent `builtin/entity` — chests,
|
|
@@ -10,60 +11,6 @@ import { i as renderModel } from "./renderer-DB1Oe7xs.js";
|
|
|
10
11
|
* chest icon for a chest item beats no icon, but nothing here ever guesses
|
|
11
12
|
* across item kinds.
|
|
12
13
|
*/
|
|
13
|
-
/** Tokens too generic to identify a texture on their own. */
|
|
14
|
-
const FILLER_TOKENS = /* @__PURE__ */ new Set([
|
|
15
|
-
"chest",
|
|
16
|
-
"chests",
|
|
17
|
-
"crate",
|
|
18
|
-
"crates",
|
|
19
|
-
"trapped",
|
|
20
|
-
"big",
|
|
21
|
-
"small",
|
|
22
|
-
"the",
|
|
23
|
-
"of"
|
|
24
|
-
]);
|
|
25
|
-
/** Double-chest halves, lid states, boat sheets, seasonal skins — never icon textures. */
|
|
26
|
-
const SKIP_TOKENS = /* @__PURE__ */ new Set([
|
|
27
|
-
"left",
|
|
28
|
-
"right",
|
|
29
|
-
"open",
|
|
30
|
-
"opened",
|
|
31
|
-
"christmas",
|
|
32
|
-
"boat",
|
|
33
|
-
"raft",
|
|
34
|
-
"minecart"
|
|
35
|
-
]);
|
|
36
|
-
function tokens(name) {
|
|
37
|
-
return name.split(/[./_]/).filter((token) => token.length > 1);
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Find the mod's own chest sheet for a chest-like item by token overlap over
|
|
41
|
-
* the FULL texture path — mods keep variant sheets in per-wood directories
|
|
42
|
-
* (quark's `quark_variant_chests/acacia/normal.png`), not just under entity/.
|
|
43
|
-
*/
|
|
44
|
-
async function findChestTexture(resources, namespace, itemName) {
|
|
45
|
-
const wanted = tokens(itemName).filter((token) => !FILLER_TOKENS.has(token));
|
|
46
|
-
const wantTrapped = itemName.includes("trapped");
|
|
47
|
-
let best = null;
|
|
48
|
-
let bestScore = 0;
|
|
49
|
-
let bestNoise = Infinity;
|
|
50
|
-
for (const locator of await resources.listResources("texture", namespace)) {
|
|
51
|
-
if (!locator.path.includes("chest") && !locator.path.includes("crate")) continue;
|
|
52
|
-
const candidate = tokens(locator.path);
|
|
53
|
-
if (candidate.some((token) => SKIP_TOKENS.has(token))) continue;
|
|
54
|
-
if (wantTrapped !== (candidate.includes("trapped") || candidate.includes("trap"))) continue;
|
|
55
|
-
let score = 0;
|
|
56
|
-
for (const token of wanted) if (candidate.includes(token)) score++;
|
|
57
|
-
if (locator.path.endsWith(`/${itemName}`) || locator.path.endsWith(`/${itemName}_0`)) score++;
|
|
58
|
-
if (wanted.length > 0 && score === 0) continue;
|
|
59
|
-
if (best === null || score > bestScore || score === bestScore && candidate.length < bestNoise) {
|
|
60
|
-
best = locator;
|
|
61
|
-
bestScore = score;
|
|
62
|
-
bestNoise = candidate.length;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return best;
|
|
66
|
-
}
|
|
67
14
|
/**
|
|
68
15
|
* An item's English display name, slugified, or null. Mods do not always put the
|
|
69
16
|
* kind in the id — lootr's Loot Chest is `lootr_inventory` — and the lang file is
|
|
@@ -95,74 +42,7 @@ const WHITE_BANNER = {
|
|
|
95
42
|
g: 252 / 255,
|
|
96
43
|
b: 251 / 255
|
|
97
44
|
};
|
|
98
|
-
/** Vanilla DyeColor diffuse colors, keyed by the dye name mods prefix with. */
|
|
99
|
-
const DYE_COLORS = {
|
|
100
|
-
white: 16383998,
|
|
101
|
-
orange: 16351261,
|
|
102
|
-
magenta: 13061821,
|
|
103
|
-
light_blue: 3847130,
|
|
104
|
-
yellow: 16701501,
|
|
105
|
-
lime: 8439583,
|
|
106
|
-
pink: 15961002,
|
|
107
|
-
gray: 4673362,
|
|
108
|
-
light_gray: 10329495,
|
|
109
|
-
cyan: 1481884,
|
|
110
|
-
purple: 8991416,
|
|
111
|
-
blue: 3949738,
|
|
112
|
-
brown: 8606770,
|
|
113
|
-
green: 6192150,
|
|
114
|
-
red: 11546150,
|
|
115
|
-
black: 1908001
|
|
116
|
-
};
|
|
117
45
|
const DYE_PREFIX = new RegExp(`^(${Object.keys(DYE_COLORS).join("|")})_(.+)$`);
|
|
118
|
-
/** Skin sheet sizes the mobheads pack has base geometry for. */
|
|
119
|
-
const HEAD_LAYOUTS = {
|
|
120
|
-
"64x32": "head_64_32",
|
|
121
|
-
"64x64": "head_64_64",
|
|
122
|
-
"32x32": "head_32_32"
|
|
123
|
-
};
|
|
124
|
-
/**
|
|
125
|
-
* The entity sheet for a mob, preferring a file named exactly after it. Sheets
|
|
126
|
-
* live at entity/<mob>.png or entity/<mob>/<mob>.png depending on the mod, and a
|
|
127
|
-
* head only ever wants the base skin — never a blink or armour overlay.
|
|
128
|
-
*/
|
|
129
|
-
async function findMobTexture(resources, namespace, mob) {
|
|
130
|
-
for (const source of [namespace, "minecraft"]) {
|
|
131
|
-
for (const candidate of [`entity/${mob}`, `entity/${mob}/${mob}`]) {
|
|
132
|
-
const locator = new ResourceLocator(source, candidate);
|
|
133
|
-
if (await resources.hasResource("texture", locator)) return locator;
|
|
134
|
-
}
|
|
135
|
-
let best = null;
|
|
136
|
-
for (const locator of await resources.listResources("texture", source)) {
|
|
137
|
-
const leaf = locator.path.split("/").pop();
|
|
138
|
-
if (!locator.path.startsWith("entity/") || leaf !== mob) continue;
|
|
139
|
-
if (best === null || locator.path.length < best.path.length) best = locator;
|
|
140
|
-
}
|
|
141
|
-
if (best) return best;
|
|
142
|
-
}
|
|
143
|
-
return null;
|
|
144
|
-
}
|
|
145
|
-
/** Find a full modded bed entity sheet, preferring every distinguishing item-name token. */
|
|
146
|
-
async function findBedTexture(resources, namespace, itemName) {
|
|
147
|
-
const wanted = tokens(itemName).filter((token) => token !== "bed");
|
|
148
|
-
const hasDye = wanted.some((token) => Object.hasOwn(DYE_COLORS, token));
|
|
149
|
-
let best = null;
|
|
150
|
-
let bestScore = 0;
|
|
151
|
-
let bestNoise = Infinity;
|
|
152
|
-
for (const locator of await resources.listResources("texture", namespace)) {
|
|
153
|
-
if (!locator.path.startsWith("entity/") || !locator.path.includes("bed")) continue;
|
|
154
|
-
const candidate = tokens(locator.path);
|
|
155
|
-
let score = hasDye ? 0 : candidate.includes("undyed") ? 1 : 0;
|
|
156
|
-
for (const token of wanted) if (candidate.includes(token)) score++;
|
|
157
|
-
if (score === 0) continue;
|
|
158
|
-
if (best === null || score > bestScore || score === bestScore && candidate.length < bestNoise) {
|
|
159
|
-
best = locator;
|
|
160
|
-
bestScore = score;
|
|
161
|
-
bestNoise = candidate.length;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
return best;
|
|
165
|
-
}
|
|
166
46
|
/** The dye name a `<dye>_<thing>` mod item is prefixed with, or null. */
|
|
167
47
|
function dyeVariantName(id) {
|
|
168
48
|
const locator = ResourceLocator.tryParse(id);
|
|
@@ -394,38 +274,6 @@ async function renderEntityFallback(id, models, textures, resources, options) {
|
|
|
394
274
|
return null;
|
|
395
275
|
}
|
|
396
276
|
/**
|
|
397
|
-
* Whether a sheet is a whole skin rather than an overlay. Vanilla's shulker
|
|
398
|
-
* covers 44% of its sheet; a tier trim covers under 5%.
|
|
399
|
-
*/
|
|
400
|
-
async function coversSheet(textures, locator) {
|
|
401
|
-
const texture = await textures.load(locator).catch(() => null);
|
|
402
|
-
if (!texture) return false;
|
|
403
|
-
let opaque = 0;
|
|
404
|
-
for (let offset = 3; offset < texture.data.length; offset += 4) if (texture.data[offset] > 16) opaque++;
|
|
405
|
-
return opaque / (texture.width * texture.height) > .2;
|
|
406
|
-
}
|
|
407
|
-
/**
|
|
408
|
-
* A mod's entity sheet for a kind, preferring the one its item name points at.
|
|
409
|
-
* Not restricted to `entity/`: lootr files its whole-skin sheets straight under
|
|
410
|
-
* `textures/` (`lootr:shulker`), and the caller gates on coverage anyway.
|
|
411
|
-
*/
|
|
412
|
-
async function findEntityTexture(resources, namespace, itemName, kind) {
|
|
413
|
-
const wanted = tokens(itemName).filter((token) => token !== kind && token !== "box");
|
|
414
|
-
let best = null;
|
|
415
|
-
let bestScore = -1;
|
|
416
|
-
for (const locator of await resources.listResources("texture", namespace)) {
|
|
417
|
-
if (!locator.path.includes(kind)) continue;
|
|
418
|
-
const candidate = tokens(locator.path);
|
|
419
|
-
let score = locator.path.startsWith("entity/") ? 1 : 0;
|
|
420
|
-
for (const token of wanted) if (candidate.includes(token)) score += 2;
|
|
421
|
-
if (score > bestScore) {
|
|
422
|
-
best = locator;
|
|
423
|
-
bestScore = score;
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
return best;
|
|
427
|
-
}
|
|
428
|
-
/**
|
|
429
277
|
* A trident's entity sheet. Vanilla's is `entity/trident` (32×32) and mods
|
|
430
278
|
* that reuse the vanilla model ship the same path under their own namespace;
|
|
431
279
|
* a mod that names its sheet after the item instead (an `iron_trident` with
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { n as ResourceLocator } from "./locator-COMEtEn9.js";
|
|
2
|
+
//#region src/resources/entity-textures.ts
|
|
3
|
+
/**
|
|
4
|
+
* Finding a mod's own entity sheet for a thing the game draws in Java.
|
|
5
|
+
*
|
|
6
|
+
* A chest, a mob head, a bed or a shulker box carries no texture in its model,
|
|
7
|
+
* and mods put theirs wherever they like: quark nests variant chests under
|
|
8
|
+
* `quark_variant_chests/acacia/normal`, lootr files whole skins straight under
|
|
9
|
+
* `textures/`. So the sheet is searched for by token overlap with the block or
|
|
10
|
+
* item name rather than assumed from a path.
|
|
11
|
+
*
|
|
12
|
+
* Shared by the icon renderer's `builtin/entity` fallback and the block pack's
|
|
13
|
+
* substitution table, which are the same problem seen from two directions.
|
|
14
|
+
*/
|
|
15
|
+
/** Tokens too generic to identify a texture on their own. */
|
|
16
|
+
const FILLER_TOKENS = /* @__PURE__ */ new Set([
|
|
17
|
+
"chest",
|
|
18
|
+
"chests",
|
|
19
|
+
"crate",
|
|
20
|
+
"crates",
|
|
21
|
+
"trapped",
|
|
22
|
+
"big",
|
|
23
|
+
"small",
|
|
24
|
+
"the",
|
|
25
|
+
"of"
|
|
26
|
+
]);
|
|
27
|
+
/** Double-chest halves, lid states, boat sheets, seasonal skins — never icon textures. */
|
|
28
|
+
const SKIP_TOKENS = /* @__PURE__ */ new Set([
|
|
29
|
+
"left",
|
|
30
|
+
"right",
|
|
31
|
+
"open",
|
|
32
|
+
"opened",
|
|
33
|
+
"christmas",
|
|
34
|
+
"boat",
|
|
35
|
+
"raft",
|
|
36
|
+
"minecart"
|
|
37
|
+
]);
|
|
38
|
+
function tokens(name) {
|
|
39
|
+
return name.split(/[./_]/).filter((token) => token.length > 1);
|
|
40
|
+
}
|
|
41
|
+
/** Vanilla DyeColor diffuse colors, keyed by the dye name mods prefix with. */
|
|
42
|
+
const DYE_COLORS = {
|
|
43
|
+
white: 16383998,
|
|
44
|
+
orange: 16351261,
|
|
45
|
+
magenta: 13061821,
|
|
46
|
+
light_blue: 3847130,
|
|
47
|
+
yellow: 16701501,
|
|
48
|
+
lime: 8439583,
|
|
49
|
+
pink: 15961002,
|
|
50
|
+
gray: 4673362,
|
|
51
|
+
light_gray: 10329495,
|
|
52
|
+
cyan: 1481884,
|
|
53
|
+
purple: 8991416,
|
|
54
|
+
blue: 3949738,
|
|
55
|
+
brown: 8606770,
|
|
56
|
+
green: 6192150,
|
|
57
|
+
red: 11546150,
|
|
58
|
+
black: 1908001
|
|
59
|
+
};
|
|
60
|
+
/** Skin sheet sizes the mobheads pack has base geometry for. */
|
|
61
|
+
const HEAD_LAYOUTS = {
|
|
62
|
+
"64x32": "head_64_32",
|
|
63
|
+
"64x64": "head_64_64",
|
|
64
|
+
"32x32": "head_32_32"
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* The entity sheet for a mob, preferring a file named exactly after it. Sheets
|
|
68
|
+
* live at entity/<mob>.png or entity/<mob>/<mob>.png depending on the mod, and a
|
|
69
|
+
* head only ever wants the base skin — never a blink or armour overlay.
|
|
70
|
+
*/
|
|
71
|
+
async function findMobTexture(resources, namespace, mob) {
|
|
72
|
+
for (const source of [namespace, "minecraft"]) {
|
|
73
|
+
for (const candidate of [`entity/${mob}`, `entity/${mob}/${mob}`]) {
|
|
74
|
+
const locator = new ResourceLocator(source, candidate);
|
|
75
|
+
if (await resources.hasResource("texture", locator)) return locator;
|
|
76
|
+
}
|
|
77
|
+
let best = null;
|
|
78
|
+
for (const locator of await resources.listResources("texture", source)) {
|
|
79
|
+
const leaf = locator.path.split("/").pop();
|
|
80
|
+
if (!locator.path.startsWith("entity/") || leaf !== mob) continue;
|
|
81
|
+
if (best === null || locator.path.length < best.path.length) best = locator;
|
|
82
|
+
}
|
|
83
|
+
if (best) return best;
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Find the mod's own chest sheet for a chest-like item by token overlap over
|
|
89
|
+
* the FULL texture path — mods keep variant sheets in per-wood directories
|
|
90
|
+
* (quark's `quark_variant_chests/acacia/normal.png`), not just under entity/.
|
|
91
|
+
*/
|
|
92
|
+
async function findChestTexture(resources, namespace, itemName) {
|
|
93
|
+
const wanted = tokens(itemName).filter((token) => !FILLER_TOKENS.has(token));
|
|
94
|
+
const wantTrapped = itemName.includes("trapped");
|
|
95
|
+
let best = null;
|
|
96
|
+
let bestScore = 0;
|
|
97
|
+
let bestNoise = Infinity;
|
|
98
|
+
for (const locator of await resources.listResources("texture", namespace)) {
|
|
99
|
+
if (!locator.path.includes("chest") && !locator.path.includes("crate")) continue;
|
|
100
|
+
const candidate = tokens(locator.path);
|
|
101
|
+
if (candidate.some((token) => SKIP_TOKENS.has(token))) continue;
|
|
102
|
+
if (wantTrapped !== (candidate.includes("trapped") || candidate.includes("trap"))) continue;
|
|
103
|
+
let score = 0;
|
|
104
|
+
for (const token of wanted) if (candidate.includes(token)) score++;
|
|
105
|
+
if (locator.path.endsWith(`/${itemName}`) || locator.path.endsWith(`/${itemName}_0`)) score++;
|
|
106
|
+
if (wanted.length > 0 && score === 0) continue;
|
|
107
|
+
if (best === null || score > bestScore || score === bestScore && candidate.length < bestNoise) {
|
|
108
|
+
best = locator;
|
|
109
|
+
bestScore = score;
|
|
110
|
+
bestNoise = candidate.length;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return best;
|
|
114
|
+
}
|
|
115
|
+
/** Find a full modded bed entity sheet, preferring every distinguishing item-name token. */
|
|
116
|
+
async function findBedTexture(resources, namespace, itemName) {
|
|
117
|
+
const wanted = tokens(itemName).filter((token) => token !== "bed");
|
|
118
|
+
const hasDye = wanted.some((token) => Object.hasOwn(DYE_COLORS, token));
|
|
119
|
+
let best = null;
|
|
120
|
+
let bestScore = 0;
|
|
121
|
+
let bestNoise = Infinity;
|
|
122
|
+
for (const locator of await resources.listResources("texture", namespace)) {
|
|
123
|
+
if (!locator.path.startsWith("entity/") || !locator.path.includes("bed")) continue;
|
|
124
|
+
const candidate = tokens(locator.path);
|
|
125
|
+
let score = hasDye ? 0 : candidate.includes("undyed") ? 1 : 0;
|
|
126
|
+
for (const token of wanted) if (candidate.includes(token)) score++;
|
|
127
|
+
if (score === 0) continue;
|
|
128
|
+
if (best === null || score > bestScore || score === bestScore && candidate.length < bestNoise) {
|
|
129
|
+
best = locator;
|
|
130
|
+
bestScore = score;
|
|
131
|
+
bestNoise = candidate.length;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return best;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Whether a sheet is a whole skin rather than an overlay. Vanilla's shulker
|
|
138
|
+
* covers 44% of its sheet; a tier trim covers under 5%.
|
|
139
|
+
*/
|
|
140
|
+
async function coversSheet(textures, locator) {
|
|
141
|
+
const texture = await textures.load(locator).catch(() => null);
|
|
142
|
+
if (!texture) return false;
|
|
143
|
+
let opaque = 0;
|
|
144
|
+
for (let offset = 3; offset < texture.data.length; offset += 4) if (texture.data[offset] > 16) opaque++;
|
|
145
|
+
return opaque / (texture.width * texture.height) > .2;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* A mod's entity sheet for a kind, preferring the one its item name points at.
|
|
149
|
+
* Not restricted to `entity/`: lootr files its whole-skin sheets straight under
|
|
150
|
+
* `textures/` (`lootr:shulker`), and the caller gates on coverage anyway.
|
|
151
|
+
*/
|
|
152
|
+
async function findEntityTexture(resources, namespace, itemName, kind) {
|
|
153
|
+
const wanted = tokens(itemName).filter((token) => token !== kind && token !== "box");
|
|
154
|
+
let best = null;
|
|
155
|
+
let bestScore = -1;
|
|
156
|
+
for (const locator of await resources.listResources("texture", namespace)) {
|
|
157
|
+
if (!locator.path.includes(kind)) continue;
|
|
158
|
+
const candidate = tokens(locator.path);
|
|
159
|
+
let score = locator.path.startsWith("entity/") ? 1 : 0;
|
|
160
|
+
for (const token of wanted) if (candidate.includes(token)) score += 2;
|
|
161
|
+
if (score > bestScore) {
|
|
162
|
+
best = locator;
|
|
163
|
+
bestScore = score;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return best;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* The still sprite for a fluid block. A fluid's two frames are `<name>_still`
|
|
170
|
+
* and `<name>_flow` by a convention every loader follows, but where they live
|
|
171
|
+
* is up to the mod — Create puts them under `fluid/`, Immersive Engineering
|
|
172
|
+
* under `block/fluid/`, Mekanism under `liquid/` — and the block is named for
|
|
173
|
+
* the fluid with a suffix the mod picks.
|
|
174
|
+
*/
|
|
175
|
+
async function findFluidStill(resources, namespace, blockName) {
|
|
176
|
+
const wanted = tokens(blockName.replace(/_?(?:fluid|liquid|block|source)$/, "")).filter((token) => token !== "fluid" && token !== "liquid" && token !== "block");
|
|
177
|
+
if (wanted.length === 0) return null;
|
|
178
|
+
let best = null;
|
|
179
|
+
let bestScore = 0;
|
|
180
|
+
let bestNoise = Infinity;
|
|
181
|
+
for (const locator of await resources.listResources("texture", namespace)) {
|
|
182
|
+
if (!locator.path.endsWith("_still")) continue;
|
|
183
|
+
const candidate = tokens(locator.path.slice(0, -6));
|
|
184
|
+
const score = wanted.filter((token) => candidate.includes(token)).length;
|
|
185
|
+
if (score < wanted.length) continue;
|
|
186
|
+
if (best === null || score > bestScore || score === bestScore && candidate.length < bestNoise) {
|
|
187
|
+
best = locator;
|
|
188
|
+
bestScore = score;
|
|
189
|
+
bestNoise = candidate.length;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return best;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* A sheet filed beside a fluid's still one — its flow frames, or the overlay
|
|
196
|
+
* drawn where it meets a solid face. Vanilla and most mods spell flow `_flow`;
|
|
197
|
+
* some spell it `_flowing`. Only water has an overlay, and deriving it as a
|
|
198
|
+
* sibling is what keeps lava from borrowing water's.
|
|
199
|
+
*/
|
|
200
|
+
async function findFluidSibling(resources, still, suffixes) {
|
|
201
|
+
const base = still.path.slice(0, -6);
|
|
202
|
+
for (const suffix of suffixes) {
|
|
203
|
+
const candidate = new ResourceLocator(still.namespace, base + suffix);
|
|
204
|
+
if (await resources.hasResource("texture", candidate)) return candidate;
|
|
205
|
+
}
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
//#endregion
|
|
209
|
+
export { findChestTexture as a, findFluidStill as c, findBedTexture as i, findMobTexture as l, HEAD_LAYOUTS as n, findEntityTexture as o, coversSheet as r, findFluidSibling as s, DYE_COLORS as t, tokens as u };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { a as ResourceManager } from "./manager-Cv1NeFyF.js";
|
|
2
|
+
//#region src/blocks/export.d.ts
|
|
3
|
+
interface ExportBlockPackOptions {
|
|
4
|
+
resources: ResourceManager;
|
|
5
|
+
/** Directory the pack is written into; the pack *is* the directory. */
|
|
6
|
+
output: string;
|
|
7
|
+
/** Recorded in meta.json. One version per pack, immutable once published. */
|
|
8
|
+
version: string;
|
|
9
|
+
namespaces?: string[];
|
|
10
|
+
/**
|
|
11
|
+
* Path to the vendored builtin pack (`src/builtin.ts`'s `builtinPackPath`).
|
|
12
|
+
* Layered *under* the input so it only supplies the `renderchest:` and
|
|
13
|
+
* `mobheads:` namespaces nothing else has, never shadowing a real one. It is
|
|
14
|
+
* what gives chests, shulker boxes and the rest a shape; without it those
|
|
15
|
+
* blocks stay empty and are recorded as such.
|
|
16
|
+
*/
|
|
17
|
+
builtin?: string;
|
|
18
|
+
/** Edge-extended border per sprite side (default 4). */
|
|
19
|
+
padding?: number;
|
|
20
|
+
/** Ceiling on an atlas page's dimensions (default 4096). */
|
|
21
|
+
maxPageSize?: number;
|
|
22
|
+
onProgress?: (stage: string, done: number, total: number) => void;
|
|
23
|
+
}
|
|
24
|
+
interface BlockPackFailure {
|
|
25
|
+
id: string;
|
|
26
|
+
/**
|
|
27
|
+
* `intentional` is not a failure: it marks a block that is empty because the
|
|
28
|
+
* game draws nothing for it. Filter it out to get the real gaps.
|
|
29
|
+
*/
|
|
30
|
+
kind: 'blockstate' | 'model' | 'texture' | 'sprite' | 'geometry' | 'tint' | 'intentional';
|
|
31
|
+
reason: string;
|
|
32
|
+
}
|
|
33
|
+
interface BlockPackResult {
|
|
34
|
+
blocks: number;
|
|
35
|
+
models: number;
|
|
36
|
+
sprites: number;
|
|
37
|
+
pages: number;
|
|
38
|
+
/** Files actually written — the only trustworthy success signal. */
|
|
39
|
+
writtenFiles: number;
|
|
40
|
+
failures: BlockPackFailure[];
|
|
41
|
+
}
|
|
42
|
+
declare function exportBlockPack(options: ExportBlockPackOptions): Promise<BlockPackResult>;
|
|
43
|
+
//#endregion
|
|
44
|
+
export { exportBlockPack as i, BlockPackResult as n, ExportBlockPackOptions as r, BlockPackFailure as t };
|