using Cryville.Common; using Cryville.Common.Collections.Specialized; using Cryville.Common.Pdt; using Cryville.Crtr.Skin.Components; using System; using System.Collections.Generic; using System.Linq; using UnityEngine; namespace Cryville.Crtr.Skin { public abstract class SkinPropertyKey { public static SkinPropertyKey Construct(HashSet a, IReadOnlyList k, bool compKeyFlag) { if (a.Remove("has")) { if (k.Count != 1) throw new FormatException("Invalid anchor name"); return new CreateAnchor(a, IdentifierManager.Shared.Request(k[0])); } else if (a.Remove("at")) { if (k.Count != 1) throw new FormatException("Invalid anchor name"); return new SetAnchor(a, IdentifierManager.Shared.Request(k[0])); } else if (a.Remove("emit")) { if (k.Count != 1) throw new FormatException("Invalid effect name"); return new EmitEffect(a, IdentifierManager.Shared.Request(k[0])); } else if (a.Remove("emit_self")) { if (k.Count != 1) throw new FormatException("Invalid effect name"); return new EmitEffect(a, IdentifierManager.Shared.Request(k[0]), true); } else if (a.Remove("var")) { if (k.Count != 1) throw new FormatException("Invalid variable name"); return new SetVariable(a, IdentifierManager.Shared.Request(k[0])); } switch (k.Count) { case 1: if (compKeyFlag) return new CreateComponent(a, GetComponentByName(k[0])); else return new SetProperty(a, typeof(TransformInterface), IdentifierManager.Shared.Request(k[0])); case 2: return new SetProperty(a, GetComponentByName(k[0]), IdentifierManager.Shared.Request(k[1])); default: throw new FormatException("Unknown error"); } static Type GetComponentByName(string name) { if (BuiltinResources.Components.TryGetValue(name, out Type result)) return result; throw new ArgumentException(string.Format("Component type \"{0}\" not found", name)); } } public readonly HashSet annotations; public SkinPropertyKey(IEnumerable a) { annotations = a.ToHashSet(); } public abstract override string ToString(); public abstract bool IsValueRequired { get; } public abstract void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, IntKeyedDictionary vars); public abstract void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, IntKeyedDictionary vars, int dl); public class CreateComponent : SkinPropertyKey { public Type Component { get; private set; } public CreateComponent(IEnumerable a, Type component) : base(a) { Component = component; } public override string ToString() { return string.Format("*{0}", Component.Name); } public override bool IsValueRequired { get { return false; } } public override void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, IntKeyedDictionary vars) { ctx.WriteTransform.gameObject.AddComponent(Component); } public override void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, IntKeyedDictionary vars, int dl) { throw new InvalidOperationException("Component creation in dynamic context is not allowed"); } } public class SetProperty : SkinPropertyKey { public Type Component { get; private set; } public int Name { get; private set; } public SetProperty(IEnumerable a, Type component, int name) : base(a) { Component = component; Name = name; } public override string ToString() { return string.Format("{0}.{1}", Component.Name, IdentifierManager.Shared.Retrieve(Name)); } public override bool IsValueRequired { get { return true; } } public override void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, IntKeyedDictionary vars) { Execute(ctx, GetPropOp(ctx.WriteTransform).Operator, exp); } public override void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, IntKeyedDictionary vars, int dl) { var prop = GetPropOp(ctx.WriteTransform); if (dl > prop.UpdateDynamicLevel) return; Execute(ctx, prop.Operator, exp); } void Execute(RuntimeSkinContext ctx, PdtOperator op, PdtExpression exp) { var psrcs = ctx.ReadContext.PropSrcs; if (psrcs != null) PdtEvaluator.Instance.ContextCascadeInsert(psrcs); if (!PdtEvaluator.Instance.Evaluate(op, exp)) throw new EvaluationFailureException(); if (psrcs != null) PdtEvaluator.Instance.ContextCascadeDiscard(); } SkinProperty GetPropOp(Transform obj) { var ctype = Component; var comp = (SkinComponent)obj.GetComponent(ctype); if (comp == null) throw new InvalidOperationException(string.Format( "Trying to set property \"{0}\" but the component is not found", IdentifierManager.Shared.Retrieve(Name) )); if (!comp.Properties.TryGetValue(Name, out SkinProperty result)) throw new InvalidOperationException(string.Format( "Property \"{0}\" not found on component", IdentifierManager.Shared.Retrieve(Name) )); return result; } } public class CreateAnchor : SkinPropertyKey { public int Name { get; private set; } public CreateAnchor(IEnumerable a, int name) : base(a) { Name = name; } public override string ToString() { return string.Format("@has {0}", IdentifierManager.Shared.Retrieve(Name)); } public override bool IsValueRequired { get { return false; } } public override void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, IntKeyedDictionary vars) { group.RegisterAnchor(Name); } public override void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, IntKeyedDictionary vars, int dl) { throw new InvalidOperationException("Anchor creation in dynamic context is not allowed"); } } public class SetAnchor : SkinPropertyKey { public int Name { get; private set; } public SetAnchor(IEnumerable a, int name) : base(a) { Name = name; _timeOp = new PropOp.Float(v => _time = v); } public override string ToString() { return string.Format("@at {0}", IdentifierManager.Shared.Retrieve(Name)); } public override bool IsValueRequired { get { return true; } } public override void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, IntKeyedDictionary vars) { throw new InvalidOperationException("Setting anchor in static context is not allowed"); } float _time; readonly PropOp _timeOp; public override void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, IntKeyedDictionary vars, int dl) { if (dl != 1) return; var psrcs = ctx.ReadContext.PropSrcs; if (psrcs != null) PdtEvaluator.Instance.ContextCascadeInsert(psrcs); if (!PdtEvaluator.Instance.Evaluate(_timeOp, exp)) throw new EvaluationFailureException(); if (psrcs != null) PdtEvaluator.Instance.ContextCascadeDiscard(); group.PushAnchorEvent(_time, Name); } } public class EmitEffect : SkinPropertyKey { public int Name { get; private set; } public bool IsSelf { get; private set; } public EmitEffect(IEnumerable a, int name, bool isSelf = false) : base(a) { Name = name; IsSelf = isSelf; _op = new PropOp.Float(v => _index = v); } public override string ToString() { return string.Format(IsSelf ? "@emit_self {0}" : "@emit {0}", IdentifierManager.Shared.Retrieve(Name)); } public override bool IsValueRequired { get { return true; } } public override void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, IntKeyedDictionary vars) { throw new InvalidOperationException("Emitting effect in static context is not allowed"); } float _index; readonly PropOp _op; public override void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, IntKeyedDictionary vars, int dl) { if (!PdtEvaluator.Instance.Evaluate(_op, exp)) throw new EvaluationFailureException(); if (IsSelf) ChartPlayer.effectManager.EmitSelf(Name, _index, ctx.WriteTransform); else ChartPlayer.effectManager.Emit(Name, _index); } } public class SetVariable : SkinPropertyKey { public int Name { get; private set; } public SetVariable(IEnumerable a, int name) : base(a) { Name = name; } public override string ToString() { return string.Format("@var {0}", IdentifierManager.Shared.Retrieve(Name)); } public override bool IsValueRequired { get { return true; } } public override void ExecuteStatic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, IntKeyedDictionary vars) { if (!vars.TryGetValue(Name, out PropStores.Float v)) vars.Add(Name, v = new PropStores.Float()); if (!PdtEvaluator.Instance.Evaluate(v.Target, exp)) throw new EvaluationFailureException(); } public override void ExecuteDynamic(ISkinnableGroup group, RuntimeSkinContext ctx, PdtExpression exp, IntKeyedDictionary vars, int dl) { if (!vars.TryGetValue(Name, out PropStores.Float v)) throw new InvalidOperationException(string.Format("Variable \"{0}\" not defined", IdentifierManager.Shared.Retrieve(Name))); if (!PdtEvaluator.Instance.Evaluate(v.Target, exp)) throw new EvaluationFailureException(); } } } }