fuzzyworld

godot-shooter / files / actors / Enemy.gd

Enemy.gd

5.7 KB · history · blame

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