diff --git a/scenes/end_screen.gd b/scenes/end_screen.gd index 32cac19..e249d1e 100644 --- a/scenes/end_screen.gd +++ b/scenes/end_screen.gd @@ -24,16 +24,19 @@ func _apply_loss_palette(): var border := Color(1, 0.3, 0.3, 1) var subtle := Color(0.95, 0.7, 0.7, 1) - var card_style = $Center/Card.get_theme_stylebox("panel") as StyleBoxFlat + var card_style := ($Center/Card.get_theme_stylebox("panel") as StyleBoxFlat).duplicate() as StyleBoxFlat if card_style: card_style.border_color = border card_style.bg_color = Color(0.18, 0.05, 0.05, 1) + $Center/Card.add_theme_stylebox_override("panel", card_style) var button = $Center/Card/Margin/Column/RestartButton - var button_style = button.get_theme_stylebox("normal") as StyleBoxFlat + var button_style := (button.get_theme_stylebox("normal") as StyleBoxFlat).duplicate() as StyleBoxFlat if button_style: button_style.border_color = border button_style.bg_color = Color(0.3, 0.1, 0.1, 1) + for state in ["normal", "hover", "pressed", "focus"]: + button.add_theme_stylebox_override(state, button_style) button.add_theme_color_override("font_color", border) $Center/Card/Margin/Column/Headline.add_theme_color_override("font_color", border) diff --git a/scenes/screen.gd b/scenes/screen.gd index b47da16..3d2152a 100644 --- a/scenes/screen.gd +++ b/scenes/screen.gd @@ -13,12 +13,23 @@ var pulse_gap_min := 0.3 var block_zone_shrink := 3.0 var block_zone_min := 12.0 +const TRAIL_GHOSTS := 8 +const TRAIL_SPACING := 3.0 +const TRAIL_ALPHAS := [0.5, 0.42, 0.34, 0.27, 0.2, 0.14, 0.09, 0.05] +const GLOW_PADDING := Vector2(6, 6) +const GLOW_COLOR := Color(1, 0.3, 0.3, 0.35) + # Visual var base_color: Color # State var active := false var pulses_blocked := 0 +var _ready_pulse_tween: Tween = null +var _trail: Array[ColorRect] = [] +var _glow: ColorRect +var _face_scale_tween: Tween +var _face_shake_tween: Tween signal pulse_blocked signal doors_closing @@ -41,6 +52,23 @@ func _ready(): face.size = Vector2(size.x, 30) face.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER face.text = ">:)" + face.pivot_offset = face.size / 2 + + _glow = ColorRect.new() + _glow.color = GLOW_COLOR + _glow.size = Vector2($SweepLine.size.x + GLOW_PADDING.x * 2, $SweepLine.size.y + GLOW_PADDING.y * 2) + _glow.visible = false + add_child(_glow) + move_child(_glow, 0) + + for i in TRAIL_GHOSTS: + var ghost := ColorRect.new() + ghost.color = Color($SweepLine.color.r, $SweepLine.color.g, $SweepLine.color.b, TRAIL_ALPHAS[i]) + ghost.size = $SweepLine.size + ghost.visible = false + add_child(ghost) + move_child(ghost, 0) + _trail.append(ghost) # Duplicate the StyleBoxFlat so flash tweens don't bleed into other panels var style = get_theme_stylebox("panel") as StyleBoxFlat @@ -62,7 +90,8 @@ func start(): func stop(): active = false pulse_active = false - $SweepLine.visible = false + _hide_pulse_visuals() + _stop_ready_pulse() func shrink_block_zone(): var bz = $TargetZone @@ -80,7 +109,11 @@ func launch_pulse(): pulse_active = true $SweepLine.visible = true $SweepLine.position.x = 0 + _glow.visible = true + for ghost in _trail: + ghost.visible = true $AIFace.text = ">:)" + _start_ready_pulse() func get_pulse_gap() -> float: return pulse_gap @@ -91,12 +124,18 @@ func _process(delta): pulse_x += pulse_speed * delta $SweepLine.position.x = pulse_x + _glow.position = Vector2(pulse_x - GLOW_PADDING.x, $SweepLine.position.y - GLOW_PADDING.y) + for i in _trail.size(): + var gx = pulse_x - (i + 1) * TRAIL_SPACING + _trail[i].visible = gx >= 0 + _trail[i].position = Vector2(gx, $SweepLine.position.y) # Pulse reached the far side unblocked: AI wins, doors close if pulse_x >= size.x: pulse_active = false - $SweepLine.visible = false + _hide_pulse_visuals() $AIFace.text = ":D" + _punch_face(1.5, 0.0) flash(Color.RED) active = false doors_closing.emit() @@ -116,16 +155,18 @@ func attempt_block() -> bool: pulses_blocked += 1 pulse_speed += pulse_speed_increase pulse_gap = max(pulse_gap - pulse_gap_decrease, pulse_gap_min) - $SweepLine.visible = false + _hide_pulse_visuals() $AIFace.text = ">:(" + _punch_face(1.4, 4.0) flash(Color.GREEN) pulse_blocked.emit() return true else: # Mistimed: same result as letting it through pulse_active = false - $SweepLine.visible = false + _hide_pulse_visuals() $AIFace.text = ":D" + _punch_face(1.5, 0.0) flash(Color.RED) active = false doors_closing.emit() @@ -172,3 +213,41 @@ func show_onboarding(msg: String): func end_onboarding(): $AIFace.add_theme_font_size_override("font_size", 32) $AIFace.size = Vector2(size.x, 30) + +func _start_ready_pulse(): + if _ready_pulse_tween: + _ready_pulse_tween.kill() + $TargetZone.modulate.a = 1.0 + _ready_pulse_tween = create_tween() + _ready_pulse_tween.set_loops() + _ready_pulse_tween.tween_property($TargetZone, "modulate:a", 0.6, 0.5) + _ready_pulse_tween.tween_property($TargetZone, "modulate:a", 1.0, 0.5) + +func _stop_ready_pulse(): + if _ready_pulse_tween: + _ready_pulse_tween.kill() + _ready_pulse_tween = null + $TargetZone.modulate.a = 1.0 + +func _hide_pulse_visuals(): + $SweepLine.visible = false + _glow.visible = false + for ghost in _trail: + ghost.visible = false + +func _punch_face(scale_amount: float, shake_amount: float): + if _face_scale_tween: + _face_scale_tween.kill() + if _face_shake_tween: + _face_shake_tween.kill() + var face := $AIFace + face.position = Vector2(0, 0) + face.scale = Vector2(scale_amount, scale_amount) + _face_scale_tween = create_tween() + _face_scale_tween.tween_property(face, "scale", Vector2.ONE, 0.25).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT) + if shake_amount > 0: + _face_shake_tween = create_tween() + _face_shake_tween.tween_property(face, "position:x", -shake_amount, 0.04) + _face_shake_tween.tween_property(face, "position:x", shake_amount, 0.04) + _face_shake_tween.tween_property(face, "position:x", -shake_amount * 0.5, 0.04) + _face_shake_tween.tween_property(face, "position:x", 0.0, 0.04)