feat: add housing location

This commit is contained in:
Anna 2021-11-09 17:58:52 -05:00
parent 5e9321cc86
commit f9db679957
5 changed files with 178 additions and 0 deletions

View File

@ -0,0 +1,56 @@
using System;
using Dalamud.Game;
namespace XivCommon.Functions.Housing {
/// <summary>
/// The class containing housing functionality
/// </summary>
public class Housing {
private static class Signatures {
internal const string HousingPointer = "48 8B 05 ?? ?? ?? ?? 48 83 78 ?? ?? 74 16 48 8D 8F ?? ?? ?? ?? 66 89 5C 24 ?? 48 8D 54 24 ?? E8 ?? ?? ?? ?? 48 8B 7C 24";
}
private IntPtr HousingPointer { get; }
/// <summary>
/// Gets the raw struct containing information about the player's current location in a housing ward.
///
/// <returns>struct if player is in a housing ward, null otherwise</returns>
/// </summary>
// Updated: 5.55
// 48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 56 41 57 48 83 EC 20 49 8B 00 (ward?)
public unsafe RawHousingLocation? RawLocation {
get {
if (this.HousingPointer == IntPtr.Zero) {
return null;
}
var loc = Util.FollowPointerChain(this.HousingPointer, new[] { 0, 0 });
if (loc == IntPtr.Zero) {
return null;
}
var locPtr = (RawHousingLocation*) (loc + 0x96a0);
return *locPtr;
}
}
/// <summary>
/// Gets process information about the player's current location in a housing ward.
///
/// <returns>information class if player is in a housing ward, null otherwise</returns>
/// </summary>
public HousingLocation? Location {
get {
var loc = this.RawLocation;
return loc == null ? null : new HousingLocation(loc.Value);
}
}
internal Housing(SigScanner scanner) {
if (scanner.TryGetStaticAddressFromSig(Signatures.HousingPointer, out var ptr)) {
this.HousingPointer = ptr;
}
}
}
}

View File

@ -0,0 +1,59 @@
namespace XivCommon.Functions.Housing {
/// <summary>
/// Information about a player's current location in a housing ward.
/// </summary>
public class HousingLocation {
/// <summary>
/// The housing ward that the player is in.
/// </summary>
public ushort Ward;
/// <summary>
/// <para>
/// The yard that the player is in.
/// </para>
/// <para>
/// This is the same as plot number but indicates that the player is in
/// the exterior area (the yard) of that plot.
/// </para>
/// </summary>
public ushort? Yard;
/// <summary>
/// The plot that the player is in.
/// </summary>
public ushort? Plot;
/// <summary>
/// The apartment wing (1 or 2 for normal or subdivision) that the
/// player is in.
/// </summary>
public ushort? ApartmentWing;
/// <summary>
/// The apartment that the player is in.
/// </summary>
public ushort? Apartment;
internal HousingLocation(RawHousingLocation loc) {
var ward = loc.CurrentWard;
if ((loc.CurrentPlot & 0x80) > 0) {
// the struct is in apartment mode
this.ApartmentWing = (ushort?) ((loc.CurrentPlot & ~0x80) + 1);
this.Apartment = (ushort?) (ward >> 6);
this.Ward = (ushort) ((ward & 0x3F) + 1);
if (this.Apartment == 0) {
this.Apartment = null;
}
} else if (loc.InsideIndicator == 0) {
// inside a plot
this.Plot = (ushort?) (loc.CurrentPlot + 1);
} else if (loc.CurrentYard != 0xFF) {
// not inside a plot
// yard is 0xFF when not in one
this.Yard = (ushort?) (loc.CurrentYard + 1);
}
if (this.Ward == 0) {
this.Ward = (ushort) (ward + 1);
}
}
}
}

View File

@ -0,0 +1,41 @@
using System.Runtime.InteropServices;
namespace XivCommon.Functions.Housing {
/// <summary>
/// Information about the player's current location in a housing ward as
/// kept by the game's internal structures.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct RawHousingLocation {
/// <summary>
/// The zero-indexed plot number that the player is in.
///
/// <para>
/// Contains apartment data when inside an apartment building.
/// </para>
/// </summary>
public readonly ushort CurrentPlot; // a0 -> a2
/// <summary>
/// The zero-indexed ward number that the player is in.
///
/// <para>
/// Contains apartment data when inside an apartment building.
/// </para>
/// </summary>
public readonly ushort CurrentWard; // a2 -> a4
private readonly uint unknownBytes1; // a4 -> a8
/// <summary>
/// The zero-indexed yard number that the player is in.
///
/// <para>
/// Is <c>0xFF</c> when not in a yard.
/// </para>
/// </summary>
public readonly byte CurrentYard; // a8 -> a9
private readonly byte unknownBytes2; // a9 -> aa
/// <summary>
/// A byte that is zero when the player is inside a plot.
/// </summary>
public readonly byte InsideIndicator; // aa -> ab
}
}

View File

@ -9,6 +9,7 @@ using FFXIVClientStructs.FFXIV.Component.GUI;
using XivCommon.Functions;
using XivCommon.Functions.ContextMenu;
using XivCommon.Functions.FriendList;
using XivCommon.Functions.Housing;
using XivCommon.Functions.NamePlates;
using XivCommon.Functions.Tooltips;
using Framework = FFXIVClientStructs.FFXIV.Client.System.Framework.Framework;
@ -83,6 +84,11 @@ namespace XivCommon {
/// Journal functions
/// </summary>
public Journal Journal { get; }
/// <summary>
/// Housing functions
/// </summary>
public Housing Housing { get; }
internal GameFunctions(Hooks hooks) {
this.Framework = Util.GetService<Dalamud.Game.Framework>();
@ -106,6 +112,7 @@ namespace XivCommon {
this.DutyFinder = new DutyFinder(this, scanner);
this.Journal = new Journal(this, scanner);
this.FriendList = new FriendList(this);
this.Housing = new Housing(scanner);
}
/// <inheritdoc />

View File

@ -44,5 +44,20 @@ namespace XivCommon {
var get = service.GetMethod("Get", BindingFlags.Public | BindingFlags.Static)!;
return (T) get.Invoke(null, null)!;
}
internal static unsafe IntPtr FollowPointerChain(IntPtr start, IEnumerable<int> offsets) {
if (start == IntPtr.Zero) {
return IntPtr.Zero;
}
foreach (var offset in offsets) {
start = *(IntPtr*) (start + offset);
if (start == IntPtr.Zero) {
return IntPtr.Zero;
}
}
return start;
}
}
}