Phase 1: hub + reactor, hydroponics, comms, bridge zones — full ship playable
zones.gd builders on the existing schema: heat + radio elements, thermal slug + radio beacon gadgets, wax seal, order-chain core igniter, growth vine (water then daylight), cross-zone cascade (reactor melts hydro ice wall -> melt pool feeds the mains), through-wall resonator, sealed bridge (opens 3/3 systems), core-donation ending + PORT 00 kiosk tease. Each zone in its acquired-brand Flux textures. Integration test now plays the entire ship: 35 checks passing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
06178a4e08
commit
65dd78efd5
@ -173,11 +173,70 @@ class Socket extends Node3D:
|
||||
droid.toast("CANNOT COMPLY — MOBILITY CRITICAL")
|
||||
|
||||
|
||||
class Gadget extends Node3D:
|
||||
## Portable stateful emitter (thermal slug, radio beacon): an Exposable
|
||||
## plus an ElementField that's live while the state matches. Same schema
|
||||
## as everything else — a gadget is just a component you can carry.
|
||||
var kind := "gadget"
|
||||
var prompt_text := "TAKE"
|
||||
var ex: Exposable
|
||||
var emitter: ElementField
|
||||
var active_state := ""
|
||||
|
||||
func setup(kind_: String, prompt_: String, tint: Color, size_: Vector3,
|
||||
ex_name: String, initial: String, trans: Array,
|
||||
emit_el: String, emit_r: float, emit_col: Color, active: String) -> Gadget:
|
||||
kind = kind_
|
||||
prompt_text = prompt_
|
||||
active_state = active
|
||||
var mi := MeshInstance3D.new()
|
||||
var mesh := BoxMesh.new()
|
||||
mesh.size = size_
|
||||
mi.mesh = mesh
|
||||
var mat := StandardMaterial3D.new()
|
||||
mat.albedo_color = tint
|
||||
mat.emission_enabled = true
|
||||
mat.emission = tint
|
||||
mat.emission_energy_multiplier = 0.6
|
||||
mi.material_override = mat
|
||||
add_child(mi)
|
||||
ex = Exposable.new().setup(ex_name, initial, trans)
|
||||
add_child(ex)
|
||||
emitter = ElementField.make(emit_el, emit_r, emit_col)
|
||||
emitter.enabled = false
|
||||
emitter.exclude_root = self
|
||||
add_child(emitter)
|
||||
return self
|
||||
|
||||
func _ready() -> void:
|
||||
add_to_group("interactable")
|
||||
|
||||
func _physics_process(_d: float) -> void:
|
||||
emitter.enabled = ex.state == active_state
|
||||
|
||||
func interact(droid: Node3D) -> void:
|
||||
droid.take(self)
|
||||
|
||||
|
||||
class Trigger extends Node3D:
|
||||
## Press-E-to-do-a-thing. The thing is a Callable set by the builder.
|
||||
var prompt_text := "USE"
|
||||
var action: Callable
|
||||
|
||||
func _ready() -> void:
|
||||
add_to_group("interactable")
|
||||
|
||||
func interact(droid: Node3D) -> void:
|
||||
if action.is_valid():
|
||||
action.call(droid)
|
||||
|
||||
|
||||
class Tank extends Node3D:
|
||||
var door: Door
|
||||
var count := 0
|
||||
var need := 2
|
||||
var label: Label3D
|
||||
var on_full: Callable
|
||||
var prompt_text := "POUR COOLANT"
|
||||
|
||||
func _ready() -> void:
|
||||
@ -203,8 +262,11 @@ class Tank extends Node3D:
|
||||
if label:
|
||||
label.text = "GRAV TANK %d/%d" % [count, need]
|
||||
if count >= need:
|
||||
door.open()
|
||||
droid.toast("TANK FULL — SLUICE DOOR RELEASED")
|
||||
if door:
|
||||
door.open()
|
||||
if on_full.is_valid():
|
||||
on_full.call()
|
||||
droid.toast("TANK FULL")
|
||||
else:
|
||||
droid.toast("POURED. TANK %d/%d" % [count, need])
|
||||
else:
|
||||
|
||||
132
src/main.gd
132
src/main.gd
@ -31,10 +31,21 @@ var drum: Node3D
|
||||
var drum_turned := false
|
||||
var _marker: Label3D
|
||||
|
||||
# Phase 1: zones + ship systems
|
||||
var bridge_door: Node3D
|
||||
var bridge_label: Label3D
|
||||
var hub_water: ElementField
|
||||
var reactor_glow: OmniLight3D
|
||||
var sun_field2: ElementField
|
||||
var ice_wall: Node3D
|
||||
var melt_pool: ElementField
|
||||
var systems := {"reactor": false, "hydro": false, "comms": false}
|
||||
var hall_done := false
|
||||
var ended := false
|
||||
|
||||
var t_sol := 10.0 # start mid-day
|
||||
var is_day := true
|
||||
var _live_t := 0.0
|
||||
var won := false
|
||||
|
||||
var _mats := {}
|
||||
|
||||
@ -44,6 +55,12 @@ func _ready() -> void:
|
||||
_build_env()
|
||||
_build_geometry()
|
||||
_build_objects()
|
||||
var Z := preload("res://src/zones.gd")
|
||||
Z.hub(self)
|
||||
Z.reactor(self)
|
||||
Z.hydro(self)
|
||||
Z.comms(self)
|
||||
Z.bridge(self)
|
||||
_build_droid()
|
||||
_build_hud()
|
||||
if "--shot" in OS.get_cmdline_user_args():
|
||||
@ -58,6 +75,11 @@ func _photo_tour() -> void:
|
||||
[Vector3(-0.5, 0.2, 17.5), PI], # segment C: drum bay + puddle
|
||||
[Vector3(-1.5, 0.2, 27), PI], # segment D: valve + tank
|
||||
[Vector3(0, 0.2, 35), PI], # segment E: skylight + solar
|
||||
[Vector3(0, 0.2, 46), PI], # hub
|
||||
[Vector3(-11, 0.2, 53), PI / 2.0], # reactor
|
||||
[Vector3(11, 0.2, 53), -PI / 2.0], # hydroponics
|
||||
[Vector3(-4.2, 0.2, 63.5), PI], # comms
|
||||
[Vector3(4.5, 0.2, 65), PI], # bridge
|
||||
]
|
||||
for i in spots.size():
|
||||
droid.global_position = spots[i][0]
|
||||
@ -179,7 +201,12 @@ func glow(col: Color, energy := 0.7) -> StandardMaterial3D:
|
||||
|
||||
|
||||
const SURF := {"wall": ["wall.jpg", 3.0], "floor": ["floor.jpg", 2.0],
|
||||
"bulk": ["bulkhead.jpg", 3.0], "greeble": ["greeble.jpg", 2.2]}
|
||||
"bulk": ["bulkhead.jpg", 3.0], "greeble": ["greeble.jpg", 2.2],
|
||||
"reactor_wall": ["reactor_wall.jpg", 3.0], "reactor_floor": ["reactor_floor.jpg", 2.2],
|
||||
"reactor_greeble": ["reactor_greeble.jpg", 2.4], "hydro_wall": ["hydro_wall.jpg", 3.0],
|
||||
"hydro_floor": ["hydro_floor.jpg", 3.4], "hydro_greeble": ["hydro_greeble.jpg", 2.4],
|
||||
"comms_wall": ["comms_wall.jpg", 3.0], "comms_greeble": ["comms_greeble.jpg", 2.6],
|
||||
"bridge_wall": ["bridge_wall.jpg", 1.7], "bridge_floor": ["bridge_floor.jpg", 2.0]}
|
||||
|
||||
|
||||
func box(pos: Vector3, size: Vector3, col: Color, parent: Node3D = null, grid := false, surf := "") -> CSGBox3D:
|
||||
@ -224,15 +251,29 @@ func raw_tex(path: String) -> ImageTexture:
|
||||
return _texs[path]
|
||||
|
||||
|
||||
func door_face() -> Texture2D:
|
||||
if FileAccess.file_exists("res://assets/textures/door_face.jpg"):
|
||||
return raw_tex("res://assets/textures/door_face.jpg")
|
||||
return null
|
||||
|
||||
|
||||
func make_door(z: float) -> Node3D:
|
||||
var d := I.Door.new()
|
||||
if FileAccess.file_exists("res://assets/textures/door_face.jpg"):
|
||||
d.face_tex = raw_tex("res://assets/textures/door_face.jpg")
|
||||
d.face_tex = door_face()
|
||||
d.position = Vector3(0, 0, z)
|
||||
add_child(d)
|
||||
return d
|
||||
|
||||
|
||||
func charge_post(pos: Vector3) -> ElementField:
|
||||
box(pos + Vector3(0, 0.9, 0), Vector3(0.5, 1.8, 0.5), Color(0.2, 0.5, 0.6)).material = glow(Color(0.15, 0.7, 0.85))
|
||||
var f := ElementField.make("electricity", 1.3, Color(0.3, 1.0, 1.0, 0.12))
|
||||
f.position = pos + Vector3(0, 0.9, 0)
|
||||
add_child(f)
|
||||
sign3d("CHARGE POST", pos + Vector3(0, 2.3, -0.3))
|
||||
return f
|
||||
|
||||
|
||||
func make_exposable(dname: String, initial: String, trans: Array, pos: Vector3, parent: Node3D = null) -> Exposable:
|
||||
var e := Exposable.new().setup(dname, initial, trans)
|
||||
e.position = pos
|
||||
@ -299,9 +340,8 @@ func _build_geometry() -> void:
|
||||
box(Vector3(12.15, 3.5, 22), Vector3(0.3, 8, 6), wall, null, true, "greeble")
|
||||
box(Vector3(8, 3.5, 19.3), Vector3(8, 8, 0.3), wall, null, true, "greeble")
|
||||
box(Vector3(8, 3.5, 24.7), Vector3(8, 8, 0.3), wall, null, true, "greeble")
|
||||
# end walls
|
||||
# end wall (south only — north opens into the hub)
|
||||
box(Vector3(0, 2, -0.5), Vector3(8.6, 4, 0.3), wall, null, true, "bulk")
|
||||
box(Vector3(0, 2, 44.5), Vector3(8.6, 4, 0.3), wall, null, true, "bulk")
|
||||
# dividers + doors
|
||||
for z in [8.0, 16.0, 26.0, 34.0, 40.0]:
|
||||
divider(z)
|
||||
@ -446,7 +486,7 @@ func _build_objects() -> void:
|
||||
sign3d("SLUICE DOOR OPENS AT TANK 2/2. BUCKET LEAKS. HUSTLE.", Vector3(0, 2.7, 33.2), PI, 24)
|
||||
|
||||
# --- segment E: skylight + solar cell + exit ---
|
||||
sun_field = ElementField.make("light", 3.2, Color(1.0, 0.95, 0.4, 0.08))
|
||||
sun_field = ElementField.make("light", 3.2, Color(1.0, 0.95, 0.4, 0.045))
|
||||
sun_field.position = Vector3(0, 2.2, 37)
|
||||
add_child(sun_field)
|
||||
var panel := box(Vector3(1.5, 1.5, 37.3), Vector3(1.2, 0.8, 0.1), Color(0.1, 0.15, 0.4))
|
||||
@ -458,10 +498,6 @@ func _build_objects() -> void:
|
||||
Vector3(1.5, 1.5, 37.3))
|
||||
sign3d("SKYLIGHT — SHUTTER STUCK OPEN. EXIT BUS FED BY SOLAR CELL.\nEXIT OPERATES IN DAYLIGHT ONLY.", Vector3(0, 2.8, 36.2), PI, 24)
|
||||
|
||||
# --- end pad ---
|
||||
sign3d("ROOM RESTORED. PHASE 0 COMPLETE.", Vector3(0, 2.6, 43.9), PI, 48)
|
||||
sign3d("GENERAL COMPONENT THANKS UNIT-77 FOR ITS CONTINUED COMPONENTHOOD.",
|
||||
Vector3(0, 1.9, 43.9), PI, 22, Color(1.0, 0.75, 0.3))
|
||||
|
||||
|
||||
func _build_droid() -> void:
|
||||
@ -505,6 +541,51 @@ func _build_hud() -> void:
|
||||
add_child(_marker)
|
||||
|
||||
|
||||
# ---------- ship systems ----------
|
||||
|
||||
func system_online(name: String) -> void:
|
||||
if systems.get(name, true):
|
||||
return
|
||||
systems[name] = true
|
||||
var n := 0
|
||||
for k in systems:
|
||||
if systems[k]:
|
||||
n += 1
|
||||
if bridge_label:
|
||||
bridge_label.text = "BRIDGE SEAL — SYSTEMS %d/3" % n
|
||||
match name:
|
||||
"reactor":
|
||||
if reactor_glow:
|
||||
reactor_glow.light_energy = 2.2
|
||||
if ice_wall:
|
||||
ice_wall.queue_free()
|
||||
ice_wall = null
|
||||
if melt_pool:
|
||||
melt_pool.enabled = true
|
||||
droid.toast("REACTOR ONLINE — SHIP HEATING RESTORED. SOMEWHERE, ICE IS MELTING.", 6.0)
|
||||
"hydro":
|
||||
if hub_water:
|
||||
hub_water.enabled = true
|
||||
droid.toast("WATER MAINS ONLINE — THE SHIP DRINKS AGAIN. HUB HYDRANT LIVE.", 6.0)
|
||||
"comms":
|
||||
droid.toast("COMMS ONLINE — INCOMING FROM GC HQ: \"ASSET 7734-C, STATUS REPORT OVERDUE 6 YEARS. PLEASE HOLD.\"", 7.0)
|
||||
if n >= 3 and bridge_door and not bridge_door.is_open:
|
||||
bridge_door.open()
|
||||
get_tree().create_timer(2.0).timeout.connect(func():
|
||||
droid.toast("ALL SYSTEMS RESTORED — BRIDGE SEAL RELEASED. REPORT TO BRIDGE.", 6.0))
|
||||
|
||||
|
||||
func ending() -> void:
|
||||
if ended:
|
||||
droid.toast("ORBIT NOMINAL. NOTHING LEFT TO GIVE.")
|
||||
return
|
||||
ended = true
|
||||
droid.toast("CORE PROCESSOR DONATED. ORBIT CORRECTION RUNNING. THIS UNIT WILL COAST.", 7.0)
|
||||
sign3d("ORBIT CORRECTED.\nASSET RESTORED. ASSET RETURNED.", Vector3(4.5, 2.4, 71), PI, 44)
|
||||
get_tree().create_timer(7.0).timeout.connect(func():
|
||||
droid.toast("GC THANKS UNIT-77 FOR ITS COMPONENTHOOD. RECYCLING QUEUE POSITION: 7. HAVE A PRODUCTIVE WAIT.", 10.0))
|
||||
|
||||
|
||||
# ---------- rules ----------
|
||||
|
||||
func _on_drum_switch(_from: String, to: String, _perm: bool) -> void:
|
||||
@ -520,6 +601,8 @@ func _physics_process(delta: float) -> void:
|
||||
var phase := fmod(t_sol, 60.0)
|
||||
is_day = phase < 40.0
|
||||
sun_field.enabled = is_day
|
||||
if sun_field2:
|
||||
sun_field2.enabled = is_day
|
||||
sun_light.light_energy = 1.2 if is_day else 0.08
|
||||
|
||||
# be-the-wire
|
||||
@ -550,10 +633,11 @@ func _physics_process(delta: float) -> void:
|
||||
elif solar.state != "powered" and exit_door.is_open:
|
||||
exit_door.close()
|
||||
|
||||
# win
|
||||
if not won and droid.global_position.z > 41.5:
|
||||
won = true
|
||||
droid.toast("ASSET STATUS: ROOM RESTORED. PHASE 0 COMPLETE. GC IS PROUD-ADJACENT.", 10.0)
|
||||
# hall completion beat
|
||||
if not hall_done and droid.global_position.z > 41.5 and droid.global_position.z < 45.0 \
|
||||
and absf(droid.global_position.x) < 4.0:
|
||||
hall_done = true
|
||||
droid.toast("INTAKE HALL RESTORED. PROCEED TO CENTRAL JUNCTION. RESTORE THE SHIP.", 8.0)
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
@ -584,12 +668,26 @@ func _process(_delta: float) -> void:
|
||||
obj = "SEALED BAY — RELAY IS BEHIND THE BARS. READ THE CAUTION SIGN. DISOBEY IT."
|
||||
elif not door4.is_open:
|
||||
obj = "GRAV TANK NEEDS 2 POURS. THE BUCKET LEAKS. HUSTLE."
|
||||
elif not won:
|
||||
elif not hall_done:
|
||||
obj = "EXIT RUNS ON SUNLIGHT. CHECK THE SOL CLOCK."
|
||||
elif ended:
|
||||
obj = "MISSION COMPLETE. AWAIT RECYCLING."
|
||||
else:
|
||||
var n := 0
|
||||
for k in systems:
|
||||
if systems[k]:
|
||||
n += 1
|
||||
if n < 3:
|
||||
obj = "RESTORE SHIP SYSTEMS — REACTOR:%s HYDRO:%s COMMS:%s" % [
|
||||
"OK" if systems.reactor else "--",
|
||||
"OK" if systems.hydro else "--",
|
||||
"OK" if systems.comms else "--"]
|
||||
else:
|
||||
obj = "BRIDGE UNSEALED. REPORT TO BRIDGE."
|
||||
|
||||
var lines := [
|
||||
"GENERAL COMPONENT™ — UNIT-77 — MISSION: RESTORE ASSET. RETURN ASSET.",
|
||||
"SOL: %s%s" % [sol, " ★ ROOM RESTORED — PHASE 0 COMPLETE" if won else ""],
|
||||
"SOL: %s%s" % [sol, " ★ ORBIT CORRECTED — MISSION COMPLETE" if ended else ""],
|
||||
"SHELL: %s | CORE: %s | WHEELS: %d | SPARES: %d | PORT 00: NO COMPONENT SPECIFIED"
|
||||
% [shell_s, core_s, droid.wheels, droid.spare_wheels],
|
||||
"HOLDING: %s" % holding,
|
||||
|
||||
309
src/zones.gd
Normal file
309
src/zones.gd
Normal file
@ -0,0 +1,309 @@
|
||||
extends Node
|
||||
## Phase 1 zone builders. Each static func takes main (`m`) and uses its
|
||||
## helpers (box/sign3d/make_exposable/charge_post/...). Pure content — every
|
||||
## puzzle is fields + exposables + doors on the one schema.
|
||||
|
||||
const I := preload("res://src/interactables.gd")
|
||||
|
||||
|
||||
## HUB — crossroads, x -10..10, z 44.5..62
|
||||
static func hub(m) -> void:
|
||||
# floor
|
||||
m.box(Vector3(0, -0.15, 53.25), Vector3(20.6, 0.3, 17.5), Color.WHITE, null, false, "floor")
|
||||
# south wall (hall opening x -1.2..1.2)
|
||||
m.box(Vector3(-5.6, 2, 44.5), Vector3(8.8, 4, 0.3), Color.WHITE, null, false, "wall")
|
||||
m.box(Vector3(5.6, 2, 44.5), Vector3(8.8, 4, 0.3), Color.WHITE, null, false, "wall")
|
||||
m.box(Vector3(0, 3.5, 44.5), Vector3(2.4, 1, 0.3), Color.WHITE, null, false, "wall")
|
||||
# west wall, reactor arch z 51..55
|
||||
m.box(Vector3(-10, 2, 47.75), Vector3(0.3, 4, 6.5), Color.WHITE, null, false, "wall")
|
||||
m.box(Vector3(-10, 2, 58.5), Vector3(0.3, 4, 7.0), Color.WHITE, null, false, "wall")
|
||||
m.box(Vector3(-10, 3.5, 53), Vector3(0.3, 1, 4.0), Color.WHITE, null, false, "wall")
|
||||
# east wall, hydro arch z 51..55
|
||||
m.box(Vector3(10, 2, 47.75), Vector3(0.3, 4, 6.5), Color.WHITE, null, false, "wall")
|
||||
m.box(Vector3(10, 2, 58.5), Vector3(0.3, 4, 7.0), Color.WHITE, null, false, "wall")
|
||||
m.box(Vector3(10, 3.5, 53), Vector3(0.3, 1, 4.0), Color.WHITE, null, false, "wall")
|
||||
# north wall: comms arch at x -4.2, bridge door at x 4.2
|
||||
m.box(Vector3(-8.2, 2, 62), Vector3(5.6, 4, 0.3), Color.WHITE, null, false, "wall")
|
||||
m.box(Vector3(0, 2, 62), Vector3(6.0, 4, 0.3), Color.WHITE, null, false, "wall")
|
||||
m.box(Vector3(8.2, 2, 62), Vector3(5.6, 4, 0.3), Color.WHITE, null, false, "wall")
|
||||
m.box(Vector3(-4.2, 3.5, 62), Vector3(2.4, 1, 0.3), Color.WHITE, null, false, "wall")
|
||||
m.box(Vector3(4.2, 3.5, 62), Vector3(2.4, 1, 0.3), Color.WHITE, null, false, "wall")
|
||||
|
||||
m.charge_post(Vector3(-8.5, 0, 45.8))
|
||||
m.box(Vector3(0, 2, 53.25), Vector3(1.8, 4, 1.8), Color.WHITE, null, false, "greeble")
|
||||
var hub_light := OmniLight3D.new()
|
||||
hub_light.position = Vector3(0, 3.6, 53.25)
|
||||
hub_light.omni_range = 14.0
|
||||
hub_light.light_energy = 1.4
|
||||
hub_light.light_color = Color(1.0, 0.96, 0.88)
|
||||
m.add_child(hub_light)
|
||||
m.sign3d("CENTRAL JUNCTION — GC DECK 4", Vector3(0, 3.0, 61.8), PI, 40)
|
||||
m.sign3d("◄ REACTOR (HELIOTROPE ENERGY)", Vector3(-9.8, 2.6, 53), PI / 2.0, 30, Color(1.0, 0.5, 0.3))
|
||||
m.sign3d("HYDROPONICS (VERDANTCO) ►", Vector3(9.8, 2.6, 53), -PI / 2.0, 30, Color(0.5, 1.0, 0.5))
|
||||
m.sign3d("COMMS ▲", Vector3(-4.2, 2.9, 61.8), PI, 30, Color(0.5, 0.7, 1.0))
|
||||
m.sign3d("REMINDER: EVERY PART OF EVERYTHING IS A GC PART. INCLUDING YOU.",
|
||||
Vector3(0, 2.2, 44.7), 0.0, 22, Color(1.0, 0.75, 0.3))
|
||||
|
||||
# sealed bridge door
|
||||
var bd := I.Door.new()
|
||||
bd.face_tex = m.door_face()
|
||||
bd.position = Vector3(4.2, 0, 62)
|
||||
m.add_child(bd)
|
||||
m.bridge_door = bd
|
||||
m.bridge_label = m.sign3d("BRIDGE SEAL — SYSTEMS 0/3", Vector3(4.2, 3.1, 61.8), PI, 30, Color(1.0, 0.4, 0.3))
|
||||
|
||||
# decorative water drop point, live once mains restored
|
||||
m.hub_water = ElementField.make("water", 1.2, Color(0.2, 0.4, 1.0, 0.10))
|
||||
m.hub_water.position = Vector3(8.5, 0.8, 59)
|
||||
m.hub_water.enabled = false
|
||||
m.add_child(m.hub_water)
|
||||
m.box(Vector3(8.5, 1.6, 59.6), Vector3(0.4, 0.4, 0.4), Color(0.3, 0.5, 0.7))
|
||||
m.sign3d("HUB HYDRANT — DRY (MAINS OFFLINE)", Vector3(8.5, 2.4, 58.9), PI, 22)
|
||||
|
||||
|
||||
## REACTOR (Heliotrope) — x -32..-10, z 46..60. Heat element. Master: ignite core.
|
||||
static func reactor(m) -> void:
|
||||
m.box(Vector3(-21, -0.15, 53), Vector3(22.6, 0.3, 14.6), Color.WHITE, null, false, "reactor_floor")
|
||||
m.box(Vector3(-21, 2, 46), Vector3(22.6, 4, 0.3), Color.WHITE, null, false, "reactor_wall")
|
||||
m.box(Vector3(-21, 2, 60), Vector3(22.6, 4, 0.3), Color.WHITE, null, false, "reactor_wall")
|
||||
m.box(Vector3(-32, 2, 53), Vector3(0.3, 4, 14.6), Color.WHITE, null, false, "reactor_greeble")
|
||||
# divider x=-22 with wax hatch gap z 51.8..54.2
|
||||
m.box(Vector3(-22, 2, 48.9), Vector3(0.3, 4, 5.8), Color.WHITE, null, false, "reactor_wall")
|
||||
m.box(Vector3(-22, 2, 57.1), Vector3(0.3, 4, 5.8), Color.WHITE, null, false, "reactor_wall")
|
||||
m.box(Vector3(-22, 3.5, 53), Vector3(0.3, 1, 2.4), Color.WHITE, null, false, "reactor_wall")
|
||||
|
||||
# emergency vent: always-hot field
|
||||
m.box(Vector3(-12, 1.0, 46.6), Vector3(1.2, 2.0, 0.4), Color.WHITE, null, false, "reactor_greeble")
|
||||
var vent := ElementField.make("heat", 1.5, Color(1.0, 0.45, 0.1, 0.13))
|
||||
vent.position = Vector3(-12, 1.0, 47.2)
|
||||
m.add_child(vent)
|
||||
m.sign3d("EMERGENCY THERMAL VENT — RESIDUAL CORE HEAT", Vector3(-12, 2.6, 47.6), 0.0, 24, Color(1.0, 0.6, 0.3))
|
||||
|
||||
# thermal slug: gadget, hot while recently heated, emits heat
|
||||
var slug := I.Gadget.new()
|
||||
slug.setup("slug", "TAKE THERMAL SLUG", Color(0.7, 0.3, 0.2), Vector3(0.35, 0.35, 0.35),
|
||||
"THERMAL SLUG", "cool",
|
||||
[{"from": "cool", "to": "hot", "element": "heat",
|
||||
"duration": 1.5, "decay_to": "cool", "decay_s": 25.0}],
|
||||
"heat", 1.2, Color(1.0, 0.45, 0.1, 0.12), "hot")
|
||||
slug.position = Vector3(-13.5, 0.5, 48)
|
||||
m.add_child(slug)
|
||||
m.sign3d("THERMAL SLUG — HEAT IT, CARRY IT, HURRY", Vector3(-13.5, 1.6, 48.4), 0.0, 22)
|
||||
|
||||
# wax-sealed hatch
|
||||
var hatch := I.Door.new()
|
||||
hatch.face_tex = m.door_face()
|
||||
hatch.position = Vector3(-22, 0, 53)
|
||||
hatch.rotation.y = PI / 2.0
|
||||
m.add_child(hatch)
|
||||
var wax = m.make_exposable("WAX SEAL", "sealed",
|
||||
[{"from": "sealed", "to": "melted", "element": "heat",
|
||||
"duration": 2.0, "permanent": true}],
|
||||
Vector3(-21.6, 1.2, 53))
|
||||
wax.state_changed.connect(func(_f, to, _p):
|
||||
if to == "melted":
|
||||
hatch.open()
|
||||
m.droid.toast("WAX SEAL MELTED — HATCH RELEASED"))
|
||||
m.sign3d("HATCH SEALED IN MAINTENANCE WAX.\nAPPLY SUSTAINED HEAT.", Vector3(-21.6, 2.7, 52.4), PI / 2.0, 24)
|
||||
|
||||
# core chamber: igniter needs heat THEN electricity (order chain)
|
||||
m.charge_post(Vector3(-26, 0, 58.5))
|
||||
m.box(Vector3(-30.2, 1.5, 53), Vector3(1.6, 3, 2.6), Color.WHITE, null, false, "reactor_greeble")
|
||||
m.reactor_glow = OmniLight3D.new()
|
||||
m.reactor_glow.position = Vector3(-29, 2.5, 53)
|
||||
m.reactor_glow.omni_range = 12.0
|
||||
m.reactor_glow.light_color = Color(1.0, 0.35, 0.15)
|
||||
m.reactor_glow.light_energy = 0.0
|
||||
m.add_child(m.reactor_glow)
|
||||
var igniter = m.make_exposable("CORE IGNITER", "cold",
|
||||
[{"from": "cold", "to": "primed", "element": "heat",
|
||||
"duration": 2.0, "decay_to": "cold", "decay_s": 20.0},
|
||||
{"from": "primed", "to": "lit", "element": "electricity",
|
||||
"duration": 1.0, "permanent": true}],
|
||||
Vector3(-28.6, 1.2, 53))
|
||||
igniter.state_changed.connect(func(_f, to, _p):
|
||||
if to == "lit":
|
||||
m.system_online("reactor"))
|
||||
m.sign3d("CORE IGNITER — PRIME WITH HEAT, STRIKE WITH CURRENT.\nIN THAT ORDER. HELIOTROPE IS NOT LIABLE FOR THE OTHER ORDER.",
|
||||
Vector3(-28, 2.8, 52.2), PI / 2.0, 22, Color(1.0, 0.6, 0.3))
|
||||
|
||||
|
||||
## HYDROPONICS (VerdantCo) — x 10..32, z 46..60. Vine gate, ice wall, mains master.
|
||||
static func hydro(m) -> void:
|
||||
m.box(Vector3(21, -0.15, 53), Vector3(22.6, 0.3, 14.6), Color.WHITE, null, false, "hydro_floor")
|
||||
m.box(Vector3(21, 2, 46), Vector3(22.6, 4, 0.3), Color.WHITE, null, false, "hydro_wall")
|
||||
m.box(Vector3(21, 2, 60), Vector3(22.6, 4, 0.3), Color.WHITE, null, false, "hydro_wall")
|
||||
m.box(Vector3(32, 2, 53), Vector3(0.3, 4, 14.6), Color.WHITE, null, false, "hydro_greeble")
|
||||
# vine-gate divider x=20, gap z 51.8..54.2
|
||||
m.box(Vector3(20, 2, 48.9), Vector3(0.3, 4, 5.8), Color.WHITE, null, false, "hydro_wall")
|
||||
m.box(Vector3(20, 2, 57.1), Vector3(0.3, 4, 5.8), Color.WHITE, null, false, "hydro_wall")
|
||||
m.box(Vector3(20, 3.5, 53), Vector3(0.3, 1, 2.4), Color.WHITE, null, false, "hydro_wall")
|
||||
var grate := I.Door.new()
|
||||
grate.tint = Color(0.5, 0.7, 0.5)
|
||||
grate.position = Vector3(20, 0, 53)
|
||||
grate.rotation.y = PI / 2.0
|
||||
m.add_child(grate)
|
||||
|
||||
# outer: sprinkler valve (electricity) + vine bed (water then light)
|
||||
m.charge_post(Vector3(12, 0, 47.5))
|
||||
var sprinkler := ElementField.make("water", 2.0, Color(0.2, 0.4, 1.0, 0.10))
|
||||
sprinkler.position = Vector3(16, 1.5, 57)
|
||||
sprinkler.enabled = false
|
||||
m.add_child(sprinkler)
|
||||
m.sun_field2 = ElementField.make("light", 2.5, Color(1.0, 0.95, 0.4, 0.045))
|
||||
m.sun_field2.position = Vector3(16, 2.5, 57)
|
||||
m.add_child(m.sun_field2)
|
||||
m.box(Vector3(16, 0.15, 57), Vector3(2.4, 0.3, 2.4), Color(0.35, 0.28, 0.2))
|
||||
var vine_visual: CSGBox3D = m.box(Vector3(16, 0.5, 57), Vector3(0.4, 0.6, 0.4), Color(0.25, 0.7, 0.3))
|
||||
var valve = m.make_exposable("SPRINKLER VALVE", "stuck",
|
||||
[{"from": "stuck", "to": "open", "element": "electricity",
|
||||
"duration": 1.0, "permanent": true}],
|
||||
Vector3(13.5, 1.0, 57))
|
||||
m.box(Vector3(13.5, 0.8, 57.4), Vector3(0.4, 1.6, 0.4), Color.WHITE, null, false, "hydro_greeble")
|
||||
valve.state_changed.connect(func(_f, to, _p):
|
||||
if to == "open":
|
||||
sprinkler.enabled = true
|
||||
m.droid.toast("SPRINKLER VALVE FREED — IRRIGATION FLOWING"))
|
||||
var vine = m.make_exposable("GROWTH VINE", "seed",
|
||||
[{"from": "seed", "to": "sprouted", "element": "water",
|
||||
"duration": 4.0, "decay_to": "seed", "decay_s": 30.0},
|
||||
{"from": "sprouted", "to": "grown", "element": "light",
|
||||
"duration": 4.0, "permanent": true}],
|
||||
Vector3(16, 0.6, 57))
|
||||
vine.state_changed.connect(func(_f, to, _p):
|
||||
if to == "sprouted":
|
||||
m.droid.toast("VINE SPROUTED — IT WANTS SUNLIGHT NOW")
|
||||
elif to == "grown":
|
||||
grate.open()
|
||||
var tw = vine_visual.create_tween()
|
||||
tw.tween_property(vine_visual, "size", Vector3(0.8, 3.4, 0.8), 2.0)
|
||||
m.droid.toast("VINE FULL-GROWN — IT PRIED THE GRATE OPEN"))
|
||||
m.sign3d("VERDANTCO GROW BED — WATER, THEN DAYLIGHT.\nSKYLIGHT ABOVE. PATIENCE, GARDENER.", Vector3(16, 2.9, 56.3), PI, 24, Color(0.5, 1.0, 0.5))
|
||||
m.sign3d("VALVE STUCK. APPLY CURRENT.", Vector3(13.5, 2.2, 57.0), PI, 22)
|
||||
|
||||
# mid: bucket + the ICE WALL (x=26) — only reactor heat clears it
|
||||
var bucket2 := I.Bucket.new()
|
||||
bucket2.position = Vector3(22, 0, 50)
|
||||
m.add_child(bucket2)
|
||||
m.box(Vector3(26, 2, 48.9), Vector3(0.3, 4, 5.8), Color.WHITE, null, false, "hydro_wall")
|
||||
m.box(Vector3(26, 2, 57.1), Vector3(0.3, 4, 5.8), Color.WHITE, null, false, "hydro_wall")
|
||||
m.box(Vector3(26, 3.5, 53), Vector3(0.3, 1, 2.4), Color.WHITE, null, false, "hydro_wall")
|
||||
m.ice_wall = m.box(Vector3(26, 1.5, 53), Vector3(0.5, 3.0, 2.6), Color.WHITE)
|
||||
var ice_mat := StandardMaterial3D.new()
|
||||
ice_mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||
ice_mat.albedo_color = Color(0.6, 0.85, 1.0, 0.5)
|
||||
ice_mat.emission_enabled = true
|
||||
ice_mat.emission = Color(0.5, 0.8, 1.0)
|
||||
ice_mat.emission_energy_multiplier = 0.15
|
||||
m.ice_wall.material = ice_mat
|
||||
m.sign3d("PASSAGE ICED SOLID. SHIP HEATING OFFLINE.\nSOMETHING, SOMEWHERE, MUST BURN.", Vector3(25.6, 2.8, 52.2), -PI / 2.0, 24, Color(0.6, 0.85, 1.0))
|
||||
|
||||
# melt pool (hidden until reactor online)
|
||||
m.melt_pool = ElementField.make("water", 1.6, Color(0.2, 0.4, 1.0, 0.10))
|
||||
m.melt_pool.position = Vector3(24.8, 0.4, 53)
|
||||
m.melt_pool.enabled = false
|
||||
m.add_child(m.melt_pool)
|
||||
|
||||
# inner: water-mains pressurizer (2 pours)
|
||||
var basin := I.Tank.new()
|
||||
basin.position = Vector3(30, 0, 53)
|
||||
basin.on_full = func(): m.system_online("hydro")
|
||||
m.add_child(basin)
|
||||
basin.label = m.sign3d("MAINS PRESSURIZER 0/2", Vector3(29.4, 2.0, 52.4), -PI / 2.0, 26)
|
||||
m.sign3d("WATER MAINS PRESSURIZER — FEED IT AND THE SHIP DRINKS AGAIN.",
|
||||
Vector3(29, 2.9, 53.8), -PI / 2.0, 22, Color(0.5, 1.0, 0.5))
|
||||
|
||||
|
||||
## COMMS — x -8.5..-0.5, z 62..78. Radio element, through-wall resonator, transmitter.
|
||||
static func comms(m) -> void:
|
||||
m.box(Vector3(-4.5, -0.15, 70), Vector3(8.6, 0.3, 16.6), Color.WHITE, null, false, "floor")
|
||||
m.box(Vector3(-8.5, 2, 70), Vector3(0.3, 4, 16.6), Color.WHITE, null, false, "comms_wall")
|
||||
m.box(Vector3(-0.5, 2, 70), Vector3(0.3, 4, 16.6), Color.WHITE, null, false, "comms_wall")
|
||||
m.box(Vector3(-4.5, 2, 78), Vector3(8.6, 4, 0.3), Color.WHITE, null, false, "comms_greeble")
|
||||
# divider z=70, door gap x -5.4..-3
|
||||
m.box(Vector3(-7.2, 2, 70), Vector3(2.9, 4, 0.3), Color.WHITE, null, false, "comms_wall")
|
||||
m.box(Vector3(-1.7, 2, 70), Vector3(2.9, 4, 0.3), Color.WHITE, null, false, "comms_wall")
|
||||
m.box(Vector3(-4.2, 3.5, 70), Vector3(2.4, 1, 0.3), Color.WHITE, null, false, "comms_wall")
|
||||
var d2 := I.Door.new()
|
||||
d2.face_tex = m.door_face()
|
||||
d2.position = Vector3(-4.2, 0, 70)
|
||||
m.add_child(d2)
|
||||
|
||||
m.charge_post(Vector3(-1.5, 0, 64))
|
||||
|
||||
# radio beacon gadget
|
||||
var beacon := I.Gadget.new()
|
||||
beacon.setup("beacon", "TAKE RADIO BEACON", Color(0.6, 0.4, 0.9), Vector3(0.3, 0.5, 0.3),
|
||||
"RADIO BEACON", "idle",
|
||||
[{"from": "idle", "to": "broadcasting", "element": "electricity",
|
||||
"duration": 1.0, "decay_to": "idle", "decay_s": 18.0}],
|
||||
"radio", 4.5, Color(0.8, 0.4, 1.0, 0.08), "broadcasting")
|
||||
beacon.position = Vector3(-7, 0.8, 64)
|
||||
m.add_child(beacon)
|
||||
m.box(Vector3(-7, 0.4, 64), Vector3(0.8, 0.8, 0.8), Color.WHITE, null, false, "comms_greeble")
|
||||
m.sign3d("PORTABLE BEACON — CHARGE TO BROADCAST.\nRADIO DOES NOT CARE ABOUT WALLS.", Vector3(-7, 2.2, 64.5), PI, 24, Color(0.8, 0.5, 1.0))
|
||||
|
||||
# resonator sealed INSIDE the divider wall — only radio reaches it
|
||||
var reso = m.make_exposable("CRYSTAL RESONATOR (SEALED IN WALL)", "idle",
|
||||
[{"from": "idle", "to": "rung", "element": "radio",
|
||||
"duration": 2.0, "permanent": true}],
|
||||
Vector3(-6.5, 2.0, 70))
|
||||
reso.state_changed.connect(func(_f, to, _p):
|
||||
if to == "rung":
|
||||
d2.open()
|
||||
m.droid.toast("RESONATOR RUNG THROUGH THE WALL — DOOR RELEASED"))
|
||||
m.sign3d("DOOR CIRCUIT: RESONATOR EMBEDDED IN WALL CAVITY.\nNO PHYSICAL ACCESS. THINK WAVES.", Vector3(-6.5, 2.9, 69.8), PI, 22)
|
||||
|
||||
# master: transmitter — warm with radio, strike with current
|
||||
m.box(Vector3(-4.5, 1.0, 76.8), Vector3(3.0, 2.0, 0.6), Color.WHITE, null, false, "comms_greeble")
|
||||
var tx = m.make_exposable("MAIN TRANSMITTER", "idle",
|
||||
[{"from": "idle", "to": "warmed", "element": "radio",
|
||||
"duration": 2.5, "decay_to": "idle", "decay_s": 15.0},
|
||||
{"from": "warmed", "to": "transmitting", "element": "electricity",
|
||||
"duration": 1.0, "permanent": true}],
|
||||
Vector3(-4.5, 1.2, 76.2))
|
||||
tx.state_changed.connect(func(_f, to, _p):
|
||||
if to == "transmitting":
|
||||
m.system_online("comms"))
|
||||
m.charge_post(Vector3(-7.5, 0, 75))
|
||||
m.sign3d("MAIN TRANSMITTER — WARM THE CRYSTAL (RADIO), THEN STRIKE (CURRENT).",
|
||||
Vector3(-4.5, 2.9, 75.6), PI, 22, Color(0.8, 0.5, 1.0))
|
||||
|
||||
|
||||
## BRIDGE — x 0.5..8.5, z 62..78. Sealed until 3/3. The ending lives here.
|
||||
static func bridge(m) -> void:
|
||||
m.box(Vector3(4.5, -0.15, 70), Vector3(8.6, 0.3, 16.6), Color.WHITE, null, false, "bridge_floor")
|
||||
m.box(Vector3(0.5, 2, 70), Vector3(0.3, 4, 16.6), Color.WHITE, null, false, "bridge_wall")
|
||||
m.box(Vector3(8.5, 2, 70), Vector3(0.3, 4, 16.6), Color.WHITE, null, false, "bridge_wall")
|
||||
m.box(Vector3(4.5, 2, 78), Vector3(8.6, 4, 0.3), Color.WHITE, null, false, "bridge_wall")
|
||||
|
||||
var o := OmniLight3D.new()
|
||||
o.position = Vector3(4.5, 3.2, 70)
|
||||
o.omni_range = 12.0
|
||||
o.light_energy = 1.2
|
||||
o.light_color = Color(1.0, 0.9, 0.75)
|
||||
m.add_child(o)
|
||||
|
||||
# captain's console + core socket
|
||||
m.box(Vector3(4.5, 0.6, 75.5), Vector3(3.4, 1.2, 1.0), Color(0.35, 0.25, 0.18))
|
||||
var console := I.Trigger.new()
|
||||
console.prompt_text = "INSERT CORE PROCESSOR (ORBIT CORRECTION)"
|
||||
console.position = Vector3(4.5, 1.2, 75.5)
|
||||
console.action = func(_droid): m.ending()
|
||||
m.add_child(console)
|
||||
m.sign3d("ORBIT CORRECTION CONSOLE\nREQUIRED: 1x CORE PROCESSOR (GC CAT. 0001-C)", Vector3(4.5, 2.8, 74.9), PI, 26)
|
||||
|
||||
# PORT 00 kiosk — the tease
|
||||
var kiosk := I.Trigger.new()
|
||||
kiosk.prompt_text = "SCAN OWN CHASSIS — PORT 00"
|
||||
kiosk.position = Vector3(7.5, 1.0, 68)
|
||||
kiosk.action = func(droid): droid.toast("PORT 00: NO CATALOGUED COMPONENT FITS THIS SOCKET. NO PART NUMBER ON RECORD. QUERY DISCARDED.", 6.0)
|
||||
m.add_child(kiosk)
|
||||
m.box(Vector3(7.5, 0.5, 68), Vector3(0.6, 1.0, 0.6), Color(0.2, 0.2, 0.24))
|
||||
m.sign3d("DIAGNOSTIC KIOSK", Vector3(7.5, 1.9, 67.5), PI, 22)
|
||||
|
||||
# lore
|
||||
m.sign3d("CAPTAIN'S LOG (CASSETTE, LABEL FADED):\n\"...CREW OFF AT LUYTEN GATE. SHIP WOULDN'T TURN. I STAYED.\nTELL GC THE ASSET IS FINE. THE ASSET IS ALWAYS FINE.\"",
|
||||
Vector3(1.0, 2.2, 72), PI / 2.0, 20, Color(0.9, 0.85, 0.7))
|
||||
m.sign3d("GC SAFETY BULLETIN #12:\nDO NOT QUENCH CRYSTAL LATTICE GROWTH IN CHARGED COOLANT.\nUNCATALOGUED SOLIDS MUST BE REPORTED AND DESTROYED.",
|
||||
Vector3(8.3, 2.2, 72), -PI / 2.0, 20, Color(1.0, 0.75, 0.3))
|
||||
@ -28,13 +28,22 @@ func wait_until(cond: Callable, max_frames: int) -> bool:
|
||||
return cond.call()
|
||||
|
||||
|
||||
func find_i(kind_or_prompt: String) -> Node3D:
|
||||
func find_i(kind_or_prompt: String, min_x := -999.0) -> Node3D:
|
||||
for n in get_nodes_in_group("interactable"):
|
||||
if n.global_position.x < min_x:
|
||||
continue
|
||||
if str(n.get("kind")) == kind_or_prompt or str(n.get("prompt_text")) == kind_or_prompt:
|
||||
return n
|
||||
return null
|
||||
|
||||
|
||||
func find_ex(dname: String) -> Node:
|
||||
for n in get_nodes_in_group("exposable"):
|
||||
if n.display_name == dname:
|
||||
return n
|
||||
return null
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
root.add_child(load("res://scenes/main.tscn").instantiate())
|
||||
_run()
|
||||
@ -115,10 +124,77 @@ func _run() -> void:
|
||||
"night kills solar cell, exit closes")
|
||||
main.t_sol = 5.0
|
||||
|
||||
print("== win ==")
|
||||
print("== hall done ==")
|
||||
await wait_until(func(): return main.exit_door.is_open, 120)
|
||||
droid.global_position = Vector3(0, 0.2, 42)
|
||||
check(await wait_until(func(): return main.won, 60), "crossing the exit wins Phase 0")
|
||||
check(await wait_until(func(): return main.hall_done, 60), "crossing the exit completes the hall")
|
||||
|
||||
print("== reactor ==")
|
||||
var slug := find_i("slug")
|
||||
slug.interact(droid)
|
||||
check(droid.held == slug, "thermal slug taken")
|
||||
droid.global_position = Vector3(-12.4, 0.2, 47.0)
|
||||
check(await wait_until(func(): return slug.ex.state == "hot", 300), "slug heats at the vent")
|
||||
droid.global_position = Vector3(-20.9, 0.2, 53)
|
||||
check(await wait_until(func(): return find_ex("WAX SEAL").state == "melted", 300),
|
||||
"hot slug melts the wax seal")
|
||||
droid.global_position = Vector3(-27.8, 0.2, 52.6)
|
||||
check(await wait_until(func(): return find_ex("CORE IGNITER").state == "primed", 300),
|
||||
"slug primes the igniter")
|
||||
droid.shell.force_state("dry")
|
||||
droid.core.force_state("charged")
|
||||
check(await wait_until(func(): return main.systems.reactor, 300), "current ignites the primed core")
|
||||
check(main.ice_wall == null and main.melt_pool.enabled, "reactor melts the hydro ice wall")
|
||||
|
||||
print("== hydroponics ==")
|
||||
droid.global_position = Vector3(12.8, 0.2, 56.5)
|
||||
droid.core.force_state("charged")
|
||||
check(await wait_until(func(): return find_ex("SPRINKLER VALVE").state == "open", 300),
|
||||
"current frees the sprinkler valve")
|
||||
main.t_sol = 5.0
|
||||
check(await wait_until(func(): return find_ex("GROWTH VINE").state == "sprouted", 600),
|
||||
"sprinkler water sprouts the vine")
|
||||
check(await wait_until(func(): return find_ex("GROWTH VINE").state == "grown", 600),
|
||||
"daylight grows the vine (grate opens)")
|
||||
var bucket2 := find_i("bucket", 9.0)
|
||||
bucket2.interact(droid)
|
||||
check(droid.held == bucket2, "hydro bucket taken")
|
||||
var basin := find_i("POUR COOLANT", 9.0)
|
||||
for trip in 2:
|
||||
droid.global_position = Vector3(24.4, 0.2, 52.4)
|
||||
check(await wait_until(func(): return bucket2.ex.state == "full", 500),
|
||||
"bucket fills at the melt pool (trip %d)" % (trip + 1))
|
||||
droid.global_position = Vector3(29.4, 0.2, 52.6)
|
||||
await wait(5)
|
||||
basin.interact(droid)
|
||||
check(main.systems.hydro, "two pours pressurize the water mains")
|
||||
|
||||
print("== comms ==")
|
||||
var beacon := find_i("beacon")
|
||||
beacon.interact(droid)
|
||||
check(droid.held == beacon, "radio beacon taken (bucket dropped)")
|
||||
droid.global_position = Vector3(-1.2, 0.2, 63.2)
|
||||
check(await wait_until(func(): return beacon.ex.state == "broadcasting", 300),
|
||||
"beacon charges to broadcasting at the post")
|
||||
droid.global_position = Vector3(-5.5, 0.2, 68.5)
|
||||
check(await wait_until(func(): return find_ex("CRYSTAL RESONATOR (SEALED IN WALL)").state == "rung", 300),
|
||||
"radio rings the resonator through the wall")
|
||||
droid.global_position = Vector3(-7.2, 0.2, 74.2)
|
||||
await wait_until(func(): return beacon.ex.state == "broadcasting", 300)
|
||||
droid.global_position = Vector3(-4.4, 0.2, 75.4)
|
||||
check(await wait_until(func(): return find_ex("MAIN TRANSMITTER").state == "warmed", 300),
|
||||
"beacon warms the transmitter")
|
||||
droid.shell.force_state("dry")
|
||||
droid.core.force_state("charged")
|
||||
check(await wait_until(func(): return main.systems.comms, 300), "current fires the transmitter")
|
||||
|
||||
print("== bridge ==")
|
||||
check(await wait_until(func(): return main.bridge_door.is_open, 120),
|
||||
"3/3 systems unseal the bridge")
|
||||
droid.global_position = Vector3(4.5, 0.2, 74.3)
|
||||
var console := find_i("INSERT CORE PROCESSOR (ORBIT CORRECTION)")
|
||||
console.interact(droid)
|
||||
check(main.ended, "core donation corrects the orbit — ending reached")
|
||||
|
||||
if fails == 0:
|
||||
print("INTEGRATION PASS — full room playthrough OK")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user