using Cryville.Common; using Cryville.Crtr.Components; using Cryville.Crtr.Event; using System; using System.Collections.Generic; using System.Globalization; using UnityEngine; namespace Cryville.Crtr { 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); ChartPlayer.etor.ContextCascadeInsertBlock(); _skinContainer.MatchStatic(); ChartPlayer.etor.ContextCascadeDiscardBlock(); _comps = RootTransform.GetComponentsInChildren(); foreach (var i in _comps) i.Init(); _indexSrc = new PropSrc.Float(() => Index); _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); } private float m_index; public float Index { get { return m_index; } set { if (m_index == value) return; m_index = value; _indexSrc.Invalidate(); } } Transform _currentTarget; Identifier _currentStateName = Identifier.Empty; EffectState _currentState; ChartEvent _ctxev; ContainerState _ctxstate; internal static readonly int _VAR_EFFECT_INDEX = IdentifierManager.SharedInstance.Request("effect_index"); readonly PropSrc _indexSrc; 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 = ChartPlayer.etor.ContextEvent; _ctxstate = ChartPlayer.etor.ContextState; 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); ChartPlayer.etor.ContextCascadeInsert(); ChartPlayer.etor.ContextCascadeUpdate(_VAR_EFFECT_INDEX, _indexSrc); ChartPlayer.etor.Evaluate(_durationOp, _currentState.duration); if (emitting) _skinContainer.MatchDynamic(0, true); _skinContainer.MatchDynamic(1, emitting); ChartPlayer.etor.ContextCascadeDiscard(); } public bool OnStateDone() { if (_currentState.next.Key == 0) { RootTransform.gameObject.SetActive(false); _currentStateName = Identifier.Empty; _currentState = null; return false; } else { ChartPlayer.etor.ContextEvent = _ctxev; ChartPlayer.etor.ContextState = _ctxstate; EnterState(_currentState.next, EndTime, _currentTarget, false); ChartPlayer.etor.ContextEvent = null; ChartPlayer.etor.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 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()); } } }