using System; using System.Collections.Generic; using System.Linq; using UnityEditor.Animations; using UnityEngine; using VRC.SDK3.Avatars.Components; using VRC.SDKBase; // ReSharper disable once CheckNamespace namespace AnimatorAsCode.V0 { internal class AacBackingAnimator { private readonly AacAnimatorGenerator _generator; public AacBackingAnimator(AacAnimatorGenerator animatorGenerator) { _generator = animatorGenerator; } public AacFlBoolParameter BoolParameter(string parameterName) { var result = AacFlBoolParameter.Internally(parameterName); _generator.CreateParamsAsNeeded(result); return result; } public AacFlBoolParameter TriggerParameter(string parameterName) { var result = AacFlBoolParameter.Internally(parameterName); _generator.CreateTriggerParamsAsNeeded(result); return result; } public AacFlFloatParameter FloatParameter(string parameterName) { var result = AacFlFloatParameter.Internally(parameterName); _generator.CreateParamsAsNeeded(result); return result; } public AacFlIntParameter IntParameter(string parameterName) { var result = AacFlIntParameter.Internally(parameterName); _generator.CreateParamsAsNeeded(result); return result; } public AacFlEnumIntParameter EnumParameter(string parameterName) where TEnum : Enum { var result = AacFlEnumIntParameter.Internally(parameterName); _generator.CreateParamsAsNeeded(result); return result; } public AacFlBoolParameterGroup BoolParameters(params string[] parameterNames) { var result = AacFlBoolParameterGroup.Internally(parameterNames); _generator.CreateParamsAsNeeded(result.ToList().ToArray()); return result; } public AacFlBoolParameterGroup TriggerParameters(params string[] parameterNames) { var result = AacFlBoolParameterGroup.Internally(parameterNames); _generator.CreateTriggerParamsAsNeeded(result.ToList().ToArray()); return result; } public AacFlFloatParameterGroup FloatParameters(params string[] parameterNames) { var result = AacFlFloatParameterGroup.Internally(parameterNames); _generator.CreateParamsAsNeeded(result.ToList().ToArray()); return result; } public AacFlIntParameterGroup IntParameters(params string[] parameterNames) { var result = AacFlIntParameterGroup.Internally(parameterNames); _generator.CreateParamsAsNeeded(result.ToList().ToArray()); return result; } public AacFlBoolParameterGroup BoolParameters(params AacFlBoolParameter[] parameters) { var result = AacFlBoolParameterGroup.Internally(parameters.Select(parameter => parameter.Name).ToArray()); _generator.CreateParamsAsNeeded(parameters); return result; } public AacFlBoolParameterGroup TriggerParameters(params AacFlBoolParameter[] parameters) { var result = AacFlBoolParameterGroup.Internally(parameters.Select(parameter => parameter.Name).ToArray()); _generator.CreateTriggerParamsAsNeeded(parameters); return result; } public AacFlFloatParameterGroup FloatParameters(params AacFlFloatParameter[] parameters) { var result = AacFlFloatParameterGroup.Internally(parameters.Select(parameter => parameter.Name).ToArray()); _generator.CreateParamsAsNeeded(parameters); return result; } public AacFlIntParameterGroup IntParameters(params AacFlIntParameter[] parameters) { var result = AacFlIntParameterGroup.Internally(parameters.Select(parameter => parameter.Name).ToArray()); _generator.CreateParamsAsNeeded(parameters); return result; } } internal class AacStateMachine { private readonly AnimatorStateMachine _machine; private readonly AnimationClip _emptyClip; private readonly AacBackingAnimator _backingAnimator; private readonly IAacDefaultsProvider _defaultsProvider; private readonly float _gridShiftX; private readonly float _gridShiftY; public AacStateMachine(AnimatorStateMachine machine, AnimationClip emptyClip, AacBackingAnimator backingAnimator, IAacDefaultsProvider defaultsProvider) { _machine = machine; _emptyClip = emptyClip; _backingAnimator = backingAnimator; _defaultsProvider = defaultsProvider; var grid = defaultsProvider.Grid(); _gridShiftX = grid.x; _gridShiftY = grid.y; } internal AacBackingAnimator BackingAnimator() { return _backingAnimator; } public AacStateMachine WithEntryPosition(int x, int y) { _machine.entryPosition = GridPosition(x, y); return this; } public AacStateMachine WithExitPosition(int x, int y) { _machine.exitPosition = GridPosition(x, y); return this; } public AacStateMachine WithAnyStatePosition(int x, int y) { _machine.anyStatePosition = GridPosition(x, y); return this; } public AacFlState NewState(string name, int x, int y) { var state = _machine.AddState(name, GridPosition(x, y)); _defaultsProvider.ConfigureState(state, _emptyClip); return new AacFlState(state, _machine, _defaultsProvider); } public AacFlTransition AnyTransitionsTo(AacFlState destination) { return AnyTransition(destination, _machine); } public AacFlEntryTransition EntryTransitionsTo(AacFlState destination) { return EntryTransition(destination, _machine); } private AacFlTransition AnyTransition(AacFlState destination, AnimatorStateMachine animatorStateMachine) { return new AacFlTransition(ConfigureTransition(animatorStateMachine.AddAnyStateTransition(destination.State)), animatorStateMachine, null, destination.State); } private AnimatorStateTransition ConfigureTransition(AnimatorStateTransition transition) { _defaultsProvider.ConfigureTransition(transition); return transition; } private AacFlEntryTransition EntryTransition(AacFlState destination, AnimatorStateMachine animatorStateMachine) { return new AacFlEntryTransition(animatorStateMachine.AddEntryTransition(destination.State), animatorStateMachine, null, destination.State); } internal Vector3 LastStatePosition() { return _machine.states.Length > 0 ? _machine.states.Last().position : Vector3.zero; } private Vector3 GridPosition(int x, int y) { return new Vector3(x * _gridShiftX , y * _gridShiftY, 0); } public AacStateMachine WithDefaultState(AacFlState newDefaultState) { _machine.defaultState = newDefaultState.State; return this; } } public class AacFlState { public readonly AnimatorState State; private readonly AnimatorStateMachine _machine; private readonly IAacDefaultsProvider _defaultsProvider; private readonly AacBackingAnimator _backingAnimator; private VRCAvatarParameterDriver _driver; private VRCAnimatorTrackingControl _tracking; private VRCAnimatorLocomotionControl _locomotionControl; private VRCAnimatorTemporaryPoseSpace _temporaryPoseSpace; public AacFlState(AnimatorState state, AnimatorStateMachine machine, IAacDefaultsProvider defaultsProvider) { State = state; _machine = machine; _defaultsProvider = defaultsProvider; } public AacFlState LeftOf(AacFlState otherState) => MoveNextTo(otherState, -1, 0); public AacFlState RightOf(AacFlState otherState) => MoveNextTo(otherState, 1, 0); public AacFlState Over(AacFlState otherState) => MoveNextTo(otherState, 0, -1); public AacFlState Under(AacFlState otherState) => MoveNextTo(otherState, 0, 1); public AacFlState LeftOf() => MoveNextTo(null, -1, 0); public AacFlState RightOf() => MoveNextTo(null, 1, 0); public AacFlState Over() => MoveNextTo(null, 0, -1); public AacFlState Under() => MoveNextTo(null, 0, 1); public AacFlState Shift(AacFlState otherState, int shiftX, int shiftY) => MoveNextTo(otherState, shiftX, shiftY); private AacFlState MoveNextTo(AacFlState otherStateOrSecondToLastWhenNull, int x, int y) { if (otherStateOrSecondToLastWhenNull == null) { var other = _machine.states[_machine.states.Length - 2]; Shift(other.position, x, y); return this; } else { var other = _machine.states.First(animatorState => animatorState.state == otherStateOrSecondToLastWhenNull.State); Shift(other.position, x, y); return this; } } public AacFlState Shift(Vector3 otherPosition, int shiftX, int shiftY) { var states = _machine.states; for (var index = 0; index < states.Length; index++) { var childAnimatorState = states[index]; if (childAnimatorState.state == State) { var cms = childAnimatorState; cms.position = otherPosition + new Vector3(shiftX * _defaultsProvider.Grid().x, shiftY * _defaultsProvider.Grid().y, 0); states[index] = cms; break; } } _machine.states = states; return this; } public AacFlState WithAnimation(Motion clip) { State.motion = clip; return this; } public AacFlState WithAnimation(AacFlClip clip) { State.motion = clip.Clip; return this; } public AacFlTransition TransitionsTo(AacFlState destination) { return new AacFlTransition(ConfigureTransition(State.AddTransition(destination.State)), _machine, State, destination.State); } public AacFlTransition TransitionsFromAny() { return new AacFlTransition(ConfigureTransition(_machine.AddAnyStateTransition(State)), _machine, null, State); } public AacFlEntryTransition TransitionsFromEntry() { return new AacFlEntryTransition(_machine.AddEntryTransition(State), _machine, null, State); } public AacFlState AutomaticallyMovesTo(AacFlState destination) { var transition = ConfigureTransition(State.AddTransition(destination.State)); transition.hasExitTime = true; return this; } public AacFlTransition Exits() { return new AacFlTransition(ConfigureTransition(State.AddExitTransition()), _machine, State, null); } private AnimatorStateTransition ConfigureTransition(AnimatorStateTransition transition) { _defaultsProvider.ConfigureTransition(transition); return transition; } public AacFlState Drives(AacFlIntParameter parameter, int value) { CreateDriverBehaviorIfNotExists(); _driver.parameters.Add(new VRC_AvatarParameterDriver.Parameter { type = VRC_AvatarParameterDriver.ChangeType.Set, name = parameter.Name, value = value }); return this; } public AacFlState Drives(AacFlFloatParameter parameter, float value) { CreateDriverBehaviorIfNotExists(); _driver.parameters.Add(new VRC_AvatarParameterDriver.Parameter { type = VRC_AvatarParameterDriver.ChangeType.Set, name = parameter.Name, value = value }); return this; } public AacFlState DrivingIncreases(AacFlFloatParameter parameter, float additiveValue) { CreateDriverBehaviorIfNotExists(); _driver.parameters.Add(new VRC_AvatarParameterDriver.Parameter { type = VRC_AvatarParameterDriver.ChangeType.Add, name = parameter.Name, value = additiveValue }); return this; } public AacFlState DrivingDecreases(AacFlFloatParameter parameter, float positiveValueToDecreaseBy) { CreateDriverBehaviorIfNotExists(); _driver.parameters.Add(new VRC_AvatarParameterDriver.Parameter { type = VRC_AvatarParameterDriver.ChangeType.Add, name = parameter.Name, value = -positiveValueToDecreaseBy }); return this; } public AacFlState DrivingIncreases(AacFlIntParameter parameter, int additiveValue) { CreateDriverBehaviorIfNotExists(); _driver.parameters.Add(new VRC_AvatarParameterDriver.Parameter { type = VRC_AvatarParameterDriver.ChangeType.Add, name = parameter.Name, value = additiveValue }); return this; } public AacFlState DrivingDecreases(AacFlIntParameter parameter, int positiveValueToDecreaseBy) { CreateDriverBehaviorIfNotExists(); _driver.parameters.Add(new VRC_AvatarParameterDriver.Parameter { type = VRC_AvatarParameterDriver.ChangeType.Add, name = parameter.Name, value = -positiveValueToDecreaseBy }); return this; } public AacFlState DrivingRandomizesLocally(AacFlFloatParameter parameter, float min, float max) { CreateDriverBehaviorIfNotExists(); _driver.parameters.Add(new VRC_AvatarParameterDriver.Parameter { type = VRC_AvatarParameterDriver.ChangeType.Random, name = parameter.Name, valueMin = min, valueMax = max }); _driver.localOnly = true; return this; } public AacFlState DrivingRandomizesLocally(AacFlBoolParameter parameter, float chance) { CreateDriverBehaviorIfNotExists(); _driver.parameters.Add(new VRC_AvatarParameterDriver.Parameter { type = VRC_AvatarParameterDriver.ChangeType.Random, name = parameter.Name, chance = chance }); _driver.localOnly = true; return this; } public AacFlState DrivingRandomizesLocally(AacFlIntParameter parameter, int min, int max) { CreateDriverBehaviorIfNotExists(); _driver.parameters.Add(new VRC_AvatarParameterDriver.Parameter { type = VRC_AvatarParameterDriver.ChangeType.Random, name = parameter.Name, valueMin = min, valueMax = max }); _driver.localOnly = true; return this; } public AacFlState Drives(AacFlBoolParameter parameter, bool value) { CreateDriverBehaviorIfNotExists(); _driver.parameters.Add(new VRC_AvatarParameterDriver.Parameter { name = parameter.Name, value = value ? 1 : 0 }); return this; } public AacFlState Drives(AacFlBoolParameterGroup parameters, bool value) { CreateDriverBehaviorIfNotExists(); foreach (var parameter in parameters.ToList()) { _driver.parameters.Add(new VRC_AvatarParameterDriver.Parameter { name = parameter.Name, value = value ? 1 : 0 }); } return this; } public AacFlState DrivingLocally() { CreateDriverBehaviorIfNotExists(); _driver.localOnly = true; return this; } private void CreateDriverBehaviorIfNotExists() { if (_driver != null) return; _driver = State.AddStateMachineBehaviour(); _driver.parameters = new List(); } public AacFlState WithWriteDefaultsSetTo(bool shouldWriteDefaults) { State.writeDefaultValues = shouldWriteDefaults; return this; } public AacFlState PrintsToLogUsingTrackingBehaviour(string value) { CreateTrackingBehaviorIfNotExists(); _tracking.debugString = value; return this; } public AacFlState TrackingTracks(TrackingElement element) { CreateTrackingBehaviorIfNotExists(); SettingElementTo(element, VRC_AnimatorTrackingControl.TrackingType.Tracking); return this; } public AacFlState TrackingAnimates(TrackingElement element) { CreateTrackingBehaviorIfNotExists(); SettingElementTo(element, VRC_AnimatorTrackingControl.TrackingType.Animation); return this; } public AacFlState TrackingSets(TrackingElement element, VRC_AnimatorTrackingControl.TrackingType trackingType) { CreateTrackingBehaviorIfNotExists(); SettingElementTo(element, trackingType); return this; } public AacFlState LocomotionEnabled() { CreateLocomotionBehaviorIfNotExists(); _locomotionControl.disableLocomotion = false; return this; } public AacFlState LocomotionDisabled() { CreateLocomotionBehaviorIfNotExists(); _locomotionControl.disableLocomotion = true; return this; } public AacFlState PlayableEnables(VRC_PlayableLayerControl.BlendableLayer blendable, float blendDurationSeconds = 0f) { return PlayableSets(blendable, blendDurationSeconds, 1.0f); } public AacFlState PlayableDisables(VRC_PlayableLayerControl.BlendableLayer blendable, float blendDurationSeconds = 0f) { return PlayableSets(blendable, blendDurationSeconds, 0.0f); } public AacFlState PlayableSets(VRC_PlayableLayerControl.BlendableLayer blendable, float blendDurationSeconds, float weight) { var playable = State.AddStateMachineBehaviour(); playable.layer = blendable; playable.goalWeight = weight; playable.blendDuration = blendDurationSeconds; return this; } public AacFlState PoseSpaceEntered(float delaySeconds = 0f) { CreateTemporaryPoseSpaceBehaviorIfNotExists(); _temporaryPoseSpace.enterPoseSpace = true; _temporaryPoseSpace.fixedDelay = true; _temporaryPoseSpace.delayTime = delaySeconds; return this; } public AacFlState PoseSpaceExited(float delaySeconds = 0f) { CreateTemporaryPoseSpaceBehaviorIfNotExists(); _temporaryPoseSpace.enterPoseSpace = false; _temporaryPoseSpace.fixedDelay = true; _temporaryPoseSpace.delayTime = delaySeconds; return this; } public AacFlState PoseSpaceEnteredPercent(float delayNormalized) { CreateTemporaryPoseSpaceBehaviorIfNotExists(); _temporaryPoseSpace.enterPoseSpace = true; _temporaryPoseSpace.fixedDelay = false; _temporaryPoseSpace.delayTime = delayNormalized; return this; } public AacFlState PoseSpaceExitedPercent(float delayNormalized) { CreateTemporaryPoseSpaceBehaviorIfNotExists(); _temporaryPoseSpace.enterPoseSpace = false; _temporaryPoseSpace.fixedDelay = false; _temporaryPoseSpace.delayTime = delayNormalized; return this; } public AacFlState MotionTime(AacFlFloatParameter floatParam) { State.timeParameterActive = true; State.timeParameter = floatParam.Name; return this; } public AacFlState WithCycleOffset(AacFlFloatParameter floatParam) { State.cycleOffsetParameterActive = false; State.cycleOffsetParameter = floatParam.Name; return this; } public AacFlState WithCycleOffsetSetTo(float cycleOffset) { State.cycleOffsetParameterActive = false; State.cycleOffset = cycleOffset; return this; } private void SettingElementTo(TrackingElement element, VRC_AnimatorTrackingControl.TrackingType target) { switch (element) { case TrackingElement.Head: _tracking.trackingHead = target; break; case TrackingElement.LeftHand: _tracking.trackingLeftHand = target; break; case TrackingElement.RightHand: _tracking.trackingRightHand = target; break; case TrackingElement.Hip: _tracking.trackingHip = target; break; case TrackingElement.LeftFoot: _tracking.trackingLeftFoot = target; break; case TrackingElement.RightFoot: _tracking.trackingRightFoot = target; break; case TrackingElement.LeftFingers: _tracking.trackingLeftFingers = target; break; case TrackingElement.RightFingers: _tracking.trackingRightFingers = target; break; case TrackingElement.Eyes: _tracking.trackingEyes = target; break; case TrackingElement.Mouth: _tracking.trackingMouth = target; break; default: throw new ArgumentOutOfRangeException(nameof(element), element, null); } } private void CreateTrackingBehaviorIfNotExists() { if (_tracking != null) return; _tracking = State.AddStateMachineBehaviour(); } private void CreateLocomotionBehaviorIfNotExists() { if (_locomotionControl != null) return; _locomotionControl = State.AddStateMachineBehaviour(); } private void CreateTemporaryPoseSpaceBehaviorIfNotExists() { if (_temporaryPoseSpace != null) return; _temporaryPoseSpace = State.AddStateMachineBehaviour(); } public enum TrackingElement { Head, LeftHand, RightHand, Hip, LeftFoot, RightFoot, LeftFingers, RightFingers, Eyes, Mouth } public AacFlState WithSpeed(AacFlFloatParameter parameter) { State.speedParameterActive = true; State.speedParameter = parameter.Name; return this; } public AacFlState WithSpeedSetTo(float speed) { State.speedParameterActive = false; State.speed = speed; return this; } } public class AacFlTransition : AacFlNewTransitionContinuation { private readonly AnimatorStateTransition _transition; public AacFlTransition(AnimatorStateTransition transition, AnimatorStateMachine machine, AnimatorState sourceNullableIfAny, AnimatorState destinationNullableIfExits) : base(transition, machine, sourceNullableIfAny, destinationNullableIfExits) { _transition = transition; } public AacFlTransition WithSourceInterruption() { _transition.interruptionSource = TransitionInterruptionSource.Source; return this; } public AacFlTransition WithInterruption(TransitionInterruptionSource interruptionSource) { _transition.interruptionSource = interruptionSource; return this; } public AacFlTransition WithTransitionDurationSeconds(float transitionDuration) { _transition.duration = transitionDuration; return this; } public AacFlTransition WithOrderedInterruption() { _transition.orderedInterruption = true; return this; } public AacFlTransition WithNoOrderedInterruption() { _transition.orderedInterruption = false; return this; } public AacFlTransition WithTransitionToSelf() { _transition.canTransitionToSelf = true; return this; } public AacFlTransition WithNoTransitionToSelf() { _transition.canTransitionToSelf = false; return this; } public AacFlTransition AfterAnimationFinishes() { _transition.hasExitTime = true; _transition.exitTime = 1; return this; } public AacFlTransition AfterAnimationIsAtLeastAtPercent(float exitTimeNormalized) { _transition.hasExitTime = true; _transition.exitTime = exitTimeNormalized; return this; } public AacFlTransition WithTransitionDurationPercent(float transitionDurationNormalized) { _transition.hasFixedDuration = false; _transition.duration = transitionDurationNormalized; return this; } } public class AacFlEntryTransition : AacFlNewTransitionContinuation { public AacFlEntryTransition(AnimatorTransition transition, AnimatorStateMachine machine, AnimatorState sourceNullableIfAny, AnimatorState destinationNullableIfExits) : base(transition, machine, sourceNullableIfAny, destinationNullableIfExits) { } } public interface IAacFlCondition { void ApplyTo(AacFlCondition appender); } public interface IAacFlOrCondition { List ApplyTo(AacFlNewTransitionContinuation firstContinuation); } public class AacFlCondition { private readonly AnimatorTransitionBase _transition; public AacFlCondition(AnimatorTransitionBase transition) { _transition = transition; } public AacFlCondition Add(string parameter, AnimatorConditionMode mode, float threshold) { _transition.AddCondition(mode, threshold, parameter); return this; } } public class AacFlNewTransitionContinuation { public readonly AnimatorTransitionBase Transition; private readonly AnimatorStateMachine _machine; private readonly AnimatorState _sourceNullableIfAny; private readonly AnimatorState _destinationNullableIfExits; public AacFlNewTransitionContinuation(AnimatorTransitionBase transition, AnimatorStateMachine machine, AnimatorState sourceNullableIfAny, AnimatorState destinationNullableIfExits) { Transition = transition; _machine = machine; _sourceNullableIfAny = sourceNullableIfAny; _destinationNullableIfExits = destinationNullableIfExits; } /// Adds a condition to the transition. /// /// The settings of the transition can no longer be modified after this point. /// /// /// .When(_aac.BoolParameter(my.myBoolParameterName).IsTrue()) /// .And(_aac.BoolParameter(my.myIntParameterName).IsGreaterThan(2)) /// .And(AacAv3.ItIsLocal()) /// .Or() /// .When(_aac.BoolParameters( /// my.myBoolParameterName, /// my.myOtherBoolParameterName /// ).AreTrue()) /// .And(AacAv3.ItIsRemote()); /// /// public AacFlTransitionContinuation When(IAacFlCondition action) { action.ApplyTo(new AacFlCondition(Transition)); return AsContinuationWithOr(); } /// /// Applies a series of conditions to this transition, but this series of conditions cannot include an Or operator. /// /// /// public AacFlTransitionContinuation When(Action actionsWithoutOr) { actionsWithoutOr(new AacFlTransitionContinuationWithoutOr(Transition)); return AsContinuationWithOr(); } /// /// Applies a series of conditions, and this series may contain Or operators. However, the result can not be followed by an And operator. It can only be an Or operator. /// /// /// public AacFlTransitionContinuationOnlyOr When(Action actionsWithOr) { actionsWithOr(this); return AsContinuationOnlyOr(); } /// /// Applies a series of conditions, and this series may contain Or operators. All And operators that follow will apply to all the conditions generated by this series, until the next Or operator. /// /// /// public AacFlMultiTransitionContinuation When(IAacFlOrCondition actionsWithOr) { var pendingContinuations = actionsWithOr.ApplyTo(this); return new AacFlMultiTransitionContinuation(Transition, _machine, _sourceNullableIfAny, _destinationNullableIfExits, pendingContinuations); } public AacFlTransitionContinuation WhenConditions() { return AsContinuationWithOr(); } private AacFlTransitionContinuation AsContinuationWithOr() { return new AacFlTransitionContinuation(Transition, _machine, _sourceNullableIfAny, _destinationNullableIfExits); } private AacFlTransitionContinuationOnlyOr AsContinuationOnlyOr() { return new AacFlTransitionContinuationOnlyOr(Transition, _machine, _sourceNullableIfAny, _destinationNullableIfExits); } } public class AacFlTransitionContinuation : AacFlTransitionContinuationAbstractWithOr { public AacFlTransitionContinuation(AnimatorTransitionBase transition, AnimatorStateMachine machine, AnimatorState sourceNullableIfAny, AnimatorState destinationNullableIfExits) : base(transition, machine, sourceNullableIfAny, destinationNullableIfExits) { } /// Adds an additional condition to the transition that requires all preceding conditions to be true. /// /// /// .When(_aac.BoolParameter(my.myBoolParameterName).IsTrue()) /// .And(_aac.BoolParameter(my.myIntParameterName).IsGreaterThan(2)) /// .And(AacAv3.ItIsLocal()) /// .Or() /// .When(_aac.BoolParameters( /// my.myBoolParameterName, /// my.myOtherBoolParameterName /// ).AreTrue()) /// .And(AacAv3.ItIsRemote()); /// /// public AacFlTransitionContinuation And(IAacFlCondition action) { action.ApplyTo(new AacFlCondition(Transition)); return this; } /// /// Applies a series of conditions to this transition. The conditions cannot include an Or operator. /// /// /// public AacFlTransitionContinuation And(Action actionsWithoutOr) { actionsWithoutOr(new AacFlTransitionContinuationWithoutOr(Transition)); return this; } } public class AacFlMultiTransitionContinuation : AacFlTransitionContinuationAbstractWithOr { private readonly List _pendingContinuations; public AacFlMultiTransitionContinuation(AnimatorTransitionBase transition, AnimatorStateMachine machine, AnimatorState sourceNullableIfAny, AnimatorState destinationNullableIfExits, List pendingContinuations) : base(transition, machine, sourceNullableIfAny, destinationNullableIfExits) { _pendingContinuations = pendingContinuations; } /// Adds an additional condition to these transitions that requires all preceding conditions to be true. /// /// /// .When(_aac.BoolParameter(my.myBoolParameterName).IsTrue()) /// .And(_aac.BoolParameter(my.myIntParameterName).IsGreaterThan(2)) /// .And(AacAv3.ItIsLocal()) /// .Or() /// .When(_aac.BoolParameters( /// my.myBoolParameterName, /// my.myOtherBoolParameterName /// ).AreTrue()) /// .And(AacAv3.ItIsRemote()); /// /// public AacFlMultiTransitionContinuation And(IAacFlCondition action) { foreach (var pendingContinuation in _pendingContinuations) { pendingContinuation.And(action); } return this; } /// /// Applies a series of conditions to these transitions. The conditions cannot include an Or operator. /// /// /// public AacFlMultiTransitionContinuation And(Action actionsWithoutOr) { foreach (var pendingContinuation in _pendingContinuations) { pendingContinuation.And(actionsWithoutOr); } return this; } } public class AacFlTransitionContinuationOnlyOr : AacFlTransitionContinuationAbstractWithOr { public AacFlTransitionContinuationOnlyOr(AnimatorTransitionBase transition, AnimatorStateMachine machine, AnimatorState sourceNullableIfAny, AnimatorState destinationNullableIfExits) : base(transition, machine, sourceNullableIfAny, destinationNullableIfExits) { } } public abstract class AacFlTransitionContinuationAbstractWithOr { protected readonly AnimatorTransitionBase Transition; private readonly AnimatorStateMachine _machine; private readonly AnimatorState _sourceNullableIfAny; private readonly AnimatorState _destinationNullableIfExits; public AacFlTransitionContinuationAbstractWithOr(AnimatorTransitionBase transition, AnimatorStateMachine machine, AnimatorState sourceNullableIfAny, AnimatorState destinationNullableIfExits) { Transition = transition; _machine = machine; _sourceNullableIfAny = sourceNullableIfAny; _destinationNullableIfExits = destinationNullableIfExits; } /// /// Creates a new transition with identical settings but having no conditions defined yet. /// /// /// /// .When(_aac.BoolParameter(my.myBoolParameterName).IsTrue()) /// .And(_aac.BoolParameter(my.myIntParameterName).IsGreaterThan(2)) /// .And(AacAv3.ItIsLocal()) /// .Or() /// .When(_aac.BoolParameters( /// my.myBoolParameterName, /// my.myOtherBoolParameterName /// ).AreTrue()) /// .And(AacAv3.ItIsRemote()); /// /// public AacFlNewTransitionContinuation Or() { return new AacFlNewTransitionContinuation(NewTransitionFromTemplate(), _machine, _sourceNullableIfAny, _destinationNullableIfExits); } private AnimatorTransitionBase NewTransitionFromTemplate() { AnimatorTransitionBase newTransition; if (Transition is AnimatorStateTransition templateStateTransition) { var stateTransition = NewTransition(); stateTransition.duration = templateStateTransition.duration; stateTransition.offset = templateStateTransition.offset; stateTransition.interruptionSource = templateStateTransition.interruptionSource; stateTransition.orderedInterruption = templateStateTransition.orderedInterruption; stateTransition.exitTime = templateStateTransition.exitTime; stateTransition.hasExitTime = templateStateTransition.hasExitTime; stateTransition.hasFixedDuration = templateStateTransition.hasFixedDuration; stateTransition.canTransitionToSelf = templateStateTransition.canTransitionToSelf; newTransition = stateTransition; } else { newTransition = _machine.AddEntryTransition(_destinationNullableIfExits); } return newTransition; } private AnimatorStateTransition NewTransition() { if (_sourceNullableIfAny == null) { return _machine.AddAnyStateTransition(_destinationNullableIfExits); } if (_destinationNullableIfExits == null) { return _sourceNullableIfAny.AddExitTransition(); } return _sourceNullableIfAny.AddTransition(_destinationNullableIfExits); } } public class AacFlTransitionContinuationWithoutOr { private readonly AnimatorTransitionBase _transition; public AacFlTransitionContinuationWithoutOr(AnimatorTransitionBase transition) { _transition = transition; } public AacFlTransitionContinuationWithoutOr And(IAacFlCondition action) { action.ApplyTo(new AacFlCondition(_transition)); return this; } /// /// Applies a series of conditions to this transition. The conditions cannot include an Or operator. /// /// /// public AacFlTransitionContinuationWithoutOr AndWhenever(Action action) { action(this); return this; } } }