| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123 |
- extends Control
- ## Level map: a tile per level with its best stars. Levels up to the saved one can be
- ## played (replays keep the best stars); a few locked ones show what is ahead, and a dot
- ## marks levels where a new power or segment unlocks.
- const COLUMNS := 5
- const LOCKED_AHEAD := 10 # locked tiles shown past the saved level
- const TILE := Vector2(88, 96)
- var _saved := 0
- var _unlocks := {} # level -> Array of { name, color }
- @onready var grid: GridContainer = $Scroll/Margin/Grid
- @onready var scroll: ScrollContainer = $Scroll
- @onready var info: Label = $Info
- func _ready() -> void:
- Music.play("menu")
- _saved = Progress.load_level()[0]
- $Header/Totals/Count.text = str(Progress.total_stars())
- $Header/Home.pressed.connect(func() -> void: get_tree().change_scene_to_file("res://scenes/title.tscn"))
- _collect_unlocks()
- grid.columns = COLUMNS
- var last := maxi(_saved, Progress.highest_won()) + LOCKED_AHEAD
- var current: Control
- for level in last + 1:
- var tile := _make_tile(level)
- grid.add_child(tile)
- if level == _saved:
- current = tile
- info.text = "Tap a level to play it. Replays keep your best stars."
- # Wait for layout, then centre the current level.
- await get_tree().process_frame
- await get_tree().process_frame
- scroll.scroll_vertical = int(current.position.y + TILE.y / 2.0 - scroll.size.y / 2.0)
- func _collect_unlocks() -> void:
- for info_dict: Dictionary in PowerUp.KINDS.values() + Block.KINDS.values():
- var unlock: int = info_dict.unlock
- if unlock > 0:
- if not _unlocks.has(unlock):
- _unlocks[unlock] = []
- _unlocks[unlock].append(info_dict)
- func _make_tile(level: int) -> Button:
- var playable := level <= _saved
- var tile := Button.new()
- tile.custom_minimum_size = TILE
- tile.focus_mode = Control.FOCUS_NONE
- var style := _style(level)
- for state in ["normal", "hover", "pressed", "disabled"]:
- tile.add_theme_stylebox_override(state, style)
- tile.pressed.connect(_on_tile_pressed.bind(level))
- var box := VBoxContainer.new()
- box.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
- box.alignment = BoxContainer.ALIGNMENT_CENTER
- box.mouse_filter = Control.MOUSE_FILTER_IGNORE
- tile.add_child(box)
- var number := Label.new()
- number.text = str(level)
- number.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
- number.add_theme_font_size_override("font_size", 30)
- var ink := Params.COLOR_BACK if level == _saved else Color(1, 1, 1, 1.0 if playable else 0.3)
- number.add_theme_color_override("font_color", ink)
- box.add_child(number)
- var stars := Stars.new()
- stars.star_size = 15.0
- stars.spacing = 3.0
- stars.filled = Progress.stars(level)
- stars.modulate.a = 1.0 if playable else 0.3
- box.add_child(stars)
- for child in box.get_children():
- child.mouse_filter = Control.MOUSE_FILTER_IGNORE
- if _unlocks.has(level):
- var dot := UnlockDot.new()
- dot.color = _unlocks[level][0].color
- dot.position = Vector2(TILE.x - 16, 8)
- tile.add_child(dot)
- return tile
- func _style(level: int) -> StyleBoxFlat:
- var style := StyleBoxFlat.new()
- style.set_corner_radius_all(14)
- style.anti_aliasing_size = 1.5
- if level == _saved:
- style.bg_color = Params.COLOR_ORANGE
- elif level < _saved:
- style.bg_color = Color("#3e3f37")
- else:
- style.bg_color = Color("#30312b")
- return style
- func _on_tile_pressed(level: int) -> void:
- if level <= _saved:
- Progress.current_level = level
- get_tree().change_scene_to_file("res://scenes/game.tscn")
- return
- var names := []
- for unlock: Dictionary in _unlocks.get(level, []):
- names.append(unlock.name)
- info.text = "Level %d is locked. Reach it to play" % level
- info.text += (": %s unlocks there." % ", ".join(names)) if not names.is_empty() else "."
- ## The coloured dot on a tile where something new unlocks.
- class UnlockDot:
- extends Control
- var color := Color.WHITE
- func _ready() -> void:
- mouse_filter = Control.MOUSE_FILTER_IGNORE
- size = Vector2(10, 10)
- func _draw() -> void:
- draw_circle(Vector2(5, 5), 5.0, color, true, -1.0, true)
|