240 lines
5.5 KiB
GDScript
240 lines
5.5 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 suspended = false
|
|
var direction = Vector3.ZERO
|
|
var target_velocity = Vector3.ZERO
|
|
|
|
var _health = 100
|
|
|
|
func resize_ui():
|
|
var hud_h = 200
|
|
var size = get_viewport().get_visible_rect().size
|
|
$Message.size.x = size.x
|
|
$HUD.size.x = size.x
|
|
$HUD.size.y = hud_h
|
|
$HUD.position.x = 0
|
|
$HUD.position.y = size.y - hud_h
|
|
|
|
func make_hud():
|
|
var text = ""
|
|
for node in $Backpack.get_children():
|
|
if not node is Gear:
|
|
continue
|
|
text += "%s (%s)\n" % [node.gear_name(), node.name]
|
|
text += "Holding " + $Pivot/Container/Gear.gear_name() + "\n"
|
|
text += str(health()) + " hp"
|
|
$HUD.text = text
|
|
|
|
func message(string):
|
|
$Message.text = string
|
|
$Message.visible = true
|
|
$Message/VanishTimer.start()
|
|
|
|
func harm(hp):
|
|
assert(hp >= 0)
|
|
_health -= hp
|
|
make_hud()
|
|
|
|
func heal(hp):
|
|
assert(hp >= 0)
|
|
_health += hp
|
|
make_hud()
|
|
|
|
func health():
|
|
return _health
|
|
|
|
func die():
|
|
suspended = true
|
|
visible = false
|
|
$HUD.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_hud()
|
|
$HUD.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_hud()
|
|
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_hud()
|
|
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_hud()
|
|
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
|