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"
312 lines
8.1 KiB
GDScript
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()
|