class_name Player extends CharacterBody3D @export var walk_speed = 12 @export var jump_power = 32 @export var fall_speed = 86 var health = 100 var suspended = false var spawn = Vector3.ZERO var direction = Vector3.ZERO var target_velocity = Vector3.ZERO var initial_gear = null var gear_slots = ["1", "2", "3"] 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 $RespawnTimer.start() message("Le gone") func respawn(): position = Vector3(spawn) $CameraGimbal.reset() health = 100 visible = true suspended = false $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(): spawn = Vector3(position) initial_gear = $Pivot/Container/Gear.duplicate() get_viewport().size_changed.connect(resize_ui) resize_ui() make_backpack_ui() 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