using System; using RebootKit.Engine.Extensions; using RebootKit.Engine.Foundation; using RebootKit.Engine.Input; using RebootKit.Engine.Main; using RebootKit.Engine.Network; using RebootKit.Engine.Simulation; using RebootReality.jelycho.Main; using RebootReality.jelycho.Player.HUD; using Unity.Mathematics; using UnityEngine; using UnityEngine.InputSystem; using Logger = RebootKit.Engine.Foundation.Logger; namespace RebootReality.jelycho.Player { public class PlayerController : NetworkPlayerController { static readonly Logger s_Logger = new Logger(nameof(PlayerController)); [SerializeField] Config m_Config; PlayerActor m_Actor; PlayerHUD m_HUD; InputState m_InputState; IDisposable m_TargetInteractableLabelDisposable; IInteractable m_Interactable; void Awake() { m_HUD = Instantiate(m_Config.playerHUDPrefab); m_InputState = RR.Input.NewInputState("Player Controller"); m_InputState.HideCursor = true; m_InputState.LockCursor = true; m_InputState.AddActiveMap(GameConsts.k_FPPActionMapName); } void OnDestroy() { m_InputState?.Dispose(); m_InputState = null; if (m_HUD != null) { Destroy(m_HUD.gameObject); m_HUD = null; } if (m_Actor.OrNull() != null) { UnsubscribeFromPlayerActorEvents(); } } void OnEnable() { m_InputState.IsActive = true; RR.Input.EnableControls(); } void OnDisable() { m_InputState.IsActive = false; RR.Input.DisableControls(); } protected override void OnPossessActor(Actor actor) { if (actor is PlayerActor playerActor) { m_Actor = playerActor; } else { m_Actor = null; s_Logger.Error($"Tried to possess non-PlayerActor: {actor.GetType().Name}"); return; } m_Actor.SetupAsOwner(); m_Actor.OnTargetInteractableChanged += OnTargetInteractableChanged; OnTargetInteractableChanged(m_Actor.TargetInteractable); m_HUD.SetPlayerActor(m_Actor); if (RR.World.Context is WorldContext worldContext) { m_Actor.WarpTo(worldContext.PlayerSpawnPoint.position); } } protected override void OnUnpossessActor(Actor actor) { base.OnUnpossessActor(actor); UnsubscribeFromPlayerActorEvents(); m_Actor = null; } void UnsubscribeFromPlayerActorEvents() { if (m_Actor.OrNull() == null) { s_Logger.Error("Cannot unsubscribe from player actor events: actor is not set"); return; } m_Actor.OnTargetInteractableChanged -= OnTargetInteractableChanged; } void OnTargetInteractableChanged(IInteractable interactable) { m_Interactable?.SetHighlight(false); if (m_TargetInteractableLabelDisposable != null) { m_TargetInteractableLabelDisposable.Dispose(); m_TargetInteractableLabelDisposable = null; } m_Interactable = interactable; m_Interactable?.SetHighlight(true); if (interactable is Actor interactableActor) { m_TargetInteractableLabelDisposable = m_HUD.ObjectsLabels.CreateLabel(interactableActor.transform, interactableActor.ActorName); } } void Update() { if (m_Actor is null) { return; } Vector2 lookInput = SensitivityReader.Read(m_Config.lookActionReference.action); m_Actor.Look(lookInput * FPPConfig.MouseSensitivity.FloatValue); Vector2 moveInput = m_Config.moveActionReference.action.ReadValue(); m_Actor.SetMoveInput(moveInput); m_Actor.SetSprint(m_Config.sprintActionReference.action.IsPressed()); if (m_Config.jumpActionReference.action.WasPerformedThisFrame()) { m_Actor.Jump(); } if (m_Config.dropItemActionReference.action.WasReleasedThisFrame()) { m_Actor.DropItem(); } if (m_Config.kickActionReference.action.WasReleasedThisFrame()) { m_Actor.Kick(); } if (m_Config.primaryActionReference.action.WasPressedThisFrame()) { m_Actor.BeginPrimaryAction(); } if (m_Config.primaryActionReference.action.IsPressed()) { m_Actor.HoldingPrimaryAction(); } if (m_Config.primaryActionReference.action.WasReleasedThisFrame()) { m_Actor.EndPrimaryAction(); } if (m_Config.secondaryActionReference.action.WasReleasedThisFrame()) { m_Actor.SecondaryAction(); } if (m_Config.interactActionReference.action.WasReleasedThisFrame()) { m_Actor.Interact(); } for (int i = 0; i < m_Config.inventorySlotSelectActionReferences.Length; i++) { if (m_Config.inventorySlotSelectActionReferences[i].action.WasReleasedThisFrame()) { m_Actor.SelectItemSlot(i); } } float slotChangeAxis = m_Config.inventorySlotChangeActionReference.action.ReadValue(); if (math.abs(slotChangeAxis) > 0.5f) { if (slotChangeAxis > 0) { m_Actor.SelectNextItemSlot(); } else { m_Actor.SelectPreviousItemSlot(); } } } [Serializable] public class Config { public PlayerHUD playerHUDPrefab; public InputActionReference moveActionReference; public InputActionReference lookActionReference; public InputActionReference jumpActionReference; public InputActionReference sprintActionReference; public InputActionReference dropItemActionReference; public InputActionReference kickActionReference; public InputActionReference primaryActionReference; public InputActionReference secondaryActionReference; public InputActionReference interactActionReference; public InputActionReference[] inventorySlotSelectActionReferences; public InputActionReference inventorySlotChangeActionReference; } } public static class FPPConfig { [ConfigVar("fpp.mouse.sens", 0.25f, "Mouse look sensitivity")] public static ConfigVar MouseSensitivity; [ConfigVar("fpp.mouse.invert.y", 0, "Invert mouse look")] public static ConfigVar MouseInvertY; [ConfigVar("fpp.gamepad.sens", 4.0f, "Gamepad look sensitivity")] public static ConfigVar GamepadSensitivity; } public static class SensitivityReader { public static float2 Read(InputAction action) { if (action == null || action.activeControl == null) { return float2.zero; } float2 sensitivity = action.ReadValue(); bool isGamepad = action.activeControl.device is Gamepad; if (isGamepad) { sensitivity *= FPPConfig.GamepadSensitivity.FloatValue; } else { sensitivity *= FPPConfig.MouseSensitivity.FloatValue; if (FPPConfig.MouseInvertY.IndexValue == 1) { sensitivity.y *= -1; } } return sensitivity; } } }