Compare commits
29 Commits
api9
...
86686bf0ca
| Author | SHA1 | Date | |
|---|---|---|---|
| 86686bf0ca | |||
| 8d92c79822 | |||
| 9900230520 | |||
| a9264fdd21 | |||
| 331bd63e2e | |||
| dfc85d7890 | |||
| ddd6954142 | |||
| 22c3497fec | |||
| 675225658c | |||
| 8273c68318 | |||
| 7f41e593c3 | |||
| ab3bf20ffa | |||
| 4b21e569a0 | |||
| bfd5cd1b12 | |||
| 83fdf5613e | |||
| 545a812b66 | |||
| cb2cd8e32c | |||
| e699a758f3 | |||
| e6e90cdcd3 | |||
| e7ce31dc0b | |||
| 1e9bd2484a | |||
| a4d3e8b0f3 | |||
| 5695a0823b | |||
| 22475d7223 | |||
| 348d8f50e6 | |||
| 5f78d24c78 | |||
| 0af7217e63 | |||
| e01ebcde37 | |||
| d127662959 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -361,3 +361,6 @@ MigrationBackup/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
|
||||
# Idea
|
||||
.idea
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
namespace Pilz.Dalamud.ActivityContexts;
|
||||
|
||||
namespace Pilz.Dalamud.ActivityContexts
|
||||
public class ActivityContext
|
||||
{
|
||||
public class ActivityContext
|
||||
{
|
||||
public ActivityType ActivityType { get; init; }
|
||||
public ZoneType ZoneType { get; init; }
|
||||
|
||||
@@ -21,5 +15,4 @@ namespace Pilz.Dalamud.ActivityContexts
|
||||
{
|
||||
get => ZoneType != ZoneType.Overworld;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
using Dalamud.Logging;
|
||||
using Lumina.Excel;
|
||||
using Lumina.Excel;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pilz.Dalamud.ActivityContexts
|
||||
namespace Pilz.Dalamud.ActivityContexts;
|
||||
|
||||
public class ActivityContextManager : IDisposable
|
||||
{
|
||||
public class ActivityContextManager : IDisposable
|
||||
{
|
||||
public delegate void ActivityContextChangedEventHandler(ActivityContextManager sender, ActivityContext activityContext);
|
||||
public event ActivityContextChangedEventHandler ActivityContextChanged;
|
||||
|
||||
@@ -35,7 +29,7 @@ namespace Pilz.Dalamud.ActivityContexts
|
||||
PluginServices.ClientState.TerritoryChanged -= ClientState_TerritoryChanged;
|
||||
}
|
||||
|
||||
private void ClientState_TerritoryChanged(object? sender, ushort e)
|
||||
private void ClientState_TerritoryChanged(ushort obj)
|
||||
{
|
||||
CheckCurrentTerritory();
|
||||
}
|
||||
@@ -85,5 +79,4 @@ namespace Pilz.Dalamud.ActivityContexts
|
||||
CurrentActivityContext = new(newActivityContext, newZoneType);
|
||||
ActivityContextChanged?.Invoke(this, CurrentActivityContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pilz.Dalamud.ActivityContexts
|
||||
namespace Pilz.Dalamud.ActivityContexts;
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum ActivityType
|
||||
{
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum ActivityType
|
||||
{
|
||||
None = 0x0,
|
||||
PveDuty = 0x1,
|
||||
PvpDuty = 0x2
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pilz.Dalamud.ActivityContexts
|
||||
namespace Pilz.Dalamud.ActivityContexts;
|
||||
|
||||
[Flags, JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum ZoneType
|
||||
{
|
||||
[Flags, JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum ZoneType
|
||||
{
|
||||
Overworld = 1,
|
||||
Doungen = 2,
|
||||
Raid = 4,
|
||||
@@ -18,5 +13,4 @@ namespace Pilz.Dalamud.ActivityContexts
|
||||
Foray = 16,
|
||||
Pvp = 32,
|
||||
Everywhere = int.MaxValue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pilz.Dalamud
|
||||
namespace Pilz.Dalamud;
|
||||
|
||||
public static class Extensions
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Removes a Payload from a given SeString.
|
||||
/// Using <code>SeString.Payloads.Remove()</code> does not use the reference to compare for some reason. Tis is a workaround.
|
||||
@@ -24,7 +19,7 @@ namespace Pilz.Dalamud
|
||||
/// Removes a Payload from a given list.
|
||||
/// Using <code>List.Remove()</code> does not use the reference to compare for some reason. Tis is a workaround.
|
||||
/// </summary>
|
||||
/// <param name="seString"></param>
|
||||
/// <param name="payloads"></param>
|
||||
/// <param name="payload"></param>
|
||||
public static void Remove(this List<Payload> payloads, Payload payload)
|
||||
{
|
||||
@@ -37,5 +32,4 @@ namespace Pilz.Dalamud
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Memory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pilz.Dalamud
|
||||
namespace Pilz.Dalamud;
|
||||
|
||||
public static class GameInterfaceHelper
|
||||
{
|
||||
public static class GameInterfaceHelper
|
||||
{
|
||||
public static SeString ReadSeString(IntPtr ptr)
|
||||
{
|
||||
if (ptr == IntPtr.Zero)
|
||||
@@ -149,5 +145,4 @@ namespace Pilz.Dalamud
|
||||
IMemorySpace.Free((void*)ptr, size);
|
||||
ptr = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
namespace Pilz.Dalamud.Icons;
|
||||
|
||||
namespace Pilz.Dalamud.Icons
|
||||
public class JobIconSet
|
||||
{
|
||||
public class JobIconSet
|
||||
{
|
||||
private readonly int[] icons;
|
||||
|
||||
public float IconScale { get; init; }
|
||||
@@ -23,5 +16,4 @@ namespace Pilz.Dalamud.Icons
|
||||
{
|
||||
return icons[jobID - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
namespace Pilz.Dalamud.Icons;
|
||||
|
||||
namespace Pilz.Dalamud.Icons
|
||||
public enum JobIconSetName
|
||||
{
|
||||
public enum JobIconSetName
|
||||
{
|
||||
Gold,
|
||||
Framed,
|
||||
Glowing,
|
||||
@@ -20,5 +14,4 @@ namespace Pilz.Dalamud.Icons
|
||||
Green,
|
||||
Grey,
|
||||
Role
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,113 +1,118 @@
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
namespace Pilz.Dalamud.Icons;
|
||||
|
||||
namespace Pilz.Dalamud.Icons
|
||||
public class JobIconSets
|
||||
{
|
||||
public class JobIconSets
|
||||
{
|
||||
private readonly Dictionary<JobIconSetName, JobIconSet> iconSets = new();
|
||||
private readonly Dictionary<JobIconSetName, JobIconSet> iconSets = [];
|
||||
|
||||
public JobIconSets()
|
||||
{
|
||||
Add(JobIconSetName.Gold, new[]
|
||||
{
|
||||
Add(JobIconSetName.Gold,
|
||||
[
|
||||
62001, 62002, 62003, 62004, 62005, 62006, 62007, 62008, 62009, 62010,
|
||||
62011, 62012, 62013, 62014, 62015, 62016, 62017, 62018, 62019, 62020,
|
||||
62021, 62022, 62023, 62024, 62025, 62026, 62027, 62028, 62029, 62030,
|
||||
62031, 62032, 62033, 62034, 62035, 62036, 62037, 62038, 62039, 62040
|
||||
}, 1);
|
||||
62031, 62032, 62033, 62034, 62035, 62036, 62037, 62038, 62039, 62040,
|
||||
62041, 62042,
|
||||
], 1);
|
||||
|
||||
Add(JobIconSetName.Framed, new[]
|
||||
{
|
||||
Add(JobIconSetName.Framed,
|
||||
[
|
||||
62101, 62102, 62103, 62104, 62105, 62106, 62107, 62108, 62109, 62110,
|
||||
62111, 62112, 62113, 62114, 62115, 62116, 62117, 62118, 62119, 62120,
|
||||
62121, 62122, 62123, 62124, 62125, 62126, 62127, 62128, 62129, 62130,
|
||||
62131, 62132, 62133, 62134, 62135, 62136, 62137, 62138, 62139, 62140
|
||||
});
|
||||
62131, 62132, 62133, 62134, 62135, 62136, 62137, 62138, 62139, 62140,
|
||||
62141, 62142,
|
||||
]);
|
||||
|
||||
Add(JobIconSetName.Glowing, new[]
|
||||
{
|
||||
Add(JobIconSetName.Glowing,
|
||||
[
|
||||
62301, 62302, 62303, 62304, 62305, 62306, 62307, 62310, 62311, 62312,
|
||||
62313, 62314, 62315, 62316, 62317, 62318, 62319, 62320, 62401, 62402,
|
||||
62403, 62404, 62405, 62406, 62407, 62308, 62408, 62409, 62309, 62410,
|
||||
62411, 62412, 62413, 62414, 62415, 62416, 62417, 62418, 62419, 62420
|
||||
});
|
||||
62411, 62412, 62413, 62414, 62415, 62416, 62417, 62418, 62419, 62420,
|
||||
62421, 62422,
|
||||
]);
|
||||
|
||||
Add(JobIconSetName.Grey, new[]
|
||||
{
|
||||
Add(JobIconSetName.Grey,
|
||||
[
|
||||
91022, 91023, 91024, 91025, 91026, 91028, 91029, 91031, 91032, 91033,
|
||||
91034, 91035, 91036, 91037, 91038, 91039, 91040, 91041, 91079, 91080,
|
||||
91081, 91082, 91083, 91084, 91085, 91030, 91086, 91087, 91121, 91122,
|
||||
91125, 91123, 91124, 91127, 91128, 91129, 91130, 91131, 91132, 91133
|
||||
}, 2);
|
||||
91125, 91123, 91124, 91127, 91128, 91129, 91130, 91131, 91132, 91133,
|
||||
91185, 91186,
|
||||
], 2);
|
||||
|
||||
Add(JobIconSetName.Black, new[]
|
||||
{
|
||||
Add(JobIconSetName.Black,
|
||||
[
|
||||
91522, 91523, 91524, 91525, 91526, 91528, 91529, 91531, 91532, 91533,
|
||||
91534, 91535, 91536, 91537, 91538, 91539, 91540, 91541, 91579, 91580,
|
||||
91581, 91582, 91583, 91584, 91585, 91530, 91586, 91587, 91621, 91622,
|
||||
91625, 91623, 91624, 91627, 91628, 91629, 91630, 91631, 91632, 91633
|
||||
}, 2);
|
||||
91625, 91623, 91624, 91627, 91628, 91629, 91630, 91631, 91632, 91633,
|
||||
91685, 91686,
|
||||
], 2);
|
||||
|
||||
Add(JobIconSetName.Yellow, new[]
|
||||
{
|
||||
Add(JobIconSetName.Yellow,
|
||||
[
|
||||
92022, 92023, 92024, 92025, 92026, 92028, 92029, 92031, 92032, 92033,
|
||||
92034, 92035, 92036, 92037, 92038, 92039, 92040, 92041, 92079, 92080,
|
||||
92081, 92082, 92083, 92084, 92085, 92030, 92086, 92087, 92121, 92122,
|
||||
92125, 92123, 92124, 92127, 92128, 92129, 92130, 92131, 92132, 92133
|
||||
}, 2);
|
||||
92125, 92123, 92124, 92127, 92128, 92129, 92130, 92131, 92132, 92133,
|
||||
92185, 92186,
|
||||
], 2);
|
||||
|
||||
Add(JobIconSetName.Orange, new[]
|
||||
{
|
||||
Add(JobIconSetName.Orange,
|
||||
[
|
||||
92522, 92523, 92524, 92525, 92526, 92528, 92529, 92531, 92532, 92533,
|
||||
92534, 92535, 92536, 92537, 92538, 92539, 92540, 92541, 92579, 92580,
|
||||
92581, 92582, 92583, 92584, 92585, 92530, 92586, 92587, 92621, 92622,
|
||||
92625, 92623, 92624, 92627, 92628, 92629, 92630, 92631, 92632, 92633
|
||||
}, 2);
|
||||
92625, 92623, 92624, 92627, 92628, 92629, 92630, 92631, 92632, 92633,
|
||||
92685, 92686,
|
||||
], 2);
|
||||
|
||||
Add(JobIconSetName.Red, new[]
|
||||
{
|
||||
Add(JobIconSetName.Red,
|
||||
[
|
||||
93022, 93023, 93024, 93025, 93026, 93028, 93029, 93031, 93032, 93033,
|
||||
93034, 93035, 93036, 93037, 93038, 93039, 93040, 93041, 93079, 93080,
|
||||
93081, 93082, 93083, 93084, 93085, 93030, 93086, 93087, 93121, 93122,
|
||||
93125, 93123, 93124, 93127, 93128, 93129, 93130, 93131, 93132, 93133
|
||||
}, 2);
|
||||
93125, 93123, 93124, 93127, 93128, 93129, 93130, 93131, 93132, 93133,
|
||||
93185, 93186,
|
||||
], 2);
|
||||
|
||||
Add(JobIconSetName.Purple, new[]
|
||||
Add(JobIconSetName.Purple, icons: new[]
|
||||
{
|
||||
93522, 93523, 93524, 93525, 93526, 93528, 93529, 93531, 93532, 93533,
|
||||
93534, 93535, 93536, 93537, 93538, 93539, 93540, 93541, 93579, 93580,
|
||||
93581, 93582, 93583, 93584, 93585, 93530, 93586, 93587, 93621, 93622,
|
||||
93625, 93623, 93624, 93627, 93628, 93629, 93630, 93631, 93632, 93633
|
||||
}, 2);
|
||||
93625, 93623, 93624, 93627, 93628, 93629, 93630, 93631, 93632, 93633,
|
||||
93685, 93686,
|
||||
}, scale: 2);
|
||||
|
||||
Add(JobIconSetName.Blue, new[]
|
||||
{
|
||||
Add(JobIconSetName.Blue,
|
||||
[
|
||||
94022, 94023, 94024, 94025, 94026, 94028, 94029, 94031, 94032, 94033,
|
||||
94034, 94035, 94036, 94037, 94038, 94039, 94040, 94041, 94079, 94080,
|
||||
94081, 94082, 94083, 94084, 94085, 94030, 94086, 94087, 94121, 94122,
|
||||
94125, 94123, 94124, 94127, 94128, 94129, 94130, 94131, 94132, 94133
|
||||
}, 2);
|
||||
94125, 94123, 94124, 94127, 94128, 94129, 94130, 94131, 94132, 94133,
|
||||
94185, 94186,
|
||||
], 2);
|
||||
|
||||
Add(JobIconSetName.Green, new[]
|
||||
{
|
||||
Add(JobIconSetName.Green,
|
||||
[
|
||||
94522, 94523, 94524, 94525, 94526, 94528, 94529, 94531, 94532, 94533,
|
||||
94534, 94535, 94536, 94537, 94538, 94539, 94540, 94541, 94579, 94580,
|
||||
94581, 94582, 94583, 94584, 94585, 94530, 94586, 94587, 94621, 94622,
|
||||
94625, 94623, 94624, 94627, 94628, 94629, 94630, 94631, 94632, 94633
|
||||
}, 2);
|
||||
94625, 94623, 94624, 94627, 94628, 94629, 94630, 94631, 94632, 94633,
|
||||
94685, 94686,
|
||||
], 2);
|
||||
|
||||
Add(JobIconSetName.Role, new[]
|
||||
{
|
||||
Add(JobIconSetName.Role,
|
||||
[
|
||||
62581, 62584, 62581, 62584, 62586, 62582, 62502, 62502, 62503, 62504,
|
||||
62505, 62506, 62507, 62508, 62509, 62510, 62511, 62512, 62581, 62584,
|
||||
62581, 62584, 62586, 62582, 62587, 62587, 62587, 62582, 62584, 62584,
|
||||
62586, 62581, 62582, 62584, 62587, 62587, 62581, 62586, 62584, 62582
|
||||
});
|
||||
62586, 62581, 62582, 62584, 62587, 62587, 62581, 62586, 62584, 62582,
|
||||
62584, 62584,
|
||||
]);
|
||||
}
|
||||
|
||||
private void Add(JobIconSetName id, int[] icons, float scale = 1f)
|
||||
@@ -124,5 +129,4 @@ namespace Pilz.Dalamud.Icons
|
||||
{
|
||||
return iconSets[set].IconScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
25
Pilz.Dalamud/Icons/StatusIcons.cs
Normal file
25
Pilz.Dalamud/Icons/StatusIcons.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace Pilz.Dalamud.Icons;
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum StatusIcons
|
||||
{
|
||||
Disconnecting = 061503,
|
||||
InDuty = 061506,
|
||||
ViewingCutscene = 061508,
|
||||
Busy = 061509,
|
||||
Idle = 061511,
|
||||
DutyFinder = 061517,
|
||||
PartyLeader = 061521,
|
||||
PartyMember = 061522,
|
||||
RolePlaying = 061545,
|
||||
GroupPose = 061546,
|
||||
NewAdventurer = 061523,
|
||||
Mentor = 061540,
|
||||
MentorPvE = 061542,
|
||||
MentorCrafting = 061543,
|
||||
MentorPvP = 061544,
|
||||
Returner = 061547,
|
||||
}
|
||||
43
Pilz.Dalamud/NamePlate/INamePlateGui.cs
Normal file
43
Pilz.Dalamud/NamePlate/INamePlateGui.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Pilz.Dalamud.NamePlate;
|
||||
|
||||
/// <summary>
|
||||
/// Class used to modify the data used when rendering nameplates.
|
||||
/// </summary>
|
||||
public interface INamePlateGui
|
||||
{
|
||||
/// <summary>
|
||||
/// The delegate used for receiving nameplate update events.
|
||||
/// </summary>
|
||||
/// <param name="context">An object containing information about the pending data update.</param>
|
||||
/// <param name="handlers>">A list of handlers used for updating nameplate data.</param>
|
||||
public delegate void OnPlateUpdateDelegate(INamePlateUpdateContext context, IReadOnlyList<INamePlateUpdateHandler> handlers);
|
||||
|
||||
/// <summary>
|
||||
/// An event which fires when nameplate data is updated and at least one nameplate has important updates. The
|
||||
/// subscriber is provided with a list of handlers for nameplates with important updates.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Fires after <see cref="OnDataUpdate"/>.
|
||||
/// </remarks>
|
||||
event OnPlateUpdateDelegate? OnNamePlateUpdate;
|
||||
|
||||
/// <summary>
|
||||
/// An event which fires when nameplate data is updated. The subscriber is provided with a list of handlers for all
|
||||
/// nameplates.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This event is likely to fire every frame even when no nameplates are actually updated, so in most cases
|
||||
/// <see cref="OnNamePlateUpdate"/> is preferred. Fires before <see cref="OnNamePlateUpdate"/>.
|
||||
/// </remarks>
|
||||
event OnPlateUpdateDelegate? OnDataUpdate;
|
||||
|
||||
/// <summary>
|
||||
/// Requests that all nameplates should be redrawn on the following frame.
|
||||
/// </summary>
|
||||
void RequestRedraw();
|
||||
|
||||
private static NamePlateGui instance;
|
||||
public static INamePlateGui Instance => instance ??= new ();
|
||||
}
|
||||
202
Pilz.Dalamud/NamePlate/NamePlateGui.cs
Normal file
202
Pilz.Dalamud/NamePlate/NamePlateGui.cs
Normal file
@@ -0,0 +1,202 @@
|
||||
using Dalamud.Game.Addon.Lifecycle;
|
||||
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Pilz.Dalamud.NamePlate;
|
||||
|
||||
/// <summary>
|
||||
/// Class used to modify the data used when rendering nameplates.
|
||||
/// </summary>
|
||||
public sealed class NamePlateGui : IDisposable, INamePlateGui
|
||||
{
|
||||
/// <summary>
|
||||
/// The index for the number array used by the NamePlate addon.
|
||||
/// </summary>
|
||||
public const int NumberArrayIndex = 5;
|
||||
|
||||
/// <summary>
|
||||
/// The index for the string array used by the NamePlate addon.
|
||||
/// </summary>
|
||||
public const int StringArrayIndex = 4;
|
||||
|
||||
/// <summary>
|
||||
/// The index for of the FullUpdate entry in the NamePlate number array.
|
||||
/// </summary>
|
||||
internal const int NumberArrayFullUpdateIndex = 4;
|
||||
|
||||
/// <summary>
|
||||
/// An empty null-terminated string pointer allocated in unmanaged memory, used to tag removed fields.
|
||||
/// </summary>
|
||||
internal static readonly nint EmptyStringPointer = CreateEmptyStringPointer();
|
||||
|
||||
private readonly IAddonLifecycle addonLifecycle = PluginServices.AddonLifecycle;
|
||||
|
||||
private readonly IGameGui gameGui = PluginServices.GameGui;
|
||||
|
||||
private readonly IObjectTable objectTable = PluginServices.ObjectTable;
|
||||
|
||||
private NamePlateUpdateContext? context;
|
||||
|
||||
private NamePlateUpdateHandler[] updateHandlers = [];
|
||||
|
||||
internal NamePlateGui()
|
||||
{
|
||||
this.addonLifecycle.RegisterListener(AddonEvent.PreRequestedUpdate, "NamePlate", this.OnPreRequestedUpdate);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event INamePlateGui.OnPlateUpdateDelegate? OnNamePlateUpdate;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event INamePlateGui.OnPlateUpdateDelegate? OnDataUpdate;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public unsafe void RequestRedraw()
|
||||
{
|
||||
var addon = this.gameGui.GetAddonByName("NamePlate");
|
||||
if (addon != 0)
|
||||
{
|
||||
var raptureAtkModule = RaptureAtkModule.Instance();
|
||||
if (raptureAtkModule == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
((AddonNamePlate*)addon)->DoFullUpdate = 1;
|
||||
var namePlateNumberArrayData = raptureAtkModule->AtkArrayDataHolder.NumberArrays[NumberArrayIndex];
|
||||
namePlateNumberArrayData->SetValue(NumberArrayFullUpdateIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
this.addonLifecycle.UnregisterListener(AddonEvent.PreRequestedUpdate, "NamePlate", this.OnPreRequestedUpdate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Strips the surrounding quotes from a free company tag. If the quotes are not present in the expected location,
|
||||
/// no modifications will be made.
|
||||
/// </summary>
|
||||
/// <param name="text">A quoted free company tag.</param>
|
||||
/// <returns>A span containing the free company tag without its surrounding quote characters.</returns>
|
||||
internal static ReadOnlySpan<byte> StripFreeCompanyTagQuotes(ReadOnlySpan<byte> text)
|
||||
{
|
||||
if (text.Length > 4 && text.StartsWith(" «"u8) && text.EndsWith("»"u8))
|
||||
{
|
||||
return text[3..^2];
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Strips the surrounding quotes from a title. If the quotes are not present in the expected location, no
|
||||
/// modifications will be made.
|
||||
/// </summary>
|
||||
/// <param name="text">A quoted title.</param>
|
||||
/// <returns>A span containing the title without its surrounding quote characters.</returns>
|
||||
internal static ReadOnlySpan<byte> StripTitleQuotes(ReadOnlySpan<byte> text)
|
||||
{
|
||||
if (text.Length > 5 && text.StartsWith("《"u8) && text.EndsWith("》"u8))
|
||||
{
|
||||
return text[3..^3];
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
private static nint CreateEmptyStringPointer()
|
||||
{
|
||||
var pointer = Marshal.AllocHGlobal(1);
|
||||
Marshal.WriteByte(pointer, 0, 0);
|
||||
return pointer;
|
||||
}
|
||||
|
||||
private void CreateHandlers(NamePlateUpdateContext createdContext)
|
||||
{
|
||||
var handlers = new List<NamePlateUpdateHandler>();
|
||||
for (var i = 0; i < AddonNamePlate.NumNamePlateObjects; i++)
|
||||
{
|
||||
handlers.Add(new NamePlateUpdateHandler(createdContext, i));
|
||||
}
|
||||
|
||||
this.updateHandlers = handlers.ToArray();
|
||||
}
|
||||
|
||||
private void OnPreRequestedUpdate(AddonEvent type, AddonArgs args)
|
||||
{
|
||||
if (this.OnDataUpdate == null && this.OnNamePlateUpdate == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var reqArgs = (AddonRequestedUpdateArgs)args;
|
||||
if (this.context == null)
|
||||
{
|
||||
this.context = new NamePlateUpdateContext(this.objectTable, reqArgs);
|
||||
this.CreateHandlers(this.context);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.context.ResetState(reqArgs);
|
||||
}
|
||||
|
||||
var activeNamePlateCount = this.context.ActiveNamePlateCount;
|
||||
if (activeNamePlateCount == 0)
|
||||
return;
|
||||
|
||||
var activeHandlers = this.updateHandlers[..activeNamePlateCount];
|
||||
|
||||
if (this.context.IsFullUpdate)
|
||||
{
|
||||
foreach (var handler in activeHandlers)
|
||||
{
|
||||
handler.ResetState();
|
||||
}
|
||||
|
||||
this.OnDataUpdate?.Invoke(this.context, activeHandlers);
|
||||
this.OnNamePlateUpdate?.Invoke(this.context, activeHandlers);
|
||||
if (this.context.HasParts)
|
||||
this.ApplyBuilders(activeHandlers);
|
||||
}
|
||||
else
|
||||
{
|
||||
var udpatedHandlers = new List<NamePlateUpdateHandler>(activeNamePlateCount);
|
||||
foreach (var handler in activeHandlers)
|
||||
{
|
||||
handler.ResetState();
|
||||
if (handler.IsUpdating)
|
||||
udpatedHandlers.Add(handler);
|
||||
}
|
||||
|
||||
if (this.OnDataUpdate is not null)
|
||||
{
|
||||
this.OnDataUpdate?.Invoke(this.context, activeHandlers);
|
||||
this.OnNamePlateUpdate?.Invoke(this.context, udpatedHandlers);
|
||||
if (this.context.HasParts)
|
||||
this.ApplyBuilders(activeHandlers);
|
||||
}
|
||||
else if (udpatedHandlers.Count != 0)
|
||||
{
|
||||
var changedHandlersSpan = udpatedHandlers.ToArray().AsSpan();
|
||||
this.OnNamePlateUpdate?.Invoke(this.context, udpatedHandlers);
|
||||
if (this.context.HasParts)
|
||||
this.ApplyBuilders(changedHandlersSpan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyBuilders(Span<NamePlateUpdateHandler> handlers)
|
||||
{
|
||||
foreach (var handler in handlers)
|
||||
{
|
||||
if (handler.PartsContainer is { } container)
|
||||
{
|
||||
container.ApplyBuilders(handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
105
Pilz.Dalamud/NamePlate/NamePlateInfoView.cs
Normal file
105
Pilz.Dalamud/NamePlate/NamePlateInfoView.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
|
||||
namespace Pilz.Dalamud.NamePlate;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a read-only view of the nameplate info object data for a nameplate. Modifications to
|
||||
/// <see cref="NamePlateUpdateHandler"/> fields do not affect this data.
|
||||
/// </summary>
|
||||
public interface INamePlateInfoView
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the displayed name for this nameplate according to the nameplate info object.
|
||||
/// </summary>
|
||||
SeString Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the displayed free company tag for this nameplate according to the nameplate info object. For this field,
|
||||
/// the quote characters which appear on either side of the title are NOT included.
|
||||
/// </summary>
|
||||
SeString FreeCompanyTag { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the displayed free company tag for this nameplate according to the nameplate info object. For this field,
|
||||
/// the quote characters which appear on either side of the title ARE included.
|
||||
/// </summary>
|
||||
SeString QuotedFreeCompanyTag { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the displayed title for this nameplate according to the nameplate info object. For this field, the quote
|
||||
/// characters which appear on either side of the title are NOT included.
|
||||
/// </summary>
|
||||
SeString Title { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the displayed title for this nameplate according to the nameplate info object. For this field, the quote
|
||||
/// characters which appear on either side of the title ARE included.
|
||||
/// </summary>
|
||||
SeString QuotedTitle { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the displayed level text for this nameplate according to the nameplate info object.
|
||||
/// </summary>
|
||||
SeString LevelText { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flags for this nameplate according to the nameplate info object.
|
||||
/// </summary>
|
||||
int Flags { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this nameplate is considered 'dirty' or not according to the nameplate
|
||||
/// info object.
|
||||
/// </summary>
|
||||
bool IsDirty { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the title for this nameplate is a prefix title or not according to the nameplate
|
||||
/// info object. This value is derived from the <see cref="Flags"/> field.
|
||||
/// </summary>
|
||||
bool IsPrefixTitle { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides a read-only view of the nameplate info object data for a nameplate. Modifications to
|
||||
/// <see cref="NamePlateUpdateHandler"/> fields do not affect this data.
|
||||
/// </summary>
|
||||
internal unsafe class NamePlateInfoView(RaptureAtkModule.NamePlateInfo* info) : INamePlateInfoView
|
||||
{
|
||||
private SeString? name;
|
||||
private SeString? freeCompanyTag;
|
||||
private SeString? quotedFreeCompanyTag;
|
||||
private SeString? title;
|
||||
private SeString? quotedTitle;
|
||||
private SeString? levelText;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public SeString Name => this.name ??= SeString.Parse(info->Name);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public SeString FreeCompanyTag => this.freeCompanyTag ??=
|
||||
SeString.Parse(NamePlateGui.StripFreeCompanyTagQuotes(info->FcName));
|
||||
|
||||
/// <inheritdoc/>
|
||||
public SeString QuotedFreeCompanyTag => this.quotedFreeCompanyTag ??= SeString.Parse(info->FcName);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public SeString Title => this.title ??= SeString.Parse(info->Title);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public SeString QuotedTitle => this.quotedTitle ??= SeString.Parse(info->DisplayTitle);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public SeString LevelText => this.levelText ??= SeString.Parse(info->LevelText);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int Flags => info->Flags;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsDirty => info->IsDirty;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsPrefixTitle => ((info->Flags >> (8 * 3)) & 0xFF) == 1;
|
||||
}
|
||||
57
Pilz.Dalamud/NamePlate/NamePlateKind.cs
Normal file
57
Pilz.Dalamud/NamePlate/NamePlateKind.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
namespace Pilz.Dalamud.NamePlate;
|
||||
|
||||
/// <summary>
|
||||
/// An enum describing what kind of game object this nameplate represents.
|
||||
/// </summary>
|
||||
public enum NamePlateKind : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// A player character.
|
||||
/// </summary>
|
||||
PlayerCharacter = 0,
|
||||
|
||||
/// <summary>
|
||||
/// An event NPC or companion.
|
||||
/// </summary>
|
||||
EventNpcCompanion = 1,
|
||||
|
||||
/// <summary>
|
||||
/// A retainer.
|
||||
/// </summary>
|
||||
Retainer = 2,
|
||||
|
||||
/// <summary>
|
||||
/// An enemy battle NPC.
|
||||
/// </summary>
|
||||
BattleNpcEnemy = 3,
|
||||
|
||||
/// <summary>
|
||||
/// A friendly battle NPC.
|
||||
/// </summary>
|
||||
BattleNpcFriendly = 4,
|
||||
|
||||
/// <summary>
|
||||
/// An event object.
|
||||
/// </summary>
|
||||
EventObject = 5,
|
||||
|
||||
/// <summary>
|
||||
/// Treasure.
|
||||
/// </summary>
|
||||
Treasure = 6,
|
||||
|
||||
/// <summary>
|
||||
/// A gathering point.
|
||||
/// </summary>
|
||||
GatheringPoint = 7,
|
||||
|
||||
/// <summary>
|
||||
/// A battle NPC with subkind 6.
|
||||
/// </summary>
|
||||
BattleNpcSubkind6 = 8,
|
||||
|
||||
/// <summary>
|
||||
/// Something else.
|
||||
/// </summary>
|
||||
Other = 9,
|
||||
}
|
||||
46
Pilz.Dalamud/NamePlate/NamePlatePartsContainer.cs
Normal file
46
Pilz.Dalamud/NamePlate/NamePlatePartsContainer.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
namespace Pilz.Dalamud.NamePlate;
|
||||
|
||||
/// <summary>
|
||||
/// A container for parts.
|
||||
/// </summary>
|
||||
internal class NamePlatePartsContainer
|
||||
{
|
||||
private NamePlateSimpleParts? nameParts;
|
||||
private NamePlateQuotedParts? titleParts;
|
||||
private NamePlateQuotedParts? freeCompanyTagParts;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NamePlatePartsContainer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="context">The currently executing update context.</param>
|
||||
public NamePlatePartsContainer(NamePlateUpdateContext context)
|
||||
{
|
||||
context.HasParts = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a parts object for constructing a nameplate name.
|
||||
/// </summary>
|
||||
internal NamePlateSimpleParts Name => this.nameParts ??= new NamePlateSimpleParts(NamePlateStringField.Name);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a parts object for constructing a nameplate title.
|
||||
/// </summary>
|
||||
internal NamePlateQuotedParts Title => this.titleParts ??= new NamePlateQuotedParts(NamePlateStringField.Title, false);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a parts object for constructing a nameplate free company tag.
|
||||
/// </summary>
|
||||
internal NamePlateQuotedParts FreeCompanyTag => this.freeCompanyTagParts ??= new NamePlateQuotedParts(NamePlateStringField.FreeCompanyTag, true);
|
||||
|
||||
/// <summary>
|
||||
/// Applies all container parts.
|
||||
/// </summary>
|
||||
/// <param name="handler">The handler to apply the builders to.</param>
|
||||
internal void ApplyBuilders(NamePlateUpdateHandler handler)
|
||||
{
|
||||
this.nameParts?.Apply(handler);
|
||||
this.freeCompanyTagParts?.Apply(handler);
|
||||
this.titleParts?.Apply(handler);
|
||||
}
|
||||
}
|
||||
104
Pilz.Dalamud/NamePlate/NamePlateQuotedParts.cs
Normal file
104
Pilz.Dalamud/NamePlate/NamePlateQuotedParts.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
|
||||
namespace Pilz.Dalamud.NamePlate;
|
||||
|
||||
/// <summary>
|
||||
/// A part builder for constructing and setting quoted nameplate fields (i.e. free company tag and title).
|
||||
/// </summary>
|
||||
/// <param name="field">The field type which should be set.</param>
|
||||
/// <remarks>
|
||||
/// This class works as a lazy writer initialized with empty parts, where an empty part signifies no change should be
|
||||
/// performed. Only after all handler processing is complete does it write out any parts which were set to the
|
||||
/// associated field. Reading fields from this class is usually not what you want to do, as you'll only be reading the
|
||||
/// contents of parts which other plugins have written to. Prefer reading from the base handler's properties or using
|
||||
/// <see cref="NamePlateInfoView"/>.
|
||||
/// </remarks>
|
||||
public class NamePlateQuotedParts(NamePlateStringField field, bool isFreeCompany)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the opening and closing SeStrings which will wrap the entire contents, which can be used to apply
|
||||
/// colors or styling to the entire field.
|
||||
/// </summary>
|
||||
public (SeString, SeString)? OuterWrap { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the opening quote string which appears before the text and opening text-wrap.
|
||||
/// </summary>
|
||||
public SeString? LeftQuote { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the closing quote string which appears after the text and closing text-wrap.
|
||||
/// </summary>
|
||||
public SeString? RightQuote { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the opening and closing SeStrings which will wrap the text, which can be used to apply colors or
|
||||
/// styling to the field's text.
|
||||
/// </summary>
|
||||
public (SeString, SeString)? TextWrap { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets this field's text.
|
||||
/// </summary>
|
||||
public SeString? Text { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Applies the changes from this builder to the actual field.
|
||||
/// </summary>
|
||||
/// <param name="handler">The handler to perform the changes on.</param>
|
||||
internal unsafe void Apply(NamePlateUpdateHandler handler)
|
||||
{
|
||||
if ((nint)handler.GetFieldAsPointer(field) == NamePlateGui.EmptyStringPointer)
|
||||
return;
|
||||
|
||||
var sb = new SeStringBuilder();
|
||||
if (this.OuterWrap is { Item1: var outerLeft })
|
||||
{
|
||||
sb.Append(outerLeft);
|
||||
}
|
||||
|
||||
if (this.LeftQuote is not null)
|
||||
{
|
||||
sb.Append(this.LeftQuote);
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(isFreeCompany ? " «" : "《");
|
||||
}
|
||||
|
||||
if (this.TextWrap is { Item1: var left, Item2: var right })
|
||||
{
|
||||
sb.Append(left);
|
||||
sb.Append(this.Text ?? this.GetStrippedField(handler));
|
||||
sb.Append(right);
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(this.Text ?? this.GetStrippedField(handler));
|
||||
}
|
||||
|
||||
if (this.RightQuote is not null)
|
||||
{
|
||||
sb.Append(this.RightQuote);
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(isFreeCompany ? "»" : "》");
|
||||
}
|
||||
|
||||
if (this.OuterWrap is { Item2: var outerRight })
|
||||
{
|
||||
sb.Append(outerRight);
|
||||
}
|
||||
|
||||
handler.SetField(field, sb.Build());
|
||||
}
|
||||
|
||||
private SeString GetStrippedField(NamePlateUpdateHandler handler)
|
||||
{
|
||||
return SeString.Parse(
|
||||
isFreeCompany
|
||||
? NamePlateGui.StripFreeCompanyTagQuotes(handler.GetFieldAsSpan(field))
|
||||
: NamePlateGui.StripTitleQuotes(handler.GetFieldAsSpan(field)));
|
||||
}
|
||||
}
|
||||
51
Pilz.Dalamud/NamePlate/NamePlateSimpleParts.cs
Normal file
51
Pilz.Dalamud/NamePlate/NamePlateSimpleParts.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
|
||||
namespace Pilz.Dalamud.NamePlate;
|
||||
|
||||
/// <summary>
|
||||
/// A part builder for constructing and setting a simple (unquoted) nameplate field.
|
||||
/// </summary>
|
||||
/// <param name="field">The field type which should be set.</param>
|
||||
/// <remarks>
|
||||
/// This class works as a lazy writer initialized with empty parts, where an empty part signifies no change should be
|
||||
/// performed. Only after all handler processing is complete does it write out any parts which were set to the
|
||||
/// associated field. Reading fields from this class is usually not what you want to do, as you'll only be reading the
|
||||
/// contents of parts which other plugins have written to. Prefer reading from the base handler's properties or using
|
||||
/// <see cref="NamePlateInfoView"/>.
|
||||
/// </remarks>
|
||||
public class NamePlateSimpleParts(NamePlateStringField field)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the opening and closing SeStrings which will wrap the text, which can be used to apply colors or
|
||||
/// styling to the field's text.
|
||||
/// </summary>
|
||||
public (SeString, SeString)? TextWrap { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets this field's text.
|
||||
/// </summary>
|
||||
public SeString? Text { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Applies the changes from this builder to the actual field.
|
||||
/// </summary>
|
||||
/// <param name="handler">The handler to perform the changes on.</param>
|
||||
internal unsafe void Apply(NamePlateUpdateHandler handler)
|
||||
{
|
||||
if ((nint)handler.GetFieldAsPointer(field) == NamePlateGui.EmptyStringPointer)
|
||||
return;
|
||||
|
||||
if (this.TextWrap is { Item1: var left, Item2: var right })
|
||||
{
|
||||
var sb = new SeStringBuilder();
|
||||
sb.Append(left);
|
||||
sb.Append(this.Text ?? handler.GetFieldAsSeString(field));
|
||||
sb.Append(right);
|
||||
handler.SetField(field, sb.Build());
|
||||
}
|
||||
else if (this.Text is not null)
|
||||
{
|
||||
handler.SetField(field, this.Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
38
Pilz.Dalamud/NamePlate/NamePlateStringField.cs
Normal file
38
Pilz.Dalamud/NamePlate/NamePlateStringField.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
namespace Pilz.Dalamud.NamePlate;
|
||||
|
||||
/// <summary>
|
||||
/// An enum describing the string fields available in nameplate data. The <see cref="NamePlateKind"/> and various flags
|
||||
/// determine which fields will actually be rendered.
|
||||
/// </summary>
|
||||
public enum NamePlateStringField
|
||||
{
|
||||
/// <summary>
|
||||
/// The object's name.
|
||||
/// </summary>
|
||||
Name = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The object's title.
|
||||
/// </summary>
|
||||
Title = 50,
|
||||
|
||||
/// <summary>
|
||||
/// The object's free company tag.
|
||||
/// </summary>
|
||||
FreeCompanyTag = 100,
|
||||
|
||||
/// <summary>
|
||||
/// The object's status prefix.
|
||||
/// </summary>
|
||||
StatusPrefix = 150,
|
||||
|
||||
/// <summary>
|
||||
/// The object's target suffix.
|
||||
/// </summary>
|
||||
TargetSuffix = 200,
|
||||
|
||||
/// <summary>
|
||||
/// The object's level prefix.
|
||||
/// </summary>
|
||||
LevelPrefix = 250,
|
||||
}
|
||||
152
Pilz.Dalamud/NamePlate/NamePlateUpdateContext.cs
Normal file
152
Pilz.Dalamud/NamePlate/NamePlateUpdateContext.cs
Normal file
@@ -0,0 +1,152 @@
|
||||
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||
using Dalamud.Game.ClientState.Objects;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
|
||||
namespace Pilz.Dalamud.NamePlate;
|
||||
|
||||
/// <summary>
|
||||
/// Contains information related to the pending nameplate data update. This is only valid for a single frame and should
|
||||
/// not be kept across frames.
|
||||
/// </summary>
|
||||
public interface INamePlateUpdateContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the number of active nameplates. The actual number visible may be lower than this in cases where some
|
||||
/// nameplates are hidden by default (based on in-game "Display Name Settings" and so on).
|
||||
/// </summary>
|
||||
int ActiveNamePlateCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the game is currently performing a full update of all active nameplates.
|
||||
/// </summary>
|
||||
bool IsFullUpdate { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address of the NamePlate addon.
|
||||
/// </summary>
|
||||
nint AddonAddress { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address of the NamePlate addon's number array data container.
|
||||
/// </summary>
|
||||
nint NumberArrayDataAddress { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address of the NamePlate addon's string array data container.
|
||||
/// </summary>
|
||||
nint StringArrayDataAddress { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address of the first entry in the NamePlate addon's int array.
|
||||
/// </summary>
|
||||
nint NumberArrayDataEntryAddress { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains information related to the pending nameplate data update. This is only valid for a single frame and should
|
||||
/// not be kept across frames.
|
||||
/// </summary>
|
||||
internal unsafe class NamePlateUpdateContext : INamePlateUpdateContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NamePlateUpdateContext"/> class.
|
||||
/// </summary>
|
||||
/// <param name="objectTable">An object table.</param>
|
||||
/// <param name="args">The addon lifecycle arguments for the update request.</param>
|
||||
internal NamePlateUpdateContext(IObjectTable objectTable, AddonRequestedUpdateArgs args)
|
||||
{
|
||||
this.ObjectTable = objectTable;
|
||||
this.RaptureAtkModule = FFXIVClientStructs.FFXIV.Client.UI.RaptureAtkModule.Instance();
|
||||
this.Ui3DModule = UIModule.Instance()->GetUI3DModule();
|
||||
this.ResetState(args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of active nameplates. The actual number visible may be lower than this in cases where some
|
||||
/// nameplates are hidden by default (based on in-game "Display Name Settings" and so on).
|
||||
/// </summary>
|
||||
public int ActiveNamePlateCount { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the game is currently performing a full update of all active nameplates.
|
||||
/// </summary>
|
||||
public bool IsFullUpdate { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address of the NamePlate addon.
|
||||
/// </summary>
|
||||
public nint AddonAddress => (nint)this.Addon;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address of the NamePlate addon's number array data container.
|
||||
/// </summary>
|
||||
public nint NumberArrayDataAddress => (nint)this.NumberData;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address of the NamePlate addon's string array data container.
|
||||
/// </summary>
|
||||
public nint StringArrayDataAddress => (nint)this.StringData;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address of the first entry in the NamePlate addon's int array.
|
||||
/// </summary>
|
||||
public nint NumberArrayDataEntryAddress => (nint)this.NumberStruct;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the RaptureAtkModule.
|
||||
/// </summary>
|
||||
internal RaptureAtkModule* RaptureAtkModule { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Ui3DModule.
|
||||
/// </summary>
|
||||
internal UI3DModule* Ui3DModule { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ObjectTable.
|
||||
/// </summary>
|
||||
internal IObjectTable ObjectTable { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a pointer to the NamePlate addon.
|
||||
/// </summary>
|
||||
internal AddonNamePlate* Addon { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a pointer to the NamePlate addon's number array data container.
|
||||
/// </summary>
|
||||
internal NumberArrayData* NumberData { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a pointer to the NamePlate addon's string array data container.
|
||||
/// </summary>
|
||||
internal StringArrayData* StringData { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a pointer to the NamePlate addon's number array entries as a struct.
|
||||
/// </summary>
|
||||
internal AddonNamePlate.NamePlateIntArrayData* NumberStruct { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether any handler in the current context has instantiated a part builder.
|
||||
/// </summary>
|
||||
internal bool HasParts { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Resets the state of the context based on the provided addon lifecycle arguments.
|
||||
/// </summary>
|
||||
/// <param name="args">The addon lifecycle arguments for the update request.</param>
|
||||
internal void ResetState(AddonRequestedUpdateArgs args)
|
||||
{
|
||||
this.Addon = (AddonNamePlate*)args.Addon;
|
||||
this.NumberData = ((NumberArrayData**)args.NumberArrayData)![NamePlateGui.NumberArrayIndex];
|
||||
this.NumberStruct = (AddonNamePlate.NamePlateIntArrayData*)this.NumberData->IntArray;
|
||||
this.StringData = ((StringArrayData**)args.StringArrayData)![NamePlateGui.StringArrayIndex];
|
||||
this.HasParts = false;
|
||||
|
||||
this.ActiveNamePlateCount = this.NumberStruct->ActiveNamePlateCount;
|
||||
this.IsFullUpdate = this.Addon->DoFullUpdate != 0;
|
||||
}
|
||||
}
|
||||
606
Pilz.Dalamud/NamePlate/NamePlateUpdateHandler.cs
Normal file
606
Pilz.Dalamud/NamePlate/NamePlateUpdateHandler.cs
Normal file
@@ -0,0 +1,606 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
using FFXIVClientStructs.Interop;
|
||||
|
||||
namespace Pilz.Dalamud.NamePlate;
|
||||
|
||||
/// <summary>
|
||||
/// A class representing a single nameplate. Provides mechanisms to look up the game object associated with the
|
||||
/// nameplate and allows for modification of various backing fields in number and string array data, which in turn
|
||||
/// affect aspects of the nameplate's appearance when drawn. Instances of this class are only valid for a single frame
|
||||
/// and should not be kept across frames.
|
||||
/// </summary>
|
||||
public interface INamePlateUpdateHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the GameObjectId of the game object associated with this nameplate.
|
||||
/// </summary>
|
||||
ulong GameObjectId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IGameObject"/> associated with this nameplate, if possible. Performs an object table scan
|
||||
/// and caches the result if successful.
|
||||
/// </summary>
|
||||
IGameObject? GameObject { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read-only view of the nameplate info object data for a nameplate. Modifications to
|
||||
/// <see cref="NamePlateUpdateHandler"/> fields do not affect fields in the returned view.
|
||||
/// </summary>
|
||||
INamePlateInfoView InfoView { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index for this nameplate data in the backing number and string array data. This is not the same as the
|
||||
/// rendered or object index, which can be retrieved from <see cref="NamePlateIndex"/>.
|
||||
/// </summary>
|
||||
int ArrayIndex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IBattleChara"/> associated with this nameplate, if possible. Returns null if the nameplate
|
||||
/// has an associated <see cref="IGameObject"/>, but that object cannot be assigned to <see cref="IBattleChara"/>.
|
||||
/// </summary>
|
||||
IBattleChara? BattleChara { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IPlayerCharacter"/> associated with this nameplate, if possible. Returns null if the
|
||||
/// nameplate has an associated <see cref="IGameObject"/>, but that object cannot be assigned to
|
||||
/// <see cref="IPlayerCharacter"/>.
|
||||
/// </summary>
|
||||
IPlayerCharacter? PlayerCharacter { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address of the nameplate info struct.
|
||||
/// </summary>
|
||||
nint NamePlateInfoAddress { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address of the first entry associated with this nameplate in the NamePlate addon's int array.
|
||||
/// </summary>
|
||||
nint NamePlateObjectAddress { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating what kind of nameplate this is, based on the kind of object it is associated with.
|
||||
/// </summary>
|
||||
NamePlateKind NamePlateKind { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the update flags for this nameplate.
|
||||
/// </summary>
|
||||
int UpdateFlags { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the overall text color for this nameplate. If this value is changed, the appropriate update flag
|
||||
/// will be set so that the game will reflect this change immediately.
|
||||
/// </summary>
|
||||
uint TextColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the overall text edge color for this nameplate. If this value is changed, the appropriate update
|
||||
/// flag will be set so that the game will reflect this change immediately.
|
||||
/// </summary>
|
||||
uint EdgeColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the icon ID for the nameplate's marker icon, which is the large icon used to indicate quest
|
||||
/// availability and so on. This value is read from and reset by the game every frame, not just when a nameplate
|
||||
/// changes. Setting this to 0 disables the icon.
|
||||
/// </summary>
|
||||
int MarkerIconId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the icon ID for the nameplate's name icon, which is the small icon shown to the left of the name.
|
||||
/// Setting this to -1 disables the icon.
|
||||
/// </summary>
|
||||
int NameIconId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the nameplate index, which is the index used for rendering and looking up entries in the object array. For
|
||||
/// number and string array data, <see cref="ArrayIndex"/> is used.
|
||||
/// </summary>
|
||||
int NamePlateIndex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the draw flags for this nameplate.
|
||||
/// </summary>
|
||||
int DrawFlags { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the visibility flags for this nameplate.
|
||||
/// </summary>
|
||||
int VisibilityFlags { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this nameplate is undergoing a major update or not. This is usually true when a
|
||||
/// nameplate has just appeared or something meaningful about the entity has changed (e.g. its job or status). This
|
||||
/// flag is reset by the game during the update process (during requested update and before draw).
|
||||
/// </summary>
|
||||
bool IsUpdating { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the title (when visible) will be displayed above the object's name (a
|
||||
/// prefix title) instead of below the object's name (a suffix title).
|
||||
/// </summary>
|
||||
bool IsPrefixTitle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the title should be displayed at all.
|
||||
/// </summary>
|
||||
bool DisplayTitle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name for this nameplate.
|
||||
/// </summary>
|
||||
SeString Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a builder which can be used to help cooperatively build a new name for this nameplate even when other
|
||||
/// plugins modifying the name are present. Specifically, this builder allows setting text and text-wrapping
|
||||
/// payloads (e.g. for setting text color) separately.
|
||||
/// </summary>
|
||||
NamePlateSimpleParts NameParts { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the title for this nameplate.
|
||||
/// </summary>
|
||||
SeString Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a builder which can be used to help cooperatively build a new title for this nameplate even when other
|
||||
/// plugins modifying the title are present. Specifically, this builder allows setting text, text-wrapping
|
||||
/// payloads (e.g. for setting text color), and opening and closing quote sequences separately.
|
||||
/// </summary>
|
||||
NamePlateQuotedParts TitleParts { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the free company tag for this nameplate.
|
||||
/// </summary>
|
||||
SeString FreeCompanyTag { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a builder which can be used to help cooperatively build a new FC tag for this nameplate even when other
|
||||
/// plugins modifying the FC tag are present. Specifically, this builder allows setting text, text-wrapping
|
||||
/// payloads (e.g. for setting text color), and opening and closing quote sequences separately.
|
||||
/// </summary>
|
||||
NamePlateQuotedParts FreeCompanyTagParts { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the status prefix for this nameplate. This prefix is used by the game to add BitmapFontIcon-based
|
||||
/// online status icons to player nameplates.
|
||||
/// </summary>
|
||||
SeString StatusPrefix { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the target suffix for this nameplate. This suffix is used by the game to add the squared-letter
|
||||
/// target tags to the end of combat target nameplates.
|
||||
/// </summary>
|
||||
SeString TargetSuffix { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the level prefix for this nameplate. This "Lv60" style prefix is added to enemy and friendly battle
|
||||
/// NPC nameplates to indicate the NPC level.
|
||||
/// </summary>
|
||||
SeString LevelPrefix { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Removes the contents of the name field for this nameplate. This differs from simply setting the field
|
||||
/// to an empty string because it writes a special value to memory, and other setters (except SetField variants)
|
||||
/// will refuse to overwrite this value. Therefore, fields removed this way are more likely to stay removed.
|
||||
/// </summary>
|
||||
void RemoveName();
|
||||
|
||||
/// <summary>
|
||||
/// Removes the contents of the title field for this nameplate. This differs from simply setting the field
|
||||
/// to an empty string because it writes a special value to memory, and other setters (except SetField variants)
|
||||
/// will refuse to overwrite this value. Therefore, fields removed this way are more likely to stay removed.
|
||||
/// </summary>
|
||||
void RemoveTitle();
|
||||
|
||||
/// <summary>
|
||||
/// Removes the contents of the FC tag field for this nameplate. This differs from simply setting the field
|
||||
/// to an empty string because it writes a special value to memory, and other setters (except SetField variants)
|
||||
/// will refuse to overwrite this value. Therefore, fields removed this way are more likely to stay removed.
|
||||
/// </summary>
|
||||
void RemoveFreeCompanyTag();
|
||||
|
||||
/// <summary>
|
||||
/// Removes the contents of the status prefix field for this nameplate. This differs from simply setting the field
|
||||
/// to an empty string because it writes a special value to memory, and other setters (except SetField variants)
|
||||
/// will refuse to overwrite this value. Therefore, fields removed this way are more likely to stay removed.
|
||||
/// </summary>
|
||||
void RemoveStatusPrefix();
|
||||
|
||||
/// <summary>
|
||||
/// Removes the contents of the target suffix field for this nameplate. This differs from simply setting the field
|
||||
/// to an empty string because it writes a special value to memory, and other setters (except SetField variants)
|
||||
/// will refuse to overwrite this value. Therefore, fields removed this way are more likely to stay removed.
|
||||
/// </summary>
|
||||
void RemoveTargetSuffix();
|
||||
|
||||
/// <summary>
|
||||
/// Removes the contents of the level prefix field for this nameplate. This differs from simply setting the field
|
||||
/// to an empty string because it writes a special value to memory, and other setters (except SetField variants)
|
||||
/// will refuse to overwrite this value. Therefore, fields removed this way are more likely to stay removed.
|
||||
/// </summary>
|
||||
void RemoveLevelPrefix();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a pointer to the string array value in the provided field.
|
||||
/// </summary>
|
||||
/// <param name="field">The field to read from.</param>
|
||||
/// <returns>A pointer to a sequence of non-null bytes.</returns>
|
||||
unsafe byte* GetFieldAsPointer(NamePlateStringField field);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a byte span containing the string array value in the provided field.
|
||||
/// </summary>
|
||||
/// <param name="field">The field to read from.</param>
|
||||
/// <returns>A ReadOnlySpan containing a sequence of non-null bytes.</returns>
|
||||
ReadOnlySpan<byte> GetFieldAsSpan(NamePlateStringField field);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a UTF8 string copy of the string array value in the provided field.
|
||||
/// </summary>
|
||||
/// <param name="field">The field to read from.</param>
|
||||
/// <returns>A copy of the string array value as a string.</returns>
|
||||
string GetFieldAsString(NamePlateStringField field);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a parsed SeString copy of the string array value in the provided field.
|
||||
/// </summary>
|
||||
/// <param name="field">The field to read from.</param>
|
||||
/// <returns>A copy of the string array value as a parsed SeString.</returns>
|
||||
SeString GetFieldAsSeString(NamePlateStringField field);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the string array value for the provided field.
|
||||
/// </summary>
|
||||
/// <param name="field">The field to write to.</param>
|
||||
/// <param name="value">The string to write.</param>
|
||||
void SetField(NamePlateStringField field, string value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the string array value for the provided field.
|
||||
/// </summary>
|
||||
/// <param name="field">The field to write to.</param>
|
||||
/// <param name="value">The SeString to write.</param>
|
||||
void SetField(NamePlateStringField field, SeString value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the string array value for the provided field.
|
||||
/// </summary>
|
||||
/// <param name="field">The field to write to.</param>
|
||||
/// <param name="value">The ReadOnlySpan of bytes to write.</param>
|
||||
void SetField(NamePlateStringField field, ReadOnlySpan<byte> value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the string array value for the provided field.
|
||||
/// </summary>
|
||||
/// <param name="field">The field to write to.</param>
|
||||
/// <param name="value">The pointer to a null-terminated sequence of bytes to write.</param>
|
||||
unsafe void SetField(NamePlateStringField field, byte* value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the string array value for the provided field to a fixed pointer to an empty string in unmanaged memory.
|
||||
/// Other methods may notice this fixed pointer and refuse to overwrite it, preserving the emptiness of the field.
|
||||
/// </summary>
|
||||
/// <param name="field">The field to write to.</param>
|
||||
void RemoveField(NamePlateStringField field);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A class representing a single nameplate. Provides mechanisms to look up the game object associated with the
|
||||
/// nameplate and allows for modification of various backing fields in number and string array data, which in turn
|
||||
/// affect aspects of the nameplate's appearance when drawn. Instances of this class are only valid for a single frame
|
||||
/// and should not be kept across frames.
|
||||
/// </summary>
|
||||
internal unsafe class NamePlateUpdateHandler : INamePlateUpdateHandler
|
||||
{
|
||||
private readonly NamePlateUpdateContext context;
|
||||
|
||||
private ulong? gameObjectId;
|
||||
private IGameObject? gameObject;
|
||||
private NamePlateInfoView? infoView;
|
||||
private NamePlatePartsContainer? partsContainer;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NamePlateUpdateHandler"/> class.
|
||||
/// </summary>
|
||||
/// <param name="context">The current update context.</param>
|
||||
/// <param name="arrayIndex">The index for this nameplate data in the backing number and string array data. This is
|
||||
/// not the same as the rendered index, which can be retrieved from <see cref="NamePlateIndex"/>.</param>
|
||||
internal NamePlateUpdateHandler(NamePlateUpdateContext context, int arrayIndex)
|
||||
{
|
||||
this.context = context;
|
||||
this.ArrayIndex = arrayIndex;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int ArrayIndex { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ulong GameObjectId => this.gameObjectId ??= this.NamePlateInfo->ObjectId;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IGameObject? GameObject => this.gameObject ??= this.context.ObjectTable.CreateObjectReference(
|
||||
(nint)this.context.Ui3DModule->NamePlateObjectInfoPointers[
|
||||
this.ArrayIndex].Value->GameObject);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IBattleChara? BattleChara => this.GameObject as IBattleChara;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IPlayerCharacter? PlayerCharacter => this.GameObject as IPlayerCharacter;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public INamePlateInfoView InfoView => this.infoView ??= new NamePlateInfoView(this.NamePlateInfo);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public nint NamePlateInfoAddress => (nint)this.NamePlateInfo;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public nint NamePlateObjectAddress => (nint)this.NamePlateObject;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public NamePlateKind NamePlateKind => (NamePlateKind)this.ObjectData->NamePlateKind;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int UpdateFlags
|
||||
{
|
||||
get => this.ObjectData->UpdateFlags;
|
||||
private set => this.ObjectData->UpdateFlags = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public uint TextColor
|
||||
{
|
||||
get => this.ObjectData->NameTextColor;
|
||||
set
|
||||
{
|
||||
if (value != this.TextColor) this.UpdateFlags |= 2;
|
||||
this.ObjectData->NameTextColor = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public uint EdgeColor
|
||||
{
|
||||
get => this.ObjectData->NameEdgeColor;
|
||||
set
|
||||
{
|
||||
if (value != this.EdgeColor) this.UpdateFlags |= 2;
|
||||
this.ObjectData->NameEdgeColor = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int MarkerIconId
|
||||
{
|
||||
get => this.ObjectData->MarkerIconId;
|
||||
set => this.ObjectData->MarkerIconId = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int NameIconId
|
||||
{
|
||||
get => this.ObjectData->NameIconId;
|
||||
set => this.ObjectData->NameIconId = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int NamePlateIndex => this.ObjectData->NamePlateObjectIndex;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int DrawFlags
|
||||
{
|
||||
get => this.ObjectData->DrawFlags;
|
||||
private set => this.ObjectData->DrawFlags = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int VisibilityFlags
|
||||
{
|
||||
get => ObjectData->VisibilityFlags;
|
||||
set => ObjectData->VisibilityFlags = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsUpdating => (this.UpdateFlags & 1) != 0;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsPrefixTitle
|
||||
{
|
||||
get => (this.DrawFlags & 1) != 0;
|
||||
set => this.DrawFlags = value ? this.DrawFlags | 1 : this.DrawFlags & ~1;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool DisplayTitle
|
||||
{
|
||||
get => (this.DrawFlags & 0x80) == 0;
|
||||
set => this.DrawFlags = value ? this.DrawFlags & ~0x80 : this.DrawFlags | 0x80;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public SeString Name
|
||||
{
|
||||
get => this.GetFieldAsSeString(NamePlateStringField.Name);
|
||||
set => this.WeakSetField(NamePlateStringField.Name, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public NamePlateSimpleParts NameParts => this.PartsContainer.Name;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public SeString Title
|
||||
{
|
||||
get => this.GetFieldAsSeString(NamePlateStringField.Title);
|
||||
set => this.WeakSetField(NamePlateStringField.Title, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public NamePlateQuotedParts TitleParts => this.PartsContainer.Title;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public SeString FreeCompanyTag
|
||||
{
|
||||
get => this.GetFieldAsSeString(NamePlateStringField.FreeCompanyTag);
|
||||
set => this.WeakSetField(NamePlateStringField.FreeCompanyTag, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public NamePlateQuotedParts FreeCompanyTagParts => this.PartsContainer.FreeCompanyTag;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public SeString StatusPrefix
|
||||
{
|
||||
get => this.GetFieldAsSeString(NamePlateStringField.StatusPrefix);
|
||||
set => this.WeakSetField(NamePlateStringField.StatusPrefix, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public SeString TargetSuffix
|
||||
{
|
||||
get => this.GetFieldAsSeString(NamePlateStringField.TargetSuffix);
|
||||
set => this.WeakSetField(NamePlateStringField.TargetSuffix, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public SeString LevelPrefix
|
||||
{
|
||||
get => this.GetFieldAsSeString(NamePlateStringField.LevelPrefix);
|
||||
set => this.WeakSetField(NamePlateStringField.LevelPrefix, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or (lazily) creates a part builder container for this nameplate.
|
||||
/// </summary>
|
||||
internal NamePlatePartsContainer PartsContainer =>
|
||||
this.partsContainer ??= new NamePlatePartsContainer(this.context);
|
||||
|
||||
private RaptureAtkModule.NamePlateInfo* NamePlateInfo =>
|
||||
this.context.RaptureAtkModule->NamePlateInfoEntries.GetPointer(this.NamePlateIndex);
|
||||
|
||||
private AddonNamePlate.NamePlateObject* NamePlateObject =>
|
||||
&this.context.Addon->NamePlateObjectArray[this.NamePlateIndex];
|
||||
|
||||
private AddonNamePlate.NamePlateIntArrayData.NamePlateObjectIntArrayData* ObjectData =>
|
||||
this.context.NumberStruct->ObjectData.GetPointer(this.ArrayIndex);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void RemoveName() => this.RemoveField(NamePlateStringField.Name);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void RemoveTitle() => this.RemoveField(NamePlateStringField.Title);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void RemoveFreeCompanyTag() => this.RemoveField(NamePlateStringField.FreeCompanyTag);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void RemoveStatusPrefix() => this.RemoveField(NamePlateStringField.StatusPrefix);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void RemoveTargetSuffix() => this.RemoveField(NamePlateStringField.TargetSuffix);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void RemoveLevelPrefix() => this.RemoveField(NamePlateStringField.LevelPrefix);
|
||||
|
||||
/// <inheritdoc/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public byte* GetFieldAsPointer(NamePlateStringField field)
|
||||
{
|
||||
return this.context.StringData->StringArray[this.ArrayIndex + (int)field];
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ReadOnlySpan<byte> GetFieldAsSpan(NamePlateStringField field)
|
||||
{
|
||||
return MemoryMarshal.CreateReadOnlySpanFromNullTerminated(this.GetFieldAsPointer(field));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public string GetFieldAsString(NamePlateStringField field)
|
||||
{
|
||||
return Encoding.UTF8.GetString(this.GetFieldAsSpan(field));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public SeString GetFieldAsSeString(NamePlateStringField field)
|
||||
{
|
||||
return SeString.Parse(this.GetFieldAsSpan(field));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetField(NamePlateStringField field, string value)
|
||||
{
|
||||
this.context.StringData->SetValue(this.ArrayIndex + (int)field, value, true, true, true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetField(NamePlateStringField field, SeString value)
|
||||
{
|
||||
this.context.StringData->SetValue(this.ArrayIndex + (int)field, value.Encode(), true, true, true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetField(NamePlateStringField field, ReadOnlySpan<byte> value)
|
||||
{
|
||||
this.context.StringData->SetValue(this.ArrayIndex + (int)field, value, true, true, true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetField(NamePlateStringField field, byte* value)
|
||||
{
|
||||
this.context.StringData->SetValue(this.ArrayIndex + (int)field, value, true, true, true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void RemoveField(NamePlateStringField field)
|
||||
{
|
||||
this.context.StringData->SetValue(
|
||||
this.ArrayIndex + (int)field,
|
||||
(byte*)NamePlateGui.EmptyStringPointer,
|
||||
true,
|
||||
false,
|
||||
true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the state of this handler for re-use in a new update.
|
||||
/// </summary>
|
||||
internal void ResetState()
|
||||
{
|
||||
this.gameObjectId = null;
|
||||
this.gameObject = null;
|
||||
this.infoView = null;
|
||||
this.partsContainer = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the string array value for the provided field, unless it was already set to the special empty string
|
||||
/// pointer used by the Remove methods.
|
||||
/// </summary>
|
||||
/// <param name="field">The field to write to.</param>
|
||||
/// <param name="value">The SeString to write.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void WeakSetField(NamePlateStringField field, SeString value)
|
||||
{
|
||||
if ((nint)this.GetFieldAsPointer(field) == NamePlateGui.EmptyStringPointer)
|
||||
return;
|
||||
this.context.StringData->SetValue(this.ArrayIndex + (int)field, value.Encode(), true, true, true);
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pilz.Dalamud.Nameplates.EventArgs
|
||||
{
|
||||
public class AddonNamePlate_SetPlayerNameEventArgs : HookWithResultBaseEventArgs<IntPtr>
|
||||
{
|
||||
public IntPtr PlayerNameplateObjectPtr { get; set; }
|
||||
public IntPtr TitlePtr { get; set; }
|
||||
public IntPtr NamePtr { get; set; }
|
||||
public IntPtr FreeCompanyPtr { get; set; }
|
||||
public IntPtr PrefixPtr { get; set; }
|
||||
public bool IsTitleAboveName { get; set; }
|
||||
public bool IsTitleVisible { get; set; }
|
||||
public int IconID { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Pilz.Dalamud.Nameplates.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pilz.Dalamud.Nameplates.EventArgs
|
||||
{
|
||||
public class AddonNamePlate_SetPlayerNameManagedEventArgs : HookWithResultManagedBaseEventArgs<IntPtr>
|
||||
{
|
||||
public new AddonNamePlate_SetPlayerNameEventArgs OriginalEventArgs
|
||||
{
|
||||
get => base.OriginalEventArgs as AddonNamePlate_SetPlayerNameEventArgs;
|
||||
set => base.OriginalEventArgs = value;
|
||||
}
|
||||
|
||||
public SafeNameplateObject SafeNameplateObject { get; set; }
|
||||
public SeString Title { get; internal set; }
|
||||
public SeString Name { get; internal set; }
|
||||
public SeString FreeCompany { get; internal set; }
|
||||
public SeString Prefix { get; internal set; }
|
||||
|
||||
public bool IsTitleAboveName
|
||||
{
|
||||
get => OriginalEventArgs.IsTitleAboveName;
|
||||
set => OriginalEventArgs.IsTitleAboveName = value;
|
||||
}
|
||||
|
||||
public bool IsTitleVisible
|
||||
{
|
||||
get => OriginalEventArgs.IsTitleVisible;
|
||||
set => OriginalEventArgs.IsTitleVisible = value;
|
||||
}
|
||||
|
||||
public int IconID
|
||||
{
|
||||
get => OriginalEventArgs.IconID;
|
||||
set => OriginalEventArgs.IconID = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pilz.Dalamud.Nameplates.EventArgs
|
||||
{
|
||||
public abstract class HookBaseEventArgs
|
||||
{
|
||||
internal event Action CallOriginal;
|
||||
|
||||
public void Original()
|
||||
{
|
||||
CallOriginal?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pilz.Dalamud.Nameplates.EventArgs
|
||||
{
|
||||
public abstract class HookManagedBaseEventArgs
|
||||
{
|
||||
public HookBaseEventArgs OriginalEventArgs { get; internal set; }
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pilz.Dalamud.Nameplates.EventArgs
|
||||
{
|
||||
public abstract class HookWithResultBaseEventArgs<TResult>
|
||||
{
|
||||
internal event Func<TResult> CallOriginal;
|
||||
|
||||
public TResult Result { get; set; }
|
||||
|
||||
// Call Original based on the given properties
|
||||
public TResult Original()
|
||||
{
|
||||
return CallOriginal.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pilz.Dalamud.Nameplates.EventArgs
|
||||
{
|
||||
public abstract class HookWithResultManagedBaseEventArgs<TResult>
|
||||
{
|
||||
public HookWithResultBaseEventArgs<TResult> OriginalEventArgs { get; internal set; }
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
using Dalamud.Logging;
|
||||
using Dalamud.Plugin;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pilz.Dalamud.Nameplates.Model
|
||||
{
|
||||
public class SafeAddonNameplate
|
||||
{
|
||||
private readonly DalamudPluginInterface Interface;
|
||||
|
||||
public IntPtr Pointer => PluginServices.GameGui.GetAddonByName("NamePlate", 1);
|
||||
|
||||
public SafeAddonNameplate(DalamudPluginInterface pluginInterface)
|
||||
{
|
||||
Interface = pluginInterface;
|
||||
}
|
||||
|
||||
public unsafe SafeNameplateObject GetNamePlateObject(int index)
|
||||
{
|
||||
SafeNameplateObject result = null;
|
||||
|
||||
if (Pointer != IntPtr.Zero)
|
||||
{
|
||||
var npObjectArrayPtrPtr = Pointer + Marshal.OffsetOf(typeof(AddonNamePlate), nameof(AddonNamePlate.NamePlateObjectArray)).ToInt32();
|
||||
var npObjectArrayPtr = Marshal.ReadIntPtr(npObjectArrayPtrPtr);
|
||||
|
||||
if (npObjectArrayPtr != IntPtr.Zero)
|
||||
{
|
||||
var npObjectPtr = npObjectArrayPtr + Marshal.SizeOf(typeof(AddonNamePlate.NamePlateObject)) * index;
|
||||
result = new(npObjectPtr, index);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
using FFXIVClientStructs.FFXIV.Client.System.String;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pilz.Dalamud.Nameplates.Model
|
||||
{
|
||||
public class SafeNameplateInfo
|
||||
{
|
||||
public readonly IntPtr Pointer;
|
||||
public readonly RaptureAtkModule.NamePlateInfo Data;
|
||||
|
||||
public SafeNameplateInfo(IntPtr pointer)
|
||||
{
|
||||
Pointer = pointer;
|
||||
Data = Marshal.PtrToStructure<RaptureAtkModule.NamePlateInfo>(Pointer);
|
||||
}
|
||||
|
||||
internal IntPtr NameAddress => GetStringPtr(nameof(RaptureAtkModule.NamePlateInfo.Name));
|
||||
internal IntPtr FcNameAddress => GetStringPtr(nameof(RaptureAtkModule.NamePlateInfo.FcName));
|
||||
internal IntPtr TitleAddress => GetStringPtr(nameof(RaptureAtkModule.NamePlateInfo.Title));
|
||||
internal IntPtr DisplayTitleAddress => GetStringPtr(nameof(RaptureAtkModule.NamePlateInfo.DisplayTitle));
|
||||
internal IntPtr LevelTextAddress => GetStringPtr(nameof(RaptureAtkModule.NamePlateInfo.LevelText));
|
||||
|
||||
public string Name => GetString(NameAddress);
|
||||
public string FcName => GetString(FcNameAddress);
|
||||
public string Title => GetString(TitleAddress);
|
||||
public string DisplayTitle => GetString(DisplayTitleAddress);
|
||||
public string LevelText => GetString(LevelTextAddress);
|
||||
|
||||
//public bool IsPlayerCharacter() => XivApi.IsPlayerCharacter(Data.ObjectID.ObjectID);
|
||||
|
||||
//public bool IsPartyMember() => XivApi.IsPartyMember(Data.ObjectID.ObjectID);
|
||||
|
||||
//public bool IsAllianceMember() => XivApi.IsAllianceMember(Data.ObjectID.ObjectID);
|
||||
|
||||
//public uint GetJobID() => GetJobId(Data.ObjectID.ObjectID);
|
||||
|
||||
private unsafe IntPtr GetStringPtr(string name)
|
||||
{
|
||||
var namePtr = Pointer + Marshal.OffsetOf(typeof(RaptureAtkModule.NamePlateInfo), name).ToInt32();
|
||||
var stringPtrPtr = namePtr + Marshal.OffsetOf(typeof(Utf8String), nameof(Utf8String.StringPtr)).ToInt32();
|
||||
var stringPtr = Marshal.ReadIntPtr(stringPtrPtr);
|
||||
return stringPtr;
|
||||
}
|
||||
|
||||
private string GetString(IntPtr stringPtr)
|
||||
{
|
||||
return Marshal.PtrToStringUTF8(stringPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
using Dalamud.Logging;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pilz.Dalamud.Nameplates.Model
|
||||
{
|
||||
public class SafeNameplateObject
|
||||
{
|
||||
public IntPtr Pointer { get; }
|
||||
public AddonNamePlate.NamePlateObject Data { get; }
|
||||
|
||||
private int _Index;
|
||||
private SafeNameplateInfo _NamePlateInfo;
|
||||
|
||||
public SafeNameplateObject(IntPtr pointer, int index = -1)
|
||||
{
|
||||
Pointer = pointer;
|
||||
Data = Marshal.PtrToStructure<AddonNamePlate.NamePlateObject>(pointer);
|
||||
_Index = index;
|
||||
}
|
||||
|
||||
public int Index
|
||||
{
|
||||
get
|
||||
{
|
||||
int result = _Index;
|
||||
|
||||
if (_Index == -1)
|
||||
{
|
||||
var addon = XivApi.GetSafeAddonNamePlate();
|
||||
var npObject0 = addon.GetNamePlateObject(0);
|
||||
|
||||
if (npObject0 == null)
|
||||
result = -1; // NamePlateObject0 was null
|
||||
else
|
||||
{
|
||||
var npObjectBase = npObject0.Pointer;
|
||||
var npObjectSize = Marshal.SizeOf(typeof(AddonNamePlate.NamePlateObject));
|
||||
var index = (Pointer.ToInt64() - npObjectBase.ToInt64()) / npObjectSize;
|
||||
|
||||
if (index < 0 || index >= 50)
|
||||
result = -2; // NamePlateObject index was out of bounds
|
||||
else
|
||||
result = _Index = (int)index;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public SafeNameplateInfo NamePlateInfo
|
||||
{
|
||||
get
|
||||
{
|
||||
SafeNameplateInfo result = null;
|
||||
|
||||
if (_NamePlateInfo != null)
|
||||
{
|
||||
var rapturePtr = XivApi.RaptureAtkModulePtr;
|
||||
|
||||
if (rapturePtr != IntPtr.Zero)
|
||||
{
|
||||
var npInfoArrayPtr = rapturePtr + Marshal.OffsetOf(typeof(RaptureAtkModule), nameof(RaptureAtkModule.NamePlateInfoArray)).ToInt32();
|
||||
var npInfoPtr = npInfoArrayPtr + Marshal.SizeOf(typeof(RaptureAtkModule.NamePlateInfo)) * Index;
|
||||
result = _NamePlateInfo = new SafeNameplateInfo(npInfoPtr);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
#region Getters
|
||||
|
||||
public unsafe IntPtr IconImageNodeAddress => Marshal.ReadIntPtr(Pointer + Marshal.OffsetOf(typeof(AddonNamePlate.NamePlateObject), nameof(AddonNamePlate.NamePlateObject.IconImageNode)).ToInt32());
|
||||
public unsafe IntPtr NameNodeAddress => Marshal.ReadIntPtr(Pointer + Marshal.OffsetOf(typeof(AddonNamePlate.NamePlateObject), nameof(AddonNamePlate.NamePlateObject.NameText)).ToInt32());
|
||||
|
||||
public AtkImageNode IconImageNode => Marshal.PtrToStructure<AtkImageNode>(IconImageNodeAddress);
|
||||
public AtkTextNode NameTextNode => Marshal.PtrToStructure<AtkTextNode>(NameNodeAddress);
|
||||
|
||||
#endregion
|
||||
|
||||
public unsafe bool IsVisible => Data.IsVisible;
|
||||
public unsafe bool IsLocalPlayer => Data.IsLocalPlayer;
|
||||
public bool IsPlayer => Data.NameplateKind == 0;
|
||||
|
||||
//public void SetIconScale(float scale, bool force = false)
|
||||
//{
|
||||
// if (force || IconImageNode.AtkResNode.ScaleX != scale || IconImageNode.AtkResNode.ScaleY != scale)
|
||||
// {
|
||||
// Instance.SetNodeScale(IconImageNodeAddress, scale, scale);
|
||||
// }
|
||||
//}
|
||||
|
||||
//public void SetNameScale(float scale, bool force = false)
|
||||
//{
|
||||
// if (force || NameTextNode.AtkResNode.ScaleX != scale || NameTextNode.AtkResNode.ScaleY != scale)
|
||||
// {
|
||||
// Instance.SetNodeScale(NameNodeAddress, scale, scale);
|
||||
// }
|
||||
//}
|
||||
|
||||
//public unsafe void SetName(IntPtr ptr)
|
||||
//{
|
||||
// NameTextNode.SetText("aaa");
|
||||
//}
|
||||
|
||||
//public void SetIcon(int icon)
|
||||
//{
|
||||
// IconImageNode.LoadIconTexture(icon, 1);
|
||||
//}
|
||||
|
||||
public void SetIconPosition(short x, short y)
|
||||
{
|
||||
var iconXAdjustPtr = Pointer + Marshal.OffsetOf(typeof(AddonNamePlate.NamePlateObject), nameof(AddonNamePlate.NamePlateObject.IconXAdjust)).ToInt32();
|
||||
var iconYAdjustPtr = Pointer + Marshal.OffsetOf(typeof(AddonNamePlate.NamePlateObject), nameof(AddonNamePlate.NamePlateObject.IconYAdjust)).ToInt32();
|
||||
Marshal.WriteInt16(iconXAdjustPtr, x);
|
||||
Marshal.WriteInt16(iconYAdjustPtr, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pilz.Dalamud.Nameplates.Model
|
||||
{
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum StatusIcons
|
||||
{
|
||||
Disconnecting = 061503,
|
||||
InDuty = 061506,
|
||||
ViewingCutscene = 061508,
|
||||
Busy = 061509,
|
||||
Idle = 061511,
|
||||
DutyFinder = 061517,
|
||||
PartyLeader = 061521,
|
||||
PartyMember = 061522,
|
||||
RolePlaying = 061545,
|
||||
GroupPose = 061546,
|
||||
NewAdventurer = 061523,
|
||||
Mentor = 061540,
|
||||
MentorPvE = 061542,
|
||||
MentorCrafting = 061543,
|
||||
MentorPvP = 061544,
|
||||
Returner = 061547,
|
||||
}
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
using Dalamud.Hooking;
|
||||
using Pilz.Dalamud.Nameplates.EventArgs;
|
||||
using Dalamud.Utility.Signatures;
|
||||
using ImGuiNET;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Pilz.Dalamud.Nameplates.Model;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Pilz.Dalamud.Nameplates
|
||||
{
|
||||
public class NameplateHooks : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Will be executed when the the Game wants to update the content of a nameplate with the details of the Player.
|
||||
/// </summary>
|
||||
public event AddonNamePlate_SetPlayerNameEventHandler AddonNamePlate_SetPlayerName;
|
||||
public delegate void AddonNamePlate_SetPlayerNameEventHandler(AddonNamePlate_SetPlayerNameEventArgs eventArgs);
|
||||
|
||||
/// <summary>
|
||||
/// Will be executed when the the Game wants to update the content of a nameplate with the details of the Player.
|
||||
/// This will event acts on a higher level with SeString instead of IntPtr e.g.
|
||||
/// </summary>
|
||||
public event AddonNamePlate_SetPlayerNameManagedEventHandler AddonNamePlate_SetPlayerNameManaged;
|
||||
public delegate void AddonNamePlate_SetPlayerNameManagedEventHandler(AddonNamePlate_SetPlayerNameManagedEventArgs eventArgs);
|
||||
|
||||
[Signature("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 48 8B 5C 24 ?? 45 38 BE", DetourName = nameof(SetPlayerNameplateDetour))]
|
||||
private Hook<AddonNamePlate_SetPlayerNameplateDetour>? hook_AddonNamePlate_SetPlayerNameplateDetour = null;
|
||||
private unsafe delegate IntPtr AddonNamePlate_SetPlayerNameplateDetour(IntPtr playerNameplateObjectPtr, bool isTitleAboveName, bool isTitleVisible, IntPtr titlePtr, IntPtr namePtr, IntPtr freeCompanyPtr, IntPtr prefix, int iconId);
|
||||
|
||||
/// <summary>
|
||||
/// Defines if all hooks are enabled. If this is false, then there might be something wrong or the class already has been disposed.
|
||||
/// </summary>
|
||||
public bool IsValid
|
||||
{
|
||||
get
|
||||
{
|
||||
var isValid = true;
|
||||
|
||||
isValid &= IsHookEnabled(hook_AddonNamePlate_SetPlayerNameplateDetour);
|
||||
|
||||
return isValid;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new instance of NAmeplateHooks and automatically initialize and enable all Hooks.
|
||||
/// </summary>
|
||||
public NameplateHooks()
|
||||
{
|
||||
SignatureHelper.Initialise(this);
|
||||
}
|
||||
|
||||
~NameplateHooks()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Unhook();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize and enable all Hooks.
|
||||
/// </summary>
|
||||
internal void Initialize()
|
||||
{
|
||||
hook_AddonNamePlate_SetPlayerNameplateDetour?.Enable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disable all Hooks.
|
||||
/// </summary>
|
||||
internal void Unhook()
|
||||
{
|
||||
hook_AddonNamePlate_SetPlayerNameplateDetour?.Disable();
|
||||
}
|
||||
|
||||
private static bool IsHookEnabled<T>(Hook<T> hook) where T : Delegate
|
||||
{
|
||||
return hook != null && hook.IsEnabled;
|
||||
}
|
||||
|
||||
private IntPtr SetPlayerNameplateDetour(IntPtr playerNameplateObjectPtr, bool isTitleAboveName, bool isTitleVisible, IntPtr titlePtr, IntPtr namePtr, IntPtr freeCompanyPtr, IntPtr prefix, int iconId)
|
||||
{
|
||||
var result = IntPtr.Zero;
|
||||
|
||||
if (IsHookEnabled(hook_AddonNamePlate_SetPlayerNameplateDetour))
|
||||
{
|
||||
var eventArgs = new AddonNamePlate_SetPlayerNameEventArgs
|
||||
{
|
||||
PlayerNameplateObjectPtr = playerNameplateObjectPtr,
|
||||
TitlePtr = titlePtr,
|
||||
NamePtr = namePtr,
|
||||
FreeCompanyPtr = freeCompanyPtr,
|
||||
PrefixPtr = prefix,
|
||||
IsTitleAboveName = isTitleAboveName,
|
||||
IsTitleVisible = isTitleVisible,
|
||||
IconID = iconId
|
||||
};
|
||||
|
||||
void callOriginal()
|
||||
{
|
||||
eventArgs.Result = eventArgs.Original();
|
||||
}
|
||||
|
||||
// Add handler for the Original call
|
||||
eventArgs.CallOriginal += () =>
|
||||
{
|
||||
return hook_AddonNamePlate_SetPlayerNameplateDetour.Original(
|
||||
eventArgs.PlayerNameplateObjectPtr,
|
||||
eventArgs.IsTitleAboveName,
|
||||
eventArgs.IsTitleVisible,
|
||||
eventArgs.TitlePtr,
|
||||
eventArgs.NamePtr,
|
||||
eventArgs.FreeCompanyPtr,
|
||||
prefix,
|
||||
eventArgs.IconID);
|
||||
};
|
||||
|
||||
// Invoke Event
|
||||
var hasDefaultHookEvent = AddonNamePlate_SetPlayerName != null;
|
||||
AddonNamePlate_SetPlayerName?.Invoke(eventArgs);
|
||||
|
||||
if (AddonNamePlate_SetPlayerNameManaged != null)
|
||||
{
|
||||
var freeTitle = false;
|
||||
var freeName = false;
|
||||
var freeFreeCompany = false;
|
||||
var freePrefix = false;
|
||||
|
||||
// Create NamePlateObject if possible
|
||||
var namePlateObj = new SafeNameplateObject(playerNameplateObjectPtr);
|
||||
|
||||
// Create new event
|
||||
var managedEventArgs = new AddonNamePlate_SetPlayerNameManagedEventArgs
|
||||
{
|
||||
OriginalEventArgs = eventArgs,
|
||||
SafeNameplateObject = namePlateObj,
|
||||
Title = GameInterfaceHelper.ReadSeString(eventArgs.TitlePtr),
|
||||
Name = GameInterfaceHelper.ReadSeString(eventArgs.NamePtr),
|
||||
FreeCompany = GameInterfaceHelper.ReadSeString(eventArgs.FreeCompanyPtr),
|
||||
Prefix = GameInterfaceHelper.ReadSeString(eventArgs.PrefixPtr)
|
||||
};
|
||||
|
||||
// Get raw string content
|
||||
var titleRaw = managedEventArgs.Title.Encode();
|
||||
var nameRaw = managedEventArgs.Name.Encode();
|
||||
var freeCompanyRaw = managedEventArgs.FreeCompany.Encode();
|
||||
var prefixRaw = managedEventArgs.Prefix.Encode();
|
||||
|
||||
// Invoke Managed Event
|
||||
AddonNamePlate_SetPlayerNameManaged.Invoke(managedEventArgs);
|
||||
|
||||
// Get new Title string content
|
||||
var titleNewRaw = managedEventArgs.Title.Encode();
|
||||
if (!titleRaw.SequenceEqual(titleNewRaw))
|
||||
{
|
||||
eventArgs.TitlePtr = GameInterfaceHelper.PluginAllocate(titleNewRaw);
|
||||
freeTitle = true;
|
||||
}
|
||||
|
||||
// Get new Name string content
|
||||
var nameNewRaw = managedEventArgs.Name.Encode();
|
||||
if (!nameRaw.SequenceEqual(nameNewRaw))
|
||||
{
|
||||
eventArgs.NamePtr = GameInterfaceHelper.PluginAllocate(nameNewRaw);
|
||||
freeName = true;
|
||||
}
|
||||
|
||||
// Get new Free Company string content
|
||||
var freeCompanyNewRaw = managedEventArgs.FreeCompany.Encode();
|
||||
if (!freeCompanyRaw.SequenceEqual(freeCompanyNewRaw))
|
||||
{
|
||||
eventArgs.FreeCompanyPtr = GameInterfaceHelper.PluginAllocate(freeCompanyNewRaw);
|
||||
freeFreeCompany = true;
|
||||
}
|
||||
|
||||
// Get new Prefix string content
|
||||
var prefixNewRaw = managedEventArgs.Prefix.Encode();
|
||||
if (!prefixRaw.SequenceEqual(prefixNewRaw))
|
||||
{
|
||||
eventArgs.PrefixPtr = GameInterfaceHelper.PluginAllocate(prefixNewRaw);
|
||||
freePrefix = true;
|
||||
}
|
||||
|
||||
// Call Original as we changed something
|
||||
callOriginal();
|
||||
|
||||
// Free memory
|
||||
if (freeTitle)
|
||||
GameInterfaceHelper.PluginFree(eventArgs.TitlePtr);
|
||||
if (freeName)
|
||||
GameInterfaceHelper.PluginFree(eventArgs.NamePtr);
|
||||
if (freeFreeCompany)
|
||||
GameInterfaceHelper.PluginFree(eventArgs.FreeCompanyPtr);
|
||||
if (freePrefix)
|
||||
GameInterfaceHelper.PluginFree(eventArgs.PrefixPtr);
|
||||
}
|
||||
else if(!hasDefaultHookEvent)
|
||||
{
|
||||
// Call original in case of nothing get called, just to get secure it will not break the game when not calling it.
|
||||
callOriginal();
|
||||
}
|
||||
|
||||
// Set result
|
||||
result = eventArgs.Result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
using Dalamud.Hooking;
|
||||
using Pilz.Dalamud.Nameplates.EventArgs;
|
||||
using Dalamud.Utility.Signatures;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
using System.Runtime.InteropServices;
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Pilz.Dalamud.Nameplates.Model;
|
||||
|
||||
namespace Pilz.Dalamud.Nameplates
|
||||
{
|
||||
public class NameplateManager : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides events that you can hook to.
|
||||
/// </summary>
|
||||
public NameplateHooks Hooks { get; init; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Defines if all hooks are enabled and the NameplateManager is ready to go. If this is false, then there might be something wrong or something already has been disposed.
|
||||
/// </summary>
|
||||
public bool IsValid => Hooks.IsValid;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the NameplateManager.
|
||||
/// </summary>
|
||||
public NameplateManager()
|
||||
{
|
||||
Hooks.Initialize();
|
||||
}
|
||||
|
||||
~NameplateManager()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Hooks?.Dispose();
|
||||
}
|
||||
|
||||
public static T? GetNameplateGameObject<T>(SafeNameplateObject namePlateObject) where T : GameObject
|
||||
{
|
||||
return GetNameplateGameObject<T>(namePlateObject.Pointer);
|
||||
}
|
||||
|
||||
public static T? GetNameplateGameObject<T>(IntPtr nameplateObjectPtr) where T : GameObject
|
||||
{
|
||||
// Get the nameplate object array
|
||||
var nameplateAddonPtr = PluginServices.GameGui.GetAddonByName("NamePlate", 1);
|
||||
var nameplateObjectArrayPtrPtr = nameplateAddonPtr + Marshal.OffsetOf(typeof(AddonNamePlate), nameof(AddonNamePlate.NamePlateObjectArray)).ToInt32();
|
||||
var nameplateObjectArrayPtr = Marshal.ReadIntPtr(nameplateObjectArrayPtrPtr);
|
||||
if (nameplateObjectArrayPtr == IntPtr.Zero)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Determine the index of the nameplate object within the nameplate object array
|
||||
var namePlateObjectSize = Marshal.SizeOf(typeof(AddonNamePlate.NamePlateObject));
|
||||
var namePlateObjectPtr0 = nameplateObjectArrayPtr + namePlateObjectSize * 0;
|
||||
var namePlateIndex = (nameplateObjectPtr.ToInt64() - namePlateObjectPtr0.ToInt64()) / namePlateObjectSize;
|
||||
if (namePlateIndex < 0 || namePlateIndex >= AddonNamePlate.NumNamePlateObjects)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get the nameplate info array
|
||||
IntPtr nameplateInfoArrayPtr = IntPtr.Zero;
|
||||
unsafe
|
||||
{
|
||||
var framework = FFXIVClientStructs.FFXIV.Client.System.Framework.Framework.Instance();
|
||||
nameplateInfoArrayPtr = new IntPtr(&framework->GetUiModule()->GetRaptureAtkModule()->NamePlateInfoArray);
|
||||
}
|
||||
|
||||
// Get the nameplate info for the nameplate object
|
||||
var namePlateInfoPtr = new IntPtr(nameplateInfoArrayPtr.ToInt64() + Marshal.SizeOf(typeof(RaptureAtkModule.NamePlateInfo)) * namePlateIndex);
|
||||
RaptureAtkModule.NamePlateInfo namePlateInfo = Marshal.PtrToStructure<RaptureAtkModule.NamePlateInfo>(namePlateInfoPtr);
|
||||
|
||||
// Return the object for its object id
|
||||
var objectId = namePlateInfo.ObjectID.ObjectID;
|
||||
return PluginServices.ObjectTable.SearchById(objectId) as T;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Pilz.Dalamud.Nameplates.EventArgs;
|
||||
using Pilz.Dalamud.Tools.Strings;
|
||||
|
||||
namespace Pilz.Dalamud.Nameplates.Tools
|
||||
{
|
||||
public class NameplateChanges
|
||||
{
|
||||
private readonly Dictionary<NameplateElements, StringChangesProps> changes = new();
|
||||
|
||||
public NameplateChanges()
|
||||
{
|
||||
changes.Add(NameplateElements.Title, new());
|
||||
changes.Add(NameplateElements.Name, new());
|
||||
changes.Add(NameplateElements.FreeCompany, new());
|
||||
}
|
||||
|
||||
public NameplateChanges(AddonNamePlate_SetPlayerNameManagedEventArgs eventArgs) : this()
|
||||
{
|
||||
GetProps(NameplateElements.Title).Destination = eventArgs.Title;
|
||||
GetProps(NameplateElements.Name).Destination = eventArgs.Name;
|
||||
GetProps(NameplateElements.FreeCompany).Destination = eventArgs.FreeCompany;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the properties with the changes of an element of your choice where you can add your payloads to a change and setup some options.
|
||||
/// </summary>
|
||||
/// <param name="element">The position of your choice.</param>
|
||||
/// <returns></returns>
|
||||
public StringChangesProps GetProps(NameplateElements element)
|
||||
{
|
||||
return changes[element];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the changes of an element of your choice where you can add your payloads to a change.
|
||||
/// </summary>
|
||||
/// <param name="element">The position of your choice.</param>
|
||||
/// <returns></returns>
|
||||
public StringChanges GetChanges(NameplateElements element)
|
||||
{
|
||||
return GetProps(element).StringChanges;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a change of the position of the element of your choice where you can add your payloads.
|
||||
/// </summary>
|
||||
/// <param name="element">The position of your choice.</param>
|
||||
/// <returns></returns>
|
||||
public StringChange GetChange(NameplateElements element, StringPosition position)
|
||||
{
|
||||
return GetChanges(element).GetChange(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pilz.Dalamud.Nameplates.Tools
|
||||
{
|
||||
public class NameplateChangesProps
|
||||
{
|
||||
/// <summary>
|
||||
/// All the changes to the nameplate that should be made.
|
||||
/// </summary>
|
||||
public NameplateChanges Changes { get; set; }
|
||||
|
||||
public NameplateChangesProps()
|
||||
{
|
||||
}
|
||||
|
||||
public NameplateChangesProps(NameplateChanges changes) : this()
|
||||
{
|
||||
Changes = changes;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pilz.Dalamud.Nameplates.Tools
|
||||
{
|
||||
public enum NameplateElements
|
||||
{
|
||||
Name,
|
||||
Title,
|
||||
FreeCompany
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||
using Pilz.Dalamud.ActivityContexts;
|
||||
using Pilz.Dalamud.Nameplates.Model;
|
||||
using Pilz.Dalamud.Tools;
|
||||
using Pilz.Dalamud.Tools.Strings;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pilz.Dalamud.Nameplates.Tools
|
||||
{
|
||||
public static class NameplateUpdateFactory
|
||||
{
|
||||
public static void ApplyNameplateChanges(NameplateChangesProps props)
|
||||
{
|
||||
foreach (NameplateElements element in Enum.GetValues(typeof(NameplateElements)))
|
||||
{
|
||||
var change = props.Changes.GetProps(element);
|
||||
StringUpdateFactory.ApplyStringChanges(change);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ApplyStatusIconWithPrio(ref int statusIcon, int newStatusIcon, StringChange stringChange, ActivityContext activityContext, StatusIconPriorizer priorizer, bool moveIconToNameplateIfPossible)
|
||||
{
|
||||
bool? isPrio = null;
|
||||
var fontIcon = StatusIconFontConverter.GetBitmapFontIconFromStatusIcon((StatusIcons)statusIcon);
|
||||
|
||||
if (moveIconToNameplateIfPossible)
|
||||
{
|
||||
if (fontIcon != null)
|
||||
{
|
||||
// Set new font icon as string change
|
||||
var iconPayload = new IconPayload(fontIcon.Value);
|
||||
stringChange.Payloads.Insert(0, iconPayload);
|
||||
|
||||
// If we moved it, we don't need it as icon anymore, yay :D
|
||||
isPrio = false;
|
||||
}
|
||||
}
|
||||
|
||||
isPrio ??= priorizer.IsPriorityIcon(statusIcon, activityContext);
|
||||
|
||||
if (!isPrio.Value)
|
||||
statusIcon = newStatusIcon;
|
||||
|
||||
return isPrio.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Pilz.Dalamud.ActivityContexts;
|
||||
using Pilz.Dalamud.Nameplates.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pilz.Dalamud.Nameplates.Tools
|
||||
{
|
||||
public class StatusIconPriorizer
|
||||
{
|
||||
private static StatusIconPriorizerSettings DefaultSettings { get; } = new();
|
||||
public StatusIconPriorizerSettings Settings { get; init; }
|
||||
|
||||
public StatusIconPriorizer() : this(DefaultSettings)
|
||||
{
|
||||
}
|
||||
|
||||
public StatusIconPriorizer(StatusIconPriorizerSettings settings)
|
||||
{
|
||||
Settings = settings;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check for an icon that should take priority over the job icon,
|
||||
/// taking into account whether or not the player is in a duty.
|
||||
/// </summary>
|
||||
/// <param name="iconId">The incoming icon id that is being overwritten by the plugin.</param>
|
||||
/// <param name="priorityIconId">The icon id that should be used.</param>
|
||||
/// <returns>Whether a priority icon was found.</returns>
|
||||
public bool IsPriorityIcon(int iconId, ActivityContext activityContext)
|
||||
{
|
||||
bool isPrioIcon;
|
||||
|
||||
if (!Settings.UsePriorizedIcons && iconId != (int)StatusIcons.Disconnecting && iconId != (int)StatusIcons.Disconnecting + 50)
|
||||
isPrioIcon = false;
|
||||
else
|
||||
{
|
||||
// Select which set of priority icons to use based on whether we're in a duty
|
||||
// In the future, there can be a third list used when in combat
|
||||
var priorityIcons = GetPriorityIcons(activityContext);
|
||||
|
||||
// Determine whether the incoming icon should take priority over the job icon
|
||||
// Check the id plus 50 as that's an alternately sized version
|
||||
isPrioIcon = priorityIcons.Contains(iconId) || priorityIcons.Contains(iconId + 50);
|
||||
}
|
||||
|
||||
return isPrioIcon;
|
||||
}
|
||||
|
||||
private IEnumerable<int> GetPriorityIcons(ActivityContext activityContext)
|
||||
{
|
||||
StatusIconPriorizerConditionSets set;
|
||||
|
||||
if (activityContext.ZoneType == ZoneType.Foray)
|
||||
set = StatusIconPriorizerConditionSets.InForay;
|
||||
else if (activityContext.IsInDuty)
|
||||
set = StatusIconPriorizerConditionSets.InDuty;
|
||||
else
|
||||
set = StatusIconPriorizerConditionSets.Overworld;
|
||||
|
||||
return Settings.GetConditionSet(set).Select(n => (int)n);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pilz.Dalamud.Nameplates.Tools
|
||||
{
|
||||
public enum StatusIconPriorizerConditionSets
|
||||
{
|
||||
Overworld,
|
||||
InDuty,
|
||||
InForay
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
using Pilz.Dalamud.ActivityContexts;
|
||||
using Pilz.Dalamud.Nameplates.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pilz.Dalamud.Nameplates.Tools
|
||||
{
|
||||
public class StatusIconPriorizerSettings
|
||||
{
|
||||
[JsonProperty("IconConditionSets")]
|
||||
private Dictionary<StatusIconPriorizerConditionSets, List<StatusIcons>> iconConditionSets = new();
|
||||
public bool UsePriorizedIcons { get; set; } = true;
|
||||
|
||||
[JsonConstructor]
|
||||
private StatusIconPriorizerSettings(JsonConstructorAttribute dummy)
|
||||
{
|
||||
}
|
||||
|
||||
public StatusIconPriorizerSettings() : this(false)
|
||||
{
|
||||
}
|
||||
|
||||
public StatusIconPriorizerSettings(bool fillWithDefaultSettings)
|
||||
{
|
||||
foreach (StatusIconPriorizerConditionSets set in Enum.GetValues(typeof(StatusIconPriorizerConditionSets)))
|
||||
iconConditionSets.Add(set, new List<StatusIcons>());
|
||||
|
||||
if (fillWithDefaultSettings)
|
||||
FillWithDefaultSettings();
|
||||
}
|
||||
|
||||
public List<StatusIcons> GetConditionSet(StatusIconPriorizerConditionSets set)
|
||||
{
|
||||
return iconConditionSets[set];
|
||||
}
|
||||
|
||||
public void ResetToEmpty()
|
||||
{
|
||||
foreach (var kvp in iconConditionSets)
|
||||
kvp.Value.Clear();
|
||||
}
|
||||
|
||||
public void ResetToDefault()
|
||||
{
|
||||
ResetToEmpty();
|
||||
FillWithDefaultSettings();
|
||||
}
|
||||
|
||||
private void FillWithDefaultSettings()
|
||||
{
|
||||
var setOverworld = GetConditionSet(StatusIconPriorizerConditionSets.Overworld);
|
||||
setOverworld.AddRange(new[]
|
||||
{
|
||||
StatusIcons.Disconnecting, // Disconnecting
|
||||
StatusIcons.InDuty, // In Duty
|
||||
StatusIcons.ViewingCutscene, // Viewing Cutscene
|
||||
StatusIcons.Busy, // Busy
|
||||
StatusIcons.Idle, // Idle
|
||||
StatusIcons.DutyFinder, // Duty Finder
|
||||
StatusIcons.PartyLeader, // Party Leader
|
||||
StatusIcons.PartyMember, // Party Member
|
||||
StatusIcons.RolePlaying, // Role Playing
|
||||
StatusIcons.GroupPose, // Group Pose
|
||||
StatusIcons.Mentor,
|
||||
StatusIcons.MentorCrafting,
|
||||
StatusIcons.MentorPvE,
|
||||
StatusIcons.MentorPvP,
|
||||
StatusIcons.Returner,
|
||||
StatusIcons.NewAdventurer,
|
||||
});
|
||||
|
||||
var setInDuty = GetConditionSet(StatusIconPriorizerConditionSets.InDuty);
|
||||
setInDuty.AddRange(new[]
|
||||
{
|
||||
StatusIcons.Disconnecting, // Disconnecting
|
||||
StatusIcons.ViewingCutscene, // Viewing Cutscene
|
||||
StatusIcons.Idle, // Idle
|
||||
StatusIcons.GroupPose, // Group Pose
|
||||
StatusIcons.Mentor,
|
||||
StatusIcons.MentorCrafting,
|
||||
StatusIcons.MentorPvE,
|
||||
StatusIcons.MentorPvP,
|
||||
StatusIcons.Returner,
|
||||
StatusIcons.NewAdventurer,
|
||||
});
|
||||
|
||||
var setInForay = GetConditionSet(StatusIconPriorizerConditionSets.InForay);
|
||||
setInForay.AddRange(new[]
|
||||
{
|
||||
// This allows you to see which players don't have a party
|
||||
StatusIcons.InDuty, // In Duty
|
||||
|
||||
StatusIcons.Disconnecting, // Disconnecting
|
||||
StatusIcons.ViewingCutscene, // Viewing Cutscene
|
||||
StatusIcons.Idle, // Idle
|
||||
StatusIcons.GroupPose, // Group Pose
|
||||
StatusIcons.Mentor,
|
||||
StatusIcons.MentorCrafting,
|
||||
StatusIcons.MentorPvE,
|
||||
StatusIcons.MentorPvP,
|
||||
StatusIcons.Returner,
|
||||
StatusIcons.NewAdventurer,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>annotations</Nullable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
@@ -9,6 +9,7 @@
|
||||
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<Platforms>x64</Platforms>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
@@ -24,10 +25,18 @@
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<RepositoryUrl>https://github.com/Pilzinsel64/Pilz.Dalamud</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<Version>0.4.0</Version>
|
||||
<Version>0.6.1.1</Version>
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<NoWarn>1701;1702;1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<NoWarn>1701;1702;1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="FFXIVClientStructs">
|
||||
<HintPath>$(DalamudLibPath)FFXIVClientStructs.dll</HintPath>
|
||||
@@ -60,7 +69,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\README.md" Pack="true" PackagePath="\"/>
|
||||
<None Include="..\README.md" Pack="true" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,28 +1,21 @@
|
||||
using Dalamud.Data;
|
||||
using Dalamud.Game.ClientState;
|
||||
using Dalamud.Game.ClientState.Objects;
|
||||
using Dalamud.Game.Gui;
|
||||
using Dalamud.IoC;
|
||||
using Dalamud.IoC;
|
||||
using Dalamud.Plugin;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Dalamud.Plugin.Services;
|
||||
|
||||
namespace Pilz.Dalamud
|
||||
namespace Pilz.Dalamud;
|
||||
|
||||
public class PluginServices
|
||||
{
|
||||
public class PluginServices
|
||||
{
|
||||
[PluginService] public static GameGui GameGui { get; set; } = null;
|
||||
[PluginService] public static DalamudPluginInterface PluginInterface { get; set; } = null;
|
||||
[PluginService] public static ClientState ClientState { get; set; } = null;
|
||||
[PluginService] public static DataManager DataManager { get; set; } = null;
|
||||
[PluginService] public static ObjectTable ObjectTable { get; set; } = null;
|
||||
[PluginService] public static IDalamudPluginInterface PluginInterface { get; set; }
|
||||
[PluginService] public static IGameGui GameGui { get; set; }
|
||||
[PluginService] public static IClientState ClientState { get; set; }
|
||||
[PluginService] public static IDataManager DataManager { get; set; }
|
||||
[PluginService] public static IObjectTable ObjectTable { get; set; }
|
||||
[PluginService] public static IGameInteropProvider GameInteropProvider { get; set; }
|
||||
[PluginService] public static IAddonLifecycle AddonLifecycle { get; set; }
|
||||
|
||||
public static void Initialize(DalamudPluginInterface dalamudPluginInterface)
|
||||
public static void Initialize(IDalamudPluginInterface dalamudPluginInterface)
|
||||
{
|
||||
dalamudPluginInterface.Create<PluginServices>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
25
Pilz.Dalamud/Tools/NamePlates/NameplateChanges.cs
Normal file
25
Pilz.Dalamud/Tools/NamePlates/NameplateChanges.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using Pilz.Dalamud.NamePlate;
|
||||
|
||||
namespace Pilz.Dalamud.Tools.NamePlates;
|
||||
|
||||
public class NameplateChanges
|
||||
{
|
||||
private readonly List<NameplateElementChange> changes = [];
|
||||
|
||||
public NameplateChanges(INamePlateUpdateHandler handler)
|
||||
{
|
||||
changes.Add(new(NameplateElements.Title, handler));
|
||||
changes.Add(new(NameplateElements.Name, handler));
|
||||
changes.Add(new(NameplateElements.FreeCompany, handler));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the properties with the changes of an element of your choice where you can add your payloads to a change and setup some options.
|
||||
/// </summary>
|
||||
/// <param name="element">The position of your choice.</param>
|
||||
/// <returns></returns>
|
||||
public NameplateElementChange GetChange(NameplateElements element)
|
||||
{
|
||||
return changes.FirstOrDefault(n => n.Element == element);
|
||||
}
|
||||
}
|
||||
18
Pilz.Dalamud/Tools/NamePlates/NameplateChangesProps.cs
Normal file
18
Pilz.Dalamud/Tools/NamePlates/NameplateChangesProps.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace Pilz.Dalamud.Tools.NamePlates;
|
||||
|
||||
public class NameplateChangesProps
|
||||
{
|
||||
/// <summary>
|
||||
/// All the changes to the nameplate that should be made.
|
||||
/// </summary>
|
||||
public NameplateChanges Changes { get; set; }
|
||||
|
||||
public NameplateChangesProps()
|
||||
{
|
||||
}
|
||||
|
||||
public NameplateChangesProps(NameplateChanges changes) : this()
|
||||
{
|
||||
Changes = changes;
|
||||
}
|
||||
}
|
||||
47
Pilz.Dalamud/Tools/NamePlates/NameplateElementChange.cs
Normal file
47
Pilz.Dalamud/Tools/NamePlates/NameplateElementChange.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Pilz.Dalamud.NamePlate;
|
||||
using Pilz.Dalamud.Tools.Strings;
|
||||
|
||||
namespace Pilz.Dalamud.Tools.NamePlates;
|
||||
|
||||
public class NameplateElementChange(NameplateElements element, INamePlateUpdateHandler handler)
|
||||
{
|
||||
public NameplateElements Element => element;
|
||||
public StringChanges Changes { get; set; } = new();
|
||||
|
||||
public void ApplyFormatting(SeString prefix, SeString postfix)
|
||||
{
|
||||
var parts = (prefix, postfix);
|
||||
|
||||
switch (element)
|
||||
{
|
||||
case NameplateElements.Name:
|
||||
handler.NameParts.TextWrap = parts;
|
||||
break;
|
||||
case NameplateElements.Title:
|
||||
handler.TitleParts.OuterWrap = parts;
|
||||
break;
|
||||
case NameplateElements.FreeCompany:
|
||||
handler.FreeCompanyTagParts.OuterWrap = parts;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyChanges()
|
||||
{
|
||||
if (Changes.Any())
|
||||
{
|
||||
StringUpdateFactory.ApplyStringChanges(new()
|
||||
{
|
||||
StringChanges = Changes,
|
||||
Destination = element switch
|
||||
{
|
||||
NameplateElements.Name => handler.NameParts.Text ??= handler.InfoView.Name,
|
||||
NameplateElements.Title => handler.TitleParts.Text ??= handler.InfoView.Title,
|
||||
NameplateElements.FreeCompany => handler.FreeCompanyTagParts.Text ??= handler.InfoView.FreeCompanyTag,
|
||||
_ => null,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
8
Pilz.Dalamud/Tools/NamePlates/NameplateElements.cs
Normal file
8
Pilz.Dalamud/Tools/NamePlates/NameplateElements.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace Pilz.Dalamud.Tools.NamePlates;
|
||||
|
||||
public enum NameplateElements
|
||||
{
|
||||
Name,
|
||||
Title,
|
||||
FreeCompany,
|
||||
}
|
||||
48
Pilz.Dalamud/Tools/NamePlates/NameplateUpdateFactory.cs
Normal file
48
Pilz.Dalamud/Tools/NamePlates/NameplateUpdateFactory.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||
using Pilz.Dalamud.ActivityContexts;
|
||||
using Pilz.Dalamud.Icons;
|
||||
using Pilz.Dalamud.NamePlate;
|
||||
|
||||
namespace Pilz.Dalamud.Tools.NamePlates;
|
||||
|
||||
public static class NameplateUpdateFactory
|
||||
{
|
||||
public static void ApplyNameplateChanges(NameplateChangesProps props)
|
||||
{
|
||||
foreach (NameplateElements element in Enum.GetValues(typeof(NameplateElements)))
|
||||
{
|
||||
var change = props.Changes.GetChange(element);
|
||||
change.ApplyChanges();
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ApplyStatusIconWithPrio(INamePlateUpdateHandler handler, int newStatusIcon, ActivityContext activityContext, StatusIconPriorizer priorizer, bool moveIconToNameplateIfPossible)
|
||||
{
|
||||
bool? isPrio = null;
|
||||
var fontIcon = StatusIconFontConverter.GetBitmapFontIconFromStatusIcon((StatusIcons)handler.NameIconId);
|
||||
|
||||
if (moveIconToNameplateIfPossible)
|
||||
{
|
||||
if (fontIcon != null)
|
||||
{
|
||||
// Set new font icon as string change
|
||||
var icon = new IconPayload(fontIcon.Value); ;
|
||||
if (handler.StatusPrefix is SeString str)
|
||||
str.Payloads.Insert(0, icon);
|
||||
else
|
||||
handler.StatusPrefix = SeString.Empty.Append(icon);
|
||||
|
||||
// If we moved it, we don't need it as icon anymore, yay :D
|
||||
isPrio = false;
|
||||
}
|
||||
}
|
||||
|
||||
isPrio ??= priorizer.IsPriorityIcon(handler.NameIconId, activityContext);
|
||||
|
||||
if (!isPrio.Value)
|
||||
handler.NameIconId = newStatusIcon;
|
||||
|
||||
return isPrio.Value;
|
||||
}
|
||||
}
|
||||
60
Pilz.Dalamud/Tools/NamePlates/StatusIconPriorizer.cs
Normal file
60
Pilz.Dalamud/Tools/NamePlates/StatusIconPriorizer.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using Pilz.Dalamud.ActivityContexts;
|
||||
using Pilz.Dalamud.Icons;
|
||||
|
||||
namespace Pilz.Dalamud.Tools.NamePlates;
|
||||
|
||||
public class StatusIconPriorizer
|
||||
{
|
||||
private static StatusIconPriorizerSettings DefaultSettings { get; } = new();
|
||||
public StatusIconPriorizerSettings Settings { get; init; }
|
||||
|
||||
public StatusIconPriorizer() : this(DefaultSettings)
|
||||
{
|
||||
}
|
||||
|
||||
public StatusIconPriorizer(StatusIconPriorizerSettings settings)
|
||||
{
|
||||
Settings = settings;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check for an icon that should take priority over the job icon,
|
||||
/// taking into account whether or not the player is in a duty.
|
||||
/// </summary>
|
||||
/// <param name="iconId">The incoming icon id that is being overwritten by the plugin.</param>
|
||||
/// <param name="activityContext"></param>
|
||||
/// <returns>Whether a priority icon was found.</returns>
|
||||
public bool IsPriorityIcon(int iconId, ActivityContext activityContext)
|
||||
{
|
||||
bool isPrioIcon;
|
||||
|
||||
if (!Settings.UsePriorizedIcons && iconId != (int)StatusIcons.Disconnecting && iconId != (int)StatusIcons.Disconnecting + 50)
|
||||
isPrioIcon = false;
|
||||
else
|
||||
{
|
||||
// Select which set of priority icons to use based on whether we're in a duty
|
||||
// In the future, there can be a third list used when in combat
|
||||
var priorityIcons = GetPriorityIcons(activityContext);
|
||||
|
||||
// Determine whether the incoming icon should take priority over the job icon
|
||||
// Check the id plus 50 as that's an alternately sized version
|
||||
isPrioIcon = priorityIcons.Contains(iconId) || priorityIcons.Contains(iconId + 50);
|
||||
}
|
||||
|
||||
return isPrioIcon;
|
||||
}
|
||||
|
||||
private IEnumerable<int> GetPriorityIcons(ActivityContext activityContext)
|
||||
{
|
||||
StatusIconPriorizerConditionSets set;
|
||||
|
||||
if (activityContext.ZoneType == ZoneType.Foray)
|
||||
set = StatusIconPriorizerConditionSets.InForay;
|
||||
else if (activityContext.IsInDuty)
|
||||
set = StatusIconPriorizerConditionSets.InDuty;
|
||||
else
|
||||
set = StatusIconPriorizerConditionSets.Overworld;
|
||||
|
||||
return Settings.GetConditionSet(set).Select(n => (int)n);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Pilz.Dalamud.Tools.NamePlates;
|
||||
|
||||
public enum StatusIconPriorizerConditionSets
|
||||
{
|
||||
Overworld,
|
||||
InDuty,
|
||||
InForay
|
||||
}
|
||||
103
Pilz.Dalamud/Tools/NamePlates/StatusIconPriorizerSettings.cs
Normal file
103
Pilz.Dalamud/Tools/NamePlates/StatusIconPriorizerSettings.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using Newtonsoft.Json;
|
||||
using Pilz.Dalamud.Icons;
|
||||
|
||||
namespace Pilz.Dalamud.Tools.NamePlates;
|
||||
|
||||
public class StatusIconPriorizerSettings
|
||||
{
|
||||
[JsonProperty("IconConditionSets")]
|
||||
private Dictionary<StatusIconPriorizerConditionSets, List<StatusIcons>> iconConditionSets = [];
|
||||
public bool UsePriorizedIcons { get; set; } = true;
|
||||
|
||||
[JsonConstructor]
|
||||
private StatusIconPriorizerSettings(JsonConstructorAttribute dummy)
|
||||
{
|
||||
}
|
||||
|
||||
public StatusIconPriorizerSettings() : this(false)
|
||||
{
|
||||
}
|
||||
|
||||
public StatusIconPriorizerSettings(bool fillWithDefaultSettings)
|
||||
{
|
||||
foreach (StatusIconPriorizerConditionSets set in Enum.GetValues(typeof(StatusIconPriorizerConditionSets)))
|
||||
iconConditionSets.Add(set, []);
|
||||
|
||||
if (fillWithDefaultSettings)
|
||||
FillWithDefaultSettings();
|
||||
}
|
||||
|
||||
public List<StatusIcons> GetConditionSet(StatusIconPriorizerConditionSets set)
|
||||
{
|
||||
return iconConditionSets[set];
|
||||
}
|
||||
|
||||
public void ResetToEmpty()
|
||||
{
|
||||
foreach (var kvp in iconConditionSets)
|
||||
kvp.Value.Clear();
|
||||
}
|
||||
|
||||
public void ResetToDefault()
|
||||
{
|
||||
ResetToEmpty();
|
||||
FillWithDefaultSettings();
|
||||
}
|
||||
|
||||
private void FillWithDefaultSettings()
|
||||
{
|
||||
var setOverworld = GetConditionSet(StatusIconPriorizerConditionSets.Overworld);
|
||||
setOverworld.AddRange(new[]
|
||||
{
|
||||
StatusIcons.Disconnecting, // Disconnecting
|
||||
StatusIcons.InDuty, // In Duty
|
||||
StatusIcons.ViewingCutscene, // Viewing Cutscene
|
||||
StatusIcons.Busy, // Busy
|
||||
StatusIcons.Idle, // Idle
|
||||
StatusIcons.DutyFinder, // Duty Finder
|
||||
StatusIcons.PartyLeader, // Party Leader
|
||||
StatusIcons.PartyMember, // Party Member
|
||||
StatusIcons.RolePlaying, // Role Playing
|
||||
StatusIcons.GroupPose, // Group Pose
|
||||
StatusIcons.Mentor,
|
||||
StatusIcons.MentorCrafting,
|
||||
StatusIcons.MentorPvE,
|
||||
StatusIcons.MentorPvP,
|
||||
StatusIcons.Returner,
|
||||
StatusIcons.NewAdventurer,
|
||||
});
|
||||
|
||||
var setInDuty = GetConditionSet(StatusIconPriorizerConditionSets.InDuty);
|
||||
setInDuty.AddRange(new[]
|
||||
{
|
||||
StatusIcons.Disconnecting, // Disconnecting
|
||||
StatusIcons.ViewingCutscene, // Viewing Cutscene
|
||||
StatusIcons.Idle, // Idle
|
||||
StatusIcons.GroupPose, // Group Pose
|
||||
StatusIcons.Mentor,
|
||||
StatusIcons.MentorCrafting,
|
||||
StatusIcons.MentorPvE,
|
||||
StatusIcons.MentorPvP,
|
||||
StatusIcons.Returner,
|
||||
StatusIcons.NewAdventurer,
|
||||
});
|
||||
|
||||
var setInForay = GetConditionSet(StatusIconPriorizerConditionSets.InForay);
|
||||
setInForay.AddRange(new[]
|
||||
{
|
||||
// This allows you to see which players don't have a party
|
||||
StatusIcons.InDuty, // In Duty
|
||||
|
||||
StatusIcons.Disconnecting, // Disconnecting
|
||||
StatusIcons.ViewingCutscene, // Viewing Cutscene
|
||||
StatusIcons.Idle, // Idle
|
||||
StatusIcons.GroupPose, // Group Pose
|
||||
StatusIcons.Mentor,
|
||||
StatusIcons.MentorCrafting,
|
||||
StatusIcons.MentorPvE,
|
||||
StatusIcons.MentorPvP,
|
||||
StatusIcons.Returner,
|
||||
StatusIcons.NewAdventurer,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,10 @@
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Pilz.Dalamud.Nameplates.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Pilz.Dalamud.Icons;
|
||||
|
||||
namespace Pilz.Dalamud.Tools
|
||||
namespace Pilz.Dalamud.Tools;
|
||||
|
||||
public static class StatusIconFontConverter
|
||||
{
|
||||
public static class StatusIconFontConverter
|
||||
{
|
||||
public static StatusIcons? GetStatusIconFromBitmapFontIcon(BitmapFontIcon fontIcon)
|
||||
{
|
||||
return fontIcon switch
|
||||
@@ -37,5 +32,4 @@ namespace Pilz.Dalamud.Tools
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pilz.Dalamud.Tools.Strings
|
||||
namespace Pilz.Dalamud.Tools.Strings;
|
||||
|
||||
public class StringChange
|
||||
{
|
||||
public class StringChange
|
||||
{
|
||||
/// <summary>
|
||||
/// The payloads to use for inserting/replacing.
|
||||
/// </summary>
|
||||
public List<Payload> Payloads { get; init; } = new();
|
||||
public List<Payload> Payloads { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Defines if only one anchor payload should be used, if using anchor payloads.
|
||||
@@ -20,5 +15,4 @@ namespace Pilz.Dalamud.Tools.Strings
|
||||
/// Not needed to be true for the most cases.
|
||||
/// </summary>
|
||||
public bool ForceUsingSingleAnchorPayload { get; set; } = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
namespace Pilz.Dalamud.Tools.Strings;
|
||||
|
||||
namespace Pilz.Dalamud.Tools.Strings
|
||||
public class StringChanges
|
||||
{
|
||||
public class StringChanges
|
||||
{
|
||||
private readonly Dictionary<StringPosition, StringChange> changes = new();
|
||||
private readonly Dictionary<StringPosition, StringChange> changes = [];
|
||||
|
||||
public StringChanges()
|
||||
{
|
||||
@@ -36,5 +29,4 @@ namespace Pilz.Dalamud.Tools.Strings
|
||||
{
|
||||
return changes.Sum(n => n.Value.Payloads.Count) != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
|
||||
namespace Pilz.Dalamud.Tools.Strings
|
||||
namespace Pilz.Dalamud.Tools.Strings;
|
||||
|
||||
public class StringChangesProps
|
||||
{
|
||||
public class StringChangesProps
|
||||
{
|
||||
/// <summary>
|
||||
/// The string where the changes should be applied.
|
||||
/// </summary>
|
||||
@@ -21,11 +15,10 @@ namespace Pilz.Dalamud.Tools.Strings
|
||||
/// <summary>
|
||||
/// Payloads to use as anchor where the changes should be applied to.
|
||||
/// </summary>
|
||||
public List<Payload> AnchorPayloads { get; set; } = new();
|
||||
public List<Payload> AnchorPayloads { get; set; } = [];
|
||||
/// <summary>
|
||||
/// A single payload to use as anchor where the changes should be applied to.
|
||||
/// This property will only be used if StringChange.ForceSingleAnchorPayload is true.
|
||||
/// </summary>
|
||||
public Payload AnchorPayload { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
namespace Pilz.Dalamud.Tools.Strings;
|
||||
|
||||
namespace Pilz.Dalamud.Tools.Strings
|
||||
public enum StringPosition
|
||||
{
|
||||
public enum StringPosition
|
||||
{
|
||||
Before,
|
||||
After,
|
||||
Replace
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||
using Lumina.Text;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pilz.Dalamud.Tools.Strings
|
||||
namespace Pilz.Dalamud.Tools.Strings;
|
||||
|
||||
public static class StringUpdateFactory
|
||||
{
|
||||
public static class StringUpdateFactory
|
||||
{
|
||||
public static void ApplyStringChanges(StringChangesProps props)
|
||||
{
|
||||
if (props.StringChanges != null && props.StringChanges.Any())
|
||||
@@ -128,5 +122,4 @@ namespace Pilz.Dalamud.Tools.Strings
|
||||
|
||||
return tagPositionsOrdered;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Pilz.Dalamud.Nameplates.Model;
|
||||
|
||||
namespace Pilz.Dalamud
|
||||
{
|
||||
public class XivApi
|
||||
{
|
||||
private static IntPtr _RaptureAtkModulePtr = IntPtr.Zero;
|
||||
|
||||
public static IntPtr RaptureAtkModulePtr
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_RaptureAtkModulePtr == IntPtr.Zero)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var framework = Framework.Instance();
|
||||
var uiModule = framework->GetUiModule();
|
||||
|
||||
_RaptureAtkModulePtr = new IntPtr(uiModule->GetRaptureAtkModule());
|
||||
}
|
||||
}
|
||||
|
||||
return _RaptureAtkModulePtr;
|
||||
}
|
||||
}
|
||||
|
||||
public static SafeAddonNameplate GetSafeAddonNamePlate()
|
||||
{
|
||||
return new(PluginServices.PluginInterface);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": 1,
|
||||
"dependencies": {
|
||||
"net7.0-windows7.0": {}
|
||||
"net8.0-windows7.0": {}
|
||||
}
|
||||
}
|
||||
14
Publish NuGet packages.bat
Normal file
14
Publish NuGet packages.bat
Normal file
@@ -0,0 +1,14 @@
|
||||
@echo off
|
||||
|
||||
set sourceP="https://git.pilzinsel64.de/api/v4/projects/25/packages/nuget/index.json"
|
||||
set apikeyP=%GitLab_git_pilzinsel64_de%
|
||||
|
||||
set sourceN="https://api.nuget.org/v3/index.json"
|
||||
set apikeyN=%NuGet_ApiKey%
|
||||
|
||||
for %%x in (%*) do (
|
||||
dotnet nuget push "%%x" --source "%sourceP%" --api-key "%apikeyP%" --skip-duplicate
|
||||
dotnet nuget push "%%x" --source "%sourceN%" --api-key "%apikeyN%" --skip-duplicate
|
||||
)
|
||||
|
||||
pause
|
||||
79
README.md
79
README.md
@@ -8,6 +8,9 @@ At the moment it's far away from being complete or even good. Right now it as so
|
||||
Install the latest version of `Pilz.Dalamud` via NuGet Package Manager or NuGet Console:\
|
||||
https://www.nuget.org/packages/Pilz.Dalamud
|
||||
|
||||
Or add the alternative package source for slightly quicker updates:\
|
||||
`<add key="Pilz.Dalamud" value="https://git.pilzinsel64.de/api/v4/projects/8/packages/nuget/index.json" />`\
|
||||
|
||||
## Get started
|
||||
|
||||
### Initialize Plugin Services
|
||||
@@ -25,78 +28,16 @@ public Plugin(DalamudPluginInterface pluginInterface)
|
||||
}
|
||||
```
|
||||
|
||||
### Hook into Nameplates
|
||||
### Hook into NamePlates
|
||||
|
||||
To edit the nameplate, you first need to hook and listen to the Game's updates. Also don't forget to unhook and dispose on unloading the plugins!
|
||||
Nameplates has been reworked by @nebel and will be part of core Dalamud soon.
|
||||
|
||||
Read more at: https://github.com/goatcorp/Dalamud/pull/1915
|
||||
|
||||
Use the new service already now via Pilz.Dalamud:
|
||||
|
||||
```cs
|
||||
public class NameplateFeature : IDisposable
|
||||
{
|
||||
public NameplateManager NameplateManager { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a player nameplate is updated by the game.
|
||||
/// </summary>
|
||||
public event PlayerNameplateUpdatedDelegate? PlayerNameplateUpdated;
|
||||
|
||||
public NameplateFeature()
|
||||
{
|
||||
NameplateManager = new();
|
||||
NameplateManager.Hooks.AddonNamePlate_SetPlayerNameManaged += Hooks_AddonNamePlate_SetPlayerNameManaged;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
NameplateManager.Hooks.AddonNamePlate_SetPlayerNameManaged -= Hooks_AddonNamePlate_SetPlayerNameManaged;
|
||||
NameplateManager.Dispose();
|
||||
}
|
||||
|
||||
private void Hooks_AddonNamePlate_SetPlayerNameManaged(Pilz.Dalamud.Nameplates.EventArgs.AddonNamePlate_SetPlayerNameManagedEventArgs eventArgs)
|
||||
{
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This is an example of editing the title to "Good Player", make the name italic and also force the title to always be above the name:
|
||||
|
||||
```cs
|
||||
private void Hooks_AddonNamePlate_SetPlayerNameManaged(Pilz.Dalamud.Nameplates.EventArgs.AddonNamePlate_SetPlayerNameManagedEventArgs eventArgs)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get the referenced player object for the nameplate object
|
||||
PlayerCharacter? playerCharacter = NameplateManager.GetNameplateGameObject<PlayerCharacter>(eventArgs.SafeNameplateObject);
|
||||
|
||||
if (playerCharacter != null && playerCharacter.StatusFlags.HasFlag(StatusFlags.Friend))
|
||||
{
|
||||
const string TEXT_GOOD_PLAYER = "Good Player";
|
||||
|
||||
// Create a new change
|
||||
var nameplateChanges = new NameplateChanges(eventArgs);
|
||||
|
||||
// Replace the title
|
||||
var titleChange = nameplateChanges.GetChange(NameplateElements.Title, StringPosition.Replace);
|
||||
titleChange.Payloads.Add(new TextPayload(TEXT_GOOD_PLAYER));
|
||||
|
||||
// Make the name italic
|
||||
var nameChangeBefore = nameplateChanges.GetChange(NameplateElements.Name, StringPosition.Before);
|
||||
nameChangeBefore.Payloads.Add(new EmphasisItalicPayload(true));
|
||||
|
||||
var nameChangeAfter = nameplateChanges.GetChange(NameplateElements.Name, StringPosition.After);
|
||||
nameChangeAfter.Payloads.Add(new EmphasisItalicPayload(false));
|
||||
|
||||
// Forge the title to be always above the name (this we can edit directly)
|
||||
eventArgs.IsTitleAboveName = true;
|
||||
|
||||
// Apply the string changes!
|
||||
NameplateUpdateFactory.ApplyNameplateChanges(new NameplateChangesProps(nameplateChanges));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, $"SetPlayerNameplateDetour");
|
||||
}
|
||||
}
|
||||
var namePlateGui = Pilz.Dalamud.NamePlate.INamePlateGui.Instance;
|
||||
```
|
||||
|
||||
## Contribute
|
||||
|
||||
Reference in New Issue
Block a user