Code commit
This commit is contained in:
35
CustomizePlus/Game/Services/ChatService.cs
Normal file
35
CustomizePlus/Game/Services/ChatService.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Plugin.Services;
|
||||
|
||||
namespace CustomizePlus.Game.Services;
|
||||
|
||||
public class ChatService
|
||||
{
|
||||
private readonly IChatGui _chatGui;
|
||||
|
||||
public ChatService(IChatGui chatGui)
|
||||
{
|
||||
_chatGui = chatGui;
|
||||
}
|
||||
|
||||
public void PrintInChat(string message, ChatMessageColor color = ChatMessageColor.Info)
|
||||
{
|
||||
var stringBuilder = new SeStringBuilder();
|
||||
stringBuilder.AddUiForeground((ushort)color);
|
||||
stringBuilder.AddText($"[Customize+] {message}");
|
||||
stringBuilder.AddUiForegroundOff();
|
||||
_chatGui.Print(stringBuilder.BuiltString);
|
||||
}
|
||||
|
||||
public void PrintInChat(SeString seString)
|
||||
{
|
||||
_chatGui.Print(seString);
|
||||
}
|
||||
|
||||
public enum ChatMessageColor : ushort
|
||||
{
|
||||
Info = 45,
|
||||
Warning = 500,
|
||||
Error = 14
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using Dalamud.Game;
|
||||
|
||||
namespace CustomizePlus.Game.Services.GPose.ExternalTools;
|
||||
|
||||
/// <summary>
|
||||
/// Service which detects if Anamnesis/Ktisis posing mode is enabled.
|
||||
/// </summary>
|
||||
public class PosingModeDetectService
|
||||
{
|
||||
// Borrowed from Ktisis:
|
||||
// If this is NOP'd, Anam posing is enabled.
|
||||
internal static unsafe byte* AnamnesisFreezePosition;
|
||||
internal static unsafe byte* AnamnesisFreezeRotation;
|
||||
internal static unsafe byte* AnamnesisFreezeScale;
|
||||
|
||||
internal static unsafe bool IsAnamnesisPositionFrozen =>
|
||||
AnamnesisFreezePosition != null && *AnamnesisFreezePosition == 0x90 || *AnamnesisFreezePosition == 0x00;
|
||||
|
||||
internal static unsafe bool IsAnamnesisRotationFrozen =>
|
||||
AnamnesisFreezeRotation != null && *AnamnesisFreezeRotation == 0x90 || *AnamnesisFreezeRotation == 0x00;
|
||||
|
||||
internal static unsafe bool IsAnamnesisScalingFrozen =>
|
||||
AnamnesisFreezeScale != null && *AnamnesisFreezeScale == 0x90 || *AnamnesisFreezeScale == 0x00;
|
||||
|
||||
internal static bool IsAnamnesis =>
|
||||
IsAnamnesisPositionFrozen || IsAnamnesisRotationFrozen || IsAnamnesisScalingFrozen;
|
||||
|
||||
public bool IsInPosingMode => IsAnamnesis; //Can't detect Ktisis for now
|
||||
|
||||
public unsafe PosingModeDetectService(ISigScanner sigScanner)
|
||||
{
|
||||
AnamnesisFreezePosition = (byte*)sigScanner.ScanText("41 0F 29 24 12");
|
||||
AnamnesisFreezeRotation = (byte*)sigScanner.ScanText("41 0F 29 5C 12 10");
|
||||
AnamnesisFreezeScale = (byte*)sigScanner.ScanText("41 0F 29 44 12 20");
|
||||
}
|
||||
}
|
||||
138
CustomizePlus/Game/Services/GPose/GPoseService.cs
Normal file
138
CustomizePlus/Game/Services/GPose/GPoseService.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
using System;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Framework;
|
||||
using OtterGui.Log;
|
||||
using CustomizePlus.Game.Events;
|
||||
|
||||
namespace CustomizePlus.Game.Services.GPose;
|
||||
|
||||
public class GPoseService : IDisposable
|
||||
{
|
||||
private readonly ChatService _chatService;
|
||||
private readonly GPoseStateChanged _event;
|
||||
private readonly Logger _logger;
|
||||
|
||||
private Hook<EnterGPoseDelegate>? _enterGPoseHook;
|
||||
private Hook<ExitGPoseDelegate>? _exitGPoseHook;
|
||||
|
||||
private bool _fakeGPose;
|
||||
public GPoseState GPoseState { get; private set; }
|
||||
public bool IsInGPose => GPoseState == GPoseState.Inside;
|
||||
|
||||
public bool FakeGPose
|
||||
{
|
||||
get => _fakeGPose;
|
||||
|
||||
set
|
||||
{
|
||||
if (value != _fakeGPose)
|
||||
{
|
||||
if (!value)
|
||||
{
|
||||
_fakeGPose = false;
|
||||
HandleGPoseChange(GPoseState.Exiting);
|
||||
HandleGPoseChange(GPoseState.Outside);
|
||||
}
|
||||
else
|
||||
{
|
||||
HandleGPoseChange(GPoseState.Inside);
|
||||
_fakeGPose = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe GPoseService(
|
||||
IClientState clientState,
|
||||
IGameInteropProvider hooker,
|
||||
ChatService chatService,
|
||||
GPoseStateChanged @event,
|
||||
Logger logger)
|
||||
{
|
||||
_chatService = chatService;
|
||||
_event = @event;
|
||||
_logger = logger;
|
||||
|
||||
GPoseState = clientState.IsGPosing ? GPoseState.Inside : GPoseState.Outside;
|
||||
|
||||
var uiModule = Framework.Instance()->GetUiModule();
|
||||
var enterGPoseAddress = (nint)uiModule->VTable->EnterGPose;
|
||||
var exitGPoseAddress = (nint)uiModule->VTable->ExitGPose;
|
||||
|
||||
_enterGPoseHook = hooker.HookFromAddress<EnterGPoseDelegate>(enterGPoseAddress, EnteringGPoseDetour);
|
||||
_enterGPoseHook.Enable();
|
||||
|
||||
_exitGPoseHook = hooker.HookFromAddress<ExitGPoseDelegate>(exitGPoseAddress, ExitingGPoseDetour);
|
||||
_exitGPoseHook.Enable();
|
||||
}
|
||||
|
||||
private void ExitingGPoseDetour(nint addr)
|
||||
{
|
||||
if (HandleGPoseChange(GPoseState.AttemptExit))
|
||||
{
|
||||
HandleGPoseChange(GPoseState.Exiting);
|
||||
_exitGPoseHook!.Original.Invoke(addr);
|
||||
HandleGPoseChange(GPoseState.Outside);
|
||||
}
|
||||
}
|
||||
|
||||
private bool EnteringGPoseDetour(nint addr)
|
||||
{
|
||||
var didEnter = _enterGPoseHook!.Original.Invoke(addr);
|
||||
if (didEnter)
|
||||
{
|
||||
_fakeGPose = false;
|
||||
HandleGPoseChange(GPoseState.Inside);
|
||||
}
|
||||
|
||||
return didEnter;
|
||||
}
|
||||
|
||||
private bool HandleGPoseChange(GPoseState state)
|
||||
{
|
||||
if (state == GPoseState || _fakeGPose)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
GPoseState = state;
|
||||
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case GPoseState.Inside:
|
||||
_event.Invoke(GPoseStateChanged.Type.Entered);
|
||||
break;
|
||||
case GPoseState.AttemptExit:
|
||||
_event.Invoke(GPoseStateChanged.Type.AttemptingExit);
|
||||
break;
|
||||
case GPoseState.Exiting:
|
||||
_event.Invoke(GPoseStateChanged.Type.Exiting);
|
||||
break;
|
||||
case GPoseState.Outside:
|
||||
_event.Invoke(GPoseStateChanged.Type.Exited);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_exitGPoseHook?.Dispose();
|
||||
_enterGPoseHook?.Dispose();
|
||||
}
|
||||
|
||||
private delegate void ExitGPoseDelegate(nint addr);
|
||||
|
||||
private delegate bool EnterGPoseDelegate(nint addr);
|
||||
}
|
||||
|
||||
public enum GPoseState
|
||||
{
|
||||
Inside,
|
||||
AttemptExit,
|
||||
Exiting,
|
||||
Outside
|
||||
}
|
||||
71
CustomizePlus/Game/Services/GameObjectService.cs
Normal file
71
CustomizePlus/Game/Services/GameObjectService.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using Dalamud.Plugin.Services;
|
||||
using Penumbra.GameData.Actors;
|
||||
using System.Collections.Generic;
|
||||
using CustomizePlus.Core.Data;
|
||||
using CustomizePlus.GameData.Data;
|
||||
using CustomizePlus.GameData.Services;
|
||||
using CustomizePlus.GameData.Extensions;
|
||||
|
||||
namespace CustomizePlus.Game.Services;
|
||||
|
||||
public class GameObjectService
|
||||
{
|
||||
private readonly ActorService _actorService;
|
||||
private readonly IObjectTable _objectTable;
|
||||
private readonly ObjectManager _objectManager;
|
||||
|
||||
public GameObjectService(ActorService actorService, IObjectTable objectTable, ObjectManager objectManager)
|
||||
{
|
||||
_actorService = actorService;
|
||||
_objectTable = objectTable;
|
||||
_objectManager = objectManager;
|
||||
}
|
||||
|
||||
public string GetCurrentPlayerName()
|
||||
{
|
||||
return _objectManager.PlayerData.Identifier.ToName();
|
||||
}
|
||||
|
||||
public string GetCurrentPlayerTargetName()
|
||||
{
|
||||
return _objectManager.TargetData.Identifier.ToNameWithoutOwnerName();
|
||||
}
|
||||
|
||||
public bool IsActorHasScalableRoot(Actor actor)
|
||||
{
|
||||
if (!actor.Identifier(_actorService.AwaitedService, out var identifier))
|
||||
return false;
|
||||
|
||||
return !Constants.IsInObjectTableBusyNPCRange(actor.Index.Index)
|
||||
&& (identifier.IsAllowedForProfiles()
|
||||
|| actor == _objectTable.GetObjectAddress(0));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Case sensitive
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<(ActorIdentifier, Actor)> FindActorsByName(string name)
|
||||
{
|
||||
foreach (var kvPair in _objectManager)
|
||||
{
|
||||
var identifier = kvPair.Key;
|
||||
|
||||
if (kvPair.Key.Type == IdentifierType.Special)
|
||||
identifier = identifier.GetTrueActorForSpecialType();
|
||||
|
||||
if (!identifier.IsValid)
|
||||
continue;
|
||||
|
||||
if (identifier.ToNameWithoutOwnerName() == name)
|
||||
{
|
||||
if (kvPair.Value.Objects.Count > 1) //in gpose we can have more than a single object for one actor
|
||||
foreach (var obj in kvPair.Value.Objects)
|
||||
yield return (kvPair.Key, obj);
|
||||
else
|
||||
yield return (kvPair.Key, kvPair.Value.Objects[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
21
CustomizePlus/Game/Services/GameStateService.cs
Normal file
21
CustomizePlus/Game/Services/GameStateService.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using CustomizePlus.Game.Services.GPose;
|
||||
using CustomizePlus.Game.Services.GPose.ExternalTools;
|
||||
|
||||
namespace CustomizePlus.Game.Services;
|
||||
|
||||
public class GameStateService
|
||||
{
|
||||
private readonly GPoseService _gposeService;
|
||||
private readonly PosingModeDetectService _posingModeDetectService;
|
||||
|
||||
public GameStateService(GPoseService gposeService, PosingModeDetectService posingModeDetectService)
|
||||
{
|
||||
_gposeService = gposeService;
|
||||
_posingModeDetectService = posingModeDetectService;
|
||||
}
|
||||
|
||||
public bool GameInPosingMode()
|
||||
{
|
||||
return _gposeService.GPoseState == GPoseState.Inside || _posingModeDetectService.IsInPosingMode;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user