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); }
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); }); }
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)); }
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); }
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); }); }
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); }
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); }
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); } }
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); } }
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); }
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); }
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(); }
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); }
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); }
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); }
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); }
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."))); }
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); }
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); }