-
Notifications
You must be signed in to change notification settings - Fork 163
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #733 from AsgardXIV/animationoverride
Animation Overriding
- Loading branch information
Showing
7 changed files
with
327 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
// © Anamnesis. | ||
// Licensed under the MIT license. | ||
|
||
namespace Anamnesis.Services | ||
{ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Threading.Tasks; | ||
using Anamnesis.Core.Memory; | ||
using Anamnesis.Memory; | ||
using PropertyChanged; | ||
|
||
[AddINotifyPropertyChangedInterface] | ||
public class AnimationService : ServiceBase<AnimationService> | ||
{ | ||
private readonly List<ActorAnimation> animatingActors = new(); | ||
private NopHookViewModel? animationNopHook; | ||
private NopHookViewModel? slowMotionNopHook; | ||
|
||
private bool isEnabled = false; | ||
|
||
public bool Enabled | ||
{ | ||
get => this.isEnabled; | ||
set | ||
{ | ||
if (this.isEnabled != value) | ||
{ | ||
this.SetEnabled(value); | ||
} | ||
} | ||
} | ||
|
||
public override Task Start() | ||
{ | ||
this.animationNopHook = new NopHookViewModel(AddressService.AnimationPatch, 0x7); | ||
this.slowMotionNopHook = new NopHookViewModel(AddressService.SlowMotionPatch, 0x9); | ||
Task.Run(this.CheckThread); | ||
return base.Start(); | ||
} | ||
|
||
public void AnimateActor(ActorMemory actor, uint desiredAnimation, bool slowMotion = false, int repeatAfter = 0) | ||
{ | ||
this.ClearAnimation(actor); | ||
|
||
var animationEntry = new ActorAnimation() | ||
{ | ||
Actor = actor, | ||
AnimationId = desiredAnimation, | ||
SlowMotion = slowMotion, | ||
RepeatAfter = repeatAfter, | ||
}; | ||
|
||
this.animatingActors.Add(animationEntry); | ||
} | ||
|
||
public void ClearAnimation(ActorBasicMemory actor) | ||
{ | ||
this.animatingActors.RemoveAll(p => p.Actor == actor); | ||
} | ||
|
||
public void ClearAll() | ||
{ | ||
this.animatingActors.Clear(); | ||
} | ||
|
||
public override async Task Shutdown() | ||
{ | ||
this.Enabled = false; | ||
|
||
await base.Shutdown(); | ||
} | ||
|
||
private async Task CheckThread() | ||
{ | ||
while (this.IsAlive) | ||
{ | ||
if (this.isEnabled) | ||
{ | ||
if (!GposeService.Instance.IsGpose) | ||
this.Enabled = false; // Should only run in gpose | ||
|
||
foreach (var actor in this.animatingActors) | ||
{ | ||
this.TickActor(actor); | ||
} | ||
} | ||
|
||
await Task.Delay(100); | ||
} | ||
} | ||
|
||
private void TickActor(ActorAnimation animation) | ||
{ | ||
if (animation.Actor != null) | ||
{ | ||
if (!animation.Actor.IsAnimating) | ||
{ | ||
if(animation.State != ActorAnimation.ExecutionState.Begin) | ||
animation.LastPlayed = DateTime.Now; | ||
|
||
return; | ||
} | ||
|
||
if (animation.State == ActorAnimation.ExecutionState.Executed && animation.RepeatAfter == 0) | ||
return; | ||
|
||
if (animation.State == ActorAnimation.ExecutionState.Executed && animation.RepeatAfter != 0) | ||
{ | ||
if (DateTime.Now > animation.LastPlayed.Add(TimeSpan.FromSeconds(animation.RepeatAfter))) | ||
{ | ||
animation.State = ActorAnimation.ExecutionState.Begin; | ||
} | ||
} | ||
|
||
if (animation.State == ActorAnimation.ExecutionState.Executed) | ||
return; | ||
|
||
animation.State = ActorAnimation.ExecutionState.Executing; | ||
|
||
// The flow below is a little confusing, but basically we need to ensure that the animation is reset and then we can play our custom animation. | ||
// First we need to set TargetAnimation to 0 and wait for NextAnimation to tick over to 0 as well. | ||
// Once that's done, we set TargetAnimation to the actual animation we want, and again wait for the engine to tick that into NextAnimation. | ||
// Finally we set TargetAnimation to 0 again which will cause the engine to fire the now queued animation in NextAnimation. | ||
// This takes a couple of frames to work through, which is why this requires a tick thread to drive all that through. | ||
if (animation.Actor.TargetAnimation != animation.AnimationId && animation.Actor.TargetAnimation != 0) | ||
{ | ||
animation.Actor.TargetAnimation = animation.AnimationId; | ||
return; | ||
} | ||
|
||
if (animation.Actor.TargetAnimation == 0 && animation.Actor.NextAnimation == 0) | ||
{ | ||
animation.Actor.TargetAnimation = animation.AnimationId; | ||
animation.Actor.AnimationMode = (byte)(animation.SlowMotion ? 0x3E : 0x3F); | ||
return; | ||
} | ||
|
||
if (animation.Actor.TargetAnimation == animation.AnimationId && animation.Actor.NextAnimation == animation.AnimationId) | ||
{ | ||
animation.Actor.TargetAnimation = 0; | ||
animation.LastPlayed = DateTime.Now; | ||
animation.State = ActorAnimation.ExecutionState.Executed; | ||
return; | ||
} | ||
} | ||
} | ||
|
||
private void SetEnabled(bool enabled) | ||
{ | ||
if (this.isEnabled == enabled) | ||
return; | ||
|
||
if (enabled && !GposeService.Instance.IsGpose) | ||
return; | ||
|
||
this.isEnabled = enabled; | ||
|
||
if (enabled) | ||
{ | ||
this.animationNopHook?.SetEnabled(true); | ||
this.slowMotionNopHook?.SetEnabled(true); | ||
} | ||
else | ||
{ | ||
this.ClearAll(); | ||
this.animationNopHook?.SetEnabled(false); | ||
this.slowMotionNopHook?.SetEnabled(false); | ||
} | ||
} | ||
|
||
private class ActorAnimation | ||
{ | ||
public enum ExecutionState | ||
{ | ||
Begin, | ||
Executing, | ||
Executed, | ||
} | ||
|
||
public ActorMemory? Actor { get; init; } | ||
public uint AnimationId { get; init; } | ||
public bool SlowMotion { get; init; } | ||
public int RepeatAfter { get; init; } | ||
public DateTime LastPlayed { get; set; } = new(); | ||
public ExecutionState State { get; set; } = ExecutionState.Begin; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
<UserControl x:Class="Anamnesis.Views.ActorAnimationView" | ||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | ||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | ||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | ||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | ||
xmlns:local="clr-namespace:Anamnesis.Views" | ||
mc:Ignorable="d" | ||
d:DesignHeight="450" d:DesignWidth="800"> | ||
<Grid Margin="100" IsEnabled="{Binding GPoseService.IsGpose}"> | ||
<Grid.ColumnDefinitions> | ||
<ColumnDefinition/> | ||
<ColumnDefinition/> | ||
</Grid.ColumnDefinitions> | ||
|
||
<Grid.RowDefinitions> | ||
<RowDefinition Height="Auto"/> | ||
<RowDefinition Height="25"/> | ||
<RowDefinition Height="Auto"/> | ||
</Grid.RowDefinitions> | ||
|
||
<Button Grid.Row="0" Grid.Column="0" Click="EnableAction_Click" IsEnabled="{Binding AnimationService.Enabled, Converter={StaticResource NotConverter}}">Enable</Button> | ||
<Button Grid.Row="0" Grid.Column="1" Click="DisableAction_Click" IsEnabled="{Binding AnimationService.Enabled}">Disable</Button> | ||
|
||
<Grid Grid.Row="2" Grid.ColumnSpan="2" IsEnabled="{Binding AnimationService.Enabled}"> | ||
<Grid.ColumnDefinitions> | ||
<ColumnDefinition /> | ||
<ColumnDefinition /> | ||
</Grid.ColumnDefinitions> | ||
|
||
<Grid.RowDefinitions> | ||
<RowDefinition Height="Auto"/> | ||
<RowDefinition Height="Auto"/> | ||
<RowDefinition Height="Auto"/> | ||
<RowDefinition Height="Auto"/> | ||
<RowDefinition Height="20"/> | ||
<RowDefinition Height="Auto"/> | ||
</Grid.RowDefinitions> | ||
|
||
<TextBlock Grid.Column="0" Grid.Row="0" HorizontalAlignment="Right">ID</TextBlock> | ||
<TextBox Grid.Column="1" Grid.Row="0" Style="{StaticResource MaterialDesignTextBox}" Text="{Binding AnimationId}" /> | ||
|
||
<TextBlock Grid.Column="0" Grid.Row="1" HorizontalAlignment="Right">Repeat After</TextBlock> | ||
<TextBox Grid.Column="1" Grid.Row="1" Style="{StaticResource MaterialDesignTextBox}" Text="{Binding RepeatTimer}" /> | ||
|
||
<TextBlock Grid.Column="0" Grid.Row="2" HorizontalAlignment="Right">Slow Motion</TextBlock> | ||
<CheckBox Grid.Column="1" Grid.Row="2" IsChecked="{Binding SlowMotion}" /> | ||
|
||
<Button Grid.Row="3" Grid.Column="1" Click="ApplyAction_Click">Apply</Button> | ||
|
||
<Button Grid.Row="5" Grid.Column="1" Click="IdleAction_Click">Reset</Button> | ||
<Button Grid.Row="5" Grid.Column="0" Click="DrawAction_Click">Draw Weapon</Button> | ||
</Grid> | ||
|
||
</Grid> | ||
</UserControl> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
// © Anamnesis. | ||
// Licensed under the MIT license. | ||
|
||
namespace Anamnesis.Views | ||
{ | ||
using System.Windows.Controls; | ||
using Anamnesis.Services; | ||
|
||
public partial class ActorAnimationView : UserControl | ||
{ | ||
public ActorAnimationView() | ||
{ | ||
this.DataContext = this; | ||
this.AnimationService = AnimationService.Instance; | ||
this.GPoseService = GposeService.Instance; | ||
this.InitializeComponent(); | ||
} | ||
|
||
public uint AnimationId { get; set; } = 8047; | ||
public int RepeatTimer { get; set; } = 0; | ||
public bool SlowMotion { get; set; } = false; | ||
|
||
public AnimationService AnimationService { get; set; } | ||
public GposeService GPoseService { get; set; } | ||
|
||
private void ApplyAction_Click(object sender, System.Windows.RoutedEventArgs e) | ||
{ | ||
this.AnimationService = AnimationService.Instance; | ||
|
||
var selectedActor = TargetService.Instance.SelectedActor; | ||
if (selectedActor != null) | ||
{ | ||
this.AnimationService.AnimateActor(selectedActor, this.AnimationId, this.SlowMotion, this.RepeatTimer); | ||
} | ||
} | ||
|
||
private void IdleAction_Click(object sender, System.Windows.RoutedEventArgs e) | ||
{ | ||
var selectedActor = TargetService.Instance.SelectedActor; | ||
if (selectedActor != null) | ||
{ | ||
this.AnimationService.AnimateActor(selectedActor, 3); | ||
} | ||
} | ||
|
||
private void DrawAction_Click(object sender, System.Windows.RoutedEventArgs e) | ||
{ | ||
var selectedActor = TargetService.Instance.SelectedActor; | ||
if (selectedActor != null) | ||
{ | ||
this.AnimationService.AnimateActor(selectedActor, 190); | ||
} | ||
} | ||
|
||
private void DisableAction_Click(object sender, System.Windows.RoutedEventArgs e) | ||
{ | ||
this.AnimationService.Enabled = false; | ||
} | ||
|
||
private void EnableAction_Click(object sender, System.Windows.RoutedEventArgs e) | ||
{ | ||
this.AnimationService.Enabled = true; | ||
} | ||
} | ||
} |