class_name Player extends CharacterBody3D const SPEED = 3.0 const JUMP_VELOCITY = 3.5 @onready var geometry:Node3D = %Geometry @onready var actionable_detector = %ActionableDetector @onready var animation_tree:AnimationTree = $Geometry/AnimationTree @onready var build_location:Node3D = %BuildLocation @onready var right_hand_attachement:HandTool = %RightHandAttachement var tool_damage:bool = false : get: return tool_damage set(value): tool_damage = value if right_hand_attachement != null and right_hand_attachement.tool_hitbox: right_hand_attachement.tool_hitbox.monitorable = bool(tool_damage) var gravity = ProjectSettings.get_setting("physics/3d/default_gravity") var inventory:Inventory = Inventory.new() var selected_tool_slot_index:int = 0 var current_tool:ItemResource = null var _current_look_direction:Vector3 = Vector3.BACK var _target_look_direction:Vector3 = Vector3.BACK var _look_angle_damper:SpringDamper = SpringDamper.new(0, 4, 0.06, 0.003) var is_input_blocked:bool = false signal trigger_message(message:String) func _process(_delta): if animation_tree.get("parameters/playback").get_current_node() == "Interact": animation_tree.set("parameters/conditions/interact", false) func _handle_input() -> void: # Get the input direction and handle the movement/deceleration. # As good practice, you should replace UI actions with custom gameplay actions. var input_dir = Input.get_vector("walk_left", "walk_right", "walk_forward", "walk_back") var direction = Vector3(input_dir.x, 0, input_dir.y).normalized() if direction: velocity.x = direction.x * SPEED velocity.z = direction.z * SPEED else: velocity.x = move_toward(velocity.x, 0, SPEED) velocity.z = move_toward(velocity.z, 0, SPEED) if is_on_floor() and Input.is_action_just_pressed("jump"): velocity.y = JUMP_VELOCITY func _physics_process(delta): # Add the gravity. if not is_on_floor(): velocity.y -= gravity * delta if is_input_blocked: velocity = Vector3.ZERO else: _handle_input() move_and_slide() var ground_velocity:Vector2 = Vector2(velocity.x, velocity.z) var is_moving:bool = ground_velocity.length_squared() > 0.1 * 0.1 if is_moving: _target_look_direction = Vector3(velocity.x, 0, velocity.z).normalized() _current_look_direction = basis.z update_look_direction(delta) animation_tree.set("parameters/conditions/running", is_moving) animation_tree.set("parameters/conditions/idle", not is_moving) func init_look_target_direction() -> void: _target_look_direction = basis.z func update_look_direction(delta:float) -> void: var current_look_angle:float = _current_look_direction.signed_angle_to(Vector3.BACK, Vector3.UP) var target_look_angle:float = _target_look_direction.signed_angle_to(Vector3.BACK, Vector3.UP) if target_look_angle - current_look_angle > PI: current_look_angle = current_look_angle + PI * 2 elif current_look_angle - target_look_angle > PI: current_look_angle = current_look_angle - PI * 2 var damped_look_angle:float = _look_angle_damper.calc(current_look_angle, target_look_angle, delta) _current_look_direction = Vector3(sin(-damped_look_angle), 0, cos(-damped_look_angle)) self.basis = Basis.looking_at(-_current_look_direction) func set_tool_slot_index(value:int) -> void: if inventory == null: return selected_tool_slot_index = value set_right_hand_item(inventory.get_tool_item_stacks()[selected_tool_slot_index].item) func on_item_picked_up(item:ItemResource): trigger_message.emit("Picked up a " + item.name) inventory.add_item(item) func get_actionable_global_transform() -> Vector3: return build_location.global_position func set_right_hand_item(item:ItemResource) -> void: current_tool = null right_hand_attachement.set_tool(item.scene.instantiate()) if item != null and item.is_tool: current_tool = item animation_tree.set("parameters/Interact/conditions/tool_action_slice", false) animation_tree.set("parameters/Interact/conditions/tool_action_chop", false) match item.tool_action: 1: animation_tree.set("parameters/Interact/conditions/tool_action_chop", true) 2: animation_tree.set("parameters/Interact/conditions/tool_action_slice", true) func has_build_tool_active() -> bool: if current_tool and current_tool.resource_path == "res://data/items/hammer.tres": return true return false func handle_tool_slot_input_events(_event:InputEvent) -> bool: var key_event:InputEventKey = _event as InputEventKey if key_event and key_event.is_released(): if key_event.keycode >= KEY_1 and key_event.keycode <= KEY_9: set_tool_slot_index(key_event.keycode - KEY_1) return true if _event.is_action_released("item_next"): set_tool_slot_index((selected_tool_slot_index + 1) % (inventory.get_tool_item_stacks().size())) return true if _event.is_action_released("item_prev"): var tool_slot_size = inventory.get_tool_item_stacks().size() set_tool_slot_index((selected_tool_slot_index + tool_slot_size - 1) % (tool_slot_size)) return true return false func _unhandled_input(_event: InputEvent) -> void: if Input.is_action_just_pressed("ui_accept"): var actionables = actionable_detector.get_overlapping_areas() if actionables.size() > 0: _target_look_direction = (actionables[0].global_position - position).normalized() actionables[0].action() get_viewport().set_input_as_handled() return if Input.is_action_just_pressed("interaction"): animation_tree.set("parameters/conditions/interact", true) get_viewport().set_input_as_handled() return