using Godot; using System; using System.Collections.Generic; public partial class Player : CharacterBody2D { private const double TransitionTime = 0.5; private const double CameraTransitionTime = 0.25; public enum State { Normal, Wait, Transition, ReadChat, ChatWithNPC } private State _state; [Export] public State CurrentState { get => _state; private set { switch (value) { case State.Normal: if (_state == State.Transition) _doorSounds.Play(); Visible = true; break; case State.Transition: Visible = false; break; case State.ReadChat: _nextLabel.Visible = false; _exitLabel.Visible = true; _chatLogContainer.CurrentState = ChatLogContainer.ChatState.Default; break; case State.Wait: break; case State.ChatWithNPC: _nextLabel.Visible = true; _exitLabel.Visible = false; _chatLogContainer.CurrentState = ChatLogContainer.ChatState.Default; break; default: throw new ArgumentOutOfRangeException(); } _state = value; } } [Export] public float Speed = 300.0f; // Get the gravity from the project settings to be synced with RigidBody nodes. public float Gravity = ProjectSettings.GetSetting("physics/2d/default_gravity").AsSingle(); public List InteractableObjects = new List(); public Node2D InteractableObject { get { if (InteractableObjects.Count == 0) return null; var nearObj = InteractableObjects[0]; var minDistance = (GlobalPosition - nearObj.GlobalPosition).Length(); foreach (var obj in InteractableObjects) { var distance = (GlobalPosition - obj.GlobalPosition).Length(); if (minDistance > distance) { minDistance = distance; nearObj = obj; } } return nearObj; } } private AnimatedSprite2D _sprite; private PanelContainer _chatLog; private PanelContainer _nextLabel; private PanelContainer _exitLabel; private ChatLogContainer _chatLogContainer; private Camera2D _camera; private AudioCollection _footsteps; private AudioCollection _doorSounds; private Vector2 _newPosition; private double _currentTransitionTime = 0; private double _currentCameraTransitionTime = 0; private Vector2 _previousPosition; private Vector2 _nextPosition; private Vector2 _cameraDefaultPosition = new(0, -20); private Vector2 _cameraChatLogPosition = new(108, -20); private PackedScene _dialogBox = GD.Load("res://prefabs/Dialog.tscn"); public override void _Ready() { _sprite = (AnimatedSprite2D)FindChild("AnimatedSprite2D"); _chatLog = (PanelContainer)FindChild("ChatLog"); _nextLabel = (PanelContainer)FindChild("NextLabel"); _exitLabel = (PanelContainer)FindChild("ExitLabel"); _chatLogContainer = (ChatLogContainer)FindChild("ChatLogContainer"); _camera = (Camera2D)FindChild("Camera2D"); _footsteps = (AudioCollection)FindChild("Footsteps"); _doorSounds = (AudioCollection)FindChild("DoorSounds"); } public override void _PhysicsProcess(double delta) { switch (CurrentState) { case State.Normal: if (_camera.Offset != _cameraDefaultPosition) { if (_currentCameraTransitionTime < CameraTransitionTime) { _camera.Offset = _cameraChatLogPosition.Lerp(_cameraDefaultPosition, (float)(_currentCameraTransitionTime * 1 / CameraTransitionTime)); _currentCameraTransitionTime += delta; } else { _currentCameraTransitionTime = 0; } } Vector2 velocity = Velocity; Vector2 direction = Input.GetVector("move_left", "move_right", "move_up", "move_down"); if (direction != Vector2.Zero) { _footsteps.Play(); velocity.X = direction.X * Speed; _sprite.Play("walk"); _sprite.FlipH = direction.X switch { > 0 => false, < 0 => true, _ => _sprite.FlipH }; } else { // Footsteps.Stop(); velocity.X = Mathf.MoveToward(Velocity.X, 0, Speed); _sprite.Play("default"); } Velocity = velocity; MoveAndSlide(); break; case State.Transition: if (_currentTransitionTime < TransitionTime) { GlobalPosition = _previousPosition.Lerp(_nextPosition, (float)(_currentTransitionTime * 1 / TransitionTime)); _currentTransitionTime += delta; } else { _currentTransitionTime = 0; CurrentState = State.Normal; } break; case State.ReadChat: ShowChatLog(delta); break; case State.ChatWithNPC: ShowChatLog(delta); break; case State.Wait: break; default: throw new ArgumentOutOfRangeException(); } } private void ShowChatLog(double delta) { if (_camera.Offset != _cameraChatLogPosition) { if (_currentCameraTransitionTime < CameraTransitionTime) { _camera.Offset = _cameraDefaultPosition.Lerp(_cameraChatLogPosition, (float)(_currentCameraTransitionTime * 1 / CameraTransitionTime)); _currentCameraTransitionTime += delta; } else { _currentCameraTransitionTime = 0; } } } public override void _Input(InputEvent @event) { if (@event.IsActionPressed("Interact")) { if (InteractableObject is NPC npc) { void AddMessage() { var msg = _dialogBox.Instantiate(); msg.Text = npc.Message; msg.Author = npc.NPCName; _chatLogContainer.AddChild(msg); if (npc.IsDialogEnded) CurrentState = State.ReadChat; } switch (CurrentState) { case State.Normal: if (GlobalPosition.X - npc.GlobalPosition.X > 0) { npc.Flip = false; _sprite.FlipH = true; } else { npc.Flip = true; _sprite.FlipH = false; } CurrentState = State.ChatWithNPC; AddMessage(); break; case State.ChatWithNPC: AddMessage(); break; } } if (InteractableObject is Door door) { _doorSounds.Play(); _previousPosition = GlobalPosition; _nextPosition = door.Exit.GlobalPosition; CurrentState = State.Wait; } } if (@event.IsActionPressed("show_chat")) { switch (CurrentState) { case State.Normal: CurrentState = State.ReadChat; break; case State.ReadChat: CurrentState = State.Normal; _chatLogContainer.CurrentState = ChatLogContainer.ChatState.Hidden; break; } } } private void _on_door_opened() { if (CurrentState == State.Wait) { CurrentState = State.Transition; } } }