314 lines
11 KiB
C#
314 lines
11 KiB
C#
using UnityEngine;
|
|
using System.Collections.Generic;
|
|
using Unheimlich.Data.Dialogues;
|
|
using Unheimlich.Gameplay.Player;
|
|
using OctoCore.Localization;
|
|
|
|
namespace Unheimlich.Interactions.FloatingWords
|
|
{
|
|
public class ObjectDescriptionTrigger : MonoBehaviour
|
|
{
|
|
[Header("Description")]
|
|
[Tooltip("Localized description text (recommended)")]
|
|
[SerializeField] private OctoLocalizedTextData _descriptionTextLocalized;
|
|
|
|
[Tooltip("Fallback description text if localized version is not set")]
|
|
[SerializeField] private string _descriptionText = "Mysterious Object";
|
|
|
|
[Header("Settings")]
|
|
[SerializeField] private ObjectDescriptionSettings _settings;
|
|
|
|
[Header("Anchor")]
|
|
[SerializeField] private Transform _descriptionAnchor;
|
|
|
|
[Header("Prefab")]
|
|
[SerializeField] private ChoiceFocusHandler _wordPrefab;
|
|
|
|
private Transform _playerTransform;
|
|
private Camera _mainCamera;
|
|
private List<ChoiceFocusHandler> _spawnedWords = new List<ChoiceFocusHandler>();
|
|
private GameObject _wordsContainer;
|
|
private GameObject _wordsRotator;
|
|
private bool _isActive = false;
|
|
private bool _isMerged = false;
|
|
private bool _hasBeenMergedOnce = false;
|
|
private bool _hasCompletedMergeOnce = false;
|
|
private Vector3[] _originalSpawnPositions;
|
|
private Vector3[] _basePositions;
|
|
private float _currentConvergenceRadius;
|
|
|
|
public bool IsAnimationActive => _isActive && (!_settings.MergeOnce || !_hasCompletedMergeOnce);
|
|
public bool IsInMergeOnceMode => _settings != null && _settings.MergeOnce;
|
|
|
|
void Start()
|
|
{
|
|
_playerTransform = Player.Instance.transform;
|
|
_mainCamera = Camera.main;
|
|
|
|
if (_playerTransform == null)
|
|
Debug.LogWarning($"[ObjectDescription] No player found for {gameObject.name}");
|
|
|
|
if (_descriptionAnchor == null)
|
|
_descriptionAnchor = transform;
|
|
|
|
if (_settings == null)
|
|
{
|
|
Debug.LogError($"[ObjectDescription] No settings assigned for {gameObject.name}");
|
|
enabled = false;
|
|
}
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
if (_playerTransform == null || _settings == null) return;
|
|
|
|
float distanceToPlayer = Vector3.Distance(_playerTransform.position, transform.position);
|
|
|
|
if (!_isActive && distanceToPlayer <= _settings.ActivationDistance)
|
|
{
|
|
if (!_settings.MergeOnce || !_hasCompletedMergeOnce)
|
|
SpawnWords();
|
|
}
|
|
else if (_isActive && distanceToPlayer >= _settings.DeactivationDistance)
|
|
DespawnWords();
|
|
|
|
if (_isActive)
|
|
{
|
|
if (!_settings.MergeOnce || !_hasBeenMergedOnce)
|
|
{
|
|
UpdateWordConvergence();
|
|
UpdateContainerRotation();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SpawnWords()
|
|
{
|
|
if (_wordPrefab == null)
|
|
{
|
|
Debug.LogError($"[ObjectDescription] No word prefab assigned for {gameObject.name}");
|
|
return;
|
|
}
|
|
|
|
_isActive = true;
|
|
_isMerged = false;
|
|
_currentConvergenceRadius = _settings.SpawnRadius;
|
|
|
|
_wordsContainer = new GameObject($"{_descriptionText}_Container");
|
|
_wordsContainer.transform.position = _descriptionAnchor.position;
|
|
_wordsContainer.transform.rotation = _descriptionAnchor.rotation;
|
|
|
|
_wordsRotator = FloatingWordsSpawner.CreateWordsContainer(_descriptionText, _descriptionAnchor, _settings);
|
|
_wordsRotator.transform.SetParent(_wordsContainer.transform, false);
|
|
|
|
ObjectDescriptionTriggerReference triggerRef = _wordsRotator.AddComponent<ObjectDescriptionTriggerReference>();
|
|
triggerRef.Trigger = this;
|
|
|
|
string descText = _descriptionTextLocalized != null
|
|
? OctoLocalization.GetLocalizedText(_descriptionTextLocalized)
|
|
: _descriptionText;
|
|
|
|
List<DialogueChoiceData> dummyChoices = new List<DialogueChoiceData>();
|
|
for (int i = 0; i < _settings.WordCount; i++)
|
|
{
|
|
dummyChoices.Add(new DialogueChoiceData
|
|
{
|
|
ChoiceText = _descriptionTextLocalized,
|
|
NextNode = null
|
|
});
|
|
}
|
|
|
|
_spawnedWords = FloatingWordsSpawner.SpawnByMode(
|
|
dummyChoices,
|
|
_wordPrefab,
|
|
_wordsRotator.transform,
|
|
_settings,
|
|
out _originalSpawnPositions
|
|
);
|
|
|
|
_basePositions = new Vector3[_spawnedWords.Count];
|
|
for (int i = 0; i < _spawnedWords.Count; i++)
|
|
_basePositions[i] = _spawnedWords[i].transform.localPosition;
|
|
}
|
|
|
|
private void DespawnWords()
|
|
{
|
|
_isActive = false;
|
|
_isMerged = false;
|
|
|
|
foreach (var word in _spawnedWords)
|
|
{
|
|
if (word != null)
|
|
Destroy(word.gameObject);
|
|
}
|
|
|
|
_spawnedWords.Clear();
|
|
|
|
if (_wordsContainer != null)
|
|
Destroy(_wordsContainer);
|
|
|
|
// Debug.Log($"[ObjectDescription] Despawned words for '{descriptionText}'");
|
|
}
|
|
|
|
private void UpdateWordConvergence()
|
|
{
|
|
if (_mainCamera == null || _spawnedWords.Count == 0) return;
|
|
|
|
Vector2 screenCenter = new Vector2(Screen.width / 2f, Screen.height / 2f);
|
|
Ray ray = _mainCamera.ScreenPointToRay(screenCenter);
|
|
RaycastHit hit;
|
|
|
|
int layerMask = ~LayerMask.GetMask("Words", "UI");
|
|
|
|
bool isHoveringObject = false;
|
|
|
|
if (Physics.Raycast(ray, out hit, 100f, layerMask))
|
|
{
|
|
if (hit.collider.gameObject == gameObject || hit.collider.transform.IsChildOf(transform))
|
|
isHoveringObject = true;
|
|
}
|
|
|
|
float targetRadius = isHoveringObject ? _settings.MinConvergenceRadius : _settings.SpawnRadius;
|
|
_currentConvergenceRadius = Mathf.Lerp(_currentConvergenceRadius, targetRadius, Time.deltaTime * _settings.ConvergenceSpeed);
|
|
|
|
if (isHoveringObject && !_isMerged && _currentConvergenceRadius <= _settings.MinConvergenceRadius * 5f)
|
|
MergeWords();
|
|
else if (!isHoveringObject && _isMerged)
|
|
{
|
|
if (!_settings.MergeOnce || !_hasBeenMergedOnce)
|
|
UnmergeWords();
|
|
}
|
|
|
|
if (_isMerged) return;
|
|
|
|
float convergenceFactor = Mathf.InverseLerp(0f, _settings.SpawnRadius, _currentConvergenceRadius);
|
|
|
|
for (int i = 0; i < _spawnedWords.Count; i++)
|
|
{
|
|
if (_spawnedWords[i] == null) continue;
|
|
|
|
Vector3 targetLocalPos = GetConvergenceTargetPosition(_originalSpawnPositions[i], _currentConvergenceRadius);
|
|
|
|
_basePositions[i] = Vector3.Lerp(
|
|
_basePositions[i],
|
|
targetLocalPos,
|
|
Time.deltaTime * _settings.ConvergenceSpeed
|
|
);
|
|
|
|
_spawnedWords[i].transform.localPosition = _basePositions[i] + _spawnedWords[i].GetShakeOffset(convergenceFactor);
|
|
}
|
|
}
|
|
|
|
private Vector3 GetConvergenceTargetPosition(Vector3 originalPosition, float radius)
|
|
{
|
|
if (radius < 0.01f) return Vector3.zero;
|
|
|
|
switch (_settings.RotationAxis)
|
|
{
|
|
case RotationAxis.Forward:
|
|
Vector2 xyNormalized = new Vector2(originalPosition.x, originalPosition.y - _settings.SpawnHeightOffset).normalized;
|
|
return new Vector3(
|
|
xyNormalized.x * radius,
|
|
xyNormalized.y * radius + _settings.SpawnHeightOffset,
|
|
originalPosition.z
|
|
);
|
|
|
|
case RotationAxis.Up:
|
|
Vector2 xzNormalized = new Vector2(originalPosition.x, originalPosition.z).normalized;
|
|
return new Vector3(
|
|
xzNormalized.x * radius,
|
|
originalPosition.y,
|
|
xzNormalized.y * radius
|
|
);
|
|
|
|
case RotationAxis.Right:
|
|
Vector2 yzNormalized = new Vector2(originalPosition.y - _settings.SpawnHeightOffset, originalPosition.z).normalized;
|
|
return new Vector3(
|
|
originalPosition.x,
|
|
yzNormalized.x * radius + _settings.SpawnHeightOffset,
|
|
yzNormalized.y * radius
|
|
);
|
|
|
|
default:
|
|
return originalPosition.normalized * radius;
|
|
}
|
|
}
|
|
|
|
private void MergeWords()
|
|
{
|
|
if (_isMerged) return;
|
|
|
|
_isMerged = true;
|
|
_hasBeenMergedOnce = true;
|
|
|
|
FloatingWordsSpawner.MergeWords(_spawnedWords, _settings, () =>
|
|
{
|
|
if (_settings.MergeOnce)
|
|
{
|
|
LeanTween.delayedCall(_settings.MergeOnceDisplayDuration, () =>
|
|
{
|
|
FadeOutAndComplete();
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
private void FadeOutAndComplete()
|
|
{
|
|
if (_spawnedWords.Count > 0 && _spawnedWords[0] != null)
|
|
{
|
|
CanvasGroup canvasGroup = _spawnedWords[0].GetComponent<CanvasGroup>();
|
|
if (canvasGroup != null)
|
|
{
|
|
LeanTween.value(_spawnedWords[0].gameObject, 1f, 0f, _settings.MergeDuration)
|
|
.setOnUpdate((float val) => { canvasGroup.alpha = val; })
|
|
.setEaseInQuad()
|
|
.setOnComplete(() =>
|
|
{
|
|
_hasCompletedMergeOnce = true;
|
|
DespawnWords();
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UnmergeWords()
|
|
{
|
|
if (!_isMerged) return;
|
|
|
|
_isMerged = false;
|
|
|
|
FloatingWordsSpawner.UnmergeWords(_spawnedWords, _settings);
|
|
}
|
|
|
|
private void UpdateContainerRotation()
|
|
{
|
|
FloatingWordsSpawner.UpdateContainerRotation(_wordsContainer, _wordsRotator, _playerTransform, _settings);
|
|
}
|
|
|
|
public ObjectDescriptionSettings GetSettings()
|
|
{
|
|
return _settings;
|
|
}
|
|
|
|
void OnDrawGizmosSelected()
|
|
{
|
|
if (_settings == null) return;
|
|
|
|
Transform anchor = _descriptionAnchor != null ? _descriptionAnchor : transform;
|
|
|
|
Gizmos.color = Color.yellow;
|
|
Gizmos.DrawWireSphere(transform.position, _settings.ActivationDistance);
|
|
|
|
Gizmos.color = Color.red;
|
|
Gizmos.DrawWireSphere(transform.position, _settings.DeactivationDistance);
|
|
|
|
Gizmos.color = Color.green;
|
|
Gizmos.DrawWireSphere(anchor.position, _settings.SpawnRadius);
|
|
|
|
Gizmos.color = Color.cyan;
|
|
Gizmos.DrawWireSphere(anchor.position, _settings.MinConvergenceRadius);
|
|
}
|
|
}
|
|
}
|