class_name Player extends CharacterBody3D const FOCUS_SPEED := 30.0 const RUN_SPEED := 80.0 const SPRINT_SPEED := 160.0 const AIR_SPEED := 10.0 const JUMP_FORCE := 4.5 const GROUND_FRICTION := 0.3 const AIR_FRICTION := 0.03 @export var inventory: Dictionary[Item, int] = {} var gravity: Vector3 = ( ProjectSettings.get_setting("physics/3d/default_gravity") * ProjectSettings.get_setting("physics/3d/default_gravity_vector") ) var selected_interactive: Interactive var firing := false @onready var hud: PlayerHUD = %PlayerHUD @onready var camera_pivot: CameraController = %CameraPivot @onready var interact_ray: RayCast3D = %InteractRay @onready var tool_mount: ToolMount = %ToolMount @onready var point_spray: PointSpray = %PointSpray @onready var wide_spray: WideSpray = %WideSpray @onready var toothbrush: Tool = %Toothbrush ## Global static access to player singleton static var instance: Player func _ready() -> void: instance = self func get_speed() -> float: if is_on_floor(): if firing: return FOCUS_SPEED if Input.is_action_pressed("sprint"): return SPRINT_SPEED return RUN_SPEED return AIR_SPEED func get_friction() -> float: if is_on_floor(): return GROUND_FRICTION return AIR_FRICTION func get_tool() -> Tool: return tool_mount.get_active() ## Add the given item to the player's inventory. func add_item(item: Item, amount: int = 1) -> void: inventory[item] = inventory.get(item, 0) + amount ## Remove the given item from the player's inventory. func remove_item(item: Item, amount: int = 1) -> void: inventory[item] = inventory.get(item, 0) - amount if inventory[item] <= 0: inventory.erase(item) func _physics_process(delta: float) -> void: # Will be null if no valid interactor is selected. var interactive: Interactive = interact_ray.get_collider() as Interactive hud.select_interactive(interactive) if interactive: interactive.select() # World interaction if interactive and Input.is_action_just_pressed("interact"): interactive.activate() # Tool selection if Input.is_action_just_pressed("select_next_tool"): tool_mount.set_active_relative(1) elif Input.is_action_just_pressed("select_prev_tool"): tool_mount.set_active_relative(-1) elif Input.is_action_just_pressed("select_point_spray"): tool_mount.set_active(point_spray) elif Input.is_action_just_pressed("select_wide_spray"): tool_mount.set_active(wide_spray) elif Input.is_action_just_pressed("select_brush"): tool_mount.set_active(toothbrush) # Tool use if Input.is_action_pressed("fire"): get_tool().fire() firing = true else: get_tool().idle() firing = false if Input.is_action_just_pressed("switch_mode"): get_tool().switch_mode() # Gravity if not is_on_floor(): velocity += gravity * delta # Input movement var input_dir := Input.get_vector("move_left", "move_right", "move_forward", "move_back") var rel_input := input_dir.rotated(-camera_pivot.global_rotation.y) var direction := Vector3(rel_input.x, 0, rel_input.y).normalized() var movement := direction * get_speed() * delta velocity.x += movement.x velocity.z += movement.z if Input.is_action_just_pressed("jump") and is_on_floor(): velocity.y = JUMP_FORCE # Friction var friction := get_friction() velocity.x = lerpf(velocity.x, 0, friction) velocity.z = lerpf(velocity.z, 0, friction) move_and_slide()