e84a5e9fa7
Preserve launcher grids and authored materials; fix the Buk radar intersection and regenerate geometry bounds. Register the separate RM48U game visual for the upcoming carrier path. Co-Authored-By: Codex <noreply@openai.com>
152 lines
6.7 KiB
Python
152 lines
6.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Optional authored support and tracked-vehicle pass; no runtime IDs changed.
|
|
|
|
Buk-M2E/Tor-M2E are stylized tracked versions. Radar/EW/TZM are generic
|
|
support vehicles; depot and light are game props, not specific prototypes.
|
|
"""
|
|
import json
|
|
from pathlib import Path
|
|
import zlib
|
|
import gen_models as g
|
|
|
|
ROOT = Path(g.ROOT)
|
|
SOURCE = ROOT / 'source-assets/assets/airdef/models/block'
|
|
|
|
|
|
def tracked(tex, tor=False):
|
|
p = [g.el((-9, 4, -13), (25, 13, 30), tex),
|
|
g.el((-9, 4, -16), (25, 9, -13), tex),
|
|
g.el((-8, 9, -14), (24, 13, -9), tex, rot=('x', -22.5)),
|
|
g.el((-8, 13, -7), (24, 15, 20), tex),
|
|
g.el((-7, 13, 21), (23, 14, 30), 'dark_metal'),
|
|
*g.both(g.el((-13, 0, -15), (-9, 8.5, 31), 'track')),
|
|
*g.both(g.el((-13.3, 8.5, -14), (-9, 9.5, 31), tex))]
|
|
for z in (-11, -5, 1, 7, 13, 19, 25):
|
|
# Three broad strips for a readable round wheel over each continuous track.
|
|
for a, b, w in [(-1, -.5, .7), (-.5, .5, 1), (.5, 1, .7)]:
|
|
p += g.both(g.el((-13.08, 4.2 + a * 3.1, z - w * 2.5),
|
|
(-13.02, 4.2 + b * 3.1, z + w * 2.5), 'hub'))
|
|
# Different deck fittings distinguish the two tracked vehicles.
|
|
if tor:
|
|
p += [g.el((-7, 15, -10), (2, 15.7, -5), 'dark_metal'),
|
|
g.el((15, 14, 21), (22, 15, 28), 'metal')]
|
|
else:
|
|
p += [g.el((-7, 15, -10), (2, 15.7, -5), 'metal'),
|
|
g.el((3, 14, 23), (20, 14.5, 28), 'bare_nozzle')]
|
|
return p
|
|
|
|
|
|
def buk_turret():
|
|
# Phased-array face and two shoulders, while the rail pack keeps its pivot.
|
|
return [g.el((-4, 0, -4), (20, 7, 20), 'pilot_olive'),
|
|
g.el((-2, 7, 2), (18, 10, 18), 'pilot_olive'),
|
|
g.el((1, 2, -10), (11, 14, -4), 'radar_face'),
|
|
g.el((0, 1, -5), (16, 3, -3), 'dark_metal'),
|
|
*g.both(g.el((-4, 7, 4), (-2, 9, 16), 'metal'))]
|
|
|
|
|
|
def buk_rails():
|
|
# Keep four rails and their exact centres and forward endpoints.
|
|
return [{**p, 'tex': 'pilot_olive' if p['tex'] == 'ru_green' else p['tex']} for p in g.buk_rails()]
|
|
|
|
|
|
def tor_turret():
|
|
# Eight existing cells; the engine's launch grid is intentionally unchanged.
|
|
p = [g.el((-4, 0, -6), (20, 12, 22), 'pilot_olive'),
|
|
g.el((-3, 12, -3), (19, 16, 21), 'pilot_olive'),
|
|
g.el((-2, 2, -11), (18, 15, -6), 'radar_face'),
|
|
g.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(g.el((x, 16.5, z), (x + 4, 17.5, z + 6), 'tube_cap'))
|
|
return p
|
|
|
|
|
|
def truck(kind):
|
|
# Generic four-axle service truck, based on the project's existing dimensions.
|
|
p = [g.el((-5, 5, -15), (21, 9, 32), 'pilot_olive'),
|
|
g.el((-5, 9, -16), (21, 16, -6), 'pilot_olive'),
|
|
g.el((-4.5, 16, -15.5), (20.5, 20, -6), 'pilot_olive'),
|
|
g.el((-4, 16, -15.99), (7.5, 19.3, -15.8), 'glass_cab'),
|
|
g.el((8.5, 16, -15.99), (20, 19.3, -15.8), 'glass_cab'),
|
|
*g.both(g.el((-5.08, 16, -14), (-4.99, 19, -8), 'glass_cab')),
|
|
g.el((-5.5, 7, -16), (21.5, 8, -15.3), 'dark_metal'),
|
|
*g.both(g.el((-4, 9, -16), (-1, 10.3, -15.8), 'bare_lamp'))]
|
|
for z in (-11, -2, 17, 26):
|
|
for a, b, w in [(-1, -.6, .6), (-.6, -.2, .92), (-.2, .2, 1), (.2, .6, .92), (.6, 1, .6)]:
|
|
p += g.both(g.el((-8.5, 4 + a * 4, z - w * 4), (-5.5, 4 + b * 4, z + w * 4), 'tire'))
|
|
p += g.both(g.el((-8.56, 2.2, z - 1.8), (-8.5, 5.8, z + 1.8), 'hub'))
|
|
if kind == 'radar':
|
|
p += [g.el((-4, 9, -5), (20, 12, 30), 'pilot_olive'),
|
|
g.el((-3, 12, 4), (19, 14, 28), 'dark_metal'),
|
|
*g.both(g.el((-7, 0, 23), (-5, 6, 25), 'metal'))]
|
|
elif kind == 'tzm':
|
|
p += [g.el((-4, 9, -3), (20, 12, 30), 'dark_metal'),
|
|
g.el((18, 12, -4), (21, 26, -1), 'metal'),
|
|
g.el((6, 24, -4), (21, 26, -1), 'metal'),
|
|
g.el((6, 20, -4), (7, 24, -3), 'dark_metal')]
|
|
for x in (-3, 5, 13):
|
|
p += [g.el((x, 12, -1), (x + 6, 18, 29), 'tube', {'north':'tube_cap', 'south':'tube_cap'})]
|
|
else:
|
|
p += [g.el((-5, 9, -4), (21, 22, 30), 'pilot_olive'),
|
|
g.el((-4, 12, 30), (20, 20, 30.3), 'dark_metal'),
|
|
g.el((7, 22, 10), (9, 32, 12), 'bare_antenna')]
|
|
for y in (24, 28):
|
|
p += [g.el((-4, y, 9), (20, y + 1, 13), 'bare_antenna')]
|
|
return p
|
|
|
|
|
|
def depot():
|
|
p = [g.el((-14, 0, -14), (30, 2, 30), 'concrete'),
|
|
g.el((-13, 2, -13), (1, 12, 1), 'crate'),
|
|
g.el((-13, 2, 3), (1, 10, 17), 'crate'),
|
|
g.el((3, 2, -13), (17, 10, 1), 'crate'),
|
|
g.el((-14, 2, 27), (30, 5, 29), 'dark_metal'),
|
|
g.el((-14, 2, -14), (-12, 5, 28), 'dark_metal')]
|
|
for k in range(3):
|
|
p.append(g.el((6, 2 + k * 5, 6), (28, 7 + k * 5, 10), 'tube', {'north':'tube_cap','south':'tube_cap'}))
|
|
return p
|
|
|
|
|
|
def searchlight():
|
|
return [g.el((1, 0, 1), (15, 2, 15), 'dark_metal'),
|
|
g.el((3, 2, 3), (13, 4, 13), 'metal'),
|
|
g.el((6, 4, 6), (10, 12, 10), 'dark_metal'),
|
|
*g.both(g.el((2, 8, 7), (3, 14, 9), 'metal'))]
|
|
|
|
|
|
def searchlight_head():
|
|
return [g.el((3, 3, -2), (13, 13, 14), 'dark_metal'),
|
|
g.el((4, 2, 0), (12, 14, 12), 'dark_metal'),
|
|
g.el((3.5, 3.5, -2.5), (12.5, 12.5, -2), 'lamp'),
|
|
g.el((5, 5, 14), (11, 11, 15), 'metal')]
|
|
|
|
|
|
def export(name, parts, burnt=False):
|
|
model = g.model(parts, burnt=burnt)
|
|
for key, value in model['textures'].items():
|
|
rel = value.split(':', 1)[1] + '.png'
|
|
if not any((root / rel).exists() for root in
|
|
(ROOT / 'assets/airdef/textures', ROOT / 'source-assets/assets/airdef/textures')):
|
|
base = g.VARIANTS[key][0]
|
|
candidate = base + '_b' if burnt else base
|
|
if not (ROOT / 'assets/airdef/textures/block' / (candidate + '.png')).exists():
|
|
candidate = base
|
|
model['textures'][key] = 'airdef:block/' + candidate
|
|
SOURCE.mkdir(parents=True, exist_ok=True)
|
|
(SOURCE / (name + '.json')).write_text(json.dumps(model, indent=1) + '\n', encoding='utf-8')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
for name, make in [('buk_chassis', lambda: tracked('pilot_olive')),
|
|
('buk_turret', buk_turret), ('buk_rails', buk_rails),
|
|
('tor_chassis', lambda: tracked('pilot_olive', True)), ('tor_turret', tor_turret),
|
|
('radar_truck', lambda: truck('radar')), ('tzm_truck', lambda: truck('tzm')),
|
|
('ew_truck', lambda: truck('ew')), ('depot', depot),
|
|
('searchlight', searchlight), ('searchlight_head', searchlight_head)]:
|
|
parts = make()
|
|
export(name, parts)
|
|
export(name + '_burnt', g.burnt_parts(parts, zlib.crc32(name.encode())), True)
|
|
print(name, len(parts))
|