#102/#103 (#97d/#97e): the outpost, and the garrison you compose for it

Landed together because an outpost you cannot crew is decoration, and a
garrison menu with no building to hang it off is nothing at all.

#102, the building. Researched at "Forward Bases", two prerequisites deep
and cheaper than any mid-game structure - the ticket asks for early, and the
answer to waves attacking your buildings should arrive at about the time the
waves can hurt them. It is drawn as a watch mast on a tripod with two open
berths, deliberately the least machinery of any structure here: it makes
nothing and refines nothing, it holds a position. 120hp, more than a
crafter, because being shot at is its job.

#103, the garrison. Two separate things live on its record, and conflating
them was the first thing to get wrong: the ORDER, what the player asked for,
and the CREW, who is actually there. This menu edits the order only; #104
fills the crew. Keeping the order as a wish is what lets an outpost ask for
a hull nobody has built yet and wait for it, rather than the request
evaporating - and it is why the panel shows "here / ordered" instead of one
number.

"Only ships that attack can be assigned" is expressed as capabilities rather
than a list of types, so a new armed vessel is eligible the day it is added
and an unarmed one never is by accident. Today that means the interceptor
and the laser vessel; a shield, a hauler and an engine block cannot hold a
position however much one might want them to.

Two berths by default, and Garrison Bays raises it at outposts ALREADY
standing - the same shape as freight_yards for hubs, because a bonus that
only reached new buildings would mean demolishing and rebuilding to collect
it.

The panel builds its own rows from the catalog rather than from nodes placed
in the scene, so adding an armed vessel later needs no scene surgery.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Loïc Wisniewski 2026-08-23 14:02:20 +02:00
commit 3c632276af
12 changed files with 909 additions and 1 deletions

View file

@ -26,6 +26,22 @@ const TRANSPORT_HUB_COLOR: Color = Color(0.95, 0.55, 0.85)
const SHIP_LAB_COLOR: Color = Color(0.55, 0.95, 0.9)
const GAS_BURNER_COLOR: Color = Color(0.95, 0.35, 0.15)
const GAS_BURNER_ENERGY_COLOR: Color = Color(0.85, 0.95, 0.15)
# #102. Olive rather than another blue or amber: every existing structure is
# a place you go to DO something, and this is the one you leave people at.
const OUTPOST_COLOR: Color = Color(0.72, 0.86, 0.45)
# Outpost: a watch mast on a tripod footing, with two open berths beside it
# where a garrison sits. Deliberately the least machinery of any structure
# here - it makes nothing and refines nothing, it just holds a position -
# and deliberately not radial, so it cannot be mistaken for a small hub.
const OUTPOST_MAST_TOP: float = -84.0
const OUTPOST_MAST_FOOT: float = 46.0
const OUTPOST_HEAD_RADIUS: float = 20.0
const OUTPOST_FOOT_SPAN: float = 62.0
const OUTPOST_FOOT_DROP: float = 26.0
const OUTPOST_BERTH_X: float = 74.0
const OUTPOST_BERTH_RADIUS: float = 22.0
const OUTPOST_VANE_COUNT: int = 3
# Ship lab: a test frame with a vessel held in it and instruments sweeping
# over it. Reads as a workshop rather than a factory - the crafter already
@ -359,6 +375,47 @@ func _draw_command_center() -> void:
NeonDraw.draw_glowing_polygon(self, _diamond(Vector2.ZERO, CMD_SCREEN_RADIUS), readout, 0.9)
func _draw_outpost() -> void:
var c: Color = OUTPOST_COLOR
var lit: Color = Color(0.94, 0.99, 0.82)
# Tripod footing.
var foot: Vector2 = Vector2(0.0, OUTPOST_MAST_FOOT)
for side in [-1.0, 0.0, 1.0]:
var toe: Vector2 = Vector2(OUTPOST_FOOT_SPAN * side, OUTPOST_MAST_FOOT + OUTPOST_FOOT_DROP)
NeonDraw.draw_glowing_line(self, foot, toe, c, 0.7, 0.42)
NeonDraw.draw_glowing_line(self,
Vector2(-OUTPOST_FOOT_SPAN, OUTPOST_MAST_FOOT + OUTPOST_FOOT_DROP),
Vector2(OUTPOST_FOOT_SPAN, OUTPOST_MAST_FOOT + OUTPOST_FOOT_DROP), c, 0.55, 0.34)
# The mast, with rungs so it reads as climbable structure rather than a
# drawn line.
var top: Vector2 = Vector2(0.0, OUTPOST_MAST_TOP)
NeonDraw.draw_glowing_line(self, foot, top, c, 0.9, 0.5)
for i in range(4):
var y: float = lerpf(OUTPOST_MAST_FOOT, OUTPOST_MAST_TOP + OUTPOST_HEAD_RADIUS,
float(i + 1) / 5.0)
NeonDraw.draw_glowing_line(self, Vector2(-11.0, y), Vector2(11.0, y), c, 0.45, 0.28)
# Sensor head: a lit diamond inside a cage, with vanes looking outward.
NeonDraw.draw_glowing_polygon(self, _ring(top, OUTPOST_HEAD_RADIUS, 6, PI / 6.0), c, 1.0)
NeonDraw.draw_glowing_polygon(self, _diamond(top, OUTPOST_HEAD_RADIUS * 0.5), lit, 0.95)
for i in range(OUTPOST_VANE_COUNT):
var a: float = PI + (float(i) / float(OUTPOST_VANE_COUNT - 1)) * PI
var dir: Vector2 = Vector2(cos(a), sin(a))
NeonDraw.draw_glowing_line(self, top + dir * OUTPOST_HEAD_RADIUS,
top + dir * (OUTPOST_HEAD_RADIUS + 16.0), c, 0.7, 0.3)
# Two open berths. Open, not enclosed: what sits in them belongs to the
# player and flies away, so they are cradles rather than hangars.
for side in [-1.0, 1.0]:
var berth: Vector2 = Vector2(OUTPOST_BERTH_X * side, 4.0)
NeonDraw.draw_glowing_path(self, _arc(berth, OUTPOST_BERTH_RADIUS,
-PI * 0.5, PI * 0.5, 10), c, 0.75, 0.36)
NeonDraw.draw_glowing_line(self, Vector2(12.0 * side, 4.0),
berth - Vector2(OUTPOST_BERTH_RADIUS * side, 0.0), c, 0.6, 0.34)
# --- mothership: a radial flagship ----------------------------------------
# Not a big fighter. A mothership is the thing everything else orbits, so it
# is built radially around a reactor core rather than pointed in a direction: