grotesque disc
inverted inferno
This commit is contained in:
1 parent
0510d21fa3
commit
6ee542609d
17 files changed
+57
-456
No files matched your search
+1
@@ -389,6 +389,7 @@ public final class CreativeTabContents {
|
|||||||
entry("music_disc_underside", "tools_and_utilities"),
|
entry("music_disc_underside", "tools_and_utilities"),
|
||||||
entry("music_disc_the_queen", "tools_and_utilities"),
|
entry("music_disc_the_queen", "tools_and_utilities"),
|
||||||
entry("music_disc_verdance", "tools_and_utilities"),
|
entry("music_disc_verdance", "tools_and_utilities"),
|
||||||
|
entry("music_disc_grotesque", "tools_and_utilities"),
|
||||||
entry("diamond_minecart", "tools_and_utilities", "minecart"),
|
entry("diamond_minecart", "tools_and_utilities", "minecart"),
|
||||||
entry("bile_bucket", "tools_and_utilities"),
|
entry("bile_bucket", "tools_and_utilities"),
|
||||||
entry("ichor_bucket", "tools_and_utilities"),
|
entry("ichor_bucket", "tools_and_utilities"),
|
||||||
|
|||||||
@@ -398,6 +398,8 @@
|
|||||||
"jukebox_song.antarchy.the_queen": "Karlljp - The Queen",
|
"jukebox_song.antarchy.the_queen": "Karlljp - The Queen",
|
||||||
"item.antarchy.music_disc_verdance": "Music Disc",
|
"item.antarchy.music_disc_verdance": "Music Disc",
|
||||||
"jukebox_song.antarchy.verdance": "Hewo - Verdance",
|
"jukebox_song.antarchy.verdance": "Hewo - Verdance",
|
||||||
|
"item.antarchy.music_disc_grotesque": "Music Disc",
|
||||||
|
"jukebox_song.antarchy.grotesque": "DocShrekBot - Grotesque",
|
||||||
"item.antarchy.nightmare_helmet": "Nightmare Helmet",
|
"item.antarchy.nightmare_helmet": "Nightmare Helmet",
|
||||||
"item.antarchy.nightmare_chestplate": "Nightmare Chestplate",
|
"item.antarchy.nightmare_chestplate": "Nightmare Chestplate",
|
||||||
"item.antarchy.nightmare_leggings": "Nightmare Leggings",
|
"item.antarchy.nightmare_leggings": "Nightmare Leggings",
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "antarchy:item/music_disc_grotesque"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1081,6 +1081,14 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"music_disc_grotesque": {
|
||||||
|
"sounds": [
|
||||||
|
{
|
||||||
|
"name": "antarchy:records/grotesque",
|
||||||
|
"stream": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"lucid_lullaby": {
|
"lucid_lullaby": {
|
||||||
"sounds": [
|
"sounds": [
|
||||||
{
|
{
|
||||||
@@ -1113,6 +1121,14 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"inverted_inferno": {
|
||||||
|
"sounds": [
|
||||||
|
{
|
||||||
|
"name": "antarchy:music/game/thoraxis/inverted_inferno",
|
||||||
|
"stream": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"music.game.thoraxis": {
|
"music.game.thoraxis": {
|
||||||
"sounds": [
|
"sounds": [
|
||||||
{
|
{
|
||||||
@@ -1134,6 +1150,11 @@
|
|||||||
"name": "antarchy:inversion_layer",
|
"name": "antarchy:inversion_layer",
|
||||||
"type": "event",
|
"type": "event",
|
||||||
"stream": true
|
"stream": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "antarchy:inverted_inferno",
|
||||||
|
"type": "event",
|
||||||
|
"stream": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
|
After Width: | Height: | Size: 493 B |
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"sound_event": "antarchy:music_disc_grotesque",
|
||||||
|
"description": {
|
||||||
|
"translate": "jukebox_song.antarchy.grotesque"
|
||||||
|
},
|
||||||
|
"length_in_seconds": 206.86,
|
||||||
|
"comparator_output": 13
|
||||||
|
}
|
||||||
+5
@@ -155,6 +155,11 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "minecraft:item",
|
||||||
|
"name": "antarchy:music_disc_grotesque",
|
||||||
|
"weight": 2
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "minecraft:item",
|
"type": "minecraft:item",
|
||||||
"name": "antos:floppy_disk",
|
"name": "antos:floppy_disk",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"values": [
|
"values": [
|
||||||
"antarchy:music_disc_underside",
|
"antarchy:music_disc_underside",
|
||||||
"antarchy:music_disc_the_queen",
|
"antarchy:music_disc_the_queen",
|
||||||
"antarchy:music_disc_verdance"
|
"antarchy:music_disc_verdance",
|
||||||
|
"antarchy:music_disc_grotesque"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
+5
@@ -102,6 +102,11 @@ public final class AntarchyFabricItems {
|
|||||||
.jukeboxPlayable(ResourceKey.create(Registries.JUKEBOX_SONG,
|
.jukeboxPlayable(ResourceKey.create(Registries.JUKEBOX_SONG,
|
||||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "verdance"))));
|
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "verdance"))));
|
||||||
|
|
||||||
|
public static final DeferredItem<Item> MUSIC_DISC_GROTESQUE = ITEMS.registerSimpleItem("music_disc_grotesque",
|
||||||
|
new Item.Properties().stacksTo(1).rarity(Rarity.RARE)
|
||||||
|
.jukeboxPlayable(ResourceKey.create(Registries.JUKEBOX_SONG,
|
||||||
|
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "grotesque"))));
|
||||||
|
|
||||||
|
|
||||||
public static final DeferredItem<Item> PRIMORDIAL_SCUTE = ITEMS.registerSimpleItem("primordial_scute",
|
public static final DeferredItem<Item> PRIMORDIAL_SCUTE = ITEMS.registerSimpleItem("primordial_scute",
|
||||||
new Item.Properties().rarity(Rarity.UNCOMMON));
|
new Item.Properties().rarity(Rarity.UNCOMMON));
|
||||||
|
|||||||
+1
@@ -533,6 +533,7 @@ public final class AntarchyFabricSounds {
|
|||||||
public static final DeferredHolder<SoundEvent, SoundEvent> MUSIC_DISC_UNDERSIDE = registerSoundEvent("music_disc_underside");
|
public static final DeferredHolder<SoundEvent, SoundEvent> MUSIC_DISC_UNDERSIDE = registerSoundEvent("music_disc_underside");
|
||||||
public static final DeferredHolder<SoundEvent, SoundEvent> THE_QUEEN = registerSoundEvent("the_queen");
|
public static final DeferredHolder<SoundEvent, SoundEvent> THE_QUEEN = registerSoundEvent("the_queen");
|
||||||
public static final DeferredHolder<SoundEvent, SoundEvent> MUSIC_DISC_VERDANCE = registerSoundEvent("music_disc_verdance");
|
public static final DeferredHolder<SoundEvent, SoundEvent> MUSIC_DISC_VERDANCE = registerSoundEvent("music_disc_verdance");
|
||||||
|
public static final DeferredHolder<SoundEvent, SoundEvent> MUSIC_DISC_GROTESQUE = registerSoundEvent("music_disc_grotesque");
|
||||||
public static final DeferredHolder<SoundEvent, SoundEvent> WORM_HOOK_FLYING = registerSoundEvent("worm_hook_flying");
|
public static final DeferredHolder<SoundEvent, SoundEvent> WORM_HOOK_FLYING = registerSoundEvent("worm_hook_flying");
|
||||||
public static final DeferredHolder<SoundEvent, SoundEvent> BLOOD_CRYSTAL_KATANA_DASH = registerSoundEvent("blood_crystal_katana_dash");
|
public static final DeferredHolder<SoundEvent, SoundEvent> BLOOD_CRYSTAL_KATANA_DASH = registerSoundEvent("blood_crystal_katana_dash");
|
||||||
public static final DeferredHolder<SoundEvent, SoundEvent> BLOOD_CRYSTAL_ARMOR_EQUIP = registerSoundEvent("blood_crystal_armor_equip");
|
public static final DeferredHolder<SoundEvent, SoundEvent> BLOOD_CRYSTAL_ARMOR_EQUIP = registerSoundEvent("blood_crystal_armor_equip");
|
||||||
|
|||||||
Whitespace-only changes.
+5
@@ -110,6 +110,11 @@ public final class AntarchyNeoforgeItems {
|
|||||||
.jukeboxPlayable(ResourceKey.create(Registries.JUKEBOX_SONG,
|
.jukeboxPlayable(ResourceKey.create(Registries.JUKEBOX_SONG,
|
||||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "verdance"))));
|
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "verdance"))));
|
||||||
|
|
||||||
|
public static final DeferredItem<Item> MUSIC_DISC_GROTESQUE = ITEMS.registerSimpleItem("music_disc_grotesque",
|
||||||
|
new Item.Properties().stacksTo(1).rarity(Rarity.RARE)
|
||||||
|
.jukeboxPlayable(ResourceKey.create(Registries.JUKEBOX_SONG,
|
||||||
|
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "grotesque"))));
|
||||||
|
|
||||||
private static final DeferredHolder<ArmorMaterial, ArmorMaterial> ULTIMATE_ARMOR_MATERIAL = ARMOR_MATERIALS.register("ultimate",
|
private static final DeferredHolder<ArmorMaterial, ArmorMaterial> ULTIMATE_ARMOR_MATERIAL = ARMOR_MATERIALS.register("ultimate",
|
||||||
() -> new ArmorMaterial(
|
() -> new ArmorMaterial(
|
||||||
createUltimateArmorDefense(),
|
createUltimateArmorDefense(),
|
||||||
|
|||||||
+1
@@ -210,6 +210,7 @@ public final class AntarchyNeoforgeSounds {
|
|||||||
public static final DeferredHolder<SoundEvent, SoundEvent> MUSIC_DISC_UNDERSIDE = registerSoundEvent("music_disc_underside");
|
public static final DeferredHolder<SoundEvent, SoundEvent> MUSIC_DISC_UNDERSIDE = registerSoundEvent("music_disc_underside");
|
||||||
public static final DeferredHolder<SoundEvent, SoundEvent> THE_QUEEN = registerSoundEvent("the_queen");
|
public static final DeferredHolder<SoundEvent, SoundEvent> THE_QUEEN = registerSoundEvent("the_queen");
|
||||||
public static final DeferredHolder<SoundEvent, SoundEvent> MUSIC_DISC_VERDANCE = registerSoundEvent("music_disc_verdance");
|
public static final DeferredHolder<SoundEvent, SoundEvent> MUSIC_DISC_VERDANCE = registerSoundEvent("music_disc_verdance");
|
||||||
|
public static final DeferredHolder<SoundEvent, SoundEvent> MUSIC_DISC_GROTESQUE = registerSoundEvent("music_disc_grotesque");
|
||||||
public static final DeferredHolder<SoundEvent, SoundEvent> WORM_HOOK_FLYING = registerSoundEvent("worm_hook_flying");
|
public static final DeferredHolder<SoundEvent, SoundEvent> WORM_HOOK_FLYING = registerSoundEvent("worm_hook_flying");
|
||||||
public static final DeferredHolder<SoundEvent, SoundEvent> BLOOD_CRYSTAL_KATANA_DASH = registerSoundEvent("blood_crystal_katana_dash");
|
public static final DeferredHolder<SoundEvent, SoundEvent> BLOOD_CRYSTAL_KATANA_DASH = registerSoundEvent("blood_crystal_katana_dash");
|
||||||
public static final DeferredHolder<SoundEvent, SoundEvent> BLOOD_CRYSTAL_ARMOR_EQUIP = registerSoundEvent("blood_crystal_armor_equip");
|
public static final DeferredHolder<SoundEvent, SoundEvent> BLOOD_CRYSTAL_ARMOR_EQUIP = registerSoundEvent("blood_crystal_armor_equip");
|
||||||
|
|||||||
@@ -1,125 +0,0 @@
|
|||||||
import re
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from PIL import Image
|
|
||||||
|
|
||||||
|
|
||||||
SOURCE_DIR = (
|
|
||||||
Path(__file__).resolve().parents[1]
|
|
||||||
/ "common-1.21.1/src/main/resources/assets/antarchy/textures/gui/antpaint"
|
|
||||||
)
|
|
||||||
ICON_NAMES = ("undo", "redo", "pencil", "eraser", "trash")
|
|
||||||
TOKEN = re.compile(r"[MmLlHhVvZz]|-?(?:\d+(?:\.\d*)?|\.\d+)")
|
|
||||||
|
|
||||||
|
|
||||||
def polygons_from_path(data):
|
|
||||||
tokens = TOKEN.findall(data)
|
|
||||||
polygons = []
|
|
||||||
points = []
|
|
||||||
x = y = start_x = start_y = 0.0
|
|
||||||
command = None
|
|
||||||
i = 0
|
|
||||||
|
|
||||||
while i < len(tokens):
|
|
||||||
if tokens[i].isalpha():
|
|
||||||
command = tokens[i]
|
|
||||||
i += 1
|
|
||||||
if command in "Zz":
|
|
||||||
if len(points) > 2:
|
|
||||||
polygons.append(points)
|
|
||||||
points = []
|
|
||||||
x, y = start_x, start_y
|
|
||||||
command = None
|
|
||||||
continue
|
|
||||||
|
|
||||||
if command is None:
|
|
||||||
raise ValueError(f"Unexpected SVG path data near token {i}: {data}")
|
|
||||||
|
|
||||||
relative = command.islower()
|
|
||||||
op = command.upper()
|
|
||||||
if op in ("M", "L"):
|
|
||||||
px = float(tokens[i])
|
|
||||||
py = float(tokens[i + 1])
|
|
||||||
i += 2
|
|
||||||
if relative:
|
|
||||||
px += x
|
|
||||||
py += y
|
|
||||||
x, y = px, py
|
|
||||||
if op == "M":
|
|
||||||
if len(points) > 2:
|
|
||||||
polygons.append(points)
|
|
||||||
points = [(x, y)]
|
|
||||||
start_x, start_y = x, y
|
|
||||||
command = "l" if relative else "L"
|
|
||||||
else:
|
|
||||||
points.append((x, y))
|
|
||||||
elif op == "H":
|
|
||||||
px = float(tokens[i])
|
|
||||||
i += 1
|
|
||||||
x = x + px if relative else px
|
|
||||||
points.append((x, y))
|
|
||||||
elif op == "V":
|
|
||||||
py = float(tokens[i])
|
|
||||||
i += 1
|
|
||||||
y = y + py if relative else py
|
|
||||||
points.append((x, y))
|
|
||||||
else:
|
|
||||||
raise ValueError(f"Unsupported SVG command {command!r}: {data}")
|
|
||||||
|
|
||||||
if len(points) > 2:
|
|
||||||
polygons.append(points)
|
|
||||||
return polygons
|
|
||||||
|
|
||||||
|
|
||||||
def contains_point(polygon, px, py):
|
|
||||||
inside = False
|
|
||||||
previous = polygon[-1]
|
|
||||||
for current in polygon:
|
|
||||||
x1, y1 = previous
|
|
||||||
x2, y2 = current
|
|
||||||
if (y1 > py) != (y2 > py):
|
|
||||||
crossing_x = (x2 - x1) * (py - y1) / (y2 - y1) + x1
|
|
||||||
if px < crossing_x:
|
|
||||||
inside = not inside
|
|
||||||
previous = current
|
|
||||||
return inside
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
for icon_name in ICON_NAMES:
|
|
||||||
source = (SOURCE_DIR / f"{icon_name}.svg").read_text(encoding="utf-8")
|
|
||||||
paths = re.findall(r'<path\b[^>]*\bd="([^"]+)"', source)
|
|
||||||
if not paths:
|
|
||||||
raise ValueError(f"No SVG path found in {icon_name}.svg")
|
|
||||||
polygons = [polygon for path in paths for polygon in polygons_from_path(path)]
|
|
||||||
|
|
||||||
mask = [
|
|
||||||
[
|
|
||||||
any(contains_point(poly, x + 0.5, y + 0.5) for poly in polygons)
|
|
||||||
for x in range(24)
|
|
||||||
]
|
|
||||||
for y in range(24)
|
|
||||||
]
|
|
||||||
image = Image.new("RGBA", (24, 24), (0, 0, 0, 0))
|
|
||||||
pixels = image.load()
|
|
||||||
for y in range(24):
|
|
||||||
for x in range(24):
|
|
||||||
if mask[y][x]:
|
|
||||||
pixels[x, y] = (101, 255, 101, 255)
|
|
||||||
continue
|
|
||||||
outlined = any(
|
|
||||||
0 <= nx < 24 and 0 <= ny < 24 and mask[ny][nx]
|
|
||||||
for nx, ny in (
|
|
||||||
(x - 1, y),
|
|
||||||
(x + 1, y),
|
|
||||||
(x, y - 1),
|
|
||||||
(x, y + 1),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if outlined:
|
|
||||||
pixels[x, y] = (3, 6, 3, 255)
|
|
||||||
image.save(SOURCE_DIR / f"{icon_name}.png")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@@ -1,330 +0,0 @@
|
|||||||
import fs from "node:fs";
|
|
||||||
import path from "node:path";
|
|
||||||
import zlib from "node:zlib";
|
|
||||||
|
|
||||||
const TAG = {
|
|
||||||
END: 0,
|
|
||||||
BYTE: 1,
|
|
||||||
SHORT: 2,
|
|
||||||
INT: 3,
|
|
||||||
LONG: 4,
|
|
||||||
FLOAT: 5,
|
|
||||||
DOUBLE: 6,
|
|
||||||
BYTE_ARRAY: 7,
|
|
||||||
STRING: 8,
|
|
||||||
LIST: 9,
|
|
||||||
COMPOUND: 10,
|
|
||||||
INT_ARRAY: 11,
|
|
||||||
LONG_ARRAY: 12
|
|
||||||
};
|
|
||||||
|
|
||||||
class Reader {
|
|
||||||
constructor(buffer) {
|
|
||||||
this.buffer = buffer;
|
|
||||||
this.offset = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte() { return this.buffer.readInt8(this.offset++); }
|
|
||||||
ushort() { const value = this.buffer.readUInt16BE(this.offset); this.offset += 2; return value; }
|
|
||||||
short() { const value = this.buffer.readInt16BE(this.offset); this.offset += 2; return value; }
|
|
||||||
int() { const value = this.buffer.readInt32BE(this.offset); this.offset += 4; return value; }
|
|
||||||
long() { const value = this.buffer.readBigInt64BE(this.offset); this.offset += 8; return value; }
|
|
||||||
float() { const value = this.buffer.readFloatBE(this.offset); this.offset += 4; return value; }
|
|
||||||
double() { const value = this.buffer.readDoubleBE(this.offset); this.offset += 8; return value; }
|
|
||||||
string() { const length = this.ushort(); const value = this.buffer.toString("utf8", this.offset, this.offset + length); this.offset += length; return value; }
|
|
||||||
|
|
||||||
payload(type) {
|
|
||||||
switch (type) {
|
|
||||||
case TAG.BYTE: return this.byte();
|
|
||||||
case TAG.SHORT: return this.short();
|
|
||||||
case TAG.INT: return this.int();
|
|
||||||
case TAG.LONG: return this.long();
|
|
||||||
case TAG.FLOAT: return this.float();
|
|
||||||
case TAG.DOUBLE: return this.double();
|
|
||||||
case TAG.BYTE_ARRAY: {
|
|
||||||
const length = this.int();
|
|
||||||
const value = Buffer.from(this.buffer.subarray(this.offset, this.offset + length));
|
|
||||||
this.offset += length;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
case TAG.STRING: return this.string();
|
|
||||||
case TAG.LIST: {
|
|
||||||
const elementType = this.byte();
|
|
||||||
const length = this.int();
|
|
||||||
const items = new Array(length);
|
|
||||||
for (let i = 0; i < length; i++) items[i] = this.payload(elementType);
|
|
||||||
return { elementType, items };
|
|
||||||
}
|
|
||||||
case TAG.COMPOUND: {
|
|
||||||
const value = {};
|
|
||||||
while (true) {
|
|
||||||
const childType = this.byte();
|
|
||||||
if (childType === TAG.END) break;
|
|
||||||
value[this.string()] = { type: childType, value: this.payload(childType) };
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
case TAG.INT_ARRAY: {
|
|
||||||
const length = this.int();
|
|
||||||
const value = new Int32Array(length);
|
|
||||||
for (let i = 0; i < length; i++) value[i] = this.int();
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
case TAG.LONG_ARRAY: {
|
|
||||||
const length = this.int();
|
|
||||||
const value = new Array(length);
|
|
||||||
for (let i = 0; i < length; i++) value[i] = this.long();
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
default: throw new Error(`Unsupported NBT tag type ${type}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
root() {
|
|
||||||
const type = this.byte();
|
|
||||||
if (type !== TAG.COMPOUND) throw new Error(`Expected compound root, got ${type}`);
|
|
||||||
this.string();
|
|
||||||
return { type, value: this.payload(type) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Writer {
|
|
||||||
constructor() { this.parts = []; }
|
|
||||||
push(buffer) { this.parts.push(buffer); }
|
|
||||||
byte(value) { const b = Buffer.allocUnsafe(1); b.writeInt8(value); this.push(b); }
|
|
||||||
short(value) { const b = Buffer.allocUnsafe(2); b.writeInt16BE(value); this.push(b); }
|
|
||||||
ushort(value) { const b = Buffer.allocUnsafe(2); b.writeUInt16BE(value); this.push(b); }
|
|
||||||
int(value) { const b = Buffer.allocUnsafe(4); b.writeInt32BE(value); this.push(b); }
|
|
||||||
long(value) { const b = Buffer.allocUnsafe(8); b.writeBigInt64BE(BigInt(value)); this.push(b); }
|
|
||||||
float(value) { const b = Buffer.allocUnsafe(4); b.writeFloatBE(value); this.push(b); }
|
|
||||||
double(value) { const b = Buffer.allocUnsafe(8); b.writeDoubleBE(value); this.push(b); }
|
|
||||||
string(value) { const b = Buffer.from(value, "utf8"); this.ushort(b.length); this.push(b); }
|
|
||||||
|
|
||||||
payload(type, value) {
|
|
||||||
switch (type) {
|
|
||||||
case TAG.BYTE: this.byte(value); break;
|
|
||||||
case TAG.SHORT: this.short(value); break;
|
|
||||||
case TAG.INT: this.int(value); break;
|
|
||||||
case TAG.LONG: this.long(value); break;
|
|
||||||
case TAG.FLOAT: this.float(value); break;
|
|
||||||
case TAG.DOUBLE: this.double(value); break;
|
|
||||||
case TAG.BYTE_ARRAY: this.int(value.length); this.push(value); break;
|
|
||||||
case TAG.STRING: this.string(value); break;
|
|
||||||
case TAG.LIST:
|
|
||||||
this.byte(value.elementType);
|
|
||||||
this.int(value.items.length);
|
|
||||||
for (const item of value.items) this.payload(value.elementType, item);
|
|
||||||
break;
|
|
||||||
case TAG.COMPOUND:
|
|
||||||
for (const [name, child] of Object.entries(value)) {
|
|
||||||
this.byte(child.type);
|
|
||||||
this.string(name);
|
|
||||||
this.payload(child.type, child.value);
|
|
||||||
}
|
|
||||||
this.byte(TAG.END);
|
|
||||||
break;
|
|
||||||
case TAG.INT_ARRAY:
|
|
||||||
this.int(value.length);
|
|
||||||
for (const item of value) this.int(item);
|
|
||||||
break;
|
|
||||||
case TAG.LONG_ARRAY:
|
|
||||||
this.int(value.length);
|
|
||||||
for (const item of value) this.long(item);
|
|
||||||
break;
|
|
||||||
default: throw new Error(`Unsupported NBT tag type ${type}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
root(root) {
|
|
||||||
this.byte(TAG.COMPOUND);
|
|
||||||
this.string("");
|
|
||||||
this.payload(TAG.COMPOUND, root.value);
|
|
||||||
return Buffer.concat(this.parts);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const inputDir = path.resolve("common-1.21.1/src/main/resources/data/antarchy/structure/kings_tree");
|
|
||||||
const outputDir = path.resolve("common-1.21.1/src/main/resources/data/antarchy/structure/kings_tree_tiles");
|
|
||||||
const Y_SHIFT = 22;
|
|
||||||
const TREE_HEIGHT = 358;
|
|
||||||
const CENTER_X = 137;
|
|
||||||
const CENTER_Z = 163;
|
|
||||||
const sources = [
|
|
||||||
{ file: "kings_tree_bottom.nbt", offset: [0, -22, 0] },
|
|
||||||
{ file: "kings_tree1.nbt", offset: [0, 0, 0] },
|
|
||||||
{ file: "kings_tree2.nbt", offset: [-40, 12, -52] },
|
|
||||||
{ file: "kings_tree3.nbt", offset: [-40, 149, -52] },
|
|
||||||
{ file: "kings_tree4.nbt", offset: [-54, 233, -64] }
|
|
||||||
];
|
|
||||||
const expectedConnections = [
|
|
||||||
["kings_tree1.nbt", [287, 0, 363], "kings_tree_bottom.nbt", [287, 21, 363]],
|
|
||||||
["kings_tree1.nbt", [274, 11, 363], "kings_tree2.nbt", [314, 0, 415]],
|
|
||||||
["kings_tree2.nbt", [314, 136, 442], "kings_tree3.nbt", [314, 0, 442]],
|
|
||||||
["kings_tree3.nbt", [314, 83, 420], "kings_tree4.nbt", [328, 0, 432]]
|
|
||||||
];
|
|
||||||
|
|
||||||
const intList = (...values) => ({ type: TAG.LIST, value: { elementType: TAG.INT, items: values } });
|
|
||||||
const doubleList = (...values) => ({ type: TAG.LIST, value: { elementType: TAG.DOUBLE, items: values } });
|
|
||||||
const floorDiv = (value, divisor) => Math.floor(value / divisor);
|
|
||||||
const floorMod = (value, divisor) => ((value % divisor) + divisor) % divisor;
|
|
||||||
const paletteKey = value => JSON.stringify(value, (_, item) => typeof item === "bigint" ? `${item}n` : item);
|
|
||||||
const tileNamePart = value => value < 0 ? `m${-value}` : `p${value}`;
|
|
||||||
|
|
||||||
const tiles = new Map();
|
|
||||||
const globalPalette = [];
|
|
||||||
const globalPaletteIds = new Map();
|
|
||||||
let dataVersion;
|
|
||||||
let sourceBlockCount = 0;
|
|
||||||
let skippedMarkerCount = 0;
|
|
||||||
const jigsaws = new Map();
|
|
||||||
|
|
||||||
function getTile(tileX, tileZ) {
|
|
||||||
const key = `${tileX},${tileZ}`;
|
|
||||||
let tile = tiles.get(key);
|
|
||||||
if (!tile) {
|
|
||||||
tile = { tileX, tileZ, blocks: new Map(), entities: [] };
|
|
||||||
tiles.set(key, tile);
|
|
||||||
}
|
|
||||||
return tile;
|
|
||||||
}
|
|
||||||
|
|
||||||
function paletteId(stateTag) {
|
|
||||||
const key = paletteKey(stateTag);
|
|
||||||
let id = globalPaletteIds.get(key);
|
|
||||||
if (id === undefined) {
|
|
||||||
id = globalPalette.length;
|
|
||||||
globalPaletteIds.set(key, id);
|
|
||||||
globalPalette.push(stateTag);
|
|
||||||
}
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const source of sources) {
|
|
||||||
const compressed = fs.readFileSync(path.join(inputDir, source.file));
|
|
||||||
const root = new Reader(zlib.gunzipSync(compressed)).root();
|
|
||||||
dataVersion ??= root.value.DataVersion;
|
|
||||||
const paletteTag = root.value.palette;
|
|
||||||
if (!paletteTag || paletteTag.type !== TAG.LIST) throw new Error(`${source.file} has no primary palette`);
|
|
||||||
const palette = paletteTag.value.items;
|
|
||||||
const remap = palette.map(paletteId);
|
|
||||||
const blocks = root.value.blocks?.value.items ?? [];
|
|
||||||
sourceBlockCount += blocks.length;
|
|
||||||
|
|
||||||
for (const block of blocks) {
|
|
||||||
const stateIndex = block.state.value;
|
|
||||||
const blockName = palette[stateIndex].Name.value;
|
|
||||||
if (blockName === "minecraft:jigsaw") {
|
|
||||||
jigsaws.set(`${source.file}:${block.pos.value.items.join(",")}`, {
|
|
||||||
state: palette[stateIndex],
|
|
||||||
nbt: block.nbt?.value ?? {}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (blockName === "minecraft:jigsaw" || blockName === "minecraft:structure_void" || blockName === "minecraft:structure_block") {
|
|
||||||
skippedMarkerCount++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [x, y, z] = block.pos.value.items;
|
|
||||||
const relativeX = x + source.offset[0] - CENTER_X;
|
|
||||||
const relativeY = y + source.offset[1] + Y_SHIFT;
|
|
||||||
const relativeZ = z + source.offset[2] - CENTER_Z;
|
|
||||||
if (relativeY < 0 || relativeY >= TREE_HEIGHT) throw new Error(`${source.file} produced out-of-range Y ${relativeY}`);
|
|
||||||
const tileX = floorDiv(relativeX, 16);
|
|
||||||
const tileZ = floorDiv(relativeZ, 16);
|
|
||||||
const localX = floorMod(relativeX, 16);
|
|
||||||
const localZ = floorMod(relativeZ, 16);
|
|
||||||
const translated = {
|
|
||||||
...block,
|
|
||||||
pos: intList(localX, relativeY, localZ),
|
|
||||||
state: { type: TAG.INT, value: remap[stateIndex] }
|
|
||||||
};
|
|
||||||
getTile(tileX, tileZ).blocks.set(`${localX},${relativeY},${localZ}`, translated);
|
|
||||||
}
|
|
||||||
|
|
||||||
const entities = root.value.entities?.value.items ?? [];
|
|
||||||
for (const entity of entities) {
|
|
||||||
const [blockX, blockY, blockZ] = entity.blockPos.value.items;
|
|
||||||
const [x, y, z] = entity.pos.value.items;
|
|
||||||
const relativeBlockX = blockX + source.offset[0] - CENTER_X;
|
|
||||||
const relativeBlockY = blockY + source.offset[1] + Y_SHIFT;
|
|
||||||
const relativeBlockZ = blockZ + source.offset[2] - CENTER_Z;
|
|
||||||
const tileX = floorDiv(relativeBlockX, 16);
|
|
||||||
const tileZ = floorDiv(relativeBlockZ, 16);
|
|
||||||
const tileOriginX = tileX * 16;
|
|
||||||
const tileOriginZ = tileZ * 16;
|
|
||||||
getTile(tileX, tileZ).entities.push({
|
|
||||||
...entity,
|
|
||||||
blockPos: intList(relativeBlockX - tileOriginX, relativeBlockY, relativeBlockZ - tileOriginZ),
|
|
||||||
pos: doubleList(
|
|
||||||
x + source.offset[0] - CENTER_X - tileOriginX,
|
|
||||||
y + source.offset[1] + Y_SHIFT,
|
|
||||||
z + source.offset[2] - CENTER_Z - tileOriginZ
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [parentFile, parentPos, childFile, childPos] of expectedConnections) {
|
|
||||||
const parent = sources.find(source => source.file === parentFile);
|
|
||||||
const child = sources.find(source => source.file === childFile);
|
|
||||||
const parentJigsaw = jigsaws.get(`${parentFile}:${parentPos.join(",")}`);
|
|
||||||
const childJigsaw = jigsaws.get(`${childFile}:${childPos.join(",")}`);
|
|
||||||
if (!parentJigsaw || !childJigsaw) {
|
|
||||||
throw new Error(`Missing expected jigsaw connection between ${parentFile} and ${childFile}`);
|
|
||||||
}
|
|
||||||
const parentTarget = parentJigsaw.nbt.target?.value;
|
|
||||||
const childName = childJigsaw.nbt.name?.value;
|
|
||||||
if (parentTarget !== childName) {
|
|
||||||
throw new Error(`Jigsaw target mismatch: ${parentFile} targets ${parentTarget}, but ${childFile} is named ${childName}`);
|
|
||||||
}
|
|
||||||
const parentOrientation = parentJigsaw.state.Properties?.value.orientation?.value;
|
|
||||||
const childOrientation = childJigsaw.state.Properties?.value.orientation?.value;
|
|
||||||
console.log(`${parentFile} ${parentOrientation} -> ${childFile} ${childOrientation}`);
|
|
||||||
const [parentFront, parentTop] = parentOrientation.split("_");
|
|
||||||
const [childFront, childTop] = childOrientation.split("_");
|
|
||||||
const opposite = { up: "down", down: "up", north: "south", south: "north", east: "west", west: "east" };
|
|
||||||
const facingStep = {
|
|
||||||
up: [0, 1, 0], down: [0, -1, 0], north: [0, 0, -1],
|
|
||||||
south: [0, 0, 1], east: [1, 0, 0], west: [-1, 0, 0]
|
|
||||||
};
|
|
||||||
const parentWorld = parentPos.map((value, axis) => value + parent.offset[axis]);
|
|
||||||
const expectedChildWorld = parentWorld.map((value, axis) => value + facingStep[parentFront][axis]);
|
|
||||||
const childWorld = childPos.map((value, axis) => value + child.offset[axis]);
|
|
||||||
if (expectedChildWorld.some((value, axis) => value !== childWorld[axis])) {
|
|
||||||
throw new Error(`Misaligned jigsaw connection: ${parentFile} expects ${expectedChildWorld}, but ${childFile} is at ${childWorld}`);
|
|
||||||
}
|
|
||||||
if (opposite[parentFront] !== childFront) {
|
|
||||||
throw new Error(`Jigsaw facing mismatch: ${parentOrientation} cannot connect to ${childOrientation}`);
|
|
||||||
}
|
|
||||||
const joint = parentJigsaw.nbt.joint?.value ?? "rollable";
|
|
||||||
if (joint === "aligned" && parentTop !== childTop) {
|
|
||||||
throw new Error(`Jigsaw ${parentFile} requires a rotated child: ${parentOrientation} vs ${childOrientation}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.rmSync(outputDir, { recursive: true, force: true });
|
|
||||||
fs.mkdirSync(outputDir, { recursive: true });
|
|
||||||
let outputBlockCount = 0;
|
|
||||||
for (const tile of tiles.values()) {
|
|
||||||
if (tile.blocks.size === 0 && tile.entities.length === 0) continue;
|
|
||||||
const root = {
|
|
||||||
type: TAG.COMPOUND,
|
|
||||||
value: {
|
|
||||||
DataVersion: dataVersion,
|
|
||||||
size: intList(16, TREE_HEIGHT, 16),
|
|
||||||
palette: { type: TAG.LIST, value: { elementType: TAG.COMPOUND, items: globalPalette } },
|
|
||||||
blocks: { type: TAG.LIST, value: { elementType: TAG.COMPOUND, items: [...tile.blocks.values()] } },
|
|
||||||
entities: { type: TAG.LIST, value: { elementType: TAG.COMPOUND, items: tile.entities } }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
outputBlockCount += tile.blocks.size;
|
|
||||||
const encoded = new Writer().root(root);
|
|
||||||
const name = `${tileNamePart(tile.tileX)}_${tileNamePart(tile.tileZ)}.nbt`;
|
|
||||||
fs.writeFileSync(path.join(outputDir, name), zlib.gzipSync(encoded, { level: 9 }));
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`Read ${sourceBlockCount.toLocaleString()} blocks from ${sources.length} templates.`);
|
|
||||||
console.log(`Skipped ${skippedMarkerCount.toLocaleString()} structure marker blocks.`);
|
|
||||||
console.log(`Wrote ${outputBlockCount.toLocaleString()} blocks in ${tiles.size} chunk-local templates.`);
|
|
||||||
console.log(`Global palette contains ${globalPalette.length} block states.`);
|
|
||||||
console.log(`Validated ${expectedConnections.length} matching jigsaw connections.`);
|
|
||||||
Reference in new issue
Block a user