game.gd 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. extends Node2D
  2. class_name Game
  3. @onready var health_bar: HealthBar = $HealthBar
  4. @onready var overlay: CanvasLayer = $Overlay
  5. @onready var transition: Transition = $Overlay/Transition
  6. @onready var player_spawn: PlayerSpawnZone = $PlayerSpawnZone
  7. @onready var spawn_timer: Timer = $Timer
  8. @onready var ui_root: Control = $UI/Root
  9. @onready var wave_label: Label = $UI/Root/WaveLabel
  10. @onready var phase_label: Label = $UI/Root/PhaseLabel
  11. @onready var next_phase_button: Button = $UI/Root/NextPhaseButton
  12. @onready var combat_start_sound: AudioStreamPlayer = $Sounds/CombatStartSound
  13. @onready var main_menu: PackedScene = load("res://screens/main_menu.tscn")
  14. @onready var bot_scene: PackedScene = load("res://assets/scenes/bot.tscn")
  15. @onready var game_over_scene: PackedScene = load("res://assets/scenes/game_over.tscn")
  16. @onready var victory_scene: PackedScene = load("res://assets/scenes/victory.tscn")
  17. @onready var intro_scene: PackedScene = load("res://assets/scenes/intro.tscn")
  18. @onready var left_enemy_label: Label = $UI/Root/LeftEnemyLabel
  19. @onready var right_enemy_label: Label = $UI/Root/RightEnemyLabel
  20. @onready var top_enemy_label: Label = $UI/Root/TopEnemyLabel
  21. @onready var spawn_points: Array[Marker2D] = [
  22. $EnemySpawns/EnemySpawn_Left,
  23. $EnemySpawns/EnemySpawn_Right,
  24. $EnemySpawns/EnemySpawn_Top,
  25. ]
  26. @onready var enemy_labels: Array[Label] = [
  27. $UI/Root/LeftEnemyLabel,
  28. $UI/Root/RightEnemyLabel,
  29. $UI/Root/TopEnemyLabel,
  30. ]
  31. enum Phase {
  32. PHASE_SETUP,
  33. PHASE_COMBAT,
  34. PHASE_LOOT,
  35. }
  36. enum Loot {
  37. LOOT_SURVIVOR,
  38. LOOT_HEAL,
  39. LOOT_LOWER_COOLDOWN,
  40. }
  41. const RELOAD_TIME: float = 4.0
  42. static var reload_time: float = RELOAD_TIME # seconds
  43. static var max_ammo: int = 1 # shots
  44. static var base_accuracy: float = 0.33 # percent
  45. var phase: Phase = Phase.PHASE_SETUP:
  46. set(new_value):
  47. phase = new_value
  48. match new_value:
  49. Phase.PHASE_SETUP:
  50. phase_label.text = "PHASE: SETUP"
  51. Phase.PHASE_COMBAT:
  52. phase_label.text = "PHASE: COMBAT"
  53. Phase.PHASE_LOOT:
  54. phase_label.text = "PHASE: LOOT"
  55. var waves_completed: int = 0:
  56. set(new_value):
  57. waves_completed = new_value
  58. wave_label.text = "WAVE: %d" % (new_value + 1)
  59. func update_enemy_labels() -> void:
  60. for n in 3:
  61. var enemies = spawn_points[n].get_child_count()
  62. enemy_labels[n].text = str(enemies)
  63. func set_enemy_label_visible(value: bool) -> void:
  64. for label in enemy_labels:
  65. label.visible = value
  66. func _ready() -> void:
  67. Game.reload_time = RELOAD_TIME # seconds
  68. Game.max_ammo = 1 # shots
  69. Game.base_accuracy = 0.33 # percent
  70. GlobalInput.dragging_disabled = false
  71. waves_completed = 0
  72. phase = Phase.PHASE_SETUP
  73. overlay.visible = true
  74. start_setup()
  75. player_spawn.add_survivor()
  76. var intro: Intro = null
  77. if Config.show_tutorial:
  78. intro = intro_scene.instantiate()
  79. add_child(intro)
  80. ui_root.visible = false
  81. transition.slide_out()
  82. await transition.game_fully_visible
  83. if intro != null:
  84. intro.start()
  85. await intro.finished
  86. await transition.transition("WAVE 1", func ():
  87. ui_root.visible = true
  88. intro.queue_free()
  89. )
  90. func _process(_delta: float) -> void:
  91. var intro_visible: bool = get_tree().get_first_node_in_group("intro") != null
  92. if Input.is_action_pressed("ui_cancel") && not intro_visible:
  93. go_to_main_menu()
  94. if phase == Phase.PHASE_COMBAT:
  95. if get_foes().is_empty() and health_bar.health != 0:
  96. end_combat()
  97. func go_to_main_menu() -> void:
  98. var tree = get_tree()
  99. transition.text = ""
  100. tree.paused = true
  101. transition.slide_in()
  102. await transition.game_fully_hidden
  103. tree.change_scene_to_packed(main_menu)
  104. tree.paused = false
  105. func spawn_bot(spawn: Marker2D, index: int) -> void:
  106. var bot: Bot = bot_scene.instantiate()
  107. bot.name = "Bot #%d" % (index + 1)
  108. bot.speed += waves_completed
  109. bot.visible = false
  110. bot.health += waves_completed / 30
  111. spawn.add_child(bot)
  112. bot.position = Vector2.ZERO
  113. func get_foes() -> Array[Area2D]:
  114. var foes: Array[Area2D] = []
  115. foes.assign(get_tree().get_nodes_in_group("foe"))
  116. return foes
  117. func get_survivors() -> Array[Area2D]:
  118. var survivors: Array[Area2D] = []
  119. survivors.assign(get_tree().get_nodes_in_group("survivor"))
  120. return survivors
  121. func get_waiting_foes() -> Array[Area2D]:
  122. return get_foes().filter(func (foe): return not foe.visible)
  123. func get_active_foes() -> Array[Area2D]:
  124. return get_foes().filter(func (foe): return foe.visible)
  125. func activate_foe() -> void:
  126. var foes = get_waiting_foes()
  127. if not foes.is_empty():
  128. foes.pick_random().visible = true
  129. func reset_survivors() -> void:
  130. var survivors = get_survivors()
  131. #for survivor in survivors:
  132. # survivor.reparent(self)
  133. for survivor in survivors:
  134. #player_spawn.drop(survivor)
  135. #survivor.position = Vector2.ZERO
  136. #survivor.rotation = 0.0
  137. survivor.ammo = Game.max_ammo
  138. func _on_timer_timeout() -> void:
  139. activate_foe()
  140. func base_hit() -> void:
  141. health_bar.health -= 1
  142. if health_bar.health == 0:
  143. await game_over()
  144. func game_over() -> void:
  145. spawn_timer.stop()
  146. var foes = get_tree().get_nodes_in_group("foe")
  147. for foe in foes:
  148. foe.queue_free()
  149. var gos: GameOver = game_over_scene.instantiate()
  150. gos.message = "YOU DIED AFTER\nCOMPLETING %d WAVES!" % waves_completed
  151. ui_root.add_child(gos)
  152. await gos.closed
  153. await go_to_main_menu()
  154. func _on_base_area_entered(area: Area2D) -> void:
  155. if area.is_in_group("foe") || area.is_in_group("foe_weapons"):
  156. base_hit()
  157. func start_setup() -> void:
  158. for n in (waves_completed + 1):
  159. var spawner = spawn_points.pick_random()
  160. spawn_bot(spawner, n)
  161. update_enemy_labels()
  162. set_enemy_label_visible(true)
  163. func start_combat() -> void:
  164. set_enemy_label_visible(false)
  165. GlobalInput.dragging_disabled = true
  166. spawn_timer.wait_time = lerpf(3.5, 0.5, minf(float(waves_completed)/20.0, 1.0))
  167. spawn_timer.start()
  168. combat_start_sound.play()
  169. activate_foe()
  170. const LOOT_SURVIVOR: String = "+1 SURVIVOR"
  171. const LOOT_HEALTH: String = "+3 HEALTH"
  172. const LOOT_FULL_HEALTH: String = "FULL HEALTH"
  173. const LOOT_AMMO: String = "BIGGER MAGAZINES"
  174. const LOOT_RELOAD_TIME: String = "FASTER RELOADING"
  175. const LOOT_ACCURACY: String = "HIGHER ACCURACY"
  176. func determine_loot_options() -> Array[String]:
  177. var loots: Array[String] = []
  178. if get_survivors().size() < 6: # this is, so the player has a reason to move survivors even later
  179. loots.push_back(LOOT_SURVIVOR)
  180. if Game.base_accuracy < 0.67:
  181. loots.push_back(LOOT_ACCURACY)
  182. if Game.reload_time > 1.5:
  183. loots.push_back(LOOT_RELOAD_TIME)
  184. if Game.max_ammo < 9:
  185. loots.push_back(LOOT_AMMO)
  186. var health = health_bar.health
  187. if health < HealthBar.MAX_HEALTH:
  188. if health == 1:
  189. loots.push_back(LOOT_FULL_HEALTH)
  190. else:
  191. loots.push_back(LOOT_HEALTH)
  192. loots.shuffle()
  193. if loots.size() < 3:
  194. for n in (3-loots.size()):
  195. loots.push_back("NOTHING")
  196. return loots
  197. func end_combat() -> void:
  198. phase = Phase.PHASE_LOOT
  199. spawn_timer.stop()
  200. await get_tree().create_timer(1.5).timeout
  201. var vs: Victory = victory_scene.instantiate()
  202. ui_root.add_child(vs)
  203. var loots: Array[String] = determine_loot_options()
  204. vs.set_loot_options(loots[0], loots[1], loots[2])
  205. await vs.loot_selected
  206. var loot: String = loots[vs.selection]
  207. print("selected loot: " + str(loot))
  208. var next_wave = waves_completed + 2
  209. await transition.transition("WAVE %d" % next_wave, func ():
  210. self.waves_completed += 1
  211. self.phase = Phase.PHASE_SETUP
  212. self.next_phase_button.disabled = false
  213. match loot:
  214. LOOT_SURVIVOR:
  215. self.player_spawn.add_survivor()
  216. LOOT_HEALTH:
  217. self.health_bar.health += 3
  218. LOOT_FULL_HEALTH:
  219. self.health_bar.health = HealthBar.MAX_HEALTH
  220. LOOT_RELOAD_TIME:
  221. Game.reload_time *= 0.9
  222. LOOT_AMMO:
  223. Game.max_ammo += 1
  224. LOOT_ACCURACY:
  225. Game.base_accuracy += 0.01
  226. self.reset_survivors()
  227. self.start_setup()
  228. )
  229. GlobalInput.dragging_disabled = false
  230. func _on_next_phase_button_pressed() -> void:
  231. match phase:
  232. Phase.PHASE_SETUP:
  233. phase = Phase.PHASE_COMBAT
  234. next_phase_button.disabled = true
  235. start_combat()
  236. Phase.PHASE_COMBAT:
  237. pass # this should not happen
  238. Phase.PHASE_LOOT:
  239. pass # this should not happen