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