Polish the bear's thank-you ending screen; add a video debug command
- The finish screen's continue button now reads "Play Again" and starts a fresh run instead of "Continue"-ing to a lobby gate that no longer exists past the run's last fight. - The "Your Time" readout moves from the top (overlapping the bull in thankyou.ogv) to just under the clip's own baked title, restyled to match its flat dark-ink look instead of the gold HUD outline, and pulses on the same ~1.4s breathing cycle sampled off the source clip so it reads as part of the poster rather than a readout stamped on it. - Added a `video <win|loss|finish>` debug console command to preview any outcome screen without playing through a fight, via a new hud.gd debug entry point (`debug_force_outcome`). - Dropped the console's per-frame "focus stolen by ... — reclaiming" log line — its own comment flagged it as temporary diagnostic instrumentation, and it fired on every console open. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MXBNaEBmrYDSMcv4NgUWtV
This commit is contained in:
@@ -3,6 +3,10 @@ extends CanvasLayer
|
||||
const _GOLD := Color(1.00, 0.78, 0.22)
|
||||
const _CREAM := Color(0.93, 0.88, 0.72)
|
||||
const _MUTED := Color(0.66, 0.60, 0.46)
|
||||
# Sampled from thankyou.ogv's baked "THANK YOU FOR PLAYING!" title — a flat dark ink on the
|
||||
# clip's sunburst poster art, no outline/glow. The run-time label mimics it so it reads as
|
||||
# part of that same poster instead of a HUD readout stamped on top.
|
||||
const _INK := Color(0.12, 0.08, 0.02)
|
||||
|
||||
# Per-ability trim colour, tying each tile (and its key cap) to that ability's FX.
|
||||
# Order matches _ABILITY_ICONS: kick, dash, slam, roll.
|
||||
@@ -79,11 +83,15 @@ const _WIN_DELAY := 3.0
|
||||
|
||||
var _game_over: bool = false
|
||||
var _result_win: bool = false
|
||||
# Set per-outcome in _show_game_over: the bear is the run's last fight, so its win screen
|
||||
# offers a fresh run instead of a lobby gate to continue through.
|
||||
var _is_final_boss: bool = false
|
||||
var _over_root: Control
|
||||
var _over_video: VideoStreamPlayer
|
||||
var _over_label: Label
|
||||
var _over_continue: Button
|
||||
var _over_again: Button
|
||||
var _over_label_tween: Tween # the finish screen's breathing "Your Time" pulse
|
||||
|
||||
# Run clock readout — top-left, desktop only (touch's top-left corner is already the
|
||||
# mobile HP readout from mobile_hud.gd). Tracks Run.run_elapsed(); hidden until the timer
|
||||
@@ -95,6 +103,7 @@ var _run_timer_label: Label
|
||||
func _ready() -> void:
|
||||
layer = 11
|
||||
process_mode = Node.PROCESS_MODE_ALWAYS
|
||||
add_to_group(&"hud") # lets the debug console's `video` command find this instance
|
||||
_build_controls_panel()
|
||||
_build_tab_hint()
|
||||
_build_bottom_hud()
|
||||
@@ -387,7 +396,7 @@ func _unhandled_input(event: InputEvent) -> void:
|
||||
if event is InputEventKey and event.pressed and not event.echo:
|
||||
if _game_over and event.physical_keycode in [KEY_ENTER, KEY_KP_ENTER]:
|
||||
if _result_win:
|
||||
_continue_to_lobby()
|
||||
_on_win_button_pressed()
|
||||
else:
|
||||
_restart()
|
||||
elif event.physical_keycode == KEY_TAB and not _game_over:
|
||||
@@ -417,6 +426,24 @@ func _continue_to_lobby() -> void:
|
||||
get_tree().change_scene_to_file(Run.active_level().scene_path)
|
||||
|
||||
|
||||
# The bear is the run's last fight — there's no further lobby gate to continue through, so
|
||||
# the win screen offers a fresh run instead. Mirrors main_menu.gd's Play button.
|
||||
func _play_again() -> void:
|
||||
get_tree().paused = false
|
||||
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
|
||||
Run.start_new_run()
|
||||
get_tree().change_scene_to_file("res://levels/game_shell.tscn")
|
||||
|
||||
|
||||
# Shared by the win-screen button and its Enter-key shortcut, routing to whichever the
|
||||
# current outcome needs (set in _show_game_over).
|
||||
func _on_win_button_pressed() -> void:
|
||||
if _is_final_boss:
|
||||
_play_again()
|
||||
else:
|
||||
_continue_to_lobby()
|
||||
|
||||
|
||||
func _toggle_controls() -> void:
|
||||
_controls_visible = not _controls_visible
|
||||
_toggle_btn.text = "Hide controls" if _controls_visible else "Show controls"
|
||||
@@ -498,16 +525,73 @@ func _show_game_over(win: bool, _cause: String = "") -> void:
|
||||
|
||||
await get_tree().create_timer(_WIN_DELAY if win else _LOSS_DELAY, false, false, true).timeout
|
||||
|
||||
var is_final_boss := _result_win and Run.active_level().id == &"bear"
|
||||
# The thank-you clip already bakes in its own title, so this label carries the
|
||||
# winning time instead — the one thing that clip can't show.
|
||||
_over_label.text = ("Your Time: %s" % _format_time(Run.run_elapsed())) if is_final_boss \
|
||||
_is_final_boss = _result_win and Run.active_level().id == &"bear"
|
||||
_present_outcome()
|
||||
|
||||
|
||||
## Debug-only entry point for the console's `video` command — skips the fight and the
|
||||
## outcome delay, jumping straight to an outcome screen so a clip can be previewed
|
||||
## (`video finish` for thankyou.ogv) without clearing the bear for real. kind: one of
|
||||
## "win" / "loss" / "finish".
|
||||
func debug_force_outcome(kind: String) -> void:
|
||||
if _controls_panel:
|
||||
_controls_panel.visible = false
|
||||
if _tab_hint:
|
||||
_tab_hint.visible = false
|
||||
_game_over = true
|
||||
match kind:
|
||||
"finish":
|
||||
_result_win = true
|
||||
_is_final_boss = true
|
||||
"win":
|
||||
_result_win = true
|
||||
_is_final_boss = false
|
||||
_:
|
||||
_result_win = false
|
||||
_is_final_boss = false
|
||||
_present_outcome()
|
||||
|
||||
|
||||
# Populates and reveals the outcome screen from _result_win / _is_final_boss, set by
|
||||
# either _show_game_over (a real fight ending) or debug_force_outcome (console preview).
|
||||
func _present_outcome() -> void:
|
||||
# The thank-you clip already bakes in its own title, so this label carries the winning
|
||||
# time instead — the one thing that clip can't show. Anchored as a fraction of the
|
||||
# screen (not fixed pixels) at ~83% down — just under the clip's own baked title (which
|
||||
# sits at ~72-81% in the source video) and clear of the bull it frames, instead of the
|
||||
# shared top-of-screen spot "Bull Won!"/"Matadors Won!" use. Styled to match the baked
|
||||
# title's flat dark-ink poster look rather than the gold HUD outline style.
|
||||
_over_label.text = ("Your Time: %s" % _format_time(Run.run_elapsed())) if _is_final_boss \
|
||||
else ("Bull Won!" if _result_win else "Matadors Won!")
|
||||
# A win advances the run (on to the map); a loss ends it (retry from the top).
|
||||
if _is_final_boss:
|
||||
_over_label.anchor_top = 0.83
|
||||
_over_label.anchor_bottom = 0.83
|
||||
_over_label.grow_vertical = Control.GROW_DIRECTION_END
|
||||
_over_label.offset_top = 0.0
|
||||
_over_label.offset_bottom = 0.0
|
||||
_over_label.add_theme_font_size_override("font_size", 40)
|
||||
_over_label.add_theme_color_override("font_color", _INK)
|
||||
_over_label.add_theme_color_override("font_outline_color", Color(0.0, 0.0, 0.0, 0.0))
|
||||
_over_label.add_theme_constant_override("outline_size", 0)
|
||||
_start_over_label_pulse()
|
||||
else:
|
||||
_over_label.anchor_top = 0.0
|
||||
_over_label.anchor_bottom = 0.0
|
||||
_over_label.grow_vertical = Control.GROW_DIRECTION_END
|
||||
_over_label.offset_top = 80.0
|
||||
_over_label.offset_bottom = 0.0
|
||||
_over_label.add_theme_font_size_override("font_size", 64)
|
||||
_over_label.add_theme_color_override("font_color", _GOLD)
|
||||
_over_label.add_theme_color_override("font_outline_color", Color(0.0, 0.0, 0.0, 0.9))
|
||||
_over_label.add_theme_constant_override("outline_size", 6)
|
||||
_stop_over_label_pulse()
|
||||
# A win advances the run (on to the map, or — for the bear, the run's last fight — into
|
||||
# a fresh run); a loss ends it (retry from the top).
|
||||
_over_continue.text = "Play Again (Enter)" if _is_final_boss else "Continue (Enter)"
|
||||
_over_continue.visible = _result_win
|
||||
_over_again.visible = not _result_win
|
||||
|
||||
var path := _THANKYOU_VIDEO if is_final_boss else (_WIN_VIDEO if _result_win else _LOSS_VIDEO)
|
||||
var path := _THANKYOU_VIDEO if _is_final_boss else (_WIN_VIDEO if _result_win else _LOSS_VIDEO)
|
||||
if ResourceLoader.exists(path):
|
||||
_over_video.stream = load(path)
|
||||
_over_video.play()
|
||||
@@ -520,6 +604,29 @@ func _show_game_over(win: bool, _cause: String = "") -> void:
|
||||
create_tween().tween_property(_over_root, "modulate:a", 1.0, 0.5)
|
||||
|
||||
|
||||
# thankyou.ogv's baked title isn't static — it breathes, scaling gently in and out on a loop
|
||||
# (measured off the source clip: roughly a 1.4 s in/out cycle, matching the clip's own loop
|
||||
# length). The "Your Time" label mimics that same pulse so it reads as part of the clip
|
||||
# rather than a HUD readout stamped over it. Scales around the label's own centre (its size
|
||||
# only settles a frame after the anchor/text changes above, hence the deferred pivot read).
|
||||
func _start_over_label_pulse() -> void:
|
||||
_stop_over_label_pulse()
|
||||
_over_label.scale = Vector2.ONE
|
||||
await get_tree().process_frame
|
||||
_over_label.pivot_offset = _over_label.size * 0.5
|
||||
_over_label_tween = create_tween().set_loops()
|
||||
_over_label_tween.set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
||||
_over_label_tween.tween_property(_over_label, "scale", Vector2(1.08, 1.08), 0.7)
|
||||
_over_label_tween.tween_property(_over_label, "scale", Vector2(0.92, 0.92), 0.7)
|
||||
|
||||
|
||||
func _stop_over_label_pulse() -> void:
|
||||
if _over_label_tween:
|
||||
_over_label_tween.kill()
|
||||
_over_label_tween = null
|
||||
_over_label.scale = Vector2.ONE
|
||||
|
||||
|
||||
func _build_game_over() -> void:
|
||||
_over_root = Control.new()
|
||||
_over_root.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
@@ -576,10 +683,11 @@ func _build_game_over() -> void:
|
||||
row.offset_bottom = -48.0
|
||||
_over_root.add_child(row)
|
||||
|
||||
# Win → continue to the run map; loss → restart from the top. Shown per-result in
|
||||
# _show_game_over; both share the Enter shortcut and label.
|
||||
# Win → continue to the run map, or — beating the bear, the run's last fight — start a
|
||||
# fresh run; loss → restart from the top. Text is set per-outcome in _show_game_over;
|
||||
# both routes share the Enter shortcut.
|
||||
_over_continue = _make_button("Continue (Enter)")
|
||||
_over_continue.pressed.connect(_continue_to_lobby)
|
||||
_over_continue.pressed.connect(_on_win_button_pressed)
|
||||
row.add_child(_over_continue)
|
||||
|
||||
_over_again = _make_button("Continue (Enter)")
|
||||
|
||||
Reference in New Issue
Block a user