title.gd 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. extends Node2D
  2. ## Title screen: the big core with turning rings and bouncing balls, the player's progress,
  3. ## what unlocks next, and a music toggle.
  4. const BALL_SCENE := preload("res://scenes/ball.tscn")
  5. const BALLS := 6
  6. const RING_SPEEDS := [12.0, -8.0] # degrees per second, one per decorative ring
  7. ## Kinds sprinkled into the decorative rings, as a taste of what is coming.
  8. const SHOWCASE := [
  9. Block.Kind.INDESTRUCTIBLE, Block.Kind.WATER, Block.Kind.ICE, Block.Kind.TNT,
  10. Block.Kind.RUBBER, Block.Kind.MOSS,
  11. ]
  12. var _level := 0
  13. var _rings: Array[Node2D] = []
  14. @onready var core: Core = $Core
  15. func _ready() -> void:
  16. var screen := get_viewport_rect().size
  17. core.position = Vector2(screen.x / 2.0, screen.y * 0.28)
  18. _add_rings()
  19. for i in BALLS:
  20. var ball: Ball = BALL_SCENE.instantiate()
  21. ball.position = Vector2(screen.x / 2.0, screen.y - Params.BALL_RADIUS * 2.0)
  22. $Balls.add_child(ball)
  23. ball.throw()
  24. var progress := Progress.load_level()
  25. _level = progress[0]
  26. $UI/Info/Level.text = "Level %d" % _level
  27. $UI/Info/Totals/Count.text = str(Progress.total_stars())
  28. var best := Progress.best_score()
  29. $UI/Info/Best.text = "Best score %d" % best if best > 0 else ""
  30. $UI/Play.text = "Play" if _level == 0 else "Continue"
  31. $UI/Next.text = _next_unlock(_level)
  32. if _level != 0:
  33. $UI/Decay.text = Params.MSG_LEVEL_BACK % [_level, progress[1]]
  34. $UI/Play.pressed.connect(_on_play_pressed)
  35. $UI/Levels.pressed.connect(func() -> void: get_tree().change_scene_to_file("res://scenes/levels.tscn"))
  36. $UI/MusicToggle.pressed.connect(_on_music_toggled)
  37. _show_music_state()
  38. Music.play("menu")
  39. func _physics_process(delta: float) -> void:
  40. for i in _rings.size():
  41. _rings[i].rotation_degrees += RING_SPEEDS[i] * delta
  42. ## Two thin rings of segments around the core, mostly normal with a few special kinds.
  43. ## They are only decoration: no collisions.
  44. func _add_rings() -> void:
  45. var inner := core.radius + 6.0
  46. for r in RING_SPEEDS.size():
  47. var ring := Node2D.new()
  48. ring.position = core.position
  49. var segments := 12 + r * 4
  50. var step := TAU / segments
  51. for i in segments:
  52. var kind: Block.Kind = SHOWCASE[(i + r * 3) % SHOWCASE.size()] if i % 3 == 0 else Block.Kind.NORMAL
  53. var block := Block.new(inner + r * 30.0, inner + r * 30.0 + 26.0, i * step, (i + 1) * step, kind)
  54. block.collision_layer = 0
  55. block.set_physics_process(false)
  56. ring.add_child(block)
  57. add_child(ring)
  58. move_child(ring, core.get_index())
  59. _rings.append(ring)
  60. ## "Next: Fireball at level 12" for the next power or segment to unlock after [param level].
  61. static func _next_unlock(level: int) -> String:
  62. var best_level := -1
  63. var best_name := ""
  64. for info: Dictionary in PowerUp.KINDS.values() + Block.KINDS.values():
  65. var unlock: int = info.unlock
  66. if unlock > level and (best_level < 0 or unlock < best_level):
  67. best_level = unlock
  68. best_name = info.name
  69. if best_level < 0:
  70. return "Everything unlocked. How far can you go?"
  71. return "Next: %s at level %d" % [best_name, best_level]
  72. func _on_play_pressed() -> void:
  73. Progress.current_level = _level
  74. get_tree().change_scene_to_file("res://scenes/game.tscn")
  75. func _on_music_toggled() -> void:
  76. Music.set_enabled(not Progress.music_on())
  77. _show_music_state()
  78. func _show_music_state() -> void:
  79. $UI/MusicToggle.text = "Music: %s" % ("on" if Progress.music_on() else "off")