""" The outpost on the window galleries, at the outer doors, on Relay Control's drum and on the roofs. See build/outpost.py for the whole; this file holds the parts that are not plain wall runs. clad_galleries a base course, fins on the piers between the kit's window frames, plates and a light strip over every window, a visor along the roof's edge (it turns the corner to the door's portal), a spine and ribs on the roof portals at the four outer doors: two leaning jamb pylons, a deep lintel with a lamp under it and a sign plate, an orange reveal relay_crown eleven canted armour plates round the drum's head, orange underneath, a rail round the skylight, aerials roof_works plant units with louvres and fans, the Sci-Fi Lite pack's 0.8 m ducts on stands between them, condensers, hatches, a mast on the crossing """ import math from mathutils import Matrix, Vector import exterior as ext from hs import Frame, clipped, cylinder, loft, prism, rect, sweep, tube from outpost import BASE, PLINTH_TOP LITE = "/Game/SciFI_Lite_Assets/StaticMeshes/" PORTAL_IN, PORTAL_OUT = 4.52, 5.72 # the jamb pylons stand between these distances from the doorway's middle PIPE_Z = 6.40 # the roof ducts' middle: they pass over the junction blocks' parapets def ccw(pts): a = sum(pts[i][0] * pts[(i + 1) % len(pts)][1] - pts[(i + 1) % len(pts)][0] * pts[i][1] for i in range(len(pts))) return list(pts) if a > 0 else list(pts)[::-1] def aerial(part, x, y, z, h, r=0.018): cylinder(part, (x, y, z - 0.02), (x, y, z + 0.18), 0.06, "black", sides=8, cham=0.012) cylinder(part, (x, y, z + 0.16), (x, y, z + h), r, "grey", sides=6) cylinder(part, (x, y, z + h), (x, y, z + h + 0.08), 0.035, "red", sides=6, smooth=False) def louvre(part, fr, a0, a1, z0, z1): """A dark field with slats, on a face.""" prism(part, fr, rect(a0, a1, z0, z1), -0.03, 0.012, "black", back=False) n = max(3, int(round((z1 - z0) / 0.16))) step = (z1 - z0) / n for k in range(n): zc = z0 + step * (k + 0.5) prism(part, fr, rect(a0, a1, zc - step * 0.30, zc + step * 0.30), 0.0, 0.05, "grey", cham=min(0.02, step * 0.2), back=False) # --------------------------------------------------------------------------------------------------------------- # window galleries BASE_COURSE = ([(-0.02, BASE - 0.02), (0.30, BASE - 0.02), (0.30, 0.18), (0.20, 0.40), (0.20, 0.58), (-0.02, 0.58)], ["concrete", "concrete", "concrete", "white", "white", "concrete"]) VISOR_LOW = ([(-0.02, 4.02), (0.42, 4.02), (0.66, 4.26), (0.66, 4.56), (0.30, 4.56), (-0.02, 4.56)], ["orange", "orange", "white", "white", "white", "hull"]) VISOR_UP = ([(0.131, 4.56), (0.66, 4.56), (0.66, 5.14), (0.58, 5.22), (0.30, 5.22), (0.131, 5.22)], ["white", "white", "darkmetal", "darkmetal", "darkmetal", "white"]) VISOR_IN = ([(-0.13, 4.86), (0.131, 4.86), (0.131, 5.22), (-0.13, 5.22)], ["darkmetal", "white", "darkmetal", "white"]) def side_frame(sd, w): return Frame(sd.p(0.0, w, 0.0), sd.along(1), (0.0, 0.0, 1.0), sd.lateral()) def fin(part, sd, a, c): """A fin on a pier between two windows: it leans with the windows and widens upwards.""" cl = min(0.05, (c - a) * 0.2) def rg(z, t): w0 = ext._w_out(z) if z <= ext.ZK else ext.WK wi, wo = w0 - 0.03, w0 + t return [sd.p(a, wi, z), sd.p(c, wi, z), sd.p(c, wo - cl, z), sd.p(c - cl, wo, z), sd.p(a + cl, wo, z), sd.p(a, wo - cl, z)] loft(part, [rg(0.60, 0.22), rg(ext.ZK, 0.50), rg(3.99, 0.50)], "white") prism(part, side_frame(sd, ext.WK + 0.50), rect(a + cl + 0.01, c - cl - 0.01, 3.34, 3.90), -0.01, 0.03, "orange", cham=0.008, back=False) def lean_plate(part, sd, a, c): """An armour plate on a wide pier's leaning face.""" lat, up = Vector(sd.lateral()), Vector((0.0, 0.0, 1.0)) fr = Frame(sd.p(0.0, ext.WB, ext.ZS), sd.along(1), (lat * ext.SLOPE + up).normalized(), (lat - up * ext.SLOPE).normalized()) length = (ext.ZK - ext.ZS) * ext.SLANT_LEN prism(part, fr, clipped(a, c, 0.22, length - 0.22, 0.10, 0.10, 0.10, 0.10), -0.03, 0.12, "white", cham=0.025, back=False, panel=(0.09, 0.014)) def gallery_side(part, sd, s0, s1, frames, end0, end1, info): lat = Vector(sd.lateral()) main = Vector(sd.p(s1, ext.WK, 0.0)) - Vector(sd.p(s0, ext.WK, 0.0)) forward = Vector((main.y, -main.x, 0.0)).dot(lat) > 0 # walking from s0 to s1 has the outside to the right def path(pts): pts = pts if forward else pts[::-1] return [(p[0], p[1]) for p in pts] cuts = [s0] + [v for f in frames for v in f] + [s1] piers = [(cuts[i], cuts[i + 1]) for i in range(0, len(cuts), 2)] sweep(part, BASE_COURSE[0], path([sd.p(s0, ext.WB, 0.0), sd.p(s1, ext.WB, 0.0)]), BASE_COURSE[1]) for a, c in piers: w = c - a if w < 0.25: continue # A fin reaches 2 cm over the window frame beside it: the shell's pier and the kit's frame meet there edge to # edge, and the full check found a ray that went between them (seen from due south, a hair's breadth wide). # At the gallery's own ends there is no frame: there the fin stays inside the pier. ea = 0.02 if a > s0 + 1e-3 else -0.03 ec = 0.02 if c < s1 - 1e-3 else -0.03 for fa, fc in ([(a - ea, c + ec)] if w <= 0.8 else [(a - ea, a + 0.34), (c - 0.34, c + ec)]): fin(part, sd, fa, fc) info["fins"] += 1 if w > 1.3: lean_plate(part, sd, a + 0.47, c - 0.47) fr = side_frame(sd, ext.WK) for f0, f1 in frames: mid = (f0 + f1) / 2.0 for a0, a1 in ((f0 + 0.06, mid - 0.035), (mid + 0.035, f1 - 0.06)): prism(part, fr, clipped(a0, a1, 3.34, 3.92, 0.0, 0.0, 0.08, 0.08), -0.03, 0.10, "white", cham=0.02, back=False, panel=(0.07, 0.012)) prism(part, fr, rect(f0 + 0.3, f1 - 0.3, 3.945, 3.995), -0.02, 0.05, "light", back=False) # the visor: along the side, and round the corner to the portal where the gallery ends in a door pts = [sd.p(s0, ext.WK, 0.0), sd.p(s1, ext.WK, 0.0)] if end0[0] == "door": pts.insert(0, sd.p(s0, PORTAL_OUT, 0.0)) if end1[0] == "door": pts.append(sd.p(s1, PORTAL_OUT, 0.0)) for prof, mats in (VISOR_LOW, VISOR_UP, VISOR_IN): sweep(part, prof, path(pts), mats) info["visor"] += abs(s1 - s0) def gallery_roof(part, axis, c, s0, s1, frames, end0=("room",), end1=("room",)): """A spine along the roof and ribs out to the parapet, one over every pier between two windows. It stops short of a door's end: the door's housing stands there.""" g = Frame.ground(0.0, 0.0, ext.G_ROOF) def fp(sa, sb, wa, wb): pts = [(c + wa, sa), (c + wa, sb), (c + wb, sb), (c + wb, sa)] if axis == "y" else [(sa, c + wa), (sb, c + wa), (sb, c + wb), (sa, c + wb)] return ccw(pts) m0, m1 = (1.9 if end0[0] == "door" else 0.9), (1.9 if end1[0] == "door" else 0.9) prism(part, g, fp(s0 + m0, s1 - m1, -0.55, 0.55), -0.02, 0.26, "white", cham=0.06, panel=(0.14, 0.03), panel_mat="darkmetal") for f, n in zip(frames[:-1], frames[1:]): s = (f[1] + n[0]) / 2.0 for wa, wb in ((0.55, 6.85), (-6.85, -0.55)): prism(part, g, fp(s - 0.14, s + 0.14, wa, wb), -0.02, 0.07, "white", cham=0.02) prism(part, g, fp(s - 0.32, s + 0.32, -0.40, 0.40), 0.24, 0.34, "orange", cham=0.03) def clad_galleries(part, b): info = {"fins": 0, "visor": 0.0} for name, axis, c, s0, s1, e0, e1 in ext.station_plan(b)[3]: frames = ext.gallery_frames(axis, c, s0, s1) for side in (1, -1): gallery_side(part, ext._Side(axis, c, side), s0, s1, frames[side], e0, e1, info) gallery_roof(part, axis, c, s0, s1, frames[1], e0, e1) return info # --------------------------------------------------------------------------------------------------------------- # portals LINTEL = ([(-0.02, 4.52), (0.85, 4.52), (1.15, 4.80), (1.15, 5.30), (0.95, 5.50), (-0.02, 5.50)], ["white", "orange", "white", "darkmetal", "darkmetal", "hull"]) def portal(part, o, n, jambs=True, half=PORTAL_OUT, base=BASE): """A portal round an outer doorway. o: the doorway's middle on the wall's plane; n: looking out of it.""" fr = Frame.facing((o[0], o[1], 0.0), (n[0], n[1], 0.0)) up = Vector((0.0, 0.0, 1.0)) if jambs: for sign in (-1, 1): a0, a1 = sorted((sign * PORTAL_IN, sign * PORTAL_OUT)) foot = [fr.p(a, 0.0, h) for a, h in ((a0 - 0.06, -0.03), (a1 + 0.06, -0.03), (a1 + 0.06, 1.12), (a0 - 0.06, 1.12))] prism(part, Frame.ground(0.0, 0.0, base), ccw([(p.x, p.y) for p in foot]), 0.0, PLINTH_TOP - base, "concrete", cham=0.05) def rg(z, p, d=0.0, cl=0.07): return [fr.p(a0 + d, z, -0.03), fr.p(a1 - d, z, -0.03), fr.p(a1 - d, z, p - cl), fr.p(a1 - d - cl, z, p), fr.p(a0 + d + cl, z, p), fr.p(a0 + d, z, p - cl)] loft(part, [rg(PLINTH_TOP, 1.05), rg(5.22, 0.62), rg(5.30, 0.54, d=0.08)], "white") # an orange reveal on the side that looks at the doorway inner = sign * PORTAL_IN rv = Frame(fr.p(inner, 0.0, 0.0), fr.n, up, fr.u * (-sign)) prism(part, rv, [(0.10, 0.50), (0.90, 0.50), (0.52, 4.35), (0.10, 4.35)], -0.01, 0.03, "orange", cham=0.008, back=False) # a light strip up the leaning front am = (a0 + a1) / 2.0 v = (up * (5.22 - PLINTH_TOP) + fr.n * (0.62 - 1.05)).normalized() fn = (fr.n * (5.22 - PLINTH_TOP) + up * (1.05 - 0.62)).normalized() lf = Frame(fr.p(am, PLINTH_TOP, 1.05), fr.u, v, fn) prism(part, lf, rect(-0.15, 0.15, 0.55, 4.25), -0.01, 0.012, "black", back=False) prism(part, lf, rect(-0.055, 0.055, 0.75, 4.05), 0.0, 0.03, "light", cham=0.008, back=False) a, c = fr.p(-half, 0.0, 0.0), fr.p(half, 0.0, 0.0) sweep(part, LINTEL[0], [(a.x, a.y), (c.x, c.y)], LINTEL[1]) under = Frame(fr.p(0.0, 4.52, 0.46), fr.u, fr.n, (0.0, 0.0, -1.0)) prism(part, under, rect(-4.1, 4.1, -0.11, 0.11), -0.01, 0.035, "light", back=False) front = Frame.facing(fr.p(0.0, 0.0, 1.15), fr.n) prism(part, front, clipped(-1.7, 1.7, 4.87, 5.23, 0.10, 0.10, 0.10, 0.10), -0.01, 0.045, "orange", cham=0.012, back=False, panel=(0.06, 0.012)) for s in (-1, 1): prism(part, front, rect(*sorted((s * 2.30, s * 2.62)), 4.97, 5.13), -0.01, 0.05, "amber", cham=0.012, back=False) def door_housing(part, lo, hi, thin, n, z_roof, front_from): """The housing a door leaf lifts into. The kit's big leaf is one piece, 6.8 x 4.0 m, and can only go up; over the doorway there is half a metre of wall, so an open leaf stood three metres clear above the roof (the user, 2026-10-14: "they will clip through the walls because there is not enough coverage"). The housing stands on the roof over the slot the leaf lifts through (build/exterior.py door_slots) and takes all of it: four walls and a cap, closed; armour on its long faces, a light strip up the middle, a motor box behind. lo, hi: the closed leaf's box; thin: the axis it is thin along; n: the way its front looks (on the plan); front_from: the height from which the front is free for armour (above a portal's lintel).""" cx, cy = (lo.x + hi.x) / 2.0, (lo.y + hi.y) / 2.0 aw = ((hi.y - lo.y) if thin == "x" else (hi.x - lo.x)) / 2.0 th = ((hi.x - lo.x) if thin == "x" else (hi.y - lo.y)) / 2.0 fr = Frame.facing((cx, cy, 0.0), (n[0], n[1], 0.0)) zt = hi.z + ext.DOOR_TRAVEL + 0.22 # underside of the cap: the lifted leaf's top and some air ca, ch = aw + ext.DOOR_SIDE, th + ext.DOOR_PLAY # the shaft: the slot in the roof wa, wh = ca + 0.14, ch + 0.14 # the walls' outside def fp(pts): return ccw([(fr.p(a, 0.0, h).x, fr.p(a, 0.0, h).y) for a, h in pts]) g = Frame.ground(0.0, 0.0, z_roof - 0.02) height = zt - (z_roof - 0.02) for a0, a1, h0, h1 in ((-wa, wa, ch, wh), (-wa, wa, -wh, -ch), (-wa, -ca, -ch, ch), (ca, wa, -ch, ch)): prism(part, g, fp(rect(a0, a1, h0, h1)), 0.0, height, "white") prism(part, Frame.ground(0.0, 0.0, zt - 0.16), fp(rect(-wa - 0.04, wa + 0.04, -wh - 0.04, wh + 0.04)), 0.0, 0.16, "orange", cham=0.02) prism(part, Frame.ground(0.0, 0.0, zt), fp(clipped(-wa - 0.10, wa + 0.10, -wh - 0.10, wh + 0.10, 0.08, 0.08, 0.08, 0.08)), 0.0, 0.18, "darkmetal", cham=0.05, side="white") for s in (-1, 1): p = fr.p(s * (wa - 0.25), 0.0, 0.0) cylinder(part, (p.x, p.y, zt + 0.16), (p.x, p.y, zt + 0.30), 0.06, "red", sides=8, smooth=False) # armour on the two long faces: four plates, a light strip between the middle two for sign, zb in ((1, max(z_roof + 0.20, front_from)), (-1, z_roof + 0.20)): face = Frame.facing(fr.p(0.0, 0.0, sign * wh), (n[0] * sign, n[1] * sign, 0.0)) z1 = zt - 0.24 if z1 - zb < 0.8: continue for s in (-1, 1): e0, e1 = sorted((s * 0.20, s * (wa - 0.12))) mid = (e0 + e1) / 2.0 for a0, a1 in ((e0, mid - 0.035), (mid + 0.035, e1)): prism(part, face, clipped(a0, a1, zb, z1, 0.0, 0.0, 0.14, 0.14), -0.02, 0.10, "white", cham=0.025, back=False, panel=(0.09, 0.014)) prism(part, face, rect(-0.13, 0.13, zb + 0.10, z1 - 0.10), -0.02, 0.012, "black", back=False) prism(part, face, rect(-0.05, 0.05, zb + 0.25, z1 - 0.25), 0.0, 0.03, "light", cham=0.008, back=False) for s in (-1, 1): end = Frame.facing(fr.p(s * wa, 0.0, 0.0), (fr.u.x * s, fr.u.y * s, 0.0)) prism(part, end, rect(-0.05, 0.05, z_roof + 0.9, zt - 0.6), -0.01, 0.03, "light", cham=0.008, back=False) # the hoist's motor box, behind, on the roof prism(part, Frame.ground(0.0, 0.0, z_roof - 0.02), fp(clipped(-1.25, 1.25, -wh - 0.58, -wh + 0.02, 0.08, 0.08, 0.0, 0.0)), 0.0, 0.92, "white", cham=0.05, panel=(0.16, 0.02), panel_mat="darkmetal") back = Frame.facing(fr.p(0.0, z_roof, -wh - 0.58), (-n[0], -n[1], 0.0)) louvre(part, back, -0.95, 0.95, 0.22, 0.72) return zt + 0.18 def portals(part, b): n = 0 for name, axis, c, s0, s1, e0, e1 in ext.station_plan(b)[3]: sd = ext._Side(axis, c, 1) for s_end, sign, end in ((s0, -1, e0), (s1, 1, e1)): if end[0] == "door": portal(part, sd.p(s_end, 0.0, 0.0), sd.along(sign)) n += 1 portal(part, (20.43, 0.0), (1.0, 0.0), jambs=False, half=5.50) # the cargo hall's end: its corner pylons are the jambs # a housing over every door leaf, tall enough for the leaf when it is up rooms = ext.station_plan(b)[0] housings = 0 for ob, lo, hi, thin in ext.door_leaves(): cx, cy = (lo.x + hi.x) / 2.0, (lo.y + hi.y) / 2.0 h = [r[4] for r in rooms if r[0] < cx < r[1] and r[2] < cy < r[3]] z_roof = max(h) if h else ext.G_ROOF outer = (not h) or abs(cx - 20.43) < 0.8 # in a gallery's end, or the cargo hall's if thin == "x": out = (1.0 if (cx > 0 or h) else -1.0, 0.0) else: out = (0.0, 1.0 if cy > 0 else -1.0) door_housing(part, lo, hi, thin, out, z_roof, 5.62 if outer else z_roof + 0.20) ob["ue_door_travel_m"] = [0.0, 0.0, ext.DOOR_TRAVEL] # how it opens: for the game, and for build/check_doors.py housings += 1 return n + 1, housings # --------------------------------------------------------------------------------------------------------------- # Relay Control's drum def relay_crown(part): cx, cy = ext.CC r0 = ext.CC_BAND_R af = math.atan2(math.sqrt(ext.CC_CAP_R ** 2 - ext.CC_STEP ** 2), -ext.CC_STEP) a0, a1 = af + 0.012, 2.0 * math.pi - af - 0.012 n = 11 step = (a1 - a0) / n gap = 0.20 / 12.3 shield = ([(0.96, 5.10), (1.43, 5.25), (1.73, 5.55), (1.73, 7.30), (1.63, 7.40), (0.96, 7.40)], ["orange", "orange", "white", "darkmetal", "darkmetal", "hull"]) wedge = ([(-0.02, 4.80), (0.98, 5.105), (0.98, 6.68), (-0.02, 6.68)], ["black", "hull", "hull", "hull"]) inner = ([(0.63, 7.02), (0.96, 7.02), (0.96, 7.40), (0.63, 7.40)], ["hull", "hull", "darkmetal", "white"]) for k in range(n): t0, t1 = a0 + k * step + gap / 2.0, a0 + (k + 1) * step - gap / 2.0 seg = 5 path = [(cx + r0 * math.cos(t0 + (t1 - t0) * i / seg), cy + r0 * math.sin(t0 + (t1 - t0) * i / seg)) for i in range(seg + 1)] for prof, mats in (shield, wedge, inner): sweep(part, prof, path, mats) tm = (t0 + t1) / 2.0 rad = (math.cos(tm), math.sin(tm), 0.0) fr = Frame.facing((cx + (r0 + 1.73) * rad[0], cy + (r0 + 1.73) * rad[1], 0.0), rad) if k == n // 2: prism(part, fr, clipped(-0.95, 0.95, 5.85, 6.95, 0.25, 0.25, 0.25, 0.25), -0.05, 0.05, "orange", cham=0.02, back=False, panel=(0.10, 0.014)) elif k % 2 == 1: prism(part, fr, rect(-0.80, 0.80, 6.02, 6.16), -0.04, 0.035, "light", back=False) for k, h in ((2, 4.2), (5, 5.6), (8, 3.6), (10, 2.8)): t = a0 + k * step aerial(part, cx + (r0 + 1.30) * math.cos(t), cy + (r0 + 1.30) * math.sin(t), 7.40, h) # a rail round the skylight rr, m = 4.35, 20 pts = [(cx + rr * math.cos(2.0 * math.pi * i / m), cy + rr * math.sin(2.0 * math.pi * i / m)) for i in range(m)] for x, y in pts: cylinder(part, (x, y, ext.CC_CAP_Z1 - 0.02), (x, y, ext.CC_CAP_Z1 + 1.08), 0.025, "darkmetal", sides=6) for z in (ext.CC_CAP_Z1 + 0.58, ext.CC_CAP_Z1 + 1.08): tube(part, [(x, y, z) for x, y in pts + pts[:1]], 0.022, "darkmetal", sides=6) return n # --------------------------------------------------------------------------------------------------------------- # roofs def roof_unit(part, cx, cy, z0, sx, sy, h, fan=True): """A plant unit on a roof: a white housing on a dark base, louvres on its sides, a fan on top.""" g = Frame.ground(0.0, 0.0, z0) e = 0.06 prism(part, g, clipped(cx - sx / 2 - e, cx + sx / 2 + e, cy - sy / 2 - e, cy + sy / 2 + e, 0.2, 0.2, 0.2, 0.2), -0.02, 0.16, "darkmetal", cham=0.03) prism(part, g, clipped(cx - sx / 2, cx + sx / 2, cy - sy / 2, cy + sy / 2, 0.16, 0.16, 0.16, 0.16), 0.14, h, "white", cham=0.06, panel=(0.22, 0.03), panel_mat="darkmetal") for nx, ny, half, wid in ((1, 0, sx / 2, sy), (-1, 0, sx / 2, sy), (0, 1, sy / 2, sx), (0, -1, sy / 2, sx)): w2 = wid / 2.0 - 0.42 if w2 > 0.25: louvre(part, Frame.facing((cx + nx * half, cy + ny * half, z0), (nx, ny, 0.0)), -w2, w2, 0.40, h - 0.30) if fan: r = min(sx, sy) * 0.28 cylinder(part, (cx, cy, z0 + h - 0.05), (cx, cy, z0 + h + 0.10), r, "grey", sides=16, cham=0.025) cylinder(part, (cx, cy, z0 + h + 0.08), (cx, cy, z0 + h + 0.115), r - 0.07, "black", sides=16) def duct(part, kit, col, p0, p1, roof, tag): """The pack's 0.8 m duct from p0 to p1 at PIPE_Z, in whole pieces (3 m each, stretched a little to fit), on stands at the joints.""" d = Vector((p1[0] - p0[0], p1[1] - p0[1])) length = d.length d = d / length n = max(1, int(round(length / 3.0))) sx = length / (3.0 * n) yaw = math.atan2(d.y, d.x) placed = 0 for i in range(n): x, y = p0[0] + d.x * 3.0 * sx * i, p0[1] + d.y * 3.0 * sx * i m = Matrix.Translation((x, y, PIPE_Z)) @ Matrix.Rotation(yaw, 4, "Z") @ Matrix.Diagonal((sx, 1.0, 1.0, 1.0)) ob = kit.place(LITE + "SM_Pipe_Straight_3m_01", m, col, name="Roof duct") if ob is not None: tag(ob, "outpost: roof ducts (Sci-Fi Lite pack)") placed += 1 for i in range(1, n): x, y = p0[0] + d.x * 3.0 * sx * i, p0[1] + d.y * 3.0 * sx * i zr = roof(x, y) if zr is None: continue g = Frame.ground(x, y, zr, math.degrees(yaw)) top = PIPE_Z - 0.38 - zr for s in (-1, 1): prism(part, g, rect(-0.13, 0.13, *sorted((s * 0.36, s * 0.64))), -0.01, 0.04, "darkmetal") prism(part, g, rect(-0.05, 0.05, *sorted((s * 0.44, s * 0.56))), 0.03, top - 0.10, "darkmetal") prism(part, g, rect(-0.09, 0.09, -0.62, 0.62), top - 0.12, top, "grey", cham=0.02) return placed def roof_works(part, b, kit, col, tag): rooms = ext.station_plan(b)[0] def roof(x, y): h = [r[4] for r in rooms if r[0] <= x <= r[1] and r[2] <= y <= r[3]] return max(h) if h else None hy = b.DOCK_HEAD_Y info = {"ducts": 0, "units": 0} roof_unit(part, 0.0, 0.0, ext.HUB_ROOF_Z, 4.0, 4.0, 1.75) roof_unit(part, 0.0, hy, ext.HUB_ROOF_Z, 3.2, 3.2, 1.45) # each duct runs from inside the crossing's plant unit (its faces are 2.0 m from the middle) into its air handler: # the pack's duct is open at its ends, and a duct that stopped short showed its inside for p0, p1, (ux, uy, usx, usy) in (((0.0, 1.94), (0.0, 29.06), (0.0, 30.3, 1.9, 2.6)), ((-1.94, 0.0), (-9.46, 0.0), (-10.7, 0.0, 2.6, 1.9))): roof_unit(part, ux, uy, roof(ux, uy), usx, usy, 1.95, fan=False) info["ducts"] += duct(part, kit, col, p0, p1, roof, tag) info["units"] += 1 # The cargo hall's air handler is not fed from the crossing: the housing of the door between the crossing and # the hall stands across that line, and a door leaf lifts inside it. Its duct runs west along the hall's roof # and turns down into it with the pack's bend, well short of the housing. roof_unit(part, 17.3, 0.0, roof(17.3, 0.0), 2.6, 1.9, 1.95, fan=False) info["units"] += 1 info["ducts"] += duct(part, kit, col, (16.06, 0.0), (10.06, 0.0), roof, tag) bend = kit.place(LITE + "SM_Pipe_Corner_01", Matrix.Translation((10.06, 0.0, PIPE_Z)) @ Matrix.Rotation(math.pi, 4, "Z"), col, name="Roof duct, bend") if bend is not None: tag(bend, "outpost: roof ducts (Sci-Fi Lite pack)") info["ducts"] += 1 zr = roof(9.08, 0.0) cylinder(part, (9.08, 0.0, zr - 0.02), (9.08, 0.0, PIPE_Z - 0.93), 0.47, "darkmetal", sides=16, cham=0.03) g = Frame.ground(10.06, 0.0, zr, 0.0) for s in (-1, 1): prism(part, g, rect(-0.05, 0.05, *sorted((s * 0.44, s * 0.56))), -0.01, PIPE_Z - 0.48 - zr, "darkmetal") prism(part, g, rect(-0.09, 0.09, -0.62, 0.62), PIPE_Z - 0.50 - zr, PIPE_Z - 0.38 - zr, "grey", cham=0.02) for x, y in ((9.7, 21.0), (9.7, 23.2), (9.7, 25.4), (9.7, 27.6), (-7.4, 16.6), (-7.4, 18.8), (-7.4, 21.0), (-7.4, 23.2)): roof_unit(part, x, y, roof(x, y), 1.5, 1.2, 0.95) info["units"] += 1 for x, y in ((-3.3, 10.6), (3.4, 26.0), (13.8, -3.7), (-11.0, 3.0), (6.5, -27.5), (-6.5, -27.5)): zr = roof(x, y) g = Frame.ground(0.0, 0.0, zr) prism(part, g, clipped(x - 0.65, x + 0.65, y - 0.65, y + 0.65, 0.1, 0.1, 0.1, 0.1), -0.02, 0.26, "darkmetal", cham=0.04, panel=(0.14, 0.02), panel_mat="grey") prism(part, g, rect(x - 0.35, x + 0.35, y - 0.04, y + 0.04), 0.24, 0.32, "orange", cham=0.02) # the mast on the crossing's plant unit z = ext.HUB_ROOF_Z + 1.75 cylinder(part, (0.0, 0.0, z + 0.09), (0.0, 0.0, z + 0.45), 0.16, "darkmetal", sides=10, cham=0.03) cylinder(part, (0.0, 0.0, z + 0.40), (0.0, 0.0, z + 8.6), 0.07, "white", sides=8) for zz, half in ((z + 4.2, 0.9), (z + 6.6, 0.65)): tube(part, [(-half, 0.0, zz), (half, 0.0, zz)], 0.03, "darkmetal", sides=6) tube(part, [(0.0, -half, zz + 0.25), (0.0, half, zz + 0.25)], 0.03, "darkmetal", sides=6) for sx, sy in ((half, 0.0), (-half, 0.0)): cylinder(part, (sx, sy, zz), (sx, sy, zz + 1.5), 0.014, "grey", sides=6) for sx, sy in ((0.0, half), (0.0, -half)): cylinder(part, (sx, sy, zz + 0.25), (sx, sy, zz + 1.2), 0.014, "grey", sides=6) cylinder(part, (0.07, 0.0, z + 2.6), (0.42, 0.0, z + 2.6), 0.50, "white", sides=16, cham=0.03, cap_mat="darkmetal") cylinder(part, (0.0, 0.0, z + 8.6), (0.0, 0.0, z + 8.72), 0.06, "red", sides=6, smooth=False) return info