hero-mark-2/objects/player/player.gd
Hazel Snider a2974d8dd3 the damage system refactor!
killing is now done based on groups, the same way enemies already killed
the player.

these groups go on the entity's hitbox, not the entity itself.
enemies' hitboxes have all been put in "enemy_hitbox"

the arrow_projectile has a variable for the target group, so it could
easily be simply set to "player" for arrows shot by enemies

blocking is also done with groups. any hitbox with "blocks_arrow"
will block arrows, same with "blocks_sword" and "blocks_squash"
2023-01-20 00:06:34 -05:00

312 lines
8.1 KiB
GDScript

extends KinematicBody2D
const ArrowProjectile = preload("res://objects/player/arrow_projectile.tscn")
##CLEAN UP CODE LATER
##Children
onready var sprite = $Sprite
onready var climb_ray = $ClimbRay
onready var anims = $AnimationPlayer
onready var sword_sprite = $SwordSprite
onready var sword_hitbox = $SwordArea
onready var death_particles = $DeathSplatter
onready var dust_particles = $DustParticles
#Map
onready var map = get_owner()
##States
enum State {IDLE,WALK,JUMP,FALL,STUNNED,CLIMB,SWORD,SHOOT,INACTIVE}
var current_state = State.IDLE
##Runtime
var axis = Vector2.ZERO #Current direction being held
var trail_color = Color(0.25,0,1,0.4)
#Physics
var velocity = Vector2.ZERO
var walk_speed = 50
var gravity = 12
var jump_pressure = 0
var jump_force = 150
var doublejump_force = 120
var current_ladder = null #Used for checking climbing every frame instead of area entered
var can_doublejump = true
var can_move_in_air = false
#Positions
var arrowpos = Vector2(5,3)
##Preload
#Set initial respawn point
func _ready():
Game.respawn_point = global_position
func _physics_process(delta):
axis = Vector2(Input.get_axis("ui_left","ui_right"),Input.get_axis("ui_up","ui_down"))
#Check ladder
check_ladder()
match current_state:
State.INACTIVE:
return
State.IDLE:
_process_idle()
continue
State.WALK:
_process_walk()
continue
State.IDLE, State.WALK:
_process_idle_walk()
continue
State.JUMP:
_process_jump()
continue
State.FALL:
_process_fall()
continue
State.JUMP, State.FALL:
_process_jump_fall()
continue
State.CLIMB:
_process_climb()
continue
State.SWORD:
_process_sword()
continue
State.SHOOT:
_process_shoot()
#Gravity
if current_state != State.CLIMB:
velocity.y += gravity
#Cut y velocity when hitting ceiling
if is_on_ceiling():
current_state = State.FALL
position.y += 1
velocity.y = 0
#Apply velocity
move_and_slide(velocity,Vector2.UP)
func _process_idle():
if anims.get_current_animation() != "idle": anims.play("idle")
#Stop
velocity.x = 0
#Goto Walk
if axis.x != 0: current_state = State.WALK
func _process_walk():
if anims.get_current_animation() != "walk": anims.play("walk")
#Move
move(walk_speed,0,true)
#Goto Idle
if axis.x == 0: current_state = State.IDLE
#Push Blocks
for i in get_slide_count():
var collision = get_slide_collision(i)
if collision.get_collider().is_in_group("pushable"):
collision.get_collider().push(collision.normal)
func _process_idle_walk():
can_doublejump = false
can_move_in_air = false
velocity.y = 0
#Goto Fall
if !is_on_floor(): current_state = State.FALL
#Goto Jump
check_jump()
#Goto Sword
if Input.is_action_just_pressed("sword"):
Game.play_sound(Game.a_sword,Game.ac_jump)
current_state = State.SWORD
return
#Goto Shoot
check_shoot()
func _process_jump():
jump_pressure += 1
#Pressure sensitive jump
if jump_pressure == 15 or Input.is_action_just_released("jump"):
velocity.y = -jump_force / 4
#velocity.y = 0
current_state = State.FALL
func _process_fall():
if anims.get_current_animation() != "doublejump": anims.play("jump")
#Return to idle
if is_on_floor():
current_state = State.IDLE
return
#Cant move in air
if !can_move_in_air: velocity.x = 0
func _process_jump_fall():
check_double_jump()
move(walk_speed,0,true)
#Goto Shoot
check_shoot()
func _process_climb():
can_move_in_air = true
can_doublejump = true
#Graphics
anims.play("climb")
anims.set_speed_scale(abs(axis.y))
#Climb
position.y += axis.y * 0.65
#Sound
if axis.y == -1:
if Game.ac_climb.get_stream() != Game.a_climb_up: Game.play_sound(Game.a_climb_up,Game.ac_climb)
if axis.y == 1:
if Game.ac_climb.get_stream() != Game.a_climb_down: Game.play_sound(Game.a_climb_down,Game.ac_climb)
if axis.y == 0: Game.ac_climb.set_stream(null)
#Manual Jump,, only works when holding neutral or away from ladder
if axis.x != sprite.scale.x: check_jump()
if climb_ray.get_collider() == null:
if axis.y == -1:
#Auto Jump
velocity.y = -jump_force
Game.play_sound(Game.a_jump,Game.ac_jump)
#Auto dismount
Game.ac_climb.set_stream(null)
current_state = State.FALL
return
func _process_sword():
anims.play("stab")
#Stop
velocity.x = 0
sword_sprite.scale.x = sprite.scale.x
#Move hitbox with flip
sword_hitbox.position.x = sprite.scale.x * 10
#Return to idle after animationplayer end anim signal
func _process_shoot():
#Stop
velocity.x = 0
if anims.get_current_animation() == "shoot air":
#Cancel air shoot animation when grounded
if is_on_floor():
current_state = State.IDLE
return
move(walk_speed,0,true)
func spawn_arrow():
Game.play_sound(Game.a_shoot,Game.ac_jump)
var arrow = ArrowProjectile.instance()
arrow.global_position = Vector2(
global_position.x + arrowpos.x * sprite.scale.x,
global_position.y + arrowpos.y
)
arrow.direction = sprite.scale.x
map.add_child(arrow)
func check_jump():
if Input.is_action_just_pressed("jump"):
#Detach ladder
if current_state == State.CLIMB:
Game.ac_climb.set_stream(null) # stop climb sound
position.x -= sprite.scale.x * 5
else:
dust_particles.restart()
anims.set_speed_scale(1)
# Jump
can_doublejump = true
can_move_in_air = true
velocity.y = 0
jump_pressure = 0
current_state = State.JUMP
Game.play_sound(Game.a_jump,Game.ac_jump)
anims.play("jump")
velocity.y = -jump_force
move(walk_speed,0,true)
func check_double_jump():
if Input.is_action_just_pressed("jump") && can_doublejump:
Game.play_sound(Game.a_doublejump,Game.ac_jump)
can_doublejump = false
velocity.y = -doublejump_force
anims.play("doublejump")
func check_shoot():
#Only Shoot if have arrows and there are no arrows onscreen
if Input.is_action_just_pressed("shoot") && Game.arrows > 0 && get_tree().get_nodes_in_group("arrow").size() == 0:
current_state = State.SHOOT
if is_on_floor():
anims.play("shoot grounded")
else:
anims.play("shoot air") #Shoot immediately in air
func move(hsp,vsp,flip:bool):
if is_on_floor() or can_move_in_air:
velocity.x = hsp * axis.x
#Flip
if flip: if sign(axis.x) != 0: sprite.scale.x = axis.x
func check_ladder():
if climb_ray.get_collider() != null:
current_ladder = climb_ray.get_collider().get_parent()
#Stop the velocity
velocity = Vector2.ZERO
#Snap to closest side
if position.x < current_ladder.middle:
position.x = current_ladder.left_snap.global_position.x
sprite.scale.x = 1
else:
position.x = current_ladder.right_snap.global_position.x
sprite.scale.x = -1
#Start Climbing
current_state = State.CLIMB
#Move the raycast
#climb_ray.position.x = 4 * sprite.scale.x
func die():
var new_particles = death_particles.duplicate()
get_parent().add_child(new_particles)
new_particles.global_position = global_position
new_particles.emitting = true
sprite.visible = false
Game.lives -= 1
if Game.lives < 0:
new_particles.amount = 64
new_particles.lifetime = 0.45
new_particles.speed_scale = 1.5
current_state = State.INACTIVE
Game.play_sound(Game.a_gover, Game.ac_die)
var time_tween = get_tree().create_tween()
time_tween.tween_property(Engine, "time_scale", 0.1, 0.3)
Game.ac_music.stop()
yield(time_tween, "finished")
yield(get_tree().create_timer(1.0 * 0.1), "timeout")
Game.call_deferred("restart_level")
else:
Game.play_sound(Game.a_die, Game.ac_die)
yield(Game.freeze_frame(0.3), "timeout")
position = Game.respawn_point
current_state = State.IDLE
sprite.visible = true
func _on_AnimationPlayer_animation_finished(anim_name):
#Return to idle after slash
if anim_name == "stab":
current_state = State.IDLE
return
#Return to idle after grounded shoot
if anim_name == "shoot grounded":
current_state = State.IDLE
return
#Return to fall or idle after air shoot
if anim_name == "shoot air":
if is_on_floor():
current_state = State.IDLE
return
else:
current_state = State.FALL
return
func _on_SwordArea_area_entered(area):
if area.is_in_group("enemy_hitbox"):
var target = area.get_parent()
# create block text and return if blocked
if area.is_in_group("blocks_sword"):
var pos = target.global_position
Game.instance_node(Game.block_text, pos.x, pos.y, target.get_parent())
return
else:
target.die()