51f9609327
build / mod (push) Successful in 3m50s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1713 lines
70 KiB
Python
1713 lines
70 KiB
Python
#!/usr/bin/env python3
|
||
"""Генерирует контент мода AirDef (airdef-mod/assets и data): текстуры, модели частей, предметы.
|
||
|
||
Каждая часть — блок со своей моделью в одной ориентации: нос смотрит на север (-z), центр — (8, 8, 8).
|
||
Поворачивает и масштабирует их движок форка (render.set-models), так что ракурсы больше не нужны.
|
||
Координаты элементов — в пределах -16..32 (иначе Horizon отбрасывает модель).
|
||
|
||
Запуск: python3 tools/gen_models.py
|
||
"""
|
||
import json
|
||
import math
|
||
import os
|
||
import sys
|
||
import random
|
||
import shutil
|
||
import zlib
|
||
|
||
from PIL import Image
|
||
|
||
import gen_tex as GT
|
||
|
||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||
ROOT = os.path.normpath(os.path.join(os.path.dirname(__file__), "..", "airdef-mod"))
|
||
NS = "airdef"
|
||
|
||
|
||
# ---------------------------------------------------------------- текстуры
|
||
def clamp(v):
|
||
return max(0, min(255, int(v)))
|
||
|
||
|
||
def noise(base, amp, seed, size=16):
|
||
rnd = random.Random(seed)
|
||
im = Image.new("RGBA", (size, size))
|
||
for y in range(size):
|
||
for x in range(size):
|
||
d = rnd.randint(-amp, amp)
|
||
im.putpixel((x, y), tuple(clamp(c + d) for c in base) + (255,))
|
||
return im
|
||
|
||
|
||
def panels(base, seed, size=16, every=8):
|
||
im = noise(base, 4, seed, size)
|
||
line = tuple(clamp(c - 16) for c in base) + (255,)
|
||
lite = tuple(clamp(c + 10) for c in base) + (255,)
|
||
for i in range(size):
|
||
for k in range(0, size, every):
|
||
im.putpixel((i, k), line)
|
||
im.putpixel((k, i), line)
|
||
if k + 1 < size:
|
||
im.putpixel((i, k + 1), lite)
|
||
rnd = random.Random(seed + 99)
|
||
for _ in range(size // 2): # заклёпки
|
||
x, y = rnd.randrange(size), rnd.randrange(size)
|
||
im.putpixel((x, y), lite)
|
||
return im
|
||
|
||
|
||
def camo(colors, seed, size=32, blobs=7):
|
||
rnd = random.Random(seed)
|
||
im = noise(colors[0], 4, seed, size)
|
||
for col in colors[1:]:
|
||
for _ in range(blobs):
|
||
cx, cy = rnd.randrange(size), rnd.randrange(size)
|
||
rx, ry = rnd.randint(2, 6), rnd.randint(1, 4)
|
||
for y in range(cy - ry, cy + ry + 1):
|
||
for x in range(cx - rx, cx + rx + 1):
|
||
if ((x - cx) / rx) ** 2 + ((y - cy) / ry) ** 2 <= 1 + rnd.random() * 0.3:
|
||
im.putpixel((x % size, y % size), tuple(clamp(c + rnd.randint(-5, 5)) for c in col) + (255,))
|
||
return im
|
||
|
||
|
||
def stripes(base, line, step, seed, size=16):
|
||
im = noise(base, 3, seed, size)
|
||
for y in range(0, size, step):
|
||
for x in range(size):
|
||
im.putpixel((x, y), line + (255,))
|
||
return im
|
||
|
||
|
||
def gradient(top, bottom, size=16):
|
||
im = Image.new("RGBA", (size, size))
|
||
for y in range(size):
|
||
t = y / (size - 1)
|
||
c = tuple(clamp(top[i] + (bottom[i] - top[i]) * t) for i in range(3))
|
||
for x in range(size):
|
||
im.putpixel((x, y), c + (255,))
|
||
return im
|
||
|
||
|
||
def canister_front():
|
||
im = noise((92, 100, 72), 3, 11)
|
||
for cx in (4, 11):
|
||
for cy in (4, 11):
|
||
for y in range(16):
|
||
for x in range(16):
|
||
d = (x - cx + 0.5) ** 2 + (y - cy + 0.5) ** 2
|
||
if d <= 9:
|
||
im.putpixel((x, y), (36, 38, 30, 255))
|
||
elif d <= 12:
|
||
im.putpixel((x, y), (120, 124, 100, 255))
|
||
return im
|
||
|
||
|
||
def radar_face():
|
||
im = noise((70, 76, 56), 3, 17)
|
||
for y in range(1, 15):
|
||
for x in range(1, 15):
|
||
if (x + y) % 3 == 0:
|
||
im.putpixel((x, y), (54, 58, 44, 255))
|
||
return im
|
||
|
||
|
||
def glass():
|
||
im = noise((40, 58, 78), 5, 31)
|
||
for i in range(16):
|
||
im.putpixel((i, 15 - i), (130, 160, 180, 255))
|
||
if i < 15:
|
||
im.putpixel((i + 1, 15 - i), (100, 130, 150, 255))
|
||
return im
|
||
|
||
|
||
def missile():
|
||
im = noise((232, 232, 226), 3, 21)
|
||
for x in range(16):
|
||
for y in (3, 4):
|
||
im.putpixel((x, y), (40, 40, 40, 255))
|
||
return im
|
||
|
||
|
||
def target():
|
||
im = Image.new("RGBA", (16, 16))
|
||
for y in range(16):
|
||
for x in range(16):
|
||
d = abs(x - 7.5) + abs(y - 7.5)
|
||
im.putpixel((x, y), (255, clamp(40 + d * 12), 40, 255))
|
||
return im
|
||
|
||
|
||
def fire(seed):
|
||
rnd = random.Random(seed)
|
||
im = Image.new("RGBA", (16, 16))
|
||
for y in range(16):
|
||
for x in range(16):
|
||
t = rnd.random()
|
||
im.putpixel((x, y), (255, clamp(140 + 110 * t), clamp(30 + 120 * t * t), 255))
|
||
return im
|
||
|
||
|
||
def missile_ru():
|
||
im = noise((226, 228, 222), 3, 49)
|
||
for x in range(16):
|
||
im.putpixel((x, 5), (180, 40, 40, 255))
|
||
im.putpixel((x, 11), (40, 40, 40, 255))
|
||
return im
|
||
|
||
|
||
def tube_cap():
|
||
im = noise((80, 88, 60), 3, 50)
|
||
for y in range(16):
|
||
for x in range(16):
|
||
d = (x - 7.5) ** 2 + (y - 7.5) ** 2
|
||
if d <= 36:
|
||
im.putpixel((x, y), (44, 48, 36, 255))
|
||
elif d <= 48:
|
||
im.putpixel((x, y), (120, 128, 96, 255))
|
||
return im
|
||
|
||
|
||
def tubes6():
|
||
im = noise((82, 96, 60), 3, 51)
|
||
for cx in (3, 8, 13):
|
||
for cy in (4, 11):
|
||
for y in range(16):
|
||
for x in range(16):
|
||
if (x - cx) ** 2 + (y - cy) ** 2 <= 4:
|
||
im.putpixel((x, y), (30, 32, 26, 255))
|
||
return im
|
||
|
||
|
||
def radar_big():
|
||
im = noise((92, 100, 70), 3, 52)
|
||
for y in range(16):
|
||
for x in range(16):
|
||
if x % 2 == 0 and y % 2 == 0:
|
||
im.putpixel((x, y), (60, 66, 46, 255))
|
||
return im
|
||
|
||
|
||
TEXTURES = {
|
||
"shahed": lambda: panels((66, 68, 70), 1),
|
||
"shahed_nose": lambda: noise((104, 106, 108), 4, 2),
|
||
"geran": lambda: panels((28, 28, 31), 3),
|
||
"geran_engine": lambda: panels((60, 60, 64), 4),
|
||
"intake": lambda: noise((14, 14, 14), 2, 5),
|
||
"prop": lambda: noise((22, 22, 22), 3, 6),
|
||
"missile": missile,
|
||
"nozzle": lambda: noise((44, 42, 40), 4, 7),
|
||
"flame": lambda: gradient((255, 250, 210), (255, 120, 20)),
|
||
"fire": lambda: fire(8),
|
||
"nato_green": lambda: camo([(76, 86, 58), (58, 68, 44), (44, 48, 34)], 9),
|
||
"flecktarn": lambda: camo([(92, 100, 62), (62, 74, 44), (104, 84, 54), (30, 32, 26)], 10),
|
||
"track": lambda: stripes((36, 36, 34), (74, 74, 70), 3, 11),
|
||
"tire": lambda: stripes((24, 24, 24), (40, 40, 40), 4, 12),
|
||
"canister": lambda: panels((94, 102, 74), 13),
|
||
"canister_front": canister_front,
|
||
"radar_face": radar_face,
|
||
"glass": glass,
|
||
"metal": lambda: panels((118, 120, 122), 14),
|
||
"dark_metal": lambda: panels((52, 54, 52), 15),
|
||
"barrel": lambda: noise((40, 42, 38), 3, 16),
|
||
"dish": lambda: noise((176, 178, 174), 5, 18),
|
||
"target": target,
|
||
"foam": lambda: panels((196, 180, 150), 40),
|
||
"kh101": lambda: panels((84, 88, 92), 41),
|
||
"kh101_belly": lambda: noise((150, 152, 150), 4, 42),
|
||
"lancet": lambda: panels((70, 78, 66), 43),
|
||
"ru_green": lambda: camo([(82, 96, 60), (54, 70, 40), (110, 96, 64), (34, 36, 28)], 44, blobs=6),
|
||
"ru_khaki": lambda: camo([(108, 110, 80), (84, 90, 60), (60, 64, 44)], 45, blobs=5),
|
||
"missile_ru": missile_ru,
|
||
"missile_grey": lambda: panels((150, 154, 156), 46),
|
||
"booster": lambda: noise((60, 62, 58), 3, 47),
|
||
"tube": lambda: stripes((96, 106, 74), (70, 78, 54), 4, 48),
|
||
"tube_cap": tube_cap,
|
||
"tubes6": tubes6,
|
||
"radar_big": radar_big,
|
||
"tracer": lambda: noise((255, 40, 20), 6, 60),
|
||
"tracer_hot": lambda: noise((255, 190, 150), 6, 61),
|
||
"white": lambda: noise((250, 250, 246), 2, 19),
|
||
"nato_tan": lambda: camo([(150, 130, 96), (124, 106, 76), (96, 84, 62)], 62, blobs=5),
|
||
"crate": lambda: stripes((92, 78, 48), (70, 58, 34), 5, 63),
|
||
"concrete": lambda: panels((132, 130, 124), 64),
|
||
"lamp": lambda: gradient((255, 255, 240), (255, 230, 170)),
|
||
"beam": lambda: gradient((255, 250, 225), (255, 240, 200)),
|
||
"antenna": lambda: noise((70, 72, 70), 3, 65),
|
||
"nose_dark": lambda: noise((58, 60, 62), 3, 66),
|
||
}
|
||
|
||
|
||
RINGED = set()
|
||
|
||
|
||
def _ring(name, base, bands, seed, text=None):
|
||
RINGED.add(name)
|
||
TEXTURES[name] = lambda: GT.band_tex(base, bands, seed, text=text)
|
||
TEXTURES[name + "_t"] = lambda: GT.band_tex(base, bands, seed, transpose=True, text=text)
|
||
|
||
|
||
STRETCH_FN = {}
|
||
for _i, _k in enumerate(("nato_green", "flecktarn", "ru_green", "ru_khaki", "nato_tan")):
|
||
STRETCH_FN[_k] = (lambda sx, sy, k=_k, i=_i: GT.camo(k, 100 + i, 64, sx, sy))
|
||
TEXTURES[_k] = (lambda k=_k, i=_i: GT.camo(k, 100 + i, 64))
|
||
STRETCH_FN["track_side"] = lambda sx, sy: GT.track_side(7, 32, sx, sy)
|
||
PAL_RU, PAL_RU2 = GT.PAL["ru_green"][0], GT.PAL["ru_khaki"][0]
|
||
TEXTURES.update({
|
||
"track_side": lambda: GT.track_side(7),
|
||
"track": lambda: GT.track_tex(8),
|
||
"tire": lambda: GT.tire_tex(9),
|
||
"hub": lambda: GT.hub_tex(10),
|
||
"glass_cab": lambda: GT.glass_tex(11),
|
||
"bare_lamp": lambda: GT.glass_tex(12, tint=(200, 205, 190), refl=False),
|
||
"bare_barrel": lambda: GT.metal_tex((44, 46, 44), 13),
|
||
"bare_nozzle": lambda: GT.metal_tex((66, 58, 52), 14),
|
||
"bare_dish": lambda: GT.metal_tex((178, 180, 176), 15),
|
||
"bare_antenna": lambda: GT.metal_tex((112, 114, 112), 16),
|
||
"canister_front": lambda: GT.cap_tex(28),
|
||
"mk_ru": lambda: GT.marking("digits", PAL_RU, 40, "312"),
|
||
"mk_ru2": lambda: GT.marking("digits", PAL_RU2, 41, "507"),
|
||
"mk_us": lambda: GT.marking("digits", (76, 86, 58), 42, "1-7"),
|
||
"mk_star": lambda: GT.marking("star", PAL_RU, 43),
|
||
"mk_de": lambda: GT.marking("cross", (88, 98, 62), 44),
|
||
"glow_ember": lambda: GT.Canvas(16, 1, 1, (255, 170, 80), 17).out(),
|
||
"metal": lambda: GT.painted((118, 120, 122), 18, lift=1.15),
|
||
"dark_metal": lambda: GT.painted((52, 54, 52), 19, lift=1.1),
|
||
"canister": lambda: GT.painted((92, 102, 74), 20),
|
||
"shahed": lambda: GT.painted((92, 96, 98), 21, panel=(20, 10), lift=1.2),
|
||
"geran": lambda: GT.painted((28, 28, 31), 22, panel=(20, 10), lift=1.0),
|
||
"kh101": lambda: GT.painted((84, 88, 92), 23, panel=(18, 10)),
|
||
"lancet": lambda: GT.painted((74, 82, 70), 24),
|
||
"missile_grey": lambda: GT.painted((150, 154, 156), 25, lift=1.0),
|
||
"radar_face": lambda: GT.lattice(26, 32, (96, 104, 76), (170, 176, 150)),
|
||
"radar_big": lambda: GT.lattice(27, 32, (112, 124, 92), (190, 196, 168)),
|
||
})
|
||
_ring("rm_pac3", (228, 228, 222), [(0.12, 2, (30, 30, 30)), (0.78, 1.5, (200, 170, 40)), (0.9, 1, (30, 30, 30))], 30, [(8, 14, 10)])
|
||
_ring("rm_white", (232, 232, 226), [(0.1, 2, (200, 40, 36)), (0.82, 1.5, (40, 40, 44))], 31, [(10, 12, 8)])
|
||
_ring("rm_ru", (226, 228, 222), [(0.15, 2, (170, 40, 36)), (0.55, 1, (40, 40, 40)), (0.85, 2, (170, 40, 36))], 32, [(9, 20, 10)])
|
||
_ring("rm_grey", (148, 152, 154), [(0.2, 1.5, (210, 210, 205)), (0.8, 1.5, (210, 210, 205))], 33, [(6, 14, 12)])
|
||
_ring("rm_khaki", (110, 112, 84), [(0.25, 2, (60, 62, 48)), (0.7, 1.5, (200, 190, 150))], 34, [(8, 18, 8)])
|
||
_ring("rm_dark", (58, 60, 62), [(0.3, 1.5, (150, 150, 146)), (0.85, 1.5, (150, 150, 146))], 35)
|
||
|
||
|
||
# ---------------------------------------------------------------- модели
|
||
def el(frm, to, tex, faces=None, rot=None):
|
||
"""Элемент; rot = (ось, угол) — поворот вокруг центра элемента."""
|
||
return {"from": [float(v) for v in frm], "to": [float(v) for v in to], "tex": tex, "faces": faces, "rot": rot}
|
||
|
||
|
||
def mx(e):
|
||
"""Зеркальная копия относительно x = 8."""
|
||
rot = e.get("rot")
|
||
if rot and rot[0] in ("y", "z"):
|
||
rot = (rot[0], -rot[1])
|
||
return {**e, "from": [16 - e["to"][0], e["from"][1], e["from"][2]],
|
||
"to": [16 - e["from"][0], e["to"][1], e["to"][2]], "rot": rot}
|
||
|
||
|
||
def both(e):
|
||
return [e, mx(e)]
|
||
|
||
|
||
def face_uv(frm, to, face):
|
||
dx, dy, dz = (abs(to[i] - frm[i]) for i in range(3))
|
||
w, h = {"north": (dx, dy), "south": (dx, dy), "east": (dz, dy), "west": (dz, dy),
|
||
"up": (dx, dz), "down": (dx, dz)}[face]
|
||
return [0, 0, max(0.25, min(16, w)), max(0.25, min(16, h))]
|
||
|
||
|
||
VARIANTS = {}
|
||
TEXMAP = {"barrel": "bare_barrel", "nozzle": "bare_nozzle", "dish": "bare_dish", "antenna": "bare_antenna",
|
||
"glass": "glass_cab", "intake": "bare_nozzle"}
|
||
STRETCH = {"nato_green", "flecktarn", "ru_green", "ru_khaki", "nato_tan", "track_side"}
|
||
|
||
|
||
def _q(v):
|
||
return 1 if v < 1.5 else 2 if v < 2.5 else 3
|
||
|
||
|
||
def vname(tex, qx, qy, burnt):
|
||
tex = TEXMAP.get(tex, tex)
|
||
if burnt and tex.startswith("glow_"):
|
||
tex = "bare_nozzle"
|
||
n = tex
|
||
if tex in STRETCH and (qx > 1 or qy > 1):
|
||
n += f"_s{qx}{qy}"
|
||
if burnt:
|
||
n += "_b"
|
||
if n != tex:
|
||
VARIANTS[n] = (tex, qx, qy, burnt)
|
||
return n
|
||
|
||
|
||
def model(parts, burnt=False):
|
||
for p in parts:
|
||
for i in range(3):
|
||
assert -16 <= p["from"][i] <= 32 and -16 <= p["to"][i] <= 32, p
|
||
textures, elements = {}, []
|
||
for p in parts:
|
||
faces = {}
|
||
for f in ("north", "south", "east", "west", "up", "down"):
|
||
tex = (p["faces"] or {}).get(f, p["tex"])
|
||
uv = face_uv(p["from"], p["to"], f)
|
||
dx, dy, dz = (abs(p["to"][i] - p["from"][i]) for i in range(3))
|
||
w, h = {"north": (dx, dy), "south": (dx, dy), "east": (dz, dy), "west": (dz, dy),
|
||
"up": (dx, dz), "down": (dx, dz)}[f]
|
||
tex = vname(tex, _q(w / 16), _q(h / 16), burnt)
|
||
textures[tex] = f"{NS}:block/{tex}"
|
||
faces[f] = {"uv": uv, "texture": "#" + tex}
|
||
e = {"from": p["from"], "to": p["to"], "faces": faces}
|
||
if p.get("rot"):
|
||
axis, angle = p["rot"]
|
||
origin = [(p["from"][i] + p["to"][i]) / 2 for i in range(3)]
|
||
e["rotation"] = {"angle": angle, "axis": axis, "origin": origin}
|
||
elements.append(e)
|
||
textures["particle"] = next(iter(textures.values()))
|
||
return {"ambientocclusion": False, "textures": textures, "elements": elements}
|
||
|
||
|
||
def burnt_parts(parts, seed):
|
||
"""Остов: часть мелких деталей оторвана, антенны сорваны; текстуры — обгоревшие варианты (см. model(burnt=True))."""
|
||
rnd = random.Random(seed)
|
||
out = []
|
||
for i, p in enumerate(parts):
|
||
vol = 1.0
|
||
for k in range(3):
|
||
vol *= abs(p["to"][k] - p["from"][k])
|
||
if i > 2 and ((vol < 40 and rnd.random() < 0.3) or p["tex"] in ("antenna", "dish")):
|
||
continue
|
||
out.append(p)
|
||
return out
|
||
|
||
|
||
def taper(tex, z0, z1, w0, w1, h0, h1, steps, y=8.0):
|
||
"""Корпус-веретено лесенкой: ширина w0→w1 и высота h0→h1 вдоль z0→z1."""
|
||
out = []
|
||
for i in range(steps):
|
||
za = z0 + (z1 - z0) * i / steps
|
||
zb = z0 + (z1 - z0) * (i + 1) / steps
|
||
t = (i + 0.5) / steps
|
||
w, h = w0 + (w1 - w0) * t, h0 + (h1 - h0) * t
|
||
out.append(el((8 - w / 2, y - h / 2, za), (8 + w / 2, y + h / 2, zb), tex))
|
||
return out
|
||
|
||
|
||
def delta(tex, z_root, z_tip, z_trail, half, steps, y0, y1):
|
||
"""Дельта-крыло: передняя кромка от корня (z_root) к законцовке (half, z_tip), задняя кромка — z_trail."""
|
||
out = []
|
||
for i in range(steps):
|
||
za = z_root + (z_tip - z_root) * i / steps
|
||
s = half * (i + 1) / steps
|
||
out.append(el((8 - s, y0, za), (8 + s, y1, z_trail), tex))
|
||
return out
|
||
|
||
|
||
def shahed():
|
||
p = taper("shahed_nose", -16, -10, 2.5, 5, 2.5, 5, 3)
|
||
p += [el((5.5, 5.5, -10), (10.5, 10.5, 24), "shahed")]
|
||
p += [el((6, 10.5, -6), (10, 11.5, 4), "dark_metal")] # люк ГСН/антенны
|
||
p += delta("shahed", -2, 21, 26, 21, 9, 7.2, 8.4)
|
||
p += both(el((-13.6, 6.5, 17), (-12.6, 14, 26), "shahed")) # законцовки-кили
|
||
p += [el((6, 6, 24), (10, 10, 29), "dark_metal")] # двигатель
|
||
p += both(el((6.5, 4, 25), (7.5, 6, 28), "dark_metal")) # выхлопы
|
||
return p
|
||
|
||
|
||
def shahed_prop():
|
||
return [el((7.6, -4, 7.5), (8.4, 20, 8.5), "prop"), el((-4, 7.6, 7.5), (20, 8.4, 8.5), "prop"),
|
||
el((7, 7, 7), (9, 9, 9.5), "dark_metal")]
|
||
|
||
|
||
def geran3():
|
||
p = taper("geran", -16, -9, 2.5, 5.5, 2.5, 5.5, 3)
|
||
p += [el((5, 5, -9), (11, 11, 26), "geran")]
|
||
p += delta("geran", 0, 20, 26, 18, 8, 7.2, 8.4)
|
||
p += both(el((-10.6, 6.5, 18), (-9.6, 13, 26), "geran"))
|
||
p += [el((4.5, 11, 6), (11.5, 16, 28), "geran_engine"), # реактивный двигатель сверху
|
||
el((5, 11.5, 5), (11, 15.5, 6), "intake"),
|
||
el((5.5, 11.5, 28), (10.5, 15.5, 30), "nozzle")]
|
||
return p
|
||
|
||
|
||
def geran3_flame():
|
||
return taper("flame", 6, 24, 4, 1, 4, 1, 5)
|
||
|
||
|
||
def pac3():
|
||
p = taper("missile", -16, -11, 1, 3, 1, 3, 3)
|
||
p += [el((6.5, 6.5, -11), (9.5, 9.5, 26), "missile")]
|
||
p += both(el((1.5, 7.6, 20), (6.5, 8.4, 26), "missile"))
|
||
p += [el((7.6, 1.5, 20), (8.4, 6.5, 26), "missile"), el((7.6, 9.5, 20), (8.4, 14.5, 26), "missile")]
|
||
p += both(el((4.5, 7.7, -6), (6.5, 8.3, -2), "missile")) # рули ГСН
|
||
p += [el((7, 7, 26), (9, 9, 28), "nozzle")]
|
||
return p
|
||
|
||
|
||
def irist_missile():
|
||
"""IRIS-T SLM: тонкая белая ракета, две пары малых канардов у носа и четыре хвостовых киля."""
|
||
p = taper("white", -16, -10, 1, 3, 1, 3, 3)
|
||
p += [el((6.5, 6.5, -10), (9.5, 9.5, 24), "white")]
|
||
p += both(el((3, 7.6, -9), (6.5, 8.4, -5), "missile_grey")) # канарды у носа
|
||
p += [el((7.6, 3, -9), (8.4, 6.5, -5), "missile_grey"), el((7.6, 9.5, -9), (8.4, 13, -5), "missile_grey")]
|
||
p += both(el((2, 7.6, 18), (6.5, 8.4, 26), "white")) # хвостовые киля
|
||
p += [el((7.6, 2, 18), (8.4, 6.5, 26), "white"), el((7.6, 9.5, 18), (8.4, 14, 26), "white")]
|
||
p += [el((7, 7, 24), (9, 9, 28), "nozzle")]
|
||
return p
|
||
|
||
|
||
def amraam_missile():
|
||
"""AMRAAM: белое тело с тёмным носом, четыре крыла на середине корпуса и четыре хвостовых киля."""
|
||
p = taper("nose_dark", -16, -9, 1, 3, 1, 3, 3)
|
||
p += [el((6.5, 6.5, -9), (9.5, 9.5, 26), "white")]
|
||
p += both(el((1, 7.6, 2), (6.5, 8.4, 9), "white")) # среднекорпусные крылья
|
||
p += [el((7.6, 1, 2), (8.4, 6.5, 9), "white"), el((7.6, 9.5, 2), (8.4, 15, 9), "white")]
|
||
p += both(el((4, 7.6, 19), (6.5, 8.4, 25), "white")) # хвостовые киля
|
||
p += [el((7.6, 4, 19), (8.4, 6.5, 25), "white"), el((7.6, 9.5, 19), (8.4, 12, 25), "white")]
|
||
p += [el((7, 7, 26), (9, 9, 28), "nozzle")]
|
||
return p
|
||
|
||
|
||
def flame():
|
||
"""Факел двигателя: светящаяся «капля», хвост — на юг (+z)."""
|
||
return taper("flame", -8, 26, 5, 1, 5, 1, 8)
|
||
|
||
|
||
def patriot_launcher():
|
||
"""Пакет 4 ТПК M903. Ось качания — задняя нижняя кромка (z = 30, y = 0)."""
|
||
p = [el((-6, 1, -14), (22, 19, 30), "canister", {"north": "canister_front"})]
|
||
p += [el((-6.5, 9.5, -14), (22.5, 10.5, 30), "dark_metal"),
|
||
el((7.5, 1, -14), (8.5, 19, 30), "dark_metal"),
|
||
el((-7, 0, 24), (23, 2, 30), "metal")]
|
||
p += both(el((-8, 2, -10), (-6, 17, -6), "metal")) + both(el((-8, 2, 20), (-6, 17, 24), "metal"))
|
||
return p
|
||
|
||
|
||
def patriot_truck():
|
||
"""Тягач (HEMTT): кабина, рама, колёса. Длина 3 блока при масштабе 1."""
|
||
p = [el((-6, 5, -16), (22, 9, 32), "nato_green"),
|
||
el((-6, 9, -15), (22, 24, -1), "nato_green"),
|
||
el((-5, 16, -16), (21, 23, -15), "glass"),
|
||
el((-6.2, 16, -13), (-6, 23, -4), "glass"), el((22, 16, -13), (22.2, 23, -4), "glass"),
|
||
el((-6, 9, 2), (22, 15, 16), "nato_green"),
|
||
el((-2, 24, -12), (18, 25, -4), "dark_metal"),
|
||
el((2, 9, 20), (14, 13, 30), "metal")]
|
||
for z in (-12, 14, 23):
|
||
p += wheel(-9, -5.5, z + 4)
|
||
return p
|
||
|
||
|
||
def patriot_trailer():
|
||
p = [el((-7, 6, -16), (23, 11, 32), "nato_green"),
|
||
el((-1, 11, 14), (17, 16, 30), "metal"),
|
||
el((-8, 3, -16), (24, 7, -12), "dark_metal")]
|
||
for z in (14, 23):
|
||
p += wheel(-10, -6.5, z + 4)
|
||
p += both(el((-9, 0, -6), (-7, 6, -4), "metal")) # опоры
|
||
return p
|
||
|
||
|
||
def patriot_radar():
|
||
"""РЛС AN/MPQ-65: фазированная решётка на прицепе."""
|
||
p = [el((-6, 6, -10), (22, 10, 26), "nato_green"),
|
||
el((-4, 10, -6), (20, 26, 8), "nato_green"),
|
||
el((-5, 12, -7), (21, 30, -6), "radar_face"),
|
||
el((-2, 10, 10), (18, 18, 24), "metal")]
|
||
for z in (14,):
|
||
p += wheel(-9, -5.5, z + 4)
|
||
return p
|
||
|
||
|
||
def gepard_hull():
|
||
p = [el((-10, 4, -12), (26, 17, 30), "flecktarn"),
|
||
el((-10, 4, -16), (26, 12, -12), "flecktarn"),
|
||
el((-8, 17, 18), (24, 18, 30), "dark_metal"),
|
||
el((-6, 17, -10), (4, 18, -4), "metal")]
|
||
p += both(el((-14, 0, -14), (-10, 10, 31), "track"))
|
||
p += both(el((-14.5, 6, -14), (-10, 11, 31), "flecktarn")) # надгусеничные полки
|
||
return p
|
||
|
||
|
||
def gepard_turret():
|
||
p = [el((-4, 0, -6), (20, 12, 16), "flecktarn"),
|
||
el((-2, 12, -2), (18, 14, 14), "flecktarn"),
|
||
el((3, 4, -10), (13, 12, -6), "radar_face"), # РЛС сопровождения
|
||
el((7, 14, 10), (9, 18, 12), "metal")]
|
||
return p
|
||
|
||
|
||
def gepard_guns():
|
||
"""Две 35-мм пушки по бортам башни; ось качания — центр модели."""
|
||
p = both(el((-8, 5, -16), (-5.5, 7.5, 12), "barrel"))
|
||
p += both(el((-9, 3.5, 2), (-4, 9, 14), "flecktarn"))
|
||
p += both(el((-8.5, 4.5, -16), (-5, 8, -13), "dark_metal")) # дульные тормоза
|
||
return p
|
||
|
||
|
||
def gepard_dish():
|
||
"""Обзорная РЛС: вращается вокруг вертикальной оси."""
|
||
return [el((-4, 6, 6), (20, 14, 8), "dish"), el((7, 0, 6), (9, 8, 8), "metal")]
|
||
|
||
|
||
def target_marker():
|
||
return [el((7, -8, 7), (9, 24, 9), "target"),
|
||
el((2, 7.5, 2), (14, 8.5, 14), "target"),
|
||
el((5, 3, 5), (11, 13, 11), "target")]
|
||
|
||
|
||
def fireball():
|
||
return [el((2, 2, 2), (14, 14, 14), "fire"), el((0, 4, 4), (16, 12, 12), "fire"),
|
||
el((4, 0, 4), (12, 16, 12), "fire"), el((4, 4, 0), (12, 12, 16), "fire")]
|
||
|
||
|
||
def flash():
|
||
return [el((7, 7, -4), (9, 9, 20), "white"), el((-4, 7, 7), (20, 9, 9), "white"),
|
||
el((7, -4, 7), (9, 20, 9), "white"), el((5, 5, 5), (11, 11, 11), "flame")]
|
||
|
||
|
||
def debris():
|
||
return [el((5, 6, 3), (11, 9, 13), "dark_metal"), el((2, 7, 6), (14, 8, 10), "shahed")]
|
||
|
||
|
||
def lancet():
|
||
p = taper("lancet", -16, -10, 2, 3.5, 2, 3.5, 3)
|
||
p += [el((6.2, 6.2, -10), (9.8, 9.8, 20), "lancet"),
|
||
el((6.8, 4.8, -9), (9.2, 6.2, -5), "intake")] # оптика
|
||
for z0, z1, half in ((-5, 1, 13), (12, 18, 11)): # Х-образные крылья
|
||
for ang in (45, -45):
|
||
p.append(el((8 - half, 7.7, z0), (8 + half, 8.3, z1), "lancet", rot=("z", ang)))
|
||
p += [el((6.8, 6.8, 20), (9.2, 9.2, 22), "dark_metal")]
|
||
return p
|
||
|
||
|
||
def gerbera():
|
||
p = taper("foam", -15, -10, 2.5, 4.5, 2.5, 4.5, 3)
|
||
p += [el((5.8, 5.8, -10), (10.2, 10.2, 20), "foam")]
|
||
p += delta("foam", 0, 18, 22, 17, 7, 7.3, 8.5)
|
||
p += both(el((-9.6, 6.5, 15), (-8.6, 13, 22), "foam"))
|
||
p += [el((6.5, 6.5, 20), (9.5, 9.5, 24), "dark_metal")]
|
||
return p
|
||
|
||
|
||
def kh101():
|
||
p = taper("kh101", -16, -9, 2, 6, 2, 4.5, 4, y=7.5)
|
||
p += [el((5, 5.5, -9), (11, 8, 30), "kh101", {"down": "kh101_belly"}),
|
||
el((6, 8, -6), (10, 10.5, 28), "kh101")]
|
||
p += [el((-12, 7.1, 4), (28, 7.7, 10), "kh101")] # крылья
|
||
p += [el((7.6, 10.5, 24), (8.4, 15, 30), "kh101")] # киль
|
||
p += both(el((1, 7.2, 25), (6, 7.8, 30), "kh101"))
|
||
p += [el((6, 2, 15), (10, 5.5, 28), "dark_metal"), # двигатель под хвостом
|
||
el((6.5, 2.5, 14), (9.5, 5, 15), "intake"),
|
||
el((6.5, 2.5, 28), (9.5, 5, 29.5), "nozzle")]
|
||
return p
|
||
|
||
|
||
def wheels(zs, x0=-9, x1=-5.5, tex="tire"):
|
||
out = []
|
||
for z in zs:
|
||
out += both(el((x0, 0, z), (x1, 9, z + 7), tex))
|
||
return out
|
||
|
||
|
||
def pantsir_truck():
|
||
p = [el((-6, 5, -16), (22, 9, 32), "ru_green"),
|
||
el((-6, 9, -16), (22, 21, -6), "ru_green", {"north": "glass"}),
|
||
el((-5, 13, -16), (21, 20, -15.9), "glass"),
|
||
el((-5, 9, -5), (21, 18, 6), "ru_green"),
|
||
el((-4, 9, 8), (20, 12, 30), "dark_metal"),
|
||
el((-6.5, 3, 26), (22.5, 6, 32), "dark_metal")]
|
||
return p + wheels((-13, -4, 16, 24))
|
||
|
||
|
||
def pantsir_turret():
|
||
return [el((-2, 0, -6), (18, 10, 16), "ru_green"),
|
||
el((1, 1, -11), (15, 11, -6), "radar_face"),
|
||
el((7, 10, 4), (9, 17, 6), "metal"),
|
||
el((0, 10, 8), (16, 12, 16), "ru_green")]
|
||
|
||
|
||
def pantsir_pack():
|
||
"""Пакеты ракет 57Э6 по бортам и спаренные 30-мм пушки над ними; ось качания — центр."""
|
||
p = both(el((-11, 2, -16), (-3, 12, 12), "ru_green", {"north": "tubes6"}))
|
||
p += both(el((-9.5, 12.5, -16), (-8, 14, 6), "barrel"))
|
||
p += both(el((-6.5, 12.5, -16), (-5, 14, 6), "barrel"))
|
||
p += both(el((-10, 12, 2), (-4, 15, 10), "dark_metal"))
|
||
return p
|
||
|
||
|
||
def flat_radar(tex="radar_face"):
|
||
"""Плоская вращающаяся антенна (ось — вертикаль через центр модели)."""
|
||
return [el((-6, 2, 7), (22, 16, 9), tex), el((-6.5, 1.5, 9), (22.5, 16.5, 10), "dark_metal"),
|
||
el((7, -8, 8), (9, 2, 10), "metal")]
|
||
|
||
|
||
def maz_chassis(tex):
|
||
"""8×8 шасси МАЗ-543/БАЗ с раздельными кабинами; рама под пусковой контейнер."""
|
||
p = [el((-6, 5, -16), (22, 10, 32), tex),
|
||
el((-6, 10, -16), (3, 19, -7), tex, {"north": "glass"}),
|
||
el((13, 10, -16), (22, 19, -7), tex, {"north": "glass"}),
|
||
el((3, 10, -16), (13, 15, -6), "dark_metal"),
|
||
el((-5, 10, -6), (21, 13, 4), tex),
|
||
el((-3, 10, 26), (19, 14, 32), "dark_metal")]
|
||
p += both(el((-8, 0, 28), (-6, 6, 31), "metal")) # опоры
|
||
return p + wheels((-14, -5, 13, 22))
|
||
|
||
|
||
def tubes(n_wide, n_high, size, tex="tube"):
|
||
"""Пакет ТПК: n_wide × n_high труб сечением size, от z=-16 до 32. Ось подъёма — задняя нижняя кромка."""
|
||
p = []
|
||
w = n_wide * size + (n_wide - 1)
|
||
x0 = 8 - w / 2
|
||
for i in range(n_wide):
|
||
for j in range(n_high):
|
||
x = x0 + i * (size + 1)
|
||
y = j * (size + 1)
|
||
p.append(el((x, y, -16), (x + size, y + size, 32), tex, {"north": "tube_cap", "south": "tube_cap"}))
|
||
p += [el((x0 - 1, 0, 26), (x0 + w + 1, n_high * (size + 1) - 1, 28), "dark_metal"),
|
||
el((x0 - 1, 0, -2), (x0 + w + 1, n_high * (size + 1) - 1, 0), "dark_metal")]
|
||
return p
|
||
|
||
|
||
def radar_trailer():
|
||
p = [el((-6, 5, -12), (22, 10, 26), "ru_khaki"),
|
||
el((2, 10, 2), (14, 16, 14), "dark_metal"),
|
||
el((-5, 10, 16), (21, 18, 26), "ru_khaki")]
|
||
return p + wheels((-9, 17))
|
||
|
||
|
||
def big_radar_panel():
|
||
return [el((-10, 0, 7), (26, 26, 9), "radar_big"), el((-10.5, -0.5, 9), (26.5, 26.5, 10), "dark_metal"),
|
||
el((6, -6, 8), (10, 0, 12), "metal")]
|
||
|
||
|
||
def missile_57e6():
|
||
p = taper("missile_ru", -16, -12, 0.5, 2, 0.5, 2, 2)
|
||
p += [el((7, 7, -12), (9, 9, 4), "missile_ru"), el((6.4, 6.4, 4), (9.6, 9.6, 28), "booster")]
|
||
p += both(el((3.5, 7.7, 22), (6.4, 8.3, 28), "booster"))
|
||
p += [el((7.7, 3.5, 22), (8.3, 6.4, 28), "booster"), el((7.7, 9.6, 22), (8.3, 12.5, 28), "booster")]
|
||
return p
|
||
|
||
|
||
def missile_48n6():
|
||
p = taper("missile_ru", -16, -11, 1, 3.5, 1, 3.5, 3)
|
||
p += [el((6.25, 6.25, -11), (9.75, 9.75, 28), "missile_ru"),
|
||
el((6.5, 6.5, 28), (9.5, 9.5, 30), "nozzle")]
|
||
p += both(el((1.5, 7.7, 20), (6.25, 8.3, 28), "missile_ru"))
|
||
p += [el((7.7, 1.5, 20), (8.3, 6.25, 28), "missile_ru"), el((7.7, 9.75, 20), (8.3, 14.5, 28), "missile_ru")]
|
||
return p
|
||
|
||
|
||
def missile_77n6():
|
||
p = taper("missile_grey", -16, -10, 1, 4, 1, 4, 3)
|
||
p += [el((6, 6, -10), (10, 10, 30), "missile_grey"),
|
||
el((6, 6, 4), (10, 10, 5), "booster"),
|
||
el((6.5, 6.5, 30), (9.5, 9.5, 32), "nozzle")]
|
||
p += both(el((3, 7.7, 24), (6, 8.3, 30), "missile_grey"))
|
||
p += [el((7.7, 3, 24), (8.3, 6, 30), "missile_grey"), el((7.7, 10, 24), (8.3, 13, 30), "missile_grey")]
|
||
return p
|
||
|
||
|
||
def kalibr():
|
||
p = taper("missile_grey", -16, -11, 1.5, 4.5, 1.5, 4.5, 3)
|
||
p += [el((5.75, 5.75, -11), (10.25, 10.25, 30), "missile_grey"),
|
||
el((-6, 7.6, 6), (22, 8.4, 11), "missile_grey"),
|
||
el((7.6, 10.25, 25), (8.4, 14, 30), "missile_grey"),
|
||
el((6.5, 3, 18), (9.5, 5.75, 26), "dark_metal")]
|
||
p += both(el((2, 7.6, 26), (5.75, 8.4, 30), "missile_grey"))
|
||
return p
|
||
|
||
|
||
def kh22():
|
||
"""Х-22/Х-32: толстое тело, треугольные крылья, киль сверху и снизу."""
|
||
p = taper("missile_ru", -16, -8, 1.5, 6, 1.5, 6, 4)
|
||
p += [el((5, 5, -8), (11, 11, 30), "missile_ru"),
|
||
el((7.5, 11, 14), (8.5, 17, 30), "missile_ru"),
|
||
el((7.5, -1, 18), (8.5, 5, 30), "missile_ru"),
|
||
el((5.5, 5.5, 30), (10.5, 10.5, 32), "nozzle")]
|
||
p += delta("missile_ru", 4, 22, 28, 12, 5, 7.6, 8.4)
|
||
return p
|
||
|
||
|
||
def zircon():
|
||
p = taper("dark_metal", -16, -6, 1, 5, 1, 4, 4)
|
||
p += [el((5.5, 6, -6), (10.5, 10, 30), "kh101"),
|
||
el((6, 3.5, 0), (10, 6, 24), "intake"),
|
||
el((5.5, 6, 30), (10.5, 10, 32), "nozzle")]
|
||
p += both(el((1, 7.6, 22), (5.5, 8.4, 30), "kh101"))
|
||
p += [el((7.6, 10, 22), (8.4, 14, 30), "kh101")]
|
||
return p
|
||
|
||
|
||
def iskander():
|
||
"""9М723: толстая ракета с конической головной частью и решётчатыми рулями."""
|
||
p = taper("ru_khaki", -16, -6, 1.5, 7, 1.5, 7, 5)
|
||
p += [el((4.5, 4.5, -6), (11.5, 11.5, 30), "ru_khaki"),
|
||
el((4.4, 4.4, 4), (11.6, 11.6, 5), "dark_metal"),
|
||
el((5, 5, 30), (11, 11, 32), "nozzle")]
|
||
p += both(el((1, 7.5, 24), (4.5, 8.5, 30), "ru_khaki"))
|
||
p += [el((7.5, 1, 24), (8.5, 4.5, 30), "ru_khaki"), el((7.5, 11.5, 24), (8.5, 15, 30), "ru_khaki")]
|
||
return p
|
||
|
||
|
||
def kinzhal():
|
||
p = taper("missile_grey", -16, -8, 1.5, 6.5, 1.5, 6.5, 4)
|
||
p += [el((4.75, 4.75, -8), (11.25, 11.25, 30), "dark_metal"),
|
||
el((4.6, 4.6, -4), (11.4, 11.4, -3), "missile_grey"),
|
||
el((5.5, 5.5, 30), (10.5, 10.5, 32), "nozzle")]
|
||
p += both(el((0, 7.6, 22), (4.75, 8.4, 30), "dark_metal"))
|
||
p += [el((7.6, 0, 22), (8.4, 4.75, 30), "dark_metal"), el((7.6, 11.25, 22), (8.4, 16, 30), "dark_metal")]
|
||
return p
|
||
|
||
|
||
def tracked(tex):
|
||
"""Гусеничное шасси (Бук, Тор): корпус, гусеницы и надгусеничные полки."""
|
||
p = [el((-10, 4, -14), (26, 15, 32), tex),
|
||
el((-10, 4, -16), (26, 10, -14), tex),
|
||
el((-8, 15, 22), (24, 16, 32), "dark_metal")]
|
||
p += both(el((-14, 0, -15), (-10, 9, 31), "track"))
|
||
p += both(el((-14.5, 5, -15), (-10, 10, 31), tex))
|
||
return p
|
||
|
||
|
||
def buk_turret():
|
||
return [el((-4, 0, -4), (20, 9, 20), "ru_green"),
|
||
el((1, 2, -10), (15, 14, -4), "radar_face"), # РЛС подсвета на башне
|
||
el((-2, 9, 6), (18, 11, 18), "ru_green")]
|
||
|
||
|
||
def buk_rails():
|
||
"""Четыре ракеты 9М317 на направляющих; ось качания — центр."""
|
||
p = []
|
||
for x in (-9, -3, 13, 19):
|
||
p += [el((x - 1.5, 4, -16), (x + 1.5, 7, 26), "missile_ru"),
|
||
el((x - 1.8, 3.5, 20), (x + 1.8, 7.5, 27), "booster"),
|
||
el((x - 0.5, 2, -6), (x + 0.5, 4, 16), "dark_metal")]
|
||
p += [el((-12, 0, -2), (28, 2, 14), "ru_green")]
|
||
return p
|
||
|
||
|
||
def tor_turret():
|
||
"""Башня Тора: блок вертикального пуска (8 ячеек сверху) и РЛС спереди; вращается целиком."""
|
||
p = [el((-4, 0, -6), (20, 16, 22), "ru_khaki"),
|
||
el((-2, 2, -11), (18, 15, -6), "radar_face"),
|
||
el((-3, 16, 2), (19, 17, 20), "dark_metal")]
|
||
for i in range(4):
|
||
for j in range(2):
|
||
x, z = -1 + i * 5, 4 + j * 8
|
||
p.append(el((x, 16.5, z), (x + 4, 17.5, z + 6), "tube_cap"))
|
||
return p
|
||
|
||
|
||
def nasams_trailer():
|
||
p = [el((-6, 5, -10), (22, 9, 26), "nato_green"),
|
||
el((-4, 9, 10), (20, 13, 24), "dark_metal"),
|
||
el((6, 3, -16), (10, 6, -10), "metal")]
|
||
return p + wheels((4, 14))
|
||
|
||
|
||
def p1sun():
|
||
"""Дрон-перехватчик: короткий корпус, X-образные лучи с моторами; нос — север."""
|
||
p = [el((6.5, 6.5, -10), (9.5, 9.5, 10), "dark_metal"),
|
||
el((7, 7, -14), (9, 9, -10), "missile")]
|
||
for dx, dy in ((-1, -1), (-1, 1), (1, -1), (1, 1)):
|
||
x, y = 8 + dx * 6, 8 + dy * 6
|
||
p += [el((min(8, x) , min(8, y), 0), (max(8, x), max(8, y), 1.5), "dark_metal", rot=None),
|
||
el((x - 2.5, y - 2.5, 0), (x + 2.5, y + 2.5, 0.6), "prop")]
|
||
return p
|
||
|
||
|
||
def radar_truck():
|
||
return maz_chassis("ru_khaki")
|
||
|
||
|
||
def depot():
|
||
"""Склад боеприпасов: бетонная площадка, штабели ящиков и контейнеров ракет."""
|
||
p = [el((-14, 0, -14), (30, 2, 30), "concrete")]
|
||
for x, z in ((-12, -12), (-12, 4), (4, -12)):
|
||
p.append(el((x, 2, z), (x + 12, 10, z + 12), "crate"))
|
||
for k in range(3):
|
||
p.append(el((6, 2 + k * 5, 6), (28, 7 + k * 5, 10), "tube", {"north": "tube_cap", "south": "tube_cap"}))
|
||
return p
|
||
|
||
|
||
def tzm_truck():
|
||
"""Транспортно-заряжающая машина: кабина, кузов с ракетами и кран."""
|
||
p = [el((-6, 5, -16), (22, 9, 32), "ru_green"),
|
||
el((-6, 9, -16), (22, 20, -6), "ru_green", {"north": "glass"}),
|
||
el((-4, 9, -2), (20, 12, 30), "dark_metal")]
|
||
for x in (-3, 5, 13):
|
||
p.append(el((x, 12, -1), (x + 6, 18, 29), "tube", {"north": "tube_cap", "south": "tube_cap"}))
|
||
p += [el((18, 12, -4), (21, 26, -1), "metal"), el((6, 24, -4), (21, 26, -1), "metal")]
|
||
return p + wheels((-12, -3, 16, 24))
|
||
|
||
|
||
def ew_truck():
|
||
"""Станция РЭБ: кузов-фургон, мачта с антенными решётками."""
|
||
p = [el((-6, 5, -16), (22, 9, 32), "ru_khaki"),
|
||
el((-6, 9, -16), (22, 19, -6), "ru_khaki", {"north": "glass"}),
|
||
el((-5, 9, -4), (21, 22, 30), "ru_khaki"),
|
||
el((7, 22, 10), (9, 32, 12), "antenna")]
|
||
for y in (24, 28):
|
||
p += both(el((-4, y, 9), (7, y + 3, 13), "antenna"))
|
||
return p + wheels((-12, -3, 16, 24))
|
||
|
||
|
||
def searchlight():
|
||
"""Прожектор на тумбе: тумба и голова отдельно (голова — `searchlight_head`)."""
|
||
return [el((2, 0, 2), (14, 3, 14), "dark_metal"), el((6, 3, 6), (10, 12, 10), "dark_metal")]
|
||
|
||
|
||
def searchlight_head():
|
||
"""Голова прожектора: нос (стекло) смотрит на север; ось — центр."""
|
||
return [el((3, 3, -2), (13, 13, 14), "dark_metal"), el((3.5, 3.5, -2.5), (12.5, 12.5, -2), "lamp")]
|
||
|
||
|
||
def beam():
|
||
"""Луч прожектора: длинная светящаяся трубка вдоль z, от z=32 к -16 (масштабируется по длине)."""
|
||
return [el((6.5, 6.5, -16), (9.5, 9.5, 32), "beam")]
|
||
|
||
|
||
def flare():
|
||
return [el((6.5, 6.5, 6.5), (9.5, 9.5, 9.5), "lamp"), el((7.6, 9.5, 7.6), (8.4, 16, 8.4), "antenna")]
|
||
|
||
|
||
# ---------------------------------------------------------------- обновлённая геометрия
|
||
def B(x0, y0, z0, x1, y1, z1, tex, faces=None, rot=None):
|
||
"""Элемент с x относительно центра модели."""
|
||
return el((8 + x0, y0, z0), (8 + x1, y1, z1), tex, faces, rot)
|
||
|
||
|
||
def wheel(x0, x1, zc, r=4.6, tex="tire", hub="hub"):
|
||
"""Колесо-восьмиугольник (квадрат + квадрат под 45 градусов) с диском на боковой грани, обе стороны.
|
||
x0..x1 — абсолютные координаты левого колеса; ось колеса лежит на y = r."""
|
||
s = r * 1.8478
|
||
faces = {"east": hub, "west": hub}
|
||
a = el((x0, r - s / 2, zc - s / 2), (x1, r + s / 2, zc + s / 2), tex, faces)
|
||
b = el((x0 + 0.03, r - s / 2, zc - s / 2), (x1 - 0.03, r + s / 2, zc + s / 2), tex, faces, rot=("x", 45))
|
||
return both(a) + both(b)
|
||
|
||
|
||
def wheels(zs, x0=-9, x1=-5.5, tex="tire"):
|
||
out = []
|
||
for z in zs:
|
||
out += wheel(x0, x1, z + 3.5, 4.5, tex)
|
||
return out
|
||
|
||
|
||
def tracked(tex):
|
||
"""Гусеничное шасси (Бук, Тор): корпус, скошенный лоб, гусеницы с катками, решётки МТО, люки."""
|
||
p = [el((-10, 4, -14), (26, 15, 32), tex),
|
||
el((-10, 4, -16), (26, 10, -14), tex),
|
||
el((-9, 10, -15), (25, 14, -12), tex, rot=("x", -22.5)), # лобовой лист
|
||
el((-8, 15, 22), (24, 16, 32), "dark_metal"),
|
||
el((-5, 15.2, 24), (21, 15.8, 31), "bare_nozzle"), # решётка МТО
|
||
el((-1, 15, -10), (9, 16, -4), "dark_metal")]
|
||
p += both(el((-14, 0, -15), (-10, 9, 31), "track", {"west": "track_side", "east": "track_side"}))
|
||
p += both(el((-14.5, 5, -15), (-10, 10, 31), tex))
|
||
p += both(el((-4, 11, -16), (-1, 13, -15), "bare_lamp"))
|
||
return p
|
||
|
||
|
||
def oct_z(z0, z1, r, tex, x=0.0, y=8.0):
|
||
"""Восьмигранная «труба» вдоль z (квадрат + квадрат под 45 градусов): корпус ракеты."""
|
||
s = r * 1.8478
|
||
f = {"up": tex + "_t", "down": tex + "_t"} if tex in RINGED else None
|
||
return [B(x - s / 2, y - s / 2, z0, x + s / 2, y + s / 2, z1, tex, f),
|
||
B(x - s / 2, y - s / 2, z0 + 0.03, x + s / 2, y + s / 2, z1 - 0.03, tex, f, rot=("z", 45))]
|
||
|
||
|
||
def ogive(z_tip, z_base, rb, tex, steps=4):
|
||
out = []
|
||
for i in range(steps):
|
||
a = z_tip + (z_base - z_tip) * i / steps
|
||
b = z_tip + (z_base - z_tip) * (i + 1) / steps
|
||
t = (i + 0.7) / steps
|
||
out += oct_z(a, b, max(0.45, rb * min(1.0, t ** 0.55)), tex)
|
||
return out
|
||
|
||
|
||
def fins4(z0, z1, r, span, tex, sweep=3.0, th=0.7, y=8.0):
|
||
"""Четыре стреловидных оперения крестом: корневая и концевая части."""
|
||
m, k = r * 0.9, span * 0.55
|
||
p = both(B(m, y - th / 2, z0, m + k, y + th / 2, z1, tex))
|
||
p += both(B(m + k, y - th / 2, z0 + sweep, m + span, y + th / 2, z1, tex))
|
||
for sg in (1, -1):
|
||
lo, hi = (y + m, y + m + k) if sg > 0 else (y - m - k, y - m)
|
||
p.append(B(-th / 2, lo, z0, th / 2, hi, z1, tex))
|
||
lo, hi = (y + m + k, y + m + span) if sg > 0 else (y - m - span, y - m - k)
|
||
p.append(B(-th / 2, lo, z0 + sweep, th / 2, hi, z1, tex))
|
||
return p
|
||
|
||
|
||
def nozzle(z, r, y=8.0):
|
||
"""Сопло с раскалённым срезом (glow_ember — светящийся материал)."""
|
||
return [B(-r * .8, y - r * .8, z, r * .8, y + r * .8, z + 1.6, "nozzle"),
|
||
B(-r * .55, y - r * .55, z + 1.6, r * .55, y + r * .55, z + 1.9, "glow_ember")]
|
||
|
||
|
||
def pac3():
|
||
p = ogive(-16, -9, 1.7, "nose_dark", 3) + oct_z(-9, 24, 1.7, "rm_pac3")
|
||
p += oct_z(-8, -7, 2.2, "dark_metal") # кольцо двигателей ориентации
|
||
p += fins4(18, 24, 1.7, 5, "missile", 3.5) + nozzle(24, 1.7)
|
||
return p
|
||
|
||
|
||
def irist_missile():
|
||
p = ogive(-16, -9, 1.7, "white", 3) + oct_z(-9, 22, 1.7, "rm_white")
|
||
p += fins4(-8, -5.5, 1.7, 3.6, "missile_grey", 1.0) + fins4(16, 22, 1.7, 5, "white", 3.5) + nozzle(22, 1.7)
|
||
return p
|
||
|
||
|
||
def amraam_missile():
|
||
p = ogive(-16, -9, 1.7, "nose_dark", 3) + oct_z(-9, 24, 1.7, "rm_white")
|
||
p += fins4(2, 8, 1.7, 5.5, "white", 3.5) + fins4(19, 24, 1.7, 3.8, "white", 2.5) + nozzle(24, 1.7)
|
||
return p
|
||
|
||
|
||
def missile_57e6():
|
||
p = ogive(-16, -9, 1.2, "missile_ru", 3) + oct_z(-9, 4, 1.2, "missile_ru") + oct_z(4, 25, 1.7, "rm_ru")
|
||
p += fins4(18, 25, 1.7, 5, "booster", 3.0) + nozzle(25, 1.7)
|
||
return p
|
||
|
||
|
||
def missile_48n6():
|
||
p = ogive(-16, -9, 1.9, "missile_ru", 3) + oct_z(-9, 26, 1.9, "rm_ru")
|
||
p += fins4(18, 26, 1.9, 6.2, "missile_ru", 4.0) + fins4(-6, -3, 1.9, 2.5, "missile_ru", 1.0) + nozzle(26, 1.9)
|
||
return p
|
||
|
||
|
||
def missile_77n6():
|
||
p = ogive(-16, -8, 2.2, "missile_grey", 4) + oct_z(-8, 28, 2.2, "rm_grey")
|
||
p += oct_z(4, 6, 2.4, "booster") + fins4(22, 28, 2.2, 5.2, "missile_grey", 3.0) + nozzle(28, 2.2)
|
||
return p
|
||
|
||
|
||
def kalibr():
|
||
p = ogive(-16, -9, 2.2, "missile_grey", 4) + oct_z(-9, 28, 2.2, "rm_grey")
|
||
p += [B(-14, 7.6, 6, 14, 8.4, 11, "missile_grey"), B(-0.8, 3.2, 17, 0.8, 5.4, 26, "dark_metal")]
|
||
p += fins4(23, 28, 2.2, 4.5, "missile_grey", 2.5) + nozzle(28, 2.0)
|
||
return p
|
||
|
||
|
||
def iskander():
|
||
p = ogive(-16, -5, 3.6, "rm_khaki", 6) + oct_z(-5, 28, 3.6, "rm_khaki")
|
||
p += oct_z(3, 4, 3.8, "dark_metal") + fins4(23, 28, 3.6, 6.5, "ru_khaki", 2.0, 0.9) + nozzle(28, 3.4)
|
||
return p
|
||
|
||
|
||
def kinzhal():
|
||
p = ogive(-16, -6, 3.4, "rm_dark", 5) + oct_z(-6, 28, 3.4, "rm_dark")
|
||
p += fins4(20, 28, 3.4, 8.0, "dark_metal", 4.0, 0.9) + nozzle(28, 3.0)
|
||
return p
|
||
|
||
|
||
def zircon():
|
||
p = ogive(-16, -6, 2.6, "rm_dark", 4) + oct_z(-6, 28, 2.6, "rm_dark")
|
||
p += [B(-1.8, 3.4, 0, 1.8, 5.2, 24, "intake")]
|
||
p += fins4(22, 28, 2.6, 5.5, "dark_metal", 3.0) + nozzle(28, 2.4)
|
||
return p
|
||
|
||
|
||
|
||
# ---------------------------------------------------------------- часть 2: беспилотники, Х-101/Х-22, детали техники
|
||
def delta2(tex, under, z_root, z_tip, z_trail, half, steps, y0, y1):
|
||
"""Дельта-крыло со своей нижней обшивкой."""
|
||
out = []
|
||
for i in range(steps):
|
||
za = z_root + (z_tip - z_root) * i / steps
|
||
sp = half * (i + 1) / steps
|
||
out.append(B(-sp, y0, za, sp, y1, z_trail, tex, {"down": under}))
|
||
return out
|
||
|
||
|
||
def decal(tex, y0, y1, z0, z1, x, w=0.12):
|
||
"""Маркировка на борту (тонкая плита, обе стороны), x — внешняя грань корпуса от центра."""
|
||
return both(B(x, y0, z0, x + w, y1, z1, tex))
|
||
|
||
|
||
def shahed():
|
||
p = ogive(-16, -9, 2.6, "shahed_nose", 3) + oct_z(-9, 22, 2.6, "shahed")
|
||
p += delta2("shahed", "kh101_belly", -2, 21, 26, 21, 9, 7.2, 8.4)
|
||
p += both(B(-20.8, 5.5, 17, -19.8, 13.5, 26, "shahed")) # шайбы-кили на законцовках
|
||
p += oct_z(22, 28, 1.9, "dark_metal") + both(B(0.8, 5.2, 24, 1.6, 6.8, 28, "bare_nozzle"))
|
||
p += [B(-1.5, 10.5, -4, 1.5, 12, 8, "dark_metal"), # люк антенны ГНСС
|
||
B(-0.3, 12, 2, 0.3, 14.5, 3, "bare_antenna"),
|
||
B(-1.2, 4.8, -12, 1.2, 5.6, -6, "bare_lamp")] # оптика под носом
|
||
return p
|
||
|
||
|
||
def geran3():
|
||
p = ogive(-16, -9, 2.8, "geran", 3) + oct_z(-9, 26, 2.8, "geran")
|
||
p += delta2("geran", "geran", 0, 20, 26, 18, 8, 7.2, 8.4)
|
||
p += both(B(-10.6, 6.5, 18, -9.6, 13, 26, "geran"))
|
||
p += oct_z(6, 28, 2.3, "geran_engine", 0, 14) # турбореактивный двигатель на пилоне
|
||
p += [B(-1.2, 10.5, 8, 1.2, 12, 22, "geran"), B(-2, 12, 4, 2, 16, 6, "bare_nozzle"),
|
||
B(-1.7, 12.3, 28, 1.7, 15.7, 29.5, "bare_nozzle"), B(-1.2, 12.8, 29.5, 1.2, 15.2, 29.9, "glow_ember"),
|
||
B(-0.3, 10.8, -6, 0.3, 12.8, -5, "bare_antenna")]
|
||
return p
|
||
|
||
|
||
def lancet():
|
||
p = ogive(-16, -10, 2.0, "lancet", 3) + oct_z(-10, 20, 2.0, "lancet")
|
||
p += [B(-1.1, 6.8, -16.4 + 0.4, 1.1, 8.8, -15.6, "bare_glass_dark")] if False else []
|
||
p += [B(-1.2, 5.2, -14, 1.2, 6.4, -9, "bare_lamp")] # оптоэлектронная головка
|
||
for z0, z1, half in ((-5, 1, 13), (12, 18, 11)): # Х-образные крылья
|
||
for ang in (45, -45):
|
||
p.append(B(-half, 7.7, z0, half, 8.3, z1, "lancet", rot=("z", ang)))
|
||
p += oct_z(20, 24, 1.5, "dark_metal")
|
||
p += [B(-5, 7.85, 24, 5, 8.15, 24.5, "prop"), B(-0.15, 3, 24, 0.15, 13, 24.5, "prop")]
|
||
return p
|
||
|
||
|
||
def gerbera():
|
||
p = ogive(-15, -10, 2.3, "foam", 3) + oct_z(-10, 20, 2.3, "foam")
|
||
p += delta2("foam", "foam", 0, 18, 22, 17, 7, 7.3, 8.5)
|
||
p += both(B(-9.6, 6.5, 15, -8.6, 13, 22, "foam"))
|
||
p += [B(-1.5, 10, -6, 1.5, 11.6, 4, "dark_metal"), B(-1.4, 5.0, -14, 1.4, 6.2, -9, "bare_lamp")]
|
||
p += oct_z(20, 24, 1.6, "dark_metal") + [B(-0.2, 3, 24, 0.2, 13, 24.5, "prop"), B(-5, 7.8, 24, 5, 8.2, 24.5, "prop")]
|
||
return p
|
||
|
||
|
||
def kh101():
|
||
p = ogive(-16, -8, 2.9, "kh101", 4, ) + oct_z(-8, 27, 2.9, "kh101")
|
||
p = [dict(e, **{"from": [e["from"][0], e["from"][1] - 0.5, e["from"][2]], "to": [e["to"][0], e["to"][1] - 0.5, e["to"][2]]}) for e in p]
|
||
p += both(B(2.5, 6.9, 2, 14, 7.6, 11, "kh101", {"down": "kh101_belly"})) # крылья: корень и конец
|
||
p += both(B(14, 6.9, 5, 20.5, 7.4, 11, "kh101", {"down": "kh101_belly"}))
|
||
p += [B(-0.4, 10, 20, 0.4, 16, 28, "kh101")] + both(B(1.5, 6.9, 22, 8, 7.4, 28, "kh101"))
|
||
p += [B(-1.9, 3.2, 12, 1.9, 5.4, 26, "dark_metal"), B(-1.6, 3.5, 11.4, 1.6, 5.1, 12, "bare_nozzle"),
|
||
B(-1.5, 3.6, 26, 1.5, 5, 28, "bare_nozzle"), B(-1.1, 3.9, 28, 1.1, 4.7, 28.4, "glow_ember"),
|
||
B(-1, 10, -2, 1, 10.6, 10, "dark_metal")]
|
||
return p
|
||
|
||
|
||
def kh22():
|
||
p = ogive(-16, -8, 3.0, "rm_ru", 4) + oct_z(-8, 28, 3.0, "rm_ru")
|
||
p += both(B(3, 7.6, 4, 14, 8.4, 16, "missile_ru")) + both(B(14, 7.6, 8, 18, 8.2, 16, "missile_ru"))
|
||
p += fins4(21, 28, 3.0, 6.5, "missile_ru", 4.0)
|
||
p += [B(-0.5, 11, 14, 0.5, 17, 28, "missile_ru"), B(-0.5, -1, 16, 0.5, 5, 28, "missile_ru")]
|
||
p += nozzle(28, 2.6)
|
||
return p
|
||
|
||
|
||
def patriot_launcher():
|
||
"""Пакет 4 ТПК M903: четыре отдельных контейнера с торцами, ремни, рама. Центры труб — как у `Muzzle::Patriot`."""
|
||
p = []
|
||
for x0, x1 in ((-6, 7.8), (8.2, 22)):
|
||
for y0, y1 in ((1.5, 9.5), (10.5, 18.5)):
|
||
p.append(el((x0, y0, -14), (x1, y1, 30), "canister", {"north": "canister_front", "south": "dark_metal"}))
|
||
for z in (-6, 8, 20):
|
||
p.append(el((x0 - 0.2, y0 - 0.2, z), (x1 + 0.2, y1 + 0.2, z + 1.5), "dark_metal"))
|
||
p += [el((7.5, 1, -14), (8.5, 19, 30), "dark_metal"), el((-7, 0, 24), (23, 2, 30), "metal")]
|
||
p += both(el((-8, 2, -10), (-6, 17, -6), "metal")) + both(el((-8, 2, 20), (-6, 17, 24), "metal"))
|
||
return p
|
||
|
||
|
||
def _plus(fn, extra):
|
||
"""Обёртка: те же детали и ещё несколько."""
|
||
return lambda *a: fn(*a) + extra()
|
||
|
||
|
||
# кабины: фары, бампер, зеркала, выхлоп, бак, номера
|
||
_maz0 = maz_chassis
|
||
|
||
|
||
def maz_chassis(tex):
|
||
p = _maz0(tex)
|
||
p += both(B(-12.5, 11, -16, -10.5, 12.6, -15.6, "bare_lamp")) + both(B(-12.5, 14.5, -16, -10.5, 15.5, -15.6, "bare_lamp"))
|
||
p += [B(-9, 5.5, -16, 9, 7.5, -15, "dark_metal")] # бампер (z до -17 нельзя: ниже -16 отсекается)
|
||
p += both(B(-18.5, 7, 4, -15, 10, 14, "dark_metal")) # топливные баки
|
||
p += both(B(-14.5, 15, -9, -13.5, 22, -8, "bare_antenna")) # выхлопные трубы
|
||
p += decal("mk_ru", 11, 15, -15, -7, 14)
|
||
return p
|
||
|
||
|
||
_tr0 = tracked
|
||
|
||
|
||
def tracked(tex):
|
||
p = _tr0(tex)
|
||
p += decal("mk_ru2", 11, 14.5, 0, 8, 18)
|
||
p += both(B(-5, 16, 2, -1, 17.4, 8, "dark_metal")) + [B(-3, 16, -8, 3, 17, -4, "bare_lamp")]
|
||
return p
|
||
|
||
|
||
_pt0 = patriot_truck
|
||
|
||
|
||
def patriot_truck():
|
||
p = _pt0()
|
||
p += both(B(-14.2, 10, -12, -14, 14, -4, "glass_cab"))
|
||
p += decal("mk_us", 11, 15, -12, -4, 14)
|
||
p += both(B(-14, 7, 6, -17, 11, 14, "dark_metal"))
|
||
p += [B(-9, 5.5, -16, 9, 7.5, -15.2, "dark_metal")]
|
||
p += both(B(-12.5, 10.5, -16, -10.5, 12.2, -15.4, "bare_lamp"))
|
||
return p
|
||
|
||
|
||
_pz0 = pantsir_truck
|
||
|
||
|
||
def pantsir_truck():
|
||
p = _pz0()
|
||
p += decal("mk_star", 12, 16, -14, -6, 14)
|
||
p += both(B(-12.5, 10.5, -16, -10.5, 12.2, -15.4, "bare_lamp")) + both(B(-18.5, 7, 6, -15, 10, 14, "dark_metal"))
|
||
return p
|
||
|
||
|
||
_gt0 = gepard_turret
|
||
|
||
|
||
def gepard_turret():
|
||
p = _gt0()
|
||
p += decal("mk_de", 3, 6.5, -5.5, 1.5, 12)
|
||
p += [B(-2.5, 14, 2, 2.5, 14.8, 7, "metal"), B(-6, 14, 5, -5, 16, 6, "bare_antenna")]
|
||
p += both(B(-12.8, 7, -5, -12, 10, -2, "dark_metal")) + both(B(-12.8, 7, -1.5, -12, 10, 0.5, "dark_metal")) # дымовые гранатомёты
|
||
return p
|
||
|
||
|
||
_pu0 = pantsir_turret
|
||
|
||
|
||
def pantsir_turret():
|
||
p = _pu0()
|
||
p += [el((5, 10, -4), (11, 13, 0), "dark_metal"), el((6, 10.5, -4.4), (10, 12.5, -4), "bare_lamp"),
|
||
el((1, 12, 10), (15, 12.6, 15), "metal")]
|
||
p += decal("mk_ru", 3, 6.5, -4, 3, 10)
|
||
return p
|
||
|
||
|
||
_tz0 = tzm_truck
|
||
|
||
|
||
def tzm_truck():
|
||
p = _tz0()
|
||
p += decal("mk_ru2", 11, 15, -14, -7, 14) + both(B(-12.5, 10, -16, -10.5, 11.6, -15.4, "bare_lamp"))
|
||
return p
|
||
|
||
|
||
_ew0 = ew_truck
|
||
|
||
|
||
def ew_truck():
|
||
p = _ew0()
|
||
p += decal("mk_ru", 11, 15, -14, -7, 14) + both(B(-12.5, 10, -16, -10.5, 11.6, -15.4, "bare_lamp"))
|
||
p += [B(-2, 22, -4, 2, 23.5, 0, "dark_metal")]
|
||
return p
|
||
|
||
|
||
MODELS = {
|
||
"shahed": (shahed, "Шахед-136"),
|
||
"shahed_prop": (shahed_prop, "Винт"),
|
||
"geran3": (geran3, "Герань-3"),
|
||
"geran3_flame": (geran3_flame, "Факел"),
|
||
"pac3": (pac3, "PAC-3 MSE"),
|
||
"irist_missile": (irist_missile, "IRIS-T SLM"),
|
||
"amraam_missile": (amraam_missile, "AMRAAM"),
|
||
"flame": (flame, "Факел"),
|
||
"patriot_launcher": (patriot_launcher, "ПУ M903"),
|
||
"patriot_truck": (patriot_truck, "Тягач"),
|
||
"patriot_trailer": (patriot_trailer, "Полуприцеп"),
|
||
"patriot_radar": (patriot_radar, "РЛС AN/MPQ-65"),
|
||
"gepard_hull": (gepard_hull, "Корпус Гепарда"),
|
||
"gepard_turret": (gepard_turret, "Башня Гепарда"),
|
||
"gepard_guns": (gepard_guns, "Пушки Гепарда"),
|
||
"gepard_dish": (gepard_dish, "РЛС Гепарда"),
|
||
"target": (target_marker, "Цель"),
|
||
"fireball": (fireball, "Огненный шар"),
|
||
"flash": (flash, "Вспышка"),
|
||
"debris": (debris, "Обломки"),
|
||
"lancet": (lancet, "Ланцет-3"),
|
||
"gerbera": (gerbera, "Гербера"),
|
||
"kh101": (kh101, "Х-101"),
|
||
"pantsir_truck": (pantsir_truck, "Шасси Панциря"),
|
||
"pantsir_turret": (pantsir_turret, "Модуль Панциря"),
|
||
"pantsir_pack": (pantsir_pack, "Ракеты и пушки Панциря"),
|
||
"pantsir_dish": (flat_radar, "РЛС Панциря"),
|
||
"s400_chassis": (lambda: maz_chassis("ru_green"), "Шасси С-400"),
|
||
"s400_tubes": (lambda: tubes(2, 2, 7), "ТПК 48Н6"),
|
||
"s500_chassis": (lambda: maz_chassis("ru_khaki"), "Шасси С-500"),
|
||
"s500_tubes": (lambda: tubes(2, 1, 9.5), "ТПК 77Н6"),
|
||
"radar_trailer": (radar_trailer, "Прицеп РЛС"),
|
||
"radar_panel": (big_radar_panel, "РЛС обзора"),
|
||
"m57e6": (missile_57e6, "57Э6"),
|
||
"m48n6": (missile_48n6, "48Н6"),
|
||
"m77n6": (missile_77n6, "77Н6"),
|
||
"kalibr": (kalibr, "Калибр"),
|
||
"kh22": (kh22, "Х-22"),
|
||
"zircon": (zircon, "Циркон"),
|
||
"iskander": (iskander, "Искандер-М"),
|
||
"kinzhal": (kinzhal, "Кинжал"),
|
||
"tracer": (lambda: [el((7.6, 7.6, -16), (8.4, 8.4, 32), "tracer"), el((7.3, 7.3, -16), (8.7, 8.7, -4), "tracer_hot")], "Трассер"),
|
||
# Боевой блок «Орешника»: раскалённое ядро и длинный плазменный хвост.
|
||
"irist_chassis": (lambda: maz_chassis("nato_green"), "Шасси IRIS-T SLM"),
|
||
"irist_tubes": (lambda: tubes(4, 2, 4, "canister"), "Пакет IRIS-T"),
|
||
"nasams_trailer": (nasams_trailer, "Прицеп NASAMS"),
|
||
"nasams_launcher": (lambda: tubes(3, 2, 4, "canister"), "Пусковая NASAMS"),
|
||
"buk_chassis": (lambda: tracked("ru_green"), "Шасси Бука"),
|
||
"buk_turret": (buk_turret, "Башня Бука"),
|
||
"buk_rails": (buk_rails, "Ракеты Бука"),
|
||
"tor_chassis": (lambda: tracked("ru_khaki"), "Шасси Тора"),
|
||
"tor_turret": (tor_turret, "Башня Тора"),
|
||
"p1sun_launcher": (lambda: tubes(4, 2, 3, "dark_metal"), "Пусковая P1-SUN"),
|
||
"p1sun": (p1sun, "Дрон-перехватчик P1-SUN"),
|
||
"radar_truck": (radar_truck, "Шасси РЛС"),
|
||
"depot": (depot, "Склад боеприпасов"),
|
||
"tzm_truck": (tzm_truck, "Транспортно-заряжающая машина"),
|
||
"ew_truck": (ew_truck, "Станция РЭБ"),
|
||
"searchlight": (searchlight, "Прожектор"),
|
||
"searchlight_head": (searchlight_head, "Голова прожектора"),
|
||
"beam": (beam, "Луч прожектора"),
|
||
"flare": (flare, "Осветительная ракета"),
|
||
"reentry": (lambda: [el((6.5, 6.5, -16), (9.5, 9.5, -8), "white"), el((7, 7, -8), (9, 9, 12), "tracer_hot"),
|
||
el((7.5, 7.5, 12), (8.5, 8.5, 32), "flame")], "Боевой блок"),
|
||
}
|
||
|
||
WRECKS = ["patriot_trailer", "patriot_launcher", "gepard_hull", "gepard_turret", "gepard_guns", "pantsir_truck",
|
||
"pantsir_turret", "pantsir_pack", "s400_chassis", "s400_tubes", "s500_chassis", "s500_tubes",
|
||
"irist_chassis", "irist_tubes", "nasams_trailer", "nasams_launcher", "buk_chassis", "buk_turret",
|
||
"buk_rails", "tor_chassis", "tor_turret", "p1sun_launcher", "radar_truck", "depot", "tzm_truck",
|
||
"ew_truck", "searchlight", "searchlight_head"]
|
||
|
||
CAM_MODELS = ["shahed", "geran3", "lancet", "gerbera", "kh101", "kalibr", "kh22", "zircon", "iskander", "kinzhal", "reentry"]
|
||
|
||
# Предметы: (id, название, цвет иконки)
|
||
ITEMS = [
|
||
("designator", "Целеуказатель", (220, 50, 50)),
|
||
("launch_shahed", "Пуск: Шахед-136", (90, 92, 96)),
|
||
("launch_geran3", "Пуск: Герань-3", (30, 30, 34)),
|
||
("deploy_patriot", "Развернуть Patriot", (76, 86, 58)),
|
||
("deploy_gepard", "Развернуть Гепард", (104, 84, 54)),
|
||
("deploy_pantsir", "Развернуть Панцирь-С1", (82, 96, 60)),
|
||
("deploy_s400", "Развернуть С-400", (60, 80, 50)),
|
||
("deploy_s500", "Развернуть С-500", (108, 110, 80)),
|
||
("launch_lancet", "Пуск: Ланцет-3", (70, 78, 66)),
|
||
("launch_kh101", "Пуск: Х-101", (84, 88, 92)),
|
||
("launch_kalibr", "Пуск: Калибр", (150, 154, 156)),
|
||
("launch_kh22", "Пуск: Х-22", (226, 228, 222)),
|
||
("launch_zircon", "Пуск: Циркон", (52, 54, 52)),
|
||
("launch_iskander", "Пуск: Искандер-М", (108, 110, 80)),
|
||
("launch_kinzhal", "Пуск: Кинжал", (60, 62, 60)),
|
||
("launch_oreshnik", "Пуск: Орешник", (230, 120, 40)),
|
||
("deploy_irist", "Развернуть IRIS-T SLM", (76, 86, 58)),
|
||
("deploy_nasams", "Развернуть NASAMS", (76, 86, 58)),
|
||
("deploy_buk", "Развернуть Бук-М2", (82, 96, 60)),
|
||
("deploy_tor", "Развернуть Тор-М2", (108, 110, 80)),
|
||
("deploy_p1sun", "Развернуть P1-SUN", (52, 54, 52)),
|
||
("deploy_radar", "Развернуть РЛС дальнего обнаружения", (108, 110, 80)),
|
||
("deploy_depot", "Развернуть склад боеприпасов", (92, 78, 48)),
|
||
("deploy_tzm", "Развернуть ТЗМ", (82, 96, 60)),
|
||
("deploy_ew", "Развернуть станцию РЭБ", (108, 110, 80)),
|
||
("deploy_light", "Развернуть прожектор", (52, 54, 52)),
|
||
("mover", "Пульт перемещения", (60, 140, 220)),
|
||
("flare_gun", "Осветительная ракета", (255, 240, 180)),
|
||
("remover", "Свернуть ПВО", (200, 200, 200)),
|
||
]
|
||
|
||
|
||
# Иконки 16×16 без сглаживания. Каждый предмет рисуется своей функцией: категория задаёт основу
|
||
# (машина ПВО, пуск с пламенем, РЛС, склад, прожектор, целеуказатель, стрелки, сворачивание),
|
||
# внутри категории — мини-силуэт типа. Цвет предмета — только заливка, форма различает предметы.
|
||
class _Icon:
|
||
def __init__(self, color, seed):
|
||
self.rnd = random.Random(seed)
|
||
self.im = Image.new("RGBA", (16, 16), (0, 0, 0, 0))
|
||
self.pal = {
|
||
"b": tuple(color),
|
||
"d": tuple(clamp(v * 0.6) for v in color),
|
||
"l": tuple(clamp(v + 45) for v in color),
|
||
"k": (30, 30, 34), "r": (255, 60, 60), "o": (255, 150, 40),
|
||
"y": (255, 232, 120), "w": (236, 236, 236),
|
||
}
|
||
|
||
def px(self, x, y, key):
|
||
if 0 <= x < 16 and 0 <= y < 16:
|
||
c = self.pal[key]
|
||
if key in ("b", "d", "l"):
|
||
c = tuple(clamp(v + self.rnd.randint(-6, 6)) for v in c)
|
||
self.im.putpixel((x, y), c + (255,))
|
||
|
||
def rect(self, x0, y0, x1, y1, key):
|
||
for y in range(y0, y1 + 1):
|
||
for x in range(x0, x1 + 1):
|
||
self.px(x, y, key)
|
||
|
||
def line(self, x0, y0, x1, y1, key):
|
||
dx, dy = abs(x1 - x0), -abs(y1 - y0)
|
||
sx, sy = (1 if x0 < x1 else -1), (1 if y0 < y1 else -1)
|
||
err = dx + dy
|
||
while True:
|
||
self.px(x0, y0, key)
|
||
if x0 == x1 and y0 == y1:
|
||
break
|
||
e2 = 2 * err
|
||
if e2 >= dy:
|
||
err += dy
|
||
x0 += sx
|
||
if e2 <= dx:
|
||
err += dx
|
||
y0 += sy
|
||
|
||
def disc(self, cx, cy, r, key):
|
||
for y in range(cy - r - 1, cy + r + 2):
|
||
for x in range(cx - r - 1, cx + r + 2):
|
||
if (x - cx) ** 2 + (y - cy) ** 2 <= r * r + 0.5 * r:
|
||
self.px(x, y, key)
|
||
|
||
def ring(self, cx, cy, lo, hi, key):
|
||
for y in range(16):
|
||
for x in range(16):
|
||
if lo <= (x - cx) ** 2 + (y - cy) ** 2 <= hi:
|
||
self.px(x, y, key)
|
||
|
||
def poly(self, pts, key):
|
||
# Выпуклый многоугольник: заливка по строкам между крайними пересечениями рёбер.
|
||
n = len(pts)
|
||
for y in range(min(p[1] for p in pts), max(p[1] for p in pts) + 1):
|
||
xs = []
|
||
for i in range(n):
|
||
(x1, y1), (x2, y2) = pts[i], pts[(i + 1) % n]
|
||
if y1 == y2:
|
||
if y1 == y:
|
||
xs += [x1, x2]
|
||
elif min(y1, y2) <= y <= max(y1, y2):
|
||
xs.append(x1 + (y - y1) * (x2 - x1) / (y2 - y1))
|
||
if xs:
|
||
for x in range(math.ceil(min(xs) - 1e-9), math.floor(max(xs) + 1e-9) + 1):
|
||
self.px(x, y, key)
|
||
|
||
|
||
def _sam_base(c):
|
||
c.rect(2, 8, 13, 11, "b")
|
||
c.rect(3, 12, 4, 13, "k")
|
||
c.rect(11, 12, 12, 13, "k")
|
||
|
||
|
||
# Развёртывание ПВО: машина + пусковая установка.
|
||
def _gepard(c): # ствольная турель, два длинных ствола
|
||
_sam_base(c)
|
||
c.disc(7, 6, 2, "b")
|
||
c.line(9, 5, 15, 5, "d")
|
||
c.line(9, 7, 15, 7, "d")
|
||
|
||
|
||
def _pantsir(c): # ствольная турель с ракетными контейнерами сверху
|
||
_sam_base(c)
|
||
c.disc(7, 7, 2, "b")
|
||
c.rect(4, 3, 5, 5, "l")
|
||
c.rect(9, 3, 10, 5, "l")
|
||
c.line(9, 6, 11, 6, "d")
|
||
|
||
|
||
def _s400(c): # три вертикальных контейнера
|
||
_sam_base(c)
|
||
for x in (3, 6, 9):
|
||
c.rect(x, 2, x + 1, 7, "b")
|
||
c.rect(x, 2, x + 1, 2, "l")
|
||
|
||
|
||
def _s500(c): # четыре вертикальных контейнера
|
||
_sam_base(c)
|
||
for x in (2, 5, 8, 11):
|
||
c.rect(x, 2, x + 1, 7, "b")
|
||
c.rect(x, 2, x + 1, 2, "l")
|
||
|
||
|
||
def _patriot(c): # наклонный пусковой короб
|
||
_sam_base(c)
|
||
c.poly([(3, 8), (11, 3), (13, 5), (5, 10)], "b")
|
||
c.line(7, 7, 9, 5, "d")
|
||
|
||
|
||
def _buk(c): # наклонные направляющие с ракетой
|
||
_sam_base(c)
|
||
c.line(3, 8, 10, 2, "d")
|
||
c.line(5, 9, 12, 3, "d")
|
||
c.disc(12, 2, 1, "w")
|
||
|
||
|
||
def _irist(c): # два наклонных контейнера
|
||
_sam_base(c)
|
||
c.poly([(3, 8), (7, 3), (9, 4), (5, 9)], "b")
|
||
c.poly([(7, 9), (11, 3), (13, 4), (9, 10)], "b")
|
||
|
||
|
||
def _nasams(c): # два горизонтальных контейнера
|
||
_sam_base(c)
|
||
c.rect(3, 2, 12, 3, "b")
|
||
c.rect(3, 5, 12, 6, "b")
|
||
c.line(3, 4, 12, 4, "d")
|
||
|
||
|
||
def _tor(c): # сетка коротких вертикальных ячеек
|
||
_sam_base(c)
|
||
for x in (3, 6, 9):
|
||
c.rect(x, 2, x + 1, 3, "b")
|
||
c.rect(x, 5, x + 1, 6, "b")
|
||
|
||
|
||
def _p1sun(c): # короб с мачтой
|
||
_sam_base(c)
|
||
c.rect(3, 5, 12, 7, "b")
|
||
c.line(7, 1, 7, 4, "d")
|
||
c.px(7, 1, "r")
|
||
|
||
|
||
# Радиолокация и РЭБ.
|
||
def _radar(c): # параболическая антенна на стойке
|
||
c.disc(8, 5, 3, "l")
|
||
c.line(8, 8, 8, 12, "d")
|
||
c.rect(5, 13, 11, 14, "d")
|
||
|
||
|
||
def _ew(c): # штырь-антенна с сигнальными дугами
|
||
c.line(8, 6, 8, 14, "d")
|
||
c.disc(8, 4, 1, "b")
|
||
c.line(5, 2, 4, 4, "l")
|
||
c.line(4, 4, 4, 6, "l")
|
||
c.line(4, 6, 5, 8, "l")
|
||
c.line(11, 2, 12, 4, "l")
|
||
c.line(12, 4, 12, 6, "l")
|
||
c.line(12, 6, 11, 8, "l")
|
||
|
||
|
||
# Снабжение.
|
||
def _depot(c): # штабель ящиков на поддоне
|
||
c.rect(3, 8, 12, 13, "b")
|
||
c.line(3, 10, 12, 10, "d")
|
||
c.line(3, 12, 12, 12, "d")
|
||
c.rect(2, 14, 13, 14, "d")
|
||
c.rect(4, 4, 9, 7, "l")
|
||
c.line(4, 6, 9, 6, "d")
|
||
|
||
|
||
def _tzm(c): # грузовик с кабиной и кузовом
|
||
c.rect(2, 7, 5, 11, "b")
|
||
c.rect(3, 8, 4, 9, "w")
|
||
c.rect(6, 4, 14, 11, "l")
|
||
c.rect(3, 12, 4, 13, "k")
|
||
c.rect(10, 12, 11, 13, "k")
|
||
c.rect(13, 12, 14, 13, "k")
|
||
|
||
|
||
# Прожектор.
|
||
def _searchlight(c):
|
||
c.rect(3, 4, 10, 8, "b")
|
||
c.poly([(10, 4), (15, 1), (15, 11), (10, 8)], "y")
|
||
c.line(6, 9, 6, 13, "d")
|
||
c.rect(3, 14, 9, 14, "d")
|
||
|
||
|
||
# Целеуказатель: кольцо с засечками.
|
||
def _designator(c):
|
||
c.ring(8, 8, 16, 26, "r")
|
||
c.rect(7, 1, 8, 2, "r")
|
||
c.rect(7, 13, 8, 14, "r")
|
||
c.rect(1, 7, 2, 8, "r")
|
||
c.rect(13, 7, 14, 8, "r")
|
||
c.rect(7, 7, 8, 8, "r")
|
||
|
||
|
||
# Пуски: огонь сзади, силуэт типа спереди.
|
||
def _launch_base(c):
|
||
c.rect(0, 7, 1, 8, "o")
|
||
c.px(0, 7, "y")
|
||
|
||
|
||
def _shahed(c): # треугольное крыло
|
||
_launch_base(c)
|
||
c.poly([(5, 2), (5, 13), (13, 7)], "b")
|
||
|
||
|
||
def _geran3(c): # реактивный самолёт со стреловидным крылом
|
||
_launch_base(c)
|
||
c.rect(3, 7, 13, 8, "b")
|
||
c.poly([(6, 7), (9, 2), (11, 2), (9, 7)], "d")
|
||
c.poly([(6, 8), (9, 13), (11, 13), (9, 8)], "d")
|
||
c.poly([(3, 7), (4, 3), (5, 3), (5, 7)], "d")
|
||
|
||
|
||
def _lancet(c): # малый дрон с прямым крылом
|
||
_launch_base(c)
|
||
c.rect(3, 7, 12, 8, "b")
|
||
c.rect(7, 2, 8, 13, "d")
|
||
c.rect(3, 6, 4, 9, "d")
|
||
|
||
|
||
def _kh101(c): # крылатая ракета: стреловидные крылья и хвост
|
||
_launch_base(c)
|
||
c.rect(3, 7, 12, 8, "b")
|
||
c.poly([(12, 6), (15, 7), (15, 8), (12, 9)], "b")
|
||
c.poly([(6, 7), (9, 3), (10, 3), (8, 7)], "d")
|
||
c.poly([(6, 8), (9, 12), (10, 12), (8, 8)], "d")
|
||
c.rect(2, 5, 3, 10, "d")
|
||
|
||
|
||
def _kalibr(c): # крылатая ракета: тонкий корпус и четыре хвостовых киля
|
||
_launch_base(c)
|
||
c.rect(2, 7, 13, 8, "b")
|
||
c.poly([(13, 6), (15, 7), (15, 8), (13, 9)], "b")
|
||
c.rect(2, 4, 3, 5, "d")
|
||
c.rect(2, 10, 3, 11, "d")
|
||
|
||
|
||
def _kh22(c): # толстый корпус с большими дельта-крыльями
|
||
_launch_base(c)
|
||
c.rect(3, 6, 11, 9, "b")
|
||
c.poly([(11, 6), (15, 7), (15, 8), (11, 9)], "b")
|
||
c.poly([(4, 6), (8, 2), (9, 2), (7, 6)], "d")
|
||
c.poly([(4, 9), (8, 13), (9, 13), (7, 9)], "d")
|
||
|
||
|
||
def _zircon(c): # гиперзвук: корпус с одним передним стрейком
|
||
_launch_base(c)
|
||
c.rect(2, 7, 12, 8, "b")
|
||
c.poly([(12, 7), (15, 7), (12, 8)], "b")
|
||
c.poly([(8, 7), (12, 3), (13, 3), (11, 7)], "d")
|
||
|
||
|
||
def _iskander(c): # баллистическая: длинный корпус, конус, сопло
|
||
_launch_base(c)
|
||
c.rect(3, 7, 11, 8, "b")
|
||
c.poly([(11, 6), (15, 7), (15, 8), (11, 9)], "l")
|
||
c.rect(2, 6, 2, 9, "d")
|
||
|
||
|
||
def _oreshnik(c): # баллистическая с тремя головными частями
|
||
_launch_base(c)
|
||
c.rect(2, 6, 10, 9, "b")
|
||
c.poly([(10, 5), (13, 6), (10, 7)], "l")
|
||
c.poly([(10, 8), (13, 9), (10, 10)], "l")
|
||
c.poly([(10, 6), (15, 7), (10, 8)], "b")
|
||
|
||
|
||
def _kinzhal(c): # аэробаллистическая: корпус с треугольными килями
|
||
_launch_base(c)
|
||
c.rect(3, 7, 11, 8, "b")
|
||
c.poly([(11, 6), (15, 7), (11, 9)], "b")
|
||
c.poly([(4, 7), (7, 3), (8, 3), (6, 7)], "d")
|
||
c.poly([(4, 8), (7, 12), (8, 12), (6, 8)], "d")
|
||
|
||
|
||
def _mover(c): # четыре стрелки перемещения
|
||
c.poly([(8, 0), (4, 5), (12, 5)], "b")
|
||
c.rect(7, 4, 8, 7, "b")
|
||
c.poly([(8, 15), (4, 10), (12, 10)], "b")
|
||
c.rect(7, 8, 8, 11, "b")
|
||
c.poly([(0, 8), (5, 4), (5, 12)], "b")
|
||
c.rect(4, 7, 7, 8, "b")
|
||
c.poly([(15, 8), (10, 4), (10, 12)], "b")
|
||
c.rect(8, 7, 11, 8, "b")
|
||
|
||
|
||
def _remover(c): # стрелка вниз над лотком: свёртывание
|
||
c.poly([(8, 9), (3, 4), (13, 4)], "b")
|
||
c.rect(7, 1, 8, 4, "b")
|
||
c.rect(2, 11, 13, 13, "b")
|
||
c.line(2, 11, 13, 11, "l")
|
||
|
||
|
||
def _flare(c): # звезда осветительной ракеты на стержне
|
||
c.disc(8, 7, 1, "y")
|
||
c.line(8, 2, 8, 4, "y")
|
||
c.line(8, 10, 8, 12, "y")
|
||
c.line(3, 7, 5, 7, "y")
|
||
c.line(11, 7, 13, 7, "y")
|
||
c.line(4, 4, 5, 5, "w")
|
||
c.line(11, 4, 10, 5, "w")
|
||
c.line(4, 10, 5, 9, "w")
|
||
c.line(11, 10, 10, 9, "w")
|
||
c.rect(7, 12, 8, 15, "k")
|
||
|
||
|
||
ICON_DRAW = {
|
||
"designator": _designator,
|
||
"launch_shahed": _shahed, "launch_geran3": _geran3, "launch_lancet": _lancet,
|
||
"launch_kh101": _kh101, "launch_kalibr": _kalibr, "launch_kh22": _kh22,
|
||
"launch_zircon": _zircon, "launch_iskander": _iskander, "launch_oreshnik": _oreshnik,
|
||
"launch_kinzhal": _kinzhal,
|
||
"deploy_patriot": _patriot, "deploy_gepard": _gepard, "deploy_pantsir": _pantsir,
|
||
"deploy_s400": _s400, "deploy_s500": _s500, "deploy_irist": _irist,
|
||
"deploy_nasams": _nasams, "deploy_buk": _buk, "deploy_tor": _tor, "deploy_p1sun": _p1sun,
|
||
"deploy_radar": _radar, "deploy_ew": _ew,
|
||
"deploy_depot": _depot, "deploy_tzm": _tzm,
|
||
"deploy_light": _searchlight,
|
||
"mover": _mover, "remover": _remover, "flare_gun": _flare,
|
||
}
|
||
|
||
|
||
def item_icon(name, color, seed):
|
||
c = _Icon(color, seed)
|
||
ICON_DRAW[name](c)
|
||
# Контур по краю силуэта: тёмные предметы — светлым, светлые — тёмным, чтобы читались на обоих фонах.
|
||
# Альфа-маска не меняется: контур перекрашивает только уже нарисованные пиксели.
|
||
lum = 0.3 * color[0] + 0.59 * color[1] + 0.11 * color[2]
|
||
edge = (160, 160, 160) if lum < 100 else (70, 70, 70) if lum > 190 else None
|
||
if edge:
|
||
src = c.im.copy()
|
||
|
||
def opaque(x, y):
|
||
return 0 <= x < 16 and 0 <= y < 16 and src.getpixel((x, y))[3] > 0
|
||
|
||
for y in range(16):
|
||
for x in range(16):
|
||
if opaque(x, y) and not all(opaque(x + dx, y + dy) for dx, dy in ((1, 0), (-1, 0), (0, 1), (0, -1))):
|
||
c.im.putpixel((x, y), edge + (255,))
|
||
return c.im
|
||
|
||
|
||
def write_json(path, obj):
|
||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||
with open(path, "w", encoding="utf-8") as f:
|
||
json.dump(obj, f, ensure_ascii=False, indent=1)
|
||
|
||
|
||
def main():
|
||
# Только своё: звуки (tools/gen_sounds.py) и sounds.json не трогаем.
|
||
for sub in ("blockstates", "items", "lang", "models", "textures"):
|
||
shutil.rmtree(os.path.join(ROOT, "assets", NS, sub), ignore_errors=True)
|
||
shutil.rmtree(os.path.join(ROOT, "data"), ignore_errors=True)
|
||
a = os.path.join(ROOT, "assets", NS)
|
||
d = os.path.join(ROOT, "data", NS, "horizon")
|
||
os.makedirs(os.path.join(a, "textures", "block"))
|
||
os.makedirs(os.path.join(a, "textures", "item"))
|
||
lang = {}
|
||
for wid in WRECKS:
|
||
fn, title = MODELS[wid]
|
||
MODELS[wid + "_burnt"] = ((lambda fn=fn, wid=wid: burnt_parts(fn(), zlib.crc32(wid.encode()))), title + " (остов)")
|
||
for name, (fn, title) in MODELS.items():
|
||
write_json(os.path.join(a, "models", "block", name + ".json"), model(fn(), name.endswith("_burnt")))
|
||
write_json(os.path.join(a, "blockstates", name + ".json"), {"variants": {"": {"model": f"{NS}:block/{name}"}}})
|
||
write_json(os.path.join(d, "block", name + ".json"),
|
||
{"template": "minecraft:stone", "item": False})
|
||
lang[f"block.{NS}.{name}"] = title
|
||
for name, fn in TEXTURES.items():
|
||
fn().save(os.path.join(a, "textures", "block", name + ".png"))
|
||
for name, (base, qx, qy, burnt) in VARIANTS.items():
|
||
im = STRETCH_FN[base](qx, qy) if (base in STRETCH_FN and (qx > 1 or qy > 1)) else TEXTURES[base]()
|
||
if burnt:
|
||
im = GT.burn(im, zlib.crc32(name.encode()), "glass" if base.startswith("glass") else "paint")
|
||
im.save(os.path.join(a, "textures", "block", name + ".png"))
|
||
for i, (name, title, color) in enumerate(ITEMS):
|
||
item_icon(name, color, zlib.crc32(name.encode())).save(os.path.join(a, "textures", "item", name + ".png"))
|
||
write_json(os.path.join(a, "models", "item", name + ".json"),
|
||
{"parent": "minecraft:item/generated", "textures": {"layer0": f"{NS}:item/{name}"}})
|
||
write_json(os.path.join(a, "items", name + ".json"),
|
||
{"model": {"type": "minecraft:model", "model": f"{NS}:item/{name}"}})
|
||
write_json(os.path.join(d, "item", name + ".json"), {"template": "minecraft:stick", "max_stack_size": 1})
|
||
lang[f"item.{NS}.{name}"] = title
|
||
# Предметы-иконки для камеры монитора: 3D-модель цели в интерфейсе.
|
||
for name in CAM_MODELS:
|
||
title = MODELS[name][1]
|
||
write_json(os.path.join(a, "models", "item", "cam_" + name + ".json"),
|
||
{"parent": f"{NS}:block/{name}",
|
||
"display": {"gui": {"rotation": [20, 145, 0], "translation": [0, 0, 0], "scale": [0.36, 0.36, 0.36]}}})
|
||
write_json(os.path.join(a, "items", "cam_" + name + ".json"),
|
||
{"model": {"type": "minecraft:model", "model": f"{NS}:item/cam_{name}"}})
|
||
write_json(os.path.join(d, "item", "cam_" + name + ".json"), {"template": "minecraft:stick", "max_stack_size": 1})
|
||
lang[f"item.{NS}.cam_{name}"] = title
|
||
write_json(os.path.join(a, "lang", "en_us.json"), lang)
|
||
write_json(os.path.join(a, "lang", "ru_ru.json"), lang)
|
||
print(f"{len(MODELS)} моделей, {len(ITEMS)} предметов, {len(TEXTURES)} текстур -> {ROOT}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|