diff --git a/Assets/OctoCore/Demo/Scripts/NonEuclidean/DetectiveBoardPortalHider.cs b/Assets/OctoCore/Demo/Scripts/NonEuclidean/DetectiveBoardPortalHider.cs deleted file mode 100644 index ee1e7958..00000000 --- a/Assets/OctoCore/Demo/Scripts/NonEuclidean/DetectiveBoardPortalHider.cs +++ /dev/null @@ -1,135 +0,0 @@ -using UnityEngine; -using Unheimlich.Features.Player; -using Unheimlich.Features.DetectiveBoard.UI; - -namespace Unheimlich.NonEuclidean -{ - public class DetectiveBoardPortalHider : MonoBehaviour - { - [SerializeField] private float _checkInterval = 0.1f; - [SerializeField] private float _hideDistance = 5f; - [SerializeField] private bool _enableDebugLogs = true; - - private Canvas _detectiveBoardCanvas; - private NonEuclideanManager _manager; - private float _checkTimer; - - private void Start() - { - _manager = NonEuclideanManager.Instance; - - if (DetectiveBoardCanvas.Instance != null) - { - _detectiveBoardCanvas = DetectiveBoardCanvas.Instance.GetComponent(); - } - - if (_enableDebugLogs) - { - Debug.Log($"[DetectiveBoardPortalHider] Initialized - Manager: {_manager != null}, Canvas: {_detectiveBoardCanvas != null}"); - } - } - - private void Update() - { - if (_detectiveBoardCanvas == null || _manager == null || PlayerController.Instance == null) - { - if (_enableDebugLogs && Time.frameCount % 300 == 0) - { - Debug.LogWarning($"[DetectiveBoardPortalHider] Missing references - Canvas: {_detectiveBoardCanvas != null}, Manager: {_manager != null}, Player: {PlayerController.Instance != null}"); - } - return; - } - - _checkTimer += Time.deltaTime; - - if (_checkTimer >= _checkInterval) - { - _checkTimer = 0f; - CheckAndToggleCanvas(); - } - } - - private void CheckAndToggleCanvas() - { - Vector3 playerPos = PlayerController.Instance.transform.position; - NonEuclideanRoom currentRoom = _manager.ActiveRoom; - - if (currentRoom == null) - { - if (_enableDebugLogs) - Debug.LogWarning("[DetectiveBoardPortalHider] No active room"); - return; - } - - NonEuclideanRoom boardRoom = GetDetectiveBoardRoom(); - - if (boardRoom == null) - { - if (_enableDebugLogs && Time.frameCount % 300 == 0) - Debug.LogWarning("[DetectiveBoardPortalHider] Could not find board room"); - _detectiveBoardCanvas.enabled = true; - return; - } - - bool isInBoardRoom = (currentRoom == boardRoom); - - if (_enableDebugLogs) - Debug.Log($"[DetectiveBoardPortalHider] Current room: {currentRoom.name}, Board room: {boardRoom.name}, In board room: {isInBoardRoom}, Portals count: {currentRoom.Portals.Length}"); - - foreach (var portal in currentRoom.Portals) - { - float distance = Vector3.Distance(playerPos, portal.transform.position); - - bool shouldHide = false; - - if (isInBoardRoom) - { - shouldHide = (distance < _hideDistance); - - if (_enableDebugLogs && shouldHide) - Debug.Log($"[DetectiveBoardPortalHider] In board room, close to portal '{portal.name}' (distance: {distance:F2}m) - hiding canvas"); - } - else if (portal.TargetRoom == boardRoom) - { - shouldHide = (distance < _hideDistance); - - if (_enableDebugLogs && shouldHide) - Debug.Log($"[DetectiveBoardPortalHider] Close to portal '{portal.name}' leading to board room (distance: {distance:F2}m) - hiding canvas"); - } - - if (shouldHide) - { - _detectiveBoardCanvas.enabled = false; - return; - } - } - - if (_enableDebugLogs) - Debug.Log("[DetectiveBoardPortalHider] Not close to any relevant portal - enabling canvas"); - _detectiveBoardCanvas.enabled = true; - } - - private NonEuclideanRoom GetDetectiveBoardRoom() - { - if (_detectiveBoardCanvas == null) - return null; - - Transform current = _detectiveBoardCanvas.transform; - - while (current != null) - { - NonEuclideanRoom room = current.GetComponent(); - if (room != null) - { - if (_enableDebugLogs && Time.frameCount % 300 == 0) - Debug.Log($"[DetectiveBoardPortalHider] Found board room: {room.name}"); - return room; - } - - current = current.parent; - } - - return null; - } - } -} diff --git a/Assets/OctoCore/Demo/Scripts/NonEuclidean/DetectiveBoardPortalHider.cs.meta b/Assets/OctoCore/Demo/Scripts/NonEuclidean/DetectiveBoardPortalHider.cs.meta deleted file mode 100644 index 516f7cae..00000000 --- a/Assets/OctoCore/Demo/Scripts/NonEuclidean/DetectiveBoardPortalHider.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 16e1f1371a69f6d4db490054110293b7 \ No newline at end of file diff --git a/Assets/_Content/Scripts/Core/Audio/AudioEmitterData.cs b/Assets/_Content/Scripts/Core/Audio/AudioEmitterData.cs deleted file mode 100644 index 90de5ad1..00000000 --- a/Assets/_Content/Scripts/Core/Audio/AudioEmitterData.cs +++ /dev/null @@ -1,38 +0,0 @@ -using UnityEngine; - -namespace Unheimlich.Core.Audio -{ - [CreateAssetMenu(fileName = "AudioEmitterData", menuName = "Unheimlich/Audio/Audio Emitter Data")] - public class AudioEmitterData : ScriptableObject - { - [Header("Emitter Info")] - public AudioEmitterType EmitterType = AudioEmitterType.Generic; - public string DisplayName = "Generic"; - - [Header("Procedural Audio Settings")] - public float BaseFrequency = 440f; - public float FrequencyVariation = 50f; - public float VolumeMin = 0.3f; - public float VolumeMax = 0.7f; - public float DurationMin = 0.5f; - public float DurationMax = 2f; - - [Header("Harmonics (for complex sounds)")] - public float[] HarmonicMultipliers = { 1f }; - public float[] HarmonicAmplitudes = { 1f }; - - [Header("Noise Settings")] - [Range(0f, 1f)] - public float NoiseAmount = 0f; - - [Header("Vibration Settings")] - public bool EnableVibration = true; - public float VibrationIntensityMin = 0.1f; - public float VibrationIntensityMax = 0.5f; - public float VibrationFrequency = 10f; - - [Header("Distance Settings")] - public float MaxDistance = 10f; - public AnimationCurve DistanceAttenuation = AnimationCurve.EaseInOut(0f, 1f, 1f, 0f); - } -} diff --git a/Assets/_Content/Scripts/Core/Audio/AudioEmitterData.cs.meta b/Assets/_Content/Scripts/Core/Audio/AudioEmitterData.cs.meta deleted file mode 100644 index 9c0de138..00000000 --- a/Assets/_Content/Scripts/Core/Audio/AudioEmitterData.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: c20752cb7065e844aaaedf606c4df2db \ No newline at end of file diff --git a/Assets/_Content/Scripts/Core/Audio/AudioListenable.cs b/Assets/_Content/Scripts/Core/Audio/AudioListenable.cs deleted file mode 100644 index 58d881aa..00000000 --- a/Assets/_Content/Scripts/Core/Audio/AudioListenable.cs +++ /dev/null @@ -1,434 +0,0 @@ -using System.Collections; -using UnityEngine; -using UnityEngine.InputSystem; -using Unheimlich.Core.Utils; -using Unheimlich.World.CameraSystem; - -namespace Unheimlich.Core.Audio -{ - public enum AudioEmitterType - { - Generic, - Wall, - Wood, - Metal, - Glass, - Water, - Stone, - Mechanical, - Electrical, - Organic - } - - public class AudioListenable : MonoBehaviour - { - // String constants - private const string CONTINUOUS_AUDIO_SOURCE_NAME = "Continuous Audio Source"; - - // Volume multipliers for concentration system - private const float BASELINE_VOLUME_MULTIPLIER = 0.1f; - private const float FULL_VOLUME_MULTIPLIER = 1.5f; - private const float CONCENTRATION_FADE_DURATION = 1f; - private const float RELEASE_FADE_DURATION = 0.5f; - - [Header("Emitter Settings")] - [SerializeField] private AudioEmitterType _emitterType = AudioEmitterType.Generic; - [SerializeField] private bool _useProceduralAudio = true; - - [Header("Continuous Emission")] - [SerializeField] private bool _emitContinuously = true; - [SerializeField] private float _continuousBaseVolume = 0.1f; - [SerializeField] private float _hearingRangeMin = 4f; - [SerializeField] private float _hearingRangeMax = 8f; - - [Header("Listening Enhancement")] - [SerializeField] private float _reverbWetMix = 0.5f; - - [Header("Static Audio")] - [SerializeField] private AudioClip _staticAudioClip; - [SerializeField] private bool _loopStaticAudio = true; - - [Header("Procedural Settings")] - [SerializeField] private float _baseFrequency = 440f; - [SerializeField] private float _frequencyVariation = 50f; - [SerializeField] private float _volumeMin = 0.3f; - [SerializeField] private float _volumeMax = 0.7f; - [SerializeField] private float _durationMin = 0.5f; - [SerializeField] private float _durationMax = 2f; - - [Header("Distance Attenuation")] - [SerializeField] private float _maxDistance = 10f; - [SerializeField] private AnimationCurve _distanceAttenuation = AnimationCurve.EaseInOut(0f, 1f, 1f, 0f); - - [Header("Visual Feedback")] - [SerializeField] private ParticleSystem _emitterParticles; - - [Header("Camera Settings")] - [SerializeField] private CameraPanSettings _cameraPanSettings; - - private AudioSource _audioSource; - private AudioSource _continuousAudioSource; - private AudioReverbFilter _reverbFilter; - private bool _isCurrentlyListening; - private float _listeningTimer; - private float _nextProceduralEventTime; - private Vector3 _listenerPosition; - private bool _hasBeenTapped; - private AudioClip _continuousClip; - private Coroutine _volumeFadeCoroutine; - private Coroutine _startListeningCoroutine; - private int _concentrationFadeTweenId = -1; - private int _crossfadeTweenId = -1; - - /// - /// Returns true if this emitter has been tapped during the current listening session. - /// Used to prevent multiple taps on the same emitter. - /// - public bool HasBeenTapped => _hasBeenTapped; - - /// - /// Returns the camera pan settings for this emitter, or null if not set. - /// - public CameraPanSettings CameraPanSettings => _cameraPanSettings; - - private void Awake() - { - _audioSource = GetComponent(); - if (_audioSource == null) - _audioSource = gameObject.AddComponent(); - - _audioSource.spatialBlend = 1f; - _audioSource.maxDistance = _maxDistance; - _audioSource.rolloffMode = AudioRolloffMode.Custom; - _audioSource.playOnAwake = false; - - _reverbFilter = gameObject.AddComponent(); - _reverbFilter.reverbPreset = AudioReverbPreset.Off; - _reverbFilter.dryLevel = 0f; - _reverbFilter.room = -10000; - - GameObject continuousSourceObject = new GameObject(CONTINUOUS_AUDIO_SOURCE_NAME); - continuousSourceObject.transform.SetParent(transform); - continuousSourceObject.transform.localPosition = Vector3.zero; - _continuousAudioSource = continuousSourceObject.AddComponent(); - _continuousAudioSource.spatialBlend = 1f; - _continuousAudioSource.maxDistance = _hearingRangeMax; - _continuousAudioSource.minDistance = _hearingRangeMin; - _continuousAudioSource.rolloffMode = AudioRolloffMode.Linear; - _continuousAudioSource.playOnAwake = false; - _continuousAudioSource.loop = true; - - if (_emitterParticles != null) - { - _emitterParticles.Stop(); - var emission = _emitterParticles.emission; - emission.enabled = false; - } - } - - private void Start() - { - if (_emitContinuously && _useProceduralAudio) - { - StartContinuousEmission(); - } - } - - private void StartContinuousEmission() - { - float frequency = _baseFrequency + Random.Range(-_frequencyVariation * 0.5f, _frequencyVariation * 0.5f); - _continuousClip = GenerateProceduralAudioClip(frequency, 10f); - - _continuousAudioSource.clip = _continuousClip; - _continuousAudioSource.volume = _continuousBaseVolume * BASELINE_VOLUME_MULTIPLIER; - _continuousAudioSource.loop = true; - _continuousAudioSource.Play(); - } - - /// - /// Sets the concentration state and fades the emitter volume accordingly. - /// When concentrating: fades from 30% to 100% over 1 second. - /// When releasing: fades from 100% to 30% over 0.5 seconds. - /// - public void SetConcentrating(bool concentrating) - { - TweenHelper.CancelTween(ref _concentrationFadeTweenId); - - if (_continuousAudioSource == null) - return; - - float baseVolume = _continuousBaseVolume; - float fromMultiplier = concentrating ? BASELINE_VOLUME_MULTIPLIER : FULL_VOLUME_MULTIPLIER; - float toMultiplier = concentrating ? FULL_VOLUME_MULTIPLIER : BASELINE_VOLUME_MULTIPLIER; - float duration = concentrating ? CONCENTRATION_FADE_DURATION : RELEASE_FADE_DURATION; - - float fromVolume = baseVolume * fromMultiplier; - float toVolume = baseVolume * toMultiplier; - - _concentrationFadeTweenId = TweenHelper.FadeVolume( - _continuousAudioSource, - toVolume, - duration); - } - - /// - /// Starts the listening sequence for this emitter. - /// Blocked if the emitter has already been tapped during this session. - /// - public void StartListening(Vector3 listenerPosition) - { - // Prevent multiple taps on the same emitter - if (_hasBeenTapped) - return; - - if (_startListeningCoroutine != null) - StopCoroutine(_startListeningCoroutine); - - _startListeningCoroutine = StartCoroutine(StartListeningSequence(listenerPosition)); - } - - /// - /// Coroutine that handles the full listening sequence: - /// - Crossfades from continuous to procedural audio - /// - Activates particles - /// - Marks emitter as tapped (prevents re-triggering) - /// - Starts vibration - /// - private IEnumerator StartListeningSequence(Vector3 listenerPosition) - { - _isCurrentlyListening = true; - _listenerPosition = listenerPosition; - _listeningTimer = 0f; - _nextProceduralEventTime = 0f; - - // Mark as tapped to prevent re-triggering - _hasBeenTapped = true; - - if (_volumeFadeCoroutine != null) - StopCoroutine(_volumeFadeCoroutine); - - TweenHelper.CancelTween(ref _crossfadeTweenId); - - float crossfadeDuration = 1f; - - if (_continuousAudioSource != null) - { - _crossfadeTweenId = TweenHelper.FadeVolume( - _continuousAudioSource, - 0f, - crossfadeDuration, - () => - { - if (_continuousAudioSource != null) - _continuousAudioSource.Stop(); - }); - } - - if (_emitterParticles != null) - { - var emission = _emitterParticles.emission; - emission.enabled = true; - _emitterParticles.Play(); - } - - EnableReverb(true); - - yield return new WaitForSeconds(crossfadeDuration); - - if (_useProceduralAudio) - { - PlayProceduralSound(); - } - else if (_staticAudioClip != null) - { - _audioSource.clip = _staticAudioClip; - _audioSource.loop = _loopStaticAudio; - _audioSource.Play(); - } - } - - /// - /// Stops the listening mode and resets the emitter state. - /// This is the only place where _hasBeenTapped is reset to false, - /// allowing the emitter to be used again in a future listening session. - /// - public void StopListening(bool cancelConcentrationFade = true) - { - bool wasBeenTapped = _hasBeenTapped; - - _isCurrentlyListening = false; - _hasBeenTapped = false; - - if (_volumeFadeCoroutine != null) - StopCoroutine(_volumeFadeCoroutine); - - if (_startListeningCoroutine != null) - StopCoroutine(_startListeningCoroutine); - - if (cancelConcentrationFade) - TweenHelper.CancelTween(ref _concentrationFadeTweenId); - - if (_audioSource != null && _audioSource.isPlaying) - _audioSource.Stop(); - - if (_emitterParticles != null && wasBeenTapped) - { - _emitterParticles.Stop(); - var emission = _emitterParticles.emission; - emission.enabled = false; - } - - if (_continuousAudioSource != null && !_continuousAudioSource.isPlaying && gameObject.activeInHierarchy) - { - StartContinuousEmission(); - } - - EnableReverb(false); - } - - private void Update() - { - - if (!_isCurrentlyListening) - return; - - _listeningTimer += Time.deltaTime; - - if (_useProceduralAudio && _listeningTimer >= _nextProceduralEventTime) - { - PlayProceduralSound(); - _nextProceduralEventTime = _listeningTimer + Random.Range(_durationMin, _durationMax); - } - } - - - private void EnableReverb(bool enable) - { - if (_reverbFilter == null) - return; - - if (enable) - { - _reverbFilter.reverbPreset = AudioReverbPreset.Room; - _reverbFilter.dryLevel = Mathf.Lerp(0f, -1000f, 1f - _reverbWetMix); - _reverbFilter.room = Mathf.Lerp(-1000f, -100f, _reverbWetMix); - } - else - { - _reverbFilter.reverbPreset = AudioReverbPreset.Off; - _reverbFilter.dryLevel = 0f; - _reverbFilter.room = -10000; - } - } - - private void PlayProceduralSound() - { - float distance = Vector3.Distance(_listenerPosition, transform.position); - float distanceFactor = Mathf.Clamp01(1f - (distance / _maxDistance)); - float attenuation = _distanceAttenuation.Evaluate(distanceFactor); - - float frequency = _baseFrequency + Random.Range(-_frequencyVariation, _frequencyVariation); - float volume = Mathf.Lerp(_volumeMin, _volumeMax, Random.value) * attenuation; - - AudioClip proceduralClip = GenerateProceduralAudioClip(frequency, Random.Range(_durationMin, _durationMax)); - - _audioSource.clip = proceduralClip; - _audioSource.volume = volume; - _audioSource.loop = false; - _audioSource.Play(); - } - - private AudioClip GenerateProceduralAudioClip(float frequency, float duration) - { - int sampleRate = 44100; - int sampleCount = Mathf.RoundToInt(sampleRate * duration); - - AudioClip clip = AudioClip.Create("ProceduralSound", sampleCount, 1, sampleRate, false); - float[] samples = new float[sampleCount]; - - for (int i = 0; i < sampleCount; i++) - { - float t = (float)i / sampleRate; - float envelope = Mathf.Sin(Mathf.PI * t / duration); - - samples[i] = GenerateSampleForType(frequency, t, _emitterType) * envelope; - } - - clip.SetData(samples, 0); - return clip; - } - - private float GenerateSampleForType(float frequency, float time, AudioEmitterType type) - { - switch (type) - { - case AudioEmitterType.Metal: - return Mathf.Sin(2f * Mathf.PI * frequency * time) * 0.5f + - Mathf.Sin(2f * Mathf.PI * frequency * 2.1f * time) * 0.3f + - Mathf.Sin(2f * Mathf.PI * frequency * 3.8f * time) * 0.2f; - - case AudioEmitterType.Wood: - return Mathf.Sin(2f * Mathf.PI * frequency * time) * 0.6f + - (Random.value - 0.5f) * 0.1f; - - case AudioEmitterType.Glass: - return Mathf.Sin(2f * Mathf.PI * frequency * 1.5f * time) * 0.4f + - Mathf.Sin(2f * Mathf.PI * frequency * 2.7f * time) * 0.3f + - Mathf.Sin(2f * Mathf.PI * frequency * 4.2f * time) * 0.3f; - - case AudioEmitterType.Stone: - return Mathf.Sin(2f * Mathf.PI * frequency * 0.8f * time) * 0.5f + - (Random.value - 0.5f) * 0.3f; - - case AudioEmitterType.Water: - return (Random.value - 0.5f) * 0.5f + - Mathf.Sin(2f * Mathf.PI * frequency * 0.5f * time) * 0.3f; - - case AudioEmitterType.Mechanical: - return Mathf.Sin(2f * Mathf.PI * frequency * time) * 0.4f + - Mathf.Sign(Mathf.Sin(2f * Mathf.PI * frequency * 0.3f * time)) * 0.3f; - - case AudioEmitterType.Electrical: - return (Random.value - 0.5f) * 0.6f + - Mathf.Sin(2f * Mathf.PI * frequency * 50f * time) * 0.2f; - - case AudioEmitterType.Organic: - return Mathf.Sin(2f * Mathf.PI * frequency * 0.7f * time) * 0.5f + - Mathf.Sin(2f * Mathf.PI * frequency * 1.3f * time) * 0.3f + - (Random.value - 0.5f) * 0.2f; - - case AudioEmitterType.Wall: - return Mathf.Sin(2f * Mathf.PI * frequency * 0.5f * time) * 0.3f + - (Random.value - 0.5f) * 0.1f; - - default: - return Mathf.Sin(2f * Mathf.PI * frequency * time); - } - } - - private void OnDisable() - { - StopListening(); - } - - private void OnEnable() - { - if (_continuousAudioSource != null && !_continuousAudioSource.isPlaying && _emitContinuously) - { - StartContinuousEmission(); - } - } - - private void OnDrawGizmosSelected() - { - Gizmos.color = Color.cyan; - Gizmos.DrawWireSphere(transform.position, _maxDistance); - - if (_isCurrentlyListening) - { - Gizmos.color = Color.green; - Gizmos.DrawLine(transform.position, _listenerPosition); - } - } - } -} diff --git a/Assets/_Content/Scripts/Core/Audio/AudioListenable.cs.meta b/Assets/_Content/Scripts/Core/Audio/AudioListenable.cs.meta deleted file mode 100644 index bd04b374..00000000 --- a/Assets/_Content/Scripts/Core/Audio/AudioListenable.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: e15490f7adacb594cb114f535e71d6b6 \ No newline at end of file diff --git a/Assets/_Content/Scripts/Core/Interaction/InteracionHandler.cs b/Assets/_Content/Scripts/Core/Interaction/InteracionHandler.cs deleted file mode 100644 index fa204158..00000000 --- a/Assets/_Content/Scripts/Core/Interaction/InteracionHandler.cs +++ /dev/null @@ -1,67 +0,0 @@ -using UnityEngine; -using Unheimlich.Core; -using Unheimlich.Core.Utils; -using InputManager = Unheimlich.Core.Input.InputManager; - -namespace Unheimlich.Core.Interaction -{ - public class InteractionHandler : Singleton - { - - [Header("Settings")] - [SerializeField] private float _interactionRange = 1f; - [SerializeField] private LayerMask _interactableLayers; - [SerializeField] private float _raycastInterval = 0.1f; - - private InteractableBase _currentInteractable; - private Camera _playerCamera; - private float _raycastTimer; - - protected override void OnAwake() - { - _playerCamera = GetComponent(); - } - - private bool _didHit; - - private void Update() - { - _didHit = RaycastHelper.ThrottledRaycastComponent( - ref _raycastTimer, - _raycastInterval, - _playerCamera, - _interactionRange, - _interactableLayers, - out InteractableBase interactable); - - if (_didHit) - _currentInteractable = interactable; - else if (!_didHit && _raycastTimer <= 0f) - _currentInteractable = null; - - HandleInteractionInput(); - } - - private void HandleInteractionInput() - { - var interactAction = InputManager.GetInteractAction(); - if (interactAction != null && interactAction.WasPressedThisFrame()) - { - if (_currentInteractable != null) - _currentInteractable.Interact(); - } - } - - private void OnDrawGizmosSelected() - { - if (_playerCamera != null) - { - Gizmos.color = _currentInteractable != null ? Color.green : Color.red; - - Vector3 viewportCenter = new Vector3(0.5f, 0.5f, 0f); - Ray ray = _playerCamera.ViewportPointToRay(viewportCenter); - Gizmos.DrawRay(ray.origin, ray.direction * _interactionRange); - } - } -} -} \ No newline at end of file diff --git a/Assets/_Content/Scripts/Core/Interaction/InteracionHandler.cs.meta b/Assets/_Content/Scripts/Core/Interaction/InteracionHandler.cs.meta deleted file mode 100644 index fb33f7f0..00000000 --- a/Assets/_Content/Scripts/Core/Interaction/InteracionHandler.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: d96ceb0ee513b1e4197e55e9b926269b \ No newline at end of file diff --git a/Assets/_Content/Scripts/Core/Interaction/InteractableBase.cs b/Assets/_Content/Scripts/Core/Interaction/InteractableBase.cs deleted file mode 100644 index 277866d7..00000000 --- a/Assets/_Content/Scripts/Core/Interaction/InteractableBase.cs +++ /dev/null @@ -1,17 +0,0 @@ -using UnityEngine; - -namespace Unheimlich.Core.Interaction -{ - /// - /// Base class for all interactable objects in the game. - /// Derived classes must implement Interact() to define interaction behavior. - /// - public abstract class InteractableBase : MonoBehaviour - { - [Header("Interaction Settings")] - [Tooltip("Text displayed when the player looks at the object")] - public string InteractionText = "Interact"; - - public abstract void Interact(); - } -} diff --git a/Assets/_Content/Scripts/Core/Interaction/InteractableBase.cs.meta b/Assets/_Content/Scripts/Core/Interaction/InteractableBase.cs.meta deleted file mode 100644 index d8ebcc3f..00000000 --- a/Assets/_Content/Scripts/Core/Interaction/InteractableBase.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: c2bd6cb9667f8674294172efbdf8afd3 \ No newline at end of file diff --git a/Assets/_Content/Scripts/Core/Interaction/ObjectDescriptionTrigger.cs b/Assets/_Content/Scripts/Core/Interaction/ObjectDescriptionTrigger.cs index f20547d5..e4a7747e 100644 --- a/Assets/_Content/Scripts/Core/Interaction/ObjectDescriptionTrigger.cs +++ b/Assets/_Content/Scripts/Core/Interaction/ObjectDescriptionTrigger.cs @@ -1,8 +1,6 @@ using UnityEngine; using System.Collections.Generic; using Unheimlich.Features.Dialogue.Data; -using Unheimlich.Features.DetectiveBoard.Data; -using Unheimlich.Features.DetectiveBoard; using Unheimlich.Features.Player; using Unheimlich.Features.Dialogue; using Unheimlich.Features.Dialogue.UI; @@ -29,10 +27,6 @@ namespace Unheimlich.Core.Interaction [Header("Prefab")] [SerializeField] private ChoiceFocusHandler _wordPrefab; - [Header("Detective Board Integration")] - [Tooltip("Clue to unlock when words are fully merged (optional)")] - [SerializeField] private ClueData _clueToUnlock; - private Transform _playerTransform; private Camera _mainCamera; private List _spawnedWords = new List(); @@ -327,21 +321,12 @@ namespace Unheimlich.Core.Interaction .setOnComplete(() => { _hasCompletedMergeOnce = true; - UnlockClue(); DespawnWords(); }); } } } - - private void UnlockClue() - { - if (_clueToUnlock != null && DetectiveBoardManager.Instance != null) - { - DetectiveBoardManager.Instance.UnlockClue(_clueToUnlock); - Debug.Log($"[ObjectDescription] Unlocked clue: {_clueToUnlock.name} from description merge"); - } - } + private void UnmergeWords() { diff --git a/Assets/_Content/Scripts/Core/Interaction/PlayerInspectionHandler.cs b/Assets/_Content/Scripts/Core/Interaction/PlayerInspectionHandler.cs deleted file mode 100644 index c66a2622..00000000 --- a/Assets/_Content/Scripts/Core/Interaction/PlayerInspectionHandler.cs +++ /dev/null @@ -1,173 +0,0 @@ -using System.Collections; -using UnityEngine; -using UnityEngine.InputSystem; -using Unheimlich.Core; -using Unheimlich.Features.Items; -using Unheimlich.Features.Player; -using InputManager = Unheimlich.Core.Input.InputManager; - -namespace Unheimlich.Core.Interaction -{ - /// - /// Handles player item inspection mode. - /// - public class PlayerInspectionHandler : Singleton -{ - - [Header("Components")] - [SerializeField] private Transform _inspectionHoldPoint; - - [Header("State")] - public bool IsInspecting { get; private set; } = false; - private InspectableItem _currentInspectedItem; - - private Coroutine _interactionListener; - private bool _isUsingGamepad = false; - - public void ToggleInspection(InspectableItem item) - { - if (!IsInspecting) - { - StartInspection(item); - } - else - { - if (_interactionListener != null) - StopInspection(); - } - } - - private void StartInspection(InspectableItem item) - { - IsInspecting = true; - _currentInspectedItem = item; - - // Set Inspection context (handles cursor automatically) - InputManager.SetContext(InputManager.InputContext.Inspection); - - // Disable OctoPlayer to prevent movement and look - if (PlayerController.Instance?.Movement != null) - { - PlayerController.Instance.Movement.enabled = false; - } - - item.PrepareForInspection(_inspectionHoldPoint); - - if (_interactionListener == null) - _interactionListener = StartCoroutine(ListenForInteraction()); - - } - - private void StopInspection() - { - if (_interactionListener != null) - { - StopCoroutine(_interactionListener); - _interactionListener = null; - } - - StopInspectionInternal(); - } - - private void StopInspectionInternal() - { - if (_currentInspectedItem != null) - { - _currentInspectedItem.Release(); - _currentInspectedItem = null; - - StartCoroutine(DelayedToggleInput(true)); - } - IsInspecting = false; - } - - private IEnumerator DelayedToggleInput(bool enable) - { - yield return new WaitForEndOfFrame(); - - // Re-enable OctoPlayer - if (PlayerController.Instance?.Movement != null) - { - PlayerController.Instance.Movement.enabled = true; - } - - // Restore Normal context (handles cursor automatically) - InputManager.SetContext(InputManager.InputContext.Normal); - - yield break; - } - - - private IEnumerator ListenForInteraction() - { - yield return null; - - while (IsInspecting) - { - var interactAction = InputManager.GetInteractAction(); - if (interactAction != null && interactAction.WasPressedThisFrame()) - { - StopInspectionInternal(); - yield break; - } - - yield return null; - } - _interactionListener = null; - } - - private void Update() - { - if (IsInspecting && _currentInspectedItem != null) - { - HandleObjectRotation(); - } - } - - private void HandleObjectRotation() - { - if (_currentInspectedItem == null) return; - - Vector2 lookInput = Vector2.zero; - bool hasInput = false; - - // Read input directly (not cached) - if (Gamepad.current != null) - { - Vector2 stickInput = Gamepad.current.rightStick.ReadValue(); - if (stickInput.sqrMagnitude > 0.01f) - { - lookInput = stickInput; - hasInput = true; - _isUsingGamepad = true; - } - } - - if (!hasInput && Mouse.current != null) - { - Vector2 mouseDelta = Mouse.current.delta.ReadValue(); - if (mouseDelta.sqrMagnitude > 0.01f) - { - lookInput = mouseDelta; - hasInput = true; - _isUsingGamepad = false; - } - } - - // Only rotate if there's actual input - if (hasInput) - { - // Normalize to get constant rotation speed regardless of input magnitude - Vector2 direction = lookInput.normalized; - - float rotationMultiplier = _isUsingGamepad ? Time.deltaTime : 0.02f; - - float rotX = direction.y * _currentInspectedItem.RotationSpeed * rotationMultiplier; - float rotY = direction.x * _currentInspectedItem.RotationSpeed * rotationMultiplier; - - _currentInspectedItem.transform.Rotate(transform.up, -rotY, Space.World); - _currentInspectedItem.transform.Rotate(transform.right, rotX, Space.World); - } - } -} -} \ No newline at end of file diff --git a/Assets/_Content/Scripts/Core/Interaction/PlayerInspectionHandler.cs.meta b/Assets/_Content/Scripts/Core/Interaction/PlayerInspectionHandler.cs.meta deleted file mode 100644 index e7228868..00000000 --- a/Assets/_Content/Scripts/Core/Interaction/PlayerInspectionHandler.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 464960797e705e84a9eb87ecce4e9311 \ No newline at end of file diff --git a/Assets/_Content/Scripts/Dev/DebugTools.meta b/Assets/_Content/Scripts/Dev/DebugTools.meta deleted file mode 100644 index 06cb96f0..00000000 --- a/Assets/_Content/Scripts/Dev/DebugTools.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 494acbc99a38a1f4dbbe46579e1a4d2c -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Content/Scripts/Dev/DebugTools/AudioSourceDemo.cs b/Assets/_Content/Scripts/Dev/DebugTools/AudioSourceDemo.cs deleted file mode 100644 index a31c1f9f..00000000 --- a/Assets/_Content/Scripts/Dev/DebugTools/AudioSourceDemo.cs +++ /dev/null @@ -1,129 +0,0 @@ -#if UNITY_EDITOR || DEVELOPMENT_BUILD -using UnityEngine; -using Unheimlich.Features.Player; - -namespace Unheimlich.Dev -{ - public class AudioSourceDemo : MonoBehaviour - { - [Header("Demo Settings")] - [SerializeField] private AudioClip _clip; - [SerializeField] private bool _playOnStart = true; - [SerializeField] private bool _loop = true; - [SerializeField] private float _volume = 0.5f; - [SerializeField] private float _sourceMinDistance = 1f; - [SerializeField] private float _sourceMaxDistance = 20; - - [Header("Distance Culling")] - [SerializeField] private bool _enableDistanceCulling = true; - [SerializeField] private float _cullingDistance = 50f; - [SerializeField] private float _fadeStartDistance = 40f; - - private AudioSource _source; - private bool _hasStarted = false; - private float _savedTime = 0f; - private bool _wasPlaying = false; - private bool _isCulled = false; - private Transform _player; - - public float OriginalVolume => _volume; - - public float GetDistanceFadeMultiplier(Vector3 listenerPosition) - { - if (!_enableDistanceCulling) - return 1f; - - float distance = Vector3.Distance(transform.position, listenerPosition); - - if (distance > _cullingDistance) - return 0f; - - if (distance > _fadeStartDistance) - { - float fadeRange = _cullingDistance - _fadeStartDistance; - float fadeAmount = (distance - _fadeStartDistance) / fadeRange; - return 1f - Mathf.Clamp01(fadeAmount); - } - - return 1f; - } - - private void Awake() - { - _source = GetComponent(); - if (_source == null) - _source = gameObject.AddComponent(); - - _source.clip = _clip; - _source.loop = _loop; - _source.volume = _volume; - _source.playOnAwake = false; - _source.spatialBlend = 1f; - _source.rolloffMode = AudioRolloffMode.Logarithmic; - _source.minDistance = _sourceMinDistance; - _source.maxDistance = _sourceMaxDistance; - } - - private void OnEnable() - { - if (_wasPlaying && _savedTime > 0f) - { - _source.time = _savedTime; - _source.Play(); - } - } - - private void Start() - { - if (_playOnStart && _clip != null && !_hasStarted && !_source.isPlaying) - { - _source.Play(); - _hasStarted = true; - } - - PlayerController playerObj = PlayerController.Instance; - if (playerObj != null) - _player = playerObj.transform; - } - - private void Update() - { - if (_enableDistanceCulling && _player != null && _source != null) - { - float distance = Vector3.Distance(transform.position, _player.position); - - if (distance > _cullingDistance) - { - if (!_isCulled) - { - _source.volume = 0f; - _isCulled = true; - } - } - else if (distance > _fadeStartDistance) - { - float fadeRange = _cullingDistance - _fadeStartDistance; - float fadeAmount = (distance - _fadeStartDistance) / fadeRange; - fadeAmount = Mathf.Clamp01(fadeAmount); - _source.volume = _volume * (1f - fadeAmount); - _isCulled = false; - } - else - { - if (_isCulled || _source.volume != _volume) - { - _source.volume = _volume; - _isCulled = false; - } - } - } - } - - private void OnDisable() - { - _wasPlaying = _source.isPlaying; - _savedTime = _source.time; - } - } -} -#endif diff --git a/Assets/_Content/Scripts/Dev/DebugTools/AudioSourceDemo.cs.meta b/Assets/_Content/Scripts/Dev/DebugTools/AudioSourceDemo.cs.meta deleted file mode 100644 index 037b1e22..00000000 --- a/Assets/_Content/Scripts/Dev/DebugTools/AudioSourceDemo.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: f399329860533c14fba15187d7e340db \ No newline at end of file diff --git a/Assets/_Content/Scripts/Dev/DebugTools/DiapasonDebugger.cs b/Assets/_Content/Scripts/Dev/DebugTools/DiapasonDebugger.cs deleted file mode 100644 index e41ab665..00000000 --- a/Assets/_Content/Scripts/Dev/DebugTools/DiapasonDebugger.cs +++ /dev/null @@ -1,124 +0,0 @@ -#if UNITY_EDITOR || DEVELOPMENT_BUILD -using UnityEngine; -using Unheimlich.Features.Player; -using Unheimlich.Features.Diapason; -using Unheimlich.Core.Audio; -namespace Unheimlich.Dev -{ - public class DiapasonDebugger : MonoBehaviour - { - [Header("Debug Display")] - [SerializeField] private bool _showDebugInfo = true; - [SerializeField] private bool _showGizmos = true; - [SerializeField] private Color _listeningRangeColor = Color.cyan; - [SerializeField] private Color _activeTargetColor = Color.green; - - private DiapasonListeningHandlerV2 _listeningHandler; - private AudioListenable[] _allListenables; - - private void Awake() - { - _listeningHandler = DiapasonListeningHandlerV2.Instance; - } - - private void Start() - { - RefreshListenablesList(); - } - - private void OnGUI() - { - if (!_showDebugInfo || _listeningHandler == null) - return; - - GUILayout.BeginArea(new Rect(10, 10, 300, 200)); - GUILayout.Box("Diapason System Debug"); - - GUILayout.Label($"Is Listening: {_listeningHandler.IsListening}"); - GUILayout.Label($"Is Concentrating: {_listeningHandler.IsConcentrating}"); - - if (_allListenables != null) - GUILayout.Label($"Total Listenables: {_allListenables.Length}"); - - if (GUILayout.Button("Refresh Listenables List")) - RefreshListenablesList(); - - if (GUILayout.Button("Toggle Debug Gizmos")) - _showGizmos = !_showGizmos; - - GUILayout.EndArea(); - } - - private void RefreshListenablesList() - { - _allListenables = FindObjectsByType(FindObjectsSortMode.None); - if (!_showDebugInfo) - return; - Debug.Log($"Found {_allListenables.Length} AudioListenable objects in scene"); - } - - private void OnDrawGizmos() - { - if (!_showGizmos) - return; - - if (_allListenables == null) - _allListenables = FindObjectsByType(FindObjectsSortMode.None); - - foreach (AudioListenable listenable in _allListenables) - { - if (listenable == null) - continue; - - Gizmos.color = _listeningRangeColor; - Gizmos.DrawIcon(listenable.transform.position, "sv_icon_dot3_pix16_gizmo", true); - - Gizmos.color = new Color(_listeningRangeColor.r, _listeningRangeColor.g, _listeningRangeColor.b, 0.1f); - Gizmos.DrawSphere(listenable.transform.position, 0.2f); - } - - if (_listeningHandler != null && _listeningHandler.IsListening) - { - Gizmos.color = _activeTargetColor; - } - } - - [ContextMenu("List All Listenables")] - private void ListAllListenables() - { - RefreshListenablesList(); - - Debug.Log("=== Audio Listenables in Scene ==="); - foreach (AudioListenable listenable in _allListenables) - { - if (listenable != null) - Debug.Log($"- {listenable.gameObject.name} (Type: {listenable.GetType().Name})"); - } - } - - [ContextMenu("Enable All Listenables")] - private void EnableAllListenables() - { - RefreshListenablesList(); - foreach (AudioListenable listenable in _allListenables) - { - if (listenable != null) - listenable.enabled = true; - } - Debug.Log("Enabled all AudioListenable components"); - } - - [ContextMenu("Disable All Listenables")] - private void DisableAllListenables() - { - RefreshListenablesList(); - foreach (AudioListenable listenable in _allListenables) - { - if (listenable != null) - listenable.enabled = false; - } - Debug.Log("Disabled all AudioListenable components"); - } - } -} -#endif diff --git a/Assets/_Content/Scripts/Dev/DebugTools/DiapasonDebugger.cs.meta b/Assets/_Content/Scripts/Dev/DebugTools/DiapasonDebugger.cs.meta deleted file mode 100644 index 6d8f5e8c..00000000 --- a/Assets/_Content/Scripts/Dev/DebugTools/DiapasonDebugger.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 7093688fd39800645bbe00b6d9a3f27b \ No newline at end of file diff --git a/Assets/_Content/Scripts/Dev/DebugTools/DiapasonPoseTweaker.cs b/Assets/_Content/Scripts/Dev/DebugTools/DiapasonPoseTweaker.cs deleted file mode 100644 index 75679bcb..00000000 --- a/Assets/_Content/Scripts/Dev/DebugTools/DiapasonPoseTweaker.cs +++ /dev/null @@ -1,183 +0,0 @@ -#if UNITY_EDITOR || DEVELOPMENT_BUILD -using UnityEngine; -using Unheimlich.Features.OctoArms; - -namespace Unheimlich.Dev -{ - -[ExecuteInEditMode] -public class DiapasonPoseTweaker : MonoBehaviour -{ - [Header("References")] - [SerializeField] private OctoIkArmsImproved _armsSystem; - // [SerializeField] private int _armIndex = 1; - - [Header("Quick Presets")] - [SerializeField] private bool _applyPreset1 = false; - [SerializeField] private bool _applyPreset2 = false; - [SerializeField] private bool _applyPreset3 = false; - - [Header("Live Tweaking (Runtime Only)")] - [SerializeField] private Vector3 _shoulderRotation = new Vector3(20, 0, -10); - [SerializeField] private Vector3 _elbowRotation = new Vector3(0, 0, 0); - [SerializeField] private Vector3 _wristRotation = new Vector3(0, 0, 90); - - [Header("Preview")] - [SerializeField] private bool _previewPose = false; - - private ArmEquipment _equipment; - private bool _wasPreviewActive = false; - - void Update() - { - if (!Application.isPlaying) - return; - - if (_armsSystem == null) - return; - - if (_equipment == null) - { - var armRigField = typeof(OctoIkArmsImproved).GetField("_rightArm", - System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); - - if (armRigField != null) - { - var armRig = armRigField.GetValue(_armsSystem); - var equipmentField = armRig.GetType().GetField("Equipment"); - if (equipmentField != null) - { - _equipment = (ArmEquipment)equipmentField.GetValue(armRig); - } - } - } - - if (_equipment == null) - return; - - if (_previewPose && _equipment.State == ArmState.Environmental) - { - ApplyRotations(); - _wasPreviewActive = true; - } - else if (_wasPreviewActive && !_previewPose) - { - ResetRotations(); - _wasPreviewActive = false; - } - - if (_equipment.State == ArmState.Equipped) - { - _equipment.ShoulderRotationOffset = _shoulderRotation; - _equipment.ElbowRotationOffset = _elbowRotation; - _equipment.WristRotationOffset = _wristRotation; - } - } - - void OnValidate() - { - if (_applyPreset1) - { - _applyPreset1 = false; - ApplyPreset1(); - } - - if (_applyPreset2) - { - _applyPreset2 = false; - ApplyPreset2(); - } - - if (_applyPreset3) - { - _applyPreset3 = false; - ApplyPreset3(); - } - } - - private void ApplyPreset1() - { - _shoulderRotation = new Vector3(30, 0, -15); - _elbowRotation = new Vector3(0, 10, 0); - _wristRotation = new Vector3(0, 0, 90); - Debug.Log("[DiapasonPose] Applied Preset 1: Close to body, slight inward"); - } - - private void ApplyPreset2() - { - _shoulderRotation = new Vector3(20, -10, -20); - _elbowRotation = new Vector3(0, 0, 0); - _wristRotation = new Vector3(0, -15, 90); - Debug.Log("[DiapasonPose] Applied Preset 2: Further from left hand, angled"); - } - - private void ApplyPreset3() - { - _shoulderRotation = new Vector3(15, 0, -25); - _elbowRotation = new Vector3(0, 15, 0); - _wristRotation = new Vector3(0, 0, 85); - Debug.Log("[DiapasonPose] Applied Preset 3: Natural grip, centered"); - } - - private void ApplyRotations() - { - if (_equipment.ShoulderBone != null) - { - _equipment.ShoulderBone.localRotation = _equipment.OriginalShoulderRotation * Quaternion.Euler(_shoulderRotation); - } - - if (_equipment.ElbowBone != null) - { - _equipment.ElbowBone.localRotation = _equipment.OriginalElbowRotation * Quaternion.Euler(_elbowRotation); - } - - if (_equipment.WristBone != null) - { - _equipment.WristBone.localRotation = _equipment.OriginalWristRotation * Quaternion.Euler(_wristRotation); - } - } - - private void ResetRotations() - { - if (_equipment.ShoulderBone != null) - { - _equipment.ShoulderBone.localRotation = _equipment.OriginalShoulderRotation; - } - - if (_equipment.ElbowBone != null) - { - _equipment.ElbowBone.localRotation = _equipment.OriginalElbowRotation; - } - - if (_equipment.WristBone != null) - { - _equipment.WristBone.localRotation = _equipment.OriginalWristRotation; - } - } - - void OnGUI() - { - if (!Application.isPlaying) - return; - - GUILayout.BeginArea(new Rect(10, 150, 350, 250)); - GUILayout.Label("=== Diapason Pose Tweaker ==="); - - GUILayout.Label($"Shoulder: {_shoulderRotation}"); - GUILayout.Label($"Elbow: {_elbowRotation}"); - GUILayout.Label($"Wrist: {_wristRotation}"); - - GUILayout.Space(10); - GUILayout.Label("Tips:"); - GUILayout.Label("• Shoulder X: + = down, - = up"); - GUILayout.Label("• Shoulder Y: + = inward, - = outward"); - GUILayout.Label("• Shoulder Z: + = twist out, - = twist in"); - GUILayout.Label("• Elbow Y: + = bend out, - = bend in"); - GUILayout.Label("• Wrist Z: rotation du diapason"); - - GUILayout.EndArea(); - } -} - -} -#endif diff --git a/Assets/_Content/Scripts/Dev/DebugTools/DiapasonPoseTweaker.cs.meta b/Assets/_Content/Scripts/Dev/DebugTools/DiapasonPoseTweaker.cs.meta deleted file mode 100644 index 57b64544..00000000 --- a/Assets/_Content/Scripts/Dev/DebugTools/DiapasonPoseTweaker.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 8d1a1f500877b404fa6d23dff3bfa8c8 \ No newline at end of file diff --git a/Assets/_Content/Scripts/Dev/TestScripts/DiapasonEquipmentTester.cs b/Assets/_Content/Scripts/Dev/TestScripts/DiapasonEquipmentTester.cs deleted file mode 100644 index 16ee9689..00000000 --- a/Assets/_Content/Scripts/Dev/TestScripts/DiapasonEquipmentTester.cs +++ /dev/null @@ -1,49 +0,0 @@ -#if UNITY_EDITOR || DEVELOPMENT_BUILD -using UnityEngine; -using UnityEngine.InputSystem; -using Unheimlich.Features.Diapason; - -namespace Unheimlich.Dev -{ - -public class DiapasonEquipmentTester : MonoBehaviour -{ - [Header("References")] - [SerializeField] private DiapasonEquipmentManager _equipmentManager; - - [Header("Input")] - [SerializeField] private Key _equipKey = Key.E; - [SerializeField] private Key _unequipKey = Key.R; - - void Update() - { - if (_equipmentManager == null) - return; - - if (Keyboard.current[_equipKey].wasPressedThisFrame) - { - _equipmentManager.EquipDiapason(); - } - - if (Keyboard.current[_unequipKey].wasPressedThisFrame) - { - _equipmentManager.UnequipDiapason(); - } - } - - void OnGUI() - { - GUILayout.BeginArea(new Rect(10, 10, 350, 150)); - GUILayout.Label("=== Diapason Equipment System ==="); - GUILayout.Label($"Press {_equipKey} to Equip Diapason"); - GUILayout.Label($"Press {_unequipKey} to Unequip Diapason"); - GUILayout.Label(""); - GUILayout.Label("When equipped:"); - GUILayout.Label("• Move LEFT hand to touch AudioListenable"); - GUILayout.Label("• LEFT CLICK to tap and activate"); - GUILayout.EndArea(); - } -} - -} -#endif diff --git a/Assets/_Content/Scripts/Dev/TestScripts/DiapasonEquipmentTester.cs.meta b/Assets/_Content/Scripts/Dev/TestScripts/DiapasonEquipmentTester.cs.meta deleted file mode 100644 index 01be6d3d..00000000 --- a/Assets/_Content/Scripts/Dev/TestScripts/DiapasonEquipmentTester.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 9279e0fb73118d14ba6f2cf05170a660 \ No newline at end of file diff --git a/Assets/_Content/Scripts/Editor/AudioListenableEditor.cs b/Assets/_Content/Scripts/Editor/AudioListenableEditor.cs deleted file mode 100644 index 216c554c..00000000 --- a/Assets/_Content/Scripts/Editor/AudioListenableEditor.cs +++ /dev/null @@ -1,106 +0,0 @@ -using UnityEngine; -using UnityEditor; -using Unheimlich.Core.Audio; - -namespace Unheimlich.Editor -{ - [CustomEditor(typeof(AudioListenable))] - public class AudioListenableEditor : UnityEditor.Editor - { - public override void OnInspectorGUI() - { - DrawDefaultInspector(); - - AudioListenable listenable = (AudioListenable)target; - - EditorGUILayout.Space(10); - EditorGUILayout.LabelField("Quick Setup", EditorStyles.boldLabel); - - if (GUILayout.Button("Setup as Wall")) - SetupAsType(listenable, AudioEmitterType.Wall, 200f, 20f, true); - - if (GUILayout.Button("Setup as Metal Object")) - SetupAsType(listenable, AudioEmitterType.Metal, 800f, 100f, false); - - if (GUILayout.Button("Setup as Wood Object")) - SetupAsType(listenable, AudioEmitterType.Wood, 300f, 50f, false); - - if (GUILayout.Button("Setup as Glass")) - SetupAsType(listenable, AudioEmitterType.Glass, 1200f, 150f, false); - - if (GUILayout.Button("Setup as Mechanical")) - SetupAsType(listenable, AudioEmitterType.Mechanical, 500f, 80f, false); - - EditorGUILayout.Space(5); - - if (GUILayout.Button("Test Sound (Play Mode Only)", GUILayout.Height(30))) - { - if (Application.isPlaying) - { - listenable.StartListening(Camera.main != null ? Camera.main.transform.position : Vector3.zero); - } - else - { - EditorGUILayout.HelpBox("Enter Play Mode to test sounds", MessageType.Info); - } - } - } - - private void SetupAsType(AudioListenable listenable, AudioEmitterType type, float baseFreq, float freqVar, bool isGeneric) - { - SerializedObject so = new SerializedObject(listenable); - - so.FindProperty("_emitterType").enumValueIndex = (int)type; - so.FindProperty("_useProceduralAudio").boolValue = true; - so.FindProperty("_baseFrequency").floatValue = baseFreq; - so.FindProperty("_frequencyVariation").floatValue = freqVar; - - if (isGeneric) - { - so.FindProperty("_volumeMin").floatValue = 0.2f; - so.FindProperty("_volumeMax").floatValue = 0.4f; - } - - so.ApplyModifiedProperties(); - EditorUtility.SetDirty(listenable); - } - } - - [CanEditMultipleObjects] - [CustomEditor(typeof(Transform))] - public class TransformEditorExtension : UnityEditor.Editor - { - private UnityEditor.Editor defaultEditor; - - private void OnEnable() - { - defaultEditor = CreateEditor(targets, System.Type.GetType("UnityEditor.TransformInspector, UnityEditor")); - } - - private void OnDisable() - { - if (defaultEditor != null) - DestroyImmediate(defaultEditor); - } - - public override void OnInspectorGUI() - { - if (defaultEditor != null) - defaultEditor.OnInspectorGUI(); - - EditorGUILayout.Space(10); - - if (targets.Length == 1) - { - Transform t = (Transform)target; - if (t.GetComponent() == null) - { - if (GUILayout.Button("Add Audio Listenable", GUILayout.Height(25))) - { - Undo.AddComponent(t.gameObject); - } - } - } - } - } -} diff --git a/Assets/_Content/Scripts/Editor/AudioListenableEditor.cs.meta b/Assets/_Content/Scripts/Editor/AudioListenableEditor.cs.meta deleted file mode 100644 index c6856975..00000000 --- a/Assets/_Content/Scripts/Editor/AudioListenableEditor.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: b606ceac9567f9746b716e63b0e644ee \ No newline at end of file diff --git a/Assets/_Content/Scripts/Editor/AudioSourceInspector.cs b/Assets/_Content/Scripts/Editor/AudioSourceInspector.cs deleted file mode 100644 index 0f1a6963..00000000 --- a/Assets/_Content/Scripts/Editor/AudioSourceInspector.cs +++ /dev/null @@ -1,63 +0,0 @@ -using UnityEngine; - -namespace Unheimlich.Audio -{ - public class AudioSourceInspector : MonoBehaviour - { - [Header("Debug Settings")] - [SerializeField] private float _logInterval = 1f; - - private AudioSource _source; - private float _lastLogTime; - - private void Awake() - { - _source = GetComponent(); - } - - private void Update() - { - if (_source == null) return; - - if (Time.time - _lastLogTime >= _logInterval) - { - LogAudioSourceState(); - _lastLogTime = Time.time; - } - } - - private void OnEnable() - { - Debug.Log($"[AudioInspector] OnEnable called on {gameObject.name}"); - if (_source != null) - LogAudioSourceState(); - } - - private void OnDisable() - { - Debug.Log($"[AudioInspector] OnDisable called on {gameObject.name}"); - if (_source != null) - LogAudioSourceState(); - } - - private void LogAudioSourceState() - { - if (_source == null) - { - Debug.LogWarning($"[AudioInspector] No AudioSource on {gameObject.name}"); - return; - } - - string clipName = _source.clip != null ? _source.clip.name : "NULL"; - Debug.Log($"[AudioInspector] {gameObject.name} | " + - $"Clip: {clipName} | " + - $"Time: {_source.time:F2}s | " + - $"isPlaying: {_source.isPlaying} | " + - $"enabled: {_source.enabled} | " + - $"volume: {_source.volume:F2} | " + - $"mute: {_source.mute} | " + - $"loop: {_source.loop} | " + - $"Active: {gameObject.activeInHierarchy}"); - } - } -} diff --git a/Assets/_Content/Scripts/Editor/AudioSourceInspector.cs.meta b/Assets/_Content/Scripts/Editor/AudioSourceInspector.cs.meta deleted file mode 100644 index 68aa2126..00000000 --- a/Assets/_Content/Scripts/Editor/AudioSourceInspector.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 65fea0a39ddb811419ec546ea98e5917 \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard.meta b/Assets/_Content/Scripts/Features/DetectiveBoard.meta deleted file mode 100644 index 9c3cb40f..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 3e30b918248fbaa45bd741cc5a8bbeac -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/Data.meta b/Assets/_Content/Scripts/Features/DetectiveBoard/Data.meta deleted file mode 100644 index 0ce23752..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/Data.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 41ce6b2bff04d1e4d9eb45ac2c7dd0d9 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/Data/ClueData.cs b/Assets/_Content/Scripts/Features/DetectiveBoard/Data/ClueData.cs deleted file mode 100644 index a0acfdbb..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/Data/ClueData.cs +++ /dev/null @@ -1,30 +0,0 @@ -using UnityEngine; - -namespace Unheimlich.Features.DetectiveBoard.Data -{ - [CreateAssetMenu(fileName = "ClueData", menuName = "Unheimlich/Detective Board/Clue Data")] - public class ClueData : ScriptableObject - { - [Header("Clue Identity")] - [Tooltip("Unique identifier for this clue")] - public string ClueID; - - [Tooltip("Display title on the board")] - public string Title; - - [Tooltip("Detailed description of the clue")] - [TextArea(3, 6)] - public string Description; - - [Header("Visual Representation")] - [Tooltip("Icon or image for the node (post-it/card visual)")] - public Sprite Icon; - - [Tooltip("Color tint for the node")] - public Color NodeColor = Color.white; - - [Header("Meta")] - [Tooltip("Category for organization (e.g., 'Evidence', 'Witness', 'Location')")] - public string Category; - } -} diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/Data/ClueData.cs.meta b/Assets/_Content/Scripts/Features/DetectiveBoard/Data/ClueData.cs.meta deleted file mode 100644 index 4fe7637c..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/Data/ClueData.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 166ae120db91b064c9b98086267b9783 \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/Data/PostItNodeData.cs b/Assets/_Content/Scripts/Features/DetectiveBoard/Data/PostItNodeData.cs deleted file mode 100644 index 3e8a77d7..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/Data/PostItNodeData.cs +++ /dev/null @@ -1,75 +0,0 @@ -using UnityEngine; -using System; -using System.Collections.Generic; -using Unheimlich.Features.DetectiveBoard.UI; - -namespace Unheimlich.Features.DetectiveBoard.Data -{ - [Serializable] - public class PostItNodeData - { - public string NodeID; - public Vector2 Position; - public float Rotation; - public bool IsEliminated; - public List Connections = new List(); - - public PostItNodeData(string id, Vector2 position) - { - NodeID = id; - Position = position; - Rotation = 0f; - IsEliminated = false; - } - - public List ConnectedNodeIDs - { - get - { - List ids = new List(); - foreach (var conn in Connections) - { - ids.Add(conn.TargetNodeID); - } - return ids; - } - } - } - - [Serializable] - public class ConnectionData - { - public string TargetNodeID; - public LineColorState ColorState; - - public ConnectionData(string targetId, LineColorState colorState = LineColorState.Green) - { - TargetNodeID = targetId; - ColorState = colorState; - } - } - - [Serializable] - public class DetectiveBoardSaveData - { - public List Nodes = new List(); - - public PostItNodeData GetNodeData(string nodeID) - { - return Nodes.Find(n => n.NodeID == nodeID); - } - - public void AddOrUpdateNode(PostItNodeData nodeData) - { - int index = Nodes.FindIndex(n => n.NodeID == nodeData.NodeID); - if (index >= 0) - { - Nodes[index] = nodeData; - } - else - { - Nodes.Add(nodeData); - } - } - } -} diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/Data/PostItNodeData.cs.meta b/Assets/_Content/Scripts/Features/DetectiveBoard/Data/PostItNodeData.cs.meta deleted file mode 100644 index 8aaaf19e..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/Data/PostItNodeData.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: f2344709e137fdf46aaa5445a330db2f \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/Editor.meta b/Assets/_Content/Scripts/Features/DetectiveBoard/Editor.meta deleted file mode 100644 index 480e9b89..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/Editor.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: a7317abe29dcd654f99d8d2ad8e1d179 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/Editor/ClueDataEditor.cs b/Assets/_Content/Scripts/Features/DetectiveBoard/Editor/ClueDataEditor.cs deleted file mode 100644 index 6a6e2072..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/Editor/ClueDataEditor.cs +++ /dev/null @@ -1,39 +0,0 @@ -using UnityEngine; -using UnityEditor; -using Unheimlich.Features.DetectiveBoard.Data; -using Unheimlich.Features.DetectiveBoard; - -namespace Unheimlich.Editor -{ - [CustomEditor(typeof(ClueData))] - public class ClueDataEditor : UnityEditor.Editor - { - public override void OnInspectorGUI() - { - DrawDefaultInspector(); - - ClueData clueData = (ClueData)target; - - EditorGUILayout.Space(); - EditorGUILayout.LabelField("Quick Actions", EditorStyles.boldLabel); - - if (GUILayout.Button("Generate Unique ID")) - { - clueData.ClueID = System.Guid.NewGuid().ToString(); - EditorUtility.SetDirty(clueData); - } - - if (GUILayout.Button("Test Unlock Clue (Play Mode Only)")) - { - if (Application.isPlaying) - { - DetectiveBoardManager.Instance?.UnlockClue(clueData); - } - else - { - Debug.LogWarning("This button only works in Play Mode!"); - } - } - } - } -} diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/Editor/ClueDataEditor.cs.meta b/Assets/_Content/Scripts/Features/DetectiveBoard/Editor/ClueDataEditor.cs.meta deleted file mode 100644 index b9c8b9a9..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/Editor/ClueDataEditor.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 184cbe5b79ee0a84ba29da016ec76159 \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime.meta b/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime.meta deleted file mode 100644 index d39c8c54..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 81f81f88e30714043a121630f7bf7cb9 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/ClueUnlockOnAudio.cs b/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/ClueUnlockOnAudio.cs deleted file mode 100644 index 3fee361e..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/ClueUnlockOnAudio.cs +++ /dev/null @@ -1,40 +0,0 @@ -using UnityEngine; -using Unheimlich.Core.Audio; -using Unheimlich.Features.DetectiveBoard.Data; - -namespace Unheimlich.Features.DetectiveBoard -{ - /// - /// Unlocks a clue when a specific AudioSource plays, finishes, or reaches a timestamp. - /// Call OnAudioPlay() from whatever drives the AudioSource to start monitoring. - /// - public class ClueUnlockOnAudio : AudioTriggerBase - { - [Header("Clue Configuration")] - [Tooltip("The clue to unlock")] - [SerializeField] private ClueData _clueToUnlock; - - protected override void Execute() - { - if (_clueToUnlock == null) - { - Debug.LogError("[ClueUnlockOnAudio] No ClueData assigned!", this); - return; - } - - if (DetectiveBoardManager.Instance == null) - { - Debug.LogError("[ClueUnlockOnAudio] DetectiveBoardManager not found!", this); - return; - } - - DetectiveBoardManager.Instance.UnlockClue(_clueToUnlock); - - if (_debugMode) - Debug.Log($"[ClueUnlockOnAudio] Unlocked clue '{_clueToUnlock.Title}' on '{_fireCondition}'.", this); - } - - /// Resets the unlock state so the clue can be unlocked again. - public void ResetUnlock() => ResetFired(); - } -} diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/ClueUnlockOnAudio.cs.meta b/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/ClueUnlockOnAudio.cs.meta deleted file mode 100644 index 4291cb31..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/ClueUnlockOnAudio.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: fcedba870c1ea9642a272a53148bb933 \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/ClueUnlockOnInteraction.cs b/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/ClueUnlockOnInteraction.cs deleted file mode 100644 index 337f7048..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/ClueUnlockOnInteraction.cs +++ /dev/null @@ -1,60 +0,0 @@ -using UnityEngine; -using Unheimlich.Features.DetectiveBoard.Data; -using Unheimlich.Core.Interaction; -using Unheimlich.Features.DetectiveBoard; -using Unheimlich.Features.Items; - -namespace Unheimlich.Features.DetectiveBoard -{ - /// - /// Unlocks a clue when the player interacts with this object. - /// Can work standalone (extends InteractableBase) or piggyback on an existing InspectableItem. - /// - public class ClueUnlockOnInteraction : InteractableBase - { - [Header("Clue Configuration")] - [Tooltip("The clue to unlock when player interacts")] - [SerializeField] private ClueData _clueToUnlock; - - [Tooltip("Only unlock once")] - [SerializeField] private bool _unlockOnlyOnce = true; - - private bool _hasUnlocked; - private InspectableItem _inspectableItem; - - private void Awake() - { - _inspectableItem = GetComponent(); - } - - public override void Interact() - { - if (_unlockOnlyOnce && _hasUnlocked) - { - if (_inspectableItem != null) - _inspectableItem.Interact(); - return; - } - - if (_clueToUnlock == null) - { - Debug.LogError("[ClueUnlockOnInteraction] No clue data assigned!"); - if (_inspectableItem != null) - _inspectableItem.Interact(); - return; - } - - if (DetectiveBoardManager.Instance != null) - { - DetectiveBoardManager.Instance.UnlockClue(_clueToUnlock); - _hasUnlocked = true; - - } - - if (_inspectableItem != null) - { - _inspectableItem.Interact(); - } - } - } -} diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/ClueUnlockOnInteraction.cs.meta b/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/ClueUnlockOnInteraction.cs.meta deleted file mode 100644 index 69ceb38d..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/ClueUnlockOnInteraction.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: cca584f48d1f0ab429416470fd01d543 \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/ClueUnlockTrigger.cs b/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/ClueUnlockTrigger.cs deleted file mode 100644 index c0380cca..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/ClueUnlockTrigger.cs +++ /dev/null @@ -1,98 +0,0 @@ -using UnityEngine; -using Unheimlich.Core.Events; -using Unheimlich.Features.DetectiveBoard.Data; - -namespace Unheimlich.Features.DetectiveBoard -{ - /// - /// Unlocks a clue on the detective board. - /// Can be driven by an optional GameEventTrigger on the same GameObject, - /// or called directly via UnlockClue() from any external script or UnityEvent. - /// - public class ClueUnlockTrigger : MonoBehaviour - { - [Header("Clue Configuration")] - [Tooltip("The clue data to unlock when triggered")] - [SerializeField] private ClueData _clueToUnlock; - - [Header("Auto Trigger")] - [Tooltip("Unlock automatically on Start()")] - [SerializeField] private bool _unlockOnStart; - - [Tooltip("Delay in seconds before unlocking (only with Unlock On Start)")] - [SerializeField] private float _unlockDelay; - - [Tooltip("Prevent unlocking more than once")] - [SerializeField] private bool _unlockOnlyOnce = true; - - [Header("Debug")] - [SerializeField] private bool _debugMode; - - private GameEventTrigger _eventTrigger; - private bool _hasUnlocked; - - /// Raised after the clue has been successfully unlocked. - public event System.Action OnClueUnlocked; - - private void Awake() - { - // Optional composition with GameEventTrigger - _eventTrigger = GetComponent(); - if (_eventTrigger != null) - _eventTrigger.OnTriggered += UnlockClue; - } - - private void Start() - { - if (_unlockOnStart) - { - if (_unlockDelay > 0f) - Invoke(nameof(UnlockClue), _unlockDelay); - else - UnlockClue(); - } - } - - private void OnDestroy() - { - if (_eventTrigger != null) - _eventTrigger.OnTriggered -= UnlockClue; - } - - /// - /// Unlocks the associated clue. Can be called directly from external scripts or UnityEvents. - /// - public void UnlockClue() - { - if (_unlockOnlyOnce && _hasUnlocked) - return; - - if (_clueToUnlock == null) - { - Debug.LogError("[ClueUnlockTrigger] No clue data assigned!", this); - return; - } - - if (DetectiveBoardManager.Instance == null) - { - Debug.LogError("[ClueUnlockTrigger] DetectiveBoardManager not found!", this); - return; - } - - DetectiveBoardManager.Instance.UnlockClue(_clueToUnlock); - _hasUnlocked = true; - - if (_debugMode) - Debug.Log($"[ClueUnlockTrigger] Unlocked clue: {_clueToUnlock.Title}", this); - - OnClueUnlocked?.Invoke(); - } - - /// Resets the unlock state so the clue can be unlocked again. - public void ResetUnlock() - { - _hasUnlocked = false; - CancelInvoke(nameof(UnlockClue)); - } - } -} diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/ClueUnlockTrigger.cs.meta b/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/ClueUnlockTrigger.cs.meta deleted file mode 100644 index 2cfa6eb2..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/ClueUnlockTrigger.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 61b90106a531ea840b670f368e2c8aa9 \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/DetectiveBoardController.cs b/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/DetectiveBoardController.cs deleted file mode 100644 index e1b6ee7f..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/DetectiveBoardController.cs +++ /dev/null @@ -1,219 +0,0 @@ -using UnityEngine; -using Unheimlich.Core; -using Unheimlich.World.CameraSystem; -using Unheimlich.Features.Player; -using Unheimlich.Features.OctoArms; -using InputManager = Unheimlich.Core.Input.InputManager; -using Unheimlich.Features.DetectiveBoard.UI; -using Unheimlich.Features.Dialogue.UI; - -namespace Unheimlich.Features.DetectiveBoard -{ - /// - /// Controls player interaction with the detective board. - /// - public class DetectiveBoardController : Singleton - { - [Header("Settings")] - [SerializeField] private float _activationRange = 2f; - [SerializeField] private LayerMask _boardLayer; - [SerializeField] private CameraPanSettings _playerAlignmentSettings; - - [Header("Virtual Camera")] - [SerializeField] private DetectiveBoardVirtualCamera _virtualCamera; - - private bool _isBoardActive; - private CameraHeadBob _cameraHeadBob; - - public bool IsBoardActive => _isBoardActive; - - protected override void OnAwake() - { - _cameraHeadBob = FindFirstObjectByType(); - } - - void Start() - { - if(_virtualCamera == null) - _virtualCamera = DetectiveBoardVirtualCamera.Instance; - } - - private void Update() - { - if (_isBoardActive) - { - HandleBoardInteraction(); - } - else - { - CheckForBoardActivation(); - } - } - - private void CheckForBoardActivation() - { - if (PlayerController.Instance == null || - PlayerController.Instance.PlayerCamera == null || - DetectiveBoardCanvas.Instance == null) - return; - - var camera = PlayerController.Instance.PlayerCamera; - var boardTransform = DetectiveBoardCanvas.Instance.transform; - - float distanceToBoard = Vector3.Distance(camera.transform.position, boardTransform.position); - if (distanceToBoard > _activationRange) - return; - - Ray ray = new Ray(camera.transform.position, camera.transform.forward); - if (Physics.Raycast(ray, out RaycastHit hit, _activationRange, _boardLayer)) - { - bool isLookingAtBoard = (hit.transform == boardTransform) || - (boardTransform.IsChildOf(hit.transform)) || - (hit.transform.IsChildOf(boardTransform)); - - if (isLookingAtBoard) - { - var interactAction = InputManager.GetInteractAction(); - if (interactAction != null && interactAction.WasPressedThisFrame()) - { - ActivateBoard(); - } - } - } - } - - public void ActivateBoard() - { - if (_isBoardActive || - PlayerController.Instance?.PlayerCamera == null || - PlayerController.Instance?.CameraPanController == null || - DetectiveBoardCanvas.Instance == null || - DetectiveBoardVirtualCamera.Instance == null) - { - Debug.LogError("[DetectiveBoardController] Missing required references!"); - return; - } - - _isBoardActive = true; - - if (PlayerController.Instance.Movement != null) - { - PlayerController.Instance.Movement.enabled = false; - } - - OctoIkArmsImproved arms = PlayerController.Instance.GetComponentInChildren(); - if (arms != null) - { - arms.HideArms(3f); - } - - InputManager.SetContext(InputManager.InputContext.DetectiveBoard); - - if (_cameraHeadBob != null) - { - _cameraHeadBob.SetMode(HeadBobMode.Disabled); - } - - if (CrosshairController.Instance != null) - { - CrosshairController.Instance.Hide(); - } - - var cameraPan = PlayerController.Instance.CameraPanController; - var originalSettings = cameraPan.Settings; - - if (_playerAlignmentSettings != null) - cameraPan.Settings = _playerAlignmentSettings; - - cameraPan.SaveOriginalState(); - - cameraPan.AlignToTarget(DetectiveBoardCanvas.Instance.transform, () => - { - DetectiveBoardVirtualCamera.Instance.ActivateFromPlayerPosition( - PlayerController.Instance.PlayerCamera, - DetectiveBoardCanvas.Instance.transform, - DetectiveBoardCanvas.Instance.BoardRenderer - ); - - // Show clue hand fan when board is ready - if (ClueHandUI.Instance != null) - ClueHandUI.Instance.SetHandVisible(true); - - if (_playerAlignmentSettings != null) - cameraPan.Settings = originalSettings; - }); - } - - private void HandleBoardInteraction() - { - var exitAction = InputManager.GetExitAction(); - if (exitAction != null && exitAction.WasPressedThisFrame()) - { - DeactivateBoard(); - } - } - - public void DeactivateBoard() - { - if (!_isBoardActive || DetectiveBoardVirtualCamera.Instance == null) - return; - - _isBoardActive = false; - - // Hide clue hand fan - if (ClueHandUI.Instance != null) - ClueHandUI.Instance.SetHandVisible(false); - - DetectiveBoardVirtualCamera.Instance.Deactivate(() => - { - var cameraPan = PlayerController.Instance?.CameraPanController; - if (cameraPan != null) - { - OctoIkArmsImproved arms = PlayerController.Instance.GetComponentInChildren(); - if (arms != null) - { - arms.ShowArms(3f); - } - - cameraPan.ReturnToOriginalPosition(() => - { - if (PlayerController.Instance?.Movement != null) - { - PlayerController.Instance.Movement.enabled = true; - } - }); - } - else if (PlayerController.Instance?.Movement != null) - { - PlayerController.Instance.Movement.enabled = true; - } - }); - - InputManager.SetContext(InputManager.InputContext.Normal); - - RestoreHeadBob(); - - if (CrosshairController.Instance != null) - { - CrosshairController.Instance.Show(); - } - } - - private void RestoreHeadBob() - { - if (_cameraHeadBob == null) - return; - - _cameraHeadBob.ClearOverride(); - } - - private void OnDrawGizmosSelected() - { - if (DetectiveBoardCanvas.Instance == null) - return; - - Gizmos.color = _isBoardActive ? Color.green : Color.yellow; - Gizmos.DrawWireSphere(DetectiveBoardCanvas.Instance.transform.position, _activationRange); - } - } -} diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/DetectiveBoardController.cs.meta b/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/DetectiveBoardController.cs.meta deleted file mode 100644 index 3f528b28..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/DetectiveBoardController.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 9f9f72364623a2a4bb50f02537fd2e72 \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/DetectiveBoardManager.cs b/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/DetectiveBoardManager.cs deleted file mode 100644 index a6dc8900..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/DetectiveBoardManager.cs +++ /dev/null @@ -1,191 +0,0 @@ -using System.Collections.Generic; -using UnityEngine; -using Unheimlich.Core; -using Unheimlich.Features.DetectiveBoard.Data; - -namespace Unheimlich.Features.DetectiveBoard -{ - /// - /// Manages the detective board state and clue unlocking system. - /// Tracks unlocked clues, connections, certainty levels, and eliminated hypotheses. - /// Does NOT affect game progression - purely interpretive tool for the player. - /// - public class DetectiveBoardManager : Singleton - { - public enum CertaintyLevel - { - Uncertain, - Possible, - Confident, - Certain - } - - public class ClueState - { - public ClueData Data; - public bool IsUnlocked; - public bool IsEliminated; - } - - public class Connection - { - public string FromClueID; - public string ToClueID; - public CertaintyLevel Certainty; - public bool IsEliminated; - } - - [Header("References")] - - private Dictionary _clueStates = new Dictionary(); - private List _connections = new List(); - - public IReadOnlyDictionary ClueStates => _clueStates; - public IReadOnlyList Connections => _connections; - - /// - /// Unlocks a clue on the detective board. Can be triggered by any in-game event. - /// - public void UnlockClue(ClueData clueData) - { - if (clueData == null) - { - Debug.LogError("[DetectiveBoardManager] Attempted to unlock null clue!"); - return; - } - - if (!_clueStates.ContainsKey(clueData.ClueID)) - { - _clueStates[clueData.ClueID] = new ClueState - { - Data = clueData, - IsUnlocked = true, - IsEliminated = false - }; - - OnClueUnlocked?.Invoke(clueData); - } - else if (!_clueStates[clueData.ClueID].IsUnlocked) - { - _clueStates[clueData.ClueID].IsUnlocked = true; - OnClueUnlocked?.Invoke(clueData); - } - } - - /// - /// Returns a list of all currently unlocked clues. - /// Used by DetectiveBoardCanvas to display already-unlocked clues on initialization. - /// - public List GetUnlockedClues() - { - List unlockedClues = new List(); - foreach (var state in _clueStates.Values) - { - if (state.IsUnlocked) - { - unlockedClues.Add(state.Data); - } - } - return unlockedClues; - } - - /// - /// Unlocks a clue by its ID string. - /// - public void UnlockClueByID(string clueID) - { - if (_clueStates.TryGetValue(clueID, out ClueState state)) - { - if (!state.IsUnlocked) - { - state.IsUnlocked = true; - OnClueUnlocked?.Invoke(state.Data); - } - } - else - { - Debug.LogWarning($"[DetectiveBoardManager] Clue ID '{clueID}' not found!"); - } - } - - /// - /// Registers a clue in the system without unlocking it. - /// - public void RegisterClue(ClueData clueData) - { - if (clueData == null || _clueStates.ContainsKey(clueData.ClueID)) - return; - - _clueStates[clueData.ClueID] = new ClueState - { - Data = clueData, - IsUnlocked = false, - IsEliminated = false - }; - } - - /// - /// Creates a connection between two clues. - /// - public void CreateConnection(string fromClueID, string toClueID, CertaintyLevel certainty = CertaintyLevel.Uncertain) - { - if (!_clueStates.ContainsKey(fromClueID) || !_clueStates.ContainsKey(toClueID)) - { - Debug.LogWarning($"[DetectiveBoardManager] Cannot create connection - one or both clues not found!"); - return; - } - - Connection existing = _connections.Find(c => - (c.FromClueID == fromClueID && c.ToClueID == toClueID) || - (c.FromClueID == toClueID && c.ToClueID == fromClueID)); - - if (existing != null) - { - existing.Certainty = certainty; - } - else - { - _connections.Add(new Connection - { - FromClueID = fromClueID, - ToClueID = toClueID, - Certainty = certainty, - IsEliminated = false - }); - - OnConnectionCreated?.Invoke(fromClueID, toClueID); - } - } - - /// - /// Marks a clue as eliminated (incorrect hypothesis). - /// - public void EliminateClue(string clueID) - { - if (_clueStates.TryGetValue(clueID, out ClueState state)) - { - state.IsEliminated = true; - OnClueEliminated?.Invoke(clueID); - } - } - - /// - /// Marks a connection as eliminated. - /// - public void EliminateConnection(string fromClueID, string toClueID) - { - Connection connection = _connections.Find(c => - (c.FromClueID == fromClueID && c.ToClueID == toClueID) || - (c.FromClueID == toClueID && c.ToClueID == fromClueID)); - - if (connection != null) - { - connection.IsEliminated = true; - } - } - - public event System.Action OnClueUnlocked; - public event System.Action OnConnectionCreated; - public event System.Action OnClueEliminated; - } -} diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/DetectiveBoardManager.cs.meta b/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/DetectiveBoardManager.cs.meta deleted file mode 100644 index d557c6d1..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/Runtime/DetectiveBoardManager.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 4b187cdccaa498441a752e8f49ff4290 \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/UI.meta b/Assets/_Content/Scripts/Features/DetectiveBoard/UI.meta deleted file mode 100644 index 44d1ccf4..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/UI.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 466fa804543e82c4483a22466a9bb484 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/ClueHandCard.cs b/Assets/_Content/Scripts/Features/DetectiveBoard/UI/ClueHandCard.cs deleted file mode 100644 index 1d894607..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/ClueHandCard.cs +++ /dev/null @@ -1,122 +0,0 @@ -using UnityEngine; -using UnityEngine.EventSystems; -using Unheimlich.Features.DetectiveBoard.Data; - -namespace Unheimlich.Features.DetectiveBoard.UI -{ - /// - /// Wraps a PostItNode to behave as a card in the bottom-screen fan hand. - /// Handles hover pop animation and delegates click to ClueHandUI for board placement. - /// Add this component alongside PostItNode on the same GameObject. - /// - [RequireComponent(typeof(PostItNode))] - public class ClueHandCard : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerClickHandler - { - [Header("Hover Settings")] - [SerializeField] private float _hoverRiseAmount = 60f; - [SerializeField] private float _hoverAnimSpeed = 8f; - - private PostItNode _node; - private ClueHandUI _handUI; - - // Fan resting state assigned by ClueHandUI - private Vector2 _restAnchoredPosition; - private float _restRotation; - - // Runtime animation state - private Vector2 _currentAnchoredPosition; - private float _currentRotation; - private bool _isHovered; - private bool _isPlaced; - - public ClueData ClueData => _node != null ? _node.ClueData : null; - public PostItNode Node => _node; - public bool IsPlaced => _isPlaced; - - private void Awake() - { - if (_node == null) - _node = GetComponent(); - } - - /// - /// Binds this card to its parent hand manager. - /// - public void Initialize(PostItNode node, ClueHandUI handUI) - { - _node = node; - _handUI = handUI; - } - - /// - /// Assigns the resting fan slot (position + rotation) for this card. - /// - public void SetFanSlot(Vector2 anchoredPosition, float rotationZ) - { - _restAnchoredPosition = anchoredPosition; - _restRotation = rotationZ; - _currentAnchoredPosition = anchoredPosition; - _currentRotation = rotationZ; - - RectTransform rt = _node.RectTransform; - rt.anchoredPosition = anchoredPosition; - rt.localRotation = Quaternion.Euler(0f, 0f, rotationZ); - } - - private void Update() - { - if (_isPlaced) - return; - - RectTransform rt = _node.RectTransform; - - Vector2 targetPos = _isHovered - ? _restAnchoredPosition + Vector2.up * _hoverRiseAmount - : _restAnchoredPosition; - - float targetRot = _isHovered ? 0f : _restRotation; - - _currentAnchoredPosition = Vector2.Lerp(_currentAnchoredPosition, targetPos, Time.deltaTime * _hoverAnimSpeed); - _currentRotation = Mathf.LerpAngle(_currentRotation, targetRot, Time.deltaTime * _hoverAnimSpeed); - - rt.anchoredPosition = _currentAnchoredPosition; - rt.localRotation = Quaternion.Euler(0f, 0f, _currentRotation); - } - - public void OnPointerEnter(PointerEventData eventData) - { - if (_isPlaced) - return; - - _isHovered = true; - _handUI?.OnCardHovered(this); - } - - public void OnPointerExit(PointerEventData eventData) - { - if (_isPlaced) - return; - - _isHovered = false; - _handUI?.OnCardUnhovered(this); - } - - public void OnPointerClick(PointerEventData eventData) - { - if (_isPlaced || eventData.button != PointerEventData.InputButton.Left) - return; - - _isHovered = false; - _handUI?.OnCardSelected(this); - } - - /// - /// Hides this card from the hand after placement on the board. - /// - public void MarkAsPlaced() - { - _isPlaced = true; - gameObject.SetActive(false); - } - } -} diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/ClueHandCard.cs.meta b/Assets/_Content/Scripts/Features/DetectiveBoard/UI/ClueHandCard.cs.meta deleted file mode 100644 index d7fed6e7..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/ClueHandCard.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: dc7ce79e8bf16554fae5362980fec5b7 \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/ClueHandUI.cs b/Assets/_Content/Scripts/Features/DetectiveBoard/UI/ClueHandUI.cs deleted file mode 100644 index fc71ad09..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/ClueHandUI.cs +++ /dev/null @@ -1,252 +0,0 @@ -using UnityEngine; -using System.Collections.Generic; -using Unheimlich.Core; -using Unheimlich.Features.DetectiveBoard.Data; - -namespace Unheimlich.Features.DetectiveBoard.UI -{ - /// - /// Manages the clue hand fan displayed at the bottom of the screen (Screen Space canvas). - /// Reuses the PostItNode prefab for each card — ClueHandCard is added at runtime. - /// Hovering a card pops it up and straightens it; clicking starts a lerp-depth - /// placement animation then hands the clue to DetectiveBoardCanvas. - /// - public class ClueHandUI : Singleton - { - [Header("References")] - [SerializeField] private GameObject _postItNodePrefab; - [SerializeField] private RectTransform _handContainer; - - [Header("Fan Layout Settings")] - [SerializeField] private float _cardSpacingPixels = 80f; - [SerializeField] private float _fanArcRadius = 800f; - [SerializeField] private float _cardBaseY = -120f; - [SerializeField] private float _maxFanHalfAngle = 40f; - - [Header("Placement Settings")] - // [SerializeField] private float _placementLerpSpeed = 5f; - - private readonly List _handCards = new List(); - private bool _isPlacing; protected override void OnAwake() { } - - protected override void OnEnabled() - { - // With Scene Reload disabled, stale card references from the previous session - // may point to destroyed GameObjects. Clear and rebuild from manager state. - if (_handCards.Count > 0) - { - foreach (ClueHandCard card in _handCards) - { - if (card != null && card.gameObject != null) - Destroy(card.gameObject); - } - _handCards.Clear(); - } - - if (DetectiveBoardManager.Instance != null) - { - foreach (var clue in DetectiveBoardManager.Instance.GetUnlockedClues()) - { - // Skip clues already placed on the board — they must not reappear in the hand - if (DetectiveBoardCanvas.Instance != null && DetectiveBoardCanvas.Instance.HasNodeForClue(clue.ClueID)) - continue; - - AddClueCard(clue); - } - } - } - - private void Start() - { - if (DetectiveBoardManager.Instance != null) - { - DetectiveBoardManager.Instance.OnClueUnlocked += OnClueUnlocked; - - foreach (var clue in DetectiveBoardManager.Instance.GetUnlockedClues()) - AddClueCard(clue); - } - - // Start hidden — shown when the board activates - SetHandVisible(false); - } - - private void OnDestroy() - { - if (DetectiveBoardManager.Instance != null) - DetectiveBoardManager.Instance.OnClueUnlocked -= OnClueUnlocked; - } - - private void OnClueUnlocked(ClueData clueData) - { - AddClueCard(clueData); - } - - // ────────────────────────────────────────────────────────────────────── - // Card management - // ────────────────────────────────────────────────────────────────────── - - /// - /// Instantiates a PostItNode prefab, injects ClueHandCard, and adds it to the fan. - /// - public void AddClueCard(ClueData clueData) - { - if (_postItNodePrefab == null || _handContainer == null) - { - Debug.LogError("[ClueHandUI] Missing _postItNodePrefab or _handContainer reference."); - return; - } - - if (_handCards.Exists(c => c.ClueData != null && c.ClueData.ClueID == clueData.ClueID)) - return; - - // Instantiate the PostItNode prefab inside the screen-space hand container - GameObject cardObj = Instantiate(_postItNodePrefab, _handContainer); - - // PostItNode is nested one level inside "Canvas (Environment)" wrapper in the prefab - // — grab it directly - PostItNode node = cardObj.GetComponentInChildren(true); - if (node == null) - { - Debug.LogError("[ClueHandUI] PostItNode not found on prefab."); - Destroy(cardObj); - return; - } - - // Create a dummy NodeData for the hand display (position handled by fan layout) - PostItNodeData dummyData = new PostItNodeData(clueData.ClueID, Vector2.zero); - node.Initialize(clueData, dummyData); - - // Add ClueHandCard wrapper on the PostItNode GameObject - ClueHandCard card = node.gameObject.AddComponent(); - card.Initialize(node, this); - - // Move the PostItNode GameObject up to be a direct child of _handContainer - // so its RectTransform is in screen space - node.transform.SetParent(_handContainer, false); - - // Destroy the now-empty prefab root (the "Canvas (Environment)" wrapper) - if (cardObj != node.gameObject) - Destroy(cardObj); - - // Normalize scale — PostItNode prefab uses 0.5 scale for world space, reset for screen space - node.RectTransform.localScale = Vector3.one; - - _handCards.Add(card); - RebuildFanLayout(); - } - - /// - /// Recomputes fan arc positions and rotations for all non-placed cards. - /// Uses fixed pixel spacing between cards, capped by a max half-angle. - /// - private void RebuildFanLayout() - { - List visible = _handCards.FindAll(c => !c.IsPlaced); - int count = visible.Count; - - if (count == 0) - return; - - // Derive the angle step from pixel spacing on the arc - float angleStepDeg = (_cardSpacingPixels / _fanArcRadius) * Mathf.Rad2Deg; - - // Total spread is (count-1) steps, capped so it never exceeds _maxFanHalfAngle on each side - float totalHalfAngle = angleStepDeg * (count - 1) * 0.5f; - float clampedHalfAngle = Mathf.Min(totalHalfAngle, _maxFanHalfAngle); - - for (int i = 0; i < count; i++) - { - float t = count == 1 ? 0.5f : (float)i / (count - 1); - float angle = Mathf.Lerp(-clampedHalfAngle, clampedHalfAngle, t); - float angleRad = angle * Mathf.Deg2Rad; - - float x = _fanArcRadius * Mathf.Sin(angleRad); - float y = _cardBaseY - _fanArcRadius * (1f - Mathf.Cos(angleRad)); - - visible[i].SetFanSlot(new Vector2(x, y), -angle); - } - } - - // ────────────────────────────────────────────────────────────────────── - // Hover callbacks - // ────────────────────────────────────────────────────────────────────── - - /// Brings the hovered card in front of its siblings. - public void OnCardHovered(ClueHandCard card) - { - card.transform.SetAsLastSibling(); - } - - /// Restores natural sibling order after hover ends. - public void OnCardUnhovered(ClueHandCard card) - { - RebuildSiblingOrder(); - } - - private void RebuildSiblingOrder() - { - int index = 0; - foreach (ClueHandCard c in _handCards) - { - if (!c.IsPlaced) - c.transform.SetSiblingIndex(index++); - } - } - - // ────────────────────────────────────────────────────────────────────── - // Selection & placement - // ────────────────────────────────────────────────────────────────────── - - /// - /// Called when the player clicks a card. Creates the node on the board - /// directly under the mouse cursor and immediately starts drag mode. - /// - public void OnCardSelected(ClueHandCard card) - { - if (_isPlacing) - return; - - DetectiveBoardCanvas boardCanvas = DetectiveBoardCanvas.Instance; - if (boardCanvas == null) - { - Debug.LogWarning("[ClueHandUI] DetectiveBoardCanvas not found."); - return; - } - - DetectiveBoardInteraction interaction = DetectiveBoardInteraction.Instance; - if (interaction == null) - { - Debug.LogWarning("[ClueHandUI] DetectiveBoardInteraction not found."); - return; - } - - boardCanvas.AddClueNode(card.ClueData); - - PostItNode boardNode = boardCanvas.GetNodeForClue(card.ClueData.ClueID); - if (boardNode == null) - { - Debug.LogWarning("[ClueHandUI] Node not found on board after AddClueNode."); - return; - } - - // Position node under the mouse on the board canvas plane, then start drag - interaction.SpawnNodeUnderMouseAndDrag(boardNode); - - card.MarkAsPlaced(); - RebuildFanLayout(); - } - - // ────────────────────────────────────────────────────────────────────── - // Visibility - // ────────────────────────────────────────────────────────────────────── - - /// - /// Shows or hides the fan hand. Called by DetectiveBoardController. - /// - public void SetHandVisible(bool visible) - { - if (_handContainer != null) - _handContainer.gameObject.SetActive(visible); - } - } -} diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/ClueHandUI.cs.meta b/Assets/_Content/Scripts/Features/DetectiveBoard/UI/ClueHandUI.cs.meta deleted file mode 100644 index b6f4f247..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/ClueHandUI.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: ace0b6173dad68f47a97505680f54d4e \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/ConnectionLine.cs b/Assets/_Content/Scripts/Features/DetectiveBoard/UI/ConnectionLine.cs deleted file mode 100644 index 3699d976..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/ConnectionLine.cs +++ /dev/null @@ -1,170 +0,0 @@ -using UnityEngine; -using UnityEngine.UI; - -namespace Unheimlich.Features.DetectiveBoard.UI -{ - public enum LineColorState - { - Green = 0, - Orange = 1, - Red = 2 - } - - [RequireComponent(typeof(RectTransform))] - public class ConnectionLine : MonoBehaviour - { - [Header("References")] - [SerializeField] private Image _lineImage; - [SerializeField] private Image _outlineImage; - [SerializeField] private RectTransform _rectTransform; - - private RectTransform _lineRectTransform; - private RectTransform _outlineRectTransform; - - [Header("Settings")] - [SerializeField] private float _lineThickness = 3f; - [SerializeField] private Color _outlineColor = Color.yellow; - - [Header("Color States")] - [SerializeField] private Color _greenColor = Color.green; - [SerializeField] private Color _orangeColor = new Color(1f, 0.5f, 0f); - [SerializeField] private Color _redColor = Color.red; - - private PostItNode _startNode; - private PostItNode _endNode; - private LineColorState _colorState = LineColorState.Green; - private bool _isHovered; - - public PostItNode StartNode => _startNode; - public PostItNode EndNode => _endNode; - public LineColorState ColorState => _colorState; - - private void Awake() - { - if (_rectTransform == null) - _rectTransform = GetComponent(); - - if (_lineImage == null) - _lineImage = GetComponentInChildren(); - - if (_lineImage != null) - { - _lineRectTransform = _lineImage.GetComponent(); - _lineImage.raycastTarget = true; - } - - if (_rectTransform != null) - { - _rectTransform.anchorMin = new Vector2(0.5f, 0.5f); - _rectTransform.anchorMax = new Vector2(0.5f, 0.5f); - _rectTransform.pivot = new Vector2(0f, 0.5f); - } - - if (_lineRectTransform != null) - { - _lineRectTransform.anchorMin = new Vector2(0f, 0.5f); - _lineRectTransform.anchorMax = new Vector2(0f, 0.5f); - _lineRectTransform.pivot = new Vector2(0f, 0.5f); - } - - if (_outlineImage == null) - { - _outlineImage = transform.Find("Outline")?.GetComponent(); - } - - if (_outlineImage != null) - { - _outlineRectTransform = _outlineImage.GetComponent(); - if (_outlineRectTransform != null) - { - _outlineRectTransform.anchorMin = new Vector2(0f, 0.5f); - _outlineRectTransform.anchorMax = new Vector2(0f, 0.5f); - _outlineRectTransform.pivot = new Vector2(0f, 0.5f); - } - _outlineImage.raycastTarget = false; - _outlineImage.gameObject.SetActive(false); - } - } - - public void Initialize(PostItNode startNode, PostItNode endNode) - { - _startNode = startNode; - _endNode = endNode; - _colorState = LineColorState.Green; - - UpdateColorState(); - UpdateLine(); - } - - public void UpdateLine() - { - if (_startNode == null || _endNode == null || _rectTransform == null) - return; - - Vector2 startPos = _startNode.ConnectionPoint.anchoredPosition; - Vector2 endPos = _endNode.ConnectionPoint.anchoredPosition; - - Vector2 direction = endPos - startPos; - float distance = direction.magnitude; - - _rectTransform.anchoredPosition = startPos; - - float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg; - _rectTransform.localRotation = Quaternion.Euler(0, 0, angle); - - if (_lineRectTransform != null) - { - _lineRectTransform.sizeDelta = new Vector2(distance, _lineThickness); - } - - if (_outlineRectTransform != null) - { - _outlineRectTransform.sizeDelta = new Vector2(distance, _lineThickness + 3f); - } - } - - public void CycleColorState() - { - _colorState = (LineColorState)(((int)_colorState + 1) % 3); - UpdateColorState(); - } - - public void SetColorState(LineColorState state) - { - _colorState = state; - UpdateColorState(); - } - - private void UpdateColorState() - { - if (_lineImage == null) - return; - - Color targetColor = _colorState switch - { - LineColorState.Green => _greenColor, - LineColorState.Orange => _orangeColor, - LineColorState.Red => _redColor, - _ => _greenColor - }; - - _lineImage.color = targetColor; - } - - public void SetHovered(bool hovered) - { - _isHovered = hovered; - - if (_outlineImage != null) - { - _outlineImage.gameObject.SetActive(hovered); - } - } - - public bool ConnectsNodes(PostItNode nodeA, PostItNode nodeB) - { - return (_startNode == nodeA && _endNode == nodeB) || - (_startNode == nodeB && _endNode == nodeA); - } - } -} diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/ConnectionLine.cs.meta b/Assets/_Content/Scripts/Features/DetectiveBoard/UI/ConnectionLine.cs.meta deleted file mode 100644 index e382973c..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/ConnectionLine.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: d38fc1a64b1949443b49ec6506174085 \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/ConnectionLineRenderer.cs b/Assets/_Content/Scripts/Features/DetectiveBoard/UI/ConnectionLineRenderer.cs deleted file mode 100644 index b5373c27..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/ConnectionLineRenderer.cs +++ /dev/null @@ -1,90 +0,0 @@ -using UnityEngine; -using Unheimlich.Features.DetectiveBoard; - -namespace Unheimlich.Features.DetectiveBoard.UI -{ - /// - /// Renders a connection line between two clue nodes on the detective board. - /// Visual style changes based on certainty level and elimination state. - /// - [RequireComponent(typeof(LineRenderer))] - public class ConnectionLineRenderer : MonoBehaviour - { - private LineRenderer _lineRenderer; - private string _fromClueID; - private string _toClueID; - private DetectiveBoardManager.CertaintyLevel _certainty; - private bool _isEliminated; - - public string FromClueID => _fromClueID; - public string ToClueID => _toClueID; - - private void Awake() - { - _lineRenderer = GetComponent(); - } - - /// - /// Initializes the connection line between two nodes. - /// - public void Initialize(string fromClueID, string toClueID, - DetectiveBoardManager.CertaintyLevel certainty, Color color, float width) - { - _fromClueID = fromClueID; - _toClueID = toClueID; - _certainty = certainty; - - if (_lineRenderer != null) - { - _lineRenderer.startWidth = width; - _lineRenderer.endWidth = width; - _lineRenderer.material = new Material(Shader.Find("Sprites/Default")); - _lineRenderer.startColor = color; - _lineRenderer.endColor = color; - } - } - - /// - /// Updates the line positions based on node transforms. - /// - public void UpdateLine(Vector3 startPos, Vector3 endPos) - { - if (_lineRenderer != null) - { - _lineRenderer.SetPosition(0, startPos); - _lineRenderer.SetPosition(1, endPos); - } - } - - /// - /// Updates the certainty level and visual appearance. - /// - public void SetCertainty(DetectiveBoardManager.CertaintyLevel certainty, Color color) - { - _certainty = certainty; - - if (_lineRenderer != null) - { - _lineRenderer.startColor = color; - _lineRenderer.endColor = color; - } - } - - /// - /// Marks this connection as eliminated. - /// - public void SetEliminated(bool eliminated) - { - _isEliminated = eliminated; - - if (_lineRenderer != null) - { - float alpha = eliminated ? 0.3f : 1f; - Color color = _lineRenderer.startColor; - color.a = alpha; - _lineRenderer.startColor = color; - _lineRenderer.endColor = color; - } - } - } -} diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/ConnectionLineRenderer.cs.meta b/Assets/_Content/Scripts/Features/DetectiveBoard/UI/ConnectionLineRenderer.cs.meta deleted file mode 100644 index 77229511..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/ConnectionLineRenderer.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 8d829d9811842b241b980bd64ab73bff \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/DetectiveBoardCanvas.cs b/Assets/_Content/Scripts/Features/DetectiveBoard/UI/DetectiveBoardCanvas.cs deleted file mode 100644 index fb3ab306..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/DetectiveBoardCanvas.cs +++ /dev/null @@ -1,422 +0,0 @@ -using UnityEngine; -using UnityEngine.InputSystem; -using System.Collections.Generic; -using System.Linq; -using Unheimlich.Features.DetectiveBoard.Data; -using Unheimlich.Features.DetectiveBoard; -using Unheimlich.Core; -using Unheimlich.Features.Player; - -namespace Unheimlich.Features.DetectiveBoard.UI -{ - public class DetectiveBoardCanvas : Singleton - { - [Header("Prefabs")] - [SerializeField] private GameObject _postItNodePrefab; - [SerializeField] private GameObject _connectionLinePrefab; - - [Header("Layout References")] - [SerializeField] private RectTransform _boardArea; - [SerializeField] private Renderer _boardRenderer; - - [Header("Auto-Placement Settings")] - [SerializeField] private float _leftMargin = 50f; - [SerializeField] private float _topMargin = 50f; - [SerializeField] private float _nodeSpacing = 20f; - [SerializeField] private float _randomRotationRange = 10f; - - [Header("Input")] - [SerializeField] private Key _connectModeKey = Key.LeftCtrl; - - private Canvas _canvas; - private Camera _targetCamera; - private Dictionary _activeNodes = new Dictionary(); - private List _activeConnections = new List(); - private PostItNode _selectedNode; - private ConnectionLine _hoveredLine; - private bool _isConnectMode; - private DetectiveBoardSaveData _saveData; - private float _currentColumnYOffset = 0f; - - public bool IsConnectMode => _isConnectMode; - public RectTransform BoardArea => _boardArea; - public Renderer BoardRenderer => _boardRenderer; - - public void OnLineClicked(ConnectionLine line) - { - if (line != null) - { - line.CycleColorState(); - SaveConnectionColorState(line); - } - } - - private void SaveConnectionColorState(ConnectionLine line) - { - if (line.StartNode == null || line.EndNode == null) - return; - - ConnectionData startNodeConnection = line.StartNode.NodeData.Connections.Find(c => c.TargetNodeID == line.EndNode.NodeID); - if (startNodeConnection != null) - { - startNodeConnection.ColorState = line.ColorState; - } - - ConnectionData endNodeConnection = line.EndNode.NodeData.Connections.Find(c => c.TargetNodeID == line.StartNode.NodeID); - if (endNodeConnection != null) - { - endNodeConnection.ColorState = line.ColorState; - } - } - - public void SetHoveredLine(ConnectionLine line) - { - if (_hoveredLine == line) - return; - - if (_hoveredLine != null) - { - _hoveredLine.SetHovered(false); - } - - _hoveredLine = line; - - if (_hoveredLine != null) - { - _hoveredLine.SetHovered(true); - } - } - - /// - /// Called by DetectiveBoardInteraction when a node is left-clicked. - /// Handles connection creation in connect mode, or selection in normal mode. - /// - public void OnNodeClicked(PostItNode node) - { - if (_isConnectMode) - { - HandleConnectionModeClick(node); - } - else - { - _selectedNode = node; - UpdateAllNodeSelections(); - } - } - - /// - /// Called by DetectiveBoardInteraction when a node is right-clicked. - /// Toggles the eliminated state of the node. - /// - public void OnNodeRightClicked(PostItNode node) - { - ToggleNodeEliminated(node); - } - - protected override void OnAwake() - { - _canvas = GetComponent(); - - if (_canvas != null && _canvas.renderMode == RenderMode.WorldSpace) - { - if (PlayerController.Instance != null) - _targetCamera = PlayerController.Instance.PlayerCamera; - - if (_targetCamera == null) - _targetCamera = Camera.main; - - _canvas.worldCamera = _targetCamera; - } - - _saveData = new DetectiveBoardSaveData(); - } - - private void OnEnable() { } - - private void Start() { } - - private void OnDisable() { } - - private void Update() - { - HandleConnectModeInput(); - } - - private void HandleConnectModeInput() - { - if (Keyboard.current == null) - return; - - bool wasConnectMode = _isConnectMode; - _isConnectMode = Keyboard.current[_connectModeKey].isPressed; - - if (wasConnectMode && !_isConnectMode) - { - _selectedNode = null; - UpdateAllNodeSelections(); - } - } - - public void AddClueNode(ClueData clueData) - { - if (_activeNodes.ContainsKey(clueData.ClueID)) - return; - - if (_postItNodePrefab == null) - { - Debug.LogError("[DetectiveBoardCanvas] _postItNodePrefab is null! Assign the prefab in the inspector."); - return; - } - - if (_boardArea == null) - { - Debug.LogError("[DetectiveBoardCanvas] _boardArea is null! Assign NodesContainer in the inspector."); - return; - } - - PostItNodeData nodeData = _saveData.GetNodeData(clueData.ClueID); - if (nodeData == null) - { - Vector2 autoPlacedPos = CalculateAutoPlacementPosition(); - nodeData = new PostItNodeData(clueData.ClueID, autoPlacedPos); - _saveData.AddOrUpdateNode(nodeData); - } - - GameObject nodeObj = Instantiate(_postItNodePrefab, _boardArea); - - PostItNode node = nodeObj.GetComponent(); - - if (node == null) - { - Debug.LogError("[DetectiveBoardCanvas] PostItNode component not found on prefab"); - Destroy(nodeObj); - return; - } - - node.Initialize(clueData, nodeData); - - float savedRotation = nodeData.Rotation; - if (savedRotation == 0f) - { - savedRotation = Random.Range(-_randomRotationRange, _randomRotationRange); - nodeData.Rotation = savedRotation; - } - node.SetRotation(savedRotation); - - _activeNodes.Add(clueData.ClueID, node); - - RestoreConnectionsForNode(node); - } - - private Vector2 CalculateAutoPlacementPosition() - { - Vector2 canvasSize = _boardArea.rect.size; - - float startX = -canvasSize.x * 0.5f + _leftMargin; - float startY = canvasSize.y * 0.5f - _topMargin; - - float estimatedNodeHeight = 150f; - - Vector2 position = new Vector2(startX, startY - _currentColumnYOffset); - - _currentColumnYOffset += estimatedNodeHeight + _nodeSpacing; - - if (_currentColumnYOffset > canvasSize.y - _topMargin * 2) - { - _currentColumnYOffset = 0f; - } - - return position; - } - - public void RemoveClueNode(string clueID) - { - if (!_activeNodes.TryGetValue(clueID, out PostItNode node)) - return; - - RemoveConnectionsForNode(node); - - _activeNodes.Remove(clueID); - Destroy(node.gameObject); - } - - private void HandleConnectionModeClick(PostItNode clickedNode) - { - if (_selectedNode == null) - { - _selectedNode = clickedNode; - UpdateAllNodeSelections(); - } - else if (_selectedNode == clickedNode) - { - _selectedNode = null; - UpdateAllNodeSelections(); - } - else - { - ToggleConnection(_selectedNode, clickedNode); - _selectedNode = null; - UpdateAllNodeSelections(); - } - } - - private void ToggleConnection(PostItNode nodeA, PostItNode nodeB) - { - ConnectionLine existingConnection = _activeConnections.Find(c => c.ConnectsNodes(nodeA, nodeB)); - - if (existingConnection != null) - { - RemoveConnection(existingConnection); - } - else - { - CreateConnection(nodeA, nodeB); - } - } - - private void CreateConnection(PostItNode startNode, PostItNode endNode) - { - GameObject lineObj = Instantiate(_connectionLinePrefab, _boardArea); - lineObj.transform.SetAsFirstSibling(); - - ConnectionLine line = lineObj.GetComponent(); - if (line == null) - { - Debug.LogError("[DetectiveBoardCanvas] ConnectionLine component not found on prefab"); - Destroy(lineObj); - return; - } - - line.Initialize(startNode, endNode); - - ConnectionData existingConnectionData = startNode.NodeData.Connections.Find(c => c.TargetNodeID == endNode.NodeID); - if (existingConnectionData != null) - { - line.SetColorState(existingConnectionData.ColorState); - } - - _activeConnections.Add(line); - - if (existingConnectionData == null) - { - startNode.NodeData.Connections.Add(new ConnectionData(endNode.NodeID)); - } - - ConnectionData reverseConnectionData = endNode.NodeData.Connections.Find(c => c.TargetNodeID == startNode.NodeID); - if (reverseConnectionData == null) - { - endNode.NodeData.Connections.Add(new ConnectionData(startNode.NodeID)); - } - } - - private void RemoveConnection(ConnectionLine connection) - { - if (connection.StartNode != null && connection.EndNode != null) - { - connection.StartNode.NodeData.Connections.RemoveAll(c => c.TargetNodeID == connection.EndNode.NodeID); - connection.EndNode.NodeData.Connections.RemoveAll(c => c.TargetNodeID == connection.StartNode.NodeID); - } - - _activeConnections.Remove(connection); - Destroy(connection.gameObject); - } - - private void RemoveConnectionsForNode(PostItNode node) - { - var connectionsToRemove = _activeConnections - .Where(c => c.StartNode == node || c.EndNode == node) - .ToList(); - - foreach (var connection in connectionsToRemove) - { - RemoveConnection(connection); - } - } - - /// - /// Updates all connection lines attached to the specified node. - /// Called during node dragging to keep connections visually updated. - /// - public void UpdateConnectionsForNode(PostItNode node) - { - foreach (var connection in _activeConnections) - { - if (connection.StartNode == node || connection.EndNode == node) - { - connection.UpdateLine(); - } - } - } - - private void RestoreConnectionsForNode(PostItNode node) - { - if (node.NodeData.Connections == null || node.NodeData.Connections.Count == 0) - return; - - foreach (ConnectionData connectionData in node.NodeData.Connections) - { - if (_activeNodes.TryGetValue(connectionData.TargetNodeID, out PostItNode otherNode)) - { - bool connectionExists = _activeConnections.Any(c => c.ConnectsNodes(node, otherNode)); - if (!connectionExists) - { - CreateConnection(node, otherNode); - } - } - } - } - - private void ToggleNodeEliminated(PostItNode node) - { - node.SetEliminated(!node.IsEliminated); - } - - private void UpdateAllNodeSelections() - { - foreach (var node in _activeNodes.Values) - { - node.SetSelected(node == _selectedNode); - } - } - - /// - /// Returns the active PostItNode for the given clue ID, or null if not found. - /// Used by ClueHandUI to retrieve the node after placement. - /// - /// Returns true if a node for this clue ID is already placed on the board. - public bool HasNodeForClue(string clueID) => _activeNodes.ContainsKey(clueID); - - public PostItNode GetNodeForClue(string clueID) - { - _activeNodes.TryGetValue(clueID, out PostItNode node); - return node; - } - - public DetectiveBoardSaveData GetSaveData() - { - return _saveData; - } - - public void LoadSaveData(DetectiveBoardSaveData saveData) - { - _saveData = saveData; - - foreach (var node in _activeNodes.Values) - { - PostItNodeData nodeData = _saveData.GetNodeData(node.NodeID); - if (nodeData != null) - { - node.SetPosition(nodeData.Position); - node.SetRotation(nodeData.Rotation); - node.SetEliminated(nodeData.IsEliminated); - } - } - - _activeConnections.Clear(); - foreach (var node in _activeNodes.Values) - { - RestoreConnectionsForNode(node); - } - } - } -} diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/DetectiveBoardCanvas.cs.meta b/Assets/_Content/Scripts/Features/DetectiveBoard/UI/DetectiveBoardCanvas.cs.meta deleted file mode 100644 index 327abdba..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/DetectiveBoardCanvas.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: edfcd01879d2cb34495302ef9882965b \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/DetectiveBoardInteraction.cs b/Assets/_Content/Scripts/Features/DetectiveBoard/UI/DetectiveBoardInteraction.cs deleted file mode 100644 index 710837d7..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/DetectiveBoardInteraction.cs +++ /dev/null @@ -1,430 +0,0 @@ -using UnityEngine; -using UnityEngine.InputSystem; -using UnityEngine.UI; -using Unheimlich.Core; -using Unheimlich.Features.DetectiveBoard; -using Unheimlich.Features.Player; -using Unheimlich.World.CameraSystem; -using System.Collections.Generic; -using UnityEngine.EventSystems; - -namespace Unheimlich.Features.DetectiveBoard.UI -{ - /// - /// Handles mouse interaction for the detective board World Space canvas. - /// Manages hover, selection, dragging, and click forwarding for Post-It nodes. - /// Interactions are only active when the board is in inspection mode. - /// - public class DetectiveBoardInteraction : Singleton - { - [Header("Settings")] - [SerializeField] private LayerMask _boardLayer; - [SerializeField] private float _minInteractionDistance = 0.1f; - [SerializeField] private float _maxInteractionDistance = 10f; - - private Camera _camera; - private Canvas _boardCanvas; - private RectTransform _boardArea; - private DetectiveBoardController _boardController; - private DetectiveBoardCanvas _canvasManager; - private PostItNode _hoveredNode; - private PostItNode _selectedNode; - private ConnectionLine _hoveredLine; - private bool _isDragging; - private Plane _canvasPlane; - private Vector3 _dragOffset; - - protected override void OnAwake() { } - - private void Start() - { - _boardCanvas = GetComponent(); - _canvasManager = GetComponent(); - _boardController = DetectiveBoardController.Instance; - - if (_canvasManager != null && _canvasManager.BoardArea != null) - _boardArea = _canvasManager.BoardArea; - - if (PlayerController.Instance != null) - _camera = PlayerController.Instance.PlayerCamera; - - if (_camera == null) - _camera = Camera.main; - - UpdateCanvasPlane(); - } - - private void Update() - { - if (!IsInBoardInteractionMode()) - { - ClearInteractionState(); - return; - } - - UpdateCameraReference(); - UpdateCanvasPlane(); - - if (_isDragging) - { - HandleDrag(); - } - else - { - HandleHover(); - HandleLineHover(); - } - - HandleClick(); - } - - private void UpdateCameraReference() - { - if (DetectiveBoardVirtualCamera.Instance != null && DetectiveBoardVirtualCamera.Instance.IsActive) - { - _camera = DetectiveBoardVirtualCamera.Instance.VirtualCamera; - } - else if (PlayerController.Instance != null) - { - _camera = PlayerController.Instance.PlayerCamera; - } - else if (_camera == null) - { - _camera = Camera.main; - } - } - - private bool IsInBoardInteractionMode() - { - bool isActive = _boardController != null && _boardController.IsBoardActive; - - return isActive; - } - - private void ClearInteractionState() - { - if (_hoveredNode != null) - { - _hoveredNode.SetHovered(false); - _hoveredNode = null; - } - - if (_hoveredLine != null && _canvasManager != null) - { - _canvasManager.SetHoveredLine(null); - _hoveredLine = null; - } - - if (_selectedNode != null && _isDragging) - { - _selectedNode.SetSelected(false); - _selectedNode = null; - _isDragging = false; - } - } - - private void UpdateCanvasPlane() - { - if (_boardCanvas != null) - { - Vector3 planeNormal = -_boardCanvas.transform.forward; - Vector3 planePosition = _boardCanvas.transform.position; - _canvasPlane = new Plane(planeNormal, planePosition); - } - } - - private void HandleHover() - { - PostItNode hitNode = RaycastForNode(); - - if (hitNode != _hoveredNode) - { - if (_hoveredNode != null) - { - _hoveredNode.SetHovered(false); - } - - _hoveredNode = hitNode; - - if (_hoveredNode != null) - { - _hoveredNode.SetHovered(true); - } - } - } - - private void HandleLineHover() - { - if (_canvasManager == null || Mouse.current == null) - return; - - if (_hoveredNode != null) - { - if (_hoveredLine != null) - { - _canvasManager.SetHoveredLine(null); - _hoveredLine = null; - } - return; - } - - ConnectionLine hitLine = RaycastForLine(); - - if (hitLine != _hoveredLine) - { - _canvasManager.SetHoveredLine(hitLine); - _hoveredLine = hitLine; - } - } - - private ConnectionLine RaycastForLine() - { - if (_camera == null || Mouse.current == null) - return null; - - Vector2 mousePos = Mouse.current.position.ReadValue(); - - GraphicRaycaster raycaster = _boardCanvas?.GetComponent(); - if (raycaster != null && EventSystem.current != null) - { - var pointerEventData = new PointerEventData(EventSystem.current) - { - position = mousePos - }; - - var results = new List(); - raycaster.Raycast(pointerEventData, results); - - foreach (var result in results) - { - ConnectionLine line = result.gameObject.GetComponentInParent(); - if (line != null) - return line; - } - } - - return null; - } - - private void HandleClick() - { - if (Mouse.current == null) - return; - - PostItNode hitNode = RaycastForNode(); - - if (Mouse.current.leftButton.wasPressedThisFrame) - { - if (hitNode != null) - { - HandleLeftClick(hitNode); - } - else - { - ConnectionLine hitLine = RaycastForLine(); - if (hitLine != null && _canvasManager != null) - { - _canvasManager.OnLineClicked(hitLine); - } - } - } - else if (Mouse.current.rightButton.wasPressedThisFrame && hitNode != null && _canvasManager != null) - { - _canvasManager.OnNodeRightClicked(hitNode); - } - } - - private void HandleLeftClick(PostItNode hitNode) - { - if (_canvasManager != null && _canvasManager.IsConnectMode) - { - if (hitNode != null) - { - _canvasManager.OnNodeClicked(hitNode); - } - return; - } - - if (_isDragging) - { - _isDragging = false; - - if (_selectedNode != null && hitNode == _selectedNode) - { - return; - } - - if (_selectedNode != null) - { - _selectedNode.SetSelected(false); - } - - if (hitNode != null) - { - _selectedNode = hitNode; - _selectedNode.SetSelected(true); - _isDragging = true; - CalculateDragOffset(); - } - else - { - _selectedNode = null; - } - } - else - { - if (hitNode != null) - { - if (_selectedNode != null && _selectedNode != hitNode) - { - _selectedNode.SetSelected(false); - } - - _selectedNode = hitNode; - _selectedNode.SetSelected(true); - _isDragging = true; - CalculateDragOffset(); - } - else - { - if (_selectedNode != null) - { - _selectedNode.SetSelected(false); - _selectedNode = null; - } - } - } - } - - private void HandleDrag() - { - if (_selectedNode == null || _camera == null) - return; - - Vector2 mousePos = Mouse.current.position.ReadValue(); - Ray ray = _camera.ScreenPointToRay(new Vector3(mousePos.x, mousePos.y, 0)); - - if (_canvasPlane.Raycast(ray, out float enter)) - { - Vector3 worldHitPoint = ray.GetPoint(enter); - Vector3 newWorldPos = worldHitPoint + _dragOffset; - - Vector3 localPos = _boardCanvas.transform.InverseTransformPoint(newWorldPos); - Vector2 clampedPos = ClampPositionToBoard(new Vector2(localPos.x, localPos.y)); - - _selectedNode.SetPosition(clampedPos); - - if (_canvasManager != null) - { - _canvasManager.UpdateConnectionsForNode(_selectedNode); - } - } - } - - private Vector2 ClampPositionToBoard(Vector2 position) - { - if (_boardArea == null) - return position; - - Rect boardRect = _boardArea.rect; - float halfWidth = boardRect.width * 0.5f; - float halfHeight = boardRect.height * 0.5f; - - float clampedX = Mathf.Clamp(position.x, -halfWidth, halfWidth); - float clampedY = Mathf.Clamp(position.y, -halfHeight, halfHeight); - - return new Vector2(clampedX, clampedY); - } - - private void CalculateDragOffset() - { - if (_selectedNode == null || _camera == null) - return; - - Vector2 mousePos = Mouse.current.position.ReadValue(); - Ray ray = _camera.ScreenPointToRay(new Vector3(mousePos.x, mousePos.y, 0)); - - if (_canvasPlane.Raycast(ray, out float enter)) - { - Vector3 worldHitPoint = ray.GetPoint(enter); - Vector3 currentWorldPos = _selectedNode.transform.position; - _dragOffset = currentWorldPos - worldHitPoint; - } - } - - /// - /// Positions a freshly-created board node under the current mouse cursor - /// and immediately enters drag mode on it, so the player can place it freely. - /// Called by ClueHandUI after AddClueNode. - /// - public void SpawnNodeUnderMouseAndDrag(PostItNode node) - { - if (node == null || _camera == null) - return; - - UpdateCanvasPlane(); - - Vector2 mousePos = Mouse.current != null - ? Mouse.current.position.ReadValue() - : new Vector2(Screen.width * 0.5f, Screen.height * 0.5f); - - Ray ray = _camera.ScreenPointToRay(new Vector3(mousePos.x, mousePos.y, 0f)); - - if (_canvasPlane.Raycast(ray, out float enter)) - { - Vector3 worldHitPoint = ray.GetPoint(enter); - Vector3 localPos = _boardCanvas.transform.InverseTransformPoint(worldHitPoint); - node.SetPosition(new Vector2(localPos.x, localPos.y)); - } - - // Begin drag with zero offset so the node centre tracks the mouse exactly - if (_selectedNode != null) - _selectedNode.SetSelected(false); - - _selectedNode = node; - _selectedNode.SetSelected(true); - _isDragging = true; - _dragOffset = Vector3.zero; - } - - private PostItNode RaycastForNode() - { - if (_camera == null || Mouse.current == null) - return null; - - Vector2 mousePos = Mouse.current.position.ReadValue(); - - GraphicRaycaster raycaster = _boardCanvas?.GetComponent(); - if (raycaster != null && EventSystem.current != null) - { - var pointerEventData = new PointerEventData(EventSystem.current) - { - position = mousePos - }; - - var results = new List(); - raycaster.Raycast(pointerEventData, results); - - foreach (var result in results) - { - PostItNode node = result.gameObject.GetComponentInParent(); - if (node != null) - return node; - } - } - - Ray ray = _camera.ScreenPointToRay(new Vector3(mousePos.x, mousePos.y, 0)); - - if (Physics.Raycast(ray, out RaycastHit hit, _maxInteractionDistance, _boardLayer)) - { - float distance = hit.distance; - if (distance >= _minInteractionDistance) - { - return hit.collider.GetComponentInParent(); - } - } - - return null; - } - } -} diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/DetectiveBoardInteraction.cs.meta b/Assets/_Content/Scripts/Features/DetectiveBoard/UI/DetectiveBoardInteraction.cs.meta deleted file mode 100644 index 65aca3f6..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/DetectiveBoardInteraction.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 17bf00a333a0bc9409c21009e3ac6aea \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/PostItNode.cs b/Assets/_Content/Scripts/Features/DetectiveBoard/UI/PostItNode.cs deleted file mode 100644 index 7752b5da..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/PostItNode.cs +++ /dev/null @@ -1,132 +0,0 @@ -using UnityEngine; -using UnityEngine.UI; -using TMPro; -using Unheimlich.Features.DetectiveBoard.Data; - -namespace Unheimlich.Features.DetectiveBoard.UI -{ - public class PostItNode : MonoBehaviour - { - [Header("UI References")] - [SerializeField] private RectTransform _rectTransform; - [SerializeField] private Image _background; - [SerializeField] private TextMeshProUGUI _titleText; - [SerializeField] private TextMeshProUGUI _descriptionText; - [SerializeField] private Image _iconImage; - [SerializeField] private Image _selectionOutline; - - [Header("Effects")] - [SerializeField] private PostItScribbleEffect _scribbleEffect; - - [Header("Connection Points")] - [SerializeField] private RectTransform _connectionPoint; - - private ClueData _clueData; - private PostItNodeData _nodeData; - private bool _isEliminated; - private bool _isSelected; - private Color _originalColor; - - public string NodeID => _clueData?.ClueID; - public RectTransform RectTransform => _rectTransform; - public RectTransform ConnectionPoint => _rectTransform; - public ClueData ClueData => _clueData; - public PostItNodeData NodeData => _nodeData; - public bool IsEliminated => _isEliminated; - public bool IsSelected => _isSelected; - - private void Awake() - { - if (_rectTransform == null) - _rectTransform = GetComponent(); - - if (_scribbleEffect == null) - _scribbleEffect = GetComponent(); - - if (_selectionOutline != null) - _selectionOutline.enabled = false; - } - - public void Initialize(ClueData clueData, PostItNodeData nodeData) - { - _clueData = clueData; - _nodeData = nodeData; - - if (_titleText != null) - _titleText.text = clueData.Title; - - if (_descriptionText != null) - _descriptionText.text = clueData.Description; - - if (_iconImage != null && clueData.Icon != null) - { - _iconImage.sprite = clueData.Icon; - _iconImage.enabled = true; - } - else if (_iconImage != null) - { - _iconImage.enabled = false; - } - - if (_background != null) - { - _originalColor = clueData.NodeColor; - _background.color = _originalColor; - } - - SetPosition(nodeData.Position); - SetEliminated(nodeData.IsEliminated); - } - - public void SetPosition(Vector2 position) - { - if (_rectTransform != null) - { - _rectTransform.anchoredPosition = position; - if (_nodeData != null) - _nodeData.Position = position; - } - } - - public void SetRotation(float rotation) - { - if (_rectTransform != null) - { - _rectTransform.localRotation = Quaternion.Euler(0, 0, rotation); - if (_nodeData != null) - _nodeData.Rotation = rotation; - } - } - - public float GetRotation() - { - return _rectTransform != null ? _rectTransform.localEulerAngles.z : 0f; - } - - public void SetEliminated(bool eliminated) - { - _isEliminated = eliminated; - - if (_scribbleEffect != null) - { - _scribbleEffect.SetEliminated(eliminated); - } - - if (_nodeData != null) - _nodeData.IsEliminated = eliminated; - } - - public void SetSelected(bool selected) - { - _isSelected = selected; - } - - public void SetHovered(bool hovered) - { - if (_selectionOutline != null) - { - _selectionOutline.enabled = hovered; - } - } - } -} diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/PostItNode.cs.meta b/Assets/_Content/Scripts/Features/DetectiveBoard/UI/PostItNode.cs.meta deleted file mode 100644 index 29ddb4b3..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/PostItNode.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: a398093f79b17c04e81fe88b509b1319 \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/PostItScribbleEffect.cs b/Assets/_Content/Scripts/Features/DetectiveBoard/UI/PostItScribbleEffect.cs deleted file mode 100644 index 19f83b21..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/PostItScribbleEffect.cs +++ /dev/null @@ -1,135 +0,0 @@ -using UnityEngine; -using UnityEngine.UI; -using Unheimlich.Core.Utils; - -namespace Unheimlich.Features.DetectiveBoard.UI -{ - public class PostItScribbleEffect : MonoBehaviour - { - [SerializeField] private Material _scribbleMaterialTemplate; - [SerializeField] private RectTransform _targetRectTransform; - - private Image _scribbleOverlay; - private Material _scribbleMaterialInstance; - private bool _overlayCreated = false; - private bool _isEliminated = false; - - private static readonly int ProgressID = Shader.PropertyToID("_Progress"); - private int _scribbleTweenId = -1; - - private void Awake() - { - if (_targetRectTransform == null) - _targetRectTransform = GetComponent(); - } - - private void CreateScribbleOverlay() - { - if (_overlayCreated || _scribbleMaterialTemplate == null || _targetRectTransform == null) - return; - - GameObject overlayObject = new GameObject("ScribbleOverlay"); - overlayObject.transform.SetParent(_targetRectTransform, false); - - _scribbleOverlay = overlayObject.AddComponent(); - _scribbleOverlay.raycastTarget = false; - - RectTransform overlayRect = overlayObject.GetComponent(); - - overlayRect.anchorMin = Vector2.zero; - overlayRect.anchorMax = Vector2.one; - overlayRect.sizeDelta = Vector2.zero; - overlayRect.anchoredPosition = Vector2.zero; - - _scribbleMaterialInstance = new Material(_scribbleMaterialTemplate); - _scribbleMaterialInstance.SetFloat(ProgressID, 0f); - _scribbleOverlay.material = _scribbleMaterialInstance; - - _scribbleOverlay.gameObject.SetActive(false); - - _overlayCreated = true; - } - - public void SetEliminated(bool eliminated) - { - if (_isEliminated == eliminated) - return; - - _isEliminated = eliminated; - - if (eliminated) - { - ApplyScribble(); - } - else - { - RemoveScribble(); - } - } - - private void ApplyScribble() - { - CancelActiveTweens(); - CreateScribbleOverlay(); - - if (_scribbleOverlay == null || _scribbleMaterialInstance == null) - return; - - _scribbleOverlay.gameObject.SetActive(true); - - _scribbleTweenId = LeanTween.value(gameObject, 0f, 1f, 0.4f) - .setEase(LeanTweenType.easeInOutQuad) - .setOnUpdate((float val) => { - if (_scribbleMaterialInstance != null) - _scribbleMaterialInstance.SetFloat(ProgressID, val); - }) - .setOnComplete(() => { - _scribbleTweenId = -1; - }).id; - } - - private void RemoveScribble() - { - if (!_overlayCreated || _scribbleOverlay == null) - return; - - if (!_scribbleOverlay.gameObject.activeSelf) - return; - - CancelActiveTweens(); - - if (_scribbleMaterialInstance != null) - { - float currentProgress = _scribbleMaterialInstance.GetFloat(ProgressID); - - _scribbleTweenId = LeanTween.value(gameObject, currentProgress, 0f, 0.3f) - .setEase(LeanTweenType.easeOutQuad) - .setOnUpdate((float val) => { - if (_scribbleMaterialInstance != null) - _scribbleMaterialInstance.SetFloat(ProgressID, val); - }) - .setOnComplete(() => { - _scribbleTweenId = -1; - if (_scribbleOverlay != null) - _scribbleOverlay.gameObject.SetActive(false); - }).id; - } - } - - private void CancelActiveTweens() - { - TweenHelper.CancelTween(ref _scribbleTweenId); - } - - private void OnDestroy() - { - CancelActiveTweens(); - - if (_scribbleMaterialInstance != null) - Destroy(_scribbleMaterialInstance); - - if (_scribbleOverlay != null) - Destroy(_scribbleOverlay.gameObject); - } - } -} diff --git a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/PostItScribbleEffect.cs.meta b/Assets/_Content/Scripts/Features/DetectiveBoard/UI/PostItScribbleEffect.cs.meta deleted file mode 100644 index 90657bda..00000000 --- a/Assets/_Content/Scripts/Features/DetectiveBoard/UI/PostItScribbleEffect.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 009173206d77d184d86915685410c7d8 \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/Dialogue/Data/DialogueNode.cs b/Assets/_Content/Scripts/Features/Dialogue/Data/DialogueNode.cs index 5e173cf9..e7a583df 100644 --- a/Assets/_Content/Scripts/Features/Dialogue/Data/DialogueNode.cs +++ b/Assets/_Content/Scripts/Features/Dialogue/Data/DialogueNode.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using UnityEngine; using OctoCore.Localization; -using Unheimlich.Features.DetectiveBoard.Data; namespace Unheimlich.Features.Dialogue.Data { @@ -20,9 +19,6 @@ namespace Unheimlich.Features.Dialogue.Data [Tooltip("For end nodes (no choices), delay in seconds before automatic close. 0 = requires player interaction to close")] public float AutoCloseDelay = 3f; - [Header("Detective Board Integration")] - [Tooltip("Clue to unlock when this node is reached. Useful for end nodes to unlock evidence after a conversation")] - public ClueData ClueToUnlock; public string GetLocalizedDialogueText() { diff --git a/Assets/_Content/Scripts/Features/Dialogue/Runtime/DialogueManager.cs b/Assets/_Content/Scripts/Features/Dialogue/Runtime/DialogueManager.cs index 1fef9173..728370c0 100644 --- a/Assets/_Content/Scripts/Features/Dialogue/Runtime/DialogueManager.cs +++ b/Assets/_Content/Scripts/Features/Dialogue/Runtime/DialogueManager.cs @@ -2,8 +2,6 @@ using UnityEngine; using System; using Unheimlich.Core; using Unheimlich.Features.Dialogue.Data; -using Unheimlich.Features.DetectiveBoard.Data; -using Unheimlich.Features.DetectiveBoard; using Unheimlich.Features.Player; using Unheimlich.Features.Dialogue.UI; using Unheimlich.Features.Dialogue; @@ -194,17 +192,6 @@ namespace Unheimlich.Features.Dialogue UnlockFocus(); IsInDialogue = true; - if (_currentNode.ClueToUnlock != null) - { - if (DetectiveBoardManager.Instance != null) - { - DetectiveBoardManager.Instance.UnlockClue(_currentNode.ClueToUnlock); - } - else - { - Debug.LogWarning($"[DialogueManager] Cannot unlock clue '{_currentNode.ClueToUnlock.ClueID}': DetectiveBoardManager not found"); - } - } if(_currentChoiceAnchor == null) { diff --git a/Assets/_Content/Scripts/Features/Dialogue/Runtime/NPCDialogueTrigger.cs b/Assets/_Content/Scripts/Features/Dialogue/Runtime/NPCDialogueTrigger.cs deleted file mode 100644 index a6c2eab0..00000000 --- a/Assets/_Content/Scripts/Features/Dialogue/Runtime/NPCDialogueTrigger.cs +++ /dev/null @@ -1,142 +0,0 @@ -using UnityEngine; -using Unheimlich.Core.Interaction; -using Unheimlich.Features.Dialogue.Data; -using Unheimlich.Features.Dialogue; -using Unheimlich.Features.Player; -using Unheimlich.Features.Dialogue.UI; - -namespace Unheimlich.Features.Dialogue -{ - /// - /// NPC interactable that starts dialogue conversations. - /// Rotates to face player during dialogue and pauses if player moves too far away. - /// - public class NPCDialogueTrigger : InteractableBase -{ - [Header("Dialogue Reference")] - [Tooltip("DialogueConversation to use for this NPC")] - public DialogueConversation Conversation; - - [Header("Trigger Settings")] - [Tooltip("Can the dialogue be retriggered after completion")] - [SerializeField] private bool _isRetriggerable = true; - - [Header("Floating Word Setting")] - [SerializeField] private Transform _floatingWordAnchor; - - [Header("Animation Settings")] - [SerializeField] private DialogueAnimationSettings _animationSettings; - - [Header("UI Reference")] - [Tooltip("Local DialogueUI for this NPC. Uses DialogueManager's UI if null")] - [SerializeField] private DialogueUI _localDialogueUI; - - [Header("Distance Settings")] - [Tooltip("Maximum distance before dialogue pauses")] - [SerializeField] private float _maxDialogueDistance = 5f; - - [Header("Blur Effect")] - [Tooltip("Enable blur effect when dialogue starts")] - [SerializeField] private bool _enableBlurEffect = false; - - public Transform FloatingWordAnchor => _floatingWordAnchor; - - private bool _isDialogueActive = false; - private DialogueStateHandler _dialogueState; - - private void Awake() - { - _dialogueState = new DialogueStateHandler(this); - } - - private void Start() - { - InteractionText = "Talk"; - } - - private void Update() - { - if (_isDialogueActive && PlayerController.Instance != null) - { - float distance = Vector3.Distance(transform.position, PlayerController.Instance.transform.position); - - if (distance > _maxDialogueDistance && DialogueManager.Instance != null && DialogueManager.Instance.IsDialogueActive) - { - if ((Object)DialogueManager.Instance.CurrentOwner == this) - { - _dialogueState.PauseDialogue(); - _isDialogueActive = false; - } - } - else - LookAtPlayer(); - } - } - - public override void Interact() - { - if (DialogueManager.Instance == null) - return; - - if (_dialogueState.IsCompleted && !_isRetriggerable) - return; - - if (_dialogueState.HasPausedDialogue) - { - _dialogueState.ResumeDialogue(); - DialogueManager.Instance.OnDialogueCompleted += OnDialogueCompleted; - _isDialogueActive = true; - } - else - { - if (Conversation == null) - { - Debug.LogWarning($"NPC {gameObject.name} has no conversation configured!"); - return; - } - - DialogueManager.Instance.StartConversation( - Conversation, - _floatingWordAnchor, - _animationSettings, - _localDialogueUI, - this, - _enableBlurEffect - ); - DialogueManager.Instance.OnDialogueCompleted += OnDialogueCompleted; - _isDialogueActive = true; - } - } - - private void OnDialogueCompleted(object owner) - { - if ((Object)owner == this) - { - _dialogueState.MarkAsCompleted(); - - if (_isRetriggerable) - _dialogueState.AllowRetrigger(); - - _isDialogueActive = false; - } - - if (DialogueManager.Instance != null) - DialogueManager.Instance.OnDialogueCompleted -= OnDialogueCompleted; - } - - private void OnDestroy() - { - if (DialogueManager.Instance != null) - DialogueManager.Instance.OnDialogueCompleted -= OnDialogueCompleted; - } - - public void LookAtPlayer() - { - if (PlayerController.Instance == null) return; - Vector3 lookAtPosition = PlayerController.Instance.transform.position; - lookAtPosition.y = transform.position.y; - - transform.LookAt(lookAtPosition); - } -} -} \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/Dialogue/Runtime/NPCDialogueTrigger.cs.meta b/Assets/_Content/Scripts/Features/Dialogue/Runtime/NPCDialogueTrigger.cs.meta deleted file mode 100644 index 9ebaa412..00000000 --- a/Assets/_Content/Scripts/Features/Dialogue/Runtime/NPCDialogueTrigger.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 8696519b7eabaab4dbe80f2a4744cea0 \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/Dialogue/UI/BillBoardUI.cs b/Assets/_Content/Scripts/Features/Dialogue/UI/BillBoardUI.cs deleted file mode 100644 index bc9b4071..00000000 --- a/Assets/_Content/Scripts/Features/Dialogue/UI/BillBoardUI.cs +++ /dev/null @@ -1,32 +0,0 @@ -using UnityEngine; - -namespace Unheimlich.Features.Dialogue.UI -{ - /// - /// Makes UI element always face the main camera. - /// Commonly used for world-space UI elements like floating words. - /// - public class BillboardUI : MonoBehaviour -{ - private Transform _cameraTransform; - - private void Awake() - { - _cameraTransform = Camera.main != null ? Camera.main.transform : null; - - if (_cameraTransform == null) - Debug.LogWarning($"BillboardUI on {gameObject.name}: Main camera not found!"); - } - - private void Start() { } - - private void LateUpdate() - { - if (_cameraTransform != null) - { - transform.LookAt(transform.position + _cameraTransform.rotation * Vector3.forward, - _cameraTransform.rotation * Vector3.up); - } - } -} -} \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/Dialogue/UI/FloatingLabel.cs b/Assets/_Content/Scripts/Features/Dialogue/UI/FloatingLabel.cs index 5a4ef37c..6ba185fc 100644 --- a/Assets/_Content/Scripts/Features/Dialogue/UI/FloatingLabel.cs +++ b/Assets/_Content/Scripts/Features/Dialogue/UI/FloatingLabel.cs @@ -3,6 +3,7 @@ using TMPro; using System.Collections; using System.Collections.Generic; using Unheimlich.Features.Hallucination; +using Unheimlich.Features.Shared; namespace Unheimlich.Features.Dialogue.UI { @@ -495,7 +496,7 @@ namespace Unheimlich.Features.Dialogue.UI ghostRenderer.sortingOrder = mainRenderer.sortingOrder - 1; } - ghostObj.AddComponent(); + ghostObj.AddComponent(); // Start hidden — the manager or an override will enable it when needed _ghostText.enabled = _liveGhostEnabled; diff --git a/Assets/_Content/Scripts/Features/Diapason.meta b/Assets/_Content/Scripts/Features/Diapason.meta deleted file mode 100644 index 32436116..00000000 --- a/Assets/_Content/Scripts/Features/Diapason.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 8a26921d06d64544184c5cbcf41b0b7d -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Content/Scripts/Features/Diapason/Data.meta b/Assets/_Content/Scripts/Features/Diapason/Data.meta deleted file mode 100644 index b5d2ce6b..00000000 --- a/Assets/_Content/Scripts/Features/Diapason/Data.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 6e871b828a2e9ca4ba7cc5e025373372 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Content/Scripts/Features/Diapason/Data/EquippableTapSettings.cs b/Assets/_Content/Scripts/Features/Diapason/Data/EquippableTapSettings.cs deleted file mode 100644 index a9416114..00000000 --- a/Assets/_Content/Scripts/Features/Diapason/Data/EquippableTapSettings.cs +++ /dev/null @@ -1,37 +0,0 @@ -using UnityEngine; - -namespace Unheimlich.Features.Diapason -{ - -[System.Serializable] -public class TapAnimationSettings -{ - [Header("Tap Motion")] - [Tooltip("Elbow rotation during windup (degrees)")] - public float WindupRotation = 15f; - - [Tooltip("Duration of windup (pull back)")] - public float WindupDuration = 0.1f; - - [Tooltip("Duration of strike (forward snap)")] - public float StrikeDuration = 0.05f; - - [Tooltip("Wrist snap rotation during strike (degrees)")] - public float WristSnapRotation = 10f; - - [Header("Vibration Shake")] - [Tooltip("Shake intensity during vibration")] - public float ShakeIntensity = 2f; - - [Tooltip("Shake speed (frequency)")] - public float ShakeSpeed = 30f; -} - -public class EquippableTapSettings : MonoBehaviour -{ - [SerializeField] private TapAnimationSettings _tapSettings = new TapAnimationSettings(); - - public TapAnimationSettings TapSettings => _tapSettings; -} - -} diff --git a/Assets/_Content/Scripts/Features/Diapason/Data/EquippableTapSettings.cs.meta b/Assets/_Content/Scripts/Features/Diapason/Data/EquippableTapSettings.cs.meta deleted file mode 100644 index 44f1cb9d..00000000 --- a/Assets/_Content/Scripts/Features/Diapason/Data/EquippableTapSettings.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: b46af809fa8f34d49b3dec606a505c5e \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/Diapason/Runtime.meta b/Assets/_Content/Scripts/Features/Diapason/Runtime.meta deleted file mode 100644 index 12925fd6..00000000 --- a/Assets/_Content/Scripts/Features/Diapason/Runtime.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: adbe6317f6c8e9e469b36e06ab2abbfb -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Content/Scripts/Features/Diapason/Runtime/DiapasonEquipmentManager.cs b/Assets/_Content/Scripts/Features/Diapason/Runtime/DiapasonEquipmentManager.cs deleted file mode 100644 index 2f392a49..00000000 --- a/Assets/_Content/Scripts/Features/Diapason/Runtime/DiapasonEquipmentManager.cs +++ /dev/null @@ -1,379 +0,0 @@ -using UnityEngine; -using Unheimlich.Core; -using Unheimlich.Core.Audio; -using Unheimlich.Features.OctoArms; -using InputManager = Unheimlich.Core.Input.InputManager; -using System; -using System.Collections; - -namespace Unheimlich.Features.Diapason -{ - -public class DiapasonEquipmentManager : MonoBehaviour -{ - [Header("References")] - [SerializeField] private OctoIkArmsImproved _armsSystem; - [SerializeField] private GameObject _diapasonPrefab; - [SerializeField] private int _equipmentArmIndex = 1; - - [Header("Detection Settings")] - [SerializeField] private LayerMask _soundEmitterLayer; - [SerializeField] private float _detectionRadius = 0.15f; - [SerializeField] private Transform[] _detectionPoints; - - [Header("Visual Feedback")] - [SerializeField] private GameObject _contactFeedbackPrefab; - - [Header("Diapason Animation")] - [SerializeField] private string _vibratingBoolParam = "IsVibrating"; - [SerializeField] private string _tapTriggerParam = "Tap"; - - [Header("Tap Settings")] - [SerializeField] private AudioClip _tapSound; - [SerializeField] [Range(0f, 1f)] private float _tapVolume = 1f; - [SerializeField] private float _vibrationDuration = 15f; - - public bool IsEquipped => _isEquipped; - public AudioListenable CurrentDetectedEmitter => _currentDetectedEmitter; - public bool CanTap => _isEquipped && _currentDetectedEmitter != null && !_isVibrating && !_currentDetectedEmitter.HasBeenTapped; - - public event Action OnEmitterDetected; - public event Action OnEmitterLost; - - private GameObject _spawnedDiapason; - private bool _isEquipped = false; - private AudioListenable _currentDetectedEmitter; - private GameObject _contactFeedback; - private bool _isVibrating = false; - private AudioSource _audioSource; - private Coroutine _vibrationCoroutine; - private Animator _diapasonAnimator; - private ParticleSystem _diapasonParticles; - - void Awake() - { - if (_armsSystem == null) - { - _armsSystem = GetComponent(); - if (_armsSystem == null) - { - Debug.LogError("[DiapasonEquipment] OctoIkArmsImproved not found!"); - } - } - - _audioSource = GetComponent(); - if (_audioSource == null) - { - _audioSource = gameObject.AddComponent(); - } - } - - void Update() - { - if (_isEquipped && _armsSystem.GetArmState(_equipmentArmIndex) == ArmState.Equipped) - { - CheckForSoundEmitters(); - - var fireAction = InputManager.GetFireAction(); - if (fireAction != null && fireAction.WasPressedThisFrame()) - { - if (CanTap) - TapDiapason(); - } - } - } - - public void EquipDiapason() - { - if (_isEquipped) - { - Debug.LogWarning("[DiapasonEquipment] Diapason already equipped!"); - return; - } - - if (_diapasonPrefab == null) - { - Debug.LogError("[DiapasonEquipment] Diapason prefab not assigned!"); - return; - } - - _spawnedDiapason = Instantiate(_diapasonPrefab); - _spawnedDiapason.SetActive(true); - - _diapasonAnimator = _spawnedDiapason.GetComponentInChildren(true); - if (_diapasonAnimator == null) - { - Debug.LogWarning("[DiapasonEquipment] No Animator found on spawned diapason"); - } - else - { - _diapasonAnimator.enabled = false; - } - - _diapasonParticles = _spawnedDiapason.GetComponentInChildren(true); - if (_diapasonParticles == null) - { - Debug.LogWarning("[DiapasonEquipment] No ParticleSystem found on spawned diapason"); - } - - _armsSystem.EquipObjectOnArm(_equipmentArmIndex, _spawnedDiapason, () => { - _isEquipped = true; - OnDiapasonEquipped(); - }); - } - - public void UnequipDiapason() - { - if (!_isEquipped) - { - Debug.LogWarning("[DiapasonEquipment] Diapason not equipped!"); - return; - } - - if (_vibrationCoroutine != null) - { - StopCoroutine(_vibrationCoroutine); - _vibrationCoroutine = null; - } - - if (_currentDetectedEmitter != null) - { - _currentDetectedEmitter.SetConcentrating(false); - _currentDetectedEmitter.StopListening(cancelConcentrationFade: false); - _currentDetectedEmitter = null; - } - - _armsSystem.UnequipObjectFromArm(_equipmentArmIndex, () => { - _isEquipped = false; - _spawnedDiapason = null; - OnDiapasonUnequipped(); - }); - } - - private void CheckForSoundEmitters() - { - if (_detectionPoints == null || _detectionPoints.Length == 0) - return; - - AudioListenable closestEmitter = null; - float closestDistance = float.MaxValue; - - foreach (Transform detectionPoint in _detectionPoints) - { - if (detectionPoint == null) - continue; - - Collider[] hits = Physics.OverlapSphere(detectionPoint.position, _detectionRadius, _soundEmitterLayer); - - foreach (Collider hit in hits) - { - AudioListenable emitter = hit.GetComponent(); - - if (emitter != null) - { - float distance = Vector3.Distance(detectionPoint.position, hit.transform.position); - - if (distance < closestDistance) - { - closestDistance = distance; - closestEmitter = emitter; - } - } - } - } - - if (closestEmitter != _currentDetectedEmitter) - { - if (_currentDetectedEmitter != null) - { - OnEmitterExited(_currentDetectedEmitter); - } - - _currentDetectedEmitter = closestEmitter; - - if (_currentDetectedEmitter != null) - { - OnEmitterEntered(_currentDetectedEmitter); - } - } - } - - private void OnDiapasonEquipped() - { - PlayerEvents.TriggerDiapasonEquip(); - - if (_diapasonAnimator != null) - { - _diapasonAnimator.enabled = true; - _diapasonAnimator.SetBool(_vibratingBoolParam, false); - } - - if (_diapasonParticles != null) - { - _diapasonParticles.Stop(); - var emission = _diapasonParticles.emission; - emission.enabled = false; - } - } - - private void OnDiapasonUnequipped() - { - PlayerEvents.TriggerDiapasonUnequip(); - - _isVibrating = false; - _diapasonAnimator = null; - _diapasonParticles = null; - } - - public void TapDiapason() - { - if (!CanTap) - { - Debug.LogWarning("[DiapasonEquipment] Cannot tap: not equipped, no contact, or already tapped"); - return; - } - - TapAnimationSettings tapSettings = null; - if (_spawnedDiapason != null) - { - EquippableTapSettings equippable = _spawnedDiapason.GetComponent(); - if (equippable != null) - { - tapSettings = equippable.TapSettings; - } - } - - ArmEquipment equipment = _armsSystem.GetArmEquipment(_equipmentArmIndex); - if (equipment != null && tapSettings != null) - { - equipment.PlayTapAnimation(tapSettings); - equipment.StartShake(tapSettings); - } - - if (_tapSound != null && _audioSource != null) - { - _audioSource.PlayOneShot(_tapSound, _tapVolume); - } - - if (_diapasonAnimator != null) - { - _diapasonAnimator.SetTrigger(_tapTriggerParam); - _diapasonAnimator.SetBool(_vibratingBoolParam, true); - } - - if (_diapasonParticles != null) - { - var emission = _diapasonParticles.emission; - emission.enabled = true; - _diapasonParticles.Play(); - } - - _isVibrating = true; - - if (_currentDetectedEmitter != null) - { - _currentDetectedEmitter.StartListening(transform.position); - } - - PlayerEvents.TriggerDiapasonTap(); - - if (_vibrationCoroutine != null) - StopCoroutine(_vibrationCoroutine); - _vibrationCoroutine = StartCoroutine(StopVibrationAfterDuration()); - } - - private IEnumerator StopVibrationAfterDuration() - { - yield return new WaitForSeconds(_vibrationDuration); - - if (_diapasonAnimator != null) - { - _diapasonAnimator.SetBool(_vibratingBoolParam, false); - } - - if (_diapasonParticles != null) - { - _diapasonParticles.Stop(); - var emission = _diapasonParticles.emission; - emission.enabled = false; - } - - if (_currentDetectedEmitter != null) - { - _currentDetectedEmitter.StopListening(); - } - - ArmEquipment equipment = _armsSystem.GetArmEquipment(_equipmentArmIndex); - if (equipment != null) - { - equipment.StopShake(); - } - - _isVibrating = false; - _vibrationCoroutine = null; - } - - private void OnEmitterEntered(AudioListenable emitter) - { - - emitter.SetConcentrating(true); - - if (_contactFeedbackPrefab != null && _contactFeedback == null) - { - _contactFeedback = Instantiate(_contactFeedbackPrefab, emitter.transform); - _contactFeedback.transform.localPosition = Vector3.zero; - } - - OnEmitterDetected?.Invoke(emitter); - } - - private void OnEmitterExited(AudioListenable emitter) - { - emitter.SetConcentrating(false); - - if (!emitter.HasBeenTapped) - { - emitter.StopListening(); - } - - if (_contactFeedback != null) - { - Destroy(_contactFeedback); - _contactFeedback = null; - } - - if (_diapasonAnimator != null) - { - _diapasonAnimator.SetBool(_vibratingBoolParam, false); - } - - if (_diapasonParticles != null) - { - _diapasonParticles.Stop(); - var emission = _diapasonParticles.emission; - emission.enabled = false; - } - - _isVibrating = false; - - OnEmitterLost?.Invoke(emitter); - } - - void OnDrawGizmosSelected() - { - if (_detectionPoints == null || !_isEquipped) - return; - - Gizmos.color = Color.yellow; - - foreach (Transform detectionPoint in _detectionPoints) - { - if (detectionPoint != null) - { - Gizmos.DrawWireSphere(detectionPoint.position, _detectionRadius); - } - } - } -} - -} diff --git a/Assets/_Content/Scripts/Features/Diapason/Runtime/DiapasonEquipmentManager.cs.meta b/Assets/_Content/Scripts/Features/Diapason/Runtime/DiapasonEquipmentManager.cs.meta deleted file mode 100644 index af39970a..00000000 --- a/Assets/_Content/Scripts/Features/Diapason/Runtime/DiapasonEquipmentManager.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 38b80731eb88f834ab3d79a566e40b48 \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/Diapason/Runtime/DiapasonListeningHandlerV2.cs b/Assets/_Content/Scripts/Features/Diapason/Runtime/DiapasonListeningHandlerV2.cs deleted file mode 100644 index 1ed0c817..00000000 --- a/Assets/_Content/Scripts/Features/Diapason/Runtime/DiapasonListeningHandlerV2.cs +++ /dev/null @@ -1,193 +0,0 @@ -using System.Collections; -using UnityEngine; -using Unheimlich.Core.Audio; -using Unheimlich.Core; -using Unheimlich.Core.Utils; -using Unheimlich.Features.Player; -using Unheimlich.World.CameraSystem; -using InputManager = Unheimlich.Core.Input.InputManager; - -namespace Unheimlich.Features.Diapason -{ - public class DiapasonListeningHandlerV2 : Singleton - { - [Header("Settings")] - [SerializeField] private bool _disablePlayerMovementDuringListen = true; - [SerializeField] private bool _enablePanMode = true; - - [Header("Visual Feedback")] - [Tooltip("Vignette intensity during listening (0-1)")] - [SerializeField] [Range(0f, 1f)] private float _vignetteIntensity = 0.3f; - - [Header("Audio")] - [SerializeField] private AudioClip _breathInClip; - [SerializeField] [Range(0f, 1f)] private float _breathInVolume = 1f; - [SerializeField] private float _concentrationDuration = 1f; - [SerializeField] private float _breathInDuration = 0.5f; - - public bool IsListening { get; private set; } - public bool IsConcentrating { get; private set; } - - private AudioSource _playerAudioSource; - private Coroutine _listeningCoroutine; - private CameraHeadBob _cameraHeadBob; - private CameraPanSettings _originalCameraSettings; - private AudioListenable _lastDetectedEmitter; - private bool _isFadingOut; - - protected override void OnAwake() - { - _playerAudioSource = GetComponent(); - if (_playerAudioSource == null) - _playerAudioSource = gameObject.AddComponent(); - - _cameraHeadBob = GetComponentInChildren(); - } - - void Update() - { - var diapasonAction = InputManager.GetDiapasonAction(); - if (diapasonAction != null && diapasonAction.WasPressedThisFrame()) - { - if (!IsListening && !IsConcentrating) - { - StartListeningMode(); - } - else if (IsListening || IsConcentrating) - { - StopListeningMode(); - } - } - - if (IsListening && PlayerController.Instance?.CameraPanController != null) - { - PlayerController.Instance.CameraPanController.UpdatePan(); - } - } - - private void StartListeningMode() - { - if (_isFadingOut || PlayerController.Instance?.DiapasonEquipment == null) - return; - - if (_listeningCoroutine != null) - StopCoroutine(_listeningCoroutine); - - PlayerEvents.TriggerEnterListening(); - _listeningCoroutine = StartCoroutine(ListeningSequence()); - } - - private IEnumerator ListeningSequence() - { - IsConcentrating = true; - InputManager.SetContext(InputManager.InputContext.Listening); - - if (_disablePlayerMovementDuringListen && PlayerController.Instance?.Movement != null) - { - PlayerController.Instance.Movement.enabled = false; - } - - PlayerController.Instance.DiapasonEquipment.EquipDiapason(); - - PlayBreathIn(); - yield return new WaitForSeconds(_breathInDuration); - - if (_cameraHeadBob != null) - { - float resetDuration = _concentrationDuration - _breathInDuration; - _cameraHeadBob.LerpToZero(resetDuration); - } - - yield return new WaitForSeconds(_concentrationDuration - _breathInDuration); - - IsConcentrating = false; - IsListening = true; - - EnableVignette(true); - - if (_enablePanMode && PlayerController.Instance?.CameraPanController != null) - { - PlayerController.Instance.CameraPanController.SaveOriginalState(); - PlayerController.Instance.CameraPanController.StartPan(); - } - - Cursor.lockState = CursorLockMode.Confined; - Cursor.visible = false; - } - - private void StopListeningMode() - { - if (_listeningCoroutine != null) - { - StopCoroutine(_listeningCoroutine); - _listeningCoroutine = null; - } - - IsListening = false; - IsConcentrating = false; - _isFadingOut = true; - - PlayerController.Instance.DiapasonEquipment.UnequipDiapason(); - - EnableVignette(false); - - if (_enablePanMode && PlayerController.Instance?.CameraPanController != null) - { - PlayerController.Instance.CameraPanController.StopPan(); - PlayerController.Instance.CameraPanController.ReturnToOriginalPosition(() => - { - if (_originalCameraSettings != null && PlayerController.Instance?.CameraPanController != null) - { - PlayerController.Instance.CameraPanController.Settings = _originalCameraSettings; - _originalCameraSettings = null; - } - - if (_disablePlayerMovementDuringListen && PlayerController.Instance?.Movement != null) - { - PlayerController.Instance.Movement.enabled = true; - } - - _isFadingOut = false; - }); - } - else - { - if (_disablePlayerMovementDuringListen && PlayerController.Instance?.Movement != null) - { - PlayerController.Instance.Movement.enabled = true; - } - - _isFadingOut = false; - } - - InputManager.SetContext(InputManager.InputContext.Normal); - PlayerEvents.TriggerExitListening(); - - Cursor.lockState = CursorLockMode.Locked; - Cursor.visible = false; - } - - private void PlayBreathIn() - { - if (_breathInClip != null && _playerAudioSource != null) - { - _playerAudioSource.PlayOneShot(_breathInClip, _breathInVolume); - } - } - - private void EnableVignette(bool enable) - { - if (ListeningVignetteEffect.Instance == null) - return; - - if (enable) - { - ListeningVignetteEffect.Instance.EnableVignetteWithIntensity(_vignetteIntensity); - } - else - { - ListeningVignetteEffect.Instance.EnableVignette(false); - } - } - } -} diff --git a/Assets/_Content/Scripts/Features/Diapason/Runtime/DiapasonListeningHandlerV2.cs.meta b/Assets/_Content/Scripts/Features/Diapason/Runtime/DiapasonListeningHandlerV2.cs.meta deleted file mode 100644 index 73084010..00000000 --- a/Assets/_Content/Scripts/Features/Diapason/Runtime/DiapasonListeningHandlerV2.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 46c1f473fbd3cac4397aabaa76d18357 \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/Items/Runtime/InspectableItem.cs b/Assets/_Content/Scripts/Features/Items/Runtime/InspectableItem.cs deleted file mode 100644 index f594435a..00000000 --- a/Assets/_Content/Scripts/Features/Items/Runtime/InspectableItem.cs +++ /dev/null @@ -1,109 +0,0 @@ -using UnityEngine; -using Unheimlich.Core.Interaction; -using Unheimlich.Features.Dialogue.UI; - -namespace Unheimlich.Features.Items -{ - /// - /// Interactable item that can be picked up and examined closely by the player. - /// Temporarily moves to inspection position and allows rotation via look input. - /// - [RequireComponent(typeof(Collider), typeof(Rigidbody))] - public class InspectableItem : InteractableBase -{ - [Header("Inspection Settings")] - [SerializeField] private float _inspectionDistance = 1f; - [SerializeField] private float _rotationSpeed = 100f; - - [Header("Animation Settings")] - [SerializeField] private bool _blockInteractionDuringAnimation = true; - - private Vector3 _originalPosition; - private Quaternion _originalRotation; - private Transform _originalParent; - private Rigidbody _rigidbody; - private bool _isAnimationPlaying = false; - private ObjectDescriptionTrigger _objectDescriptionTrigger; - private LevitatingObject _levitatingObject; - - public bool IsAnimationPlaying => _isAnimationPlaying; - - private void Start() - { - _rigidbody = GetComponent(); - _rigidbody.isKinematic = true; - - _originalPosition = transform.position; - _originalRotation = transform.rotation; - _originalParent = transform.parent; - - _objectDescriptionTrigger = GetComponent(); - _levitatingObject = GetComponent(); - } - - public override void Interact() - { - if (_blockInteractionDuringAnimation) - { - if (_isAnimationPlaying) - return; - - if (_objectDescriptionTrigger != null && - _objectDescriptionTrigger.IsInMergeOnceMode && - _objectDescriptionTrigger.IsAnimationActive) - { - return; - } - } - - if (PlayerInspectionHandler.Instance != null) - PlayerInspectionHandler.Instance.ToggleInspection(this); - } - - public void SetAnimationState(bool isPlaying) - { - _isAnimationPlaying = isPlaying; - } - - /// - /// Prepares the item for inspection by parenting it to the hold point and making it kinematic. - /// - public void PrepareForInspection(Transform holdPoint) - { - _originalPosition = transform.position; - _originalRotation = transform.rotation; - _originalParent = transform.parent; - - _rigidbody.isKinematic = true; - transform.SetParent(holdPoint); - transform.localPosition = Vector3.forward * _inspectionDistance; - transform.localRotation = Quaternion.identity; - - if (_levitatingObject != null) - { - _levitatingObject.UpdateBasePosition(); - } - } - - public void Release() - { - transform.SetParent(_originalParent); - transform.position = _originalPosition; - transform.rotation = _originalRotation; - - _rigidbody.isKinematic = true; - - if (_levitatingObject != null) - { - _levitatingObject.UpdateBasePosition(); - } - } - - public float RotationSpeed => _rotationSpeed; - - private void OnDestroy() - { - LeanTween.cancel(gameObject); - } -} -} \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/Items/Runtime/InspectableItem.cs.meta b/Assets/_Content/Scripts/Features/Items/Runtime/InspectableItem.cs.meta deleted file mode 100644 index 1a77c62d..00000000 --- a/Assets/_Content/Scripts/Features/Items/Runtime/InspectableItem.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: cd19509bd29a28648a08eaf7f6146ef1 \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/OctoArms.meta b/Assets/_Content/Scripts/Features/OctoArms.meta deleted file mode 100644 index cb4d5193..00000000 --- a/Assets/_Content/Scripts/Features/OctoArms.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 649c28cf347c39f4da922da29b186810 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Content/Scripts/Features/OctoArms/Runtime.meta b/Assets/_Content/Scripts/Features/OctoArms/Runtime.meta deleted file mode 100644 index 540d9395..00000000 --- a/Assets/_Content/Scripts/Features/OctoArms/Runtime.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 68bed47480dd3d34cb3ab7611ce22c1d -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Content/Scripts/Features/OctoArms/Runtime/ArmEquipmentSystem.cs b/Assets/_Content/Scripts/Features/OctoArms/Runtime/ArmEquipmentSystem.cs deleted file mode 100644 index 08c43175..00000000 --- a/Assets/_Content/Scripts/Features/OctoArms/Runtime/ArmEquipmentSystem.cs +++ /dev/null @@ -1,451 +0,0 @@ -using UnityEngine; -using Unheimlich.Features.Diapason; - -namespace Unheimlich.Features.OctoArms -{ - -public enum ArmState -{ - Environmental, - TransitionToEquipped, - Equipped, - TransitionToEnvironmental -} - -[System.Serializable] -public class ArmEquipment -{ - [Header("Arm References")] - public OctoIkLimb IkLimb; - public Transform HandSocket; - - [Header("Bone References (for procedural animation)")] - [Tooltip("Shoulder bone (optional)")] - public Transform ShoulderBone; - - [Tooltip("Elbow bone (optional)")] - public Transform ElbowBone; - - [Tooltip("Wrist bone (required for rotation)")] - public Transform WristBone; - - [Header("Equipped Pose Settings")] - [Tooltip("Rotation to apply to shoulder when equipped")] - public Vector3 ShoulderRotationOffset = Vector3.zero; - - [Tooltip("Rotation to apply to elbow when equipped")] - public Vector3 ElbowRotationOffset = Vector3.zero; - - [Tooltip("Rotation to apply to wrist when equipped")] - public Vector3 WristRotationOffset = new Vector3(0, 0, 90); - - [Tooltip("Speed of rotation lerp")] - [Range(1f, 20f)] - public float RotationSpeed = 10f; - - [Tooltip("Duration of IK blend after unequip transition")] - [Range(0.1f, 1f)] - public float IkBlendDuration = 0.3f; - - [Header("Transition Settings")] - [Tooltip("Position offset where arm exits camera view")] - public Vector3 TransitionOffset = new Vector3(0.5f, -0.3f, 0.2f); - - [Tooltip("Duration for arm to move out of view")] - public float TransitionOutDuration = 0.3f; - - [Tooltip("Duration for arm to return to view")] - public float TransitionInDuration = 0.3f; - - [Tooltip("Curve for transition movement")] - public AnimationCurve TransitionCurve = AnimationCurve.EaseInOut(0, 0, 1, 1); - - [HideInInspector] public ArmState State = ArmState.Environmental; - [HideInInspector] public Vector3 OriginalTargetPosition; - [HideInInspector] public Quaternion OriginalTargetRotation; - [HideInInspector] public GameObject EquippedObject; - [HideInInspector] public float IkWeight = 1f; - - [HideInInspector] public Quaternion OriginalShoulderRotation; - [HideInInspector] public Quaternion OriginalElbowRotation; - [HideInInspector] public Quaternion OriginalWristRotation; - - private int _transitionTweenId = -1; - private int _ikWeightTweenId = -1; - private int _ikBlendTweenId = -1; - private bool _isBlendingToIk = false; - - private int _tapTweenId = -1; - private bool _isShaking = false; - private TapAnimationSettings _currentShakeSettings; - - private Quaternion _transitionStartShoulder; - private Quaternion _transitionStartElbow; - private Quaternion _transitionStartWrist; - - public void Initialize() - { - if (IkLimb != null && IkLimb.Target != null) - { - OriginalTargetPosition = IkLimb.Target.localPosition; - OriginalTargetRotation = IkLimb.Target.localRotation; - } - - if (ShoulderBone != null) - OriginalShoulderRotation = ShoulderBone.localRotation; - - if (ElbowBone != null) - OriginalElbowRotation = ElbowBone.localRotation; - - if (WristBone != null) - OriginalWristRotation = WristBone.localRotation; - - State = ArmState.Environmental; - IkWeight = 1f; - } - - public void StartTransitionToEquipped(GameObject objectToEquip, System.Action onTransitionComplete) - { - if (State != ArmState.Environmental) - return; - - State = ArmState.TransitionToEquipped; - EquippedObject = objectToEquip; - - CancelActiveTweens(); - - if (IkLimb == null || IkLimb.Target == null) - { - CompleteEquip(onTransitionComplete); - return; - } - - Vector3 transitionTarget = OriginalTargetPosition + TransitionOffset; - - _transitionTweenId = LeanTween.value(IkLimb.gameObject, 0f, 1f, TransitionOutDuration) - .setEase(TransitionCurve) - .setOnUpdate((float t) => { - if (IkLimb != null && IkLimb.Target != null) - { - IkLimb.Target.localPosition = Vector3.Lerp(IkLimb.Target.localPosition, transitionTarget, t); - } - }) - .setOnComplete(() => { - _transitionTweenId = -1; - CompleteEquip(onTransitionComplete); - }).id; - - _ikWeightTweenId = LeanTween.value(IkLimb.gameObject, IkWeight, 0f, TransitionOutDuration * 0.5f) - .setOnUpdate((float val) => { - IkWeight = val; - }) - .setOnComplete(() => { - _ikWeightTweenId = -1; - }).id; - } - - private void CompleteEquip(System.Action onComplete) - { - State = ArmState.Equipped; - - if (IkLimb != null) - { - IkLimb.enabled = false; - } - - if (EquippedObject != null && HandSocket != null) - { - EquippedObject.transform.SetParent(HandSocket, false); - EquippedObject.transform.localPosition = Vector3.zero; - EquippedObject.transform.localRotation = Quaternion.identity; - } - - onComplete?.Invoke(); - } - - public void UpdateEquippedPose() - { - if (State != ArmState.Equipped && !_isBlendingToIk) - return; - - if (_isShaking) - { - UpdateShake(); - return; - } - - if (State == ArmState.Equipped) - { - float deltaTime = UnityEngine.Time.deltaTime; - float lerpSpeed = 1f - Mathf.Exp(-RotationSpeed * deltaTime); - - if (ShoulderBone != null) - { - Quaternion targetRot = OriginalShoulderRotation * Quaternion.Euler(ShoulderRotationOffset); - ShoulderBone.localRotation = Quaternion.Slerp(ShoulderBone.localRotation, targetRot, lerpSpeed); - } - - if (ElbowBone != null) - { - Quaternion targetRot = OriginalElbowRotation * Quaternion.Euler(ElbowRotationOffset); - ElbowBone.localRotation = Quaternion.Slerp(ElbowBone.localRotation, targetRot, lerpSpeed); - } - - if (WristBone != null) - { - Quaternion targetRot = OriginalWristRotation * Quaternion.Euler(WristRotationOffset); - WristBone.localRotation = Quaternion.Slerp(WristBone.localRotation, targetRot, lerpSpeed); - } - } - } - - public void StartTransitionToEnvironmental(System.Action onTransitionComplete) - { - if (State != ArmState.Equipped) - return; - - State = ArmState.TransitionToEnvironmental; - - CancelActiveTweens(); - - _transitionStartShoulder = ShoulderBone != null ? ShoulderBone.localRotation : Quaternion.identity; - _transitionStartElbow = ElbowBone != null ? ElbowBone.localRotation : Quaternion.identity; - _transitionStartWrist = WristBone != null ? WristBone.localRotation : Quaternion.identity; - - if (IkLimb == null || IkLimb.Target == null) - { - CompleteUnequip(onTransitionComplete); - return; - } - - Vector3 currentPosition = IkLimb.Target.localPosition; - Vector3 transitionTarget = OriginalTargetPosition + TransitionOffset; - - _ikWeightTweenId = LeanTween.value(IkLimb.gameObject, IkWeight, 0f, TransitionInDuration * 0.5f) - .setOnUpdate((float val) => { - IkWeight = val; - }) - .setOnComplete(() => { - _ikWeightTweenId = -1; - }).id; - - _transitionTweenId = LeanTween.value(IkLimb.gameObject, 0f, 1f, TransitionInDuration) - .setEase(TransitionCurve) - .setOnUpdate((float t) => { - if (IkLimb != null && IkLimb.Target != null) - { - IkLimb.Target.localPosition = Vector3.Lerp(currentPosition, transitionTarget, t); - } - - if (ShoulderBone != null) - ShoulderBone.localRotation = Quaternion.Slerp(_transitionStartShoulder, OriginalShoulderRotation, t); - - if (ElbowBone != null) - ElbowBone.localRotation = Quaternion.Slerp(_transitionStartElbow, OriginalElbowRotation, t); - - if (WristBone != null) - WristBone.localRotation = Quaternion.Slerp(_transitionStartWrist, OriginalWristRotation, t); - }) - .setOnComplete(() => { - _transitionTweenId = -1; - StartIkBlend(onTransitionComplete); - }).id; - } - - private void StartIkBlend(System.Action onTransitionComplete) - { - if (IkLimb != null) - { - IkLimb.enabled = true; - } - - if (EquippedObject != null) - { - if (HandSocket != null) - EquippedObject.transform.SetParent(null); - - UnityEngine.Object.Destroy(EquippedObject); - EquippedObject = null; - } - - _isBlendingToIk = true; - - Quaternion startShoulder = ShoulderBone != null ? ShoulderBone.localRotation : Quaternion.identity; - Quaternion startElbow = ElbowBone != null ? ElbowBone.localRotation : Quaternion.identity; - Quaternion startWrist = WristBone != null ? WristBone.localRotation : Quaternion.identity; - Vector3 startTargetPos = IkLimb != null && IkLimb.Target != null ? IkLimb.Target.localPosition : Vector3.zero; - Quaternion startTargetRot = IkLimb != null && IkLimb.Target != null ? IkLimb.Target.localRotation : Quaternion.identity; - - if (IkLimb != null && IkLimb.gameObject != null) - { - _ikBlendTweenId = LeanTween.value(IkLimb.gameObject, 0f, 1f, IkBlendDuration) - .setEase(LeanTweenType.easeOutQuad) - .setOnUpdate((float t) => { - if (ShoulderBone != null) - ShoulderBone.localRotation = Quaternion.Slerp(startShoulder, OriginalShoulderRotation, t); - - if (ElbowBone != null) - ElbowBone.localRotation = Quaternion.Slerp(startElbow, OriginalElbowRotation, t); - - if (WristBone != null) - WristBone.localRotation = Quaternion.Slerp(startWrist, OriginalWristRotation, t); - - if (IkLimb != null && IkLimb.Target != null) - { - IkLimb.Target.localPosition = Vector3.Lerp(startTargetPos, OriginalTargetPosition, t); - IkLimb.Target.localRotation = Quaternion.Slerp(startTargetRot, OriginalTargetRotation, t); - } - - IkWeight = t; - }) - .setOnComplete(() => { - _ikBlendTweenId = -1; - CompleteUnequip(onTransitionComplete); - }).id; - } - else - { - CompleteUnequip(onTransitionComplete); - } - } - - private void CompleteUnequip(System.Action onComplete) - { - State = ArmState.Environmental; - _isBlendingToIk = false; - - if (IkLimb != null && IkLimb.Target != null) - { - IkLimb.Target.localPosition = OriginalTargetPosition; - IkLimb.Target.localRotation = OriginalTargetRotation; - } - - if (ShoulderBone != null) - ShoulderBone.localRotation = OriginalShoulderRotation; - - if (ElbowBone != null) - ElbowBone.localRotation = OriginalElbowRotation; - - if (WristBone != null) - WristBone.localRotation = OriginalWristRotation; - - onComplete?.Invoke(); - } - - public void CancelActiveTweens() - { - if (_transitionTweenId != -1) - { - LeanTween.cancel(_transitionTweenId); - _transitionTweenId = -1; - } - - if (_ikWeightTweenId != -1) - { - LeanTween.cancel(_ikWeightTweenId); - _ikWeightTweenId = -1; - } - - if (_ikBlendTweenId != -1) - { - LeanTween.cancel(_ikBlendTweenId); - _ikBlendTweenId = -1; - } - - if (_tapTweenId != -1) - { - LeanTween.cancel(_tapTweenId); - _tapTweenId = -1; - } - - _isBlendingToIk = false; - _isShaking = false; - } - - public void PlayTapAnimation(TapAnimationSettings settings) - { - if (State != ArmState.Equipped || settings == null) - return; - - if (_tapTweenId != -1) - { - LeanTween.cancel(_tapTweenId); - } - - Quaternion elbowStart = ElbowBone != null ? ElbowBone.localRotation : Quaternion.identity; - Quaternion wristStart = WristBone != null ? WristBone.localRotation : Quaternion.identity; - - Quaternion elbowWindup = elbowStart * Quaternion.Euler(0, 0, -settings.WindupRotation); - Quaternion elbowStrike = elbowStart * Quaternion.Euler(0, 0, settings.WindupRotation * 0.3f); - - Quaternion wristWindup = wristStart; - Quaternion wristStrike = wristStart * Quaternion.Euler(0, 0, -settings.WristSnapRotation); - - _tapTweenId = LeanTween.value(IkLimb.gameObject, 0f, 1f, settings.WindupDuration) - .setOnUpdate((float t) => { - if (ElbowBone != null) - { - ElbowBone.localRotation = Quaternion.Slerp(elbowStart, elbowWindup, t); - } - if (WristBone != null) - { - WristBone.localRotation = Quaternion.Slerp(wristStart, wristWindup, t); - } - }) - .setOnComplete(() => { - _tapTweenId = LeanTween.value(IkLimb.gameObject, 0f, 1f, settings.StrikeDuration) - .setOnUpdate((float t) => { - if (ElbowBone != null) - { - ElbowBone.localRotation = Quaternion.Slerp(elbowWindup, elbowStrike, t); - } - if (WristBone != null) - { - WristBone.localRotation = Quaternion.Slerp(wristWindup, wristStrike, t); - } - }) - .setOnComplete(() => { - _tapTweenId = LeanTween.value(IkLimb.gameObject, 0f, 1f, 0.1f) - .setOnUpdate((float t) => { - if (ElbowBone != null) - { - ElbowBone.localRotation = Quaternion.Slerp(elbowStrike, elbowStart, t); - } - if (WristBone != null) - { - WristBone.localRotation = Quaternion.Slerp(wristStrike, wristStart, t); - } - }) - .setOnComplete(() => { - _tapTweenId = -1; - }).id; - }).id; - }).id; - } - - public void StartShake(TapAnimationSettings settings) - { - _isShaking = true; - _currentShakeSettings = settings; - } - - public void StopShake() - { - _isShaking = false; - _currentShakeSettings = null; - } - - public void UpdateShake() - { - if (!_isShaking || State != ArmState.Equipped || WristBone == null || _currentShakeSettings == null) - return; - - float shake = Mathf.Sin(UnityEngine.Time.time * _currentShakeSettings.ShakeSpeed) * _currentShakeSettings.ShakeIntensity; - Quaternion shakeRotation = Quaternion.Euler(shake, shake * 0.5f, 0); - - Quaternion targetRot = OriginalWristRotation * Quaternion.Euler(WristRotationOffset) * shakeRotation; - WristBone.localRotation = targetRot; - } -} - -} diff --git a/Assets/_Content/Scripts/Features/OctoArms/Runtime/ArmEquipmentSystem.cs.meta b/Assets/_Content/Scripts/Features/OctoArms/Runtime/ArmEquipmentSystem.cs.meta deleted file mode 100644 index bb5a1804..00000000 --- a/Assets/_Content/Scripts/Features/OctoArms/Runtime/ArmEquipmentSystem.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 80d59ec0ac09852429c56ca6c42464c2 \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/OctoArms/Runtime/OctoIkArmsImproved.cs b/Assets/_Content/Scripts/Features/OctoArms/Runtime/OctoIkArmsImproved.cs deleted file mode 100644 index 3d514379..00000000 --- a/Assets/_Content/Scripts/Features/OctoArms/Runtime/OctoIkArmsImproved.cs +++ /dev/null @@ -1,519 +0,0 @@ -using UnityEngine; - -namespace Unheimlich.Features.OctoArms -{ - -public class OctoIkArmsImproved : MonoBehaviour -{ - // Stores raycast hit information - private class RaycastInfos - { - public bool HasHit; - public float Distance; - public float MaxDistance; - public Vector3 Position; - public Vector3 Normal; - public Transform Transform; - } - - [System.Serializable] - private class RaycastRig - { - public Transform Root; - public float Distance = 1f; - } - - [System.Serializable] - private class ArmRig - { - public bool Enabled = true; - public OctoIkLimb Arm; - public Vector3 RotationOffset; - public RaycastRig[] Raycasts; - - [Header("Equipment System")] - public ArmEquipment Equipment; - - [HideInInspector] public Vector3 TargetOriginPos; - [HideInInspector] public Quaternion TargetOriginRot; - [HideInInspector] public Vector3 PoleOriginPos; - - public void Init() - { - if (Arm) - { - if (Arm.Target) - { - TargetOriginPos = Arm.Target.localPosition; - TargetOriginRot = Arm.Target.localRotation; - } - - if (Arm.Pole) - { - PoleOriginPos = Arm.Pole.localPosition; - } - } - - if (Equipment != null) - { - Equipment.Initialize(); - } - } - - public bool IsEnvironmentalIkActive() - { - if (Equipment == null) - return Enabled; - - return Enabled && Equipment.State == ArmState.Environmental; - } - - public float GetEnvironmentalIkWeight() - { - if (Equipment == null) - return 1f; - - return Equipment.IkWeight; - } - } - - [System.Serializable] - private class Settings - { - [Header("Raycast")] - public LayerMask LayerMask; - - [Header("Stickiness")] - [Tooltip("Distance to start sticking (meters)")] - [Range(0.1f, 2f)] - public float StickStartDistance = 0.8f; - - [Tooltip("Distance to fully release (meters)")] - [Range(0.5f, 3f)] - public float StickReleaseDistance = 1.2f; - - [Header("Smoothing")] - [Tooltip("Stick speed (units/sec)")] - [Range(1f, 20f)] - public float StickSpeed = 10f; - - [Tooltip("Release speed (units/sec)")] - [Range(1f, 20f)] - public float ReleaseSpeed = 6f; - - [Tooltip("Rotation speed (deg/sec)")] - [Range(90f, 720f)] - public float RotationSpeed = 360f; - - [Header("Arm Selection")] - [Tooltip("Can both arms stick at the same time?")] - public bool AllowBothArms = true; - - [Tooltip("Max surface angle to accept sticking (degrees)")] - [Range(0f, 90f)] - public float MaxSurfaceAngle = 60f; - - [Header("Temporal Smoothing")] - [Tooltip("Frames before releasing (prevents flickering)")] - [Range(0, 10)] - public int StabilityFrames = 3; - } - - [SerializeField] private ArmRig _leftArm; - [SerializeField] private ArmRig _rightArm; - [SerializeField] private Settings _settings; - - private ArmRig[] _arms; - private RaycastInfos[][] _raycasts = new RaycastInfos[2][]; - private RaycastInfos[] _nearestHitRaycasts = new RaycastInfos[2]; - private int[] _noHitFrameCount = new int[2]; - private float[] _currentStickiness = new float[2]; - - void Awake() - { - _arms = new ArmRig[2] { _leftArm, _rightArm }; - foreach (ArmRig armRig in _arms) - armRig.Init(); - - // Initialize raycast arrays for both arms - _raycasts = new RaycastInfos[_arms.Length][]; - for (int i = 0; i < _arms.Length; i++) - { - _raycasts[i] = new RaycastInfos[_arms[i].Raycasts.Length]; - for (int u = 0; u < _raycasts[i].Length; u++) - _raycasts[i][u] = new RaycastInfos() { MaxDistance = 1 }; - } - - _nearestHitRaycasts = new RaycastInfos[_arms.Length]; - for (int i = 0; i < _nearestHitRaycasts.Length; i++) - _nearestHitRaycasts[i] = new RaycastInfos() { MaxDistance = 1 }; - - _noHitFrameCount = new int[_arms.Length]; - _currentStickiness = new float[_arms.Length]; - } - - void OnDrawGizmos() - { - if (Application.isPlaying || !enabled) return; - - foreach (ArmRig armRig in new ArmRig[2] { _leftArm, _rightArm }) - { - if (armRig.Raycasts == null) continue; - - for (int i = 0; i < armRig.Raycasts.Length; i++) - { - RaycastRig raycastRig = armRig.Raycasts[i]; - if (raycastRig.Root == null) continue; - - Debug.DrawRay(raycastRig.Root.position, raycastRig.Root.TransformDirection(Vector3.forward) * raycastRig.Distance, Color.cyan); - } - } - } - - void Update() - { - PerformRaycasts(); - FindNearestHits(); - UpdateArmPositions(); - UpdateEquippedPoses(); - UpdateHideShowAnimation(); - - if (_keepHidden && !_isShowing) - { - _leftArm.Arm.Target.localPosition = _originalLeftTargetPos + _hideOffset; - _rightArm.Arm.Target.localPosition = _originalRightTargetPos + _hideOffset; - } - } - - private void UpdateEquippedPoses() - { - for (int i = 0; i < _arms.Length; i++) - { - ArmRig armRig = _arms[i]; - if (armRig.Equipment != null) - { - armRig.Equipment.UpdateEquippedPose(); - } - } - } - - // Cast all configured raycasts and store results - private void PerformRaycasts() - { - for (int i = 0; i < _arms.Length; i++) - { - ArmRig armRig = _arms[i]; - - if (!armRig.IsEnvironmentalIkActive() || armRig.Raycasts == null) - continue; - - for (int u = 0; u < armRig.Raycasts.Length; u++) - { - RaycastRig raycastRig = armRig.Raycasts[u]; - if (raycastRig.Root == null) continue; - - RaycastInfos raycastInfos = _raycasts[i][u]; - RaycastHit hit; - - Vector3 origin = raycastRig.Root.position; - Vector3 direction = raycastRig.Root.TransformDirection(Vector3.forward); - - if (Physics.Raycast(origin, direction, out hit, raycastRig.Distance, _settings.LayerMask)) - { - Debug.DrawRay(origin, direction * hit.distance, Color.green); - - raycastInfos.HasHit = true; - raycastInfos.Distance = hit.distance; - raycastInfos.MaxDistance = raycastRig.Distance; - raycastInfos.Position = hit.point; - raycastInfos.Normal = hit.normal; - raycastInfos.Transform = hit.transform; - } - else - { - Debug.DrawRay(origin, direction * raycastRig.Distance, Color.red); - - raycastInfos.HasHit = false; - raycastInfos.Distance = raycastRig.Distance; - raycastInfos.MaxDistance = raycastRig.Distance; - raycastInfos.Position = Vector3.zero; - raycastInfos.Normal = Vector3.zero; - raycastInfos.Transform = null; - } - } - } - } - - // Find the closest raycast hit for each arm - private void FindNearestHits() - { - for (int i = 0; i < _arms.Length; i++) - { - ArmRig armRig = _arms[i]; - RaycastInfos nearest = _nearestHitRaycasts[i]; - - nearest.HasHit = false; - float closestDistance = float.MaxValue; - - if (armRig.Raycasts == null) continue; - - // Find the closest hit among all raycasts for this arm - for (int u = 0; u < armRig.Raycasts.Length; u++) - { - RaycastInfos raycastInfos = _raycasts[i][u]; - - if (raycastInfos.HasHit && raycastInfos.Distance < closestDistance) - { - closestDistance = raycastInfos.Distance; - - nearest.HasHit = true; - nearest.Distance = raycastInfos.Distance; - nearest.MaxDistance = raycastInfos.MaxDistance; - nearest.Position = raycastInfos.Position; - nearest.Normal = raycastInfos.Normal; - nearest.Transform = raycastInfos.Transform; - } - } - - // Track frames without hits for temporal smoothing - if (!nearest.HasHit) - { - _noHitFrameCount[i]++; - } - else - { - _noHitFrameCount[i] = 0; - } - } - } - - // Update arm positions based on raycast hits - private void UpdateArmPositions() - { - for (int i = 0; i < _arms.Length; i++) - { - ArmRig armRig = _arms[i]; - if (armRig.Arm == null || armRig.Arm.Target == null) continue; - - if (!armRig.IsEnvironmentalIkActive()) - continue; - - RaycastInfos raycastInfos = _nearestHitRaycasts[i]; - - Vector3 targetPos = armRig.TargetOriginPos; - Quaternion targetRot = armRig.TargetOriginRot; - - bool shouldStick = false; - - if (raycastInfos.HasHit && _noHitFrameCount[i] < _settings.StabilityFrames) - { - if (IsValidSurface(raycastInfos, i)) - { - targetPos = armRig.Arm.Target.parent.InverseTransformPoint(raycastInfos.Position); - - targetRot = Quaternion.LookRotation( - -armRig.Arm.Target.parent.InverseTransformDirection(raycastInfos.Normal), - transform.up - ) * Quaternion.Euler(armRig.RotationOffset); - - shouldStick = true; - } - } - - float targetStickiness = shouldStick ? 1f : 0f; - float stickinessSpeed = shouldStick ? _settings.StickSpeed : _settings.ReleaseSpeed; - _currentStickiness[i] = Mathf.MoveTowards(_currentStickiness[i], targetStickiness, stickinessSpeed * Time.deltaTime); - - float distanceFactor = 0f; - if (shouldStick && raycastInfos.HasHit) - { - distanceFactor = 1f - Mathf.InverseLerp(_settings.StickStartDistance, _settings.StickReleaseDistance, raycastInfos.Distance); - distanceFactor = Mathf.Clamp01(distanceFactor); - } - - float finalStickiness = _currentStickiness[i] * distanceFactor * armRig.GetEnvironmentalIkWeight(); - - float posSpeed = Mathf.Lerp(_settings.ReleaseSpeed, _settings.StickSpeed, finalStickiness); - float rotSpeed = _settings.RotationSpeed * Mathf.Deg2Rad; - - float tPos = 1f - Mathf.Exp(-posSpeed * Time.deltaTime); - float tRot = 1f - Mathf.Exp(-rotSpeed * Time.deltaTime); - - armRig.Arm.Target.localPosition = Vector3.Lerp(armRig.Arm.Target.localPosition, targetPos, tPos); - armRig.Arm.Target.localRotation = Quaternion.Slerp(armRig.Arm.Target.localRotation, targetRot, tRot); - } - } - - // Check if the surface is valid for sticking - private bool IsValidSurface(RaycastInfos raycast, int armIndex) - { - if (raycast.Transform == null) - return false; - - // Check surface angle (reject surfaces too angled) - float surfaceAngle = Vector3.Angle(-raycast.Normal, transform.forward); - if (surfaceAngle > _settings.MaxSurfaceAngle) - return false; - - // If not allowing both arms, check if arm is on correct side - if (!_settings.AllowBothArms) - { - Vector3 hitLocalPos = transform.InverseTransformPoint(raycast.Position); - bool isLeftSide = hitLocalPos.x < 0; - bool isLeftArm = armIndex == 0; - - if (isLeftSide != isLeftArm) - return false; - } - - return true; - } - - public void EquipObjectOnArm(int armIndex, GameObject objectToEquip, System.Action onComplete = null) - { - if (armIndex < 0 || armIndex >= _arms.Length) - { - Debug.LogError($"[OctoIkArms] Invalid arm index: {armIndex}"); - return; - } - - ArmRig armRig = _arms[armIndex]; - - if (armRig.Equipment == null) - { - Debug.LogError($"[OctoIkArms] Arm {armIndex} has no Equipment system configured!"); - return; - } - - armRig.Equipment.StartTransitionToEquipped(objectToEquip, onComplete); - } - - public void UnequipObjectFromArm(int armIndex, System.Action onComplete = null) - { - if (armIndex < 0 || armIndex >= _arms.Length) - { - Debug.LogError($"[OctoIkArms] Invalid arm index: {armIndex}"); - return; - } - - ArmRig armRig = _arms[armIndex]; - - if (armRig.Equipment == null) - { - Debug.LogError($"[OctoIkArms] Arm {armIndex} has no Equipment system configured!"); - return; - } - - armRig.Equipment.StartTransitionToEnvironmental(onComplete); - } - - public ArmState GetArmState(int armIndex) - { - if (armIndex < 0 || armIndex >= _arms.Length) - return ArmState.Environmental; - - if (_arms[armIndex].Equipment == null) - return ArmState.Environmental; - - return _arms[armIndex].Equipment.State; - } - - public bool IsArmInEnvironmentalMode(int armIndex) - { - return GetArmState(armIndex) == ArmState.Environmental; - } - - public ArmEquipment GetArmEquipment(int armIndex) - { - if (armIndex < 0 || armIndex >= _arms.Length) - return null; - - return _arms[armIndex].Equipment; - } - - private SkinnedMeshRenderer _armsRenderer; - private bool _armsHidden = false; - private Vector3 _originalLeftTargetPos; - private Vector3 _originalRightTargetPos; - private float _hideAnimationProgress = 0f; - private bool _isHiding = false; - private bool _isShowing = false; - private bool _keepHidden = false; - private float _hideShowSpeed = 2f; - private Vector3 _hideOffset = new Vector3(0, -0.5f, 0); - - public void HideArms(float speed = 2f) - { - if (_armsHidden) - return; - - if (_armsRenderer == null) - _armsRenderer = GetComponentInChildren(); - - _originalLeftTargetPos = _leftArm.Arm.Target.localPosition; - _originalRightTargetPos = _rightArm.Arm.Target.localPosition; - - _hideShowSpeed = speed; - _hideAnimationProgress = 0f; - _isHiding = true; - _isShowing = false; - _armsHidden = true; - _keepHidden = false; - } - - public void ShowArms(float speed = 2f) - { - if (!_armsHidden && !_isHiding) - return; - - if (_armsRenderer == null) - _armsRenderer = GetComponentInChildren(); - - if (_armsRenderer != null) - _armsRenderer.enabled = true; - - _hideShowSpeed = speed; - _hideAnimationProgress = 0f; - _isShowing = true; - _isHiding = false; - } - - private void UpdateHideShowAnimation() - { - if (!_isHiding && !_isShowing) - return; - - _hideAnimationProgress += Time.deltaTime * _hideShowSpeed; - float t = Mathf.Clamp01(_hideAnimationProgress); - - if (_isHiding) - { - _leftArm.Arm.Target.localPosition = Vector3.Lerp(_originalLeftTargetPos, _originalLeftTargetPos + _hideOffset, t); - _rightArm.Arm.Target.localPosition = Vector3.Lerp(_originalRightTargetPos, _originalRightTargetPos + _hideOffset, t); - - if (t >= 1f) - { - _isHiding = false; - _keepHidden = true; - if (_armsRenderer != null) - _armsRenderer.enabled = false; - } - } - else if (_isShowing) - { - _leftArm.Arm.Target.localPosition = Vector3.Lerp(_originalLeftTargetPos + _hideOffset, _originalLeftTargetPos, t); - _rightArm.Arm.Target.localPosition = Vector3.Lerp(_originalRightTargetPos + _hideOffset, _originalRightTargetPos, t); - - if (t >= 1f) - { - _isShowing = false; - _armsHidden = false; - _keepHidden = false; - _leftArm.Arm.Target.localPosition = _originalLeftTargetPos; - _rightArm.Arm.Target.localPosition = _originalRightTargetPos; - } - } - } -} - -} \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/OctoArms/Runtime/OctoIkArmsImproved.cs.meta b/Assets/_Content/Scripts/Features/OctoArms/Runtime/OctoIkArmsImproved.cs.meta deleted file mode 100644 index bc6d3ff8..00000000 --- a/Assets/_Content/Scripts/Features/OctoArms/Runtime/OctoIkArmsImproved.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 97777a4e863d974428be79c0d8a40883 \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/Player/Runtime/Camera/CameraEffects.cs b/Assets/_Content/Scripts/Features/Player/Runtime/Camera/CameraEffects.cs index 3ec8a84a..a1003f2a 100644 --- a/Assets/_Content/Scripts/Features/Player/Runtime/Camera/CameraEffects.cs +++ b/Assets/_Content/Scripts/Features/Player/Runtime/Camera/CameraEffects.cs @@ -36,6 +36,14 @@ namespace Unheimlich.Features.Player public bool HasActiveLookInput { get; private set; } public bool IsUsingGamepad { get; private set; } + //Cam headbob mods + public enum HeadBobMode + { + Normal, + Idle, + Disabled + } + private void Start() { if (Settings != null) diff --git a/Assets/_Content/Scripts/Features/Player/Runtime/Camera/CameraHeadBob.cs b/Assets/_Content/Scripts/Features/Player/Runtime/Camera/CameraHeadBob.cs index 5ba8709e..8c6da629 100644 --- a/Assets/_Content/Scripts/Features/Player/Runtime/Camera/CameraHeadBob.cs +++ b/Assets/_Content/Scripts/Features/Player/Runtime/Camera/CameraHeadBob.cs @@ -24,8 +24,8 @@ namespace Unheimlich.Features.Player // Strafe tilt (Z roll applied in LateUpdate, after OctoPlayer sets camera pitch). private float _currentTilt = 0f; - private HeadBobMode _overrideMode = HeadBobMode.Normal; - private bool _hasOverride = false; + private CameraEffects.HeadBobMode _overrideMode = CameraEffects.HeadBobMode.Normal; + private bool _hasOverride = false; private Coroutine _resetCoroutine; private void Start() @@ -41,17 +41,16 @@ namespace Unheimlich.Features.Player var settings = PlayerController.Instance.CameraEffects?.Settings; if (settings == null) return; - HeadBobMode currentMode = _hasOverride ? _overrideMode : HeadBobMode.Normal; + CameraEffects.HeadBobMode currentMode = _hasOverride ? _overrideMode : CameraEffects.HeadBobMode.Normal; - if (currentMode == HeadBobMode.Disabled) + if (currentMode == CameraEffects.HeadBobMode.Disabled) { SetBobDirect(settings.idleBobSpeed, 0f); ApplySmoothedBob(settings.bobRollAmplitude); return; } - bool forceIdle = currentMode == HeadBobMode.Idle - || (PlayerInspectionHandler.Instance != null && PlayerInspectionHandler.Instance.IsInspecting); + bool forceIdle = currentMode == CameraEffects.HeadBobMode.Idle; if (forceIdle) { @@ -119,7 +118,7 @@ namespace Unheimlich.Features.Player // ── Public API ──────────────────────────────────────────────────────────── /// Overrides the head bob mode (Idle, Normal, Disabled). - public void SetMode(HeadBobMode mode) + public void SetMode(CameraEffects.HeadBobMode mode) { _overrideMode = mode; _hasOverride = true; diff --git a/Assets/_Content/Scripts/Features/Player/Runtime/HandTouchFeedback.cs b/Assets/_Content/Scripts/Features/Player/Runtime/HandTouchFeedback.cs deleted file mode 100644 index ac6d003e..00000000 --- a/Assets/_Content/Scripts/Features/Player/Runtime/HandTouchFeedback.cs +++ /dev/null @@ -1,42 +0,0 @@ -using UnityEngine; -using UnityEngine.UI; - -namespace Unheimlich.Features.Player -{ - public class HandTouchFeedback : MonoBehaviour - { - [Header("Visual Settings")] - [SerializeField] private Image _feedbackImage; - [SerializeField] private Color _readyColor = Color.green; - [SerializeField] private float _pulseSpeed = 2f; - [SerializeField] private float _pulseIntensity = 0.3f; - - private float _pulseTimer; - private Color _originalColor; - - private void Awake() - { - if (_feedbackImage != null) - { - _originalColor = _feedbackImage.color; - } - } - - private void OnEnable() - { - _pulseTimer = 0f; - } - - private void Update() - { - if (_feedbackImage == null) - return; - - _pulseTimer += Time.deltaTime * _pulseSpeed; - float pulse = (Mathf.Sin(_pulseTimer) + 1f) * 0.5f; - - Color targetColor = Color.Lerp(_originalColor, _readyColor, pulse * _pulseIntensity); - _feedbackImage.color = targetColor; - } - } -} diff --git a/Assets/_Content/Scripts/Features/Player/Runtime/HandTouchFeedback.cs.meta b/Assets/_Content/Scripts/Features/Player/Runtime/HandTouchFeedback.cs.meta deleted file mode 100644 index b2a1a788..00000000 --- a/Assets/_Content/Scripts/Features/Player/Runtime/HandTouchFeedback.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 39b78978d656e894ea455342398ef8ba \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/Player/Runtime/ListeningVignetteEffect.cs b/Assets/_Content/Scripts/Features/Player/Runtime/ListeningVignetteEffect.cs deleted file mode 100644 index 06f2a765..00000000 --- a/Assets/_Content/Scripts/Features/Player/Runtime/ListeningVignetteEffect.cs +++ /dev/null @@ -1,105 +0,0 @@ -using UnityEngine; -using UnityEngine.Rendering; -using UnityEngine.Rendering.Universal; -using Unheimlich.Core; -using Unheimlich.Features.Hallucination; - -namespace Unheimlich.Features.Player -{ - public class ListeningVignetteEffect : Singleton - { - [Header("References")] - [Tooltip("Dedicated Volume for listening mode (not shared with dialogue system)")] - [SerializeField] private Volume _listeningVolume; - - [Header("Settings")] - [Tooltip("Target intensity when listening (0-1)")] - [SerializeField] [Range(0f, 1f)] private float _targetIntensity = 0.3f; - - [Tooltip("Speed of vignette fade in/out")] - [SerializeField] private float _transitionSpeed = 2f; - - private Vignette _vignette; - private bool _isEnabled; - private VolumeProfile _volumeProfile; - - protected override void OnAwake() - { - if (_listeningVolume == null) - { - GameObject volumeObject = new GameObject("Listening Mode Volume"); - volumeObject.transform.SetParent(transform); - _listeningVolume = volumeObject.AddComponent(); - _listeningVolume.isGlobal = true; - _listeningVolume.priority = 1; - _listeningVolume.weight = 0f; - } - - if (_listeningVolume.profile == null) - { - _volumeProfile = ScriptableObject.CreateInstance(); - _listeningVolume.profile = _volumeProfile; - - _vignette = _volumeProfile.Add(); - _vignette.active = true; - _vignette.intensity.Override(0f); - _vignette.smoothness.Override(0.4f); - _vignette.rounded.Override(true); - } - else - { - if (!_listeningVolume.profile.TryGet(out _vignette)) - { - _vignette = _listeningVolume.profile.Add(); - _vignette.active = true; - _vignette.intensity.Override(0f); - _vignette.smoothness.Override(0.4f); - _vignette.rounded.Override(true); - } - } - - _listeningVolume.weight = 0f; - } - - private void Update() - { - if (_vignette == null || _listeningVolume == null) - return; - - float targetWeight = _isEnabled ? 1f : 0f; - _listeningVolume.weight = Mathf.Lerp(_listeningVolume.weight, targetWeight, Time.deltaTime * _transitionSpeed); - - if (_isEnabled) - _vignette.intensity.value = _targetIntensity; - } - - public void EnableVignette(bool enable) - { - _isEnabled = enable; - - // Suppress the hallucination vignette while diapason is active to avoid additive blending. - // Restores hallucination vignette control when diapason listening ends. - if (HallucinationManager.Instance != null) - HallucinationManager.Instance.SetVignetteOverriddenExternally(enable); - } - - public void SetIntensity(float intensity) - { - _targetIntensity = Mathf.Clamp01(intensity); - if (_vignette != null) - _vignette.intensity.value = _targetIntensity; - } - - public void EnableVignetteWithIntensity(float intensity) - { - SetIntensity(intensity); - EnableVignette(true); - } - - private void OnDestroy() - { - if (_volumeProfile != null) - Destroy(_volumeProfile); - } - } -} diff --git a/Assets/_Content/Scripts/Features/Player/Runtime/ListeningVignetteEffect.cs.meta b/Assets/_Content/Scripts/Features/Player/Runtime/ListeningVignetteEffect.cs.meta deleted file mode 100644 index ad5ae6c5..00000000 --- a/Assets/_Content/Scripts/Features/Player/Runtime/ListeningVignetteEffect.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 878ebc270c13e5c49a3813b1848f6ea5 \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/Player/Runtime/PlayerController.cs b/Assets/_Content/Scripts/Features/Player/Runtime/PlayerController.cs index 02fca5f9..46b04d95 100644 --- a/Assets/_Content/Scripts/Features/Player/Runtime/PlayerController.cs +++ b/Assets/_Content/Scripts/Features/Player/Runtime/PlayerController.cs @@ -4,9 +4,6 @@ using Unheimlich.Core; using Unheimlich.Features.Player.Data; using Unheimlich.Features.Dialogue; using Unheimlich.Core.Interaction; -using Unheimlich.Features.DetectiveBoard; -using Unheimlich.Features.Diapason; -using Unheimlich.Features.OctoArms; using Unheimlich.World.CameraSystem; using InputManager = Unheimlich.Core.Input.InputManager; @@ -27,13 +24,6 @@ namespace Unheimlich.Features.Player public Transform CameraTransform { get; private set; } public Transform CameraPivot { get; private set; } public CameraEffects CameraEffects { get; private set; } - public CameraPanController CameraPanController { get; private set; } - public DiapasonListeningHandlerV2 DiapasonHandler { get; private set; } - public DiapasonEquipmentManager DiapasonEquipment { get; private set; } - public PlayerInspectionHandler InspectionHandler { get; private set; } - public DetectiveBoardController DetectiveBoardController { get; private set; } - public OctoIkArmsImproved ArmsSystem { get; private set; } - public InputManager Inputs => InputManager.Instance; // Cached sprint InputAction — resolved once in OnAwake to avoid per-frame foreach. @@ -56,14 +46,8 @@ namespace Unheimlich.Features.Player Movement = GetComponent(); CharController = GetComponent(); CameraEffects = GetComponent(); - CameraPanController = GetComponent(); - DiapasonHandler = GetComponent(); - DiapasonEquipment = GetComponent(); - InspectionHandler = GetComponent(); - DetectiveBoardController = GetComponent(); PlayerCamera = GetComponentInChildren(); - ArmsSystem = GetComponentInChildren(); if (PlayerCamera != null) { diff --git a/Assets/_Content/Scripts/Features/Shared/FacePlayer.cs b/Assets/_Content/Scripts/Features/Shared/FacePlayer.cs new file mode 100644 index 00000000..770ad384 --- /dev/null +++ b/Assets/_Content/Scripts/Features/Shared/FacePlayer.cs @@ -0,0 +1,31 @@ +using UnityEngine; + +namespace Unheimlich.Features.Shared +{ + /// + /// Makes element always face the main camera. + /// + public class FacePlayer : MonoBehaviour + { + private Transform _cameraTransform; + + private void Awake() + { + _cameraTransform = Camera.main != null ? Camera.main.transform : null; + + if (_cameraTransform == null) + Debug.LogWarning($"FacePlayer on {gameObject.name}: Main camera not found!"); + } + + private void Start() { } + + private void LateUpdate() + { + if (_cameraTransform != null) + { + transform.LookAt(transform.position + _cameraTransform.rotation * Vector3.forward, + _cameraTransform.rotation * Vector3.up); + } + } + } +} \ No newline at end of file diff --git a/Assets/_Content/Scripts/Features/Dialogue/UI/BillBoardUI.cs.meta b/Assets/_Content/Scripts/Features/Shared/FacePlayer.cs.meta similarity index 100% rename from Assets/_Content/Scripts/Features/Dialogue/UI/BillBoardUI.cs.meta rename to Assets/_Content/Scripts/Features/Shared/FacePlayer.cs.meta diff --git a/Assets/_Content/Scripts/World.meta b/Assets/_Content/Scripts/World.meta deleted file mode 100644 index 6c93af5c..00000000 --- a/Assets/_Content/Scripts/World.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: bfc84dc662a6af7438b306b5c2c0cad4 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Content/Scripts/World/Camera.meta b/Assets/_Content/Scripts/World/Camera.meta deleted file mode 100644 index 3ba4fca2..00000000 --- a/Assets/_Content/Scripts/World/Camera.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: a8de5fd3bd003994aaaf0ed9c2f5c44d -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Content/Scripts/World/Camera/Data.meta b/Assets/_Content/Scripts/World/Camera/Data.meta deleted file mode 100644 index d80bcf36..00000000 --- a/Assets/_Content/Scripts/World/Camera/Data.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 2ee4955d2b51b5e4abe0e243cab7267e -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Content/Scripts/World/Camera/Data/CameraPanSettings.cs b/Assets/_Content/Scripts/World/Camera/Data/CameraPanSettings.cs deleted file mode 100644 index af0709cf..00000000 --- a/Assets/_Content/Scripts/World/Camera/Data/CameraPanSettings.cs +++ /dev/null @@ -1,80 +0,0 @@ -using UnityEngine; - -namespace Unheimlich.World.CameraSystem -{ - /// - /// Camera pan mode. - /// - public enum CameraPanMode - { - Pan, - FreeLook - } - - /// - /// Head bob mode for camera pan. - /// - public enum HeadBobMode - { - Normal, - Idle, - Disabled - } - - /// - /// ScriptableObject configuration for camera pan and player alignment behavior. - /// Defines pan speed, borders, angles, alignment parameters, and lerp smoothness. - /// - [CreateAssetMenu(fileName = "CameraPanSettings", menuName = "Unheimlich/Camera/Camera Pan Settings")] - public class CameraPanSettings : ScriptableObject - { - [Header("Pan Mode")] - [Tooltip("Pan: Smooth edge-based panning for object inspection | FreeLook: Direct mouse/stick control for detective board")] - public CameraPanMode PanMode = CameraPanMode.Pan; - - [Header("Camera Pan")] - [Tooltip("Camera pan speed when moving the cursor/look stick")] - public float PanSpeed = 2f; - - [Tooltip("Size of the border area for mouse pan (0-1, screen percentage)")] - [Range(0f, 0.5f)] - public float PanBorderSize = 0.1f; - - [Tooltip("Maximum horizontal angle the camera can pan from center (left/right)")] - public float MaxPanAngleX = 15f; - - [Tooltip("Maximum vertical angle the camera can pan from center (up/down)")] - public float MaxPanAngleY = 15f; - - [Header("Recentering")] - [Tooltip("Enable automatic return to center when idle")] - public bool EnableRecentering = true; - - [Tooltip("Speed of automatic recentering (higher = faster return to center)")] - public float RecenterSpeed = 2f; - - [Tooltip("Delay before recentering starts (in seconds)")] - public float RecenterDelay = 0.1f; - - [Header("Player Alignment")] - [Tooltip("Speed of player/camera alignment to target")] - public float AlignmentSpeed = 3f; - - [Tooltip("Threshold for alignment completion")] - public float AlignmentThreshold = 0.5f; - - [Tooltip("Fixed distance from the player to the target when aligning (in meters)")] - public float FixedDistanceFromTarget = 1.5f; - - [Header("Head Bob Control")] - [Tooltip("Head bob mode: Normal (default behavior), Idle (force subtle breathing), Disabled (no movement)")] - public HeadBobMode HeadBobMode = HeadBobMode.Idle; - - [Header("Lerp Smoothing")] - [Tooltip("Use SmoothStep for smoother interpolation (true) or linear lerp (false)")] - public bool UseSmoothStep = true; - - [Tooltip("Custom animation curve for alignment (overrides SmoothStep if set)")] - public AnimationCurve AlignmentCurve = null; - } -} diff --git a/Assets/_Content/Scripts/World/Camera/Data/CameraPanSettings.cs.meta b/Assets/_Content/Scripts/World/Camera/Data/CameraPanSettings.cs.meta deleted file mode 100644 index 13e74fa8..00000000 --- a/Assets/_Content/Scripts/World/Camera/Data/CameraPanSettings.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 3e231c75dddecc0469543c190a9234b1 \ No newline at end of file diff --git a/Assets/_Content/Scripts/World/Camera/Runtime.meta b/Assets/_Content/Scripts/World/Camera/Runtime.meta deleted file mode 100644 index 60a62dcd..00000000 --- a/Assets/_Content/Scripts/World/Camera/Runtime.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: d6292657fd6825b4b958453d22c2de26 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Content/Scripts/World/Camera/Runtime/CameraPanController.cs b/Assets/_Content/Scripts/World/Camera/Runtime/CameraPanController.cs deleted file mode 100644 index 48136725..00000000 --- a/Assets/_Content/Scripts/World/Camera/Runtime/CameraPanController.cs +++ /dev/null @@ -1,468 +0,0 @@ -using System.Collections; -using UnityEngine; -using UnityEngine.InputSystem; -using Unheimlich.World.CameraSystem; -using Unheimlich.Features.Player; -using InputManager = Unheimlich.Core.Input.InputManager; - -namespace Unheimlich.World.CameraSystem -{ - /// - /// Handles camera panning and player/camera alignment to targets. - /// - public class CameraPanController : MonoBehaviour - { - [Header("Default Settings")] - [SerializeField] private CameraPanSettings _defaultSettings; - - private CameraPanSettings _settings; - private UnityEngine.Camera _camera; - private Transform _playerTransform; - - private Vector2 _originalCameraRotation; - private Vector3 _originalCameraLocalPosition; - private Vector3 _originalPlayerPosition; - private Vector3 _originalPlayerRotation; - private Vector3 _originalCameraLocalRotation; - private Coroutine _alignmentCoroutine; - private Coroutine _returnCoroutine; - private float _timeSinceLastInput; - - private float _targetYaw; - private float _targetPitch; - private float _currentYaw; - private float _currentPitch; - private float _lerpSpeed; - private bool _needsUpdate; - private CameraPanMode _mode = CameraPanMode.Pan; - private Vector2 _lastMousePosition; - - public bool IsPanning { get; private set; } - public CameraPanMode Mode => _mode; - - public CameraPanSettings Settings - { - get => _settings; - set => _settings = value; - } - - private void Awake() - { - // Defer initialization if PlayerController not ready - if (PlayerController.Instance != null) - { - _camera = PlayerController.Instance.PlayerCamera; - _playerTransform = PlayerController.Instance.transform; - } - - if (_defaultSettings != null) - _settings = _defaultSettings; - } - - private void Start() - { - // Retry if not initialized in Awake - if (_camera == null && PlayerController.Instance != null) - { - _camera = PlayerController.Instance.PlayerCamera; - _playerTransform = PlayerController.Instance.transform; - } - - if (_camera != null) - { - _originalCameraLocalPosition = _camera.transform.localPosition; - _originalCameraLocalRotation = _camera.transform.localEulerAngles; - } - - if (_playerTransform != null) - _originalPlayerPosition = _playerTransform.position; - _originalPlayerRotation = _playerTransform.eulerAngles; - - if (_settings == null && _defaultSettings != null) - _settings = _defaultSettings; - } - - private void LateUpdate() - { - if (IsPanning && _needsUpdate) - { - if (_mode == CameraPanMode.FreeLook) - { - float normalizedYaw = Mathf.Repeat(_currentYaw, 360f); - float normalizedPitch = _currentPitch; - - _playerTransform.eulerAngles = new Vector3(0f, normalizedYaw, 0f); - _camera.transform.localEulerAngles = new Vector3(normalizedPitch, 0f, 0f); - } - else - { - _currentYaw = Mathf.LerpAngle(_currentYaw, _targetYaw, Time.deltaTime * _lerpSpeed); - _currentPitch = Mathf.LerpAngle(_currentPitch, _targetPitch, Time.deltaTime * _lerpSpeed); - - float normalizedYaw = Mathf.Repeat(_currentYaw, 360f); - float normalizedPitch = _currentPitch; - - _playerTransform.eulerAngles = new Vector3(0f, normalizedYaw, 0f); - _camera.transform.localEulerAngles = new Vector3(normalizedPitch, 0f, 0f); - } - } - } - - /// - /// Starts camera pan mode. The mode (Pan or FreeLook) is determined by the current settings. - /// - public void StartPan() - { - if (_settings == null) - { - Debug.LogError("[CameraPanController] Settings not assigned!"); - return; - } - - IsPanning = true; - _mode = _settings.PanMode; - _timeSinceLastInput = 0f; - - if (Mouse.current != null) - _lastMousePosition = Mouse.current.position.ReadValue(); - - float currentPitch = _camera.transform.localEulerAngles.x; - if (currentPitch > 180f) - currentPitch -= 360f; - - float currentYaw = _playerTransform.eulerAngles.y; - if (currentYaw > 180f) - currentYaw -= 360f; - - _originalCameraRotation = new Vector2(currentYaw, currentPitch); - _currentYaw = currentYaw; - _currentPitch = currentPitch; - _targetYaw = currentYaw; - _targetPitch = currentPitch; - _needsUpdate = false; - } - - public void StopPan() - { - IsPanning = false; - } - - /// - /// Updates camera pan based on input (gamepad or mouse). - /// Call this from Update when pan is active. - /// - public void UpdatePan() - { - if (!IsPanning || _camera == null || _settings == null) - return; - - if (_mode == CameraPanMode.FreeLook) - { - UpdateFreeLook(); - } - else - { - UpdatePanMode(); - } - } - - private void UpdateFreeLook() - { - float lookX = 0f; - float lookY = 0f; - bool hasInput = false; - - var lookAction = InputManager.GetLookAction(); - if (lookAction != null) - { - Vector2 lookInput = lookAction.ReadValue(); - if (lookInput.sqrMagnitude > 0.01f) - { - lookX = lookInput.x; - lookY = lookInput.y; - hasInput = true; - } - } - - if (hasInput) - { - float sensitivity = _settings.PanSpeed * 0.1f; - _currentYaw += lookX * sensitivity; - _currentPitch -= lookY * sensitivity; - - _currentYaw = Mathf.Clamp(_currentYaw, _originalCameraRotation.x - _settings.MaxPanAngleX, _originalCameraRotation.x + _settings.MaxPanAngleX); - _currentPitch = Mathf.Clamp(_currentPitch, _originalCameraRotation.y - _settings.MaxPanAngleY, _originalCameraRotation.y + _settings.MaxPanAngleY); - - _needsUpdate = true; - } - } - - private void UpdatePanMode() - { - if (!IsPanning || _camera == null || _settings == null) - return; - - float panX = 0f; - float panY = 0f; - bool hasInput = false; - - var lookAction = InputManager.GetLookAction(); - if (lookAction != null) - { - Vector2 lookInput = lookAction.ReadValue(); - if (lookInput.sqrMagnitude > 0.01f) - { - panX = lookInput.x; - panY = lookInput.y; - hasInput = true; - } - } - - if (!hasInput && Mouse.current != null) - { - Vector2 mousePos = Mouse.current.position.ReadValue(); - - bool mouseHasMoved = Vector2.Distance(mousePos, _lastMousePosition) > 0.1f; - _lastMousePosition = mousePos; - - if (mouseHasMoved) - { - float screenWidth = Screen.width; - float screenHeight = Screen.height; - - float borderPixels = _settings.PanBorderSize * Mathf.Min(screenWidth, screenHeight); - - if (mousePos.x < borderPixels) - { - panX = -(borderPixels - mousePos.x) / borderPixels; - hasInput = true; - } - else if (mousePos.x > screenWidth - borderPixels) - { - panX = (mousePos.x - (screenWidth - borderPixels)) / borderPixels; - hasInput = true; - } - - if (mousePos.y < borderPixels) - { - panY = -(borderPixels - mousePos.y) / borderPixels; - hasInput = true; - } - else if (mousePos.y > screenHeight - borderPixels) - { - panY = (mousePos.y - (screenHeight - borderPixels)) / borderPixels; - hasInput = true; - } - } - } - - if (hasInput) - { - _timeSinceLastInput = 0f; - } - else - { - _timeSinceLastInput += Time.deltaTime; - } - - bool shouldRecenter = _settings.EnableRecentering && _timeSinceLastInput >= _settings.RecenterDelay; - - if (hasInput) - { - _targetYaw = _originalCameraRotation.x + panX * _settings.MaxPanAngleX; - _targetPitch = _originalCameraRotation.y - panY * _settings.MaxPanAngleY; - _targetYaw = Mathf.Clamp(_targetYaw, _originalCameraRotation.x - _settings.MaxPanAngleX, _originalCameraRotation.x + _settings.MaxPanAngleX); - _targetPitch = Mathf.Clamp(_targetPitch, _originalCameraRotation.y - _settings.MaxPanAngleY, _originalCameraRotation.y + _settings.MaxPanAngleY); - _lerpSpeed = _settings.PanSpeed; - _needsUpdate = true; - } - else if (shouldRecenter) - { - _targetYaw = _originalCameraRotation.x; - _targetPitch = _originalCameraRotation.y; - _lerpSpeed = _settings.RecenterSpeed; - _needsUpdate = true; - } - else if (_needsUpdate) - { - float yawDiff = Mathf.Abs(Mathf.DeltaAngle(_currentYaw, _targetYaw)); - float pitchDiff = Mathf.Abs(Mathf.DeltaAngle(_currentPitch, _targetPitch)); - - if (yawDiff < 0.01f && pitchDiff < 0.01f) - { - _needsUpdate = false; - } - } - } - - /// - /// Aligns the player and camera to the target object. - /// - Player moves to a fixed distance in front of the target - /// - Player rotates to face the target - /// - Camera pitch adjusts to look at target center - /// - public void AlignToTarget(Transform target, System.Action onComplete = null) - { - if (_alignmentCoroutine != null) - StopCoroutine(_alignmentCoroutine); - - _alignmentCoroutine = StartCoroutine(AlignCameraToTargetCoroutine(target, onComplete)); - } - - /// - /// Returns player and camera to original position and rotation. - /// - public void ReturnToOriginalPosition(System.Action onComplete = null) - { - if (_returnCoroutine != null) - StopCoroutine(_returnCoroutine); - - _returnCoroutine = StartCoroutine(ReturnToOriginalPositionCoroutine(onComplete)); - } - - /// - /// Saves current player and camera state as the original position. - /// Call this before starting alignment. - /// - public void SaveOriginalState() - { - if (_camera != null) - { - _originalCameraLocalPosition = _camera.transform.localPosition; - _originalCameraLocalRotation = _camera.transform.localEulerAngles; - } - - _originalPlayerPosition = _playerTransform.position; - _originalPlayerRotation = _playerTransform.eulerAngles; - } - - private IEnumerator AlignCameraToTargetCoroutine(Transform target, System.Action onComplete) - { - if (_camera == null || target == null || _settings == null) - { - onComplete?.Invoke(); - yield break; - } - - SaveOriginalState(); - - Vector3 targetCenter = target.position; - Renderer targetRenderer = target.GetComponent(); - if (targetRenderer != null) - { - targetCenter = targetRenderer.bounds.center; - } - else - { - Collider targetCollider = target.GetComponent(); - if (targetCollider != null) - targetCenter = targetCollider.bounds.center; - } - - Vector3 startPlayerPos = _playerTransform.position; - Vector3 startPlayerRotation = _playerTransform.eulerAngles; - Vector3 startCameraLocalRotation = _camera.transform.localEulerAngles; - - float startPitch = startCameraLocalRotation.x; - if (startPitch > 180f) - startPitch -= 360f; - - Vector3 targetForward = target.forward; - Vector3 targetPlayerPos = targetCenter - targetForward * _settings.FixedDistanceFromTarget; - targetPlayerPos.y = startPlayerPos.y; - - float elapsed = 0f; - float duration = 1f / _settings.AlignmentSpeed; - - while (elapsed < duration) - { - elapsed += Time.deltaTime; - float t = GetInterpolationValue(elapsed / duration); - - Vector3 currentCameraPos = _camera.transform.position; - Vector3 toTarget = targetCenter - currentCameraPos; - Vector3 directionToTarget = toTarget.normalized; - - Quaternion targetRotation = Quaternion.LookRotation(directionToTarget); - float targetYaw = targetRotation.eulerAngles.y; - float targetPitch = -Mathf.Asin(directionToTarget.y) * Mathf.Rad2Deg; - - Vector3 currentPlayerPos = Vector3.Lerp(startPlayerPos, targetPlayerPos, t); - _playerTransform.position = currentPlayerPos; - - float currentYaw = Mathf.LerpAngle(startPlayerRotation.y, targetYaw, t); - _playerTransform.eulerAngles = new Vector3(0f, currentYaw, 0f); - - float currentPitch = Mathf.LerpAngle(startPitch, targetPitch, t); - _camera.transform.localEulerAngles = new Vector3(currentPitch, 0f, 0f); - - yield return null; - } - - _alignmentCoroutine = null; - onComplete?.Invoke(); - } - - private IEnumerator ReturnToOriginalPositionCoroutine(System.Action onComplete) - { - if (_camera == null || _settings == null) - { - onComplete?.Invoke(); - yield break; - } - - Vector3 startPlayerPos = _playerTransform.position; - Vector3 startPlayerRotation = _playerTransform.eulerAngles; - Vector3 startCameraLocalPos = _camera.transform.localPosition; - Vector3 startCameraLocalRotation = _camera.transform.localEulerAngles; - - float startPitch = startCameraLocalRotation.x; - if (startPitch > 180f) - startPitch -= 360f; - - float targetPitch = _originalCameraLocalRotation.x; - if (targetPitch > 180f) - targetPitch -= 360f; - - float elapsed = 0f; - float duration = 1f / _settings.AlignmentSpeed; - - while (elapsed < duration) - { - elapsed += Time.deltaTime; - float t = GetInterpolationValue(elapsed / duration); - - _playerTransform.position = Vector3.Lerp(startPlayerPos, _originalPlayerPosition, t); - - float currentYaw = Mathf.LerpAngle(startPlayerRotation.y, _originalPlayerRotation.y, t); - _playerTransform.eulerAngles = new Vector3(0f, currentYaw, 0f); - - float currentPitch = Mathf.LerpAngle(startPitch, targetPitch, t); - _camera.transform.localEulerAngles = new Vector3(currentPitch, 0f, 0f); - - _camera.transform.localPosition = Vector3.Lerp(startCameraLocalPos, _originalCameraLocalPosition, t); - - yield return null; - } - - _playerTransform.position = _originalPlayerPosition; - _playerTransform.eulerAngles = _originalPlayerRotation; - _camera.transform.localPosition = _originalCameraLocalPosition; - _camera.transform.localEulerAngles = _originalCameraLocalRotation; - - _returnCoroutine = null; - onComplete?.Invoke(); - } - - private float GetInterpolationValue(float t) - { - if (_settings.AlignmentCurve != null && _settings.AlignmentCurve.length > 0) - return _settings.AlignmentCurve.Evaluate(t); - - if (_settings.UseSmoothStep) - return Mathf.SmoothStep(0f, 1f, t); - - return t; - } - } -} diff --git a/Assets/_Content/Scripts/World/Camera/Runtime/CameraPanController.cs.meta b/Assets/_Content/Scripts/World/Camera/Runtime/CameraPanController.cs.meta deleted file mode 100644 index 091cfce7..00000000 --- a/Assets/_Content/Scripts/World/Camera/Runtime/CameraPanController.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 69e8150bab500424eb366f0ac8761995 \ No newline at end of file diff --git a/Assets/_Content/Scripts/World/Camera/Runtime/DetectiveBoardVirtualCamera.cs b/Assets/_Content/Scripts/World/Camera/Runtime/DetectiveBoardVirtualCamera.cs deleted file mode 100644 index 1d07071c..00000000 --- a/Assets/_Content/Scripts/World/Camera/Runtime/DetectiveBoardVirtualCamera.cs +++ /dev/null @@ -1,430 +0,0 @@ -using UnityEngine; -using UnityEngine.InputSystem; -using Unheimlich.Features.Dialogue; -using Unheimlich.Core; - -namespace Unheimlich.World.CameraSystem -{ - /// - /// Virtual camera system for detective board inspection (Alan Wake 2 style). - /// Handles smooth transition, edge-based panning, and zoom. - /// - public class DetectiveBoardVirtualCamera : Singleton - { - [Header("References")] - [SerializeField] private Camera _virtualCamera; - private Transform _boardTarget; - - [Header("Transition Settings")] - [SerializeField] private float _transitionSpeed = 3f; - [SerializeField] private AnimationCurve _transitionCurve = AnimationCurve.EaseInOut(0f, 0f, 1f, 1f); - [SerializeField] private float _initialDistance = 1.5f; - - [Header("Pan Settings")] - [SerializeField] private float _panSpeed = 2f; - [SerializeField] private float _panSmoothTime = 0.2f; - [SerializeField][Range(0f, 0.5f)] private float _edgeBorderSize = 0.1f; - [SerializeField] private Vector2 _maxPanOffset = new Vector2(1.5f, 0.5f); - [SerializeField] private bool _useDynamicPanLimits = true; - [SerializeField] private float _panMargin = 0.0f; - [SerializeField] private bool _debugLimits = false; - - [Header("Zoom Settings")] - [SerializeField] private float _minDistance = 0.5f; - [SerializeField] private float _maxDistance = 3f; - // [SerializeField] private float _zoomSpeed = 2f; - [SerializeField] private float _zoomSmoothTime = 0.15f; - [SerializeField] private float _scrollSensitivity = 0.3f; - - private Camera _playerCamera; - private Transform _playerCameraTransform; - private Vector3 _startPosition; - private Quaternion _startRotation; - private Vector3 _targetCenterPosition; - private Quaternion _targetCenterRotation; - private bool _isActive; - private bool _isTransitioning; - private float _transitionProgress; - private Renderer _cachedBoardRenderer; - - private Vector2 _currentPanOffset; - private Vector2 _targetPanOffset; - private Vector2 _panVelocity; - private Vector2 _dynamicMaxPanOffset; - - private float _currentDistance; - private float _targetDistance; - private float _zoomVelocity; - - private Vector2 _lastMousePosition; - - public bool IsActive => _isActive; - public Camera VirtualCamera => _virtualCamera; - - protected override void OnAwake() - { - if (_virtualCamera == null) - _virtualCamera = GetComponent(); - - if (_virtualCamera != null) - _virtualCamera.enabled = false; - } - - public void ActivateFromPlayerPosition(Camera playerCamera, Transform boardTarget, Renderer boardRenderer = null) - { - if (_isActive) - return; - - Debug.Log($"[VirtualCam] ActivateFromPlayerPosition - DebugLimits: {_debugLimits}"); - - _playerCamera = playerCamera; - _playerCameraTransform = playerCamera.transform; - _boardTarget = boardTarget; - _cachedBoardRenderer = boardRenderer; - - _startPosition = _playerCameraTransform.position; - _startRotation = _playerCameraTransform.rotation; - - _virtualCamera.transform.position = _startPosition; - _virtualCamera.transform.rotation = _startRotation; - - CalculateTargetPosition(); - - _currentDistance = _initialDistance; - _targetDistance = _initialDistance; - _currentPanOffset = Vector2.zero; - _targetPanOffset = Vector2.zero; - - _isActive = true; - _isTransitioning = true; - _transitionProgress = 0f; - - _virtualCamera.enabled = true; - _playerCamera.enabled = false; - - Canvas boardCanvas = _boardTarget.GetComponentInChildren(); - if (boardCanvas != null && boardCanvas.renderMode == RenderMode.WorldSpace) - { - boardCanvas.worldCamera = _virtualCamera; - } - - if (Mouse.current != null) - _lastMousePosition = Mouse.current.position.ReadValue(); - } - - public void Activate(Camera playerCamera, Transform boardTarget) - { - if (_isActive) - return; - - _playerCamera = playerCamera; - _playerCameraTransform = playerCamera.transform; - _boardTarget = boardTarget; - - _startPosition = _playerCameraTransform.position; - _startRotation = _playerCameraTransform.rotation; - - CalculateTargetPosition(); - - _currentDistance = _initialDistance; - _targetDistance = _initialDistance; - _currentPanOffset = Vector2.zero; - _targetPanOffset = Vector2.zero; - - _isActive = true; - _isTransitioning = true; - _transitionProgress = 0f; - - _virtualCamera.enabled = true; - _playerCamera.enabled = false; - - Canvas boardCanvas = _boardTarget.GetComponentInChildren(); - if (boardCanvas != null && boardCanvas.renderMode == RenderMode.WorldSpace) - { - boardCanvas.worldCamera = _virtualCamera; - } - - if (Mouse.current != null) - _lastMousePosition = Mouse.current.position.ReadValue(); - } - - public void Deactivate(System.Action onComplete = null) - { - if (!_isActive) - { - onComplete?.Invoke(); - return; - } - - StartCoroutine(ReturnTransitionCoroutine(onComplete)); - } - - private void CalculateTargetPosition() - { - Vector3 boardCenter = _boardTarget.position; - Renderer renderer = _boardTarget.GetComponent(); - if (renderer != null) - boardCenter = renderer.bounds.center; - else - { - Collider collider = _boardTarget.GetComponent(); - if (collider != null) - boardCenter = collider.bounds.center; - } - - _targetCenterPosition = boardCenter - _boardTarget.forward * _initialDistance; - _targetCenterRotation = Quaternion.LookRotation(_boardTarget.forward); - } - - private void Update() - { - if (_debugLimits) - Debug.Log($"[VirtualCam] Update - Active: {_isActive} | Transitioning: {_isTransitioning}"); - - if (!_isActive) - return; - - if (_isTransitioning) - { - UpdateTransition(); - } - else - { - UpdatePanning(); - UpdateZoom(); - ApplyCameraTransform(); - } - } - - private void UpdateTransition() - { - _transitionProgress += Time.deltaTime * _transitionSpeed; - float t = _transitionCurve.Evaluate(Mathf.Clamp01(_transitionProgress)); - - Vector3 position = Vector3.Lerp(_startPosition, _targetCenterPosition, t); - Quaternion rotation = Quaternion.Slerp(_startRotation, _targetCenterRotation, t); - - _virtualCamera.transform.position = position; - _virtualCamera.transform.rotation = rotation; - - if (_transitionProgress >= 1f) - { - _isTransitioning = false; - } - } - - private void UpdatePanning() - { - float panX = 0f; - float panY = 0f; - bool hasInput = false; - - if (Mouse.current != null) - { - Vector2 mousePos = Mouse.current.position.ReadValue(); - _lastMousePosition = mousePos; - - float screenWidth = Screen.width; - float screenHeight = Screen.height; - float borderPixels = _edgeBorderSize * Mathf.Min(screenWidth, screenHeight); - - if (mousePos.x >= 0f && mousePos.x < borderPixels) - { - panX = -(borderPixels - mousePos.x) / borderPixels; - hasInput = true; - } - else if (mousePos.x > screenWidth - borderPixels && mousePos.x <= screenWidth) - { - panX = (mousePos.x - (screenWidth - borderPixels)) / borderPixels; - hasInput = true; - } - - if (mousePos.y >= 0f && mousePos.y < borderPixels) - { - panY = -(borderPixels - mousePos.y) / borderPixels; - hasInput = true; - } - else if (mousePos.y > screenHeight - borderPixels && mousePos.y <= screenHeight) - { - panY = (mousePos.y - (screenHeight - borderPixels)) / borderPixels; - hasInput = true; - } - } - - if (hasInput) - { - float deltaX = panX * _panSpeed * Time.deltaTime; - float deltaY = panY * _panSpeed * Time.deltaTime; - - _targetPanOffset.x += deltaX; - _targetPanOffset.y += deltaY; - } - - Vector2 maxOffset = _useDynamicPanLimits ? _dynamicMaxPanOffset : _maxPanOffset; - - _targetPanOffset.x = Mathf.Clamp(_targetPanOffset.x, -maxOffset.x, maxOffset.x); - _targetPanOffset.y = Mathf.Clamp(_targetPanOffset.y, -maxOffset.y, maxOffset.y); - - if (!hasInput && _useDynamicPanLimits) - { - if (Mathf.Abs(_targetPanOffset.x) > maxOffset.x) - { - float targetX = Mathf.Sign(_targetPanOffset.x) * maxOffset.x; - _targetPanOffset.x = Mathf.Lerp(_targetPanOffset.x, targetX, Time.deltaTime * 3f); - } - - if (Mathf.Abs(_targetPanOffset.y) > maxOffset.y) - { - float targetY = Mathf.Sign(_targetPanOffset.y) * maxOffset.y; - _targetPanOffset.y = Mathf.Lerp(_targetPanOffset.y, targetY, Time.deltaTime * 3f); - } - } - - _currentPanOffset = Vector2.SmoothDamp(_currentPanOffset, _targetPanOffset, ref _panVelocity, _panSmoothTime); - } - - private void UpdateZoom() - { - if (Mouse.current != null) - { - float scroll = Mouse.current.scroll.ReadValue().y; - if (Mathf.Abs(scroll) > 0.1f) - { - _targetDistance -= scroll * _scrollSensitivity * Time.deltaTime; - _targetDistance = Mathf.Clamp(_targetDistance, _minDistance, _maxDistance); - } - } - - _currentDistance = Mathf.SmoothDamp(_currentDistance, _targetDistance, ref _zoomVelocity, _zoomSmoothTime); - } - - private void ApplyCameraTransform() - { - if (_debugLimits) - Debug.Log($"[VirtualCam] ApplyCameraTransform called - BoardTarget: {(_boardTarget != null ? _boardTarget.name : "NULL")}"); - - Vector3 boardCenter = _boardTarget.position; - Renderer renderer = _cachedBoardRenderer; - if (renderer == null) - renderer = _boardTarget.GetComponent(); - - if (renderer != null) - boardCenter = renderer.bounds.center; - else - { - Collider collider = _boardTarget.GetComponent(); - if (collider != null) - boardCenter = collider.bounds.center; - } - - if (_debugLimits) - Debug.Log($"[VirtualCam] Renderer: {(renderer != null ? "Found" : "NULL")} | UseDynamicLimits: {_useDynamicPanLimits}"); - - Vector3 right = _boardTarget.right; - Vector3 up = _boardTarget.up; - Vector3 forward = _boardTarget.forward; - - if (_useDynamicPanLimits) - { - CalculateDynamicPanLimits(renderer); - } - - Vector3 panOffset = right * _currentPanOffset.x + up * _currentPanOffset.y; - Vector3 targetPos = boardCenter + panOffset - forward * _currentDistance; - - _virtualCamera.transform.position = targetPos; - _virtualCamera.transform.rotation = Quaternion.LookRotation(forward); - } - - private void CalculateDynamicPanLimits(Renderer boardRenderer) - { - if (_virtualCamera == null) - { - if (_debugLimits) - Debug.LogWarning("[VirtualCam] No VirtualCamera! Using fixed maxPanOffset."); - _dynamicMaxPanOffset = _maxPanOffset; - return; - } - - float distance = _currentDistance; - float fov = _virtualCamera.fieldOfView; - float aspect = _virtualCamera.aspect; - - float halfHeightAtDistance = distance * Mathf.Tan(fov * 0.5f * Mathf.Deg2Rad); - float halfWidthAtDistance = halfHeightAtDistance * aspect; - - float boardHalfWidth; - float boardHalfHeight; - - if (boardRenderer != null) - { - Bounds bounds = boardRenderer.bounds; - Vector3 localSize = _boardTarget.InverseTransformVector(bounds.size); - - boardHalfWidth = Mathf.Abs(localSize.x) * 0.5f; - boardHalfHeight = Mathf.Abs(localSize.y) * 0.5f; - } - else - { - RectTransform rectTransform = _boardTarget.GetComponent(); - if (rectTransform != null) - { - Vector2 size = rectTransform.rect.size; - Vector3 scale = rectTransform.lossyScale; - - boardHalfWidth = (size.x * scale.x) * 0.5f; - boardHalfHeight = (size.y * scale.y) * 0.5f; - } - else - { - if (_debugLimits) - Debug.LogWarning("[VirtualCam] No Renderer or RectTransform found! Using fixed maxPanOffset."); - _dynamicMaxPanOffset = _maxPanOffset; - return; - } - } - - _dynamicMaxPanOffset.x = Mathf.Max(0f, boardHalfWidth - halfWidthAtDistance - _panMargin); - _dynamicMaxPanOffset.y = Mathf.Max(0f, boardHalfHeight - halfHeightAtDistance - _panMargin); - - if (_debugLimits) - { - Debug.Log($"[VirtualCam] Distance: {distance:F2} | FOV: {fov:F1} | Aspect: {aspect:F2}"); - Debug.Log($"[VirtualCam] Viewport: {halfWidthAtDistance*2:F2}x{halfHeightAtDistance*2:F2} | Board: {boardHalfWidth*2:F2}x{boardHalfHeight*2:F2}"); - Debug.Log($"[VirtualCam] MaxPan: X={_dynamicMaxPanOffset.x:F2} Y={_dynamicMaxPanOffset.y:F2} | Current: X={_targetPanOffset.x:F2} Y={_targetPanOffset.y:F2}"); - } - } - - private System.Collections.IEnumerator ReturnTransitionCoroutine(System.Action onComplete) - { - Vector3 currentPos = _virtualCamera.transform.position; - Quaternion currentRot = _virtualCamera.transform.rotation; - - float elapsed = 0f; - float duration = 1f / _transitionSpeed; - - while (elapsed < duration) - { - elapsed += Time.deltaTime; - float t = _transitionCurve.Evaluate(Mathf.Clamp01(elapsed / duration)); - - _virtualCamera.transform.position = Vector3.Lerp(currentPos, _startPosition, t); - _virtualCamera.transform.rotation = Quaternion.Slerp(currentRot, _startRotation, t); - - yield return null; - } - - _virtualCamera.enabled = false; - _playerCamera.enabled = true; - _isActive = false; - - Canvas boardCanvas = _boardTarget.GetComponentInChildren(); - if (boardCanvas != null && boardCanvas.renderMode == RenderMode.WorldSpace) - { - boardCanvas.worldCamera = _playerCamera; - } - - onComplete?.Invoke(); - } - } -} diff --git a/Assets/_Content/Scripts/World/Camera/Runtime/DetectiveBoardVirtualCamera.cs.meta b/Assets/_Content/Scripts/World/Camera/Runtime/DetectiveBoardVirtualCamera.cs.meta deleted file mode 100644 index 595c9747..00000000 --- a/Assets/_Content/Scripts/World/Camera/Runtime/DetectiveBoardVirtualCamera.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: df440cd4d3cc30d4e9f5b44ab4931a12 \ No newline at end of file diff --git a/Assets/_Content/Scripts/World/Environment.meta b/Assets/_Content/Scripts/World/Environment.meta deleted file mode 100644 index 5dbdda42..00000000 --- a/Assets/_Content/Scripts/World/Environment.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: b29704286be24904999abcd0ff0cc448 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Content/Scripts/World/Environment/DoorInteractable.cs b/Assets/_Content/Scripts/World/Environment/DoorInteractable.cs deleted file mode 100644 index 3a5f2a6b..00000000 --- a/Assets/_Content/Scripts/World/Environment/DoorInteractable.cs +++ /dev/null @@ -1,49 +0,0 @@ -using UnityEngine; -using Unheimlich.Core.Interaction; -using Unheimlich.Core.Utils; - -namespace Unheimlich.World.Environment -{ - public class DoorInteractable : InteractableBase - { - [Header("Door Settings")] - public bool IsOpen = false; - [SerializeField] private float _openAngle = 90f; - [SerializeField] private float _animationDuration = 0.5f; - [SerializeField] private LeanTweenType _easeType = LeanTweenType.easeInOutQuad; - - private Quaternion _closedRotation; - private Quaternion _openRotation; - - protected virtual void Start() - { - _closedRotation = transform.rotation; - _openRotation = _closedRotation * Quaternion.Euler(0, _openAngle, 0); - - InteractionText = IsOpen ? "Close" : "Open"; - } - - public override void Interact() - { - ToggleDoor(); - } - - public void ToggleDoor() - { - gameObject.CancelAllTweens(); - - if (!IsOpen) - { - TweenHelper.RotateLocal(gameObject, _openRotation.eulerAngles, _animationDuration, null, _easeType); - IsOpen = true; - InteractionText = "Close"; - } - else - { - TweenHelper.RotateLocal(gameObject, _closedRotation.eulerAngles, _animationDuration, null, _easeType); - IsOpen = false; - InteractionText = "Open"; - } - } - } -} diff --git a/Assets/_Content/Scripts/World/Environment/DoorInteractable.cs.meta b/Assets/_Content/Scripts/World/Environment/DoorInteractable.cs.meta deleted file mode 100644 index 246b459c..00000000 --- a/Assets/_Content/Scripts/World/Environment/DoorInteractable.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: d620ab476d55f2542a31783a6684f189 \ No newline at end of file diff --git a/Assets/_Content/Scripts/World/Environment/InfiniteStaircase.meta b/Assets/_Content/Scripts/World/Environment/InfiniteStaircase.meta deleted file mode 100644 index bc093c86..00000000 --- a/Assets/_Content/Scripts/World/Environment/InfiniteStaircase.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 581e1488df9d96149925217dbedad049 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Data.meta b/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Data.meta deleted file mode 100644 index 134ec90d..00000000 --- a/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Data.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: c9e77f5824725bb49a41c5775a81bf2b -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Data/StaircaseSlotData.cs b/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Data/StaircaseSlotData.cs deleted file mode 100644 index beef646f..00000000 --- a/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Data/StaircaseSlotData.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Collections.Generic; -using UnityEngine; -using Unheimlich.Features.Hallucination; - -namespace Unheimlich.World.Environment -{ - /// - /// Configuration for a single stair slot. - /// Slot indices are relative: 0 = ground level, positive = up, negative = down. - /// - [System.Serializable] - public class StaircaseSlotData - { - [Tooltip("Slot index this data applies to. 0 = ground, positive = up, negative = down.")] - public int SlotIndex; - - [Tooltip("Word displayed on the FloatingLabel of this segment.")] - public string Word; - - [Tooltip("Optional hallucination event fired when the player enters this segment's zone.")] - public HallucinationEvent HallucinationEvent; - } - - /// - /// ScriptableObject holding the full word/event sequence for one staircase. - /// Each slot entry binds a display word and an optional hallucination event to a given slot index. - /// Slots not listed here receive no word and fire no event. - /// - /// - /// Create via Assets › Create › Unheimlich › Hallucination › Staircase Sequence Data. - /// - [CreateAssetMenu( - fileName = "StaircaseSequenceData", - menuName = "Unheimlich/Hallucination/Staircase Sequence Data")] - public class StaircaseSequenceData : ScriptableObject - { - [Tooltip("Per-slot configuration. Slots not listed here get no word and no event.")] - public List Slots = new(); - } -} \ No newline at end of file diff --git a/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Data/StaircaseSlotData.cs.meta b/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Data/StaircaseSlotData.cs.meta deleted file mode 100644 index d84ab769..00000000 --- a/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Data/StaircaseSlotData.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 7f9be4fbaead86548b21371bf4598477 \ No newline at end of file diff --git a/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Runtime.meta b/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Runtime.meta deleted file mode 100644 index 1cef8880..00000000 --- a/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Runtime.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 18369b1856c5f624cb3fd42a13f0292c -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Runtime/InfiniteStaircaseController.cs b/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Runtime/InfiniteStaircaseController.cs deleted file mode 100644 index 0aef4383..00000000 --- a/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Runtime/InfiniteStaircaseController.cs +++ /dev/null @@ -1,516 +0,0 @@ -using System.Collections.Generic; -using UnityEngine; -using Unheimlich.Features.Player; -using Unheimlich.Features.Hallucination; - -namespace Unheimlich.World.Environment -{ - // ─── Controller ─────────────────────────────────────────────────────────────── - - public class InfiniteStaircaseController : MonoBehaviour - { - // ─── Prefabs ───────────────────────────────────────────────────────── - - [Header("Stair Prefabs")] - [Tooltip("Standard segment used to fill the tower.")] - [SerializeField] private GameObject _stairPrefab; - - [Tooltip("Segment always placed at the very TOP of the tower (slot +maxIterationsUp).")] - [SerializeField] private GameObject _stairTopPrefab; - - [Tooltip("Segment always placed at the very BOTTOM of the tower (slot -maxIterationsDown).")] - [SerializeField] private GameObject _stairBottomPrefab; - - [Tooltip("If true, the first child with renderers is reused as slot 0 instead of being instantiated from the prefab.")] - [SerializeField] private bool _useExistingChildAsBase = true; - - // ─── Iterations ────────────────────────────────────────────────────── - - [Header("Iterations")] - [Tooltip("Number of segments above ground level (upward traversal).")] - [SerializeField] private int _maxIterationsUp = 4; - - [Tooltip("Number of segments below ground level (downward traversal).")] - [SerializeField] private int _maxIterationsDown = 4; - - // ─── Segment Bounds ────────────────────────────────────────────────── - - [Header("Segment Bounds — Auto-Detection")] - [Tooltip("Renderer of the central column mesh. When assigned, segment height is read from its world bounds.")] - [SerializeField] private Renderer _centralColumnRenderer; - - [Tooltip("Fallback segment height (m) used when no central column renderer is assigned.")] - [SerializeField] private float _segmentHeight = 4.82f; - - [Tooltip("Y offset added to the segment height for fine-tuning snapping.")] - [SerializeField] private float _segmentHeightOffset = 0f; - - // ─── Scroll Control ─────────────────────────────────────────────────── - - [Header("Scroll Speed Control")] - [Tooltip("Proportional gain: scroll speed (m/s) per metre of player deviation from the trigger center.")] - [SerializeField] private float _correctionGain = 4f; - - [Tooltip("Maximum tower scroll speed in m/s.")] - [SerializeField] private float _maxScrollSpeed = 8f; - - [Tooltip("SmoothDamp time for scroll speed transitions — prevents sudden jolts.")] - [SerializeField] private float _scrollSmoothTime = 0.1f; - - // ─── Rotation Offset ───────────────────────────────────────────────── - - [Header("Rotation Offset")] - [Tooltip("Y rotation applied to every spawned segment around its geometric bounds center.")] - [SerializeField] private float _segmentRotationOffsetY = 0f; - - // ─── Hallucination / Word Data ──────────────────────────────────────── - - [Header("Hallucination — Word & Event Data")] - [Tooltip("ScriptableObject holding word + event data per slot. " + - "Recommended: create a StaircaseSequenceData asset and assign it here.")] - [SerializeField] private StaircaseSequenceData _sequenceData; - - [Space] - [Tooltip("When true, the word for each slot is picked randomly from _randomWordPool instead of from _sequenceData.")] - [SerializeField] private bool _useRandomWords = false; - - [Tooltip("Pool of words to pick from when _useRandomWords is enabled.")] - [SerializeField] private List _randomWordPool = new(); - - [Tooltip("HallucinationEvent applied to all slots when _useRandomWords is enabled " + - "(slot-specific events from _sequenceData are ignored in random mode).")] - [SerializeField] private HallucinationEvent _randomModeEvent; - - // ─── Debug ─────────────────────────────────────────────────────────── - - [Header("Debug")] - [SerializeField] private bool _drawGizmos = true; - - // ─── State ─────────────────────────────────────────────────────────── - - private enum StairState { Idle, Active, Stopping, Done } - private StairState _currentState = StairState.Idle; - - private Transform _playerTransform; - - /// Single parent moved to scroll the entire tower at once. - private Transform _towerRoot; - - /// World Y of the trigger collider center, cached on Awake. - private float _triggerCenterWorldY; - - /// True when the player entered the trigger from below (travelling upward). - private bool _enteredFromBelow; - - /// - /// towerRoot.localPosition.y threshold signalling stock exhaustion. - /// Negative when going up, positive when going down. - /// - private float _limitLocalY; - - /// Current scroll speed (signed). Positive means the tower moves downward. - private float _scrollSpeed; - private float _scrollSpeedVelocity; - - private readonly Dictionary _segmentsBySlot = new(); - - // ─── Properties ────────────────────────────────────────────────────── - - private float EffectiveSegmentHeight => _segmentHeight + _segmentHeightOffset; - private float MaxTowerDisplacementUp => _maxIterationsUp * EffectiveSegmentHeight; - private float MaxTowerDisplacementDown => _maxIterationsDown * EffectiveSegmentHeight; - - // ─── Lifecycle ─────────────────────────────────────────────────────── - - private void Awake() - { - AutoDetectBoundsFromColumn(); - CacheTriggerCenterY(); - } - - private void Update() - { - EnsurePlayerCached(); - - switch (_currentState) - { - case StairState.Active: - UpdateScrollSpeed(); - ApplyScrollToTower(); - CheckLimitReached(); - break; - - case StairState.Stopping: - DecelerateAndFinish(); - break; - } - } - - // ─── Trigger Events ─────────────────────────────────────────────────── - - /// - /// Called by StaircaseTriggerZone when the player enters the collider. - /// - public void OnPlayerEnteredTrigger() - { - if (_currentState != StairState.Idle && _currentState != StairState.Done) return; - - EnsurePlayerCached(); - if (_playerTransform == null) return; - - _enteredFromBelow = _playerTransform.position.y < _triggerCenterWorldY; - _scrollSpeed = 0f; - _scrollSpeedVelocity = 0f; - - BuildTower(); - _currentState = StairState.Active; - } - - /// - /// Called by StaircaseTriggerZone when the player exits the collider. - /// - public void OnPlayerExitedTrigger() - { - if (_currentState != StairState.Active && _currentState != StairState.Stopping) return; - - bool towerHardlyMoved = _towerRoot != null && - Mathf.Abs(_towerRoot.localPosition.y) < EffectiveSegmentHeight * 0.5f; - - if (towerHardlyMoved) - { - _segmentsBySlot.TryGetValue(0, out GameObject baseSegment); - - if (baseSegment != null) - { - baseSegment.transform.SetParent(transform, true); - baseSegment.transform.position = transform.position; - } - - DestroyTower(); - - if (baseSegment != null) - _segmentsBySlot[0] = baseSegment; - - _scrollSpeed = 0f; - _scrollSpeedVelocity = 0f; - _playerTransform = null; - _currentState = StairState.Idle; - } - else - { - _currentState = StairState.Stopping; - } - } - - // ─── Tower Construction ─────────────────────────────────────────────── - - private void BuildTower() - { - DestroyTower(); - - _towerRoot = new GameObject("_TowerRoot").transform; - _towerRoot.SetParent(transform, false); - _towerRoot.localPosition = Vector3.zero; - _towerRoot.localRotation = Quaternion.identity; - _towerRoot.localScale = Vector3.one; - - if (_enteredFromBelow) - { - for (int i = 0; i <= _maxIterationsUp; i++) - SpawnSegmentAtSlot(i); - - _limitLocalY = -MaxTowerDisplacementUp; - } - else - { - for (int i = 0; i >= -_maxIterationsDown; i--) - SpawnSegmentAtSlot(i); - - _limitLocalY = MaxTowerDisplacementDown; - } - } - - private void DestroyTower() - { - if (_towerRoot != null) - { - Destroy(_towerRoot.gameObject); - _towerRoot = null; - } - _segmentsBySlot.Clear(); - } - - private void SpawnSegmentAtSlot(int slotIndex) - { - if (_segmentsBySlot.ContainsKey(slotIndex)) return; - - float localY = slotIndex * EffectiveSegmentHeight; - GameObject segment; - - // Slot 0 — attempt to reuse the existing scene child. - if (slotIndex == 0 && _useExistingChildAsBase) - { - Transform existing = FindExistingStairChild(); - if (existing != null) - { - segment = existing.gameObject; - segment.transform.SetParent(_towerRoot, false); - segment.transform.localPosition = new Vector3(0f, localY, 0f); - segment.transform.localRotation = Quaternion.identity; - ApplyRotationOffsetFromCenter(segment); - ConfigureSegment(segment, slotIndex); - _segmentsBySlot[slotIndex] = segment; - return; - } - } - - GameObject prefabToUse = PickPrefabForSlot(slotIndex); - if (prefabToUse == null) return; - - segment = Instantiate(prefabToUse, _towerRoot); - segment.transform.localPosition = new Vector3(0f, localY, 0f); - segment.transform.localRotation = Quaternion.identity; - ApplyRotationOffsetFromCenter(segment); - ConfigureSegment(segment, slotIndex); - _segmentsBySlot[slotIndex] = segment; - } - - private GameObject PickPrefabForSlot(int slotIndex) - { - if (slotIndex == _maxIterationsUp && _stairTopPrefab != null) return _stairTopPrefab; - if (slotIndex == -_maxIterationsDown && _stairBottomPrefab != null) return _stairBottomPrefab; - return _stairPrefab; - } - - // ─── Segment Configuration ──────────────────────────────────────────── - - /// - /// Resolves the word and event for this slot then calls StaircaseSegment.Configure(). - /// If the segment has no StaircaseSegment component, this is a silent no-op. - /// - private void ConfigureSegment(GameObject segmentGO, int slotIndex) - { - StaircaseSegment segment = segmentGO.GetComponent(); - if (segment == null) return; - - string word = ResolveWord(slotIndex); - HallucinationEvent evt = ResolveEvent(slotIndex); - segment.Configure(word, evt); - } - - private string ResolveWord(int slotIndex) - { - if (_useRandomWords) - { - if (_randomWordPool == null || _randomWordPool.Count == 0) - return null; - return _randomWordPool[Random.Range(0, _randomWordPool.Count)]; - } - - return FindSlotData(slotIndex)?.Word; - } - - private HallucinationEvent ResolveEvent(int slotIndex) - { - if (_useRandomWords) - return _randomModeEvent; - - return FindSlotData(slotIndex)?.HallucinationEvent; - } - - /// Returns the StaircaseSlotData matching slotIndex, or null if not found. - private StaircaseSlotData FindSlotData(int slotIndex) - { - if (_sequenceData == null) return null; - - foreach (StaircaseSlotData data in _sequenceData.Slots) - { - if (data.SlotIndex == slotIndex) - return data; - } - return null; - } - - // ─── Scroll ─────────────────────────────────────────────────────────── - - private void UpdateScrollSpeed() - { - if (_playerTransform == null) return; - - float error = _playerTransform.position.y - _triggerCenterWorldY; - float rawTarget = Mathf.Clamp(error * _correctionGain, -_maxScrollSpeed, _maxScrollSpeed); - - bool towerHasMoved = _enteredFromBelow - ? _towerRoot != null && _towerRoot.localPosition.y < -0.05f - : _towerRoot != null && _towerRoot.localPosition.y > 0.05f; - - float targetSpeed = towerHasMoved - ? rawTarget - : (_enteredFromBelow ? Mathf.Max(rawTarget, 0f) : Mathf.Min(rawTarget, 0f)); - - _scrollSpeed = Mathf.SmoothDamp(_scrollSpeed, targetSpeed, ref _scrollSpeedVelocity, _scrollSmoothTime); - } - - private void ApplyScrollToTower() - { - if (_towerRoot == null) return; - - Vector3 pos = _towerRoot.localPosition; - pos.y -= _scrollSpeed * Time.deltaTime; - - pos.y = _enteredFromBelow - ? Mathf.Max(pos.y, _limitLocalY) - : Mathf.Min(pos.y, _limitLocalY); - - _towerRoot.localPosition = pos; - } - - private void CheckLimitReached() - { - if (_towerRoot == null) return; - - bool reached = _enteredFromBelow - ? _towerRoot.localPosition.y <= _limitLocalY - : _towerRoot.localPosition.y >= _limitLocalY; - - if (reached) - _currentState = StairState.Stopping; - } - - private void DecelerateAndFinish() - { - _scrollSpeed = Mathf.SmoothDamp(_scrollSpeed, 0f, ref _scrollSpeedVelocity, _scrollSmoothTime); - ApplyScrollToTower(); - - if (Mathf.Abs(_scrollSpeed) < 0.02f) - FinishAndCleanup(); - } - - // ─── Cleanup ───────────────────────────────────────────────────────── - - private void FinishAndCleanup() - { - _currentState = StairState.Done; - if (_towerRoot == null) return; - - int keepSlot = _enteredFromBelow ? _maxIterationsUp : -_maxIterationsDown; - _segmentsBySlot.TryGetValue(keepSlot, out GameObject keepSegment); - - if (keepSegment != null) - { - keepSegment.transform.SetParent(transform, true); - keepSegment.transform.position = transform.position; - } - - DestroyTower(); - - if (keepSegment != null) - _segmentsBySlot[0] = keepSegment; - - _scrollSpeed = 0f; - _scrollSpeedVelocity = 0f; - _playerTransform = null; - } - - // ─── Helpers ───────────────────────────────────────────────────────── - - private void AutoDetectBoundsFromColumn() - { - if (_centralColumnRenderer == null) return; - _segmentHeight = _centralColumnRenderer.bounds.size.y; - } - - private void CacheTriggerCenterY() - { - StaircaseTriggerZone zone = GetComponentInChildren(); - if (zone == null) - { - Debug.LogWarning("[InfiniteStaircase] No StaircaseTriggerZone found in children.", this); - return; - } - - Collider col = zone.GetComponent(); - if (col != null) - _triggerCenterWorldY = col.bounds.center.y; - } - - private void EnsurePlayerCached() - { - if (_playerTransform != null) return; - if (PlayerController.Instance != null) - _playerTransform = PlayerController.Instance.transform; - } - - private Transform FindExistingStairChild() - { - foreach (Transform child in transform) - { - if (child == _towerRoot) continue; - if (child.GetComponentInChildren() != null) - return child; - } - return null; - } - - private void ApplyRotationOffsetFromCenter(GameObject segment) - { - if (Mathf.Approximately(_segmentRotationOffsetY, 0f)) return; - segment.transform.RotateAround(ComputeWorldBoundsCenter(segment), Vector3.up, _segmentRotationOffsetY); - } - - private Vector3 ComputeWorldBoundsCenter(GameObject segment) - { - Renderer[] renderers = segment.GetComponentsInChildren(); - if (renderers.Length == 0) - return segment.transform.position + Vector3.up * (EffectiveSegmentHeight * 0.5f); - - Bounds combined = renderers[0].bounds; - for (int i = 1; i < renderers.Length; i++) - combined.Encapsulate(renderers[i].bounds); - - return combined.center; - } - - // ─── Gizmos ────────────────────────────────────────────────────────── - - private void OnDrawGizmosSelected() - { - if (!_drawGizmos) return; - - float h = EffectiveSegmentHeight; - Vector3 o = transform.position; - - Gizmos.color = new Color(0f, 1f, 0.5f, 0.35f); - for (int i = 1; i <= _maxIterationsUp; i++) - Gizmos.DrawWireCube(new Vector3(o.x, o.y + i * h + h * 0.5f, o.z), new Vector3(1.8f, h * 0.95f, 1.8f)); - - Gizmos.color = new Color(1f, 0.6f, 0f, 0.25f); - for (int i = 1; i <= _maxIterationsDown; i++) - Gizmos.DrawWireCube(new Vector3(o.x, o.y - i * h + h * 0.5f, o.z), new Vector3(1.8f, h * 0.95f, 1.8f)); - - Gizmos.color = Color.cyan; - Gizmos.DrawLine( - new Vector3(o.x - 2f, _triggerCenterWorldY, o.z), - new Vector3(o.x + 2f, _triggerCenterWorldY, o.z) - ); - - if (_stairTopPrefab != null) - { - Gizmos.color = Color.magenta; - Gizmos.DrawWireCube( - new Vector3(o.x, o.y + _maxIterationsUp * h + h * 0.5f, o.z), - new Vector3(2f, h, 2f) - ); - } - - if (_stairBottomPrefab != null) - { - Gizmos.color = Color.red; - Gizmos.DrawWireCube( - new Vector3(o.x, o.y - _maxIterationsDown * h + h * 0.5f, o.z), - new Vector3(2f, h, 2f) - ); - } - } - } -} \ No newline at end of file diff --git a/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Runtime/InfiniteStaircaseController.cs.meta b/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Runtime/InfiniteStaircaseController.cs.meta deleted file mode 100644 index f4b914a9..00000000 --- a/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Runtime/InfiniteStaircaseController.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 3462960ec1dfaee448d860de31230d02 \ No newline at end of file diff --git a/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Runtime/StaircaseSegment.cs b/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Runtime/StaircaseSegment.cs deleted file mode 100644 index d04d2e55..00000000 --- a/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Runtime/StaircaseSegment.cs +++ /dev/null @@ -1,53 +0,0 @@ -using UnityEngine; -using Unheimlich.Features.Hallucination; -using Unheimlich.Features.Dialogue.UI; - -namespace Unheimlich.World.Environment -{ - /// - /// Sits on each stair-segment prefab and acts as the single configuration point - /// for the InfiniteStaircaseController. - /// - /// Responsibilities: - /// - Holds a reference to the segment's FloatingLabel (set in prefab). - /// - Holds a reference to the segment's HallucinationTriggerZone (set in prefab). - /// - Exposes Configure() so the controller can push the right word and event - /// at spawn time without any GetComponent chains on the controller side. - /// - public class StaircaseSegment : MonoBehaviour - { - [Header("References")] - [Tooltip("FloatingLabel present on this prefab. Assign in the prefab inspector.")] - [SerializeField] private FloatingLabel _floatingLabel; - - [Tooltip("HallucinationTriggerZone present on this prefab. Assign in the prefab inspector.")] - [SerializeField] private HallucinationTriggerZone _triggerZone; - - // ─── Public API ──────────────────────────────────────────────────────── - - /// - /// Called by InfiniteStaircaseController right after this segment is spawned. - /// Sets the word on the FloatingLabel and overrides the HallucinationEvent on the zone. - /// - /// Text to display. Null or empty = label keeps its prefab default. - /// Event to fire when the player enters the zone. Null = no event. - public void Configure(string word, HallucinationEvent hallucinationEvent) - { - if (_floatingLabel != null && !string.IsNullOrEmpty(word)) - _floatingLabel.SetText(word); - - if (_triggerZone != null) - _triggerZone.SetEvent(hallucinationEvent); - } - - // ─── Editor helpers ──────────────────────────────────────────────────── - -#if UNITY_EDITOR - private void Reset() - { - _floatingLabel = GetComponentInChildren(); - _triggerZone = GetComponentInChildren(); - } -#endif - } -} diff --git a/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Runtime/StaircaseSegment.cs.meta b/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Runtime/StaircaseSegment.cs.meta deleted file mode 100644 index 09b636bc..00000000 --- a/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Runtime/StaircaseSegment.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: ba6fa2b368d1e4f4bb84bc6af9f777bc \ No newline at end of file diff --git a/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Runtime/StaircaseTriggerZone.cs b/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Runtime/StaircaseTriggerZone.cs deleted file mode 100644 index 534cbe38..00000000 --- a/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Runtime/StaircaseTriggerZone.cs +++ /dev/null @@ -1,40 +0,0 @@ -using UnityEngine; - -namespace Unheimlich.World.Environment -{ - /// - /// Placed on the trigger child of an . - /// Forwards Player trigger enter / exit events to the parent controller. - /// - [RequireComponent(typeof(Collider))] - public class StaircaseTriggerZone : MonoBehaviour - { - private const string PlayerTag = "Player"; - - private InfiniteStaircaseController _controller; - - private void Awake() - { - _controller = GetComponentInParent(); - - if (_controller == null) - Debug.LogWarning($"[StaircaseTriggerZone] No InfiniteStaircaseController found in parent of '{name}'.", this); - } - - private void OnTriggerEnter(Collider other) - { - if (_controller == null) return; - if (!other.CompareTag(PlayerTag)) return; - - _controller.OnPlayerEnteredTrigger(); - } - - private void OnTriggerExit(Collider other) - { - if (_controller == null) return; - if (!other.CompareTag(PlayerTag)) return; - - _controller.OnPlayerExitedTrigger(); - } - } -} diff --git a/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Runtime/StaircaseTriggerZone.cs.meta b/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Runtime/StaircaseTriggerZone.cs.meta deleted file mode 100644 index 4aca71a6..00000000 --- a/Assets/_Content/Scripts/World/Environment/InfiniteStaircase/Runtime/StaircaseTriggerZone.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 89e1fa0fefb27a245bf290633fa7fdb3 \ No newline at end of file diff --git a/Assets/_Content/Scripts/World/Environment/SecurityCameraController.cs b/Assets/_Content/Scripts/World/Environment/SecurityCameraController.cs deleted file mode 100644 index 12f1da25..00000000 --- a/Assets/_Content/Scripts/World/Environment/SecurityCameraController.cs +++ /dev/null @@ -1,99 +0,0 @@ -using UnityEngine; -using Unheimlich.Features.Player; - -namespace Unheimlich.World.Environment -{ - -public class SecurityCameraController : MonoBehaviour -{ - [Header("Tracking Settings")] - [SerializeField] private float _trackingDistance = 10f; - [SerializeField] private float _rotationSpeed = 2f; - [SerializeField] private Transform _pivotPoint; - - [Header("LED Settings")] - [SerializeField] private MeshRenderer _ledRenderer; - [SerializeField] private float _blinkSpeed = 2f; - [SerializeField] private float _minSmoothness = 0f; - [SerializeField] private float _maxSmoothness = 0.5f; - - private CharacterController _playerController; - private Material _ledMaterial; - private float _blinkTimer; - private bool _isTracking; - - void Start() - { - if (PlayerController.Instance != null) - _playerController = PlayerController.Instance.GetComponent(); - - if (_ledRenderer != null) - { - _ledMaterial = _ledRenderer.material; - } - - if (_pivotPoint == null) - _pivotPoint = transform; - } - - void Update() - { - if (_playerController == null) return; - - float distanceToPlayer = Vector3.Distance(_pivotPoint.position, _playerController.transform.position); - _isTracking = distanceToPlayer <= _trackingDistance; - - if (_isTracking) - { - TrackPlayer(); - } - - UpdateLedBlink(); - } - - void TrackPlayer() - { - Vector3 directionToPlayer = _playerController.transform.position - _pivotPoint.position; - - if (directionToPlayer.sqrMagnitude > 0.001f) - { - Vector3 forward = -directionToPlayer.normalized; - Vector3 right = Vector3.Cross(Vector3.up, forward).normalized; - Vector3 up = Vector3.Cross(forward, right); - - Quaternion targetRotation = Quaternion.LookRotation(up, forward); - _pivotPoint.rotation = Quaternion.Slerp(_pivotPoint.rotation, targetRotation, Time.deltaTime * _rotationSpeed); - } - } - - void UpdateLedBlink() - { - if (_ledMaterial == null) return; - - _blinkTimer += Time.deltaTime * _blinkSpeed; - - float smoothness = Mathf.Lerp(_minSmoothness, _maxSmoothness, Mathf.PingPong(_blinkTimer, 1f)); - - _ledMaterial.SetFloat("_Smoothness", smoothness); - } - - void OnDestroy() - { - if (_ledMaterial != null) - Destroy(_ledMaterial); - } - - void OnDrawGizmosSelected() - { - if (_pivotPoint == null) - _pivotPoint = transform; - - Gizmos.color = Color.yellow; - Gizmos.DrawWireSphere(_pivotPoint.position, _trackingDistance); - - Gizmos.color = Color.red; - Gizmos.DrawLine(_pivotPoint.position, _pivotPoint.position + _pivotPoint.forward * 2f); - } -} - -} diff --git a/Assets/_Content/Scripts/World/Environment/SecurityCameraController.cs.meta b/Assets/_Content/Scripts/World/Environment/SecurityCameraController.cs.meta deleted file mode 100644 index 93634637..00000000 --- a/Assets/_Content/Scripts/World/Environment/SecurityCameraController.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 17a573927733b4343acc662995710bc2 \ No newline at end of file