Skip to content

Commit

Permalink
Merge pull request #733 from AsgardXIV/animationoverride
Browse files Browse the repository at this point in the history
Animation Overriding
  • Loading branch information
Yuki-Codes authored Dec 29, 2021
2 parents 39adf05 + 63b5e8b commit 9717683
Show file tree
Hide file tree
Showing 7 changed files with 327 additions and 0 deletions.
4 changes: 4 additions & 0 deletions Anamnesis/Core/Memory/AddressService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ public class AddressService : ServiceBase<AddressService>
public static IntPtr TimeAsm { get; private set; }
public static IntPtr TimeReal { get; set; }
public static IntPtr PlayerTargetSystem { get; set; }
public static IntPtr AnimationPatch { get; set; }
public static IntPtr SlowMotionPatch { get; set; }

public static IntPtr Camera
{
Expand Down Expand Up @@ -122,6 +124,8 @@ public static async Task Scan()
tasks.Add(GetAddressFromTextSignature("SkeletonFreezePosition", "41 0F 29 24 12", (p) => { SkeletonFreezePosition = p; })); // SkeletonAddress5
tasks.Add(GetAddressFromTextSignature("SkeletonFreezeScale2", "43 0F 29 44 18 20", (p) => { SkeletonFreezeScale2 = p; })); // SkeletonAddress6
tasks.Add(GetAddressFromTextSignature("SkeletonFreezePosition2", "43 0f 29 24 18", (p) => { SkeletonFreezePosition2 = p; })); // SkeletonAddress7
tasks.Add(GetAddressFromTextSignature("AnimationPatch", "66 89 8B D0 00 00 00 48 8B 43 60 48 85 C0", (p) => { AnimationPatch = p; }));
tasks.Add(GetAddressFromTextSignature("SlowMotionPatch", "F3 0F 11 94 ?? ?? ?? ?? ?? 80 89 ?? ?? ?? ?? 01", (p) => { SlowMotionPatch = p; }));
tasks.Add(GetAddressFromSignature("Territory", "8B 1D ?? ?? ?? ?? 0F 45 D8 39 1D", 2, (p) => { Territory = p; }));
tasks.Add(GetAddressFromSignature("Weather", "49 8B 9D ?? ?? ?? ?? 48 8D 0D", 0, (p) => { Weather = p + 0x8; }));
tasks.Add(GetAddressFromSignature("GPoseFilters", "4C 8B 05 ?? ?? ?? ?? 41 8B 80 ?? ?? ?? ?? C1 E8 02", 0, (p) => { GPoseFilters = p; }));
Expand Down
10 changes: 10 additions & 0 deletions Anamnesis/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,16 @@

<posePages:PosePage DataContext="{Binding TargetService.SelectedActor}" />
</TabItem>
<TabItem IsEnabled="{Binding TargetService.SelectedActor, Converter={StaticResource NotNullToBoolConverter}}">
<TabItem.Header>
<fa:IconImage
Foreground="{DynamicResource MaterialDesignBodyLight}"
Icon="Bicycle"
ToolTip="Animate" />
</TabItem.Header>

<views:ActorAnimationView />
</TabItem>
</TabControl>
</Grid>

Expand Down
3 changes: 3 additions & 0 deletions Anamnesis/Memory/ActorMemory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ public enum RenderModes : int
[Bind(0x0CE0)] public WeaponMemory? OffHand { get; set; }
[Bind(0x0DB0)] public ActorEquipmentMemory? Equipment { get; set; }
[Bind(0x0DD8)] public ActorCustomizeMemory? Customize { get; set; }
[Bind(0x0F30)] public uint TargetAnimation { get; set; }
[Bind(0x0F4C)] public uint NextAnimation { get; set; }
[Bind(0x0FA7)] public byte AnimationMode { get; set; }
[Bind(0x18B8)] public float Transparency { get; set; }

public bool AutomaticRefreshEnabled { get; set; } = true;
Expand Down
1 change: 1 addition & 0 deletions Anamnesis/ServiceManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public async Task InitializeServices()
await Add<TipService>();
await Add<TexToolsService>();
await Add<FavoritesService>();
await Add<AnimationService>();
////await Add<AnamnesisConnectService>();

IsInitialized = true;
Expand Down
189 changes: 189 additions & 0 deletions Anamnesis/Services/AnimationService.cs
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;
}
}
}
55 changes: 55 additions & 0 deletions Anamnesis/Views/ActorAnimationView.xaml
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>
65 changes: 65 additions & 0 deletions Anamnesis/Views/ActorAnimationView.xaml.cs
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;
}
}
}

0 comments on commit 9717683

Please sign in to comment.