Use a preloaded scene to give the player the starting gear rather than making a duplicate of the gear on spawn, and strip any gears from the backpack on death. Remove Gear from the editor scene tree and add it on respawn().
224 lines
5.4 KiB
GDScript
224 lines
5.4 KiB
GDScript
class_name Player extends CharacterBody3D
|
|
|
|
@export var walk_speed = 12
|
|
@export var jump_power = 32
|
|
@export var fall_speed = 86
|
|
|
|
const gear_slots = ["1", "2", "3"]
|
|
|
|
@onready var spawn = Vector3(position)
|
|
|
|
var starting_gear = preload("res://gears/ball.tscn")
|
|
|
|
var health = 100
|
|
var suspended = false
|
|
var direction = Vector3.ZERO
|
|
var target_velocity = Vector3.ZERO
|
|
|
|
func resize_ui():
|
|
var bpui_h = 200
|
|
var size = get_viewport().get_visible_rect().size
|
|
$Message.size.x = size.x
|
|
$BackpackUI.size.x = size.x
|
|
$BackpackUI.size.y = bpui_h
|
|
$BackpackUI.position.x = 0
|
|
$BackpackUI.position.y = size.y - bpui_h
|
|
|
|
func make_backpack_ui():
|
|
var text = "Holding " + $Pivot/Container/Gear.gear_name()
|
|
for node in $Backpack.get_children():
|
|
if not node is Gear:
|
|
continue
|
|
text += "\n%s (%s)" % [node.gear_name(), node.name]
|
|
$BackpackUI.text = text
|
|
|
|
func message(string):
|
|
$Message.text = string
|
|
$Message.visible = true
|
|
$Message/VanishTimer.start()
|
|
|
|
func die():
|
|
suspended = true
|
|
visible = false
|
|
$BackpackUI.visible = false
|
|
|
|
# strip gears
|
|
for gear in $Backpack.get_children():
|
|
gear.queue_free()
|
|
$Pivot/Container/Gear.queue_free()
|
|
|
|
$RespawnTimer.start()
|
|
message("Le gone")
|
|
|
|
func respawn():
|
|
position = Vector3(spawn)
|
|
$CameraGimbal.reset()
|
|
health = 100
|
|
visible = true
|
|
suspended = false
|
|
|
|
# add starting gear
|
|
var gear = starting_gear.instantiate()
|
|
$Pivot/Container.add_child(gear)
|
|
|
|
make_backpack_ui()
|
|
$BackpackUI.visible = true
|
|
|
|
# Backpack functions
|
|
|
|
func find_free_slot():
|
|
for slot in gear_slots:
|
|
if not has_node("Backpack/" + slot):
|
|
return slot
|
|
return null
|
|
|
|
func use_backpack_slot(n):
|
|
assert(gear_slots.has(n))
|
|
var gear_node = "Backpack/" + n
|
|
var old = $Pivot/Container/Gear
|
|
# check if gear is at slot
|
|
if not has_node(gear_node):
|
|
message("Gear not found")
|
|
return
|
|
# place current gear in first free slot
|
|
var slot = find_free_slot()
|
|
if slot:
|
|
old.name = slot
|
|
old.reparent($Backpack, false)
|
|
get_node(gear_node).reparent($Pivot/Container, false)
|
|
get_node("Pivot/Container/" + n).name = "Gear"
|
|
make_backpack_ui()
|
|
return
|
|
# couldn't find a free slot, so replace
|
|
# the new slot with the current gear
|
|
get_node(gear_node).reparent($Pivot/Container, false)
|
|
old.reparent($Backpack, false)
|
|
old.name = n
|
|
get_node("Pivot/Container/" + n).name = "Gear"
|
|
make_backpack_ui()
|
|
return
|
|
|
|
func equip(gear: Gear):
|
|
var gear_name = gear.gear_name()
|
|
# do we have the gear equipped?
|
|
if gear_name == $Pivot/Container/Gear.gear_name():
|
|
return false
|
|
# do we have the gear in our backpack?
|
|
for item in $Backpack.get_children():
|
|
if item.gear_name() == gear_name:
|
|
use_backpack_slot(item.name)
|
|
return false
|
|
# we do not have the gear, so set aside
|
|
# the current gear and equip the new one
|
|
var old = $Pivot/Container/Gear
|
|
var new_gear: Gear
|
|
# place current gear in first free slot
|
|
var slot = find_free_slot()
|
|
if slot:
|
|
old.name = slot
|
|
old.reparent($Backpack, false)
|
|
new_gear = gear.duplicate()
|
|
new_gear.name = "Gear"
|
|
$Pivot/Container.add_child(new_gear)
|
|
make_backpack_ui()
|
|
message("You picked up a " + gear_name + "!")
|
|
return true
|
|
# if no slots are free
|
|
message("Backpack full")
|
|
return false
|
|
|
|
# Player movement and engine callbacks
|
|
|
|
func move_player(x, z):
|
|
var camera_basis = $CameraGimbal.get_global_transform().basis
|
|
|
|
# rotate player
|
|
get_global_transform().basis = Basis($CameraGimbal.basis)
|
|
var z_basis = -camera_basis.z
|
|
if z == 1:
|
|
z_basis = -z_basis
|
|
$Pivot.basis = Basis.looking_at(z_basis.normalized())
|
|
$CollisionShape3D.basis = $Pivot.basis
|
|
# apply camera direction to movement direction
|
|
if z != 0:
|
|
# adding the value to the existing direction
|
|
# variable lets the user press multiple keys
|
|
# to move diagonally
|
|
direction += camera_basis.z * z
|
|
if x != 0:
|
|
direction += camera_basis.x * x
|
|
|
|
func _ready():
|
|
get_viewport().size_changed.connect(resize_ui)
|
|
resize_ui()
|
|
respawn()
|
|
|
|
func _physics_process(delta):
|
|
if suspended:
|
|
return
|
|
|
|
if health < 1 or position.y <= -1000:
|
|
die()
|
|
|
|
# UI and backpack keys
|
|
if Input.is_action_just_pressed("backpack_1"):
|
|
use_backpack_slot("1")
|
|
if Input.is_action_just_pressed("backpack_2"):
|
|
use_backpack_slot("2")
|
|
if Input.is_action_just_pressed("backpack_3"):
|
|
use_backpack_slot("3")
|
|
|
|
# Movement keys
|
|
var mx = 0
|
|
var mz = 0
|
|
|
|
if Input.is_action_pressed("move_forward"):
|
|
mz -= 1
|
|
if Input.is_action_pressed("move_back"):
|
|
mz += 1
|
|
if Input.is_action_pressed("move_left"):
|
|
mx -= 1
|
|
if Input.is_action_pressed("move_right"):
|
|
mx += 1
|
|
if Input.is_action_pressed("jump") and is_on_floor():
|
|
target_velocity.y = jump_power
|
|
|
|
if !(mx == 0 and mz == 0):
|
|
move_player(mx, mz)
|
|
|
|
target_velocity.x = direction.x * walk_speed
|
|
target_velocity.z = direction.z * walk_speed
|
|
direction = Vector3.ZERO
|
|
|
|
if not is_on_floor():
|
|
target_velocity.y = target_velocity.y - (fall_speed * delta)
|
|
velocity = target_velocity
|
|
|
|
move_and_slide()
|
|
|
|
# Gear uses
|
|
var gear = $Pivot/Container/Gear
|
|
assert(gear is Gear)
|
|
if gear.continuous():
|
|
if Input.is_action_pressed("gear_use"):
|
|
gear.use(self)
|
|
else:
|
|
if Input.is_action_just_pressed("gear_use"):
|
|
gear.use(self)
|
|
|
|
# Signals
|
|
|
|
func _on_vanish_timer_timeout():
|
|
$Message.visible = false
|
|
|
|
# Ensure that gears are invisible in the backpack.
|
|
# As children of a Node they will be detached from
|
|
# 3D space so if visible is true they may be visibly
|
|
# littered on the ground where the player changed
|
|
# gears.
|
|
func _on_backpack_child_entered_tree(node):
|
|
assert(node is Gear)
|
|
node.visible = false
|
|
func _on_backpack_child_exiting_tree(node):
|
|
assert(node is Gear)
|
|
node.visible = true
|