fuzzyworld

godot-shooter / files / actors / Player.gd

Player.gd

9.4 KB · history · blame

1extends KinematicBody2D
2class_name Player
3
4const Damage = preload("res://effects/Damage.tscn")
5const Weapons = preload("res://weapons/Weapons.gd")
6const Constants = preload("res://utils/Constants.gd")
7
8export var is_invincible = false
9export var score = 0
10export var move_speed = Constants.SPEED
11export var health = 50
12export var max_health = 100
13export var turn_rate = 0.03
14export var accel_rate = 1
15export var radar_min_radius = 350
16export var radar_max_radius = 2000
17export var destination: Vector2
18export var is_destination_set: bool = false
19signal player_died
20signal destination_set
21signal debug
22
23onready var hud = $UI/HUD
24onready var camera: Camera2D = $Camera2D
25onready var static_controls = $Graphics/Alive/StaticControls
26onready var radar_sprite = $Graphics/Alive/StaticControls/RadarSprite
27onready var radar_container = $Graphics/Alive/StaticControls/Radar
28onready var pause_menu = $UI/PauseMenu
29onready var scores_container = $UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer/TabContainer/Scores
30onready var scores_list = $UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer/TabContainer/Scores/ScoresList
31onready var pause_always_show_intro_check = $UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer/TabContainer/Pref/AlwaysShowIntroCheckBox
32onready var pause_show_items_in_radar_check = $UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer/TabContainer/Pref/ShowItemsInRadarCheckBox
33onready var intro_menu = $UI/IntroMenu
34onready var intro_always_show_intro_check = $UI/IntroMenu/CenterContainer/PanelContainer/VBoxContainer/AlwaysShowIntroCheckBox
35onready var health_label = $Graphics/Alive/StaticControls/HealthLabel
36onready var trails = $Graphics/Alive/Trails
37onready var death_screen = $UI/DeathScreen
38onready var final_score_label = $UI/DeathScreen/CenterContainer/PanelContainer/VBoxContainer/FinalScoreLabel
39onready var player_to_target = $Graphics/Alive/PlayerToTarget
40
41const Utils = preload("res://utils/Utils.gd")
42const PopupControl = preload("res://utils/Popup.tscn")
43
44var weapon: Weapon
45var dead = false
46var velocity: Vector2 = Vector2.UP * move_speed
47var settings: Settings
48const zoom_step = 1.01
49
50func create_default_weapon() -> Weapon:
51 return Weapons.create_basic_gun()
52
53func _ready() -> void:
54 velocity= Vector2.UP * move_speed
55 weapon = create_default_weapon()
56
57# Utils.remove_user_settings()
58 settings = Utils.load_user_settings()
59 settings.save_user_settings()
60 pause_always_show_intro_check.pressed = settings.show_intro
61 pause_show_items_in_radar_check.pressed = settings.show_items
62 if settings.show_intro:
63 show_intro()
64
65func _process(_delta: float) -> void:
66 if dead:
67 return
68
69 # handle up/down actions
70 if Input.is_action_pressed("up"):
71 camera.zoom /= zoom_step
72 if Input.is_action_pressed("down"):
73 camera.zoom *= zoom_step
74
75 # set the rotation for static controls
76 static_controls.global_rotation = 0
77
78 # set progress bar value and color
79 var pb: ProgressBar = $Graphics/Alive/StaticControls/ProgressBar
80 var health_ratio = float(health) / float(max_health)
81 var 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
89
90 # add history to the trail
91 trails.add_position(global_position)
92
93 # set HUD info, like score and current weapon
94 hud.set_current_weapon(weapon)
95 hud.score = score
96
97 # shoot at closest enemy
98 var enemies = get_tree().get_nodes_in_group("enemies")
99 for e in enemies:
100 var enemy: Enemy = e
101 enemy.is_targeted = false
102
103 var filteredEnemies = Utils.filter(funcref(self,"filter_invalid_enemies"), enemies)
104 if filteredEnemies.size() > 0:
105 filteredEnemies.sort_custom(self, "sort_enemies_ascending_distance")
106 var closest_enemy: Enemy = filteredEnemies[0]
107 var dist = closest_enemy.global_position.distance_to(self.global_position)
108 if dist < self.weapon.shooting_range:
109 closest_enemy.is_targeted = true
110 self.shoot_at(closest_enemy.global_position)
111 player_to_target.points[0] = self.global_position
112 player_to_target.points[1] = closest_enemy.global_position
113
114func filter_invalid_enemies(enemy: Enemy) -> bool:
115 if enemy.dead:
116 return false
117 return true
118
119func sort_enemies_ascending_distance(a: Enemy,b: Enemy):
120 var distA = self.global_position.distance_to(a.global_position)
121 var distB = self.global_position.distance_to(b.global_position)
122 var result = distA < distB
123 return result
124
125func _physics_process(delta: float) -> void:
126 if dead:
127 return
128
129 # turn toward target
130 if is_destination_set:
131 turn_towards(destination, delta)
132
133 # limit movement speed
134 var speed = velocity.length()
135 if speed < move_speed:
136 velocity = velocity * (1 + delta * accel_rate)
137 speed = velocity.length()
138 if speed > move_speed:
139 velocity = velocity.normalized() * move_speed
140 velocity = move_and_slide(velocity)
141
142 # rotate to match velocity
143 global_rotation = velocity.angle() + PI/2.0
144
145func show_intro() -> void:
146 get_tree().paused = true
147 intro_menu.visible = true
148
149func clear_radar():
150 radar_sprite.modulate = Color(1,1,1,0)
151 var children = radar_container.get_children()
152 for child in children:
153 radar_container.remove_child(child)
154
155func update_radar(items: Array, color: Color):
156 for i in items:
157 var item: Node = i
158 var diff = item.global_position - global_position
159 var dist = diff.length()
160 var ratio = Utils.dist_to_ratio(dist, radar_min_radius, radar_max_radius) * .8 + .2
161 if dist < radar_min_radius:
162 ratio = 0
163 var angle_to = Vector2.UP.angle_to(diff)
164 var sprite: Sprite = radar_sprite.duplicate()
165 sprite.scale = camera.zoom / 4
166 sprite.rotate(angle_to)
167 sprite.modulate = Color(color.r,color.g,color.b,ratio * .5)
168 radar_container.add_child(sprite)
169
170func restart():
171 var _error = get_tree().reload_current_scene()
172
173func kill():
174 if dead or is_invincible:
175 return
176 dead = true
177 emit_signal("player_died")
178 add_high_score(score)
179 $Graphics/Dead.show()
180 $Graphics/Alive.hide()
181 final_score_label.text = "Final score: %s" % score
182 death_screen.show()
183 z_index = -1
184 get_tree().paused = true
185
186func heal(amount: int = 1):
187 if dead:
188 return
189 health += amount
190 add_damage_label(-amount)
191 if health > max_health:
192 health = max_health
193
194func speed_up(delta: int = 10):
195 if dead:
196 return
197 move_speed += delta
198
199func hurt(damage_amount: int = 1):
200 if dead or is_invincible:
201 return
202 else:
203 health -= damage_amount
204 add_damage_label(damage_amount)
205 if health <= 0:
206 kill()
207
208func add_damage_label(damage_amount: int = 5) -> void:
209 var damage_label: Damage = Damage.instance()
210 damage_label.value = -damage_amount
211 damage_label.position = self.position
212 get_tree().root.add_child(damage_label)
213
214func turn_towards(dest: Vector2, _delta: float):
215 var to_dir = (dest - global_position).normalized()
216 var curr_dir = velocity.normalized()
217 var dir_diff = to_dir - curr_dir
218 var change = dir_diff * turn_rate * move_speed
219 velocity = (velocity + change).normalized() * move_speed
220
221func shoot_at(pos: Vector2):
222 if weapon.ammo != INF && weapon.ammo <= 0:
223 weapon = create_default_weapon()
224 weapon.try_shoot(self, pos)
225
226func set_objective(objective: String) -> void:
227 hud.objective = objective
228
229func _on_StartButton_pressed() -> void:
230 get_tree().paused = false
231 intro_menu.visible = false
232 settings.show_intro = intro_always_show_intro_check.pressed
233 settings.save_user_settings()
234
235func set_intro_dialog(enabled: bool) -> void:
236 settings.show_intro = enabled
237 settings.save_user_settings()
238
239func _on_PauseAlwaysShowIntroCheckBox_pressed() -> void:
240 var show_intro = pause_always_show_intro_check.pressed
241 set_intro_dialog(show_intro)
242
243func _on_ShowItemsInRadarCheckBox_pressed() -> void:
244 var show_items = pause_show_items_in_radar_check.pressed
245 settings.show_items = show_items
246 settings.save_user_settings()
247
248func sort_high_scores(a, b):
249 return (a as Settings.HighScore).score > (b as Settings.HighScore).score
250
251func add_high_score(new_score: int) -> void:
252 var hs = Settings.HighScore.new(new_score, Time.get_datetime_dict_from_system())
253 var hss = settings.high_scores
254 hss.push_back(hs)
255 hss.sort_custom(self, "sort_high_scores")
256 while hss.size() > 10:
257 hss.pop_back()
258 settings.high_scores = hss
259 settings.save_user_settings()
260
261func _on_LinkButton_pressed() -> void:
262 get_tree().paused = true
263 var _err = OS.shell_open("https://code.fuzzyworld.net/godot-shooter/")
264 get_tree().paused = true
265
266func _on_HUD_position_set(event_pos: Vector2) -> void:
267 var width = ProjectSettings.get("display/window/size/width")
268 var height = ProjectSettings.get("display/window/size/height")
269 var mid = Vector2(width / 2.0, height / 2.0)
270 var pos = camera_scale(event_pos) + self.position - camera_scale(mid)
271 set_destination(pos)
272 emit_signal("destination_set")
273
274func camera_scale(pos: Vector2) -> Vector2:
275 var scaled_pos = Vector2(pos.x * camera.zoom.x, pos.y * camera.zoom.y)
276 return scaled_pos
277
278func show_message(message: String, duration_seconds: int = 2) -> void:
279 hud.show_message(message, duration_seconds)
280
281func _on_HUD_paused() -> void:
282 get_tree().paused = true
283 pause_menu.show()
284 clear_destination()
285
286func _on_ResumeButton_pressed() -> void:
287 get_tree().paused = false
288 pause_menu.hide()
289
290func _on_HUD_debug_signal() -> void:
291 emit_signal("debug")
292
293func set_destination(pos: Vector2) -> void:
294 destination = pos
295 is_destination_set = true
296 emit_signal("destination_set")
297func clear_destination() -> void:
298 is_destination_set = false
299 emit_signal("destination_set")
300
301func _on_StartNewGameButton_pressed() -> void:
302 get_tree().paused = false
303 restart()
304
305
306func _on_Button_pressed() -> void:
307 var _err = get_tree().reload_current_scene()
308 get_tree().paused = false