using Cryville.Common; using Cryville.Crtr.Event; using System; using System.Collections.Generic; using System.Globalization; using UnityEngine; namespace Cryville.Crtr.Skin { public class EffectInstance : ISkinnableGroup, IComparable { readonly EffectDefinition _def; readonly SkinContainer _skinContainer; public Transform RootTransform { get; private set; } readonly SkinComponent[] _comps; public EffectInstance(EffectDefinition def) { _def = def; _skinContainer = new SkinContainer(this, _def.elements); RootTransform = new GameObject("effect:" + GetHashCode().ToString(CultureInfo.InvariantCulture)).transform; SkinContext = new SkinContext(RootTransform); PdtEvaluator.Instance.ContextCascadeInsertBlock(); _skinContainer.MatchStatic(); PdtEvaluator.Instance.ContextCascadeDiscardBlock(); _comps = RootTransform.GetComponentsInChildren(); foreach (var i in _comps) i.Init(); _durationOp = new PropOp.Float(v => _duration = v); } public void Rewind(double time, Transform target) { _startTime = time; foreach (var i in _comps) i.Rewind(time, target); } Transform _currentTarget; Identifier _currentStateName = Identifier.Empty; EffectState _currentState; ChartEvent _ctxev; ContainerState _ctxstate; internal static readonly int _VAR_EFFECT_INDEX = IdentifierManager.Shared.Request("effect_index"); readonly PropStores.Float _indexst = new(); public float Index { get { return _indexst.Value; } set { _indexst.Value = value; } } double _startTime; float _duration; readonly PropOp _durationOp; public double EndTime { get { return _startTime + _duration; } } public void Tick(double time) { foreach (var i in _comps) i.Tick(_skinContainer, time); } public bool CanEmit() { return _currentStateName.Key == 0 || _currentState.rewind.Key != 0; } public void OnEmit(double time, Transform target) { _currentTarget = target; _ctxev = PdtEvaluator.Instance.ContextEvent; _ctxstate = PdtEvaluator.Instance.ContextState; _skinContainer.MatchDynamic(0, true); if (_currentStateName.Key == 0) { EnterState(_def.init, time, _currentTarget, true); } else { if (_currentState.rewind.Key == 0) throw new InvalidOperationException("Cannot rewind"); EnterState(_currentState.rewind, time, _currentTarget, true); } } void EnterState(Identifier name, double time, Transform target, bool emitting) { _currentStateName = name; _currentState = _def.states[name]; Rewind(time, target); RootTransform.gameObject.SetActive(true); PdtEvaluator.Instance.ContextCascadeInsert(); PdtEvaluator.Instance.ContextCascadeUpdate(_VAR_EFFECT_INDEX, _indexst.Source); PdtEvaluator.Instance.Evaluate(_durationOp, _currentState.duration); _skinContainer.MatchDynamic(1, emitting); PdtEvaluator.Instance.ContextCascadeDiscard(); } public bool OnStateDone() { if (_currentState.next.Key == 0) { RootTransform.gameObject.SetActive(false); _currentStateName = Identifier.Empty; _currentState = null; return false; } else { PdtEvaluator.Instance.ContextEvent = _ctxev; PdtEvaluator.Instance.ContextState = _ctxstate; EnterState(_currentState.next, EndTime, _currentTarget, false); PdtEvaluator.Instance.ContextEvent = null; PdtEvaluator.Instance.ContextState = null; return true; } } public void Dispose() { GameObject.Destroy(RootTransform.gameObject); } public string TypeName { get { throw new InvalidOperationException("Type name undefined"); } } public SkinContext SkinContext { get; private set; } public int AtAnchorDynamicLevel { get { return 1; } } public int OpenedAnchorName { get { return _currentStateName.Key; } } public void PushAnchorEvent(double time, int name) { throw new InvalidOperationException("Anchor not supported"); } public void RegisterAnchor(int name) { throw new InvalidOperationException("Anchor not supported"); } public bool TryGetAnchorsByName(int name, out IReadOnlyCollection result) { throw new InvalidOperationException("Anchor not supported"); } public int CompareTo(EffectInstance other) { int r = EndTime.CompareTo(other.EndTime); if (r != 0) return r; return GetHashCode().CompareTo(other.GetHashCode()); } } }