-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPlayer.gd
405 lines (287 loc) · 10.2 KB
/
Player.gd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
extends Entity
class_name Player
@export_category("Player Adjustments")
@export var MaxHP = 100
@export var DAMAGE = 10
@export var iDuration = 1.0
@export var trauma_regress = 0.1
var current_iduration = 0
var trauma = 0.0
@export_category("Movement Adjustments")
@export var SPEED = 15
@export var JUMP_VELOCITY = 11
@export var punch_decrease = 1.0
@export var max_punch = 40.0
@export var coyote_time = 0.1
var player_rotation = 0
var rotation_speed = 15
var jump_able = false # variable needed for coyote time
var current_coyote_time = 0.0
var last_y_velocity = 0.0
@export_category("Features Adjustments")
@export_group("Melee Attack")
@export var melee_damage = 12.0
@export var melee_cooldown = 0.3
@export var melee_knockback = Vector3(15, 13, 0)
var current_melee_cooldown = 0.0
@export_group("Abilities")
@export var ability_container: Node3D
@export var start_ability = 0
@export var abilities: Array[PackedScene]
var instantiated_abilities = []
var current_ability: BaseSkill
###########################
# Character Leveling System
###########################
var level = 1
var experience = 0
var experience_required = get_required_exp(level + 1)
var experience_total = 0
###########################
# others, to sort.
###########################
@onready var anim_tree = $AnimationTree
var current_health = 0
var last_direction = 1
var last_movement := Vector3.ZERO
var timer_on = false
var punch_impact = 1.0
var temp_punch_impact = 0.0
var fragments = 0
var is_dead = false
var player_data = {}
var load_player_data = false
signal ability_changed(ability: BaseSkill)
signal vitality_changed(current_hp, max_health)
signal fragment_collected(fragments)
func _ready():
super()
randomize()
current_health = MaxHP
instantiate_abilities()
if not Game.getPlayer():
Game.SetPlayer(self)
func _physics_process(delta):
if is_dead:
return
apply_gravity(delta)
process_coyote_time(delta)
calculate_punch()
process_iDuration(delta)
move_and_slide()
super(delta)
# Handle Animations outside of the physics loop. (aka don't mix physics and visuals)
func _process(delta):
if load_player_data:
LoadPlayerData(player_data)
if is_dead:
return
super(delta)
# Handle Input in Process because we usually have more frames in here.
# Also because when testing Michel pointed out that there seems to be an input delay
# which probably only is because _process gets called more frequently than _physics_process
# Change look direction. Could be made smoother too!
update_rotation()
handle_trauma(delta)
process_state_machine(delta)
process_melee_cooldown(delta)
#####################################################################
################# Ability Functions ############################
#####################################################################
func SetPlayerData(data):
player_data = data
load_player_data = true
func LoadPlayerData(data):
load_player_data = false
if "score" in data:
fragments = data["score"]
emit_signal("fragment_collected", fragments)
if "health" in data:
current_health = data["health"]
emit_signal("vitality_changed", current_health, MaxHP)
func GetPlayerData():
return {
"score": fragments,
"health": current_health
}
func instantiate_abilities():
for ability in abilities:
var instance = ability.instantiate()
add_child(instance)
instance.Setup(self, ability_container)
instantiated_abilities.append(instance)
func set_ability(ability_idx):
if not current_ability == null:
current_ability.Destroy()
current_ability.visible = false
current_ability.process_mode = Node.PROCESS_MODE_DISABLED
var new_ability = instantiated_abilities[ability_idx]
new_ability.Setup(self, ability_container)
current_ability = new_ability
current_ability.visible = true
current_ability.process_mode = Node.PROCESS_MODE_INHERIT
emit_signal("ability_changed", current_ability)
func toggle_ability():
start_ability += 1
if start_ability >= len(instantiated_abilities):
start_ability = 0
if start_ability < len(instantiated_abilities):
set_ability(start_ability)
#####################################################################
################# Fight Functions ############################
#####################################################################
func DealDamage(Damage):
if is_dead:
return
if current_iduration > 0.0:
return
current_iduration = iDuration
current_health -= Damage
emit_signal("vitality_changed", float(current_health), float(MaxHP))
$SoundPlayer.play("Player/hit")
if current_health <= 0:
die()
func Absorbed(exp: float):
if is_dead:
return
# experience += exp
current_health = clamp(exp + current_health, 0, 100)
emit_signal("vitality_changed", float(current_health), float(MaxHP))
func absorb():
var bodies = $AbsorbArea.get_overlapping_bodies()
for body in bodies:
if body.has_method("AbsorbMonster"):
body.AbsorbMonster(self)
func process_iDuration(delta):
current_iduration = clampf(current_iduration - delta, 0.0, iDuration)
#####################################################################
################# Leveling Functions #########################
#####################################################################
func get_required_exp(level):
return round(pow(level, 1.8) + level * 4)
# Check this formula below.
# https://www.desmos.com/calculator/obopqlufy9
func gain_experience(amount):
experience_total += amount
experience += amount
while experience >= experience_required:
experience -= experience_required
level_up()
func level_up():
level += 1
experience_required = get_required_exp(level + 1)
var stats = ['MaxHP', 'DAMAGE']
# Picks one random stat from the pool above to boost up.
var random_stat = stats[ randi() % stats.size() ]
# Increases the selected stat by a random amount between 2 and 8
set(random_stat, get(random_stat) + randi() % 8 + 2)
#####################################################################
################# Movement Functions ############################
#####################################################################
func update_rotation():
$PlayerModel.rotation_degrees.y = lerpf(90 * last_direction, $PlayerModel.rotation_degrees.y, 0.6)
func PunchUnclamped(punch_velocity: Vector3, override_punch = false):
trauma += clampf(0.5 - trauma, 0.0, 0.5)
if override_punch:
velocity = punch_velocity
else:
velocity += punch_velocity
punch_impact = velocity.length()
temp_punch_impact = punch_impact
func Punch(punch_velocity: Vector3, override_punch = false):
var max_punch_vector = Vector3.ONE * max_punch
trauma += clampf(0.5 - trauma, 0.0, 0.5)
if override_punch:
velocity = punch_velocity
else:
velocity += punch_velocity
velocity = velocity.clamp(-max_punch_vector, max_punch_vector)
punch_impact = velocity.length()
temp_punch_impact = punch_impact
func calculate_punch():
temp_punch_impact = clampf(temp_punch_impact - punch_decrease, 0.0, punch_impact)
if last_movement.length() > 0.0:
velocity.x = lerpf(last_movement.x * SPEED, velocity.x, lerpf(0.5, 1.0, temp_punch_impact / punch_impact))
last_direction = 1 if last_movement.x > 0 else -1
else:
velocity.x = lerpf(move_toward(velocity.x, 0, SPEED), velocity.x, temp_punch_impact / punch_impact)
func apply_gravity(delta):
if not is_on_floor():
velocity.y -= gravity * delta
last_y_velocity = velocity.y
else:
if not jump_able:
$JumpStumpVFX.play()
if last_y_velocity < -100.0:
last_y_velocity = 0.0
trauma += 0.4
func process_coyote_time(delta):
if is_on_floor():
jump_able = true
current_coyote_time = coyote_time
if jump_able and velocity.y > 0:
jump_able = false
elif not is_on_floor() and current_coyote_time > 0:
current_coyote_time = clampf(coyote_time, 0, current_coyote_time - delta)
elif current_coyote_time <= 0:
jump_able = false
func jump():
if jump_able:
global_position += Vector3(0, 0.1, 0)
velocity.y = JUMP_VELOCITY
jump_able = false
$SoundPlayer.play("Player/jump")
func melee_attack():
if current_melee_cooldown > 0:
return
$ghost_hand.rotation.y = deg_to_rad(90) * last_direction
current_melee_cooldown = melee_cooldown
anim_tree.set("parameters/MeleeAttack/request", AnimationNodeOneShot.ONE_SHOT_REQUEST_FIRE)
func melee_trigger():
for body in %HurtArea.get_overlapping_bodies():
if body.has_method("DealDamage"):
body.DealDamage(melee_damage)
if body.has_method("Punch"):
body.Punch(Vector3(last_direction, 1, 0) * melee_knockback)
func die():
trauma = 0.0
process_mode = Node.PROCESS_MODE_ALWAYS
is_dead = true
Game.game_over()
Game.get_current_rig().set_offset(Vector3(0, 2, 14.0))
Game.get_current_rig().toggle_overlays(true)
Game.get_current_rig().screenshake_enabled = false
anim_tree.set("parameters/death_time/seek_request", 0)
anim_tree.set("parameters/dead/blend_amount", 1.0)
$PlayerModel/death_flower/AnimationPlayer.play("Action")
$SoundPlayer.play("Player/die")
func process_melee_cooldown(delta):
current_melee_cooldown = clampf(current_melee_cooldown - delta, 0, melee_cooldown)
func handle_trauma(delta):
trauma = clampf(trauma - trauma_regress * delta, 0.0, 1.0)
func _unhandled_input(event):
if event.is_action_pressed("jump", true):
jump()
get_viewport().set_input_as_handled()
if event.is_action_pressed("attack"):
melee_attack()
get_viewport().set_input_as_handled()
if event.is_action_pressed("absorb"):
absorb()
get_viewport().set_input_as_handled()
if event.is_action("move_left") or event.is_action("move_right"):
last_movement.x = Input.get_axis("move_left", "move_right")
get_viewport().set_input_as_handled()
for ability in instantiated_abilities:
ability.Use(event)
func set_condition(condition_name, condition_value):
anim_tree.set("parameters/StateMachine/conditions/" + condition_name, condition_value)
func process_state_machine(_delta):
set_condition("is_jumping", !is_on_floor() and velocity.y > 0)
set_condition("is_on_floor", is_on_floor())
set_condition("is_falling", !is_on_floor() and velocity.y < 0)
anim_tree.set("parameters/hurt_amount/blend_position", 1.0 if current_iduration > 0.0 else 0.0)
anim_tree["parameters/StateMachine/IdleWalk/IdleWalkBlend/blend_amount"] = abs(velocity.x / SPEED)
func PickUp(item):
fragments += item
emit_signal("fragment_collected", fragments)