title.gd 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  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/MusicToggle.pressed.connect(_on_music_toggled)
  36. _show_music_state()
  37. Music.play("menu")
  38. func _physics_process(delta: float) -> void:
  39. for i in _rings.size():
  40. _rings[i].rotation_degrees += RING_SPEEDS[i] * delta
  41. ## Two thin rings of segments around the core, mostly normal with a few special kinds.
  42. ## They are only decoration: no collisions.
  43. func _add_rings() -> void:
  44. var inner := core.radius + 6.0
  45. for r in RING_SPEEDS.size():
  46. var ring := Node2D.new()
  47. ring.position = core.position
  48. var segments := 12 + r * 4
  49. var step := TAU / segments
  50. for i in segments:
  51. var kind: Block.Kind = SHOWCASE[(i + r * 3) % SHOWCASE.size()] if i % 3 == 0 else Block.Kind.NORMAL
  52. var block := Block.new(inner + r * 30.0, inner + r * 30.0 + 26.0, i * step, (i + 1) * step, kind)
  53. block.collision_layer = 0
  54. block.set_physics_process(false)
  55. ring.add_child(block)
  56. add_child(ring)
  57. move_child(ring, core.get_index())
  58. _rings.append(ring)
  59. ## "Next: Fireball at level 12" for the next power or segment to unlock after [param level].
  60. static func _next_unlock(level: int) -> String:
  61. var best_level := -1
  62. var best_name := ""
  63. for info: Dictionary in PowerUp.KINDS.values() + Block.KINDS.values():
  64. var unlock: int = info.unlock
  65. if unlock > level and (best_level < 0 or unlock < best_level):
  66. best_level = unlock
  67. best_name = info.name
  68. if best_level < 0:
  69. return "Everything unlocked. How far can you go?"
  70. return "Next: %s at level %d" % [best_name, best_level]
  71. func _on_play_pressed() -> void:
  72. Progress.current_level = _level
  73. get_tree().change_scene_to_file("res://scenes/game.tscn")
  74. func _on_music_toggled() -> void:
  75. Music.set_enabled(not Progress.music_on())
  76. _show_music_state()
  77. func _show_music_state() -> void:
  78. $UI/MusicToggle.text = "Music: %s" % ("on" if Progress.music_on() else "off")