1
0

levels.gd 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. extends Control
  2. ## Level map: a tile per level with its best stars. Levels up to the saved one can be
  3. ## played (replays keep the best stars); a few locked ones show what is ahead, and a dot
  4. ## marks levels where a new power or segment unlocks.
  5. const COLUMNS := 5
  6. const LOCKED_AHEAD := 10 # locked tiles shown past the saved level
  7. const TILE := Vector2(88, 96)
  8. var _saved := 0
  9. var _unlocks := {} # level -> Array of { name, color }
  10. @onready var grid: GridContainer = $Scroll/Margin/Grid
  11. @onready var scroll: ScrollContainer = $Scroll
  12. @onready var info: Label = $Info
  13. func _ready() -> void:
  14. Music.play("menu")
  15. _saved = Progress.load_level()[0]
  16. $Header/Totals/Count.text = str(Progress.total_stars())
  17. $Header/Home.pressed.connect(func() -> void: get_tree().change_scene_to_file("res://scenes/title.tscn"))
  18. _collect_unlocks()
  19. grid.columns = COLUMNS
  20. var last := maxi(_saved, Progress.highest_won()) + LOCKED_AHEAD
  21. var current: Control
  22. for level in last + 1:
  23. var tile := _make_tile(level)
  24. grid.add_child(tile)
  25. if level == _saved:
  26. current = tile
  27. info.text = "Tap a level to play it. Replays keep your best stars."
  28. # Wait for layout, then centre the current level.
  29. await get_tree().process_frame
  30. await get_tree().process_frame
  31. scroll.scroll_vertical = int(current.position.y + TILE.y / 2.0 - scroll.size.y / 2.0)
  32. func _collect_unlocks() -> void:
  33. for info_dict: Dictionary in PowerUp.KINDS.values() + Block.KINDS.values():
  34. var unlock: int = info_dict.unlock
  35. if unlock > 0:
  36. if not _unlocks.has(unlock):
  37. _unlocks[unlock] = []
  38. _unlocks[unlock].append(info_dict)
  39. func _make_tile(level: int) -> Button:
  40. var playable := level <= _saved
  41. var tile := Button.new()
  42. tile.custom_minimum_size = TILE
  43. tile.focus_mode = Control.FOCUS_NONE
  44. var style := _style(level)
  45. for state in ["normal", "hover", "pressed", "disabled"]:
  46. tile.add_theme_stylebox_override(state, style)
  47. tile.pressed.connect(_on_tile_pressed.bind(level))
  48. var box := VBoxContainer.new()
  49. box.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
  50. box.alignment = BoxContainer.ALIGNMENT_CENTER
  51. box.mouse_filter = Control.MOUSE_FILTER_IGNORE
  52. tile.add_child(box)
  53. var number := Label.new()
  54. number.text = str(level)
  55. number.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
  56. number.add_theme_font_size_override("font_size", 30)
  57. var ink := Params.COLOR_BACK if level == _saved else Color(1, 1, 1, 1.0 if playable else 0.3)
  58. number.add_theme_color_override("font_color", ink)
  59. box.add_child(number)
  60. var stars := Stars.new()
  61. stars.star_size = 15.0
  62. stars.spacing = 3.0
  63. stars.filled = Progress.stars(level)
  64. stars.modulate.a = 1.0 if playable else 0.3
  65. box.add_child(stars)
  66. for child in box.get_children():
  67. child.mouse_filter = Control.MOUSE_FILTER_IGNORE
  68. if _unlocks.has(level):
  69. var dot := UnlockDot.new()
  70. dot.color = _unlocks[level][0].color
  71. dot.position = Vector2(TILE.x - 16, 8)
  72. tile.add_child(dot)
  73. return tile
  74. func _style(level: int) -> StyleBoxFlat:
  75. var style := StyleBoxFlat.new()
  76. style.set_corner_radius_all(14)
  77. style.anti_aliasing_size = 1.5
  78. if level == _saved:
  79. style.bg_color = Params.COLOR_ORANGE
  80. elif level < _saved:
  81. style.bg_color = Color("#3e3f37")
  82. else:
  83. style.bg_color = Color("#30312b")
  84. return style
  85. func _on_tile_pressed(level: int) -> void:
  86. if level <= _saved:
  87. Progress.current_level = level
  88. get_tree().change_scene_to_file("res://scenes/game.tscn")
  89. return
  90. var names := []
  91. for unlock: Dictionary in _unlocks.get(level, []):
  92. names.append(unlock.name)
  93. info.text = "Level %d is locked. Reach it to play" % level
  94. info.text += (": %s unlocks there." % ", ".join(names)) if not names.is_empty() else "."
  95. ## The coloured dot on a tile where something new unlocks.
  96. class UnlockDot:
  97. extends Control
  98. var color := Color.WHITE
  99. func _ready() -> void:
  100. mouse_filter = Control.MOUSE_FILTER_IGNORE
  101. size = Vector2(10, 10)
  102. func _draw() -> void:
  103. draw_circle(Vector2(5, 5), 5.0, color, true, -1.0, true)