using System; using System.Collections.Generic; using UnityEngine; namespace YachtDice.Dice { public class Dice : MonoBehaviour { [Serializable] public struct Entry : IEquatable { [field: SerializeField] public int Value { get; private set; } [field: SerializeField] public Transform Point { get; private set; } public bool Equals(Entry other) => Point == other.Point; public override bool Equals(object obj) => obj is Entry other && Equals(other); public override int GetHashCode() => Point != null ? Point.GetHashCode() : 0; } [SerializeField] private List entries = new(); private HashSet _entrySet; private void Awake() => RebuildSet(); private void OnValidate() => RebuildSet(); private void RebuildSet() { _entrySet = new HashSet(); if (entries == null) return; foreach (var t in entries) _entrySet.Add(t); } public bool TryGetTopValue(out int value) => TryGetExtremeValue(isTop: true, out value); public bool TryGetBottomValue(out int value) => TryGetExtremeValue(isTop: false, out value); public int AlignToTopByLocalAngles() { var e = GetExtremeEntryByWorldY(isTop: true); AlignByFaceLocalAngles(e.Point); return e.Value; } public int AlignToBottomByLocalAngles() { var e = GetExtremeEntryByWorldY(isTop: false); AlignByFaceLocalAngles(e.Point); return e.Value; } private bool TryGetExtremeValue(bool isTop, out int value) { value = default; if (_entrySet == null || _entrySet.Count == 0) return false; var found = false; var bestY = isTop ? float.NegativeInfinity : float.PositiveInfinity; int best = default; foreach (var e in _entrySet) { var p = e.Point; if (!p) continue; float y = p.position.y; if (!found || (isTop ? y > bestY : y < bestY)) { found = true; bestY = y; best = e.Value; } } if (!found) return false; value = best; return true; } private Entry GetExtremeEntryByWorldY(bool isTop) { if (_entrySet == null || _entrySet.Count == 0) throw new InvalidOperationException("Dice: коллекция пуста."); var found = false; var bestY = isTop ? float.NegativeInfinity : float.PositiveInfinity; Entry best = default; foreach (var e in _entrySet) { var p = e.Point; if (!p) continue; var y = p.position.y; if (found && (isTop ? !(y > bestY) : !(y < bestY))) continue; found = true; bestY = y; best = e; } if (!found) throw new InvalidOperationException("Dice: все Transform == null."); return best; } private static float Norm180(float a) { a %= 360f; return a > 180f ? a - 360f : (a < -180f ? a + 360f : a); } private void AlignByFaceLocalAngles(Transform facePoint) { if (!facePoint) throw new InvalidOperationException("Dice: facePoint == null."); transform.localEulerAngles = Vector3.Scale(facePoint.localEulerAngles, new Vector3(-1f, -1f, -1f)); var e = transform.localEulerAngles; transform.localEulerAngles = new Vector3(Norm180(e.x), Norm180(e.y), Norm180(e.z)); } } }