extends CharacterBody3D const SPEED = 5.0 const RUN_SPEED = 1.5 * SPEED const JUMP_VELOCITY = 4.5 const THROW_FORCE = 10.0 const RUN_CONSUMPTION = 20.0 const STAM_REGEN = 10.0 #@export_node_path("ProgressBar") var stam_bar_path = null @export var stam_bar: ProgressBar = null; @export var seed_text: RichTextLabel = null; var stamina: float = 100 var num_seeds: int = 0 # Get the gravity from the project settings to be synced with RigidBody nodes. var gravity = ProjectSettings.get_setting("physics/3d/default_gravity") var current_node: Node3D = null var holding: Node3D = null var climbing: bool = false var last_wall_direction: Vector3 = Vector3() func got_body(body: Node3D): if current_node == null: print("Got body ", body) current_node = body func leave_body(body: Node3D): if current_node == body: current_node = null func pickup(): if current_node.collision_layer & 0b1000: num_seeds += 1 var node = current_node node.get_parent().remove_child(node) node.queue_free() elif holding == null: holding = current_node current_node.get_parent().remove_child(current_node) holding.position = Vector3.ZERO holding.freeze = true $Holder.add_child(holding) func throw(): var g_pos = holding.global_position holding.get_parent().remove_child(holding) get_parent().add_child(holding) holding.global_position = g_pos var fwd = $ThirdPersonCamera.get_front_direction() var right = Vector3.UP var dir = (fwd + right).normalized() holding.freeze = false holding.apply_impulse(THROW_FORCE*dir) holding = null func _ready(): assert(stam_bar != null, "Stamina bar is null") assert(seed_text != null, "Seed text is null") Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) $ThirdPersonCamera.mouse_follow = true $Picker.connect("body_entered", got_body) $Picker.connect("body_exited", leave_body) func _process(_delta): stam_bar.value = stamina seed_text.text = str(num_seeds) func _physics_process(delta): var speed = SPEED var stam_consumption = 0 if Input.is_action_pressed("run") and stamina > 0: speed = RUN_SPEED stam_consumption += RUN_CONSUMPTION if Input.is_action_just_pressed("pickup"): if holding == null and current_node != null: pickup() elif holding != null: throw() # Add the gravity. if not is_on_floor() and not climbing: velocity.y -= gravity * delta var fwd = $ThirdPersonCamera.get_front_direction() var right = $ThirdPersonCamera.get_right_direction() var climb_direction_fwd = Vector3() var climb_direction_right = Vector3() var valid_climb = false if is_on_wall(): for col in get_slide_collision_count(): var col_obj = get_slide_collision(col) #print("Collider layer is ", col_obj.get_collider().collision_layer, " ", col_obj.get_collider()) if col_obj.get_collider().collision_layer & 0b100: valid_climb = true if valid_climb and stamina > 0: last_wall_direction = get_wall_normal() climbing = true elif climbing: climbing = false if climbing: climb_direction_fwd = (Vector3.UP - Vector3.UP.project(last_wall_direction)).normalized() climb_direction_right = (right - right.project(last_wall_direction)).normalized() # Handle Jump. if Input.is_action_just_pressed("ui_accept") and is_on_floor(): velocity.y = JUMP_VELOCITY look_at(position + fwd, Vector3.UP) # 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("left", "right", "backward", "forward") var direction = (input_dir.y * fwd + input_dir.x * right).normalized() var climb_direction: Vector3 = (input_dir.y * climb_direction_fwd + input_dir.x * climb_direction_right).normalized() if climbing: #print("Climb direction is ", climb_direction_fwd, " ", climb_direction_right, " ", get_wall_normal()) velocity = climb_direction * speed velocity += -speed * last_wall_direction if input_dir.length_squared() > 0: stam_consumption += RUN_CONSUMPTION #print("Velocity is ", velocity) elif 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) move_and_slide() if stam_consumption == 0 and not Input.is_action_pressed("run") and is_on_floor(): stam_consumption = -STAM_REGEN stamina = clamp(stamina - stam_consumption * delta, 0, 100)