godot-shooter / asteroids / files / actors / Enemy.gd
Branch asteroids · default branch · all branches
Enemy.gd
b856550a 2023-10-30 Pavel Safronov | 1 | extends KinematicBody2D |
f398dcb9 2023-11-02 Pavel Safronov | 2 | class_name Enemy |
b856550a 2023-10-30 Pavel Safronov | 3 | |
cf2013fa 2023-11-02 Pavel Safronov | 4 | onready var damage_points = $DamagePoints |
023ec2fb 2023-11-29 Pavel Safronov | 5 | onready var navigation_agent: NavigationAgent2D = $NavigationAgent2D |
3dea036b 2024-12-23 Pavel Safronov | 6 | onready var viewcone_polygon2d: Polygon2D = $ViewconePolygon2D |
16938031 2023-11-17 Pavel Safronov | 7 | const Utils = preload("res://utils/Utils.gd") |
| 8 | const Explosion = preload("res://effects/Explosion.tscn") | |
| 9 | const Smoke = preload("res://effects/Smoke.tscn") | |
2b4497bb 2023-11-27 Pavel Safronov | 10 | const Damage = preload("res://effects/Damage.tscn") |
ee6296b3 2023-11-17 Pavel Safronov | 11 | const Constants = preload("res://utils/Constants.gd") |
0f8974b8 2023-12-01 Pavel Safronov | 12 | const Weapons = preload("res://weapons/Weapons.gd") |
b856550a 2023-10-30 Pavel Safronov | 13 | |
50d0dc8b 2023-12-21 Pavel Safronov | 14 | export var is_invincible = false |
ee6296b3 2023-11-17 Pavel Safronov | 15 | export var move_speed = Constants.SPEED * 1.05 |
4c85b9c3 2023-11-29 Pavel Safronov | 16 | export var turn_rate = 0.03 |
e9f8f8f2 2023-11-01 Pavel Safronov | 17 | export var health = -1 |
3fcb817e 2023-11-10 Pavel Safronov | 18 | export var max_health = 100 |
1d4ba5a6 2023-11-02 Pavel Safronov | 19 | export var is_targeted = false |
4c85b9c3 2023-11-29 Pavel Safronov | 20 | export var target: Vector2 |
| 21 | export var follow_target = false | |
11ed9f7a 2023-11-08 Pavel Safronov | 22 | signal enemy_killed(position) |
b856550a 2023-10-30 Pavel Safronov | 23 | |
3dea036b 2024-12-23 Pavel Safronov | 24 | var player: Node2D |
0f8974b8 2023-12-01 Pavel Safronov | 25 | var weapon: Weapon |
b856550a 2023-10-30 Pavel Safronov | 26 | var velocity = Vector2.ZERO |
| 27 | var dead = false | |
8adb41dc 2023-11-19 Pavel Safronov | 28 | var health_ratio |
| 29 | var health_percentage | |
3dea036b 2024-12-23 Pavel Safronov | 30 | var sees_player: bool |
b856550a 2023-10-30 Pavel Safronov | 31 | |
e9f8f8f2 2023-11-01 Pavel Safronov | 32 | func _ready() -> void: |
0f8974b8 2023-12-01 Pavel Safronov | 33 | weapon = Weapons.create_basic_enemy_gun() |
6ad40cea 2023-12-01 Pavel Safronov | 34 | navigation_agent.max_speed = move_speed |
023ec2fb 2023-11-29 Pavel Safronov | 35 | var _err = navigation_agent.connect("path_changed", self, "path_changed") |
| 36 | _err = navigation_agent.connect("velocity_computed", self, "velocity_computed") | |
| 37 | _err = navigation_agent.connect("target_reached", self, "target_reached") | |
50d0dc8b 2023-12-21 Pavel Safronov | 38 | var players = get_tree().get_nodes_in_group("player") |
| 39 | self.player = players[0] | |
| 40 | ||
539576dd 2023-11-07 Pavel Safronov | 41 | $AnimationPlayer.play("fade_in") |
cf2013fa 2023-11-02 Pavel Safronov | 42 | if health < 0 or health > max_health: |
e9f8f8f2 2023-11-01 Pavel Safronov | 43 | health = max_health |
cf2013fa 2023-11-02 Pavel Safronov | 44 | else: |
| 45 | var hurt_points = max_health - health | |
| 46 | health = max_health | |
| 47 | for i in hurt_points: | |
| 48 | hurt() | |
e9f8f8f2 2023-11-01 Pavel Safronov | 49 | |
023ec2fb 2023-11-29 Pavel Safronov | 50 | func path_changed() -> void: |
638706e1 2023-11-30 Pavel Safronov | 51 | pass |
023ec2fb 2023-11-29 Pavel Safronov | 52 | |
| 53 | func velocity_computed(new_vel: Vector2) -> void: | |
1702bad9 2023-11-30 Pavel Safronov | 54 | velocity = move_and_slide(new_vel) |
6ad40cea 2023-12-01 Pavel Safronov | 55 | global_rotation = velocity.angle() + PI/2.0 |
638706e1 2023-11-30 Pavel Safronov | 56 | pass |
023ec2fb 2023-11-29 Pavel Safronov | 57 | |
| 58 | func target_reached() -> void: | |
50d0dc8b 2023-12-21 Pavel Safronov | 59 | Utils.log("enemy", self, "reached player", player, "at", self.position) |
6ad40cea 2023-12-01 Pavel Safronov | 60 | player.hurt(25) |
| 61 | var explosion = Explosion.instance() | |
| 62 | explosion.transform = transform | |
| 63 | get_parent().add_child(explosion) | |
| 64 | kill() | |
638706e1 2023-11-30 Pavel Safronov | 65 | pass |
023ec2fb 2023-11-29 Pavel Safronov | 66 | |
236d1677 2023-11-01 Pavel Safronov | 67 | func _process(_delta: float) -> void: |
3dea036b 2024-12-23 Pavel Safronov | 68 | var is_player_in_visibility_cone = Geometry.is_point_in_polygon(player.global_position, viewcone_polygon2d.polygon) |
| 69 | if is_player_in_visibility_cone: | |
| 70 | var space_state = get_world_2d().direct_space_state | |
| 71 | var intersections = space_state.intersect_ray(global_position, player.global_position, [self]) | |
| 72 | if intersections.size() == 1: | |
| 73 | var collider = intersections[0]['collider'] | |
| 74 | sees_player = collider == player | |
| 75 | ||
| 76 | if sees_player: | |
| 77 | set_target(player.global_position) | |
| 78 | ||
8adb41dc 2023-11-19 Pavel Safronov | 79 | var pb: ProgressBar = $Graphics/Alive/Container/ProgressBar |
| 80 | health_ratio = float(health) / float(max_health) | |
| 81 | health_percentage = health_ratio * 100 | |
| 82 | pb.value = health_percentage | |
| 83 | if health_percentage < 20: | |
| 84 | pb.modulate = Color.red | |
| 85 | elif health_percentage < 60: | |
| 86 | pb.modulate = Color.yellow | |
| 87 | else: | |
| 88 | pb.modulate = Color.blue | |
614e7fb1 2023-10-31 Pavel Safronov | 89 | $Graphics/Alive/Container.global_rotation = 0 |
8adb41dc 2023-11-19 Pavel Safronov | 90 | $Graphics/Alive/Sprite/Target.visible = is_targeted |
| 91 | $Graphics/Alive/Sprite/Target.global_rotation = 0 | |
| 92 | $Graphics/Alive/Sprite/Trails.add_position(global_position) | |
0f8974b8 2023-12-01 Pavel Safronov | 93 | |
| 94 | var dist_to_player = global_position.distance_to(player.global_position) | |
50d0dc8b 2023-12-21 Pavel Safronov | 95 | # Utils.log("dist to player", dist_to_player) |
| 96 | if dist_to_player < 25: | |
| 97 | target_reached() | |
0f8974b8 2023-12-01 Pavel Safronov | 98 | |
| 99 | var should_shoot = dist_to_player < weapon.shooting_range | |
| 100 | if should_shoot: | |
| 101 | var space_state = get_world_2d().direct_space_state | |
| 102 | var intersections = space_state.intersect_ray(global_position, player.global_position, [self]) | |
| 103 | var hits_player = intersections and intersections.collider == player | |
| 104 | if hits_player: | |
| 105 | weapon.try_shoot(self, player.global_position) | |
614e7fb1 2023-10-31 Pavel Safronov | 106 | |
6ad40cea 2023-12-01 Pavel Safronov | 107 | func _physics_process(delta: float) -> void: |
b856550a 2023-10-30 Pavel Safronov | 108 | if dead: |
| 109 | return | |
023ec2fb 2023-11-29 Pavel Safronov | 110 | |
| 111 | # navigate to the next position | |
| 112 | var current_pos = global_position | |
| 113 | var next_pos = navigation_agent.get_next_location() | |
1702bad9 2023-11-30 Pavel Safronov | 114 | var diff: Vector2 = next_pos - current_pos |
| 115 | var dir = diff.normalized() | |
6ad40cea 2023-12-01 Pavel Safronov | 116 | var desired_vel = dir * navigation_agent.max_speed |
| 117 | var steering = (desired_vel - velocity) * delta * 4 | |
| 118 | var new_vel = velocity + steering | |
| 119 | velocity = new_vel | |
| 120 | ||
1702bad9 2023-11-30 Pavel Safronov | 121 | if navigation_agent.avoidance_enabled: |
6ad40cea 2023-12-01 Pavel Safronov | 122 | navigation_agent.set_velocity(velocity) |
1702bad9 2023-11-30 Pavel Safronov | 123 | else: |
6ad40cea 2023-12-01 Pavel Safronov | 124 | velocity_computed(velocity) |
b856550a 2023-10-30 Pavel Safronov | 125 | |
023ec2fb 2023-11-29 Pavel Safronov | 126 | func set_target(pos: Vector2): |
| 127 | # var to_dir = (pos - global_position).normalized() | |
| 128 | # var curr_dir = velocity.normalized() | |
| 129 | # var dir_diff = to_dir - curr_dir | |
| 130 | # var change = dir_diff * turn_rate * move_speed | |
| 131 | # velocity = (velocity + change).normalized() * move_speed | |
1702bad9 2023-11-30 Pavel Safronov | 132 | # var nav_map = navigation_agent.get_navigation_map() |
638706e1 2023-11-30 Pavel Safronov | 133 | # Utils.log("setting target location", pos, "map", nav_map) |
023ec2fb 2023-11-29 Pavel Safronov | 134 | navigation_agent.set_target_location(pos) |
e496c548 2023-11-06 Pavel Safronov | 135 | |
13440f93 2023-11-27 Pavel Safronov | 136 | func _ensure_smoke_exists() -> void: |
| 137 | var max_smoke_count = $DamagePoints.get_child_count() | |
| 138 | var existing_smoke_count = $DamageObjects.get_child_count() | |
| 139 | var expected_smoke_count = ceil(health_ratio * max_smoke_count) | |
| 140 | var smoke_to_add_count = expected_smoke_count - existing_smoke_count | |
| 141 | for i in smoke_to_add_count: | |
| 142 | var smoke = Smoke.instance() | |
| 143 | var smoke_pos = get_damage_point_on_body() | |
| 144 | smoke.position = smoke_pos | |
| 145 | $DamageObjects.add_child(smoke) | |
| 146 | ||
bd0825dd 2023-11-09 Pavel Safronov | 147 | func hurt(damage: int = 1): |
52220002 2023-11-06 Pavel Safronov | 148 | if dead || is_invincible: |
b856550a 2023-10-30 Pavel Safronov | 149 | return |
bd0825dd 2023-11-09 Pavel Safronov | 150 | health -= damage |
2b4497bb 2023-11-27 Pavel Safronov | 151 | add_damage_label(damage) |
8adb41dc 2023-11-19 Pavel Safronov | 152 | # add_child(smoke) |
13440f93 2023-11-27 Pavel Safronov | 153 | _ensure_smoke_exists() |
b856550a 2023-10-30 Pavel Safronov | 154 | if health <= 0: |
| 155 | kill() | |
| 156 | ||
| 157 | func kill(): | |
52220002 2023-11-06 Pavel Safronov | 158 | if dead || is_invincible: |
b856550a 2023-10-30 Pavel Safronov | 159 | return |
| 160 | dead = true | |
| 161 | $Graphics/Alive.hide() | |
969fd5af 2023-11-06 Pavel Safronov | 162 | $CollisionPolygon2D.set_deferred("disabled", true) |
c6faad15 2023-10-31 Pavel Safronov | 163 | var explosion = Explosion.instance() |
| 164 | explosion.connect("explosion_done", self, "_on_death_explosion_finished") | |
| 165 | $Graphics.add_child(explosion) | |
b856550a 2023-10-30 Pavel Safronov | 166 | z_index = -1 |
11ed9f7a 2023-11-08 Pavel Safronov | 167 | emit_signal("enemy_killed", global_position) |
c6faad15 2023-10-31 Pavel Safronov | 168 | |
2b4497bb 2023-11-27 Pavel Safronov | 169 | func add_damage_label(damage_amount: int = 5) -> void: |
| 170 | var damage_label: Damage = Damage.instance() | |
| 171 | damage_label.value = -damage_amount | |
| 172 | damage_label.position = self.position | |
| 173 | get_tree().root.add_child(damage_label) | |
| 174 | ||
fc5de8ee 2023-11-09 Pavel Safronov | 175 | func teleport(dest: Vector2) -> void: |
656778ea 2023-11-28 Pavel Safronov | 176 | $Graphics/Alive/Sprite/Trails.clear() |
fc5de8ee 2023-11-09 Pavel Safronov | 177 | position = dest |
| 178 | ||
c6faad15 2023-10-31 Pavel Safronov | 179 | func _on_death_explosion_finished(): |
cf2013fa 2023-11-02 Pavel Safronov | 180 | $AnimationPlayer.play("fade_out") |
| 181 | ||
| 182 | func get_damage_point_on_body() -> Vector2: | |
| 183 | var child_count = damage_points.get_child_count() | |
| 184 | var random_index = randi() % child_count | |
| 185 | var random_child: Position2D = damage_points.get_child(random_index) | |
| 186 | return random_child.position | |
969fd5af 2023-11-06 Pavel Safronov | 187 | |
| 188 | func remove_thyself(): | |
| 189 | queue_free() | |
115aa5d1 2023-11-07 Pavel Safronov | 190 | |
| 191 | func _on_Area2D_body_entered(body: Node) -> void: | |
| 192 | var is_player = body == player | |
| 193 | if is_player: | |
| 194 | player.hurt(2) | |
| 195 | var explosion = Explosion.instance() | |
| 196 | explosion.transform = transform | |
| 197 | get_parent().add_child(explosion) | |
| 198 | kill() |