Refactored player code and added quick access to cardinal directions to EarthALigner
This commit is contained in:
parent
20dea147ba
commit
d9dae634ef
9 changed files with 109 additions and 76 deletions
|
|
@ -1,5 +1,4 @@
|
|||
extends CharacterBody2D
|
||||
@onready var earthaligner = $EarthAligner
|
||||
@onready var player = get_tree().get_root().get_node("main/Player")
|
||||
var moves = ["slam", "wave", "water_rise", "splash"]
|
||||
@export var big_blob : PackedScene
|
||||
|
|
@ -50,7 +49,7 @@ func _process(_delta: float) -> void:
|
|||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if dead: return
|
||||
up_direction = earthaligner.global_from_local(Vector2.UP)
|
||||
up_direction = $EarthAligner.up
|
||||
if(is_on_floor()):
|
||||
grounded.emit()
|
||||
if idle_move: move_idle(delta)
|
||||
|
|
@ -62,7 +61,7 @@ func move_idle(delta : float):
|
|||
# Pick a random target roughly above the player's head every 0.5 seconds.
|
||||
idle_dir_remaining -= delta
|
||||
if(idle_dir_remaining <= 0):
|
||||
target_pos = player.position + player.earth_aligner.global_from_local(Vector2.UP) * 400
|
||||
target_pos = player.position + player.get_node("EarthAligner").up * 400
|
||||
target_pos += randf_range(0, max(200, (target_pos - global_position).length())*0.25) * Vector2.from_angle(randf_range(0,TAU))
|
||||
idle_dir = (target_pos - global_position).normalized()* max(200, (target_pos - global_position).length()) * 0.4
|
||||
idle_dir_remaining = 0.5
|
||||
|
|
|
|||
|
|
@ -9,5 +9,5 @@ func _on_water_water_reached_max_height() -> void:
|
|||
# The boss spawns once the water has risen to its maximum height.
|
||||
var node = boss.instantiate()
|
||||
add_sibling(node)
|
||||
node.position = %Player.position + %Player.earth_aligner.local_from_global(Vector2.UP) * 1000;
|
||||
node.position = %Player.position + %Player.get_node("EarthAligner").up * 1000;
|
||||
queue_free()
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ func _process(delta: float) -> void:
|
|||
if dead: return
|
||||
# Fall slowly while not grounded
|
||||
if not $GroundSensor.has_overlapping_bodies():
|
||||
position += 200 * delta * $EarthAligner.global_from_local(Vector2.DOWN)
|
||||
position += 200 * delta * $EarthAligner.down
|
||||
|
||||
var y = position.length()
|
||||
var ratio = - move_dir * broadth / (2 * y)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ func activate():
|
|||
get_tree().get_root().add_child(arrow)
|
||||
arrow.position = player.position
|
||||
arrow.rotation = player.rotation
|
||||
arrow.direction = player.earth_aligner.global_from_local(Vector2(player.facing, 0))
|
||||
arrow.direction = player.get_node("EarthAligner").right * player.facing
|
||||
|
||||
# Make sure the arrow sprite faces the right direction
|
||||
if(player.facing == -1):
|
||||
|
|
|
|||
|
|
@ -2,14 +2,15 @@ extends ActiveItem
|
|||
@export var cooldown = 0.2
|
||||
var dash_time = 0.15
|
||||
var dash_timer : SceneTreeTimer
|
||||
var dash_dir
|
||||
var dash_velocity
|
||||
var dash_speed = 1600
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
super(delta)
|
||||
# While the dash is active, move the player in the
|
||||
# (absolute!) direction fixed at the start of the dash
|
||||
if dash_timer != null and dash_timer.time_left > 0:
|
||||
player.reset_to_velocity = player.earth_aligner.local_from_global(dash_dir)
|
||||
player.reset_to_velocity = player.get_node("EarthAligner").local_from_global(dash_velocity)
|
||||
|
||||
func actually_collect():
|
||||
player.set_cooldown(cooldown)
|
||||
|
|
@ -20,8 +21,8 @@ func activate():
|
|||
# The dash refills one air jump if possible and provides iframes.
|
||||
player.air_jumps_current += 1
|
||||
dash_timer = get_tree().create_timer(dash_time)
|
||||
dash_dir = player.earth_aligner.global_from_local(Vector2.RIGHT * player.facing * 1600)
|
||||
player.inv_time = max(player.inv_time, dash_time)
|
||||
dash_velocity = player.get_node("EarthAligner").right * player.facing * dash_speed
|
||||
player.get_node("IFrames").start(dash_time)
|
||||
|
||||
func remove(reset_player_active = true):
|
||||
# If the item is removed during the dash, clear the active item slot,
|
||||
|
|
|
|||
137
player/player.gd
137
player/player.gd
|
|
@ -1,10 +1,4 @@
|
|||
class_name Player extends CharacterBody2D
|
||||
# Child Nodes
|
||||
@onready var camera : Camera2D = $Camera2D;
|
||||
@onready var anim_sprite : AnimatedSprite2D = $AnimatedSprite2D;
|
||||
@onready var earth_aligner : Node2D = $EarthAligner;
|
||||
@onready var sword : Area2D = $Sword;
|
||||
@onready var active_item_cooldown : Timer = $ActiveItemCooldown
|
||||
|
||||
@export var double_jump_animation : PackedScene
|
||||
|
||||
|
|
@ -31,8 +25,11 @@ signal health_changed(new_health : int)
|
|||
signal max_hp_changed(new_max_hp : int)
|
||||
signal player_died
|
||||
|
||||
var hit_iframes = 0.8
|
||||
var current_hp = 5:
|
||||
set(new_hp):
|
||||
# HP can't be increased above Max HP or reduced below 0
|
||||
# When reduced to 0, the player dies.
|
||||
new_hp = min(new_hp, max_hp)
|
||||
if new_hp <= 0:
|
||||
new_hp = 0
|
||||
|
|
@ -41,14 +38,14 @@ var current_hp = 5:
|
|||
current_hp = new_hp
|
||||
health_changed.emit(current_hp)
|
||||
@export var max_hp = 5:
|
||||
# When Max HP is reduced below current health, current health is adjusted.
|
||||
set(new_max_hp):
|
||||
max_hp = new_max_hp
|
||||
if max_hp <= 0:
|
||||
max_hp = 0
|
||||
die()
|
||||
if current_hp > max_hp:
|
||||
current_hp = max_hp
|
||||
max_hp_changed.emit(max_hp)
|
||||
var hit_invulnerability = 0.8
|
||||
var inv_time = 0;
|
||||
var dead = false;
|
||||
|
||||
# Received Knockback
|
||||
|
|
@ -58,10 +55,7 @@ var damage_knockup = 500
|
|||
@export var friction = 0.5
|
||||
|
||||
# Attack Handling
|
||||
var atk_cooldown = 0.35
|
||||
var atk_timer = 0
|
||||
var can_upslash = false
|
||||
|
||||
signal attack
|
||||
|
||||
# Active Item
|
||||
|
|
@ -73,56 +67,57 @@ var active_item : ActiveItem = null:
|
|||
|
||||
|
||||
func set_cooldown(cooldown):
|
||||
active_item_cooldown.wait_time = cooldown
|
||||
$ActiveItemCooldown.wait_time = cooldown
|
||||
|
||||
func activate_cooldown():
|
||||
active_item_cooldown.start()
|
||||
$ActiveItemCooldown.start()
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
# Update the Health Bar initially
|
||||
max_hp_changed.emit(max_hp)
|
||||
health_changed.emit(current_hp)
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
# Velocity management uses the physics framework, hence runs at fix 60FPS
|
||||
manage_velocity(delta)
|
||||
move_and_slide()
|
||||
update_vine_statuses()
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
manage_iframes(delta)
|
||||
# All non-velocity management can run without FPS cap
|
||||
update_vine_statuses()
|
||||
manage_movement_options()
|
||||
manage_interaction()
|
||||
manage_active(delta)
|
||||
manage_animation()
|
||||
manage_attack(delta)
|
||||
|
||||
func manage_iframes(delta: float):
|
||||
if(inv_time > 0):
|
||||
inv_time = max(0, inv_time-delta)
|
||||
|
||||
func manage_attack(delta : float):
|
||||
atk_timer = max(0, atk_timer-delta)
|
||||
if handle_input:
|
||||
if(Input.is_action_just_pressed("attack") and atk_timer <= 0):
|
||||
if Input.is_action_pressed("up") and can_upslash:
|
||||
attack.emit("up")
|
||||
else:
|
||||
attack.emit("horizontal")
|
||||
$AnimatedSprite2D.play("attack")
|
||||
$SwordSwingAudio.play()
|
||||
atk_timer = atk_cooldown
|
||||
manage_active(delta)
|
||||
manage_attack()
|
||||
|
||||
func manage_attack():
|
||||
# If an attack is possible, a signal is sent which the weapon can connect to
|
||||
if(Input.is_action_just_pressed("attack") and $AttackCooldown.time_left <= 0):
|
||||
if Input.is_action_pressed("up") and can_upslash:
|
||||
attack.emit("up")
|
||||
else:
|
||||
attack.emit("horizontal")
|
||||
$Sprite.play("attack")
|
||||
$SwordSwingAudio.play()
|
||||
$AttackCooldown.start()
|
||||
|
||||
func manage_active(_delta : float):
|
||||
if(active_item != null and Input.is_action_just_pressed("item") and active_item_cooldown.is_stopped()):
|
||||
# Activate or remove items. Cooldown + use management is handled by the item.
|
||||
if(active_item != null and Input.is_action_just_pressed("item") and $ActiveItemCooldown.is_stopped()):
|
||||
active_item.trigger_activation()
|
||||
if(Input.is_action_just_pressed("drop_item") and active_item != null):
|
||||
active_item.remove()
|
||||
|
||||
func manage_movement_options() -> void:
|
||||
# Reset Air Jumps when grounded
|
||||
if(is_on_floor()):
|
||||
air_jumps_current = air_jumps_max
|
||||
|
||||
func manage_interaction():
|
||||
# Interacts with all overlapping interactables on button press
|
||||
if Input.is_action_just_pressed("interact"):
|
||||
for area in $InteractBox.get_overlapping_areas():
|
||||
if area.has_method("interact"): area.interact()
|
||||
|
|
@ -135,68 +130,81 @@ func manage_animation() -> void:
|
|||
if(Input.is_action_pressed("move_left")):
|
||||
walk_dir -= 1
|
||||
|
||||
if(walk_dir != 0):
|
||||
# Set the direction the player faces
|
||||
if walk_dir != 0:
|
||||
facing = walk_dir
|
||||
anim_sprite.scale.x = - abs(anim_sprite.scale.x) * facing
|
||||
if(is_on_floor() and not $AnimatedSprite2D.is_playing()):
|
||||
anim_sprite.play("walk")
|
||||
$Sprite.scale.x = - abs($Sprite.scale.x) * facing
|
||||
|
||||
# Play the walk or idle animation when appropriate
|
||||
if(walk_dir != 0):
|
||||
if(is_on_floor() and not $Sprite.is_playing()):
|
||||
$Sprite.play("walk")
|
||||
else:
|
||||
if anim_sprite.animation == "walk":
|
||||
anim_sprite.stop()
|
||||
if $Sprite.animation == "walk":
|
||||
$Sprite.stop()
|
||||
|
||||
|
||||
func manage_velocity(delta: float) -> void:
|
||||
up_direction = (position - earth_center).normalized();
|
||||
var old_local_velocity = earth_aligner.local_from_global(velocity)
|
||||
up_direction = $EarthAligner.up
|
||||
# Convert the current velocity into local coordinates, then compute changes there.
|
||||
# This is important for capped values such as fall speed.
|
||||
var old_local_velocity = $EarthAligner.local_from_global(velocity)
|
||||
# Apply friction horizontally, exponentially. Factor 1 - friction per frame at 60FPS.
|
||||
var local_velocity = Vector2(old_local_velocity.x * pow(1 - friction,60*delta), old_local_velocity.y);
|
||||
local_velocity += Vector2(0, gravity)
|
||||
|
||||
if handle_input:
|
||||
#if has_node("Slow"): print(get_node("Slow").params)
|
||||
# Apply Slow Status if present.
|
||||
var hspeed = base_hspeed if not Status.affects("Slow", self) else base_hspeed * get_node("Slow").params.slow_factor
|
||||
|
||||
# Change the local velocity by the movement speed, or more if moving in the opposite direction,
|
||||
# for more responsive turning around.
|
||||
if(Input.is_action_pressed("move_right")):
|
||||
if local_velocity.x > -700:
|
||||
if local_velocity.x > - 3 * hspeed:
|
||||
local_velocity += Vector2(hspeed, 0)
|
||||
else:
|
||||
local_velocity += Vector2(hspeed/2.0, 0)
|
||||
local_velocity += Vector2(2 * hspeed, 0)
|
||||
if(Input.is_action_pressed("move_left")):
|
||||
if local_velocity.x < 700:
|
||||
if local_velocity.x < 3 * hspeed:
|
||||
local_velocity += Vector2(-hspeed, 0)
|
||||
else:
|
||||
local_velocity += Vector2(-hspeed/2.0, 0)
|
||||
local_velocity += Vector2(-2 * hspeed, 0)
|
||||
|
||||
if(Input.is_action_just_pressed("jump") and (is_on_floor() or air_jumps_current > 0)):
|
||||
var dropped = false
|
||||
if(not is_on_floor()):
|
||||
air_jumps_current -= 1;
|
||||
play_double_jump_animation()
|
||||
elif (Input.is_action_pressed("drop")):
|
||||
dropped = true
|
||||
self.position += earth_aligner.global_from_local(Vector2(0,12))
|
||||
if(not dropped):
|
||||
# If the player holds drop, just move through the platform
|
||||
if Input.is_action_pressed("drop"):
|
||||
self.position += 12 * $EarthAligner.down
|
||||
else:
|
||||
# Otherwise, either jump from the ground or perform a double jump.
|
||||
if is_on_floor():
|
||||
local_velocity.y = -ground_jump_strength
|
||||
else:
|
||||
air_jumps_current -= 1;
|
||||
play_double_jump_animation()
|
||||
local_velocity.y = -air_jump_strength
|
||||
|
||||
if(local_velocity.y > max_fall_speed):
|
||||
local_velocity.y = max_fall_speed
|
||||
|
||||
# When knockback is applied, momentum is reset to the knockback value instead.
|
||||
if(reset_to_velocity.x != 0):
|
||||
local_velocity.x = reset_to_velocity.x
|
||||
if(reset_to_velocity.y != 0):
|
||||
local_velocity.y = reset_to_velocity.y
|
||||
reset_to_velocity = Vector2.ZERO
|
||||
velocity = earth_aligner.global_from_local(local_velocity)
|
||||
|
||||
# Return to world coordinates and apply the changes to velocity.
|
||||
velocity = $EarthAligner.global_from_local(local_velocity)
|
||||
|
||||
|
||||
func hurt(dmg: int, dir: Vector2 = Vector2.ZERO):
|
||||
if(inv_time <= 0):
|
||||
# If the player has no iframes, apply knockback and damage and start iframes.
|
||||
if $IFrames.time_left <= 0:
|
||||
if Status.affects("Vulnerable", self): dmg += 1
|
||||
$AudioStreamPlayer2D.play()
|
||||
current_hp -= dmg
|
||||
inv_time = hit_invulnerability
|
||||
reset_to_velocity = Vector2(-sign(earth_aligner.local_from_global(dir).x)*knockback_strength, -damage_knockup)
|
||||
$IFrames.start(hit_iframes)
|
||||
reset_to_velocity = Vector2(-sign($EarthAligner.local_from_global(dir).x)*knockback_strength, -damage_knockup)
|
||||
return true
|
||||
return false
|
||||
|
||||
|
|
@ -205,15 +213,19 @@ func die():
|
|||
player_died.emit()
|
||||
dead = true
|
||||
|
||||
# Connected to the signal marking the end of an attack.
|
||||
# Returns to default animation.
|
||||
func _on_attack_end():
|
||||
if($AnimatedSprite2D.animation != "idle"):
|
||||
$AnimatedSprite2D.play("idle")
|
||||
$AnimatedSprite2D.stop()
|
||||
if($Sprite.animation != "idle"):
|
||||
$Sprite.play("idle")
|
||||
$Sprite.stop()
|
||||
|
||||
# Take away player control while the Death Screen is active.
|
||||
func _on_death_screen_visibility_changed() -> void:
|
||||
handle_input = !handle_input
|
||||
|
||||
func play_double_jump_animation() -> void:
|
||||
# Instantiate a sprite which plays a double jump animation, then deletes itself.
|
||||
$AirJumpAudio.play()
|
||||
var node = double_jump_animation.instantiate()
|
||||
add_child(node)
|
||||
|
|
@ -221,6 +233,7 @@ func play_double_jump_animation() -> void:
|
|||
node.scale = .5 * Vector2.ONE
|
||||
node.reparent(get_parent())
|
||||
|
||||
# If there is any vine nearby, gain the corresponding statusses.
|
||||
func update_vine_statuses():
|
||||
var location = Grid.get_location_from_world_pos(global_position)
|
||||
for vine : Vine in Grid.vines_per_node[location.x][location.y]:
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ position = Vector2(0.2, 6)
|
|||
scale = Vector2(0.7, 0.72)
|
||||
shape = SubResource("RectangleShape2D_e7oew")
|
||||
|
||||
[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."]
|
||||
[node name="Sprite" type="AnimatedSprite2D" parent="."]
|
||||
scale = Vector2(0.37, 0.37)
|
||||
sprite_frames = SubResource("SpriteFrames_dw050")
|
||||
animation = &"attack"
|
||||
|
|
@ -100,6 +100,14 @@ animation = &"attack"
|
|||
[node name="ActiveItemCooldown" type="Timer" parent="."]
|
||||
one_shot = true
|
||||
|
||||
[node name="IFrames" type="Timer" parent="."]
|
||||
wait_time = 0.8
|
||||
one_shot = true
|
||||
|
||||
[node name="AttackCooldown" type="Timer" parent="."]
|
||||
wait_time = 0.35
|
||||
one_shot = true
|
||||
|
||||
[node name="AudioStreamPlayer2D" type="AudioStreamPlayer2D" parent="."]
|
||||
stream = ExtResource("11_2ieo8")
|
||||
volume_db = 15.0
|
||||
|
|
@ -122,4 +130,4 @@ position = Vector2(0.2, 6)
|
|||
scale = Vector2(0.7, 0.72)
|
||||
shape = SubResource("RectangleShape2D_e7oew")
|
||||
|
||||
[connection signal="animation_finished" from="AnimatedSprite2D" to="." method="_on_attack_end"]
|
||||
[connection signal="animation_finished" from="Sprite" to="." method="_on_attack_end"]
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ func _process(delta: float) -> void:
|
|||
if(slash_timer == 0):
|
||||
anim_sprite.visible = false
|
||||
for area in get_overlapping_areas():
|
||||
var hurt_dir = -get_parent().earth_aligner.global_from_local(Vector2(-facing, 0)).rotated(facing*rotation)
|
||||
var hurt_dir = -get_parent().get_node("EarthAligner").global_from_local(Vector2(-facing, 0)).rotated(facing*rotation)
|
||||
if area.hurt(damage, hurt_dir, dmg_id) and apply_swing_knockback:
|
||||
get_parent().reset_to_velocity += Vector2(swing_knockback * facing, 0)
|
||||
apply_swing_knockback = false
|
||||
|
|
|
|||
|
|
@ -3,6 +3,18 @@ extends Node2D
|
|||
var parent : Node2D
|
||||
@export var center = Vector2.ZERO
|
||||
var angle = 0
|
||||
var up : Vector2 :
|
||||
get():
|
||||
return global_from_local(Vector2.UP)
|
||||
var down : Vector2 :
|
||||
get():
|
||||
return global_from_local(Vector2.DOWN)
|
||||
var right : Vector2 :
|
||||
get():
|
||||
return global_from_local(Vector2.RIGHT)
|
||||
var left : Vector2 :
|
||||
get():
|
||||
return global_from_local(Vector2.LEFT)
|
||||
|
||||
func _ready() -> void:
|
||||
parent = get_parent()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue