120 lines
3.3 KiB
GDScript
120 lines
3.3 KiB
GDScript
class_name Ghost extends CharacterBody2D
|
|
|
|
var move_speed: float = 200.0
|
|
var shift_speed: float = 400.0
|
|
var held_item: Collectable = null
|
|
|
|
enum State { Left, Right, Up, Down }
|
|
var state: State = State.Right
|
|
|
|
@onready var animationPlayer = $AnimationPlayer
|
|
@onready var interaction_area: Area2D = $InteractionArea
|
|
|
|
func _ready() -> void:
|
|
add_to_group("ghost")
|
|
|
|
func _process(_delta: float) -> void:
|
|
if GameManager.game_over:
|
|
velocity = Vector2.ZERO
|
|
return
|
|
var shift := Input.is_action_pressed("shift")
|
|
var direction := Vector2(
|
|
Input.get_action_strength("right") - Input.get_action_strength("left"),
|
|
Input.get_action_strength("down") - Input.get_action_strength("up")
|
|
)
|
|
if direction.length() > 0:
|
|
direction = direction.normalized()
|
|
velocity = direction * (shift_speed if shift else move_speed)
|
|
_update_animation()
|
|
|
|
func _physics_process(_delta: float) -> void:
|
|
if GameManager.game_over:
|
|
return
|
|
move_and_slide()
|
|
|
|
func _unhandled_input(event: InputEvent) -> void:
|
|
if GameManager.game_over:
|
|
return
|
|
if event.is_action_pressed("interact"):
|
|
_interact()
|
|
|
|
func _interact() -> void:
|
|
if held_item == null:
|
|
_try_pick_up()
|
|
else:
|
|
_try_drop()
|
|
|
|
func _try_pick_up() -> void:
|
|
var nearest: Collectable = null
|
|
var nearest_dist := INF
|
|
for area in interaction_area.get_overlapping_areas():
|
|
if area is Collectable and area.can_pick_up():
|
|
var d := global_position.distance_squared_to(area.global_position)
|
|
if d < nearest_dist:
|
|
nearest_dist = d
|
|
nearest = area
|
|
if nearest != null:
|
|
SoundManager.play_key_pickup()
|
|
_get_collectable(nearest)
|
|
|
|
func _try_drop() -> void:
|
|
for area in interaction_area.get_overlapping_areas():
|
|
if area is DropZone and area.accepts(held_item):
|
|
var item := held_item
|
|
held_item = null
|
|
area.deliver(item)
|
|
return
|
|
_drop_on_floor()
|
|
|
|
func _drop_on_floor() -> void:
|
|
var item := held_item
|
|
held_item = null
|
|
var gpos := item.global_position
|
|
item.reparent(get_tree().current_scene)
|
|
item.global_position = gpos
|
|
item.show_behind_parent = false
|
|
item.mark_dropped()
|
|
|
|
func _get_collectable(collectable: Collectable) -> void:
|
|
held_item = collectable
|
|
held_item.mark_picked_up()
|
|
held_item.reparent(self)
|
|
_apply_held_item_state()
|
|
|
|
func _held_item_position() -> Vector2:
|
|
match state:
|
|
State.Left: return held_item.positionLeft
|
|
State.Right: return held_item.positionRight
|
|
State.Up: return held_item.positionUp
|
|
_: return held_item.positionDown
|
|
|
|
func _move_held_item(target: Vector2) -> void:
|
|
if held_item == null:
|
|
return
|
|
var tween = create_tween().set_parallel(true)
|
|
tween.tween_property(held_item, "position", target, 0.15).set_ease(Tween.EASE_OUT)
|
|
|
|
func _apply_held_item_state() -> void:
|
|
if held_item == null:
|
|
return
|
|
held_item.show_behind_parent = state == State.Up
|
|
_move_held_item(_held_item_position())
|
|
|
|
func _set_state(new_state: State, animation: String) -> void:
|
|
if state == new_state:
|
|
return
|
|
state = new_state
|
|
animationPlayer.play(animation)
|
|
_apply_held_item_state()
|
|
|
|
func _update_animation() -> void:
|
|
# Left/right take priority over up/down, preserving the correct animation on diagonals.
|
|
if velocity.x < 0:
|
|
_set_state(State.Left, "idle_left")
|
|
elif velocity.x > 0:
|
|
_set_state(State.Right, "idle_right")
|
|
elif velocity.y < 0:
|
|
_set_state(State.Up, "idle_up")
|
|
elif velocity.y > 0:
|
|
_set_state(State.Down, "idle_down")
|