"""Decor props for the six biomes after the crater meadow.

Each follows docs/inspiration/fonds/<biome>_0.png. Same conventions as
crater_meadow_props.py: metres, vertex colours, origin at the base centre, one
GLB per prop in assets/models/biomes/<biome>/. Glowing parts are separate nodes
named for their light: "Glow" (warm lamp), "Embers" (fire, lava), "Toxic"
(green sludge), "Screen" (cyan display).

Run headless from big-egg-3d/:
  blender --background --factory-startup --python tools/blender/biome_props.py [-- --only old_port/barrel,downtown]
"""
import bpy, bmesh, math, os, random, sys
from mathutils import Vector, Matrix

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import crater_meadow_props as kit  # noqa: E402
from crater_meadow_props import srgb, mix, ico, cyl, bar, torus, build, paint  # noqa: E402
from restaurant_props import box, flat, disc_y  # noqa: E402

ROT_X = Matrix.Rotation(math.pi / 2, 3, "X")
ROT_Y = Matrix.Rotation(math.pi / 2, 3, "Y")


def glowing(name, parts):
    return build(name, parts)


def with_glow(obj, glows):
    for g in glows:
        g.parent = obj
    return obj, glows


# ---------------------------------------------------------------- old port

def barrel(name, seed):
    wood = flat("#6b4a2e", "#8c6440", "#33220f", 0.12, seed)
    hoop = flat("#3a3a3c", "#5d5d61", "#1a1a1b")
    parts = [(cyl(0.3, 0.3, 0.45, 14), wood), (cyl(0.3, 0.26, 0.45, 14, (0, 0, 0.45)), wood),
             (cyl(0.26, 0.3, 0.02, 14, (0, 0, 0.0)), wood)]
    for z in (0.12, 0.45, 0.78):
        parts.append((cyl(0.31, 0.31, 0.04, 14, (0, 0, z)), hoop))
    return build(name, parts)


def crate(name, seed, size=0.6):
    wood = flat("#8a6a44", "#b08b5d", "#4a3620", 0.1, seed)
    parts = [(box((size, size, size), (0, 0, size / 2)), wood)]
    dark = flat("#5a4328")
    for z in (0.05, size - 0.05):
        parts.append((box((size + 0.02, size + 0.02, 0.06), (0, 0, z)), dark))
    parts.append((bar((-size / 2 - 0.01, -size / 2 - 0.01, 0.05), (size / 2 + 0.01, -size / 2 - 0.01, size - 0.05), 0.02, 4), dark))
    return build(name, parts)


def crate_stack(name, seed):
    r = random.Random(seed)
    wood = flat("#8a6a44", "#b08b5d", "#4a3620", 0.1, seed)
    parts = []
    for k, (x, y, z) in enumerate([(-0.33, 0, 0), (0.33, 0.05, 0), (0.0, 0.02, 0.62)]):
        parts.append((box((0.6, 0.6, 0.6), (x, y, z + 0.3)), wood))
        parts.append((box((0.62, 0.62, 0.05), (x, y, z + 0.57)), flat("#5a4328")))
    return build(name, parts)


def bollard(name, seed):
    iron = flat("#2b2d30", "#56595e", "#111213")
    return build(name, [(cyl(0.16, 0.13, 0.45, 12), iron), (cyl(0.2, 0.2, 0.08, 12, (0, 0, 0.45)), iron)])


def rope_coil(name, seed):
    rope = flat("#a88a58", "#c9ab76", "#5a4526")
    parts = [(torus(0.2 - k * 0.03, 0.03, 18, 6, (0, 0, 0.03 + k * 0.03)), rope) for k in range(3)]
    return build(name, parts)


def lantern_post(name, seed):
    wood = flat("#4a3526", "#6b4e36", "#221710")
    iron = flat("#2b2d30", "#56595e", "#111213")
    obj = build(name, [(box((0.14, 0.14, 2.1), (0, 0, 1.05)), wood), (bar((0, 0, 1.95), (0, -0.4, 1.95), 0.03, 6), iron),
                       (box((0.2, 0.2, 0.05), (0, -0.45, 1.9)), iron), (box((0.2, 0.2, 0.05), (0, -0.45, 1.62)), iron)])
    return with_glow(obj, [build("Glow", [(box((0.16, 0.16, 0.24), (0, -0.45, 1.76)), flat("#ffcf7a", "#fff0c8", "#d08a3a"))])])


def piling(name, seed, count=1):
    r = random.Random(seed)
    wood = flat("#3e2d20", "#5c4430", "#1b130c", 0.2, seed)
    parts = []
    for k in range(count):
        a = 2 * math.pi * k / max(count, 1)
        d = 0.0 if count == 1 else 0.22
        h = r.uniform(1.1, 1.6)
        parts.append((cyl(0.17, 0.15, h, 10, (math.cos(a) * d, math.sin(a) * d, -0.4)), wood))
    if count > 1:
        parts.append((torus(0.4, 0.04, 16, 6, (0, 0, 0.7)), flat("#a88a58", "#c9ab76", "#5a4526")))
    return build(name, parts)


def rowboat(name, seed):
    wood = flat("#6d4b2f", "#946a45", "#35230f", 0.1, seed)
    bm = ico(1.0, 2, (0, 0, 0.25), (0.62, 1.55, 0.32))
    for v in bm.verts:
        v.co.z = max(v.co.z, 0.05)
    inner = ico(1.0, 2, (0, 0, 0.36), (0.5, 1.42, 0.22))
    parts = [(bm, wood), (inner, flat("#3c2a1a", "#5a3f28", "#1a120a")),
             (box((1.0, 0.18, 0.05), (0, 0.2, 0.4)), wood), (box((0.9, 0.18, 0.05), (0, -0.55, 0.4)), wood)]
    return build(name, parts)


def buoy(name, seed):
    red, white = flat("#b3362c", "#dd5a4c", "#4f1612"), flat("#e5e0d4", "#ffffff", "#8a857a")
    return build(name, [(ico(0.24, 2, (0, 0, 0.12), (1, 1, 0.8)), red), (cyl(0.1, 0.1, 0.3, 10, (0, 0, 0.25)), white),
                        (cyl(0.12, 0.05, 0.12, 10, (0, 0, 0.55)), red)])


# ---------------------------------------------------------------- olympic park

CONCRETE = lambda seed=0: flat("#8a8b86", "#aeafa9", "#4a4b46", 0.1, seed)


def concrete_block(name, seed):
    parts = [(box((1.0, 1.0, 0.8), (0, 0, 0.4)), CONCRETE(seed)), (box((0.84, 0.84, 0.05), (0, 0, 0.8)), flat("#3a3226"))]
    for k in range(4):
        a = k * 1.6
        parts.append((bar((0, 0, 0.8), (math.cos(a) * 0.35, math.sin(a) * 0.35, 1.25), 0.02, 4), flat("#5a4a32", "#7a6445")))
    return build(name, parts)


def crowd_barrier(name, seed):
    steel = flat("#a2a6aa", "#d5d8db", "#4f5256")
    parts = [(bar((-1.0, 0, 0.95), (1.0, 0, 0.95), 0.025), steel), (bar((-1.0, 0, 0.2), (1.0, 0, 0.2), 0.025), steel)]
    for x in [-1.0 + k * 0.2 for k in range(11)]:
        parts.append((bar((x, 0, 0.2), (x, 0, 0.95), 0.012), steel))
    for x in (-0.95, 0.95):
        parts.append((bar((x, -0.3, 0), (x, 0, 0.2), 0.02), steel))
        parts.append((bar((x, 0.3, 0), (x, 0, 0.2), 0.02), steel))
    return build(name, parts)


def camera_pole(name, seed):
    grey = flat("#5e6166", "#8a8d92", "#2a2c2f")
    obj = build(name, [(cyl(0.08, 0.06, 3.4, 8), grey), (bar((0, 0, 3.3), (0, -0.4, 3.35), 0.03, 6), grey),
                       (box((0.18, 0.4, 0.18), (0, -0.55, 3.25)), flat("#e3e3df", "#ffffff", "#9a9a95"))])
    return with_glow(obj, [build("Glow", [(box((0.08, 0.02, 0.08), (0, -0.76, 3.25)), flat("#ff4a3a"))])])


def floodlight(name, seed):
    grey = flat("#6b6e73", "#979a9f", "#2f3134")
    parts = [(cyl(0.18, 0.12, 5.5, 8), grey), (box((1.4, 0.2, 0.7), (0, 0, 5.8)), grey)]
    obj = build(name, parts)
    return with_glow(obj, [build("Glow", [(box((1.25, 0.03, 0.55), (0, -0.11, 5.8)), flat("#f2f4f7", "#ffffff", "#c0c4ca"))])])


def bench(name, seed):
    wood = flat("#7a5a3a", "#9c7a54", "#3a2818")
    iron = flat("#2b2d30", "#56595e", "#111213")
    parts = [(box((1.6, 0.45, 0.06), (0, 0, 0.44)), wood)]
    for x in (-0.65, 0.65):
        parts.append((box((0.06, 0.4, 0.44), (x, 0, 0.22)), iron))
    return build(name, parts)


def pillar(name, seed):
    parts = [(box((0.9, 0.9, 4.6), (0, 0, 2.3)), CONCRETE(seed)), (box((1.2, 1.2, 0.3), (0, 0, 4.7)), CONCRETE(seed + 1))]
    return build(name, parts)


# ---------------------------------------------------------------- mount royal

def _dark_rock(name, seed, size, stretch=0.75):
    bm = ico(size, 2, (0, 0, size * stretch * 0.55), (1.0, random.Random(seed).uniform(0.7, 1.0), stretch), 0.3, seed)
    for v in bm.verts:
        v.co.z = max(0.0, v.co.z)
    return build(name, [(bm, paint(srgb("#3a3033"), srgb("#5c4c50"), srgb("#140d0e"), 0.2, seed, 0.9))], flat=True)


def rock_spire(name, seed):
    return _dark_rock(name, seed, 1.0, 3.2)


def scaffold(name, seed):
    wood = flat("#5a4230", "#7c5c43", "#2a1d13", 0.15, seed)
    iron = flat("#4a4c50", "#72757a", "#1e1f21")
    parts = []
    for x in (-0.9, 0.9):
        for y in (-0.45, 0.45):
            parts.append((bar((x, y, 0), (x, y, 1.9), 0.04, 6), iron))
    for z in (0.9, 1.9):
        parts.append((box((2.0, 1.0, 0.06), (0, 0, z)), wood))
    parts.append((bar((-0.9, -0.45, 0.1), (0.9, -0.45, 1.8), 0.025, 5), iron))
    return build(name, parts)


def ore_crate(name, seed):
    obj = crate(name, seed, 0.6)
    ore = build("Embers", [(ico(0.13, 1, (x, y, 0.62), (1, 1, 0.7), 0.3, seed + i), flat("#d2461f", "#ff8a4a", "#6a1a08"))
                           for i, (x, y) in enumerate([(-0.12, 0.05), (0.1, -0.08), (0.05, 0.14)])])
    return with_glow(obj, [ore])


def hazard_barrel(name, seed):
    yellow, black = flat("#c9a227", "#ecc84a", "#5f4c10"), flat("#1c1c1c")
    parts = [(cyl(0.3, 0.3, 0.9, 14), yellow)]
    for z in (0.2, 0.62):
        parts.append((cyl(0.31, 0.31, 0.1, 14, (0, 0, z)), black))
    return build(name, parts)


def derrick(name, seed):
    iron = flat("#4a3a34", "#6e5850", "#1e1512", 0.15, seed)
    parts = []
    h = 6.5
    for x, y in ((-1, -1), (1, -1), (1, 1), (-1, 1)):
        parts.append((bar((x * 1.1, y * 1.1, 0), (x * 0.25, y * 0.25, h), 0.06, 6), iron))
    for k in range(1, 6):
        z = h * k / 6
        s = 1.1 - (1.1 - 0.25) * k / 6
        pts = [(-s, -s), (s, -s), (s, s), (-s, s)]
        for i in range(4):
            parts.append((bar((pts[i][0], pts[i][1], z), (pts[(i + 1) % 4][0], pts[(i + 1) % 4][1], z), 0.035, 5), iron))
    parts.append((box((0.8, 0.8, 0.4), (0, 0, h + 0.2)), iron))
    return build(name, parts)


def lava_pool(name, seed):
    r = random.Random(seed)
    rim = [(ico(r.uniform(0.18, 0.3), 1, (math.cos(a) * 1.25, math.sin(a) * 1.25, 0.08), (1.2, 1, 0.5), 0.3, seed + k),
            paint(srgb("#3a3033"), srgb("#5c4c50"), srgb("#140d0e"), 0.2, seed, 0.9))
           for k, a in enumerate([2 * math.pi * i / 13 for i in range(13)])]
    obj = build(name, rim, flat=True)
    return with_glow(obj, [build("Embers", [(cyl(1.25, 1.25, 0.03, 24), flat("#ff6a1f", "#ffc05a", "#b8330c"))])])


# ---------------------------------------------------------------- downtown

def car_wreck(name, seed):
    rust = paint(srgb("#5a3a2a"), srgb("#8a5a3a"), srgb("#1f130c"), 0.35, seed, 0.6)
    char = flat("#1d1a18", "#35302c", "#0b0a09")
    parts = [(box((1.8, 4.2, 0.7), (0, 0, 0.55)), rust), (box((1.6, 2.2, 0.55), (0, 0.2, 1.17)), char),
             (box((1.62, 2.0, 0.05), (0, 0.2, 1.45)), rust)]
    for x, y in ((-0.85, -1.3), (0.85, -1.3), (-0.85, 1.3), (0.85, 1.3)):
        parts.append((cyl(0.34, 0.34, 0.22, 12, (x - 0.11, y, 0.34), ROT_Y), char))
    return build(name, parts)


def rubble_pile(name, seed):
    r = random.Random(seed)
    parts = []
    for k in range(14):
        tone = r.choice(["#6e655c", "#7b3f2f", "#5c5650", "#8a8078"])
        at = (r.uniform(-0.6, 0.6), r.uniform(-0.5, 0.5), 0.0)
        size = r.uniform(0.12, 0.28)
        parts.append((box((size * 1.6, size, size * 0.8), (at[0], at[1], size * 0.4 + max(0, 0.3 - abs(at[0]) * 0.4))),
                      flat(tone, None, None, 0.1, seed + k)))
    return build(name, parts, flat=True)


def banner_pole(name, seed):
    pole = flat("#3a3c40", "#5f6267", "#17181a")
    red = flat("#9c2a24", "#c4453b", "#4a120f", 0.15, seed)
    parts = [(cyl(0.05, 0.05, 3.2, 8), pole), (bar((0, 0, 3.0), (0, -0.7, 3.0), 0.025, 5), pole),
             (box((0.03, 0.6, 1.1), (0, -0.4, 2.4)), red), (box((0.035, 0.3, 0.3), (0, -0.4, 2.55)), flat("#e8dcc0"))]
    return build(name, parts)


def sawhorse(name, seed):
    white, red = flat("#e5e0d4", "#ffffff", "#8a857a"), flat("#b3362c", "#dd5a4c", "#4f1612")
    parts = []
    for x in (-0.8, 0.8):
        parts.append((bar((x, -0.3, 0), (x, 0, 0.9), 0.04, 5), white))
        parts.append((bar((x, 0.3, 0), (x, 0, 0.9), 0.04, 5), white))
    for k in range(4):
        parts.append((box((0.4, 0.04, 0.2), (-0.6 + k * 0.4, 0, 0.8)), red if k % 2 == 0 else white))
    return build(name, parts)


def traffic_cone(name, seed):
    orange, white = flat("#e0662a", "#ff8f52", "#6a2a0c"), flat("#ecebe6")
    return build(name, [(box((0.4, 0.4, 0.04), (0, 0, 0.02)), orange), (cyl(0.16, 0.03, 0.6, 12, (0, 0, 0.04)), orange),
                        (cyl(0.115, 0.09, 0.1, 12, (0, 0, 0.22)), white)])


def building(name, seed, floors=4, width=6.0, depth=5.0):
    r = random.Random(seed)
    wall = flat(r.choice(["#6a5a4e", "#5a4e48", "#6f6258"]), None, None, 0.08, seed)
    parts = [(box((width, depth, floors * 2.6), (0, 0, floors * 1.3)), wall),
             (box((width + 0.2, depth + 0.2, 0.3), (0, 0, floors * 2.6 + 0.15)), flat("#3a3430"))]
    glows = []
    for f in range(floors):
        for k in range(int(width / 1.4)):
            x = -width / 2 + 0.7 + k * 1.4
            z = 1.2 + f * 2.6
            lit = r.random() < 0.25
            win = box((0.7, 0.05, 1.1), (x, -depth / 2 - 0.02, z))
            if lit:
                glows.append((win, flat("#ffcf7a", "#fff0c8", "#d08a3a")))
            else:
                parts.append((win, flat("#1c2228", "#2c343c")))
    obj = build(name, parts)
    return with_glow(obj, [build("Glow", glows)] if glows else [])


# ---------------------------------------------------------------- underground city

RUST = lambda seed=0: paint(srgb("#6a3f28"), srgb("#8f5a3a"), srgb("#2a160c"), 0.3, seed, 0.6)


def pipe_run(name, seed):
    parts = [(cyl(0.22, 0.22, 3.2, 12, (-1.6, 0, 0.3), ROT_Y), RUST(seed)),
             (cyl(0.12, 0.12, 3.2, 10, (-1.6, 0.45, 0.16), ROT_Y), RUST(seed + 1))]
    for x in (-1.2, 0.0, 1.2):
        parts.append((cyl(0.26, 0.26, 0.1, 12, (x - 0.05, 0, 0.3), ROT_Y), flat("#3a2a20", "#5a4030")))
        parts.append((box((0.1, 0.5, 0.1), (x, 0.05, 0.05)), flat("#2a2a2c")))
    return build(name, parts)


def pipe_valve(name, seed):
    parts = [(cyl(0.16, 0.16, 1.3, 12), RUST(seed)), (cyl(0.2, 0.2, 0.1, 12, (0, 0, 1.3)), RUST(seed)),
             (bar((0, 0, 0.9), (0, -0.3, 0.9), 0.04, 6), RUST(seed)),
             (torus(0.18, 0.025, 16, 6, (0, -0.32, 0.9), ROT_X), flat("#a8322b", "#d65a4f", "#4a1512"))]
    return build(name, parts)


def sewer_outlet(name, seed):
    concrete = flat("#4a4f4c", "#6a706c", "#1e2220", 0.15, seed)
    ring = cyl(0.8, 0.8, 0.5, 20, (0, 0.0, 0.8), ROT_X)
    parts = [(box((2.2, 0.6, 1.8), (0, 0.3, 0.9)), concrete), (ring, RUST(seed)),
             (cyl(0.66, 0.66, 0.52, 20, (0, 0.01, 0.8), ROT_X), flat("#050606"))]
    obj = build(name, parts)
    return with_glow(obj, [build("Toxic", [(box((1.0, 1.0, 0.03), (0, -0.75, 0.02)), flat("#3fcf4a", "#8cff8a", "#1a6a20"))])])


def toxic_barrel(name, seed):
    obj = build(name, [(cyl(0.3, 0.3, 0.9, 14), RUST(seed)), (cyl(0.31, 0.31, 0.06, 14, (0, 0, 0.3)), flat("#2a2a2c"))])
    return with_glow(obj, [build("Toxic", [(cyl(0.26, 0.26, 0.02, 14, (0, 0, 0.9)), flat("#3fcf4a", "#8cff8a", "#1a6a20"))])])


def support_pillar(name, seed):
    concrete = flat("#3f4441", "#5c625e", "#171a18", 0.2, seed)
    parts = [(box((1.0, 1.0, 4.6), (0, 0, 2.3)), concrete), (box((1.3, 1.3, 0.4), (0, 0, 0.2)), concrete)]
    for z in (1.2, 2.8):
        parts.append((cyl(0.1, 0.1, 1.0, 8, (-0.5, -0.55, z), ROT_Y), RUST(seed)))
    return build(name, parts)


def grate(name, seed):
    iron = flat("#2a2b2c", "#474849", "#0f1010")
    parts = [(box((1.0, 1.0, 0.03), (0, 0, 0.015)), flat("#0a0b0b"))]
    for k in range(6):
        parts.append((box((0.05, 1.0, 0.04), (-0.4 + k * 0.16, 0, 0.03)), iron))
    parts.append((box((1.05, 0.06, 0.05), (0, 0.5, 0.03)), iron))
    parts.append((box((1.05, 0.06, 0.05), (0, -0.5, 0.03)), iron))
    return build(name, parts)


# ---------------------------------------------------------------- world egg center

WHITE = lambda seed=0: flat("#d9dde1", "#f4f6f8", "#8e959c", 0.05, seed)


def server_rack(name, seed):
    r = random.Random(seed)
    obj = build(name, [(box((0.8, 1.0, 2.0), (0, 0, 1.0)), WHITE(seed)), (box((0.7, 0.02, 1.8), (0, -0.5, 1.0)), flat("#2c3136"))])
    lights = [(box((0.05, 0.02, 0.03), (-0.28 + (k % 6) * 0.1, -0.52, 0.3 + (k // 6) * 0.28)), flat("#6ad3ff")) for k in range(36) if r.random() < 0.5]
    return with_glow(obj, [build("Screen", lights)])


def console(name, seed):
    obj = build(name, [(box((1.4, 0.7, 0.9), (0, 0, 0.45)), WHITE(seed)), (box((1.4, 0.5, 0.08), (0, -0.1, 0.94)), flat("#9aa3ab")),
                       (box((1.2, 0.1, 0.6), (0, 0.25, 1.3)), flat("#2c3136"))])
    return with_glow(obj, [build("Screen", [(box((1.1, 0.02, 0.5), (0, 0.19, 1.3)), flat("#5ec8ff", "#b8ecff", "#1c5a7a"))])])


def tank(name, seed):
    steel = flat("#b8bfc6", "#e6ebef", "#5f666d", 0.05, seed)
    parts = [(cyl(0.9, 0.9, 2.4, 20), steel), (ico(0.9, 2, (0, 0, 2.4), (1, 1, 0.4)), steel),
             (cyl(1.0, 1.0, 0.12, 20, (0, 0, 0.8)), flat("#8a939b")), (cyl(1.0, 1.0, 0.12, 20, (0, 0, 1.7)), flat("#8a939b")),
             (bar((0.8, 0, 0.5), (1.5, 0, 0.5), 0.1, 8), steel), (bar((1.5, 0, 0.5), (1.5, 0, 0.0), 0.1, 8), steel)]
    return build(name, parts)


def lab_crate(name, seed):
    return build(name, [(box((0.8, 0.6, 0.6), (0, 0, 0.3)), WHITE(seed)), (box((0.82, 0.62, 0.08), (0, 0, 0.56)), flat("#e0b53a", "#ffd766")),
                        (box((0.3, 0.02, 0.15), (0, -0.31, 0.3)), flat("#2c3136"))])


def egg_pod(name, seed):
    steel = flat("#9aa3ab", "#d0d6dc", "#4a5157")
    parts = [(cyl(1.0, 1.1, 0.35, 20), steel)]
    for k in range(4):
        a = k * math.pi / 2 + math.pi / 4
        parts.append((bar((math.cos(a) * 0.9, math.sin(a) * 0.9, 0.3), (math.cos(a) * 0.6, math.sin(a) * 0.6, 2.2), 0.07, 6), steel))
    parts.append((torus(0.62, 0.06, 20, 6, (0, 0, 2.2)), steel))
    obj = build(name, parts)
    egg = build("Glow", [(ico(0.55, 3, (0, 0, 1.25), (1, 1, 1.35)), flat("#f2c14e", "#fff0b0", "#c4861c"))])
    return with_glow(obj, [egg])


# ---------------------------------------------------------------- registry

PROPS = {
    "old_port": {
        "barrel": barrel, "crate": crate, "crate_stack": crate_stack, "bollard": bollard, "rope_coil": rope_coil,
        "lantern_post": lantern_post, "piling": lambda n, s: piling(n, s, 1), "piling_cluster": lambda n, s: piling(n, s, 3),
        "rowboat": rowboat, "buoy": buoy,
    },
    "olympic_park": {
        "concrete_block": concrete_block, "crowd_barrier": crowd_barrier, "camera_pole": camera_pole,
        "floodlight": floodlight, "bench": bench, "pillar": pillar,
    },
    "mount_royal": {
        "basalt_rock": lambda n, s: _dark_rock(n, s, 0.7), "basalt_small": lambda n, s: _dark_rock(n, s, 0.3),
        "rock_spire": rock_spire, "scaffold": scaffold, "ore_crate": ore_crate, "hazard_barrel": hazard_barrel,
        "derrick": derrick, "lava_pool": lava_pool,
    },
    "downtown": {
        "car_wreck": car_wreck, "rubble_pile": rubble_pile, "banner_pole": banner_pole, "sawhorse": sawhorse,
        "traffic_cone": traffic_cone, "building_a": lambda n, s: building(n, s, 4, 6.0),
        "building_b": lambda n, s: building(n, s, 5, 7.5), "building_c": lambda n, s: building(n, s, 3, 5.0),
    },
    "underground_city": {
        "pipe_run": pipe_run, "pipe_valve": pipe_valve, "sewer_outlet": sewer_outlet, "toxic_barrel": toxic_barrel,
        "support_pillar": support_pillar, "grate": grate,
    },
    "world_egg_center": {
        "server_rack": server_rack, "console": console, "tank": tank, "lab_crate": lab_crate, "egg_pod": egg_pod,
    },
}

if __name__ == "__main__":
    args = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []
    only = args[args.index("--only") + 1].split(",") if "--only" in args else list(PROPS)
    bpy.ops.wm.read_factory_settings(use_empty=True)
    for key in only:
        biome, _, single = key.partition("/")
        kit.OUT = os.path.join(kit.PROJECT, "assets", "models", "biomes", biome)
        os.makedirs(kit.OUT, exist_ok=True)
        for index, (name, make) in enumerate(PROPS[biome].items()):
            if single and name != single:
                continue
            made = make(name, 300 + index)
            obj, extra = made if isinstance(made, tuple) else (made, [])
            kit.export(obj, extra)
