282 lines
10 KiB
C#
282 lines
10 KiB
C#
using Cryville.Common;
|
|
using Cryville.Common.Buffers;
|
|
using Cryville.Common.Collections.Specialized;
|
|
using Cryville.Crtr.Ruleset;
|
|
using Cryville.Crtr.Skin;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Runtime.CompilerServices;
|
|
using UnityEngine;
|
|
|
|
namespace Cryville.Crtr.Event {
|
|
public abstract class ContainerHandler : ISkinnableGroup {
|
|
#region Struct
|
|
public ContainerHandler() { }
|
|
|
|
/// <summary>
|
|
/// Prehandling <see cref="ContainerState"/>, prehandling the events.
|
|
/// </summary>
|
|
public ContainerState ps;
|
|
|
|
/// <summary>
|
|
/// Backward <see cref="ContainerState"/>, disposing events at the backward clipping plane, firing anchor state and temporary state.
|
|
/// </summary>
|
|
public ContainerState bs;
|
|
|
|
/// <summary>
|
|
/// Current <see cref="ContainerState"/>, handling events occuring at the current time.
|
|
/// </summary>
|
|
public ContainerState cs;
|
|
|
|
/// <summary>
|
|
/// Anchor <see cref="ContainerState"/>, computing the start position of the temporary state.
|
|
/// </summary>
|
|
public ContainerState ns;
|
|
|
|
/// <summary>
|
|
/// Temporary <see cref="ContainerState"/>, rendering events between the clipping planes.
|
|
/// </summary>
|
|
public ContainerState ts;
|
|
|
|
protected Transform RootTransform;
|
|
|
|
readonly List<SkinComponent> _comps = new();
|
|
protected IEnumerable<SkinComponent> Components { get { return _comps; } }
|
|
|
|
public Vector3 Position { get; protected set; }
|
|
public Quaternion Rotation { get; protected set; }
|
|
public bool Alive { get; private set; }
|
|
bool PreGraphicalActive;
|
|
public void SetPreGraphicalActive(bool value, ContainerState s) {
|
|
if (PreGraphicalActive == value) return;
|
|
PreGraphicalActive = value;
|
|
if (PreGraphicalActive) StartPreGraphicalUpdate(s);
|
|
else EndPreGraphicalUpdate(s);
|
|
}
|
|
bool GraphicalActive;
|
|
public void SetGraphicalActive(bool value, ContainerState s) {
|
|
if (GraphicalActive == value) return;
|
|
GraphicalActive = value;
|
|
if (GraphicalActive) StartGraphicalUpdate(s);
|
|
else EndGraphicalUpdate(s);
|
|
}
|
|
|
|
public EventContainer Container {
|
|
get { return cs.Container; }
|
|
}
|
|
|
|
static readonly int _var_current_time = IdentifierManager.Shared.Request("current_time");
|
|
static readonly int _var_invisible_bounds = IdentifierManager.Shared.Request("invisible_bounds");
|
|
public readonly IntKeyedDictionary<PropSrc> PropSrcs = new();
|
|
SkinContainer skinContainer;
|
|
protected Judge judge;
|
|
public void AttachSystems(PdtSkin skin, Judge judge) {
|
|
skinContainer = new SkinContainer(this, skin.elements);
|
|
this.judge = judge;
|
|
}
|
|
|
|
public readonly IntKeyedDictionary<List<Anchor>> Anchors = new();
|
|
public readonly IntKeyedDictionary<Anchor> DynamicAnchors = new();
|
|
public readonly IntKeyedDictionary<double> DynamicAnchorSetTime = new();
|
|
Anchor a_cur;
|
|
Anchor a_head;
|
|
Anchor a_tail;
|
|
static readonly int _a_cur = IdentifierManager.Shared.Request("cur");
|
|
static readonly int _a_head = IdentifierManager.Shared.Request("head");
|
|
static readonly int _a_tail = IdentifierManager.Shared.Request("tail");
|
|
double atime_head;
|
|
double atime_tail;
|
|
public Anchor RegisterAnchor(int name, bool dyn = false, int propSrcCount = 0) {
|
|
var strname = IdentifierManager.Shared.Retrieve(name);
|
|
var go = new GameObject("." + strname).transform;
|
|
go.SetParent(RootTransform, false);
|
|
var result = new Anchor(name, go, propSrcCount);
|
|
if (dyn) {
|
|
if (DynamicAnchors.ContainsKey(name))
|
|
throw new ArgumentException(string.Format("The anchor \"{0}\" already exists", strname));
|
|
DynamicAnchors.Add(name, result);
|
|
DynamicAnchorSetTime.Add(name, double.NaN);
|
|
}
|
|
if (!Anchors.TryGetValue(name, out List<Anchor> list))
|
|
Anchors.Add(name, list = new List<Anchor>());
|
|
list.Add(result);
|
|
return result;
|
|
}
|
|
protected void OpenAnchor(Anchor anchor) {
|
|
if (_openedAnchor != null) throw new InvalidOperationException("An anchor has been opened");
|
|
anchor.Transform.gameObject.SetActive(true);
|
|
_openedAnchor = anchor;
|
|
}
|
|
protected void CloseAnchor() {
|
|
_openedAnchor = null;
|
|
}
|
|
#endregion
|
|
|
|
#region Logic
|
|
#region Init methods: Called on prehandle
|
|
const string TagRootTransform = "RT";
|
|
public virtual void PreInit() {
|
|
RootTransform = new GameObject(TypeName + ":" + Container.GetHashCode().ToString(CultureInfo.InvariantCulture)).transform;
|
|
RootTransform.tag = TagRootTransform;
|
|
SkinContext = new SkinContext(RootTransform);
|
|
if (cs.Parent != null)
|
|
RootTransform.SetParent(cs.Parent.Handler.RootTransform, false);
|
|
a_cur = RegisterAnchor(_a_cur);
|
|
a_head = RegisterAnchor(_a_head, true);
|
|
a_tail = RegisterAnchor(_a_tail, true);
|
|
}
|
|
public virtual void Init() {
|
|
PdtEvaluator.Instance.ContextState = ps;
|
|
PdtEvaluator.Instance.ContextEvent = Container;
|
|
skinContainer.MatchStatic();
|
|
MatchDynamic(ps, 0);
|
|
PdtEvaluator.Instance.ContextEvent = null;
|
|
PdtEvaluator.Instance.ContextState = null;
|
|
foreach (Transform child in RootTransform) {
|
|
if (child.CompareTag(TagRootTransform)) continue;
|
|
_comps.AddRange(child.GetComponentsInChildren<SkinComponent>());
|
|
}
|
|
foreach (var i in _comps) i.Init();
|
|
}
|
|
public virtual void PostInit() {
|
|
PropSrcs.Add(_var_current_time, new PropSrc.Float(() => (float)cs.rootPrototype.Time));
|
|
PropSrcs.Add(_var_invisible_bounds, new PropSrc.Boolean(() => atime_head > atime_tail));
|
|
RootTransform.gameObject.SetActive(false);
|
|
}
|
|
#endregion
|
|
#region Start methods
|
|
public virtual void StartPhysicalUpdate(ContainerState s) {
|
|
if (s.CloneType < 16) Alive = true;
|
|
else if (s.CloneType == 17) Init();
|
|
}
|
|
public virtual void StartLogicalUpdate(ContainerState s) { }
|
|
protected virtual void StartPreGraphicalUpdate(ContainerState s) { }
|
|
protected virtual void StartGraphicalUpdate(ContainerState s) {
|
|
if (RootTransform) RootTransform.gameObject.SetActive(true);
|
|
}
|
|
#endregion
|
|
public virtual void Update(ContainerState s, StampedEvent ev) {
|
|
if (ev is StampedEvent.Anchor tev) {
|
|
if (tev.Target == a_head) {
|
|
if (s.CloneType == 2) SetGraphicalActive(true, s);
|
|
else SetPreGraphicalActive(true, s);
|
|
}
|
|
if (RootTransform) {
|
|
OpenAnchor(tev.Target);
|
|
#if UNITY_5_6_OR_NEWER
|
|
tev.Target.Transform.SetPositionAndRotation(Position, Rotation);
|
|
#else
|
|
tev.Target.Transform.position = Position;
|
|
tev.Target.Transform.rotation = Rotation;
|
|
#endif
|
|
MatchDynamic(s, 2);
|
|
CloseAnchor();
|
|
}
|
|
if (tev.Target == a_tail) {
|
|
if (s.CloneType == 2) SetGraphicalActive(false, s);
|
|
else SetPreGraphicalActive(false, s);
|
|
}
|
|
ReturnAnchorEvent(tev);
|
|
}
|
|
else if (RootTransform && s.CloneType == 2) MatchDynamic(s, 2);
|
|
}
|
|
#region End methods
|
|
protected virtual void EndGraphicalUpdate(ContainerState s) { }
|
|
protected virtual void EndPreGraphicalUpdate(ContainerState s) { }
|
|
public virtual void EndLogicalUpdate(ContainerState s) { }
|
|
public virtual void EndPhysicalUpdate(ContainerState s) { }
|
|
public virtual void Dispose() {
|
|
if (RootTransform)
|
|
GameObject.Destroy(RootTransform.gameObject);
|
|
Alive = false;
|
|
}
|
|
public virtual void DisposeAll() { }
|
|
#endregion
|
|
#region Utils
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
protected static bool CanDoGraphicalUpdate(ContainerState s) { return s.CloneType >= 2 && s.CloneType < 16; }
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
void MatchDynamic(ContainerState s, int dl) {
|
|
PdtEvaluator.Instance.ContextState = s;
|
|
PdtEvaluator.Instance.ContextEvent = Container;
|
|
skinContainer.MatchDynamic(dl);
|
|
PdtEvaluator.Instance.ContextEvent = null;
|
|
PdtEvaluator.Instance.ContextState = null;
|
|
}
|
|
#endregion
|
|
#region Anchor
|
|
public virtual void PreAnchor() {
|
|
foreach (var p in PropSrcs) p.Value.Invalidate();
|
|
foreach (var a in DynamicAnchors) DynamicAnchorSetTime[a.Key] = double.NaN;
|
|
atime_head = cs.StampedContainer.Time;
|
|
atime_tail = atime_head + cs.StampedContainer.Duration;
|
|
MatchDynamic(cs, 1);
|
|
foreach (var i in _comps) i.Tick(skinContainer, cs.Time);
|
|
if (double.IsNaN(DynamicAnchorSetTime[_a_head])) DynamicAnchorSetTime[_a_head] = atime_head;
|
|
if (double.IsNaN(DynamicAnchorSetTime[_a_tail])) DynamicAnchorSetTime[_a_tail] = atime_tail;
|
|
PushAnchorEvent(ns, atime_head, a_head, -1, true);
|
|
PushAnchorEvent(ns, atime_tail, a_tail, 1, true);
|
|
foreach (var anchors in Anchors) foreach (var anchor in anchors.Value) anchor.Transform.gameObject.SetActive(false);
|
|
}
|
|
public virtual void Anchor() {
|
|
if (cs.Active) PushAnchorEvent(ts, cs.Time, a_cur);
|
|
foreach (var t in DynamicAnchorSetTime) {
|
|
if (double.IsNaN(t.Value)) continue;
|
|
int priority = 0;
|
|
bool forced = true;
|
|
if (t.Key == _a_head) { priority = -1; }
|
|
else if (t.Key == _a_tail) { priority = 1; }
|
|
else forced = false;
|
|
PushAnchorEvent(ts, t.Value, DynamicAnchors[t.Key], priority, forced);
|
|
}
|
|
}
|
|
static readonly SimpleObjectPool<StampedEvent.Anchor> anchorEvPool
|
|
= new(1024);
|
|
void PushAnchorEvent(ContainerState state, double time, Anchor anchor, int priority = 0, bool forced = false) {
|
|
var tev = anchorEvPool.Rent();
|
|
tev.Time = time;
|
|
tev.Container = Container;
|
|
tev.Target = anchor;
|
|
tev.Forced = forced;
|
|
tev.SetPriority(priority);
|
|
state.Bus.PushTempEvent(tev);
|
|
}
|
|
public virtual void Discard(ContainerState s, StampedEvent ev) {
|
|
if (ev is StampedEvent.Anchor anchor) {
|
|
ReturnAnchorEvent(anchor);
|
|
}
|
|
}
|
|
void ReturnAnchorEvent(StampedEvent.Anchor ev) {
|
|
ev.Container = null;
|
|
ev.Target = null;
|
|
anchorEvPool.Return(ev);
|
|
}
|
|
#endregion
|
|
#endregion
|
|
|
|
#region ISkinnableGroup
|
|
public abstract string TypeName { get; }
|
|
public SkinContext SkinContext { get; private set; }
|
|
public int AtAnchorDynamicLevel { get { return 2; } }
|
|
Anchor _openedAnchor;
|
|
public int OpenedAnchorName { get { return _openedAnchor == null ? 0 : _openedAnchor.Name; } }
|
|
public bool TryGetAnchorsByName(int name, out IReadOnlyCollection<Anchor> result) {
|
|
var ret = Anchors.TryGetValue(name, out List<Anchor> anchors);
|
|
result = anchors;
|
|
return ret;
|
|
}
|
|
void ISkinnableGroup.RegisterAnchor(int name) {
|
|
RegisterAnchor(name, true);
|
|
}
|
|
public void PushAnchorEvent(double time, int name) {
|
|
if (!DynamicAnchors.ContainsKey(name))
|
|
throw new ArgumentException(string.Format("Specified anchor \"{0}\" not found", IdentifierManager.Shared.Retrieve(name)));
|
|
if (name == _a_head) atime_head = time;
|
|
else if (name == _a_tail) atime_tail = time;
|
|
DynamicAnchorSetTime[name] = time;
|
|
}
|
|
#endregion
|
|
}
|
|
}
|