feat: add talk
This commit is contained in:
parent
f347e4e669
commit
e2f66dd12b
146
XivCommon/Functions/Talk.cs
Executable file
146
XivCommon/Functions/Talk.cs
Executable file
@ -0,0 +1,146 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using Dalamud.Game;
|
||||||
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
|
using Dalamud.Hooking;
|
||||||
|
using Dalamud.Plugin;
|
||||||
|
|
||||||
|
namespace XivCommon.Functions {
|
||||||
|
/// <summary>
|
||||||
|
/// Class containing Talk events
|
||||||
|
/// </summary>
|
||||||
|
public class Talk : IDisposable {
|
||||||
|
// Updated: 5.5
|
||||||
|
private const int TextOffset = 0;
|
||||||
|
private const int NameOffset = 0x10;
|
||||||
|
private const int StyleOffset = 0x38;
|
||||||
|
|
||||||
|
private SeStringManager SeStringManager { get; }
|
||||||
|
|
||||||
|
private delegate void AddonTalkV45Delegate(IntPtr addon, IntPtr a2, IntPtr data);
|
||||||
|
|
||||||
|
private Hook<AddonTalkV45Delegate>? AddonTalkV45Hook { get; }
|
||||||
|
|
||||||
|
private delegate IntPtr SetAtkValueStringDelegate(IntPtr atkValue, IntPtr text);
|
||||||
|
|
||||||
|
private SetAtkValueStringDelegate SetAtkValueString { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The delegate for Talk events.
|
||||||
|
/// </summary>
|
||||||
|
public delegate void TalkEventDelegate(ref SeString name, ref SeString text, ref TalkStyle style);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>
|
||||||
|
/// The event that is fired when NPCs talk.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// Requires the <see cref="Hooks.Talk"/> hook to be enabled.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
public event TalkEventDelegate? OnTalk;
|
||||||
|
|
||||||
|
internal Talk(SigScanner scanner, SeStringManager manager, bool hooksEnabled) {
|
||||||
|
this.SeStringManager = manager;
|
||||||
|
|
||||||
|
var setAtkPtr = scanner.ScanText("E8 ?? ?? ?? ?? 41 03 ED");
|
||||||
|
this.SetAtkValueString = Marshal.GetDelegateForFunctionPointer<SetAtkValueStringDelegate>(setAtkPtr);
|
||||||
|
|
||||||
|
if (!hooksEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var showMessageBoxPtr = scanner.ScanText("4C 8B DC 55 57 41 55 49 8D 6B 98");
|
||||||
|
this.AddonTalkV45Hook = new Hook<AddonTalkV45Delegate>(showMessageBoxPtr, new AddonTalkV45Delegate(this.AddonTalkV45Detour));
|
||||||
|
this.AddonTalkV45Hook.Enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Dispose() {
|
||||||
|
this.AddonTalkV45Hook?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddonTalkV45Detour(IntPtr addon, IntPtr a2, IntPtr data) {
|
||||||
|
if (this.OnTalk == null) {
|
||||||
|
this.AddonTalkV45Hook!.Original(addon, a2, data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rawName = Util.ReadTerminated(Marshal.ReadIntPtr(data + NameOffset + 8));
|
||||||
|
var rawText = Util.ReadTerminated(Marshal.ReadIntPtr(data + TextOffset + 8));
|
||||||
|
var style = (TalkStyle) Marshal.ReadByte(data + StyleOffset);
|
||||||
|
|
||||||
|
var name = this.SeStringManager.Parse(rawName);
|
||||||
|
var text = this.SeStringManager.Parse(rawText);
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.OnTalk?.Invoke(ref name, ref text, ref style);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
PluginLog.LogError(ex, "Exception in Talk detour");
|
||||||
|
}
|
||||||
|
|
||||||
|
var newName = name.Encode().Terminate();
|
||||||
|
var newText = text.Encode().Terminate();
|
||||||
|
|
||||||
|
Marshal.WriteByte(data + StyleOffset, (byte) style);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
fixed (byte* namePtr = newName, textPtr = newText) {
|
||||||
|
this.SetAtkValueString(data + NameOffset, (IntPtr) namePtr);
|
||||||
|
this.SetAtkValueString(data + TextOffset, (IntPtr) textPtr);
|
||||||
|
this.AddonTalkV45Hook!.Original(addon, a2, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Talk window styles.
|
||||||
|
/// </summary>
|
||||||
|
public enum TalkStyle : byte {
|
||||||
|
/// <summary>
|
||||||
|
/// The normal style with a white background.
|
||||||
|
/// </summary>
|
||||||
|
Normal = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A style with lights on the top and bottom border.
|
||||||
|
/// </summary>
|
||||||
|
Lights = 2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A style used for when characters are shouting.
|
||||||
|
/// </summary>
|
||||||
|
Shout = 3,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Like <see cref="Shout"/> but with flatter edges.
|
||||||
|
/// </summary>
|
||||||
|
FlatShout = 4,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The style used when dragons (and some other NPCs) talk.
|
||||||
|
/// </summary>
|
||||||
|
Dragon = 5,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The style used for Allagan machinery.
|
||||||
|
/// </summary>
|
||||||
|
Allagan = 6,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The style used for system messages.
|
||||||
|
/// </summary>
|
||||||
|
System = 7,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A mixture of the system message style and the dragon style.
|
||||||
|
/// </summary>
|
||||||
|
DragonSystem = 8,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The system message style with a purple background.
|
||||||
|
/// </summary>
|
||||||
|
PurpleSystem = 9,
|
||||||
|
}
|
||||||
|
}
|
@ -28,17 +28,27 @@ namespace XivCommon {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Examine Examine { get; }
|
public Examine Examine { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Talk events
|
||||||
|
/// </summary>
|
||||||
|
public Talk Talk { get; }
|
||||||
|
|
||||||
internal GameFunctions(Hooks hooks, DalamudPluginInterface @interface) {
|
internal GameFunctions(Hooks hooks, DalamudPluginInterface @interface) {
|
||||||
this.Interface = @interface;
|
this.Interface = @interface;
|
||||||
|
|
||||||
this.Chat = new Chat(this, @interface.TargetModuleScanner);
|
var scanner = @interface.TargetModuleScanner;
|
||||||
this.PartyFinder = new PartyFinder(@interface.TargetModuleScanner, hooks.HasFlag(Hooks.PartyFinder));
|
var seStringManager = @interface.SeStringManager;
|
||||||
this.BattleTalk = new BattleTalk(this, @interface.TargetModuleScanner, @interface.SeStringManager, hooks.HasFlag(Hooks.BattleTalk));
|
|
||||||
this.Examine = new Examine(this, @interface.TargetModuleScanner);
|
this.Chat = new Chat(this, scanner);
|
||||||
|
this.PartyFinder = new PartyFinder(scanner, hooks.HasFlag(Hooks.PartyFinder));
|
||||||
|
this.BattleTalk = new BattleTalk(this, scanner, seStringManager, hooks.HasFlag(Hooks.BattleTalk));
|
||||||
|
this.Examine = new Examine(this, scanner);
|
||||||
|
this.Talk = new Talk(scanner, seStringManager, hooks.HasFlag(Hooks.Talk));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
|
this.Talk.Dispose();
|
||||||
this.BattleTalk.Dispose();
|
this.BattleTalk.Dispose();
|
||||||
this.PartyFinder.Dispose();
|
this.PartyFinder.Dispose();
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,13 @@ namespace XivCommon {
|
|||||||
/// This hook is used in order to enable all Party Finder functions.
|
/// This hook is used in order to enable all Party Finder functions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
PartyFinder,
|
PartyFinder,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Talk hooks.
|
||||||
|
///
|
||||||
|
/// This hook is used in order to enable the Talk events.
|
||||||
|
/// </summary>
|
||||||
|
Talk,
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static class HooksExt {
|
internal static class HooksExt {
|
||||||
|
Loading…
Reference in New Issue
Block a user