示例#1
0
        protected override void PostConstruct()
        {
            base.PostConstruct();

            Label.Text = Morph.DisplayName;

            var max   = Morph.Definition.Range.Max;
            var min   = Morph.Definition.Range.Min;
            var value = Morph.Definition.Default;

            Slider.MinValue = min;
            Slider.MaxValue = max;
            Slider.Value    = value;

            Spinner.MinValue = min;
            Spinner.MaxValue = max;
            Spinner.Value    = value;
            Spinner.Step     = (max - min) / 100;

            var disposed = Disposed.Where(identity);

            Slider.OnValueChange().Merge(Spinner.OnValueChange())
            .TakeUntil(disposed)
            .Subscribe(v => Morph.Value = v, this);

            Morph.OnChange
            .Do(v => Slider.Value  = v)
            .Do(v => Spinner.Value = v)
            .TakeUntil(disposed)
            .Subscribe(this);
        }
示例#2
0
        protected override void PostConstruct()
        {
            base.PostConstruct();

            if (PauseWhenVisible)
            {
                Node.GetTree().Paused = true;
            }
            else
            {
                _initialActiveState = PlayerControl.Active;

                PlayerControl.Active = false;
            }

            Input.SetMouseMode(Input.MouseMode.Visible);

            CloseAction.Iter(action =>
            {
                Node.OnUnhandledInput()
                .Where(e => e.IsActionPressed(action) && !e.IsEcho())
                .Do(_ => Node.GetTree().SetInputAsHandled())
                .TakeUntil(Disposed.Where(identity))
                .Subscribe(_ => Resume(), this);
            });
        }
示例#3
0
        protected override void PostConstruct()
        {
            base.PostConstruct();

            if (Logger.IsEnabled(LogLevel.Debug))
            {
                Slots.Values.Iter(s => this.LogDebug("Found slot: '{}'.", s));

                this.LogDebug("Equipping initial items.");
            }

            if (Holder is IAnimatable animatable)
            {
                animatable.AnimationManager.OnAnimationEvent
                .Where(e => e.Path.HeadOrNone().Contains(AnimationEventPrefix))
                .Select(e => e.Path
                        .Skip(1)
                        .HeadOrNone()
                        .Bind(this.FindItemInSlot)
                        .Map(v => (@event: e, equipment: v)).ToObservable())
                .Switch()
                .TakeUntil(Disposed.Where(identity))
                .Subscribe(v => AnimateEquipment(v.equipment, v.@event), this);
            }

            Items.Values.Iter(v => v.Equip(Holder));
        }
示例#4
0
        protected override void PostConstruct()
        {
            base.PostConstruct();

            Tree.CreateItem();

            Tree.SetColumnTitle(0, Translate("ui.InventoryView.name"));
            Tree.SetColumnTitle(1, Translate("ui.InventoryView.type"));
            Tree.SetColumnTitle(2, Translate("ui.InventoryView.slot"));
            Tree.SetColumnTitle(3, Translate("ui.InventoryView.weight"));

            Tree.SetColumnTitlesVisible(true);

            var onDispose = Disposed.Where(identity);

            OnItemsChange
            .Do(_ => Tree.RemoveAllNodes())
            .CombineLatest(OnEquipmentContainerChange, (list, parent) => (list, parent))
            .TakeUntil(onDispose)
            .Subscribe(t => t.list.ToList().ForEach(item => CreateNode(item, t.parent)), this);

            OnSelectionChange
            .TakeUntil(onDispose)
            .Subscribe(DisplayItem, this);
        }
示例#5
0
        protected virtual void PlayAnimation(
            IEquipmentHolder holder,
            Equipment equipment,
            Option <Node> dropTo,
            Godot.Animation animation,
            IAnimationManager animationManager,
            InteractionContext context)
        {
            animationManager.OnAnimationEvent
            .OfType <TriggerEvent>()
            .Where(e => e.Name == "Action" && e.Argument.Contains(Key))
            .Take(1)
            .TakeUntil(Disposed.Where(identity))
            .Subscribe(_ => Unequip(holder, equipment, dropTo, context), this);

            if (animationManager is IAnimationStateManager stateManager &&
                AnimatorPath.IsSome && StatesPath.IsSome)
            {
                (
                    from animator in AnimatorPath.Bind(stateManager.FindAnimator)
                    from states in StatesPath.Bind(stateManager.FindStates)
                    from state in ActionState
                    select(animator, states, state)).Iter(t =>
                {
                    t.animator.Animation = animation;
                    t.states.Playback.Travel(t.state);
                });
            }
示例#6
0
        protected override void PostConstruct()
        {
            base.PostConstruct();

            OnChange
            .TakeUntil(Disposed.Where(identity))
            .Subscribe(Apply, this);
        }
        protected override void PostConstruct()
        {
            base.PostConstruct();

            OnActiveStateChange
            .Where(v => !v && Valid)
            .TakeUntil(Disposed.Where(identity))
            .Subscribe(_ => ResetAnimations(), this);
        }
示例#8
0
        protected override void PostConstruct()
        {
            base.PostConstruct();

            Input
            .Where(v => v && Active)
            .Select(_ => CreateActionContext())
            .Where(c => c.Exists(AllowedFor))
            .TakeUntil(Disposed.Where(identity))
            .Subscribe(c => c.Iter(Execute), this);
        }
示例#9
0
        protected override void PostConstruct()
        {
            base.PostConstruct();

            OnTrigger
            .TakeUntil(Disposed.Where(identity))
            .Where(_ => Animation.IsSome)
            .Subscribe(_ => Context.AnimationTree.Set(Parameter, true), this);

            if (Logger.IsEnabled(LogLevel.Trace))
            {
                OnTrigger
                .TakeUntil(Disposed.Where(identity))
                .Subscribe(_ => this.LogTrace("Animation was triggered."), this);
            }
        }
示例#10
0
        protected override void PostConstruct()
        {
            base.PostConstruct();

            var disposed = Disposed.Where(identity);

            OnItemsChange
            .Do(items => Items = items)
            .TakeUntil(disposed)
            .Subscribe(HandleItemsChange, this);

            OnNavigate
            .Do(_ => UpLabel.Iter(l => l.Active = this.CanGoUp()))
            .Select(i => i.Bind(v => v.GetPath()).Reverse())
            .Select(p => string.Join(" > ", p.Map(v => v.DisplayName)))
            .TakeUntil(disposed)
            .Subscribe(v => Breadcrumb.Iter(b => b.Text = v), this);

            UpLabel
            .Map(l => l.OnAction)
            .ToObservable()
            .Switch()
            .TakeUntil(disposed)
            .Subscribe(_ => this.GoUp());
            CloseLabel
            .Map(l => l.OnAction)
            .ToObservable()
            .Switch()
            .TakeUntil(disposed)
            .Subscribe(_ => this.Hide());

            Node.OnVisibilityChange()
            .StartWith(Visible)
            .TakeUntil(disposed)
            .Subscribe(OnVisibilityChanged, this);

            if (BackAction.IsSome)
            {
                Node.OnUnhandledInput()
                .Where(e => BackAction.Exists(v => e.IsActionPressed(v)) && this.CanGoUp())
                .TakeUntil(disposed)
                .Do(_ => Node.GetTree().SetInputAsHandled())
                .Subscribe(_ => this.GoUp(), this);
            }
        }
示例#11
0
        protected override void PostConstruct()
        {
            base.PostConstruct();

            Unit OnAttributeChange(IPlayerControl control, T service)
            {
                var attribute = control.OnCharacterChange
                                .Select(c => c.Bind(v => v.Attributes.TryGetValue(Attribute)));

                attribute
                .TakeUntil(Disposed.Where(identity))
                .Subscribe(a => Service.Iter(s => s.Attribute = a));

                return(Unit.Default);
            }

            PlayerControl.SelectMany(_ => Service.ToOption(), OnAttributeChange);
        }
示例#12
0
        protected override void PostConstruct()
        {
            base.PostConstruct();

            RotationInput
            .Select(v => v * 0.02f)
            .TakeUntil(Disposed.Where(identity))
            .Subscribe(v => Rotation -= v, this);
            ZoomInput
            .TakeUntil(Disposed.Where(identity))
            .Subscribe(v => Distance -= v * 0.15f, this);

            OnActiveStateChange
            .Do(v => _rotationInput.Iter(i => i.Active = v))
            .Do(v => _zoomInput.Iter(i => i.Active     = v))
            .TakeUntil(Disposed.Where(identity))
            .Subscribe(this);
        }
示例#13
0
        private void Start(Lst <MeshSet> meshSets)
        {
            var tasks = meshSets.Bind(m => m.Tasks).Filter(t => t.State != TaskState.UpToDate).Freeze();

            ProgressBar.Value    = 0;
            ProgressBar.MaxValue = tasks.Count();

            var process = tasks.ToObservable()
                          .TakeUntil(Disposed.Where(identity))
                          .ObserveOn(Scheduler.Default)
                          .Do(t => t.Run(this, LoggerFactory))
                          .Do(t =>
            {
                var selected = Optional(SourceList.GetSelected()).Map(i => i.GetText(0));

                if (selected.Contains(t.Parent.Key))
                {
                    MorphList.GetRoot()
                    .Children()
                    .Find(i => i.GetText(0) == t.Key && i.GetText(1) == t.Surface)
                    .Iter(n => n.SetText(2, Translate($"ui.BlendMapGenerator.status.{t.State}")));
                }
            })
                          .Publish();

            process
            .SubscribeOn(Node.GetScheduler())
            .Subscribe(_ => ProgressBar.Value += 1, this);

            var running = process.Select(_ => false).TakeLast(1).StartWith(tasks.Any());

            running
            .SubscribeOn(Node.GetScheduler())
            .Subscribe(v =>
            {
                StartButton.Disabled  = v;
                InputButton.Disabled  = v;
                InputEdit.Editable    = !v;
                OutputButton.Disabled = v;
                OutputEdit.Editable   = !v;
            }, this);

            process.Connect();
        }
示例#14
0
        protected override void PostConstruct()
        {
            base.PostConstruct();

            Label.Text = Morph.DisplayName;

            Button.EditAlpha = Morph.Definition.UseAlpha;

            var disposed = Disposed.Where(identity);

            Button.OnColorChange()
            .Select(v => Morph.Definition.UseAlpha ? v : ToOpaqueColor(v))
            .TakeUntil(disposed)
            .Subscribe(v => Morph.Value = v, this);

            Morph.OnChange
            .TakeUntil(disposed)
            .Subscribe(v => Button.Color = v, this);
        }
示例#15
0
        protected override void PostConstruct()
        {
            base.PostConstruct();

            var disposed = Disposed.Where(identity);

            OnValueChange
            .Where(_ => Visible)
            .TakeUntil(disposed)
            .Subscribe(v => ProgressBar.Value = v, this);

            OnMinChange
            .Where(_ => Visible)
            .TakeUntil(disposed)
            .Subscribe(v => ProgressBar.MinValue = v, this);

            OnMaxChange
            .Where(_ => Visible)
            .TakeUntil(disposed)
            .Subscribe(v => ProgressBar.MaxValue = v, this);
        }
示例#16
0
        public MeleeToolConfiguration(
            string key,
            string slot,
            Set <string> additionalSlots,
            Set <string> tags,
            Option <IInputBindings> armInput,
            Option <IInputBindings> swingInput,
            Godot.Animation swingAnimation,
            string statesPath,
            string seekerPath,
            string idleState,
            string swingState,
            Range <float> animationRange,
            IPlayerControl playerControl,
            bool active,
            ILoggerFactory loggerFactory) : base(key, slot, additionalSlots, tags, active, loggerFactory)
        {
            Ensure.That(swingAnimation, nameof(swingAnimation)).IsNotNull();
            Ensure.That(statesPath, nameof(statesPath)).IsNotNullOrEmpty();
            Ensure.That(seekerPath, nameof(seekerPath)).IsNotNullOrEmpty();
            Ensure.That(idleState, nameof(idleState)).IsNotNullOrEmpty();
            Ensure.That(swingState, nameof(swingState)).IsNotNullOrEmpty();
            Ensure.That(playerControl, nameof(playerControl)).IsNotNull();

            SwingAnimation = swingAnimation;
            StatesPath     = statesPath;
            SeekerPath     = seekerPath;
            IdleState      = idleState;
            SwingState     = swingState;
            AnimationRange = animationRange;
            PlayerControl  = playerControl;

            OnPlayerChange = OnHolderChange
                             .CombineLatest(PlayerControl.OnCharacterChange, (h, p) => h.Filter(v => p.Contains(v)))
                             .Select(p => p.ToObservable())
                             .Switch();

            SwingInput = swingInput
                         .Bind(i => i.AsVector2Input())
                         .MatchObservable(identity, Empty <Vector2>)
                         .Where(_ => Valid)
                         .Select(v => v * 2f);

            ArmInput = armInput.Bind(i => i.FindTrigger().HeadOrNone())
                       .MatchObservable(identity, Empty <bool>)
                       .Where(_ => Valid);

            var manager = OnPlayerChange
                          .Select(p => p.AnimationManager)
                          .OfType <IAnimationStateManager>();

            var states = manager
                         .Select(m => m.FindStates(StatesPath).ToObservable())
                         .Switch();

            var seeker = manager
                         .Select(m => m.FindSeekableAnimator(SeekerPath).ToObservable())
                         .Switch();

            var blender = manager
                          .Select(m => AnimationBlend.Bind(m.FindBlender).ToObservable())
                          .Switch();

            OnArm = OnPlayerChange
                    .Select(_ => ArmInput.Where(identity))
                    .Switch()
                    .AsUnitObservable()
                    .WithLatestFrom(states, (a, s) => s.State == IdleState ? Return(a) : Empty <Unit>())
                    .Switch();

            OnDisarm = Merge(
                ArmInput.Where(v => !v).AsUnitObservable(),
                OnHolderChange.Where(v => v.IsNone).AsUnitObservable(),
                Disposed.Where(identity).AsUnitObservable())
                       .WithLatestFrom(states, (a, s) => s.State == SwingState ? Return(a) : Empty <Unit>())
                       .Switch();

            OnSwing = OnArm
                      .Select(_ => SwingInput.TakeUntil(OnDisarm).Select(v => v.y).DistinctUntilChanged())
                      .Switch()
                      .Scan(0f, (s, v) => Mathf.Clamp(s + v, 0f, 600f))
                      .Select(v => v / 600f);

            bool Conflicts(IInput input) => swingInput.Bind(i => i.Inputs.Values).Exists(input.ConflictsWith);

            ConflictingInputs = playerControl.Inputs.Filter(i => i.Active).Filter(Conflicts);
        }
示例#17
0
        protected override void PostConstruct()
        {
            base.PostConstruct();

            SourceList.CreateItem();

            SourceList.SetColumnTitle(0, Translate("ui.BlendMapGenerator.name"));
            SourceList.SetColumnTitle(1, Translate("ui.BlendMapGenerator.path"));

            SourceList.SetColumnTitlesVisible(true);

            MorphList.CreateItem();

            MorphList.SetColumnTitle(0, Translate("ui.BlendMapGenerator.name"));
            MorphList.SetColumnTitle(1, Translate("ui.BlendMapGenerator.surface"));
            MorphList.SetColumnTitle(2, Translate("ui.BlendMapGenerator.status"));
            MorphList.SetColumnTitle(3, Translate("ui.BlendMapGenerator.path"));

            MorphList.SetColumnTitlesVisible(true);

            FileDialog.CurrentDir = "res://";

            var disposed = Disposed.Where(identity);

            IObservable <Option <DirectoryInfo> > ObserveDirectoryChange(
                Button button,
                LineEdit edit,
                // This is a horrible workaround for a problem that Popup.popup_hide never gets fired.
                IObservable <Unit> anotherButtonPressed)
            {
                Option <DirectoryInfo> Validate(string path)
                {
                    var dir = new DirectoryInfo(path);

                    return(dir.IsDirectory && dir.Exists ? Some(dir) : None);
                }

                var fromChooser = button.OnPress()
                                  .Do(_ =>
                {
                    FileDialog.Mode = ModeEnum.OpenDir;
                    FileDialog.ShowModal(true);
                })
                                  .Select(_ => FileDialog.OnSelectDirectory().Select(Some).TakeUntil(anotherButtonPressed))
                                  .Switch()
                                  .Do(dir => edit.Text = dir.Map(v => v.Path).IfNone(""));

                var fromEdit = edit.OnTextChanged()
                               .Select(Validate)
                               .Do(dir => dir.Iter(v => FileDialog.CurrentDir = v.Path));

                return(fromChooser.Merge(fromEdit));
            }

            var onInputChange  = ObserveDirectoryChange(InputButton, InputEdit, OutputButton.OnPress());
            var onOutputChange = ObserveDirectoryChange(OutputButton, OutputEdit, InputButton.OnPress());

            IObservable <Option <string> > ToErrorMessage(IObservable <Option <DirectoryInfo> > value, string msg) =>
            value.StartWith(None).Select(v => v.IsSome ? None : Some(msg));

            var message = Observable.CombineLatest(
                ToErrorMessage(onInputChange, Translate("error.invalid.directory.input")),
                ToErrorMessage(onOutputChange, Translate("error.invalid.directory.output")),
                (m1, m2) => m1.Concat(m2).HeadOrNone().IfNone("")
                ).Skip(1);

            message
            .TakeUntil(disposed)
            .Subscribe(m => InfoLabel.Text = m, this);

            var onDirectoryChange = Observable.CombineLatest(
                onInputChange.Select(v => v.ToObservable()).Switch(),
                onOutputChange.Select(v => v.ToObservable()).Switch(),
                (input, output) => new Paths(input, output));

            var meshes = onDirectoryChange
                         .Select(v => v.Input.Contents)
                         .Select(FindMeshes)
                         .Select(v => v.Cast <FileInfo>().Freeze());

            var meshSets = meshes.CombineLatest(onDirectoryChange, (files, paths) => (files, paths))
                           .Do(v => Logger.LogDebug("Searching for base meshes in '{}'.", v.paths.Input.Path))
                           .Select(v => v.files.Bind(f => MeshSet.TryCreate(f, v.paths, v.files, Logger)))
                           .Select(v => v.Freeze());

            var shownTasks = SourceList.OnItemSelect()
                             .Select(v => v.Map(i => i.GetText(0)))
                             .WithLatestFrom(meshSets, (key, s) => s.Find(v => v.Key == key).Bind(v => v.Tasks));

            var valid = meshSets.Select(set => set.Bind(v => v.Tasks).Any(t => t.State != TaskState.UpToDate));

            meshSets
            .Throttle(TimeSpan.FromSeconds(1))
            .TakeUntil(disposed)
            .Do(_ => SourceList.RemoveAllNodes())
            .Do(v => v.Iter(CreateNode))
            .Do(_ => SourceList.GetRoot().Children().HeadOrNone().Iter(c => c.Select(0)))
            .Subscribe(this);

            shownTasks
            .TakeUntil(disposed)
            .Do(_ => MorphList.RemoveAllNodes())
            .Subscribe(v => v.Iter(CreateNode), this);

            valid
            .TakeUntil(disposed)
            .Subscribe(v => StartButton.Disabled = !v, this);

            StartButton.OnPress()
            .WithLatestFrom(meshSets, (_, v) => v)
            .TakeUntil(disposed)
            .Subscribe(Start, this);

            CloseButton.OnPress()
            .TakeUntil(disposed)
            .Subscribe(_ => Quit(), this);
        }
示例#18
0
        public override void OnEquip(IEquipmentHolder holder, Equipment equipment)
        {
            base.OnEquip(holder, equipment);

            foreach (var mesh in equipment.Meshes)
            {
                equipment.Spatial.Transform = new Transform(Basis.Identity, Vector3.Zero);

                mesh.Skeleton = mesh.GetPathTo(holder.Skeleton);
            }

            UnregisterBlendShapeListeners();

            if (!(holder is IMeshObject obj && holder is IAnimatable animatable))
            {
                return;
            }

            IEnumerable <string> FindBlendShapes(Object m) =>
            m.GetPropertyList()
            .OfType <Dictionary>()
            .Select(d => d["name"])
            .OfType <string>()
            .Where(k => k.StartsWith("blend_shapes/"));

            IDictionary <string, MeshInstance> GetBlendShapeMap(IEnumerable <MeshInstance> meshes) =>
            meshes
            .Select(m => (mesh: m, blendshapes: FindBlendShapes(m)))
            .Where(i => i.blendshapes.Any())
            .Bind(i => i.blendshapes.Select(b => (blendshape: b, i.mesh)))
            .ToDictionary(i => i.blendshape, i => i.mesh);

            var sources = GetBlendShapeMap(obj.Meshes.Where(m => MeshesToSync.Contains(m.Name)));

            if (!sources.Any())
            {
                return;
            }

            var targets = GetBlendShapeMap(equipment.Meshes);

            _blendShapeMappings = toSet(sources
                                        .Filter(i => targets.ContainsKey(i.Key))
                                        .Select(i => new BlendShapeMapping(i.Key, i.Value, targets[i.Key])));

            if (!_blendShapeMappings.Any())
            {
                return;
            }

            _blendShapeListener = Some(
                animatable.AnimationManager.OnAdvance
                .TakeUntil(Disposed.Where(identity))
                .Subscribe(_ =>
            {
                foreach (var mapping in _blendShapeMappings)
                {
                    var value = mapping.Source.Get(mapping.Key);

                    mapping.Target.Set(mapping.Key, value);
                }
            }, e => this.LogError(e, "Blend shape listener terminated with an error.")));
        }
示例#19
0
        protected override void PostConstruct()
        {
            base.PostConstruct();

            var onFocus = PlayerControl.OnFocusChange;
            var ticks   = TimeSource.OnProcess;

            var showTitle = onFocus.Select(e => e.IsSome);
            var entity    = onFocus.Where(e => e.IsSome).Select(e => e.First());
            var title     = entity.Select(e => e.DisplayName);

            var action = ticks
                         .CombineLatest(entity, (_, e) => e)
                         .Select(target => PlayerControl.Character
                                 .Bind(p => p.FindAction(new InteractionContext(p, target), a => a is Interaction))
                                 .Map(a => a.DisplayName)
                                 .HeadOrNone());

            var showAction = action.Select(a => a.IsSome);

            var position = ticks
                           .CombineLatest(entity, (_, e) => e)
                           .Select(e => PlayerControl.Camera.UnprojectPosition(e.LabelPosition))
                           .Select(pos => new Vector2(pos.x - Node.RectSize.x / 2f, pos.y - Node.RectSize.y / 2f));

            var onDispose = Disposed.Where(identity);

            showTitle
            .TakeUntil(onDispose)
            .Subscribe(v => Node.Visible = v, this);

            ActionPanel.Iter(panel =>
            {
                showAction
                .TakeUntil(onDispose)
                .Subscribe(v => panel.Visible = v, this);
            });

            title
            .TakeUntil(onDispose)
            .Subscribe(v => TitleLabel.Text = v, this);

            ActionLabel.Iter(label =>
            {
                action
                .TakeUntil(onDispose)
                .Subscribe(a => a.Iter(v => label.Text = v), this);
            });

            position
            .TakeUntil(onDispose)
            .Subscribe(pos => Node.RectPosition = pos.Round(), this);

            var shortcut = InputMap
                           .GetActionList(InteractAction)
                           .OfType <InputEvent>()
                           .Bind(e => e.FindKeyLabel())
                           .HeadOrNone()
                           .IfNone(DefaultKeyLabel);

            //TODO Handle key mapping changes.
            ShortcutLabel.Iter(l => l.Text = shortcut);
        }
示例#20
0
        protected override void PostConstruct()
        {
            base.PostConstruct();

            var manager = OnPlayerChange
                          .Select(p => p.AnimationManager)
                          .OfType <IAnimationStateManager>();

            var states = manager
                         .Select(m => m.FindStates(StatesPath).ToObservable())
                         .Switch();

            var seeker = manager
                         .Select(m => m.FindSeekableAnimator(SeekerPath).ToObservable())
                         .Switch();

            var blender = manager
                          .Select(m => AnimationBlend.Bind(m.FindBlender).ToObservable())
                          .Switch();

            var conflictingInputs = OnArm.Select(_ => ConflictingInputs.Freeze());

            var disposed = Disposed.Where(identity);

            // Change animation.
            OnArm
            .Select(_ => seeker.TakeUntil(OnDisarm))
            .Switch()
            .TakeUntil(disposed)
            .Subscribe(s => s.Animation = SwingAnimation, this);

            // Change animation state.
            OnArm
            .Select(_ => states)
            .Switch()
            .TakeUntil(disposed)
            .Do(_ => Logger.LogDebug("Entering armed state."))
            .Subscribe(s => s.State = SwingState, this);

            OnArm
            .Select(_ => OnDisarm)
            .Switch()
            .Select(_ => states)
            .Switch()
            .TakeUntil(disposed)
            .Do(_ => Logger.LogDebug("Exiting armed state."))
            .Subscribe(s => s.State = IdleState, this);

            // Lock view rotation.
            conflictingInputs
            .Do(_ => Logger.LogDebug("Deactivating conflicting view controls."))
            .Do(i => i.Iter(v => v.Deactivate()))
            .Select(v => OnDisarm.Select(_ => v))
            .Switch()
            .Do(_ => Logger.LogDebug("Activating conflicting view controls."))
            .Do(i => i.Iter(v => v.Activate()))
            .TakeUntil(disposed)
            .Subscribe(this);

            var minPos = AnimationRange.Min;
            var maxPos = AnimationRange.Max > AnimationRange.Min
                ? AnimationRange.Max
                : Animation.Map(a => a.Length).IfNone(1f);

            Logger.LogDebug($"Using animation range: {minPos} - {maxPos}.");

            // Handle swing motion.
            OnSwing.Select(v => v * (maxPos - minPos) + minPos)
            .SelectMany(v => seeker, (v, s) => (v, s))
            .TakeUntil(disposed)
            .Do(t => Logger.LogDebug("Changing animation position: {}.", t.v))
            .Subscribe(t => t.s.Position = t.v, this);

            // Blend/unblend holding animation.
            OnArm
            .Select(_ => blender.TakeUntil(OnDisarm))
            .Switch()
            .Do(_ => Logger.LogDebug("Overriding default animation: '{}'.", Animation))
            .TakeUntil(disposed)
            .Subscribe(b => b.Unblend(AnimationTransition), this);

            OnArm
            .Select(_ => OnDisarm)
            .Switch()
            .Select(_ => blender)
            .Switch()
            .Do(_ => Logger.LogDebug("Restoring default animation: '{}'.", Animation))
            .TakeUntil(disposed)
            .Subscribe(
                b => Animation.Iter(a => b.Blend(a, transition: AnimationTransition)),
                this);
        }