#125: slow the prologue down, and give the breach a real black hole
Five things, all of them "this happens too fast or looks too thin". PACING. Every beat had a completion test that could be satisfied while the guide was still mid-sentence, which is why everything landed seconds after the thing before it. Beats now have a FLOOR as well as a deadline - a minimum length - and a beat whose point is the guide talking will not end while a line is still on screen. One rule in one place rather than tuning each condition. The run is 3400px instead of 1500, which at cruise was about four seconds, and a held line is 6.5s instead of 4.5. THE CONVOY. Escorts at the four corners of a box, holds packed in the middle of it, roughly three times the spread of the fleet's own wedge - this is freight under guard, not a fighting formation. CONVOY and CONVOY_FORMATION pair index-for-index, which is all it takes for spawn_ships to seat the guns on the outside, and the test pins that every hold sits further in than the furthest-out escort. THE DIALOG. Cut corners, brackets holding the short edges open, a tick strip along the bottom and a caret in the corner - geometry rather than art, so it scales with the banner and costs nothing. THE BREACH. It was a few lines turning around a point. It is now two halves driven by one number: DimensionalBreach, a growing black hole out in the world built on BlackHoleVisual's idiom - empty core, counter-rotating arc segments hotter toward the centre - with infalling streamers it does not have, because a planet's disc is steady and this one has to say "now"; and breach.gdshader over the top, bending the ACTUAL SCENE around it. Ships, rocks and the starfield all get dragged in, which is most of what makes a hole in space read as a hole rather than as a decal drawn on one. Inside the horizon the shader samples nothing at all - the same decision BlackHoleVisual makes, for the same reason: a black hole with something visible in the middle stops being one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
7d344b2a0a
commit
d455361328
8 changed files with 527 additions and 57 deletions
|
|
@ -21,6 +21,11 @@ extends SceneTree
|
|||
# I. the handover: the flag clears, the buttons come back, the spawner and
|
||||
# the asteroid field are running again, and the fleet is a new game's
|
||||
# J. skipping lands in the same state as playing it through
|
||||
# K. #125 - no beat ends before its own floor, and a beat whose point is
|
||||
# the guide talking does not end mid-sentence
|
||||
# L. #125 - the convoy is spread out, with its holds surrounded by escorts
|
||||
# M. #125 - the breach is a growing black hole with a lens over it, not a
|
||||
# few lines turning around a point
|
||||
|
||||
var game_state: Node
|
||||
var scene: Node
|
||||
|
|
@ -85,6 +90,18 @@ func _process(delta: float) -> bool:
|
|||
if not director.running:
|
||||
return _fail("the director did not start on a new game")
|
||||
|
||||
# --- K. nothing ends before its floor --------------------
|
||||
# The floors are what #125 bought: every beat had a condition
|
||||
# that could be satisfied while the guide was still talking.
|
||||
for b in range(director.Beat.AFTERMATH + 1):
|
||||
if director._floor_time() <= 0.0 and b != director.Beat.RUN:
|
||||
return _fail("beat %d has no minimum length" % b)
|
||||
if director.beat != director.Beat.BRIEF:
|
||||
return _fail("the opening beat was over before the first line finished")
|
||||
if not narration.is_speaking():
|
||||
return _fail("the opening beat is silent, so waiting for it proves nothing")
|
||||
print("opening beat still running at %.1fs, guide still talking" % director.beat_time)
|
||||
|
||||
# --- C. the convoy --------------------------------------
|
||||
convoy_size = fleet.ships.size()
|
||||
if convoy_size <= GameState.BASE_FLEET_SIZE:
|
||||
|
|
@ -104,6 +121,35 @@ func _process(delta: float) -> bool:
|
|||
return _fail("the convoy has no escorts at all")
|
||||
print("convoy: %d vessels, %d holds and %d escorts" % [convoy_size, holds, escorts])
|
||||
|
||||
# --- L. spread out, and the holds are on the inside -------
|
||||
var spread: float = 0.0
|
||||
var closest: float = 1.0e9
|
||||
for a in fleet.ships:
|
||||
for b in fleet.ships:
|
||||
if a == b:
|
||||
continue
|
||||
spread = maxf(spread, a.position.distance_to(b.position))
|
||||
closest = minf(closest, a.position.distance_to(b.position))
|
||||
if spread < 400.0:
|
||||
return _fail("the whole convoy fits in %.0fpx - that is a wedge, not a convoy" % spread)
|
||||
if closest < 80.0:
|
||||
return _fail("two convoy vessels are %.0fpx apart" % closest)
|
||||
# Every hold must be further in than the furthest-out escort is,
|
||||
# which is what "surrounded by the default vessels" means.
|
||||
var escort_reach: float = 0.0
|
||||
var hold_reach: float = 0.0
|
||||
for ship in fleet.ships:
|
||||
var from_centre: float = ship.position.length()
|
||||
if String(ship.vessel_type) == GameState.VESSEL_INTERCEPTOR:
|
||||
escort_reach = maxf(escort_reach, from_centre)
|
||||
else:
|
||||
hold_reach = maxf(hold_reach, from_centre)
|
||||
if hold_reach >= escort_reach:
|
||||
return _fail("a hold sits %.0fpx out, past the escorts at %.0f - they are not screened" \
|
||||
% [hold_reach, escort_reach])
|
||||
print("convoy %.0fpx across, holds within %.0f and escorts out at %.0f" \
|
||||
% [spread, hold_reach, escort_reach])
|
||||
|
||||
# --- B. movement and nothing else -----------------------
|
||||
for pressable in ["UI/BuildButton", "UI/MapButton", "UI/BuildActionButton",
|
||||
"UI/BuildingActionButton", "UI/EnterAtmosphereButton",
|
||||
|
|
@ -138,7 +184,10 @@ func _process(delta: float) -> bool:
|
|||
stage = 2
|
||||
return false
|
||||
2:
|
||||
if elapsed < 0.3:
|
||||
# Past the ambush beat's own floor (#125): clearing it is what
|
||||
# ENDS it, but no beat may end before its floor, so watching for
|
||||
# the advance means waiting that long.
|
||||
if elapsed < director._floor_time() + 0.5:
|
||||
return false
|
||||
# The director advances itself, so the evidence that clearing the
|
||||
# ambush ended the beat is that it has MOVED ON - asking
|
||||
|
|
@ -177,6 +226,28 @@ func _process(delta: float) -> bool:
|
|||
return _fail("player steering still moved the fleet during the breach")
|
||||
print("the breach has the wheel: steering input moves nothing")
|
||||
|
||||
# --- M. the breach is a black hole ----------------------
|
||||
var hole: Node2D = director.breach
|
||||
if hole == null or not is_instance_valid(hole):
|
||||
return _fail("the breach beat opened no black hole")
|
||||
if hole.horizon_radius() > 1.0:
|
||||
return _fail("the hole was already open the moment the beat began")
|
||||
director._drive_breach(0.5)
|
||||
var half_open: float = hole.horizon_radius()
|
||||
director._drive_breach(1.0)
|
||||
if hole.horizon_radius() <= half_open:
|
||||
return _fail("the hole does not grow - %.1f at half, %.1f at full" \
|
||||
% [half_open, hole.horizon_radius()])
|
||||
if hole.disc_radius() <= hole.horizon_radius():
|
||||
return _fail("the accretion disc is inside the horizon")
|
||||
var lens: ColorRect = scene.get_node("UI/BreachOverlay")
|
||||
if not lens.visible:
|
||||
return _fail("the screen-space lens never came up")
|
||||
if lens.material == null or lens.material.shader == null:
|
||||
return _fail("the lens has no shader on it")
|
||||
print("breach: horizon %.0f -> %.0f, disc %.0f, lens up" \
|
||||
% [half_open, hole.horizon_radius(), hole.disc_radius()])
|
||||
|
||||
# --- H. the vortex --------------------------------------
|
||||
director._begin(director.Beat.VORTEX)
|
||||
elapsed = 0.0
|
||||
|
|
@ -226,6 +297,10 @@ func _process(delta: float) -> bool:
|
|||
return _fail("the build button never came back")
|
||||
if narration.is_speaking():
|
||||
return _fail("the guide is still talking after the handover")
|
||||
if scene.get_node("UI/BreachOverlay").visible:
|
||||
return _fail("the breach lens is still over the screen after the handover")
|
||||
if is_instance_valid(director) and director.breach != null:
|
||||
return _fail("the black hole outlived the prologue")
|
||||
print("handover: flags clear, 1 interceptor, waves and rocks running, buttons back")
|
||||
_drop_scene()
|
||||
elapsed = 0.0
|
||||
|
|
|
|||
Loading…
Reference in a new issue