@@ -61,6 +61,9 @@ const SCORE_PULSE_SECONDS := 0.72
6161const HIGH_SCORE_PULSE_SECONDS := 1.18
6262const HIGH_SCORE_SPARKLE_COUNT := 14
6363const INVENTORY_FRESH_SECONDS := 1.55
64+ const IDLE_HINT_DELAY_SECONDS := 5.0
65+ const IDLE_HINT_PULSE_SECONDS := 2.0
66+ const IDLE_HINT_MAX_SCALE := 1.22
6467const INVENTORY_SLOT_SCALE := 1.28
6568const INVENTORY_CELL_SCALE := 1.10
6669const INVENTORY_CELL_Y_OFFSET := 0.06
@@ -149,6 +152,9 @@ var _hint_inventory_cursor := 0
149152var _hint_board_cursor := 0
150153var _hint_next_inventory_board := true
151154var _hint_text := ""
155+ var _idle_hint_elapsed := 0.0
156+ var _idle_hint_pulse_elapsed := IDLE_HINT_PULSE_SECONDS
157+ var _idle_hint_disabled_after_hint := false
152158var _score := 0
153159var _game_over := false
154160var _full_board_pending_check := false
@@ -213,8 +219,9 @@ func _process(delta: float) -> void:
213219 return
214220 var score_pulse_active := _advance_score_pulse (delta )
215221 var high_score_pulse_active := _advance_high_score_pulse (delta )
222+ var idle_hint_active := _advance_idle_hint_nudge (delta )
216223 var inventory_fresh_active := _has_inventory_fresh_animation ()
217- var hud_pulse_active := score_pulse_active or high_score_pulse_active
224+ var hud_pulse_active := score_pulse_active or high_score_pulse_active or idle_hint_active
218225 if _is_clear_effect_active ():
219226 _reset_full_board_game_over_grace ()
220227 _clear_effect_elapsed += maxf (delta , 0.0 )
@@ -423,6 +430,7 @@ func _start_run() -> void:
423430 _hint_board_cursor = 0
424431 _hint_next_inventory_board = true
425432 _hint_text = ""
433+ _enable_idle_hint_nudge ()
426434 _status_text = ""
427435 _clear_effect_ids .clear ()
428436 _clear_effect_elapsed = 0.0
@@ -934,6 +942,8 @@ func _clear_qualifying_groups() -> bool:
934942 var points := cleared_count + maxi (0 , cleared_count - CLEAR_GROUP_MIN_SIZE )
935943 var previous_high_score := int (Global .high_score )
936944 _score += points
945+ if _score > 4 :
946+ _reset_idle_hint_nudge ()
937947 Global .add_score (points )
938948 if _score > previous_high_score and int (Global .high_score ) > previous_high_score :
939949 _run_had_new_high_score = true
@@ -1116,6 +1126,7 @@ func _show_game_over() -> void:
11161126 if _game_over :
11171127 return
11181128 _game_over = true
1129+ _reset_idle_hint_nudge ()
11191130 _reset_full_board_game_over_grace ()
11201131 Global .record_last_score (_score )
11211132 _update_hud_text ()
@@ -1516,6 +1527,104 @@ func _reset_high_score_pulse_visual() -> void:
15161527 _high_score_label .add_theme_constant_override ("outline_size" , 3 )
15171528
15181529
1530+ func _idle_hint_should_pause () -> bool :
1531+ if _score > 4 :
1532+ return true
1533+ if _visual_profile_enabled :
1534+ return true
1535+ if _drag_source != DRAG_SOURCE_NONE :
1536+ return true
1537+ return _game_over or _is_clear_effect_active ()
1538+
1539+
1540+ func _advance_idle_hint_nudge (delta : float ) -> bool :
1541+ if _idle_hint_disabled_after_hint :
1542+ if _is_idle_hint_nudge_active ():
1543+ _reset_idle_hint_nudge ()
1544+ return true
1545+ return false
1546+ if _idle_hint_should_pause ():
1547+ if _is_idle_hint_nudge_active ():
1548+ _reset_idle_hint_nudge ()
1549+ return true
1550+ return false
1551+ if _is_idle_hint_nudge_active ():
1552+ _idle_hint_pulse_elapsed = minf (IDLE_HINT_PULSE_SECONDS , _idle_hint_pulse_elapsed + maxf (delta , 0.0 ))
1553+ _apply_idle_hint_nudge_visual ()
1554+ if _idle_hint_pulse_elapsed >= IDLE_HINT_PULSE_SECONDS :
1555+ _reset_idle_hint_button_visual ()
1556+ _idle_hint_elapsed = 0.0
1557+ return true
1558+ _idle_hint_elapsed += maxf (delta , 0.0 )
1559+ if _idle_hint_elapsed >= IDLE_HINT_DELAY_SECONDS :
1560+ _start_idle_hint_nudge ()
1561+ return true
1562+ return false
1563+
1564+
1565+ func _start_idle_hint_nudge () -> void :
1566+ _idle_hint_pulse_elapsed = 0.0
1567+ _apply_idle_hint_nudge_visual ()
1568+
1569+
1570+ func _reset_idle_hint_nudge () -> void :
1571+ _idle_hint_elapsed = 0.0
1572+ _idle_hint_pulse_elapsed = IDLE_HINT_PULSE_SECONDS
1573+ _reset_idle_hint_button_visual ()
1574+
1575+
1576+ func _enable_idle_hint_nudge () -> void :
1577+ _idle_hint_disabled_after_hint = false
1578+ _reset_idle_hint_nudge ()
1579+
1580+
1581+ func _is_idle_hint_nudge_active () -> bool :
1582+ return _idle_hint_pulse_elapsed < IDLE_HINT_PULSE_SECONDS
1583+
1584+
1585+ func _idle_hint_nudge_amount () -> float :
1586+ if not _is_idle_hint_nudge_active ():
1587+ return 0.0
1588+ var progress := clampf (_idle_hint_pulse_elapsed / IDLE_HINT_PULSE_SECONDS , 0.0 , 1.0 )
1589+ return sin (progress * PI )
1590+
1591+
1592+ func _apply_idle_hint_nudge_visual () -> void :
1593+ if not is_instance_valid (_hint_button ):
1594+ return
1595+ var pulse := _idle_hint_nudge_amount ()
1596+ var scale := 1.0 + (IDLE_HINT_MAX_SCALE - 1.0 ) * pulse
1597+ _hint_button .pivot_offset = _hint_button .size * 0.5
1598+ _hint_button .scale = Vector2 (scale , scale )
1599+ _hint_button .add_theme_color_override ("font_color" , Color (1.0 , 0.95 , 0.38 , 1.0 ).lerp (Color (0.92 , 1.0 , 0.96 , 1.0 ), 1.0 - pulse * 0.55 ))
1600+ _hint_button .add_theme_constant_override ("outline_size" , 3 + int (roundf (pulse * 3.0 )))
1601+
1602+
1603+ func _reset_idle_hint_button_visual () -> void :
1604+ if not is_instance_valid (_hint_button ):
1605+ return
1606+ _hint_button .scale = Vector2 .ONE
1607+ _hint_button .pivot_offset = _hint_button .size * 0.5
1608+ _hint_button .add_theme_color_override ("font_color" , Color (0.92 , 1.0 , 0.96 , 1.0 ))
1609+ _hint_button .add_theme_constant_override ("outline_size" , 3 )
1610+
1611+
1612+ func _draw_idle_hint_nudge () -> void :
1613+ if not is_instance_valid (_hint_button ) or not _is_idle_hint_nudge_active ():
1614+ return
1615+ var pulse := _idle_hint_nudge_amount ()
1616+ if pulse <= 0.0 :
1617+ return
1618+ var rect := Rect2 (_hint_button .position , _hint_button .size )
1619+ var center := rect .get_center ()
1620+ var radius := maxf (rect .size .x , rect .size .y ) * (0.54 + pulse * 0.24 )
1621+ var teal := Color (0.36 , 1.0 , 0.78 , 0.18 + pulse * 0.18 )
1622+ var gold := Color (1.0 , 0.88 , 0.22 , 0.22 + pulse * 0.24 )
1623+ draw_circle (center , radius * 1.22 , Color (teal .r , teal .g , teal .b , teal .a * pulse ))
1624+ draw_circle (center , radius , Color (gold .r , gold .g , gold .b , gold .a * pulse ))
1625+ draw_rect (rect .grow (4.0 + pulse * 8.0 ), Color (1.0 , 0.90 , 0.22 , 0.10 + pulse * 0.18 ), false , 2.0 + pulse * 2.0 )
1626+
1627+
15191628func _make_panel_style (fill : Color , border : Color , border_width : int , radius : int ) -> StyleBoxFlat :
15201629 var style := StyleBoxFlat .new ()
15211630 style .bg_color = fill
@@ -2397,6 +2506,7 @@ func _renderer_needs() -> Dictionary:
23972506func _draw () -> void :
23982507 var view_size := get_viewport_rect ().size
23992508 draw_rect (Rect2 (Vector2 .ZERO , view_size ), Color (0.025 , 0.055 , 0.065 , 1.0 ), true )
2509+ _draw_idle_hint_nudge ()
24002510 _sync_board_renderer ()
24012511 if is_instance_valid (_overlay_layer ):
24022512 _overlay_layer .queue_redraw ()
@@ -2540,6 +2650,7 @@ func _finish_pinch() -> void:
25402650func _begin_drag (screen_pos : Vector2 , touch_id : int = - 1 ) -> bool :
25412651 var board_cell := _board_cell_at (screen_pos )
25422652 if not board_cell .is_empty ():
2653+ _reset_idle_hint_nudge ()
25432654 _clear_hint (false )
25442655 _drag_source = DRAG_SOURCE_BOARD
25452656 _drag_cell_id = board_cell
@@ -2553,6 +2664,7 @@ func _begin_drag(screen_pos: Vector2, touch_id: int = -1) -> bool:
25532664 return true
25542665 var inventory_index := _inventory_cell_at (screen_pos )
25552666 if inventory_index >= 0 :
2667+ _reset_idle_hint_nudge ()
25562668 _clear_hint (false )
25572669 _drag_source = DRAG_SOURCE_INVENTORY
25582670 _drag_cell_id = str (_inventory_cells [inventory_index ].get ("id" , "" ))
@@ -2578,6 +2690,7 @@ func _update_drag(screen_pos: Vector2) -> void:
25782690func _finish_drag (screen_pos : Vector2 ) -> void :
25792691 if _drag_source == DRAG_SOURCE_NONE :
25802692 return
2693+ _reset_idle_hint_nudge ()
25812694 _drag_position = screen_pos + _drag_offset
25822695 var target_tile := _screen_to_tile (_drag_position )
25832696 var changed := false
@@ -2711,6 +2824,8 @@ func _on_main_menu_pressed() -> void:
27112824
27122825
27132826func _on_hint_pressed () -> void :
2827+ _idle_hint_disabled_after_hint = true
2828+ _reset_idle_hint_nudge ()
27142829 var candidates : Array = _arcade_hint_candidates ()
27152830 if candidates .is_empty ():
27162831 _hint_pair .clear ()
0 commit comments