godot-shooter / asteroids / files / scenes / World.gd
Branch asteroids · default branch · all branches
World.gd
| 1 | extends Node2D |
| 2 | |
| 3 | const Enemy = preload("res://actors/Enemy.tscn") |
| 4 | const Bomb = preload("res://weapons/Bomb.tscn") |
| 5 | const Asteroid = preload("res://items/Asteroid.tscn") |
| 6 | |
| 7 | const Utils = preload("res://utils/Utils.gd") |
| 8 | const Weapons = preload("res://weapons/Weapons.gd") |
| 9 | const PowerUp = preload("res://items/PowerUp.tscn") |
| 10 | const Settings = preload("res://utils/Settings.gd") |
| 11 | onready var player: Player = $Player |
| 12 | onready var update_timer = $UpdateTimer |
| 13 | onready var navigation_polygon_instance: NavigationPolygonInstance = $NavigationPolygonInstance |
| 14 | onready var environment_node = $Environment |
| 15 | |
| 16 | var generators = [ |
| 17 | ConstantItemGenerator.new("create_enemy", self, "enemies", 5), |
| 18 | ConstantItemGenerator.new("create_health", self, "health", 20), |
| 19 | ConstantItemGenerator.new("create_weapon", self, "weapon", 10), |
| 20 | ConstantItemGenerator.new("create_bomb", self, "bombs", 10), |
| 21 | LevelItemGenerator.new(5, "create_gold", self, "gold", 5), |
| 22 | ConstantItemGenerator.new("create_asteroid", self, "asteroids", 10), |
| 23 | ] |
| 24 | var completed_objectives = 0 |
| 25 | var max_objectives = 5 |
| 26 | var item_replacement_distance = 2000 |
| 27 | var is_dragging = false |
| 28 | var is_game_over = false |
| 29 | export var starting_zoom = 1.5 |
| 30 | |
| 31 | func enemy_killed(_position: Vector2): |
| 32 | player.score += 5 |
| 33 | func health_consumed(_power_up): |
| 34 | player.heal(20) |
| 35 | player.score += 1 |
| 36 | func gold_consumed(_power_up): |
| 37 | completed_objectives += 1 |
| 38 | var points = 5 * (completed_objectives+1) |
| 39 | player.score += points |
| 40 | player.show_message("+%d points" % points) |
| 41 | player.health = player.max_health |
| 42 | func weapon_consumed(power_up): |
| 43 | var data = power_up.data |
| 44 | var weapon: Weapon = data |
| 45 | var is_same_weapon = player.weapon.name == weapon.name |
| 46 | if is_same_weapon && weapon.ammo != INF: |
| 47 | player.weapon.ammo += player.weapon.reload_size |
| 48 | else: |
| 49 | player.weapon = weapon |
| 50 | func bomb_consumed(power_up): |
| 51 | var pos: Vector2 = power_up.global_position |
| 52 | var bomb = Bomb.instance() |
| 53 | environment_node.call_deferred("add_child",bomb) |
| 54 | bomb.position = pos |
| 55 | |
| 56 | func create_enemy() -> Enemy: |
| 57 | var enemy = Enemy.instance() |
| 58 | enemy.position = get_new_environment_position(1) |
| 59 | enemy.connect("enemy_killed", self, "enemy_killed") |
| 60 | return enemy |
| 61 | func create_health() -> PowerUp: |
| 62 | var health = PowerUp.instance() |
| 63 | health.position = get_new_environment_position(randf() + .5) |
| 64 | health.connect("consumed", self, "health_consumed") |
| 65 | health.label = "+HP" |
| 66 | health.color = Color.green |
| 67 | return health |
| 68 | func create_bomb() -> PowerUp: |
| 69 | var bomb = PowerUp.instance() |
| 70 | var pos = get_new_environment_position(randf() + .5) |
| 71 | bomb.position = pos |
| 72 | bomb.connect("consumed", self, "bomb_consumed") |
| 73 | bomb.label = "BOMB" |
| 74 | bomb.color = Color.purple |
| 75 | return bomb |
| 76 | func create_gold(pos: Vector2 = get_new_environment_position(1 + randf())) -> PowerUp: |
| 77 | var gold = PowerUp.instance() |
| 78 | gold.position = pos |
| 79 | gold.connect("consumed", self, "gold_consumed") |
| 80 | gold.label = "" |
| 81 | gold.color = Color.gold |
| 82 | return gold |
| 83 | func create_weapon() -> PowerUp: |
| 84 | var weapon: PowerUp = PowerUp.instance() |
| 85 | weapon.position = get_new_environment_position(randf() + .5) |
| 86 | var _err = weapon.connect("consumed", self, "weapon_consumed") |
| 87 | var r = randf() |
| 88 | if r < .45: |
| 89 | weapon.data = Weapons.create_rocket_gun() |
| 90 | weapon.label = "ROC" |
| 91 | weapon.color = Color.red |
| 92 | elif r < .9: |
| 93 | weapon.data = Weapons.create_smg_gun() |
| 94 | weapon.label = "SMG" |
| 95 | weapon.color = Color.orange |
| 96 | else: |
| 97 | weapon.data = Weapons.create_basic_gun() |
| 98 | weapon.label = "LZR" |
| 99 | weapon.color = Color.pink |
| 100 | return weapon |
| 101 | func create_asteroid() -> Asteroid: |
| 102 | var asteroid: Asteroid = Asteroid.instance() |
| 103 | asteroid.position = get_new_environment_position(randf() * 2) |
| 104 | asteroid.spin_rate = randf() * 2 - 1 |
| 105 | asteroid.scale *= (randf() * 2 + .5) |
| 106 | asteroid.velocity = Vector2.UP.rotated(randf() * PI * 2) * randf() * 100 |
| 107 | return asteroid |
| 108 | |
| 109 | func get_new_item_pos(dist_ratio: float = 1.0) -> Vector2: |
| 110 | var pos_angle = randi() % 360 |
| 111 | var unit_vector = Vector2.UP.rotated(pos_angle) |
| 112 | var size = get_viewport().get_visible_rect().size |
| 113 | var max_side = max(size.x, size.y) |
| 114 | var screen_radius = max_side + 10 |
| 115 | var enemy_spawn_dist = screen_radius * dist_ratio |
| 116 | var pos = unit_vector * enemy_spawn_dist + $Player.global_position |
| 117 | return pos |
| 118 | func get_new_environment_position(dist_ratio: float = 1.0) -> Vector2: |
| 119 | var potential_pos: Vector2 |
| 120 | while true: |
| 121 | # find the positions of the closest Environment items |
| 122 | var items = environment_node.get_children() |
| 123 | potential_pos = get_new_item_pos(dist_ratio) |
| 124 | var closest_pos = 10000 |
| 125 | for i in items: |
| 126 | var item: Node2D = i |
| 127 | var pos = item.global_position |
| 128 | var dist = potential_pos.distance_to(pos) |
| 129 | if dist < closest_pos: |
| 130 | closest_pos = dist |
| 131 | var is_too_close = closest_pos < 50 |
| 132 | var found_one = !is_too_close |
| 133 | if found_one: |
| 134 | break |
| 135 | return potential_pos |
| 136 | |
| 137 | func _ready() -> void: |
| 138 | player.camera.zoom = Vector2(starting_zoom, starting_zoom) |
| 139 | run_generators() |
| 140 | # # position first two enemies |
| 141 | # var enemies = get_tree().get_nodes_in_group("enemies") |
| 142 | # var enemy1: Enemy = enemies[0] |
| 143 | # enemy1.position = Vector2(-300, 200) |
| 144 | # var enemy2: Enemy = enemies[1] |
| 145 | # enemy2.position = Vector2(300, 200) |
| 146 | pass |
| 147 | |
| 148 | func run_generators() -> void: |
| 149 | for g in generators: |
| 150 | var generator = g |
| 151 | while true: |
| 152 | var is_done = generator.run($Environment) |
| 153 | if is_done: |
| 154 | break |
| 155 | pass |
| 156 | |
| 157 | func _input(_event: InputEvent) -> void: |
| 158 | if player.dead: |
| 159 | return |
| 160 | |
| 161 | func _process(_delta: float) -> void: |
| 162 | if is_game_over: |
| 163 | return |
| 164 | |
| 165 | # move navigation polygon around the player, navigation won't work outside of this polygon |
| 166 | var nav_size = Vector2(item_replacement_distance * 2 * 5, item_replacement_distance * 2 * 5) |
| 167 | var nav_pos = player.global_position - (nav_size * 0.5) |
| 168 | var vertices = PoolVector2Array([ |
| 169 | nav_pos, |
| 170 | nav_pos + Vector2(nav_size.x, 0), |
| 171 | nav_pos + nav_size, |
| 172 | nav_pos + Vector2(0, nav_size.y) |
| 173 | ]) |
| 174 | var polygon = NavigationPolygon.new() |
| 175 | polygon.set_vertices(vertices) |
| 176 | var indices = PoolIntArray([0, 1, 2, 3]) |
| 177 | polygon.add_polygon(indices) |
| 178 | navigation_polygon_instance.navpoly = polygon |
| 179 | |
| 180 | # set the objective text |
| 181 | player.set_objective("%s / %s" % [completed_objectives, max_objectives]) |
| 182 | |
| 183 | # if all objectives are complete, show the game finished menu |
| 184 | var all_done = completed_objectives == max_objectives |
| 185 | if all_done: |
| 186 | is_game_over = true |
| 187 | var score = player.score |
| 188 | player.add_high_score(score) |
| 189 | var final_message = "You won with %s points!" % score |
| 190 | $Player/UI/GameFinishedMenu.find_node("Label").text = final_message |
| 191 | $Player/UI/GameFinishedMenu.show() |
| 192 | $Player/UI/HUD.is_game_over = true |
| 193 | get_tree().paused = true |
| 194 | return |
| 195 | |
| 196 | run_generators() |
| 197 | |
| 198 | # position the destination |
| 199 | $Destination.visible = player.is_destination_set |
| 200 | $Destination.global_position = player.destination |
| 201 | |
| 202 | # clear radar and populate with all items |
| 203 | var enemies = get_tree().get_nodes_in_group("enemies") |
| 204 | var filteredEnemies = Utils.filter(funcref(self,"filter_invalid_enemies"), enemies) |
| 205 | player.clear_radar() |
| 206 | player.update_radar(get_tree().get_nodes_in_group("gold"), Color.gold) |
| 207 | player.update_radar(filteredEnemies, Color.red) |
| 208 | if player.settings.show_items: |
| 209 | player.update_radar(get_tree().get_nodes_in_group("health"), Color.gray) |
| 210 | player.update_radar(get_tree().get_nodes_in_group("weapons"), Color.white) |
| 211 | player.update_radar(get_tree().get_nodes_in_group("bombs"), Color.purple) |
| 212 | |
| 213 | func _on_Destination_body_entered(body: Node) -> void: |
| 214 | if body == player: |
| 215 | player.clear_destination() |
| 216 | |
| 217 | func filter_invalid_enemies(enemy: Enemy) -> bool: |
| 218 | if enemy.dead: |
| 219 | return false |
| 220 | return true |
| 221 | |
| 222 | func _on_ShootTimer_timeout() -> void: |
| 223 | if player.dead: |
| 224 | return |
| 225 | |
| 226 | func _on_Button_pressed() -> void: |
| 227 | get_tree().paused = false |
| 228 | $UI/PauseMenu.hide() |
| 229 | |
| 230 | func _on_Game_over_button_pressed() -> void: |
| 231 | $UI/GameFinishedMenu.hide() |
| 232 | restart() |
| 233 | |
| 234 | func restart() -> void: |
| 235 | get_tree().paused = false |
| 236 | var _err = get_tree().reload_current_scene() |
| 237 | |
| 238 | func _on_Player_debug() -> void: |
| 239 | # player.score += 1 |
| 240 | # player.kill() |
| 241 | # player.score = 123 |
| 242 | # completed_objectives = max_objectives |
| 243 | # player.debug() |
| 244 | # weapon_consumed(Weapons.create_rocket_gun()) |
| 245 | pass |
| 246 | |
| 247 | func _on_Player_destination_set() -> void: |
| 248 | $Destination.visible = player.is_destination_set |
| 249 | $Destination.global_position = player.destination |
| 250 | pass |
| 251 | |
| 252 | func _on_UpdateTimer_timeout() -> void: |
| 253 | var env_items = environment_node.get_children() |
| 254 | for ei in env_items: |
| 255 | var item = ei as Node2D |
| 256 | var enemy := item as Enemy |
| 257 | |
| 258 | # # if item is enemy, have them target the player |
| 259 | # if enemy: |
| 260 | # enemy.set_target($Player.position) |
| 261 | |
| 262 | # if the item is too far from the player, teleport it to a closer location |
| 263 | var diff = item.global_position - player.global_position |
| 264 | var dist = diff.length() |
| 265 | var is_too_far = dist > item_replacement_distance |
| 266 | if is_too_far: |
| 267 | var new_pos = get_new_environment_position(1 + randf()) |
| 268 | Utils.log("moving distant item from", item.global_position, "to", new_pos) |
| 269 | if enemy: |
| 270 | enemy.teleport(new_pos) |
| 271 | else: |
| 272 | item.global_position = new_pos |