extends CharacterBody3D class_name Player var speed = 5.0 const JUMP_VELOCITY = 12 const throw_strength = 50 var speeds = [5.0, 2.5, 1.5, 1.0] var animation_player_speeds = [1.5, 1.0, 0.7, 0.5] var counter_speed := 0 enum PlayerMode { NORMAL, AIM } var mode = PlayerMode.NORMAL var xform : Transform3D var grabbed_item : Grabbable_Object var dialogueActive : bool var weight: float = 5.0 # --- Camera --- var camera_pitch := 0.0 var zoom := 5.0 @export var min_zoom := 1 @export var max_zoom := 10.0 @export var zoom_speed := 0.5 @export var min_pitch := deg_to_rad(-80) @export var max_pitch := deg_to_rad(0) @export var mouse_sensitivity:float = 0.0007 @onready var camera_3d: Camera3D = $Camera_Controller/Camera_Pivot/Camera3D @onready var crosshair: Control = $Crosshair @onready var spring_arm: SpringArm3D = $Camera_Controller/Camera_Pivot @onready var interact_ray: ShapeCast3D = $InteractRay @onready var chatbot = $Chatbot @onready var env = $".." var rock_scene: PackedScene = preload("res://Resources/Objects/rock.tscn") signal inventory_opened signal inventory_closed signal chatbot_opened func _ready() -> void: Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) $Camera_Controller.position = position $Armature/Skeleton3D/CharacterMesh.mesh = load(Global.mesh_resource_player) camera_pitch = $Camera_Controller/Camera_Pivot.rotation.x zoom = spring_arm.spring_length if DialogueState.currentTask > 1: $AnimationPlayer.play('helper_visible') if Global.balloon_collected == true: print(Global.resource_balloon) _on_balloon_balloon_chosen(Global.resource_balloon) func _process(delta: float) -> void: if Global.chatbot_opened: return if Input.is_action_just_pressed("help"): if DialogueState.is_dialogue_open == false: DialogueState.is_dialogue_open = true DialogueManager.show_chosen_dialogue_balloon(load("res://Interact/dialogue/helper.dialogue"), "/game_balloons/game_balloon_v_1.tscn", "start", [self, $Player/helper]) func _input(event): if event is InputEventMouseMotion and mode == PlayerMode.NORMAL: $Camera_Controller.rotate_y(-event.relative.x * mouse_sensitivity) camera_pitch -= event.relative.y * mouse_sensitivity camera_pitch = clamp(camera_pitch, min_pitch, max_pitch) $Camera_Controller/Camera_Pivot.rotation.x = camera_pitch if event is InputEventMouseButton and event.pressed: if event.button_index == MOUSE_BUTTON_WHEEL_UP: zoom -= zoom_speed elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN: zoom += zoom_speed zoom = clamp(zoom, min_zoom, max_zoom) spring_arm.spring_length = zoom print(spring_arm.spring_length) func _physics_process(delta: float) -> void: if Global.chatbot_opened or $Inventory.visible: return dialogueActive = DialogueState.is_dialogue_open var thisTime = Time.get_unix_time_from_system() # --- DROP / THROW --- if Input.is_action_just_pressed("drop") and grabbed_item: grabbed_item.release(self) $AnimationPlayer.play_backwards("pickup") play_sound('drop') if Input.is_action_just_pressed("throw") and grabbed_item: grabbed_item.release(self, true) play_sound('drop') elif Input.is_action_just_pressed("throw") and not grabbed_item: if mode == PlayerMode.NORMAL: mode = PlayerMode.AIM Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) else: mode = PlayerMode.NORMAL Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) # --- AIM THROW --- elif Input.is_action_just_pressed("click") and mode == PlayerMode.AIM and not dialogueActive and not $Inventory.visible: var mouse_pos = get_viewport().get_mouse_position() var ray_origin = camera_3d.project_ray_origin(mouse_pos) var ray_direction = camera_3d.project_ray_normal(mouse_pos) var ray_length = 1000 var space_state = get_world_3d().direct_space_state var query = PhysicsRayQueryParameters3D.create( ray_origin, ray_origin + ray_direction * ray_length ) query.exclude = [self] #Exclude Boundary Layer (Layer 7) query.collision_mask = ~(1 << 6) var result = space_state.intersect_ray(query) var target_position: Vector3 if result.size() > 0: target_position = result.position else: target_position = ray_origin + ray_direction * ray_length var from = global_transform.origin + Vector3(0, 1.5, 0) var throw_direction = (target_position - from).normalized() var rock_instance = rock_scene.instantiate() get_tree().current_scene.add_child(rock_instance) var forward_dir = -camera_3d.global_transform.basis.z.normalized() rock_instance.global_transform.origin = from + forward_dir * 1.0 rock_instance.freeze = false rock_instance.sleeping = false rock_instance.gravity_scale = 0.0 rock_instance.linear_velocity = throw_direction * throw_strength $AnimationPlayer.play("jump") $audio_throw.play() # --- GRAVITY --- if not is_on_floor(): velocity += get_gravity() * delta # --- JUMP --- if Input.is_action_just_pressed("ui_accept") and is_on_floor(): var deltaTime = thisTime - DialogueState.time_since_dialoge_open if not dialogueActive and (deltaTime > 0.5): $audio_jump.play() velocity.y = JUMP_VELOCITY $AnimationPlayer.stop() $AnimationPlayer.play("jump") await get_tree().create_timer(0.4).timeout $AnimationPlayer.play_backwards("jump") # --- MOVEMENT --- var input_dir := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") if dialogueActive or $Inventory.visible: input_dir = Vector2.ZERO var direction : Vector3 = ($Camera_Controller.transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized() if input_dir != Vector2.ZERO: self.rotation_degrees.y = $Camera_Controller.rotation_degrees.y - rad_to_deg(input_dir.angle()) - 90 # --- FLOOR ALIGN --- if is_on_floor() and input_dir != Vector2.ZERO: align_with_floor($RayCast3D.get_collision_normal()) global_transform = global_transform.interpolate_with(xform, 0.3) elif not is_on_floor(): align_with_floor(Vector3.UP) global_transform = global_transform.interpolate_with(xform, 0.3) # --- VELOCITY --- if direction: velocity.x = direction.x * speed velocity.z = direction.z * speed if grabbed_item: $AnimationPlayer.play("pickup_walk") else: $AnimationPlayer.play("walk") else: velocity.x = move_toward(velocity.x, 0, speed) velocity.z = move_toward(velocity.z, 0, speed) move_and_slide() $Camera_Controller.position = lerp($Camera_Controller.position, position, 0.15) func align_with_floor(floor_normal): xform = global_transform xform.basis.y = floor_normal xform.basis.x = -xform.basis.z.cross(floor_normal) xform.basis = xform.basis.orthonormalized() func _on_fall_zone_body_entered(body: Node3D) -> void: get_tree().reload_current_scene() func openChatBot(): chatbot_opened.emit() chatbot.visible = true Global.chatbot_opened = true chatbot._prompt_edit.grab_focus() Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) env.quiet_down_music() func get_weight(): return weight func _on_interact_ray_grabbed() -> void: $AnimationPlayer.play("pickup") func set_mode(mode_new: String) -> void: if mode_new == "aim": mode = PlayerMode.AIM Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) elif mode_new == "normal": mode = PlayerMode.NORMAL Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) func toggle_inventory() -> void: if $Inventory.visible: $Inventory.visible = false set_mode("normal") inventory_closed.emit() else: $Inventory.visible = true set_mode("aim") inventory_opened.emit() func _on_balloon_balloon_chosen(mesh: String) -> void: $Balloon.visible = true $Balloon.set_mesh(mesh) Global.resource_balloon = mesh Global.balloon_collected = true func slow_down() -> void: if counter_speed < len(speeds)-1: counter_speed += 1 speed = speeds[counter_speed] func reset_speed() -> void: counter_speed = 0 speed = 5.0 func _on_animation_player_animation_started(anim_name: StringName) -> void: if anim_name == 'walk' or anim_name == 'pickup_walk': $AnimationPlayer.speed_scale = animation_player_speeds[counter_speed] else: $AnimationPlayer.speed_scale = animation_player_speeds[1] func play_sound(sound_name: String) -> void: if sound_name == 'pickup': $audio_pickup.play() elif sound_name == 'drop': $audio_pickup.play() func toggle_music() -> void: env.toggle_music()