Project2/player/Player.gd
2024-10-25 13:21:58 +02:00

278 lines
8.3 KiB
GDScript

# An example of a script that could be on an enemy/player (in this case enemy).
# Controls state changes and state input such as direction, could use
# area detectors then change the state for example.
extends CharacterBody2D
func current_state():
return stm.state
@onready var stm = $StateMachine
@onready var animation_player = $AnimationPlayer
@onready var player_character_sprite = $PlayerCharacterSprite
@onready var camera = $Camera
@onready var air = $StateMachine/Air
@onready var run = $StateMachine/Run
@onready var dash = $StateMachine/Dash
@onready var wallslide = $StateMachine/Wallslide
@onready var hitbox_up = $Hitboxes/Up
@onready var hitbox_down = $Hitboxes/Down
@onready var hitbox_side = $Hitboxes/Side
@export var buffer_jump := 0.0
var buffer_jump_tmp := 0.0
@export var coyote_jump := 0.0
var coyote_jump_tmp := coyote_jump
@export var double_jump_delay := 0.0
var double_jump_delay_tmp := 0.0
var double_jump_delay_initiated := false
@export var dash_time: float
var dash_time_tmp := dash_time
@export var hit_time: float
var hit_time_tmp: float
var hit_active := false
@export var pogo_jump: float
@export var wavedash_velocity: Vector2
@export var camera_look_aside := 0.0
@export var camera_look_aside_transition_speed := 0.0
var lives := 5
var double_jump_used := false
var dash_used := false
# Not an animation variable
var looking_right := true
# Animation variables 😀
var previous_input_dir := get_dir()
var previous_velocity := Vector2.ZERO
func _ready():
camera
stm.state_debug_stat = { "key": "player state" }
stm.state_debug_log = { "description": "player state" }
air.jump_debug_stat = { "key": "player jump curve" }
air.air_run_debug_stat = { "key": "player run curve" }
run.run_debug_stat = { "key": "player run curve" }
func update_looking_right():
var dir = get_dir()
if dir.x < 0:
looking_right = false
if dir.x > 0:
looking_right = true
func play_anim(anim_name):
var full_name = anim_name + "_" + ("r" if looking_right else "l")
# Play the AnimatedSprite2D
if player_character_sprite.sprite_frames.has_animation(full_name):
player_character_sprite.play(full_name)
elif player_character_sprite.sprite_frames.has_animation(anim_name):
player_character_sprite.play(anim_name)
else:
printerr("No animation: " + anim_name + ", or: " + full_name)
# Play AnimationPlayer
if animation_player.has_animation(full_name):
animation_player.play(full_name)
elif animation_player.has_animation(anim_name):
animation_player.play(anim_name)
# General structue: mimics querries of state, than acts apon that
func _physics_process(delta):
# Looking right (both animation and logic)
update_looking_right()
# Update camera position
var camera_pos_target = camera_look_aside if looking_right else - camera_look_aside
camera.position.x = lerp(
camera.position.x,
camera_pos_target,
delta * camera_look_aside_transition_speed
)
# Flip player nodes that want to be flipped
for child in self.find_children("*"):
if child.is_in_group("flip_me"):
child.scale.x = 1 if looking_right else -1
# Animations!
if current_state() in [run]:
if previous_input_dir.sign().x - get_dir().sign().x == 0:
if velocity.is_zero_approx():
play_anim("idle")
else:
play_anim("run")
if current_state() in [air]:
if previous_velocity.y <= 0 && velocity.y > 0:
play_anim("fall")
previous_input_dir = get_dir()
previous_velocity = velocity
# Jump buffering
if Input.is_action_just_pressed("jump"):
buffer_jump_tmp = buffer_jump
else:
buffer_jump_tmp -= delta
# Start falling
if current_state() in [run] && not is_on_floor():
if coyote_jump_tmp <= 0:
# Fall
coyote_jump_tmp = coyote_jump
play_anim("fall")
stm.transition(air)
else:
coyote_jump_tmp -= delta
if Input.is_action_just_pressed("jump"):
do_jump()
StatHub.stat_set({ "key": "player did coyote jump", "indicate": true })
return
else:
coyote_jump_tmp = coyote_jump
# Land
if current_state() in [air, wallslide] && is_on_floor():
double_jump_used = false
dash_used = false
if buffer_jump_tmp > 0:
buffer_jump_tmp = 0
double_jump_delay_initiated = false
play_anim("jump")
stm.transition(air, { "jump_stats" : run.jump() })
StatHub.stat_set({ "key": "player jumped buffered jump", "indicate": true })
else:
if velocity.is_zero_approx():
play_anim("idle")
else:
play_anim("run")
stm.transition(run)
# Wallslide
if current_state() in [air, dash] && is_on_wall():
if (looking_right && get_dir().x > 0) || (!looking_right && get_dir().x < 0):
if velocity.y > 0 || current_state() in [dash]:
play_anim("wallslide")
stm.transition(wallslide)
double_jump_used = false
dash_used = false
else:
if Input.is_action_just_pressed("jump"):
stm.transition(air, { "jump_stats" : current_state().wall_jump() })
return
# Stop wallslide
if current_state() in [wallslide]:
var test_vector := Vector2.ZERO
if looking_right:
test_vector.x = 1
else:
test_vector.x = -1
if move_and_collide(test_vector, true) == null:
stm.transition(air)
# Jump
if current_state() in [run] && Input.is_action_just_pressed("jump"):
do_jump()
return
# Double Jump
if current_state() in [air]:
if double_jump_delay_initiated:
if double_jump_delay_tmp <= 0 && !double_jump_used:
double_jump_delay_initiated = false
double_jump_used = true
do_jump()
StatHub.stat_set({ "key": "player did double jump", "indicate": true })
else:
double_jump_delay_tmp -= delta
elif Input.is_action_just_pressed("jump"):
double_jump_delay_initiated = true
double_jump_delay_tmp = double_jump_delay
else:
double_jump_delay_initiated = false
# Wall Jump
if current_state() in [wallslide] && Input.is_action_just_pressed("jump"):
do_jump()
# Continue jumping
if current_state() in [air]:
current_state().continue_jump(Input.is_action_pressed("jump"))
# Walk left/right
if current_state() in [run, air]:
current_state().direction = get_dir().x
# Do dash
if current_state() in [run, air] && Input.is_action_just_pressed("dash") && !dash_used:
dash_used = true
dash_time_tmp = dash_time
var dir := get_dir()
if dir == Vector2.ZERO:
if looking_right: dir.x = 1
else: dir.x = -1
play_anim("dash")
stm.transition(dash)
current_state().direction = dir
# Done with dash
if current_state() in [dash]:
if dash_time_tmp <= 0:
play_anim("fall")
stm.transition(air) # TODO: maybe ground?
else:
dash_time_tmp -= delta
current_state().progression = (dash_time - dash_time_tmp) / dash_time
# Wavedash
if current_state() in [dash]:
if is_on_floor() && Input.is_action_just_pressed("jump") && \
abs(get_dir().x) - abs(get_dir().y) == 0:
stm.transition(air)
velocity = wavedash_velocity * get_dir()
# Hitboxes
if current_state() in [run, air]:
if hit_active:
if hit_time_tmp <= 0:
hit_active = false
disable_hitboxes()
else:
hit_time_tmp -= delta
else:
if looking_right: hitbox_side.get_parent().scale.x = 1
else: hitbox_side.get_parent().scale.x = -1
if Input.is_action_just_pressed("attack"):
hit_active = true
hit_time_tmp = hit_time
var dir = get_dir()
if dir.y < 0:
hitbox_up.set_active(true)
elif dir.y > 0:
hitbox_down.set_active(true)
else:
hitbox_side.set_active(true)
else:
disable_hitboxes()
current_state().update(delta)
StatHub.stat_set({ "key": "player velocity", "value": str(velocity) })
func disable_hitboxes():
for hitbox in [hitbox_up, hitbox_down, hitbox_side]:
hitbox.set_active(false)
func do_jump():
play_anim("jump")
stm.transition(air, { "jump_stats" : current_state().jump() })
func get_dir() -> Vector2:
var analog := Vector2.ZERO
if Input.is_action_pressed("walk_left_analog"):
analog.x += -1
if Input.is_action_pressed("walk_right_analog"):
analog.x += 1
if Input.is_action_pressed("walk_up_analog"):
analog.y += -1
if Input.is_action_pressed("walk_down_analog"):
analog.y += 1
var digital = Vector2(Input.get_axis("walk_left", "walk_right"), Input.get_axis("walk_up", "walk_down"))
if analog != Vector2.ZERO: return analog
else: return digital
func _on_down_hit(_data):
dash_used = false
double_jump_used = false
velocity.y = - pogo_jump
func _on_hurt_box_hurt(_data):
lives -= 1
StatHub.stat_set({"key": "player health", "value": str(lives), "indicate": true})