6 Commits

60 changed files with 2634 additions and 644 deletions

4
.gitmodules vendored
View File

@@ -1,4 +0,0 @@
[submodule "Pilz.Dalamud"]
path = Pilz.Dalamud
url = https://github.com/Pilzinsel64/Pilz.Dalamud.git
branch = master

Submodule Pilz.Dalamud deleted from 8e7d49ca01

View File

@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Pilz.Dalamud.ActivityContexts
{
public class ActivityContext
{
public ActivityType ActivityType { get; init; }
public ZoneType ZoneType { get; init; }
public ActivityContext(ActivityType activityType, ZoneType zoneType)
{
ActivityType = activityType;
ZoneType = zoneType;
}
public bool IsInDuty
{
get => ZoneType != ZoneType.Overworld;
}
}
}

View File

@@ -0,0 +1,89 @@
using Dalamud.Logging;
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
{
public class ActivityContextManager : IDisposable
{
public delegate void ActivityContextChangedEventHandler(ActivityContextManager sender, ActivityContext activityContext);
public event ActivityContextChangedEventHandler ActivityContextChanged;
private readonly ExcelSheet<ContentFinderCondition> contentFinderConditionsSheet;
public ActivityContext CurrentActivityContext { get; protected set; }
public ActivityContextManager()
{
// Get condition sheet
contentFinderConditionsSheet = PluginServices.DataManager.GameData.GetExcelSheet<ContentFinderCondition>();
// Checks current territory type (if enabled/installed during a dutiy e.g.)
CheckCurrentTerritory();
// Enable event for automatic checks
PluginServices.ClientState.TerritoryChanged += ClientState_TerritoryChanged;
}
public void Dispose()
{
PluginServices.ClientState.TerritoryChanged -= ClientState_TerritoryChanged;
}
private void ClientState_TerritoryChanged(object? sender, ushort e)
{
CheckCurrentTerritory();
}
private void CheckCurrentTerritory()
{
var content = contentFinderConditionsSheet.FirstOrDefault(c => c.TerritoryType.Row == PluginServices.ClientState.TerritoryType);
ActivityType newActivityContext;
ZoneType newZoneType;
if (content == null)
{
// No content found, so we must be on the overworld
newActivityContext = ActivityType.None;
newZoneType = ZoneType.Overworld;
}
else
{
if (content.PvP)
{
newActivityContext = ActivityType.PvpDuty;
newZoneType = ZoneType.Pvp;
}
else
{
newActivityContext = ActivityType.PveDuty;
// Find correct member type
var memberType = content.ContentMemberType.Row;
if (content.RowId == 16 || content.RowId == 15)
memberType = 2; // Praetorium and Castrum Meridianum
else if (content.RowId == 735 || content.RowId == 778)
memberType = 127; // Bozja
// Check for ZoneType
newZoneType = memberType switch
{
2 => ZoneType.Doungen,
3 => ZoneType.Raid,
4 => ZoneType.AllianceRaid,
127 => ZoneType.Foray,
_ => ZoneType.Doungen,
};
}
}
CurrentActivityContext = new(newActivityContext, newZoneType);
ActivityContextChanged?.Invoke(this, CurrentActivityContext);
}
}
}

View File

@@ -0,0 +1,18 @@
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
{
[JsonConverter(typeof(StringEnumConverter))]
public enum ActivityType
{
None = 0x0,
PveDuty = 0x1,
PvpDuty = 0x2
}
}

View File

@@ -0,0 +1,22 @@
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
{
[Flags, JsonConverter(typeof(StringEnumConverter))]
public enum ZoneType
{
Overworld = 1,
Doungen = 2,
Raid = 4,
AllianceRaid = 8,
Foray = 16,
Pvp = 32,
Everywhere = int.MaxValue
}
}

View File

@@ -0,0 +1,41 @@
using Dalamud.Game.Text.SeStringHandling;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Pilz.Dalamud
{
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.
/// </summary>
/// <param name="seString"></param>
/// <param name="payload"></param>
public static void Remove(this SeString seString, Payload payload)
{
Remove(seString.Payloads, payload);
}
/// <summary>
/// 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="payload"></param>
public static void Remove(this List<Payload> payloads, Payload payload)
{
for (int i = 0; i < payloads.Count; i++)
{
if (ReferenceEquals(payloads[i], payload))
{
payloads.RemoveAt(i);
break;
}
}
}
}
}

View File

@@ -0,0 +1,153 @@
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
{
public static class GameInterfaceHelper
{
public static SeString ReadSeString(IntPtr ptr)
{
if (ptr == IntPtr.Zero)
{
return new SeString();
}
if (TryReadStringBytes(ptr, out var bytes) && bytes != null)
{
return SeString.Parse(bytes);
}
return new SeString();
}
public static bool TryReadSeString(IntPtr ptr, out SeString? seString)
{
seString = null;
if (ptr == IntPtr.Zero)
{
return false;
}
if (TryReadStringBytes(ptr, out var bytes) && bytes != null)
{
seString = SeString.Parse(bytes);
return true;
}
return false;
}
public static string? ReadString(IntPtr ptr)
{
if (ptr == IntPtr.Zero)
{
return null;
}
if (TryReadStringBytes(ptr, out var bytes) && bytes != null)
{
return Encoding.UTF8.GetString(bytes);
}
return null;
}
public static bool TryReadString(IntPtr ptr, out string? str)
{
str = null;
if (ptr == IntPtr.Zero)
{
return false;
}
if (TryReadStringBytes(ptr, out var bytes) && bytes != null)
{
str = Encoding.UTF8.GetString(bytes);
return true;
}
return false;
}
public static bool TryReadStringBytes(IntPtr ptr, out byte[]? bytes)
{
bytes = null;
if (ptr == IntPtr.Zero)
{
return false;
}
var size = 0;
while (Marshal.ReadByte(ptr, size) != 0)
{
size++;
}
bytes = new byte[size];
Marshal.Copy(ptr, bytes, 0, size);
return true;
}
public static IntPtr PluginAllocate(byte[] bytes)
{
IntPtr pointer = Marshal.AllocHGlobal(bytes.Length + 1);
Marshal.Copy(bytes, 0, pointer, bytes.Length);
Marshal.WriteByte(pointer, bytes.Length, 0);
return pointer;
}
public static IntPtr PluginAllocate(SeString seString)
{
return PluginAllocate(seString.Encode());
}
public static void PluginFree(IntPtr ptr)
{
Marshal.FreeHGlobal(ptr);
}
public static void PluginFree(ref IntPtr ptr)
{
PluginFree(ptr);
ptr = IntPtr.Zero;
}
public static byte[] NullTerminate(this byte[] bytes)
{
if (bytes.Length == 0 || bytes[bytes.Length - 1] != 0)
{
var newBytes = new byte[bytes.Length + 1];
Array.Copy(bytes, newBytes, bytes.Length);
newBytes[^1] = 0;
return newBytes;
}
return bytes;
}
public static unsafe IntPtr GameUIAllocate(ulong size)
{
return (IntPtr)IMemorySpace.GetUISpace()->Malloc(size, 0);
}
public static unsafe void GameFree(ref IntPtr ptr, ulong size)
{
if (ptr == IntPtr.Zero)
{
return;
}
IMemorySpace.Free((void*)ptr, size);
ptr = IntPtr.Zero;
}
}
}

View File

@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace Pilz.Dalamud.Icons
{
public class JobIconSet
{
private readonly int[] icons;
public float IconScale { get; init; }
public JobIconSet(int[] icons, float iconScale)
{
this.icons = icons;
IconScale = iconScale;
}
public int GetIcon(uint jobID)
{
return icons[jobID - 1];
}
}
}

View File

@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Pilz.Dalamud.Icons
{
public enum JobIconSetName
{
Gold,
Framed,
Glowing,
Blue,
Red,
Purple,
Black,
Yellow,
Orange,
Green,
Grey,
Role
}
}

View File

@@ -0,0 +1,128 @@
using Lumina.Excel.GeneratedSheets;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Pilz.Dalamud.Icons
{
public class JobIconSets
{
private readonly Dictionary<JobIconSetName, JobIconSet> iconSets = new();
public JobIconSets()
{
Add(JobIconSetName.Gold, new[]
{
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);
Add(JobIconSetName.Framed, new[]
{
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
});
Add(JobIconSetName.Glowing, new[]
{
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
});
Add(JobIconSetName.Grey, new[]
{
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);
Add(JobIconSetName.Black, new[]
{
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);
Add(JobIconSetName.Yellow, new[]
{
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);
Add(JobIconSetName.Orange, new[]
{
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);
Add(JobIconSetName.Red, new[]
{
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);
Add(JobIconSetName.Purple, 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);
Add(JobIconSetName.Blue, new[]
{
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);
Add(JobIconSetName.Green, new[]
{
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);
Add(JobIconSetName.Role, new[]
{
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
});
}
private void Add(JobIconSetName id, int[] icons, float scale = 1f)
{
iconSets[id] = new JobIconSet(icons, scale);
}
public int GetJobIcon(JobIconSetName set, uint jobId)
{
return iconSets[set].GetIcon(jobId);
}
public float GetJobIconSale(JobIconSetName set)
{
return iconSets[set].IconScale;
}
}
}

View File

@@ -0,0 +1,19 @@
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 bool IsTitleAboveName { get; set; }
public bool IsTitleVisible { get; set; }
public int IconID { get; set; }
}
}

View File

@@ -0,0 +1,42 @@
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 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;
}
}
}

View File

@@ -0,0 +1,18 @@
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();
}
}
}

View File

@@ -0,0 +1,13 @@
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; }
}
}

View File

@@ -0,0 +1,21 @@
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();
}
}
}

View File

@@ -0,0 +1,13 @@
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; }
}
}

View File

@@ -0,0 +1,43 @@
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;
}
}
}

View File

@@ -0,0 +1,57 @@
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);
}
}
}

View File

@@ -0,0 +1,128 @@
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);
}
}
}

View File

@@ -0,0 +1,31 @@
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,
}
}

View File

@@ -0,0 +1,203 @@
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("48 89 5C 24 ?? 48 89 6C 24 ?? 56 57 41 54 41 56 41 57 48 83 EC 40 44 0F B6 E2", DetourName = nameof(SetPlayerNameplateDetour))]
private readonly 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, 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, int iconId)
{
var result = IntPtr.Zero;
if (IsHookEnabled(hook_AddonNamePlate_SetPlayerNameplateDetour))
{
var eventArgs = new AddonNamePlate_SetPlayerNameEventArgs
{
PlayerNameplateObjectPtr = playerNameplateObjectPtr,
TitlePtr = titlePtr,
NamePtr = namePtr,
FreeCompanyPtr = freeCompanyPtr,
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,
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;
// 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)
};
// Get raw string content
var titleRaw = managedEventArgs.Title.Encode();
var nameRaw = managedEventArgs.Name.Encode();
var freeCompanyRaw = managedEventArgs.FreeCompany.Encode();
// Invoke Managed Event
AddonNamePlate_SetPlayerNameManaged.Invoke(managedEventArgs);
// Get new Title string ontent
var titleNewRaw = managedEventArgs.Title.Encode();
if (!titleRaw.SequenceEqual(titleNewRaw))
{
eventArgs.TitlePtr = GameInterfaceHelper.PluginAllocate(titleNewRaw);
freeTitle = true;
}
// Get new Name string ontent
var nameNewRaw = managedEventArgs.Name.Encode();
if (!nameRaw.SequenceEqual(nameNewRaw))
{
eventArgs.NamePtr = GameInterfaceHelper.PluginAllocate(nameNewRaw);
freeName = true;
}
// Get new Free Company string ontent
var freeCompanyNewRaw = managedEventArgs.FreeCompany.Encode();
if (!freeCompanyRaw.SequenceEqual(freeCompanyNewRaw))
{
eventArgs.FreeCompanyPtr = GameInterfaceHelper.PluginAllocate(freeCompanyNewRaw);
freeFreeCompany = 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);
}
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;
}
}
}

View File

@@ -0,0 +1,83 @@
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;
}
}
}

View File

@@ -0,0 +1,59 @@
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);
}
}
}

View File

@@ -0,0 +1,25 @@
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;
}
}
}

View File

@@ -0,0 +1,15 @@
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
}
}

View File

@@ -0,0 +1,52 @@
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;
}
}
}

View File

@@ -0,0 +1,67 @@
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);
}
}
}

View File

@@ -0,0 +1,15 @@
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
}
}

View File

@@ -0,0 +1,110 @@
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,
});
}
}
}

View File

@@ -0,0 +1,66 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>annotations</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>latest</LangVersion>
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<Platforms>x64</Platforms>
</PropertyGroup>
<PropertyGroup>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<PropertyGroup>
<DalamudLibPath>$(appdata)\XIVLauncher\addon\Hooks\dev\</DalamudLibPath>
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
<Copyright>Pilzinsel64</Copyright>
<PackageProjectUrl>https://github.com/Pilzinsel64/Pilz.Dalamud</PackageProjectUrl>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/Pilzinsel64/Pilz.Dalamud</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<Version>0.3.1</Version>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="FFXIVClientStructs">
<HintPath>$(DalamudLibPath)FFXIVClientStructs.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>$(DalamudLibPath)Newtonsoft.Json.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Dalamud">
<HintPath>$(DalamudLibPath)Dalamud.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="ImGui.NET">
<HintPath>$(DalamudLibPath)ImGui.NET.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="ImGuiScene">
<HintPath>$(DalamudLibPath)ImGuiScene.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Lumina">
<HintPath>$(DalamudLibPath)Lumina.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Lumina.Excel">
<HintPath>$(DalamudLibPath)Lumina.Excel.dll</HintPath>
<Private>false</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<None Include="..\README.md" Pack="true" PackagePath="\"/>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,28 @@
using Dalamud.Data;
using Dalamud.Game.ClientState;
using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.Gui;
using Dalamud.IoC;
using Dalamud.Plugin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Pilz.Dalamud
{
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;
public static void Initialize(DalamudPluginInterface dalamudPluginInterface)
{
dalamudPluginInterface.Create<PluginServices>();
}
}
}

View File

@@ -0,0 +1,41 @@
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.Tools
{
public static class StatusIconFontConverter
{
public static StatusIcons? GetStatusIconFromBitmapFontIcon(BitmapFontIcon fontIcon)
{
return fontIcon switch
{
BitmapFontIcon.NewAdventurer => StatusIcons.NewAdventurer,
BitmapFontIcon.Mentor => StatusIcons.Mentor,
BitmapFontIcon.MentorPvE => StatusIcons.MentorPvE,
BitmapFontIcon.MentorCrafting => StatusIcons.MentorCrafting,
BitmapFontIcon.MentorPvP => StatusIcons.MentorPvP,
BitmapFontIcon.Returner => StatusIcons.Returner,
_ => null
};
}
public static BitmapFontIcon? GetBitmapFontIconFromStatusIcon(StatusIcons icon)
{
return icon switch
{
StatusIcons.NewAdventurer => BitmapFontIcon.NewAdventurer,
StatusIcons.Mentor => BitmapFontIcon.Mentor,
StatusIcons.MentorPvE => BitmapFontIcon.MentorPvE,
StatusIcons.MentorCrafting => BitmapFontIcon.MentorCrafting,
StatusIcons.MentorPvP => BitmapFontIcon.MentorPvP,
StatusIcons.Returner => BitmapFontIcon.Returner,
_ => null
};
}
}
}

View File

@@ -0,0 +1,24 @@
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
{
public class StringChange
{
/// <summary>
/// The payloads to use for inserting/replacing.
/// </summary>
public List<Payload> Payloads { get; init; } = new();
/// <summary>
/// Defines if only one anchor payload should be used, if using anchor payloads.
/// With this true the single anchor payload will be used in StringUpdateFactory instead of the anchor payload list.
/// Not needed to be true for the most cases.
/// </summary>
public bool ForceUsingSingleAnchorPayload { get; set; } = false;
}
}

View File

@@ -0,0 +1,40 @@
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
{
public class StringChanges
{
private readonly Dictionary<StringPosition, StringChange> changes = new();
public StringChanges()
{
changes.Add(StringPosition.Before, new StringChange());
changes.Add(StringPosition.After, new StringChange());
changes.Add(StringPosition.Replace, new StringChange());
}
/// <summary>
/// Gets a change of the position of your choice where you can add your payloads.
/// </summary>
/// <param name="position">The position of your choice.</param>
/// <returns></returns>
public StringChange GetChange(StringPosition position)
{
return changes[position];
}
/// <summary>
/// Checks if there is any string change listed.
/// </summary>
/// <returns></returns>
public bool Any()
{
return changes.Sum(n => n.Value.Payloads.Count) != 0;
}
}
}

View File

@@ -0,0 +1,31 @@
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;
namespace Pilz.Dalamud.Tools.Strings
{
public class StringChangesProps
{
/// <summary>
/// The string where the changes should be applied.
/// </summary>
public SeString Destination { get; set; }
/// <summary>
/// The changes that should be applied to the destination.
/// </summary>
public StringChanges StringChanges { get; set; } = new();
/// <summary>
/// Payloads to use as anchor where the changes should be applied to.
/// </summary>
public List<Payload> AnchorPayloads { get; set; } = new();
/// <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; }
}
}

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Pilz.Dalamud.Tools.Strings
{
public enum StringPosition
{
Before,
After,
Replace
}
}

View File

@@ -0,0 +1,132 @@
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
{
public static class StringUpdateFactory
{
public static void ApplyStringChanges(StringChangesProps props)
{
if (props.StringChanges != null && props.StringChanges.Any())
{
var seString = props.Destination;
List<StringPosition> stringPositionsOrdered = GetOrderedStringPositions(props);
foreach (var stringPosition in stringPositionsOrdered)
{
var stringChange = props.StringChanges.GetChange(stringPosition);
if (stringChange != null && stringChange.Payloads.Any())
{
AddSpacesBetweenTextPayloads(stringChange.Payloads, stringPosition);
if (stringPosition == StringPosition.Before)
{
Payload anchorFirst = stringChange.ForceUsingSingleAnchorPayload ? props.AnchorPayload : props.AnchorPayloads?.FirstOrDefault();
if (anchorFirst != null)
{
var anchorPayloadIndex = seString.Payloads.IndexOf(anchorFirst);
seString.Payloads.InsertRange(anchorPayloadIndex, stringChange.Payloads);
}
else
seString.Payloads.InsertRange(0, stringChange.Payloads);
}
else if (stringPosition == StringPosition.After)
{
Payload anchorLast = stringChange.ForceUsingSingleAnchorPayload ? props.AnchorPayload : props.AnchorPayloads?.LastOrDefault();
if (anchorLast != null)
{
var anchorPayloadIndex = seString.Payloads.IndexOf(anchorLast);
seString.Payloads.InsertRange(anchorPayloadIndex + 1, stringChange.Payloads);
}
else
seString.Payloads.AddRange(stringChange.Payloads);
}
else if (stringPosition == StringPosition.Replace)
{
Payload anchorReplace = props.AnchorPayload;
if (anchorReplace != null)
{
var anchorPayloadIndex = seString.Payloads.IndexOf(anchorReplace);
seString.Payloads.InsertRange(anchorPayloadIndex, stringChange.Payloads);
seString.Remove(anchorReplace);
}
else
{
seString.Payloads.Clear();
seString.Payloads.AddRange(stringChange.Payloads);
}
}
}
}
}
}
private static void AddSpacesBetweenTextPayloads(List<Payload> payloads, StringPosition tagPosition)
{
if (payloads != null && payloads.Any())
{
var indicesToInsertSpacesAt = new List<int>();
var lastTextPayloadIndex = -1;
static TextPayload getNewTextPayload() => new(" ");
foreach (var payload in payloads.Reverse<Payload>())
{
if (payload is IconPayload iconPayload)
lastTextPayloadIndex = -1;
else if (payload is TextPayload textPayload)
{
if (lastTextPayloadIndex != -1)
indicesToInsertSpacesAt.Add(payloads.IndexOf(textPayload) + 1);
lastTextPayloadIndex = payloads.IndexOf(textPayload);
}
}
foreach (var indexToInsertSpaceAt in indicesToInsertSpacesAt)
payloads.Insert(indexToInsertSpaceAt, getNewTextPayload());
// Decide whether to add a space to the end
if (tagPosition == StringPosition.Before)
{
var significantPayloads = payloads.Where(payload => payload is TextPayload || payload is IconPayload);
if (significantPayloads.LastOrDefault() is TextPayload)
payloads.Add(getNewTextPayload());
}
// Decide whether to add a space to the beginning
else if (tagPosition == StringPosition.After)
{
var significantPayloads = payloads.Where(payload => payload is TextPayload || payload is IconPayload);
if (significantPayloads.FirstOrDefault() is TextPayload)
payloads.Insert(0, getNewTextPayload());
}
}
}
private static List<StringPosition> GetOrderedStringPositions(StringChangesProps props)
{
var tagPositionsOrdered = new List<StringPosition>();
// If there's no anchor payload, do replaces first so that befores and afters are based on the replaced data
if (props.AnchorPayloads == null || !props.AnchorPayloads.Any())
tagPositionsOrdered.Add(StringPosition.Replace);
tagPositionsOrdered.Add(StringPosition.Before);
tagPositionsOrdered.Add(StringPosition.After);
// If there is an anchor payload, do replaces last so that we still know which payload needs to be removed
if (props.AnchorPayloads != null && props.AnchorPayloads.Any())
tagPositionsOrdered.Add(StringPosition.Replace);
return tagPositionsOrdered;
}
}
}

39
Pilz.Dalamud/XivApi.cs Normal file
View File

@@ -0,0 +1,39 @@
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);
}
}
}

View File

@@ -0,0 +1,6 @@
{
"version": 1,
"dependencies": {
"net7.0-windows7.0": {}
}
}

View File

@@ -5,7 +5,7 @@ VisualStudioVersion = 17.3.32929.385
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlayerTags", "PlayerTags\PlayerTags.csproj", "{13C812E9-0D42-4B95-8646-40EEBF30636F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pilz.Dalamud", "Pilz.Dalamud\Pilz.Dalamud\Pilz.Dalamud.csproj", "{D0362D71-E77F-4739-80BE-CD4454188B8F}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pilz.Dalamud", "Pilz.Dalamud\Pilz.Dalamud.csproj", "{D0362D71-E77F-4739-80BE-CD4454188B8F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution

View File

@@ -0,0 +1,18 @@
using PlayerTags.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PlayerTags.Configuration
{
public class GeneralConfiguration : ZoneConfigurationBase
{
public NameplateFreeCompanyVisibility NameplateFreeCompanyVisibility = NameplateFreeCompanyVisibility.Default;
public NameplateTitleVisibility NameplateTitleVisibility = NameplateTitleVisibility.WhenHasTags;
public NameplateTitlePosition NameplateTitlePosition = NameplateTitlePosition.AlwaysAboveName;
public DeadPlayerHandling NameplateDeadPlayerHandling = DeadPlayerHandling.Include;
public bool IsApplyTagsToAllChatMessagesEnabled = true;
}
}

View File

@@ -15,7 +15,7 @@ using System.Runtime.CompilerServices;
namespace PlayerTags.Configuration
{
[Serializable]
public class PluginConfiguration : IPluginConfiguration
public partial class PluginConfiguration : IPluginConfiguration
{
private const int DEFAULT_CONFIG_VERSION = 1;
@@ -23,17 +23,9 @@ namespace PlayerTags.Configuration
public int RootVersion { get; private set; } = DEFAULT_CONFIG_VERSION;
public int Version { get; set; } = DEFAULT_CONFIG_VERSION;
public bool IsVisible = false;
public bool EnabledGlobal = true;
[JsonProperty("GeneralOptionsV2")]
public Dictionary<ActivityType, GeneralOptionsClass> GeneralOptions = new()
{
{ ActivityType.None, new GeneralOptionsClass() },
{ ActivityType.PveDuty, new GeneralOptionsClass() },
{ ActivityType.PvpDuty, new GeneralOptionsClass() }
};
public DefaultPluginDataTemplate DefaultPluginDataTemplate = DefaultPluginDataTemplate.None;
public ZoneConfiguration<GeneralConfiguration> GeneralConfigs = new();
public ZoneConfiguration<TagsConfiguration> TagsConfigs = new();
public StatusIconPriorizerSettings StatusIconPriorizerSettings = new(true);
public bool MoveStatusIconToNameplateTextIfPossible = true;
public bool IsPlayerNameRandomlyGenerated = false;
@@ -46,88 +38,74 @@ namespace PlayerTags.Configuration
public bool IsPlayersTabAllianceVisible = true;
public bool IsPlayersTabEnemiesVisible = true;
public bool IsPlayersTabOthersVisible = false;
public bool IsGeneralOptionsAllTheSameEnabled = true;
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
public Dictionary<string, InheritableData> AllTagsChanges = new Dictionary<string, InheritableData>();
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
public Dictionary<string, InheritableData> AllRoleTagsChanges = new Dictionary<string, InheritableData>();
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
public Dictionary<Role, Dictionary<string, InheritableData>> RoleTagsChanges = new Dictionary<Role, Dictionary<string, InheritableData>>();
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
public Dictionary<DpsRole, Dictionary<string, InheritableData>> DpsRoleTagsChanges = new Dictionary<DpsRole, Dictionary<string, InheritableData>>();
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
public Dictionary<RangedDpsRole, Dictionary<string, InheritableData>> RangedDpsRoleTagsChanges = new Dictionary<RangedDpsRole, Dictionary<string, InheritableData>>();
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
public Dictionary<LandHandRole, Dictionary<string, InheritableData>> LandHandRoleTagsChanges = new Dictionary<LandHandRole, Dictionary<string, InheritableData>>();
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
public Dictionary<string, Dictionary<string, InheritableData>> JobTagsChanges = new Dictionary<string, Dictionary<string, InheritableData>>();
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
public Dictionary<string, InheritableData> AllCustomTagsChanges = new Dictionary<string, InheritableData>();
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
public List<Dictionary<string, InheritableData>> CustomTagsChanges = new List<Dictionary<string, InheritableData>>();
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
public List<Identity> Identities = new List<Identity>();
public bool IsSimpleUIEnabled = true;
#region Obsulate Properties
[Obsolete]
[JsonProperty("GeneralOptions")]
private Dictionary<Data.ActivityContext, GeneralOptionsClass> GeneralOptionsV1
[JsonProperty("GeneralOptionsV2"), Obsolete]
private Dictionary<ActivityType, GeneralConfiguration> GeneralOptionsV2
{
set
{
GeneralOptions.Clear();
foreach (var kvp in value)
GeneralOptions.Add((ActivityType)kvp.Key, kvp.Value);
void copyOverSettings(ActivityType srcType, ZoneType destType)
{
var src = value[srcType];
var dest = GeneralConfigs.GetConfig(destType);
dest.IsApplyTagsToAllChatMessagesEnabled = src.IsApplyTagsToAllChatMessagesEnabled;
dest.NameplateDeadPlayerHandling = src.NameplateDeadPlayerHandling;
dest.NameplateFreeCompanyVisibility = src.NameplateFreeCompanyVisibility;
dest.NameplateTitlePosition = src.NameplateTitlePosition;
dest.NameplateTitleVisibility = src.NameplateTitleVisibility;
}
copyOverSettings(ActivityType.None, ZoneType.Everywhere);
copyOverSettings(ActivityType.None, ZoneType.Overworld);
copyOverSettings(ActivityType.PvpDuty, ZoneType.Pvp);
copyOverSettings(ActivityType.PveDuty, ZoneType.Doungen);
copyOverSettings(ActivityType.PveDuty, ZoneType.Raid);
copyOverSettings(ActivityType.PveDuty, ZoneType.AllianceRaid);
copyOverSettings(ActivityType.PveDuty, ZoneType.Foray);
}
}
[JsonProperty]
[Obsolete]
public bool IsApplyToEverywhereEnabled
{
set
{
GeneralConfigs.IsEverywhere = value;
TagsConfigs.IsEverywhere = value;
}
}
[JsonProperty]
[Obsolete]
private bool IsGeneralOptionsAllTheSameEnabled
{
set => IsApplyToEverywhereEnabled = value;
}
[JsonProperty("NameplateFreeCompanyVisibility"), Obsolete]
private NameplateFreeCompanyVisibility NameplateFreeCompanyVisibilityV1
{
set
{
foreach (var key in GeneralOptions.Keys)
GeneralOptions[key].NameplateFreeCompanyVisibility = value;
}
set => GeneralConfigs.GetConfig(ZoneType.Everywhere).NameplateFreeCompanyVisibility = value;
}
[JsonProperty("NameplateTitleVisibility"), Obsolete]
public NameplateTitleVisibility NameplateTitleVisibilityV1
{
set
{
foreach (var key in GeneralOptions.Keys)
GeneralOptions[key].NameplateTitleVisibility = value;
}
set => GeneralConfigs.GetConfig(ZoneType.Everywhere).NameplateTitleVisibility = value;
}
[JsonProperty("NameplateTitlePosition"), Obsolete]
public NameplateTitlePosition NameplateTitlePositionV1
{
set
{
foreach (var key in GeneralOptions.Keys)
GeneralOptions[key].NameplateTitlePosition = value;
}
set => GeneralConfigs.GetConfig(ZoneType.Everywhere).NameplateTitlePosition = value;
}
[JsonProperty("IsApplyTagsToAllChatMessagesEnabled"), Obsolete]
private bool IsApplyTagsToAllChatMessagesEnabledV1
{
set
{
foreach (var key in GeneralOptions.Keys)
GeneralOptions[key].IsApplyTagsToAllChatMessagesEnabled = value;
}
set => GeneralConfigs.GetConfig(ZoneType.Everywhere).IsApplyTagsToAllChatMessagesEnabled = value;
}
#endregion
@@ -136,105 +114,15 @@ namespace PlayerTags.Configuration
public void Save(PluginData pluginData)
{
AllTagsChanges = pluginData.AllTags.GetChanges(pluginData.Default.AllTags.GetChanges());
AllRoleTagsChanges = pluginData.AllRoleTags.GetChanges(pluginData.Default.AllRoleTags.GetChanges());
RoleTagsChanges = new Dictionary<Role, Dictionary<string, InheritableData>>();
foreach ((var role, var roleTag) in pluginData.RoleTags)
{
Dictionary<string, InheritableData>? defaultChanges = new Dictionary<string, InheritableData>();
if (pluginData.Default.RoleTags.TryGetValue(role, out var defaultTag))
{
defaultChanges = defaultTag.GetChanges();
}
var changes = roleTag.GetChanges(defaultChanges);
if (changes.Any())
{
RoleTagsChanges[role] = changes;
}
}
DpsRoleTagsChanges = new Dictionary<DpsRole, Dictionary<string, InheritableData>>();
foreach ((var dpsRole, var dpsRoleTag) in pluginData.DpsRoleTags)
{
Dictionary<string, InheritableData>? defaultChanges = new Dictionary<string, InheritableData>();
if (pluginData.Default.DpsRoleTags.TryGetValue(dpsRole, out var defaultTag))
{
defaultChanges = defaultTag.GetChanges();
}
var changes = dpsRoleTag.GetChanges(defaultChanges);
if (changes.Any())
{
DpsRoleTagsChanges[dpsRole] = changes;
}
}
RangedDpsRoleTagsChanges = new Dictionary<RangedDpsRole, Dictionary<string, InheritableData>>();
foreach ((var rangedDpsRole, var rangedDpsRoleTag) in pluginData.RangedDpsRoleTags)
{
Dictionary<string, InheritableData>? defaultChanges = new Dictionary<string, InheritableData>();
if (pluginData.Default.RangedDpsRoleTags.TryGetValue(rangedDpsRole, out var defaultTag))
{
defaultChanges = defaultTag.GetChanges();
}
var changes = rangedDpsRoleTag.GetChanges(defaultChanges);
if (changes.Any())
{
RangedDpsRoleTagsChanges[rangedDpsRole] = changes;
}
}
LandHandRoleTagsChanges = new Dictionary<LandHandRole, Dictionary<string, InheritableData>>();
foreach ((var landHandRole, var landHandRoleTag) in pluginData.LandHandRoleTags)
{
Dictionary<string, InheritableData>? defaultChanges = new Dictionary<string, InheritableData>();
if (pluginData.Default.LandHandRoleTags.TryGetValue(landHandRole, out var defaultTag))
{
defaultChanges = defaultTag.GetChanges();
}
var changes = landHandRoleTag.GetChanges(defaultChanges);
if (changes.Any())
{
LandHandRoleTagsChanges[landHandRole] = changes;
}
}
JobTagsChanges = new Dictionary<string, Dictionary<string, InheritableData>>();
foreach ((var jobAbbreviation, var jobTag) in pluginData.JobTags)
{
Dictionary<string, InheritableData>? defaultChanges = new Dictionary<string, InheritableData>();
if (pluginData.Default.JobTags.TryGetValue(jobAbbreviation, out var defaultTag))
{
defaultChanges = defaultTag.GetChanges();
}
var changes = jobTag.GetChanges(defaultChanges);
if (changes.Any())
{
JobTagsChanges[jobAbbreviation] = changes;
}
}
AllCustomTagsChanges = pluginData.AllCustomTags.GetChanges(pluginData.Default.AllCustomTags.GetChanges());
CustomTagsChanges = new List<Dictionary<string, InheritableData>>();
foreach (var customTag in pluginData.CustomTags)
{
CustomTagsChanges.Add(customTag.GetChanges());
}
Identities = pluginData.Identities;
foreach (var tagConfig in TagsConfigs)
tagConfig.Value.ApplyTagsData(pluginData.GetTagsData(tagConfig.Key));
SavePluginConfig();
Saved?.Invoke();
}
private void SavePluginConfig()
public void SavePluginConfig()
{
Version = DEFAULT_CONFIG_VERSION;
var configFilePath = GetConfigFilePath();
@@ -242,13 +130,6 @@ namespace PlayerTags.Configuration
File.WriteAllText(configFilePath, configFileContent);
}
private static void BackupPluginConfig()
{
var configFilePath = GetConfigFilePath();
var configFilePathOld = Path.ChangeExtension(configFilePath, ".old" + Path.GetExtension(configFilePath));
File.Copy(configFilePath, configFilePathOld, true);
}
public static PluginConfiguration LoadPluginConfig()
{
var configFilePath = GetConfigFilePath();
@@ -266,15 +147,6 @@ namespace PlayerTags.Configuration
config = PluginServices.DalamudPluginInterface.GetPluginConfig();
}
if (config is PluginConfiguration pluginConfig)
{
if (PluginConfigFix(pluginConfig))
{
BackupPluginConfig();
pluginConfig.SavePluginConfig();
}
}
return config as PluginConfiguration;
}
@@ -295,58 +167,5 @@ namespace PlayerTags.Configuration
return jsonSettings;
}
private static bool PluginConfigFix(PluginConfiguration config)
{
bool hasFixes = false;
// Patch 6.4 - Disable all Job & Role specific colors & prefix
// Not used yet, but keeping it there, just for the case,
//if (config.Version <= 1)
//{
// void fixTags(Dictionary<string, InheritableData> dic)
// {
// foreach (var change in config.AllRoleTagsChanges.ToArray())
// {
// var key = change.Key;
// if (key == nameof(Tag.IsTextVisibleInChat) ||
// key == nameof(Tag.IsTextVisibleInNameplates) ||
// key == nameof(Tag.IsRoleIconVisibleInChat) ||
// key == nameof(Tag.IsRoleIconVisibleInNameplates) ||
// key == nameof(Tag.IsTextColorAppliedToNameplateName) ||
// key == nameof(Tag.IsTextColorAppliedToChatName) ||
// key == nameof(Tag.IsJobIconVisibleInNameplates))
// {
// var data = change.Value;
// data.Behavior = InheritableBehavior.Disabled;
// }
// }
// }
// // "All Roles" tag changes
// fixTags(config.AllRoleTagsChanges);
// // Role tags changes
// foreach (var kvp in config.RoleTagsChanges)
// fixTags(kvp.Value);
// // Job tags changes
// foreach (var kvp in config.JobTagsChanges)
// fixTags(kvp.Value);
// hasFixes = true;
//}
return hasFixes;
}
}
public class GeneralOptionsClass
{
public NameplateFreeCompanyVisibility NameplateFreeCompanyVisibility = NameplateFreeCompanyVisibility.Default;
public NameplateTitleVisibility NameplateTitleVisibility = NameplateTitleVisibility.WhenHasTags;
public NameplateTitlePosition NameplateTitlePosition = NameplateTitlePosition.AlwaysAboveName;
public DeadPlayerHandling NameplateDeadPlayerHandling = DeadPlayerHandling.Include;
public bool IsApplyTagsToAllChatMessagesEnabled = true;
}
}

View File

@@ -0,0 +1,142 @@
using Newtonsoft.Json;
using PlayerTags.Data;
using PlayerTags.Inheritables;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PlayerTags.Configuration
{
public class TagsConfiguration : ZoneConfigurationBase
{
public DefaultPluginDataTemplate DefaultPluginDataTemplate = DefaultPluginDataTemplate.Simple;
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
public Dictionary<string, InheritableData> AllTagsChanges = new Dictionary<string, InheritableData>();
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
public Dictionary<string, InheritableData> AllRoleTagsChanges = new Dictionary<string, InheritableData>();
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
public Dictionary<Role, Dictionary<string, InheritableData>> RoleTagsChanges = new Dictionary<Role, Dictionary<string, InheritableData>>();
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
public Dictionary<DpsRole, Dictionary<string, InheritableData>> DpsRoleTagsChanges = new Dictionary<DpsRole, Dictionary<string, InheritableData>>();
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
public Dictionary<RangedDpsRole, Dictionary<string, InheritableData>> RangedDpsRoleTagsChanges = new Dictionary<RangedDpsRole, Dictionary<string, InheritableData>>();
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
public Dictionary<LandHandRole, Dictionary<string, InheritableData>> LandHandRoleTagsChanges = new Dictionary<LandHandRole, Dictionary<string, InheritableData>>();
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
public Dictionary<string, Dictionary<string, InheritableData>> JobTagsChanges = new Dictionary<string, Dictionary<string, InheritableData>>();
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
public Dictionary<string, InheritableData> AllCustomTagsChanges = new Dictionary<string, InheritableData>();
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
public List<Dictionary<string, InheritableData>> CustomTagsChanges = new List<Dictionary<string, InheritableData>>();
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
public List<Identity> Identities = new List<Identity>();
public void ApplyTagsData(PluginTagsData tagsData)
{
AllTagsChanges = tagsData.AllTags.GetChanges(tagsData.Default.AllTags.GetChanges());
AllRoleTagsChanges = tagsData.AllRoleTags.GetChanges(tagsData.Default.AllRoleTags.GetChanges());
RoleTagsChanges = new Dictionary<Role, Dictionary<string, InheritableData>>();
foreach ((var role, var roleTag) in tagsData.RoleTags)
{
Dictionary<string, InheritableData>? defaultChanges = new Dictionary<string, InheritableData>();
if (tagsData.Default.RoleTags.TryGetValue(role, out var defaultTag))
{
defaultChanges = defaultTag.GetChanges();
}
var changes = roleTag.GetChanges(defaultChanges);
if (changes.Any())
{
RoleTagsChanges[role] = changes;
}
}
DpsRoleTagsChanges = new Dictionary<DpsRole, Dictionary<string, InheritableData>>();
foreach ((var dpsRole, var dpsRoleTag) in tagsData.DpsRoleTags)
{
Dictionary<string, InheritableData>? defaultChanges = new Dictionary<string, InheritableData>();
if (tagsData.Default.DpsRoleTags.TryGetValue(dpsRole, out var defaultTag))
{
defaultChanges = defaultTag.GetChanges();
}
var changes = dpsRoleTag.GetChanges(defaultChanges);
if (changes.Any())
{
DpsRoleTagsChanges[dpsRole] = changes;
}
}
RangedDpsRoleTagsChanges = new Dictionary<RangedDpsRole, Dictionary<string, InheritableData>>();
foreach ((var rangedDpsRole, var rangedDpsRoleTag) in tagsData.RangedDpsRoleTags)
{
Dictionary<string, InheritableData>? defaultChanges = new Dictionary<string, InheritableData>();
if (tagsData.Default.RangedDpsRoleTags.TryGetValue(rangedDpsRole, out var defaultTag))
{
defaultChanges = defaultTag.GetChanges();
}
var changes = rangedDpsRoleTag.GetChanges(defaultChanges);
if (changes.Any())
{
RangedDpsRoleTagsChanges[rangedDpsRole] = changes;
}
}
LandHandRoleTagsChanges = new Dictionary<LandHandRole, Dictionary<string, InheritableData>>();
foreach ((var landHandRole, var landHandRoleTag) in tagsData.LandHandRoleTags)
{
Dictionary<string, InheritableData>? defaultChanges = new Dictionary<string, InheritableData>();
if (tagsData.Default.LandHandRoleTags.TryGetValue(landHandRole, out var defaultTag))
{
defaultChanges = defaultTag.GetChanges();
}
var changes = landHandRoleTag.GetChanges(defaultChanges);
if (changes.Any())
{
LandHandRoleTagsChanges[landHandRole] = changes;
}
}
JobTagsChanges = new Dictionary<string, Dictionary<string, InheritableData>>();
foreach ((var jobAbbreviation, var jobTag) in tagsData.JobTags)
{
Dictionary<string, InheritableData>? defaultChanges = new Dictionary<string, InheritableData>();
if (tagsData.Default.JobTags.TryGetValue(jobAbbreviation, out var defaultTag))
{
defaultChanges = defaultTag.GetChanges();
}
var changes = jobTag.GetChanges(defaultChanges);
if (changes.Any())
{
JobTagsChanges[jobAbbreviation] = changes;
}
}
AllCustomTagsChanges = tagsData.AllCustomTags.GetChanges(tagsData.Default.AllCustomTags.GetChanges());
CustomTagsChanges = new List<Dictionary<string, InheritableData>>();
foreach (var customTag in tagsData.CustomTags)
{
CustomTagsChanges.Add(customTag.GetChanges());
}
Identities = tagsData.Identities;
}
}
}

View File

@@ -0,0 +1,28 @@
using Newtonsoft.Json;
using Pilz.Dalamud.ActivityContexts;
using PlayerTags.Data;
using PlayerTags.Inheritables;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PlayerTags.Configuration
{
public class ZoneConfiguration<TConfig> : Dictionary<ZoneType, TConfig> where TConfig : ZoneConfigurationBase, new()
{
public bool IsEverywhere { get; set; } = true;
public TConfig GetConfig(ZoneType zoneType)
{
if (IsEverywhere)
zoneType = ZoneType.Everywhere;
if (!ContainsKey(zoneType))
Add(zoneType, new TConfig());
return this[zoneType];
}
}
}

View File

@@ -0,0 +1,13 @@
using Lumina.Excel.GeneratedSheets;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PlayerTags.Configuration
{
public abstract class ZoneConfigurationBase
{
}
}

View File

@@ -1,15 +0,0 @@
using Newtonsoft.Json;
using System;
namespace PlayerTags.Data
{
[Obsolete]
[Flags]
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
public enum ActivityContext
{
None = 0x0,
PveDuty = 0x1,
PvpDuty = 0x2,
}
}

View File

@@ -50,6 +50,7 @@ namespace PlayerTags.Data
private void Clear()
{
RoleTags = new Dictionary<Role, Tag>();
DpsRoleTags = new Dictionary<DpsRole, Tag>();
RangedDpsRoleTags = new Dictionary<RangedDpsRole, Tag>();

View File

@@ -3,6 +3,7 @@ using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Game.ClientState.Party;
using Dalamud.Game.Text.SeStringHandling.Payloads;
using Dalamud.Logging;
using Pilz.Dalamud.ActivityContexts;
using PlayerTags.Configuration;
using PlayerTags.PluginStrings;
using System;
@@ -13,19 +14,8 @@ namespace PlayerTags.Data
{
public class PluginData
{
public DefaultPluginData Default;
public Tag AllTags;
public Tag AllRoleTags;
public Dictionary<Role, Tag> RoleTags;
public Dictionary<DpsRole, Tag> DpsRoleTags;
public Dictionary<RangedDpsRole, Tag> RangedDpsRoleTags;
public Dictionary<LandHandRole, Tag> LandHandRoleTags;
public Dictionary<string, Tag> JobTags;
public Tag AllCustomTags;
public List<Tag> CustomTags;
public List<Identity> Identities;
private PluginConfiguration pluginConfiguration;
public Dictionary<ZoneType, PluginTagsData> TagsData = new();
private readonly PluginConfiguration pluginConfiguration;
public PluginData(PluginConfiguration pluginConfiguration)
{
@@ -33,275 +23,38 @@ namespace PlayerTags.Data
ReloadDefault();
}
private void EnsureDataExists(ref ZoneType zoneType)
{
if (pluginConfiguration.TagsConfigs.IsEverywhere)
zoneType = ZoneType.Everywhere;
if (!TagsData.ContainsKey(zoneType))
TagsData.Add(zoneType, new PluginTagsData(pluginConfiguration, pluginConfiguration.TagsConfigs.GetConfig(zoneType)));
}
public PluginTagsData GetTagsData(ZoneType zoneType)
{
EnsureDataExists(ref zoneType);
return TagsData[zoneType];
}
public void ReloadDefault(ZoneType zoneType)
{
EnsureDataExists(ref zoneType);
if (TagsData[zoneType].ReloadDefault())
pluginConfiguration.Save(this);
}
public void ReloadDefault()
{
Default = new DefaultPluginData(pluginConfiguration.DefaultPluginDataTemplate);
var needToSave = false;
foreach (var tagsData in TagsData.Values)
needToSave |= tagsData.ReloadDefault();
// Set the default changes and saved changes
AllTags = new Tag(new LocalizedPluginString(nameof(AllTags)), Default.AllTags);
AllTags.SetChanges(pluginConfiguration.AllTagsChanges);
AllRoleTags = new Tag(new LocalizedPluginString(nameof(AllRoleTags)), Default.AllRoleTags);
AllRoleTags.SetChanges(pluginConfiguration.AllRoleTagsChanges);
RoleTags = new Dictionary<Role, Tag>();
foreach (var role in Enum.GetValues<Role>())
{
if (Default.RoleTags.TryGetValue(role, out var defaultTag))
{
RoleTags[role] = new Tag(new LocalizedPluginString(Localizer.GetName(role)), defaultTag);
if (pluginConfiguration.RoleTagsChanges.TryGetValue(role, out var savedChanges))
{
RoleTags[role].SetChanges(savedChanges);
}
}
}
DpsRoleTags = new Dictionary<DpsRole, Tag>();
foreach (var dpsRole in Enum.GetValues<DpsRole>())
{
if (Default.DpsRoleTags.TryGetValue(dpsRole, out var defaultTag))
{
DpsRoleTags[dpsRole] = new Tag(new LocalizedPluginString(Localizer.GetName(dpsRole)), defaultTag);
if (pluginConfiguration.DpsRoleTagsChanges.TryGetValue(dpsRole, out var savedChanges))
{
DpsRoleTags[dpsRole].SetChanges(savedChanges);
}
}
}
RangedDpsRoleTags = new Dictionary<RangedDpsRole, Tag>();
foreach (var rangedDpsRole in Enum.GetValues<RangedDpsRole>())
{
if (Default.RangedDpsRoleTags.TryGetValue(rangedDpsRole, out var defaultTag))
{
RangedDpsRoleTags[rangedDpsRole] = new Tag(new LocalizedPluginString(Localizer.GetName(rangedDpsRole)), defaultTag);
if (pluginConfiguration.RangedDpsRoleTagsChanges.TryGetValue(rangedDpsRole, out var savedChanges))
{
RangedDpsRoleTags[rangedDpsRole].SetChanges(savedChanges);
}
}
}
LandHandRoleTags = new Dictionary<LandHandRole, Tag>();
foreach (var landHandRole in Enum.GetValues<LandHandRole>())
{
if (Default.LandHandRoleTags.TryGetValue(landHandRole, out var defaultChanges))
{
LandHandRoleTags[landHandRole] = new Tag(new LocalizedPluginString(Localizer.GetName(landHandRole)), defaultChanges);
if (pluginConfiguration.LandHandRoleTagsChanges.TryGetValue(landHandRole, out var savedChanges))
{
LandHandRoleTags[landHandRole].SetChanges(savedChanges);
}
}
}
JobTags = new Dictionary<string, Tag>();
foreach ((var jobAbbreviation, var role) in RoleHelper.RolesByJobAbbreviation)
{
if (Default.JobTags.TryGetValue(jobAbbreviation, out var defaultChanges))
{
JobTags[jobAbbreviation] = new Tag(new LiteralPluginString(jobAbbreviation), defaultChanges);
if (pluginConfiguration.JobTagsChanges.TryGetValue(jobAbbreviation, out var savedChanges))
{
JobTags[jobAbbreviation].SetChanges(savedChanges);
}
}
}
AllCustomTags = new Tag(new LocalizedPluginString(nameof(AllCustomTags)), Default.AllCustomTags);
AllCustomTags.SetChanges(pluginConfiguration.AllCustomTagsChanges);
CustomTags = new List<Tag>();
foreach (var savedChanges in pluginConfiguration.CustomTagsChanges)
{
var tag = new Tag(new LocalizedPluginString(nameof(CustomTags)));
tag.SetChanges(savedChanges);
CustomTags.Add(tag);
}
// Set up the inheritance heirarchy
AllRoleTags.Parent = AllTags;
foreach ((var role, var roleTag) in RoleTags)
{
roleTag.Parent = AllRoleTags;
if (role == Role.Dps)
{
foreach ((var dpsRole, var dpsRoleTag) in DpsRoleTags)
{
dpsRoleTag.Parent = roleTag;
if (dpsRole == DpsRole.Ranged)
{
foreach ((var rangedDpsRole, var rangedDpsRoleTag) in RangedDpsRoleTags)
{
rangedDpsRoleTag.Parent = dpsRoleTag;
}
}
}
}
else if (role == Role.LandHand)
{
foreach ((var landHandRole, var landHandRoleTag) in LandHandRoleTags)
{
landHandRoleTag.Parent = roleTag;
}
}
}
foreach ((var jobAbbreviation, var jobTag) in JobTags)
{
if (RoleHelper.RolesByJobAbbreviation.TryGetValue(jobAbbreviation, out var role))
{
if (RoleHelper.DpsRolesByJobAbbreviation.TryGetValue(jobAbbreviation, out var dpsRole))
{
if (RoleHelper.RangedDpsRolesByJobAbbreviation.TryGetValue(jobAbbreviation, out var rangedDpsRole))
{
jobTag.Parent = RangedDpsRoleTags[rangedDpsRole];
}
else
{
jobTag.Parent = DpsRoleTags[dpsRole];
}
}
else if (RoleHelper.LandHandRolesByJobAbbreviation.TryGetValue(jobAbbreviation, out var landHandRole))
{
jobTag.Parent = LandHandRoleTags[landHandRole];
}
else
{
jobTag.Parent = RoleTags[RoleHelper.RolesByJobAbbreviation[jobAbbreviation]];
}
}
}
AllCustomTags.Parent = AllTags;
foreach (var tag in CustomTags)
{
tag.Parent = AllCustomTags;
}
Identities = pluginConfiguration.Identities;
// Migrate old custom tag identity assignments
bool customTagsMigrated = false;
foreach (var customTag in CustomTags)
{
if (customTag.CustomId.Value == Guid.Empty)
{
customTag.CustomId.Behavior = Inheritables.InheritableBehavior.Enabled;
customTag.CustomId.Value = Guid.NewGuid();
customTagsMigrated = true;
}
foreach (string identityToAddTo in customTag.IdentitiesToAddTo)
{
Identity? identity = Identities.FirstOrDefault(identity => identity.Name.ToLower() == identityToAddTo.ToLower());
if (identity == null)
{
identity = new Identity(identityToAddTo);
Identities.Add(identity);
}
if (identity != null)
{
identity.CustomTagIds.Add(customTag.CustomId.Value);
customTagsMigrated = true;
}
}
if (customTag.GameObjectNamesToApplyTo.Behavior != Inheritables.InheritableBehavior.Inherit)
{
customTag.GameObjectNamesToApplyTo.Behavior = Inheritables.InheritableBehavior.Inherit;
customTag.GameObjectNamesToApplyTo.Value = "";
customTagsMigrated = true;
}
}
if (customTagsMigrated)
{
if (needToSave)
pluginConfiguration.Save(this);
}
}
public void AddCustomTagToIdentity(Tag customTag, Identity identity)
{
if (!identity.CustomTagIds.Contains(customTag.CustomId.Value))
{
identity.CustomTagIds.Add(customTag.CustomId.Value);
}
if (!Identities.Contains(identity))
{
Identities.Add(identity);
}
}
public void RemoveCustomTagFromIdentity(Tag customTag, Identity identity)
{
identity.CustomTagIds.Remove(customTag.CustomId.Value);
if (!identity.CustomTagIds.Any())
{
Identities.Remove(identity);
}
}
public void RemoveCustomTagFromIdentities(Tag customTag)
{
foreach (var identity in Identities.ToArray())
{
RemoveCustomTagFromIdentity(customTag, identity);
}
}
public Identity GetIdentity(string name, uint? worldId)
{
foreach (var identity in Identities)
{
if (identity.Name.ToLower().Trim() == name.ToLower().Trim())
{
if (identity.WorldId == null && worldId != null)
{
identity.WorldId = worldId;
pluginConfiguration.Save(this);
}
return identity;
}
}
return new Identity(name)
{
WorldId = worldId
};
}
public Identity? GetIdentity(GameObjectContextMenuOpenArgs contextMenuOpenedArgs)
{
if (string.IsNullOrEmpty(contextMenuOpenedArgs.Text?.TextValue)
|| contextMenuOpenedArgs.ObjectWorld == 0
|| contextMenuOpenedArgs.ObjectWorld == 65535)
{
return null;
}
return GetIdentity(contextMenuOpenedArgs.Text?.TextValue ?? string.Empty, contextMenuOpenedArgs.ObjectWorld);
}
public Identity GetIdentity(PlayerCharacter playerCharacter)
{
return GetIdentity(playerCharacter.Name.TextValue, playerCharacter.HomeWorld.Id);
}
public Identity GetIdentity(PartyMember partyMember)
{
return GetIdentity(partyMember.Name.TextValue, partyMember.World.Id);
}
public Identity GetIdentity(PlayerPayload playerPayload)
{
return GetIdentity(playerPayload.PlayerName, playerPayload.World.RowId);
}
}
}

View File

@@ -0,0 +1,308 @@
using Dalamud.ContextMenu;
using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Game.ClientState.Party;
using Dalamud.Game.Text.SeStringHandling.Payloads;
using PlayerTags.Configuration;
using PlayerTags.PluginStrings;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PlayerTags.Data
{
public class PluginTagsData
{
public DefaultPluginData Default;
public Tag AllTags;
public Tag AllRoleTags;
public Dictionary<Role, Tag> RoleTags;
public Dictionary<DpsRole, Tag> DpsRoleTags;
public Dictionary<RangedDpsRole, Tag> RangedDpsRoleTags;
public Dictionary<LandHandRole, Tag> LandHandRoleTags;
public Dictionary<string, Tag> JobTags;
public Tag AllCustomTags;
public List<Tag> CustomTags;
public List<Identity> Identities;
private readonly TagsConfiguration pluginZonedConfig;
public PluginTagsData(PluginConfiguration pluginconfiguration, TagsConfiguration pluginZonedConfiguration)
{
pluginZonedConfig = pluginZonedConfiguration;
ReloadDefault();
}
public bool ReloadDefault()
{
var needToSave = false;
Default = new DefaultPluginData(pluginZonedConfig.DefaultPluginDataTemplate);
// Set the default changes and saved changes
AllTags = new Tag(new LocalizedPluginString(nameof(AllTags)), Default.AllTags);
AllTags.SetChanges(pluginZonedConfig.AllTagsChanges);
AllRoleTags = new Tag(new LocalizedPluginString(nameof(AllRoleTags)), Default.AllRoleTags);
AllRoleTags.SetChanges(pluginZonedConfig.AllRoleTagsChanges);
RoleTags = new Dictionary<Role, Tag>();
foreach (var role in Enum.GetValues<Role>())
{
if (Default.RoleTags.TryGetValue(role, out var defaultTag))
{
RoleTags[role] = new Tag(new LocalizedPluginString(Localizer.GetName(role)), defaultTag);
if (pluginZonedConfig.RoleTagsChanges.TryGetValue(role, out var savedChanges))
{
RoleTags[role].SetChanges(savedChanges);
}
}
}
DpsRoleTags = new Dictionary<DpsRole, Tag>();
foreach (var dpsRole in Enum.GetValues<DpsRole>())
{
if (Default.DpsRoleTags.TryGetValue(dpsRole, out var defaultTag))
{
DpsRoleTags[dpsRole] = new Tag(new LocalizedPluginString(Localizer.GetName(dpsRole)), defaultTag);
if (pluginZonedConfig.DpsRoleTagsChanges.TryGetValue(dpsRole, out var savedChanges))
{
DpsRoleTags[dpsRole].SetChanges(savedChanges);
}
}
}
RangedDpsRoleTags = new Dictionary<RangedDpsRole, Tag>();
foreach (var rangedDpsRole in Enum.GetValues<RangedDpsRole>())
{
if (Default.RangedDpsRoleTags.TryGetValue(rangedDpsRole, out var defaultTag))
{
RangedDpsRoleTags[rangedDpsRole] = new Tag(new LocalizedPluginString(Localizer.GetName(rangedDpsRole)), defaultTag);
if (pluginZonedConfig.RangedDpsRoleTagsChanges.TryGetValue(rangedDpsRole, out var savedChanges))
{
RangedDpsRoleTags[rangedDpsRole].SetChanges(savedChanges);
}
}
}
LandHandRoleTags = new Dictionary<LandHandRole, Tag>();
foreach (var landHandRole in Enum.GetValues<LandHandRole>())
{
if (Default.LandHandRoleTags.TryGetValue(landHandRole, out var defaultChanges))
{
LandHandRoleTags[landHandRole] = new Tag(new LocalizedPluginString(Localizer.GetName(landHandRole)), defaultChanges);
if (pluginZonedConfig.LandHandRoleTagsChanges.TryGetValue(landHandRole, out var savedChanges))
{
LandHandRoleTags[landHandRole].SetChanges(savedChanges);
}
}
}
JobTags = new Dictionary<string, Tag>();
foreach ((var jobAbbreviation, var role) in RoleHelper.RolesByJobAbbreviation)
{
if (Default.JobTags.TryGetValue(jobAbbreviation, out var defaultChanges))
{
JobTags[jobAbbreviation] = new Tag(new LiteralPluginString(jobAbbreviation), defaultChanges);
if (pluginZonedConfig.JobTagsChanges.TryGetValue(jobAbbreviation, out var savedChanges))
{
JobTags[jobAbbreviation].SetChanges(savedChanges);
}
}
}
AllCustomTags = new Tag(new LocalizedPluginString(nameof(AllCustomTags)), Default.AllCustomTags);
AllCustomTags.SetChanges(pluginZonedConfig.AllCustomTagsChanges);
CustomTags = new List<Tag>();
foreach (var savedChanges in pluginZonedConfig.CustomTagsChanges)
{
var tag = new Tag(new LocalizedPluginString(nameof(CustomTags)));
tag.SetChanges(savedChanges);
CustomTags.Add(tag);
}
// Set up the inheritance heirarchy
AllRoleTags.Parent = AllTags;
foreach ((var role, var roleTag) in RoleTags)
{
roleTag.Parent = AllRoleTags;
if (role == Role.Dps)
{
foreach ((var dpsRole, var dpsRoleTag) in DpsRoleTags)
{
dpsRoleTag.Parent = roleTag;
if (dpsRole == DpsRole.Ranged)
{
foreach ((var rangedDpsRole, var rangedDpsRoleTag) in RangedDpsRoleTags)
{
rangedDpsRoleTag.Parent = dpsRoleTag;
}
}
}
}
else if (role == Role.LandHand)
{
foreach ((var landHandRole, var landHandRoleTag) in LandHandRoleTags)
{
landHandRoleTag.Parent = roleTag;
}
}
}
foreach ((var jobAbbreviation, var jobTag) in JobTags)
{
if (RoleHelper.RolesByJobAbbreviation.TryGetValue(jobAbbreviation, out var role))
{
if (RoleHelper.DpsRolesByJobAbbreviation.TryGetValue(jobAbbreviation, out var dpsRole))
{
if (RoleHelper.RangedDpsRolesByJobAbbreviation.TryGetValue(jobAbbreviation, out var rangedDpsRole))
{
jobTag.Parent = RangedDpsRoleTags[rangedDpsRole];
}
else
{
jobTag.Parent = DpsRoleTags[dpsRole];
}
}
else if (RoleHelper.LandHandRolesByJobAbbreviation.TryGetValue(jobAbbreviation, out var landHandRole))
{
jobTag.Parent = LandHandRoleTags[landHandRole];
}
else
{
jobTag.Parent = RoleTags[RoleHelper.RolesByJobAbbreviation[jobAbbreviation]];
}
}
}
AllCustomTags.Parent = AllTags;
foreach (var tag in CustomTags)
{
tag.Parent = AllCustomTags;
}
Identities = pluginZonedConfig.Identities;
// Migrate old custom tag identity assignments
bool customTagsMigrated = false;
foreach (var customTag in CustomTags)
{
if (customTag.CustomId.Value == Guid.Empty)
{
customTag.CustomId.Behavior = Inheritables.InheritableBehavior.Enabled;
customTag.CustomId.Value = Guid.NewGuid();
customTagsMigrated = true;
}
foreach (string identityToAddTo in customTag.IdentitiesToAddTo)
{
Identity? identity = Identities.FirstOrDefault(identity => identity.Name.ToLower() == identityToAddTo.ToLower());
if (identity == null)
{
identity = new Identity(identityToAddTo);
Identities.Add(identity);
}
if (identity != null)
{
identity.CustomTagIds.Add(customTag.CustomId.Value);
customTagsMigrated = true;
}
}
if (customTag.GameObjectNamesToApplyTo.Behavior != Inheritables.InheritableBehavior.Inherit)
{
customTag.GameObjectNamesToApplyTo.Behavior = Inheritables.InheritableBehavior.Inherit;
customTag.GameObjectNamesToApplyTo.Value = "";
customTagsMigrated = true;
}
}
if (customTagsMigrated)
needToSave = true;
return needToSave;
}
public void AddCustomTagToIdentity(Tag customTag, Identity identity)
{
if (!identity.CustomTagIds.Contains(customTag.CustomId.Value))
{
identity.CustomTagIds.Add(customTag.CustomId.Value);
}
if (!Identities.Contains(identity))
{
Identities.Add(identity);
}
}
public void RemoveCustomTagFromIdentity(Tag customTag, Identity identity)
{
identity.CustomTagIds.Remove(customTag.CustomId.Value);
if (!identity.CustomTagIds.Any())
{
Identities.Remove(identity);
}
}
public void RemoveCustomTagFromIdentities(Tag customTag)
{
foreach (var identity in Identities.ToArray())
{
RemoveCustomTagFromIdentity(customTag, identity);
}
}
public Identity GetIdentity(string name, uint? worldId)
{
foreach (var identity in Identities)
{
if (identity.Name.ToLower().Trim() == name.ToLower().Trim())
{
if (identity.WorldId == null && worldId != null)
{
identity.WorldId = worldId;
pluginZonedConfig.ApplyTagsData(this);
}
return identity;
}
}
return new Identity(name)
{
WorldId = worldId
};
}
public Identity? GetIdentity(GameObjectContextMenuOpenArgs contextMenuOpenedArgs)
{
if (string.IsNullOrEmpty(contextMenuOpenedArgs.Text?.TextValue)
|| contextMenuOpenedArgs.ObjectWorld == 0
|| contextMenuOpenedArgs.ObjectWorld == 65535)
{
return null;
}
return GetIdentity(contextMenuOpenedArgs.Text?.TextValue ?? string.Empty, contextMenuOpenedArgs.ObjectWorld);
}
public Identity GetIdentity(PlayerCharacter playerCharacter)
{
return GetIdentity(playerCharacter.Name.TextValue, playerCharacter.HomeWorld.Id);
}
public Identity GetIdentity(PartyMember partyMember)
{
return GetIdentity(partyMember.Name.TextValue, partyMember.World.Id);
}
public Identity GetIdentity(PlayerPayload playerPayload)
{
return GetIdentity(playerPayload.PlayerName, playerPayload.World.RowId);
}
}
}

View File

@@ -102,8 +102,14 @@ namespace PlayerTags.Features
}
}
public ChatTagTargetFeature(PluginConfiguration pluginConfiguration, PluginData pluginData) : base(pluginConfiguration, pluginData)
private PluginConfiguration m_PluginConfiguration;
private PluginData m_PluginData;
public ChatTagTargetFeature(PluginConfiguration pluginConfiguration, PluginData pluginData)
{
m_PluginConfiguration = pluginConfiguration;
m_PluginData = pluginData;
PluginServices.ChatGui.ChatMessage += Chat_ChatMessage;
}
@@ -115,7 +121,7 @@ namespace PlayerTags.Features
private void Chat_ChatMessage(XivChatType type, uint senderId, ref SeString sender, ref SeString message, ref bool isHandled)
{
if (EnableGlobal && pluginConfiguration.GeneralOptions[ActivityContextManager.CurrentActivityContext.ActivityType].IsApplyTagsToAllChatMessagesEnabled)
if (m_PluginConfiguration.GeneralConfigs.GetConfig(ActivityContextManager.CurrentActivityContext.ZoneType).IsApplyTagsToAllChatMessagesEnabled)
{
AddTagsToChat(sender, type, true);
AddTagsToChat(message, type, false);
@@ -329,7 +335,9 @@ namespace PlayerTags.Features
if (isSender)
SplitOffPartyNumberPrefix(message, chatType);
var tagsData = m_PluginData.GetTagsData(ActivityContextManager.CurrentActivityContext.ZoneType);
var stringMatches = GetStringMatches(message);
foreach (var stringMatch in stringMatches)
{
StringChanges stringChanges = new();
@@ -341,7 +349,7 @@ namespace PlayerTags.Features
if (stringMatch.GameObject is PlayerCharacter playerCharacter)
{
// Add the job tag
if (playerCharacter.ClassJob.GameData != null && pluginData.JobTags.TryGetValue(playerCharacter.ClassJob.GameData.Abbreviation, out var jobTag))
if (playerCharacter.ClassJob.GameData != null && tagsData.JobTags.TryGetValue(playerCharacter.ClassJob.GameData.Abbreviation, out var jobTag))
{
if (isTagEnabled(jobTag))
{
@@ -355,7 +363,7 @@ namespace PlayerTags.Features
}
// Add randomly generated name tag payload
if (pluginConfiguration.IsPlayerNameRandomlyGenerated)
if (m_PluginConfiguration.IsPlayerNameRandomlyGenerated)
{
var playerName = stringMatch.GetMatchText();
if (playerName != null)
@@ -372,10 +380,10 @@ namespace PlayerTags.Features
// Add custom tags
if (stringMatch.PlayerPayload != null)
{
Identity identity = pluginData.GetIdentity(stringMatch.PlayerPayload);
Identity identity = tagsData.GetIdentity(stringMatch.PlayerPayload);
foreach (var customTagId in identity.CustomTagIds)
{
var customTag = pluginData.CustomTags.FirstOrDefault(tag => tag.CustomId.Value == customTagId);
var customTag = tagsData.CustomTags.FirstOrDefault(tag => tag.CustomId.Value == customTagId);
if (customTag != null)
{
if (isTagEnabled(customTag))
@@ -401,17 +409,17 @@ namespace PlayerTags.Features
// An additional step to apply text color to additional locations
if (stringMatch.PlayerPayload != null && stringMatch.DisplayTextPayloads.Any())
{
Identity identity = pluginData.GetIdentity(stringMatch.PlayerPayload);
Identity identity = tagsData.GetIdentity(stringMatch.PlayerPayload);
if (stringMatch.GameObject is PlayerCharacter playerCharacter1)
{
if (playerCharacter1.ClassJob.GameData != null && pluginData.JobTags.TryGetValue(playerCharacter1.ClassJob.GameData.Abbreviation, out var jobTag) && isTagEnabled(jobTag))
if (playerCharacter1.ClassJob.GameData != null && tagsData.JobTags.TryGetValue(playerCharacter1.ClassJob.GameData.Abbreviation, out var jobTag) && isTagEnabled(jobTag))
applyTextFormatting(jobTag);
}
foreach (var customTagId in identity.CustomTagIds)
{
var customTag = pluginData.CustomTags.FirstOrDefault(tag => tag.CustomId.Value == customTagId);
var customTag = tagsData.CustomTags.FirstOrDefault(tag => tag.CustomId.Value == customTagId);
if (customTag != null && isTagEnabled(customTag))
applyTextFormatting(customTag);
}

View File

@@ -1,6 +1,7 @@
using Dalamud.ContextMenu;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Logging;
using Pilz.Dalamud.ActivityContexts;
using PlayerTags.Configuration;
using PlayerTags.Data;
using PlayerTags.Resources;
@@ -13,7 +14,7 @@ namespace PlayerTags.Features
/// <summary>
/// A feature that adds options for the management of custom tags to context menus.
/// </summary>
public class CustomTagsContextMenuFeature : FeatureBase, IDisposable
public class CustomTagsContextMenuFeature : FeatureBase
{
private string?[] SupportedAddonNames = new string?[]
{
@@ -31,15 +32,20 @@ namespace PlayerTags.Features
"SocialList",
};
private PluginConfiguration m_PluginConfiguration;
private PluginData m_PluginData;
private DalamudContextMenu? m_ContextMenu;
public CustomTagsContextMenuFeature(PluginConfiguration pluginConfiguration, PluginData pluginData) : base(pluginConfiguration, pluginData)
public CustomTagsContextMenuFeature(PluginConfiguration pluginConfiguration, PluginData pluginData)
{
m_PluginConfiguration = pluginConfiguration;
m_PluginData = pluginData;
m_ContextMenu = new DalamudContextMenu();
m_ContextMenu.OnOpenGameObjectContextMenu += ContextMenuHooks_ContextMenuOpened;
}
public void Dispose()
public override void Dispose()
{
if (m_ContextMenu != null)
{
@@ -47,21 +53,22 @@ namespace PlayerTags.Features
((IDisposable)m_ContextMenu).Dispose();
m_ContextMenu = null;
}
base.Dispose();
}
private void ContextMenuHooks_ContextMenuOpened(GameObjectContextMenuOpenArgs contextMenuOpenedArgs)
{
if (!EnableGlobal || !pluginConfiguration.IsCustomTagsContextMenuEnabled
if (!m_PluginConfiguration.IsCustomTagsContextMenuEnabled
|| !SupportedAddonNames.Contains(contextMenuOpenedArgs.ParentAddonName))
{
return;
}
Identity? identity = pluginData.GetIdentity(contextMenuOpenedArgs);
var tagsData = m_PluginData.GetTagsData(ActivityContextManager.CurrentActivityContext.ZoneType);
Identity? identity = tagsData.GetIdentity(contextMenuOpenedArgs);
if (identity != null)
{
var allTags = new Dictionary<Tag, bool>();
foreach (var customTag in pluginData.CustomTags)
foreach (var customTag in tagsData.CustomTags)
{
var isAdded = identity.CustomTagIds.Contains(customTag.CustomId.Value);
allTags.Add(customTag, isAdded);
@@ -81,10 +88,10 @@ namespace PlayerTags.Features
new GameObjectContextMenuItem(menuItemText, openedEventArgs =>
{
if (tag.Value)
pluginData.RemoveCustomTagFromIdentity(tag.Key, identity);
tagsData.RemoveCustomTagFromIdentity(tag.Key, identity);
else
pluginData.AddCustomTagToIdentity(tag.Key, identity);
pluginConfiguration.Save(pluginData);
tagsData.AddCustomTagToIdentity(tag.Key, identity);
m_PluginConfiguration.Save(m_PluginData);
})
{
IsSubMenu = false

View File

@@ -1,5 +1,4 @@
using PlayerTags.Configuration;
using PlayerTags.Data;
using Pilz.Dalamud.ActivityContexts;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -8,17 +7,18 @@ using System.Threading.Tasks;
namespace PlayerTags.Features
{
public class FeatureBase
public abstract class FeatureBase : IDisposable
{
protected readonly PluginConfiguration pluginConfiguration;
protected readonly PluginData pluginData;
public ActivityContextManager ActivityContextManager { get; init; }
public virtual bool EnableGlobal => pluginConfiguration.EnabledGlobal;
protected FeatureBase(PluginConfiguration pluginConfiguration, PluginData pluginData)
public FeatureBase()
{
this.pluginConfiguration = pluginConfiguration;
this.pluginData = pluginData;
ActivityContextManager = new();
}
public virtual void Dispose()
{
ActivityContextManager.Dispose();
}
}
}

View File

@@ -24,12 +24,16 @@ namespace PlayerTags.Features
/// </summary>
public class NameplateTagTargetFeature : TagTargetFeature
{
private readonly PluginConfiguration m_PluginConfiguration;
private readonly PluginData m_PluginData;
private readonly StatusIconPriorizer statusiconPriorizer;
private readonly JobIconSets jobIconSets = new();
private Nameplate? m_Nameplate;
public NameplateTagTargetFeature(PluginConfiguration pluginConfiguration, PluginData pluginData) : base(pluginConfiguration, pluginData)
public NameplateTagTargetFeature(PluginConfiguration pluginConfiguration, PluginData pluginData)
{
m_PluginConfiguration = pluginConfiguration;
m_PluginData = pluginData;
statusiconPriorizer = new(pluginConfiguration.StatusIconPriorizerSettings);
PluginServices.ClientState.Login += ClientState_Login;
@@ -107,13 +111,11 @@ namespace PlayerTags.Features
private unsafe void Nameplate_PlayerNameplateUpdated(PlayerNameplateUpdatedArgs args)
{
if (!EnableGlobal) return;
var beforeTitleBytes = args.Title.Encode();
var iconID = args.IconId;
var generalOptions = pluginConfiguration.GeneralOptions[ActivityContextManager.CurrentActivityContext.ActivityType];
var generalOptions = m_PluginConfiguration.GeneralConfigs.GetConfig(ActivityContextManager.CurrentActivityContext.ZoneType);
AddTagsToNameplate(args.PlayerCharacter, args.Name, args.Title, args.FreeCompany, ref iconID, generalOptions);
AddTagsToNameplate(args.PlayerCharacter, args.Name, args.Title, args.FreeCompany, ref iconID);
args.IconId = iconID;
@@ -170,26 +172,29 @@ namespace PlayerTags.Features
/// <param name="name">The name text to change.</param>
/// <param name="title">The title text to change.</param>
/// <param name="freeCompany">The free company text to change.</param>
private void AddTagsToNameplate(GameObject gameObject, SeString name, SeString title, SeString freeCompany, ref int statusIcon, GeneralOptionsClass generalOptions)
private void AddTagsToNameplate(GameObject gameObject, SeString name, SeString title, SeString freeCompany, ref int statusIcon)
{
var curZoneType = ActivityContextManager.CurrentActivityContext.ZoneType;
var generalConfig = m_PluginConfiguration.GeneralConfigs.GetConfig(curZoneType);
var tagsData = m_PluginData.GetTagsData(curZoneType);
var playerCharacter = gameObject as PlayerCharacter;
int? newStatusIcon = null;
NameplateChanges nameplateChanges = GenerateEmptyNameplateChanges(name, title, freeCompany);
if (playerCharacter != null && (!playerCharacter.IsDead || generalOptions.NameplateDeadPlayerHandling != DeadPlayerHandling.Ignore))
if (playerCharacter != null && (!playerCharacter.IsDead || generalConfig.NameplateDeadPlayerHandling != DeadPlayerHandling.Ignore))
{
var classJob = playerCharacter.ClassJob;
var classJobGameData = classJob?.GameData;
// Add the job tags
if (classJobGameData != null && pluginData.JobTags.TryGetValue(classJobGameData.Abbreviation, out var jobTag))
if (classJobGameData != null && tagsData.JobTags.TryGetValue(classJobGameData.Abbreviation, out var jobTag))
{
if (jobTag.TagTargetInNameplates.InheritedValue != null && jobTag.TagPositionInNameplates.InheritedValue != null)
checkTag(jobTag);
}
// Add the randomly generated name tag payload
if (pluginConfiguration.IsPlayerNameRandomlyGenerated)
if (m_PluginConfiguration.IsPlayerNameRandomlyGenerated)
{
var characterName = playerCharacter.Name.TextValue;
if (characterName != null)
@@ -201,10 +206,10 @@ namespace PlayerTags.Features
}
// Add custom tags
Identity identity = pluginData.GetIdentity(playerCharacter);
Identity identity = tagsData.GetIdentity(playerCharacter);
foreach (var customTagId in identity.CustomTagIds)
{
var customTag = pluginData.CustomTags.FirstOrDefault(tag => tag.CustomId.Value == customTagId);
var customTag = tagsData.CustomTags.FirstOrDefault(tag => tag.CustomId.Value == customTagId);
if (customTag != null)
checkTag(customTag);
}
@@ -226,28 +231,28 @@ namespace PlayerTags.Features
if (newStatusIcon != null)
{
var change = nameplateChanges.GetChange(NameplateElements.Name, StringPosition.Before);
NameplateUpdateFactory.ApplyStatusIconWithPrio(ref statusIcon, (int)newStatusIcon, change, ActivityContextManager.CurrentActivityContext, statusiconPriorizer, pluginConfiguration.MoveStatusIconToNameplateTextIfPossible);
NameplateUpdateFactory.ApplyStatusIconWithPrio(ref statusIcon, (int)newStatusIcon, change, ActivityContextManager.CurrentActivityContext, statusiconPriorizer, m_PluginConfiguration.MoveStatusIconToNameplateTextIfPossible);
}
// Gray out the nameplate
if (playerCharacter != null && playerCharacter.IsDead && generalOptions.NameplateDeadPlayerHandling == DeadPlayerHandling.GrayOut)
if (playerCharacter != null && playerCharacter.IsDead && generalConfig.NameplateDeadPlayerHandling == DeadPlayerHandling.GrayOut)
GrayOutNameplate(gameObject, nameplateChanges);
// Build the final strings out of the payloads
ApplyNameplateChanges(nameplateChanges);
if (playerCharacter != null && (!playerCharacter.IsDead || generalOptions.NameplateDeadPlayerHandling == DeadPlayerHandling.Include))
if (playerCharacter != null && (!playerCharacter.IsDead || generalConfig.NameplateDeadPlayerHandling == DeadPlayerHandling.Include))
{
// An additional step to apply text color to additional locations
Identity identity = pluginData.GetIdentity(playerCharacter);
Identity identity = tagsData.GetIdentity(playerCharacter);
foreach (var customTagId in identity.CustomTagIds)
{
var customTag = pluginData.CustomTags.FirstOrDefault(tag => tag.CustomId.Value == customTagId);
var customTag = tagsData.CustomTags.FirstOrDefault(tag => tag.CustomId.Value == customTagId);
if (customTag != null)
applyTextFormatting(customTag);
}
if (playerCharacter.ClassJob.GameData != null && pluginData.JobTags.TryGetValue(playerCharacter.ClassJob.GameData.Abbreviation, out var jobTag))
if (playerCharacter.ClassJob.GameData != null && tagsData.JobTags.TryGetValue(playerCharacter.ClassJob.GameData.Abbreviation, out var jobTag))
applyTextFormatting(jobTag);
void applyTextFormatting(Tag tag)

View File

@@ -6,7 +6,6 @@ using FFXIVClientStructs.FFXIV.Client.Game.Object;
using Lumina.Excel.GeneratedSheets;
using Pilz.Dalamud.ActivityContexts;
using Pilz.Dalamud.Tools.Strings;
using PlayerTags.Configuration;
using PlayerTags.Configuration.GameConfig;
using PlayerTags.Data;
using PlayerTags.Inheritables;
@@ -21,18 +20,16 @@ namespace PlayerTags.Features
/// <summary>
/// The base of a feature that adds tags to UI elements.
/// </summary>
public abstract class TagTargetFeature : FeatureBase, IDisposable
public abstract class TagTargetFeature : FeatureBase
{
public ActivityContextManager ActivityContextManager { get; init; }
protected TagTargetFeature(PluginConfiguration pluginConfiguration, PluginData pluginData) : base(pluginConfiguration, pluginData)
public TagTargetFeature()
{
ActivityContextManager = new();
}
public virtual void Dispose()
public override void Dispose()
{
ActivityContextManager.Dispose();
base.Dispose();
}
protected abstract bool IsIconVisible(Tag tag);

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Authors>r00telement;Pilzinsel64</Authors>
<Version>1.9.2.0</Version>
<Version>1.9.0.0</Version>
</PropertyGroup>
<PropertyGroup>
@@ -26,7 +26,7 @@
<ItemGroup>
<PackageReference Include="Dalamud.ContextMenu" Version="1.2.1" />
<PackageReference Include="DalamudPackager" Version="2.1.10" />
<ProjectReference Include="..\Pilz.Dalamud\Pilz.Dalamud\Pilz.Dalamud.csproj" />
<ProjectReference Include="..\Pilz.Dalamud\Pilz.Dalamud.csproj" />
<Reference Include="FFXIVClientStructs">
<HintPath>$(DalamudLibPath)FFXIVClientStructs.dll</HintPath>
<Private>false</Private>

View File

@@ -10,7 +10,6 @@ using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
namespace PlayerTags
{
@@ -18,10 +17,6 @@ namespace PlayerTags
{
public string Name => "Player Tags";
private const string c_CommandName = "/playertags";
private const string c_SubCommandName_EnableGlobal = "enableglobal";
private const string c_CommandArg_On = "on";
private const string c_CommandArg_Off = "off";
private const string c_CommandArg_toggle = "toggle";
private PluginConfiguration m_PluginConfiguration;
private PluginData m_PluginData;
@@ -45,9 +40,9 @@ namespace PlayerTags
PluginServices.DalamudPluginInterface.UiBuilder.Draw += UiBuilder_Draw;
PluginServices.DalamudPluginInterface.UiBuilder.OpenConfigUi += UiBuilder_OpenConfigUi;
PluginServices.CommandManager.AddHandler(c_CommandName, new CommandInfo(CommandManager_Handler)
PluginServices.CommandManager.AddHandler(c_CommandName, new CommandInfo((string command, string arguments) => UiBuilder_OpenConfigUi())
{
HelpMessage = Resources.Strings.Loc_Command_playertags_v2
HelpMessage = Resources.Strings.Loc_Command_playertags
});
m_CustomTagsContextMenuFeature = new CustomTagsContextMenuFeature(m_PluginConfiguration, m_PluginData);
m_NameplatesTagTargetFeature = new NameplateTagTargetFeature(m_PluginConfiguration, m_PluginData);
@@ -70,44 +65,6 @@ namespace PlayerTags
Localizer.SetLanguage(langCode);
}
private void CommandManager_Handler(string command, string arguments)
{
switch (command)
{
case c_CommandName:
if (string.IsNullOrWhiteSpace(command))
UiBuilder_OpenConfigUi();
else
{
var lowerArgs = arguments.ToLower().Split(' ');
if (lowerArgs.Length >= 1)
{
switch (lowerArgs[0])
{
case c_SubCommandName_EnableGlobal:
if (lowerArgs.Length >= 2)
{
switch (lowerArgs[0])
{
case c_CommandArg_On:
m_PluginConfiguration.EnabledGlobal = true;
break;
case c_CommandArg_Off:
m_PluginConfiguration.EnabledGlobal = false;
break;
case c_CommandArg_toggle:
m_PluginConfiguration.EnabledGlobal = !m_PluginConfiguration.EnabledGlobal;
break;
}
}
break;
}
}
}
break;
}
}
private void UiBuilder_Draw()
{
if (m_PluginConfiguration.IsVisible)

View File

@@ -187,13 +187,11 @@ namespace PlayerTags.Resources {
}
/// <summary>
/// Sucht eine lokalisierte Zeichenfolge, die Shows the config window for Player Tags
///Subcommands:
///enableglobal on|of|toggle -&gt; Set a global master switch that enables or disables all plugin features without changing the current configuration. ähnelt.
/// Sucht eine lokalisierte Zeichenfolge, die Shows the config window for Player Tags ähnelt.
/// </summary>
public static string Loc_Command_playertags_v2 {
public static string Loc_Command_playertags {
get {
return ResourceManager.GetString("Loc_Command_playertags_v2", resourceCulture);
return ResourceManager.GetString("Loc_Command_playertags", resourceCulture);
}
}

View File

@@ -829,9 +829,4 @@ Benutze dies, wenn du jede Option unter deiner Kontrolle haben möchtest oder nu
<value>Wenn aktiviert, gilt das Tag für alle Chat-Nachrichten, die keinen definierten Typ haben.
Dieser Fall kann entweder auftreten, wenn das Spiel ein Update bekommt und die Aufzählung aller Chat-Typen aufgrund von geänderten Werten ungültig sind oder Plugins erstellt einen benutzerdefinierten Chat-Typ, aus welchen Gründen auch immer.</value>
</data>
<data name="Loc_Command_playertags_v2" xml:space="preserve">
<value>Zeigt das Konfigurationsfester Player Tags
Unterbefehle:
enableglobal on|of|toggle -&gt; Setzt einen globalen Hauptschalter, welcher alle PluginFeaturues ein- bzw. ausschalten kann, ohne die Konfiguration selbst zu ändern.</value>
</data>
</root>

View File

@@ -829,9 +829,7 @@ Use this if you want to to have every option under your control or just want to
<value>When enabled the Tag will apply to all Chat messages that has not a defined type.
This case can happen either if the Game updates and the Enumeration of all Chat Types gets invalid due to shifted values, or plugins creates a custom chat type for whatever reason.</value>
</data>
<data name="Loc_Command_playertags_v2" xml:space="preserve">
<value>Shows the config window for Player Tags
Subcommands:
enableglobal on|of|toggle -&gt; Set a global master switch that enables or disables all plugin features without changing the current configuration.</value>
<data name="Loc_Command_playertags" xml:space="preserve">
<value>Shows the config window for Player Tags</value>
</data>
</root>