Compare commits
6 Commits
1.12.3.0
...
feature/ta
| Author | SHA1 | Date | |
|---|---|---|---|
| 094a82b2f0 | |||
| f977d4e9b9 | |||
| 90f554df8c | |||
| 76648b4b94 | |||
| 7b76a2cbd0 | |||
| 84a8aaad31 |
4
.gitmodules
vendored
4
.gitmodules
vendored
@@ -1,4 +0,0 @@
|
||||
[submodule "Pilz.Dalamud"]
|
||||
path = Pilz.Dalamud
|
||||
url = https://git.pilzinsel64.de/pilz-framework/pilz.dalamud.git
|
||||
branch = master
|
||||
Submodule Pilz.Dalamud deleted from 62ce794e8b
25
Pilz.Dalamud/ActivityContexts/ActivityContext.cs
Normal file
25
Pilz.Dalamud/ActivityContexts/ActivityContext.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
89
Pilz.Dalamud/ActivityContexts/ActivityContextManager.cs
Normal file
89
Pilz.Dalamud/ActivityContexts/ActivityContextManager.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
18
Pilz.Dalamud/ActivityContexts/ActivityType.cs
Normal file
18
Pilz.Dalamud/ActivityContexts/ActivityType.cs
Normal 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
|
||||
}
|
||||
}
|
||||
22
Pilz.Dalamud/ActivityContexts/ZoneType.cs
Normal file
22
Pilz.Dalamud/ActivityContexts/ZoneType.cs
Normal 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
|
||||
}
|
||||
}
|
||||
41
Pilz.Dalamud/Extensions.cs
Normal file
41
Pilz.Dalamud/Extensions.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
153
Pilz.Dalamud/GameInterfaceHelper.cs
Normal file
153
Pilz.Dalamud/GameInterfaceHelper.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
27
Pilz.Dalamud/Icons/JobIconSet.cs
Normal file
27
Pilz.Dalamud/Icons/JobIconSet.cs
Normal 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];
|
||||
}
|
||||
}
|
||||
}
|
||||
24
Pilz.Dalamud/Icons/JobIconSetName.cs
Normal file
24
Pilz.Dalamud/Icons/JobIconSetName.cs
Normal 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
|
||||
}
|
||||
}
|
||||
128
Pilz.Dalamud/Icons/JobIconSets.cs
Normal file
128
Pilz.Dalamud/Icons/JobIconSets.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
18
Pilz.Dalamud/Nameplates/EventArgs/HookBaseEventArgs.cs
Normal file
18
Pilz.Dalamud/Nameplates/EventArgs/HookBaseEventArgs.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
43
Pilz.Dalamud/Nameplates/Model/SafeAddonNameplate.cs
Normal file
43
Pilz.Dalamud/Nameplates/Model/SafeAddonNameplate.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
57
Pilz.Dalamud/Nameplates/Model/SafeNameplateInfo.cs
Normal file
57
Pilz.Dalamud/Nameplates/Model/SafeNameplateInfo.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
128
Pilz.Dalamud/Nameplates/Model/SafeNameplateObject.cs
Normal file
128
Pilz.Dalamud/Nameplates/Model/SafeNameplateObject.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
31
Pilz.Dalamud/Nameplates/Model/StatusIcons.cs
Normal file
31
Pilz.Dalamud/Nameplates/Model/StatusIcons.cs
Normal 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,
|
||||
}
|
||||
}
|
||||
203
Pilz.Dalamud/Nameplates/NameplateHooks.cs
Normal file
203
Pilz.Dalamud/Nameplates/NameplateHooks.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
83
Pilz.Dalamud/Nameplates/NameplateManager.cs
Normal file
83
Pilz.Dalamud/Nameplates/NameplateManager.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
59
Pilz.Dalamud/Nameplates/Tools/NameplateChanges.cs
Normal file
59
Pilz.Dalamud/Nameplates/Tools/NameplateChanges.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
Pilz.Dalamud/Nameplates/Tools/NameplateChangesProps.cs
Normal file
25
Pilz.Dalamud/Nameplates/Tools/NameplateChangesProps.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
15
Pilz.Dalamud/Nameplates/Tools/NameplateElements.cs
Normal file
15
Pilz.Dalamud/Nameplates/Tools/NameplateElements.cs
Normal 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
|
||||
}
|
||||
}
|
||||
52
Pilz.Dalamud/Nameplates/Tools/NameplateUpdateFactory.cs
Normal file
52
Pilz.Dalamud/Nameplates/Tools/NameplateUpdateFactory.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
67
Pilz.Dalamud/Nameplates/Tools/StatusIconPriorizer.cs
Normal file
67
Pilz.Dalamud/Nameplates/Tools/StatusIconPriorizer.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
110
Pilz.Dalamud/Nameplates/Tools/StatusIconPriorizerSettings.cs
Normal file
110
Pilz.Dalamud/Nameplates/Tools/StatusIconPriorizerSettings.cs
Normal 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,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
66
Pilz.Dalamud/Pilz.Dalamud.csproj
Normal file
66
Pilz.Dalamud/Pilz.Dalamud.csproj
Normal 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>
|
||||
28
Pilz.Dalamud/PluginServices.cs
Normal file
28
Pilz.Dalamud/PluginServices.cs
Normal 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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
41
Pilz.Dalamud/Tools/StatusIconFontConverter.cs
Normal file
41
Pilz.Dalamud/Tools/StatusIconFontConverter.cs
Normal 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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
24
Pilz.Dalamud/Tools/Strings/StringChange.cs
Normal file
24
Pilz.Dalamud/Tools/Strings/StringChange.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
40
Pilz.Dalamud/Tools/Strings/StringChanges.cs
Normal file
40
Pilz.Dalamud/Tools/Strings/StringChanges.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
31
Pilz.Dalamud/Tools/Strings/StringChangesProps.cs
Normal file
31
Pilz.Dalamud/Tools/Strings/StringChangesProps.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
15
Pilz.Dalamud/Tools/Strings/StringPosition.cs
Normal file
15
Pilz.Dalamud/Tools/Strings/StringPosition.cs
Normal 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
|
||||
}
|
||||
}
|
||||
132
Pilz.Dalamud/Tools/Strings/StringUpdateFactory.cs
Normal file
132
Pilz.Dalamud/Tools/Strings/StringUpdateFactory.cs
Normal 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
39
Pilz.Dalamud/XivApi.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
6
Pilz.Dalamud/packages.lock.json
Normal file
6
Pilz.Dalamud/packages.lock.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"version": 1,
|
||||
"dependencies": {
|
||||
"net7.0-windows7.0": {}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -26,7 +26,6 @@ Global
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
RESX_ShowErrorsInErrorList = False
|
||||
SolutionGuid = {B17E85B1-5F60-4440-9F9A-3DDE877E8CDF}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@@ -1,41 +1,74 @@
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PlayerTags.Configuration.GameConfig;
|
||||
|
||||
public class GameConfigHelper
|
||||
namespace PlayerTags.Configuration.GameConfig
|
||||
{
|
||||
private static GameConfigHelper instance = null;
|
||||
private unsafe static ConfigModule* configModule = null;
|
||||
|
||||
public static GameConfigHelper Instance
|
||||
public class GameConfigHelper
|
||||
{
|
||||
get
|
||||
private static GameConfigHelper instance = null;
|
||||
private unsafe static ConfigModule* configModule = null;
|
||||
|
||||
public static GameConfigHelper Instance
|
||||
{
|
||||
instance ??= new GameConfigHelper();
|
||||
return instance;
|
||||
get
|
||||
{
|
||||
instance ??= new GameConfigHelper();
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private GameConfigHelper()
|
||||
{
|
||||
unsafe
|
||||
private GameConfigHelper()
|
||||
{
|
||||
configModule = ConfigModule.Instance();
|
||||
unsafe
|
||||
{
|
||||
configModule = ConfigModule.Instance();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private uint? GetIntValue(ConfigOption option)
|
||||
{
|
||||
if (PluginServices.GameConfig.UiConfig.TryGetUInt(nameof(ConfigOption.LogNameType), out var value))
|
||||
private int? GetIntValue(ConfigOption option)
|
||||
{
|
||||
int? value = null;
|
||||
|
||||
unsafe
|
||||
{
|
||||
var index = configModule->GetIndex(option);
|
||||
if (index.HasValue)
|
||||
value = configModule->GetIntValue(index.Value);
|
||||
}
|
||||
|
||||
return value;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public LogNameType? GetLogNameType()
|
||||
{
|
||||
uint? value = GetIntValue(ConfigOption.LogNameType);
|
||||
if (value != null)
|
||||
return (LogNameType)value;
|
||||
return null;
|
||||
public LogNameType? GetLogNameType()
|
||||
{
|
||||
LogNameType? logNameType = null;
|
||||
int? value = GetIntValue(ConfigOption.LogNameType);
|
||||
|
||||
if (value.HasValue)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case 0:
|
||||
logNameType = LogNameType.FullName;
|
||||
break;
|
||||
case 1:
|
||||
logNameType = LogNameType.LastNameShorted;
|
||||
break;
|
||||
case 2:
|
||||
logNameType = LogNameType.FirstNameShorted;
|
||||
break;
|
||||
case 3:
|
||||
logNameType = LogNameType.Initials;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return logNameType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
namespace PlayerTags.Configuration.GameConfig;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public enum LogNameType : uint
|
||||
namespace PlayerTags.Configuration.GameConfig
|
||||
{
|
||||
FullName,
|
||||
LastNameShorted,
|
||||
FirstNameShorted,
|
||||
Initials
|
||||
public enum LogNameType
|
||||
{
|
||||
FullName,
|
||||
LastNameShorted,
|
||||
FirstNameShorted,
|
||||
Initials
|
||||
}
|
||||
}
|
||||
|
||||
18
PlayerTags/Configuration/GeneralConfiguration.cs
Normal file
18
PlayerTags/Configuration/GeneralConfiguration.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -2,348 +2,170 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Pilz.Dalamud.ActivityContexts;
|
||||
using Pilz.Dalamud.Tools.NamePlates;
|
||||
using Pilz.Dalamud.Nameplates.Tools;
|
||||
using PlayerTags.Data;
|
||||
using PlayerTags.Inheritables;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection.Metadata.Ecma335;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace PlayerTags.Configuration;
|
||||
|
||||
[Serializable]
|
||||
public class PluginConfiguration : IPluginConfiguration
|
||||
namespace PlayerTags.Configuration
|
||||
{
|
||||
private const int DEFAULT_CONFIG_VERSION = 1;
|
||||
|
||||
[JsonProperty]
|
||||
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()
|
||||
[Serializable]
|
||||
public partial class PluginConfiguration : IPluginConfiguration
|
||||
{
|
||||
{ ActivityType.None, new GeneralOptionsClass() },
|
||||
{ ActivityType.PveDuty, new GeneralOptionsClass() },
|
||||
{ ActivityType.PvpDuty, new GeneralOptionsClass() }
|
||||
};
|
||||
private const int DEFAULT_CONFIG_VERSION = 1;
|
||||
|
||||
public DefaultPluginDataTemplate DefaultPluginDataTemplate = DefaultPluginDataTemplate.None;
|
||||
public StatusIconPriorizerSettings StatusIconPriorizerSettings = new(true);
|
||||
public bool MoveStatusIconToNameplateTextIfPossible = true;
|
||||
public bool IsPlayerNameRandomlyGenerated = false;
|
||||
public bool IsCustomTagsContextMenuEnabled = true;
|
||||
public bool IsShowInheritedPropertiesEnabled = true;
|
||||
public bool IsPlayersTabOrderedByProximity = false;
|
||||
public bool IsPlayersTabSelfVisible = true;
|
||||
public bool IsPlayersTabFriendsVisible = true;
|
||||
public bool IsPlayersTabPartyVisible = true;
|
||||
public bool IsPlayersTabAllianceVisible = true;
|
||||
public bool IsPlayersTabEnemiesVisible = true;
|
||||
public bool IsPlayersTabOthersVisible = false;
|
||||
public bool IsGeneralOptionsAllTheSameEnabled = true;
|
||||
[JsonProperty]
|
||||
public int RootVersion { get; private set; } = DEFAULT_CONFIG_VERSION;
|
||||
public int Version { get; set; } = DEFAULT_CONFIG_VERSION;
|
||||
public bool IsVisible = false;
|
||||
|
||||
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
|
||||
public Dictionary<string, InheritableData> AllTagsChanges = [];
|
||||
public ZoneConfiguration<GeneralConfiguration> GeneralConfigs = new();
|
||||
public ZoneConfiguration<TagsConfiguration> TagsConfigs = new();
|
||||
public StatusIconPriorizerSettings StatusIconPriorizerSettings = new(true);
|
||||
public bool MoveStatusIconToNameplateTextIfPossible = true;
|
||||
public bool IsPlayerNameRandomlyGenerated = false;
|
||||
public bool IsCustomTagsContextMenuEnabled = true;
|
||||
public bool IsShowInheritedPropertiesEnabled = true;
|
||||
public bool IsPlayersTabOrderedByProximity = false;
|
||||
public bool IsPlayersTabSelfVisible = true;
|
||||
public bool IsPlayersTabFriendsVisible = true;
|
||||
public bool IsPlayersTabPartyVisible = true;
|
||||
public bool IsPlayersTabAllianceVisible = true;
|
||||
public bool IsPlayersTabEnemiesVisible = true;
|
||||
public bool IsPlayersTabOthersVisible = false;
|
||||
public bool IsSimpleUIEnabled = true;
|
||||
|
||||
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
|
||||
public Dictionary<string, InheritableData> AllRoleTagsChanges = [];
|
||||
#region Obsulate Properties
|
||||
|
||||
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
|
||||
public Dictionary<Role, Dictionary<string, InheritableData>> RoleTagsChanges = [];
|
||||
|
||||
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
|
||||
public Dictionary<DpsRole, Dictionary<string, InheritableData>> DpsRoleTagsChanges = [];
|
||||
|
||||
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
|
||||
public Dictionary<RangedDpsRole, Dictionary<string, InheritableData>> RangedDpsRoleTagsChanges = [];
|
||||
|
||||
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
|
||||
public Dictionary<LandHandRole, Dictionary<string, InheritableData>> LandHandRoleTagsChanges = [];
|
||||
|
||||
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
|
||||
public Dictionary<string, Dictionary<string, InheritableData>> JobTagsChanges = [];
|
||||
|
||||
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
|
||||
public Dictionary<string, InheritableData> AllCustomTagsChanges = [];
|
||||
|
||||
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
|
||||
public List<Dictionary<string, InheritableData>> CustomTagsChanges = [];
|
||||
|
||||
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
|
||||
public List<Identity> Identities = [];
|
||||
|
||||
#region Obsulate Properties
|
||||
|
||||
[Obsolete]
|
||||
[JsonProperty("GeneralOptions")]
|
||||
private Dictionary<Data.ActivityContext, GeneralOptionsClass> GeneralOptionsV1
|
||||
{
|
||||
set
|
||||
[JsonProperty("GeneralOptionsV2"), Obsolete]
|
||||
private Dictionary<ActivityType, GeneralConfiguration> GeneralOptionsV2
|
||||
{
|
||||
GeneralOptions.Clear();
|
||||
foreach (var kvp in value)
|
||||
GeneralOptions.Add((ActivityType)kvp.Key, kvp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty("NameplateFreeCompanyVisibility"), Obsolete]
|
||||
private NameplateFreeCompanyVisibility NameplateFreeCompanyVisibilityV1
|
||||
{
|
||||
set
|
||||
{
|
||||
foreach (var key in GeneralOptions.Keys)
|
||||
GeneralOptions[key].NameplateFreeCompanyVisibility = value;
|
||||
}
|
||||
}
|
||||
[JsonProperty("NameplateTitleVisibility"), Obsolete]
|
||||
public NameplateTitleVisibility NameplateTitleVisibilityV1
|
||||
{
|
||||
set
|
||||
{
|
||||
foreach (var key in GeneralOptions.Keys)
|
||||
GeneralOptions[key].NameplateTitleVisibility = value;
|
||||
}
|
||||
}
|
||||
[JsonProperty("NameplateTitlePosition"), Obsolete]
|
||||
public NameplateTitlePosition NameplateTitlePositionV1
|
||||
{
|
||||
set
|
||||
{
|
||||
foreach (var key in GeneralOptions.Keys)
|
||||
GeneralOptions[key].NameplateTitlePosition = value;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty("IsApplyTagsToAllChatMessagesEnabled"), Obsolete]
|
||||
private bool IsApplyTagsToAllChatMessagesEnabledV1
|
||||
{
|
||||
set
|
||||
{
|
||||
foreach (var key in GeneralOptions.Keys)
|
||||
GeneralOptions[key].IsApplyTagsToAllChatMessagesEnabled = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public event System.Action? Saved;
|
||||
|
||||
public void Save(PluginData pluginData)
|
||||
{
|
||||
AllTagsChanges = pluginData.AllTags.GetChanges(pluginData.Default.AllTags.GetChanges());
|
||||
AllRoleTagsChanges = pluginData.AllRoleTags.GetChanges(pluginData.Default.AllRoleTags.GetChanges());
|
||||
|
||||
RoleTagsChanges = [];
|
||||
foreach ((var role, var roleTag) in pluginData.RoleTags)
|
||||
{
|
||||
Dictionary<string, InheritableData>? defaultChanges = [];
|
||||
if (pluginData.Default.RoleTags.TryGetValue(role, out var defaultTag))
|
||||
set
|
||||
{
|
||||
defaultChanges = defaultTag.GetChanges();
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
var changes = roleTag.GetChanges(defaultChanges);
|
||||
if (changes.Any())
|
||||
{
|
||||
RoleTagsChanges[role] = changes;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
DpsRoleTagsChanges = [];
|
||||
foreach ((var dpsRole, var dpsRoleTag) in pluginData.DpsRoleTags)
|
||||
[JsonProperty]
|
||||
[Obsolete]
|
||||
public bool IsApplyToEverywhereEnabled
|
||||
{
|
||||
Dictionary<string, InheritableData>? defaultChanges = [];
|
||||
if (pluginData.Default.DpsRoleTags.TryGetValue(dpsRole, out var defaultTag))
|
||||
set
|
||||
{
|
||||
defaultChanges = defaultTag.GetChanges();
|
||||
}
|
||||
|
||||
var changes = dpsRoleTag.GetChanges(defaultChanges);
|
||||
if (changes.Any())
|
||||
{
|
||||
DpsRoleTagsChanges[dpsRole] = changes;
|
||||
GeneralConfigs.IsEverywhere = value;
|
||||
TagsConfigs.IsEverywhere = value;
|
||||
}
|
||||
}
|
||||
|
||||
RangedDpsRoleTagsChanges = [];
|
||||
foreach ((var rangedDpsRole, var rangedDpsRoleTag) in pluginData.RangedDpsRoleTags)
|
||||
[JsonProperty]
|
||||
[Obsolete]
|
||||
private bool IsGeneralOptionsAllTheSameEnabled
|
||||
{
|
||||
Dictionary<string, InheritableData>? defaultChanges = [];
|
||||
if (pluginData.Default.RangedDpsRoleTags.TryGetValue(rangedDpsRole, out var defaultTag))
|
||||
{
|
||||
defaultChanges = defaultTag.GetChanges();
|
||||
}
|
||||
|
||||
var changes = rangedDpsRoleTag.GetChanges(defaultChanges);
|
||||
if (changes.Any())
|
||||
{
|
||||
RangedDpsRoleTagsChanges[rangedDpsRole] = changes;
|
||||
}
|
||||
set => IsApplyToEverywhereEnabled = value;
|
||||
}
|
||||
|
||||
LandHandRoleTagsChanges = [];
|
||||
foreach ((var landHandRole, var landHandRoleTag) in pluginData.LandHandRoleTags)
|
||||
[JsonProperty("NameplateFreeCompanyVisibility"), Obsolete]
|
||||
private NameplateFreeCompanyVisibility NameplateFreeCompanyVisibilityV1
|
||||
{
|
||||
Dictionary<string, InheritableData>? defaultChanges = [];
|
||||
if (pluginData.Default.LandHandRoleTags.TryGetValue(landHandRole, out var defaultTag))
|
||||
set => GeneralConfigs.GetConfig(ZoneType.Everywhere).NameplateFreeCompanyVisibility = value;
|
||||
}
|
||||
[JsonProperty("NameplateTitleVisibility"), Obsolete]
|
||||
public NameplateTitleVisibility NameplateTitleVisibilityV1
|
||||
{
|
||||
set => GeneralConfigs.GetConfig(ZoneType.Everywhere).NameplateTitleVisibility = value;
|
||||
}
|
||||
[JsonProperty("NameplateTitlePosition"), Obsolete]
|
||||
public NameplateTitlePosition NameplateTitlePositionV1
|
||||
{
|
||||
set => GeneralConfigs.GetConfig(ZoneType.Everywhere).NameplateTitlePosition = value;
|
||||
}
|
||||
|
||||
[JsonProperty("IsApplyTagsToAllChatMessagesEnabled"), Obsolete]
|
||||
private bool IsApplyTagsToAllChatMessagesEnabledV1
|
||||
{
|
||||
set => GeneralConfigs.GetConfig(ZoneType.Everywhere).IsApplyTagsToAllChatMessagesEnabled = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public event System.Action? Saved;
|
||||
|
||||
public void Save(PluginData pluginData)
|
||||
{
|
||||
foreach (var tagConfig in TagsConfigs)
|
||||
tagConfig.Value.ApplyTagsData(pluginData.GetTagsData(tagConfig.Key));
|
||||
|
||||
SavePluginConfig();
|
||||
|
||||
Saved?.Invoke();
|
||||
}
|
||||
|
||||
public void SavePluginConfig()
|
||||
{
|
||||
Version = DEFAULT_CONFIG_VERSION;
|
||||
var configFilePath = GetConfigFilePath();
|
||||
var configFileContent = JsonConvert.SerializeObject(this, Formatting.Indented, GetJsonSettings());
|
||||
File.WriteAllText(configFilePath, configFileContent);
|
||||
}
|
||||
|
||||
public static PluginConfiguration LoadPluginConfig()
|
||||
{
|
||||
var configFilePath = GetConfigFilePath();
|
||||
object config = null;
|
||||
|
||||
if (File.Exists(configFilePath))
|
||||
{
|
||||
defaultChanges = defaultTag.GetChanges();
|
||||
var configFileContent = File.ReadAllText(configFilePath);
|
||||
config = JsonConvert.DeserializeObject<PluginConfiguration>(configFileContent, GetJsonSettings());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try loading the old settings, if possible
|
||||
configFilePath = PluginServices.DalamudPluginInterface.ConfigFile.FullName;
|
||||
config = PluginServices.DalamudPluginInterface.GetPluginConfig();
|
||||
}
|
||||
|
||||
var changes = landHandRoleTag.GetChanges(defaultChanges);
|
||||
if (changes.Any())
|
||||
return config as PluginConfiguration;
|
||||
}
|
||||
|
||||
private static string GetConfigFilePath()
|
||||
{
|
||||
return Path.Combine(PluginServices.DalamudPluginInterface.ConfigDirectory.FullName, "Config.json");
|
||||
}
|
||||
|
||||
private static JsonSerializerSettings GetJsonSettings()
|
||||
{
|
||||
var jsonSettings = new JsonSerializerSettings
|
||||
{
|
||||
LandHandRoleTagsChanges[landHandRole] = changes;
|
||||
}
|
||||
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple,
|
||||
TypeNameHandling = TypeNameHandling.Auto,
|
||||
};
|
||||
|
||||
jsonSettings.Converters.Add(new StringEnumConverter());
|
||||
|
||||
return jsonSettings;
|
||||
}
|
||||
|
||||
JobTagsChanges = [];
|
||||
foreach ((var jobAbbreviation, var jobTag) in pluginData.JobTags)
|
||||
{
|
||||
Dictionary<string, InheritableData>? defaultChanges = [];
|
||||
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 = [];
|
||||
foreach (var customTag in pluginData.CustomTags)
|
||||
{
|
||||
CustomTagsChanges.Add(customTag.GetChanges());
|
||||
}
|
||||
|
||||
Identities = pluginData.Identities;
|
||||
|
||||
SavePluginConfig();
|
||||
|
||||
Saved?.Invoke();
|
||||
}
|
||||
|
||||
private void SavePluginConfig()
|
||||
{
|
||||
Version = DEFAULT_CONFIG_VERSION;
|
||||
var configFilePath = GetConfigFilePath();
|
||||
var configFileContent = JsonConvert.SerializeObject(this, Formatting.Indented, GetJsonSettings());
|
||||
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();
|
||||
object config = null;
|
||||
|
||||
if (File.Exists(configFilePath))
|
||||
{
|
||||
var configFileContent = File.ReadAllText(configFilePath);
|
||||
config = JsonConvert.DeserializeObject<PluginConfiguration>(configFileContent, GetJsonSettings());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try loading the old settings, if possible
|
||||
configFilePath = PluginServices.DalamudPluginInterface.ConfigFile.FullName;
|
||||
config = PluginServices.DalamudPluginInterface.GetPluginConfig();
|
||||
}
|
||||
|
||||
if (config is PluginConfiguration pluginConfig)
|
||||
{
|
||||
if (PluginConfigFix(pluginConfig))
|
||||
{
|
||||
BackupPluginConfig();
|
||||
pluginConfig.SavePluginConfig();
|
||||
}
|
||||
}
|
||||
|
||||
return config as PluginConfiguration;
|
||||
}
|
||||
|
||||
private static string GetConfigFilePath()
|
||||
{
|
||||
return Path.Combine(PluginServices.DalamudPluginInterface.ConfigDirectory.FullName, "Config.json");
|
||||
}
|
||||
|
||||
private static JsonSerializerSettings GetJsonSettings()
|
||||
{
|
||||
var jsonSettings = new JsonSerializerSettings
|
||||
{
|
||||
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple,
|
||||
TypeNameHandling = TypeNameHandling.Auto,
|
||||
};
|
||||
|
||||
jsonSettings.Converters.Add(new StringEnumConverter());
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
142
PlayerTags/Configuration/TagsConfiguration.cs
Normal file
142
PlayerTags/Configuration/TagsConfiguration.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
28
PlayerTags/Configuration/ZoneConfiguration.cs
Normal file
28
PlayerTags/Configuration/ZoneConfiguration.cs
Normal 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];
|
||||
}
|
||||
}
|
||||
}
|
||||
13
PlayerTags/Configuration/ZoneConfigurationBase.cs
Normal file
13
PlayerTags/Configuration/ZoneConfigurationBase.cs
Normal 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
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,14 +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,
|
||||
}
|
||||
@@ -1,22 +1,23 @@
|
||||
using Pilz.Dalamud.ActivityContexts;
|
||||
|
||||
namespace PlayerTags.Data;
|
||||
|
||||
public static class ActivityContextHelper
|
||||
namespace PlayerTags.Data
|
||||
{
|
||||
public static bool GetIsVisible(ActivityType playerContext, bool desiredPveDutyVisibility, bool desiredPvpDutyVisibility, bool desiredOthersVisibility)
|
||||
public static class ActivityContextHelper
|
||||
{
|
||||
bool isVisible = false;
|
||||
public static bool GetIsVisible(ActivityType playerContext, bool desiredPveDutyVisibility, bool desiredPvpDutyVisibility, bool desiredOthersVisibility)
|
||||
{
|
||||
bool isVisible = false;
|
||||
|
||||
if (playerContext.HasFlag(ActivityType.PveDuty))
|
||||
isVisible |= desiredPveDutyVisibility;
|
||||
if (playerContext.HasFlag(ActivityType.PveDuty))
|
||||
isVisible |= desiredPveDutyVisibility;
|
||||
|
||||
if (playerContext.HasFlag(ActivityType.PvpDuty))
|
||||
isVisible |= desiredPvpDutyVisibility;
|
||||
if (playerContext.HasFlag(ActivityType.PvpDuty))
|
||||
isVisible |= desiredPvpDutyVisibility;
|
||||
|
||||
if (playerContext == ActivityType.None)
|
||||
isVisible |= desiredOthersVisibility;
|
||||
if (playerContext == ActivityType.None)
|
||||
isVisible |= desiredOthersVisibility;
|
||||
|
||||
return isVisible;
|
||||
return isVisible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
namespace PlayerTags.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public enum DeadPlayerHandling
|
||||
namespace PlayerTags.Data
|
||||
{
|
||||
Ignore,
|
||||
Include,
|
||||
GrayOut
|
||||
public enum DeadPlayerHandling
|
||||
{
|
||||
Ignore,
|
||||
Include,
|
||||
GrayOut
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,492 +1,492 @@
|
||||
using Dalamud.Game.Text;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Lumina.Excel.Sheets;
|
||||
using Pilz.Dalamud;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace PlayerTags.Data;
|
||||
|
||||
public class DefaultPluginData
|
||||
namespace PlayerTags.Data
|
||||
{
|
||||
public Tag AllTags { get; private set; }
|
||||
|
||||
public Tag AllRoleTags { get; private set; }
|
||||
public Dictionary<Role, Tag> RoleTags { get; private set; }
|
||||
public Dictionary<DpsRole, Tag> DpsRoleTags { get; private set; }
|
||||
public Dictionary<RangedDpsRole, Tag> RangedDpsRoleTags { get; private set; }
|
||||
public Dictionary<LandHandRole, Tag> LandHandRoleTags { get; private set; }
|
||||
public Dictionary<string, Tag> JobTags { get; private set; }
|
||||
|
||||
public Tag AllCustomTags { get; private set; }
|
||||
|
||||
public DefaultPluginData(DefaultPluginDataTemplate template)
|
||||
public class DefaultPluginData
|
||||
{
|
||||
SetupTemplate(template);
|
||||
}
|
||||
public Tag AllTags { get; private set; }
|
||||
|
||||
private void SetupTemplate(DefaultPluginDataTemplate template)
|
||||
{
|
||||
Clear();
|
||||
public Tag AllRoleTags { get; private set; }
|
||||
public Dictionary<Role, Tag> RoleTags { get; private set; }
|
||||
public Dictionary<DpsRole, Tag> DpsRoleTags { get; private set; }
|
||||
public Dictionary<RangedDpsRole, Tag> RangedDpsRoleTags { get; private set; }
|
||||
public Dictionary<LandHandRole, Tag> LandHandRoleTags { get; private set; }
|
||||
public Dictionary<string, Tag> JobTags { get; private set; }
|
||||
|
||||
switch (template)
|
||||
public Tag AllCustomTags { get; private set; }
|
||||
|
||||
public DefaultPluginData(DefaultPluginDataTemplate template)
|
||||
{
|
||||
case DefaultPluginDataTemplate.None:
|
||||
SetupTemplateNone();
|
||||
break;
|
||||
case DefaultPluginDataTemplate.Basic:
|
||||
SetupTemplateBasic();
|
||||
break;
|
||||
case DefaultPluginDataTemplate.Simple:
|
||||
SetupTemplateSimple();
|
||||
break;
|
||||
case DefaultPluginDataTemplate.Full:
|
||||
SetupTemplateFull();
|
||||
break;
|
||||
SetupTemplate(template);
|
||||
}
|
||||
|
||||
SetupJobTags();
|
||||
}
|
||||
|
||||
private void Clear()
|
||||
{
|
||||
RoleTags = [];
|
||||
DpsRoleTags = [];
|
||||
RangedDpsRoleTags = [];
|
||||
LandHandRoleTags = [];
|
||||
}
|
||||
|
||||
private void SetupTemplateNone()
|
||||
{
|
||||
AllTags = new Tag()
|
||||
private void SetupTemplate(DefaultPluginDataTemplate template)
|
||||
{
|
||||
IsSelected = true,
|
||||
IsExpanded = true,
|
||||
};
|
||||
Clear();
|
||||
|
||||
AllRoleTags = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
};
|
||||
|
||||
RoleTags[Role.LandHand] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
RoleTags[Role.Tank] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
RoleTags[Role.Healer] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
RoleTags[Role.Dps] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
};
|
||||
|
||||
DpsRoleTags[DpsRole.Melee] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
DpsRoleTags[DpsRole.Ranged] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
};
|
||||
|
||||
RangedDpsRoleTags[RangedDpsRole.Magical] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
RangedDpsRoleTags[RangedDpsRole.Physical] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
LandHandRoleTags[LandHandRole.Land] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
LandHandRoleTags[LandHandRole.Hand] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
AllCustomTags = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
};
|
||||
}
|
||||
|
||||
private void SetupTemplateBasic()
|
||||
{
|
||||
AllTags = new Tag()
|
||||
{
|
||||
IsSelected = true,
|
||||
IsExpanded = true,
|
||||
|
||||
TagPositionInChat = TagPosition.Before,
|
||||
InsertBehindNumberPrefixInChat = true,
|
||||
TagPositionInNameplates = TagPosition.Replace,
|
||||
TagTargetInNameplates = NameplateElement.Title,
|
||||
|
||||
TargetChatTypes = new List<XivChatType>(Enum.GetValues<XivChatType>()),
|
||||
TargetChatTypesIncludeUndefined = true,
|
||||
};
|
||||
|
||||
AllRoleTags = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
};
|
||||
|
||||
RoleTags[Role.LandHand] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false
|
||||
};
|
||||
|
||||
RoleTags[Role.Tank] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
Icon = BitmapFontIcon.Tank,
|
||||
TextColor = 546,
|
||||
};
|
||||
|
||||
RoleTags[Role.Healer] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
Icon = BitmapFontIcon.Healer,
|
||||
TextColor = 43,
|
||||
};
|
||||
|
||||
RoleTags[Role.Dps] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
Icon = BitmapFontIcon.DPS,
|
||||
TextColor = 508,
|
||||
};
|
||||
|
||||
DpsRoleTags[DpsRole.Melee] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
DpsRoleTags[DpsRole.Ranged] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
};
|
||||
|
||||
RangedDpsRoleTags[RangedDpsRole.Magical] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
RangedDpsRoleTags[RangedDpsRole.Physical] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
LandHandRoleTags[LandHandRole.Land] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
LandHandRoleTags[LandHandRole.Hand] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
AllCustomTags = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
IsTextVisibleInChat = true,
|
||||
IsTextVisibleInNameplates = true,
|
||||
};
|
||||
}
|
||||
|
||||
private void SetupTemplateSimple()
|
||||
{
|
||||
AllTags = new Tag()
|
||||
{
|
||||
IsSelected = true,
|
||||
IsExpanded = true,
|
||||
TagPositionInChat = TagPosition.Before,
|
||||
InsertBehindNumberPrefixInChat = true,
|
||||
TagPositionInNameplates = TagPosition.Replace,
|
||||
TagTargetInNameplates = NameplateElement.Title,
|
||||
IsTextItalic = true,
|
||||
|
||||
IsVisibleInOverworld = true,
|
||||
IsVisibleInPveDuties = true,
|
||||
IsVisibleInPvpDuties = true,
|
||||
|
||||
IsVisibleForSelf = true,
|
||||
IsVisibleForFriendPlayers = true,
|
||||
IsVisibleForPartyPlayers = true,
|
||||
IsVisibleForAlliancePlayers = true,
|
||||
IsVisibleForEnemyPlayers = true,
|
||||
IsVisibleForOtherPlayers = true,
|
||||
|
||||
TargetChatTypes = new List<XivChatType>(Enum.GetValues<XivChatType>()),
|
||||
TargetChatTypesIncludeUndefined = true,
|
||||
};
|
||||
|
||||
AllRoleTags = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
IsRoleIconVisibleInChat = true,
|
||||
IsTextVisibleInChat = true,
|
||||
IsRoleIconVisibleInNameplates = true,
|
||||
IsTextVisibleInNameplates = true,
|
||||
IsTextColorAppliedToChatName = true,
|
||||
};
|
||||
|
||||
RoleTags[Role.LandHand] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
Icon = BitmapFontIcon.Crafter,
|
||||
TextColor = 3,
|
||||
};
|
||||
|
||||
RoleTags[Role.Tank] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
Icon = BitmapFontIcon.Tank,
|
||||
TextColor = 546,
|
||||
};
|
||||
|
||||
RoleTags[Role.Healer] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
Icon = BitmapFontIcon.Healer,
|
||||
TextColor = 43,
|
||||
};
|
||||
|
||||
RoleTags[Role.Dps] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
Icon = BitmapFontIcon.DPS,
|
||||
TextColor = 508,
|
||||
};
|
||||
|
||||
DpsRoleTags[DpsRole.Melee] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
DpsRoleTags[DpsRole.Ranged] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
};
|
||||
|
||||
RangedDpsRoleTags[RangedDpsRole.Magical] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
RangedDpsRoleTags[RangedDpsRole.Physical] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
LandHandRoleTags[LandHandRole.Land] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
LandHandRoleTags[LandHandRole.Hand] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
AllCustomTags = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
IsTextVisibleInChat = true,
|
||||
IsTextVisibleInNameplates = true,
|
||||
};
|
||||
}
|
||||
|
||||
private void SetupTemplateFull()
|
||||
{
|
||||
AllTags = new Tag()
|
||||
{
|
||||
IsSelected = true,
|
||||
IsExpanded = true,
|
||||
TagPositionInChat = TagPosition.Before,
|
||||
InsertBehindNumberPrefixInChat = true,
|
||||
TagPositionInNameplates = TagPosition.Replace,
|
||||
TagTargetInNameplates = NameplateElement.Title,
|
||||
IsTextItalic = true,
|
||||
|
||||
IsVisibleInOverworld = true,
|
||||
IsVisibleInPveDuties = true,
|
||||
IsVisibleInPvpDuties = true,
|
||||
|
||||
IsVisibleForSelf = true,
|
||||
IsVisibleForFriendPlayers = true,
|
||||
IsVisibleForPartyPlayers = true,
|
||||
IsVisibleForAlliancePlayers = true,
|
||||
IsVisibleForEnemyPlayers = true,
|
||||
IsVisibleForOtherPlayers = true,
|
||||
|
||||
TargetChatTypes = new List<XivChatType>(Enum.GetValues<XivChatType>()),
|
||||
TargetChatTypesIncludeUndefined = true,
|
||||
};
|
||||
|
||||
AllRoleTags = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
IsRoleIconVisibleInChat = true,
|
||||
IsTextVisibleInChat = true,
|
||||
IsRoleIconVisibleInNameplates = true,
|
||||
IsTextVisibleInNameplates = true,
|
||||
IsTextColorAppliedToNameplateName = true,
|
||||
IsTextColorAppliedToChatName = true,
|
||||
IsJobIconVisibleInNameplates = true,
|
||||
};
|
||||
|
||||
RoleTags[Role.LandHand] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
Icon = BitmapFontIcon.Crafter,
|
||||
TextColor = 3,
|
||||
};
|
||||
|
||||
RoleTags[Role.Tank] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
Icon = BitmapFontIcon.Tank,
|
||||
TextColor = 546,
|
||||
};
|
||||
|
||||
RoleTags[Role.Healer] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
Icon = BitmapFontIcon.Healer,
|
||||
TextColor = 43,
|
||||
};
|
||||
|
||||
RoleTags[Role.Dps] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
Icon = BitmapFontIcon.DPS,
|
||||
TextColor = 508,
|
||||
};
|
||||
|
||||
DpsRoleTags[DpsRole.Melee] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
DpsRoleTags[DpsRole.Ranged] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
};
|
||||
|
||||
RangedDpsRoleTags[RangedDpsRole.Magical] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
RangedDpsRoleTags[RangedDpsRole.Physical] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
LandHandRoleTags[LandHandRole.Land] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
LandHandRoleTags[LandHandRole.Hand] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
AllCustomTags = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
IsTextVisibleInChat = true,
|
||||
IsTextVisibleInNameplates = true,
|
||||
};
|
||||
}
|
||||
|
||||
private void SetupJobTags()
|
||||
{
|
||||
JobTags = [];
|
||||
|
||||
var classJobs = PluginServices.DataManager.GetExcelSheet<ClassJob>();
|
||||
if (classJobs != null)
|
||||
{
|
||||
foreach ((var role, var roleTagChanges) in RoleTags)
|
||||
switch(template)
|
||||
{
|
||||
foreach (var classJob in classJobs.Where(classJob => RoleHelper.RolesByRoleId[classJob.Role] == role && !string.IsNullOrEmpty(classJob.Abbreviation.ParseString())))
|
||||
case DefaultPluginDataTemplate.None:
|
||||
SetupTemplateNone();
|
||||
break;
|
||||
case DefaultPluginDataTemplate.Basic:
|
||||
SetupTemplateBasic();
|
||||
break;
|
||||
case DefaultPluginDataTemplate.Simple:
|
||||
SetupTemplateSimple();
|
||||
break;
|
||||
case DefaultPluginDataTemplate.Full:
|
||||
SetupTemplateFull();
|
||||
break;
|
||||
}
|
||||
|
||||
SetupJobTags();
|
||||
}
|
||||
|
||||
private void Clear()
|
||||
{
|
||||
|
||||
RoleTags = new Dictionary<Role, Tag>();
|
||||
DpsRoleTags = new Dictionary<DpsRole, Tag>();
|
||||
RangedDpsRoleTags = new Dictionary<RangedDpsRole, Tag>();
|
||||
LandHandRoleTags = new Dictionary<LandHandRole, Tag>();
|
||||
}
|
||||
|
||||
private void SetupTemplateNone()
|
||||
{
|
||||
AllTags = new Tag()
|
||||
{
|
||||
IsSelected = true,
|
||||
IsExpanded = true,
|
||||
};
|
||||
|
||||
AllRoleTags = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
};
|
||||
|
||||
RoleTags[Role.LandHand] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
RoleTags[Role.Tank] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
RoleTags[Role.Healer] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
RoleTags[Role.Dps] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
};
|
||||
|
||||
DpsRoleTags[DpsRole.Melee] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
DpsRoleTags[DpsRole.Ranged] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
};
|
||||
|
||||
RangedDpsRoleTags[RangedDpsRole.Magical] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
RangedDpsRoleTags[RangedDpsRole.Physical] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
LandHandRoleTags[LandHandRole.Land] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
LandHandRoleTags[LandHandRole.Hand] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
AllCustomTags = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
};
|
||||
}
|
||||
|
||||
private void SetupTemplateBasic()
|
||||
{
|
||||
AllTags = new Tag()
|
||||
{
|
||||
IsSelected = true,
|
||||
IsExpanded = true,
|
||||
|
||||
TagPositionInChat = TagPosition.Before,
|
||||
InsertBehindNumberPrefixInChat = true,
|
||||
TagPositionInNameplates = TagPosition.Replace,
|
||||
TagTargetInNameplates = NameplateElement.Title,
|
||||
|
||||
TargetChatTypes = new List<XivChatType>(Enum.GetValues<XivChatType>()),
|
||||
TargetChatTypesIncludeUndefined = true,
|
||||
};
|
||||
|
||||
AllRoleTags = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
};
|
||||
|
||||
RoleTags[Role.LandHand] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false
|
||||
};
|
||||
|
||||
RoleTags[Role.Tank] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
Icon = BitmapFontIcon.Tank,
|
||||
TextColor = 546,
|
||||
};
|
||||
|
||||
RoleTags[Role.Healer] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
Icon = BitmapFontIcon.Healer,
|
||||
TextColor = 43,
|
||||
};
|
||||
|
||||
RoleTags[Role.Dps] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
Icon = BitmapFontIcon.DPS,
|
||||
TextColor = 508,
|
||||
};
|
||||
|
||||
DpsRoleTags[DpsRole.Melee] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
DpsRoleTags[DpsRole.Ranged] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
};
|
||||
|
||||
RangedDpsRoleTags[RangedDpsRole.Magical] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
RangedDpsRoleTags[RangedDpsRole.Physical] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
LandHandRoleTags[LandHandRole.Land] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
LandHandRoleTags[LandHandRole.Hand] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
AllCustomTags = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
IsTextVisibleInChat = true,
|
||||
IsTextVisibleInNameplates = true,
|
||||
};
|
||||
}
|
||||
|
||||
private void SetupTemplateSimple()
|
||||
{
|
||||
AllTags = new Tag()
|
||||
{
|
||||
IsSelected = true,
|
||||
IsExpanded = true,
|
||||
TagPositionInChat = TagPosition.Before,
|
||||
InsertBehindNumberPrefixInChat = true,
|
||||
TagPositionInNameplates = TagPosition.Replace,
|
||||
TagTargetInNameplates = NameplateElement.Title,
|
||||
IsTextItalic = true,
|
||||
|
||||
IsVisibleInOverworld = true,
|
||||
IsVisibleInPveDuties = true,
|
||||
IsVisibleInPvpDuties = true,
|
||||
|
||||
IsVisibleForSelf = true,
|
||||
IsVisibleForFriendPlayers = true,
|
||||
IsVisibleForPartyPlayers = true,
|
||||
IsVisibleForAlliancePlayers = true,
|
||||
IsVisibleForEnemyPlayers = true,
|
||||
IsVisibleForOtherPlayers = true,
|
||||
|
||||
TargetChatTypes = new List<XivChatType>(Enum.GetValues<XivChatType>()),
|
||||
TargetChatTypesIncludeUndefined = true,
|
||||
};
|
||||
|
||||
AllRoleTags = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
IsRoleIconVisibleInChat = true,
|
||||
IsTextVisibleInChat = true,
|
||||
IsRoleIconVisibleInNameplates = true,
|
||||
IsTextVisibleInNameplates = true,
|
||||
IsTextColorAppliedToChatName = true,
|
||||
};
|
||||
|
||||
RoleTags[Role.LandHand] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
Icon = BitmapFontIcon.Crafter,
|
||||
TextColor = 3,
|
||||
};
|
||||
|
||||
RoleTags[Role.Tank] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
Icon = BitmapFontIcon.Tank,
|
||||
TextColor = 546,
|
||||
};
|
||||
|
||||
RoleTags[Role.Healer] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
Icon = BitmapFontIcon.Healer,
|
||||
TextColor = 43,
|
||||
};
|
||||
|
||||
RoleTags[Role.Dps] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
Icon = BitmapFontIcon.DPS,
|
||||
TextColor = 508,
|
||||
};
|
||||
|
||||
DpsRoleTags[DpsRole.Melee] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
DpsRoleTags[DpsRole.Ranged] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
};
|
||||
|
||||
RangedDpsRoleTags[RangedDpsRole.Magical] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
RangedDpsRoleTags[RangedDpsRole.Physical] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
LandHandRoleTags[LandHandRole.Land] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
LandHandRoleTags[LandHandRole.Hand] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
AllCustomTags = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
IsTextVisibleInChat = true,
|
||||
IsTextVisibleInNameplates = true,
|
||||
};
|
||||
}
|
||||
|
||||
private void SetupTemplateFull()
|
||||
{
|
||||
AllTags = new Tag()
|
||||
{
|
||||
IsSelected = true,
|
||||
IsExpanded = true,
|
||||
TagPositionInChat = TagPosition.Before,
|
||||
InsertBehindNumberPrefixInChat = true,
|
||||
TagPositionInNameplates = TagPosition.Replace,
|
||||
TagTargetInNameplates = NameplateElement.Title,
|
||||
IsTextItalic = true,
|
||||
|
||||
IsVisibleInOverworld = true,
|
||||
IsVisibleInPveDuties = true,
|
||||
IsVisibleInPvpDuties = true,
|
||||
|
||||
IsVisibleForSelf = true,
|
||||
IsVisibleForFriendPlayers = true,
|
||||
IsVisibleForPartyPlayers = true,
|
||||
IsVisibleForAlliancePlayers = true,
|
||||
IsVisibleForEnemyPlayers = true,
|
||||
IsVisibleForOtherPlayers = true,
|
||||
|
||||
TargetChatTypes = new List<XivChatType>(Enum.GetValues<XivChatType>()),
|
||||
TargetChatTypesIncludeUndefined = true,
|
||||
};
|
||||
|
||||
AllRoleTags = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
IsRoleIconVisibleInChat = true,
|
||||
IsTextVisibleInChat = true,
|
||||
IsRoleIconVisibleInNameplates = true,
|
||||
IsTextVisibleInNameplates = true,
|
||||
IsTextColorAppliedToNameplateName = true,
|
||||
IsTextColorAppliedToChatName = true,
|
||||
IsJobIconVisibleInNameplates = true,
|
||||
};
|
||||
|
||||
RoleTags[Role.LandHand] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
Icon = BitmapFontIcon.Crafter,
|
||||
TextColor = 3,
|
||||
};
|
||||
|
||||
RoleTags[Role.Tank] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
Icon = BitmapFontIcon.Tank,
|
||||
TextColor = 546,
|
||||
};
|
||||
|
||||
RoleTags[Role.Healer] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
Icon = BitmapFontIcon.Healer,
|
||||
TextColor = 43,
|
||||
};
|
||||
|
||||
RoleTags[Role.Dps] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
Icon = BitmapFontIcon.DPS,
|
||||
TextColor = 508,
|
||||
};
|
||||
|
||||
DpsRoleTags[DpsRole.Melee] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
DpsRoleTags[DpsRole.Ranged] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
};
|
||||
|
||||
RangedDpsRoleTags[RangedDpsRole.Magical] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
RangedDpsRoleTags[RangedDpsRole.Physical] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
LandHandRoleTags[LandHandRole.Land] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
LandHandRoleTags[LandHandRole.Hand] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
};
|
||||
|
||||
AllCustomTags = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = true,
|
||||
IsTextVisibleInChat = true,
|
||||
IsTextVisibleInNameplates = true,
|
||||
};
|
||||
}
|
||||
|
||||
private void SetupJobTags()
|
||||
{
|
||||
JobTags = new Dictionary<string, Tag>();
|
||||
|
||||
var classJobs = PluginServices.DataManager.GetExcelSheet<ClassJob>();
|
||||
if (classJobs != null)
|
||||
{
|
||||
foreach ((var role, var roleTagChanges) in RoleTags)
|
||||
{
|
||||
var abbrv = classJob.Abbreviation.ParseString();
|
||||
if (!JobTags.ContainsKey(abbrv))
|
||||
foreach (var classJob in classJobs.Where(classJob => RoleHelper.RolesByRoleId[classJob.Role] == role && !string.IsNullOrEmpty(classJob.Abbreviation.RawString)))
|
||||
{
|
||||
JobTags[abbrv] = new Tag()
|
||||
if (!JobTags.ContainsKey(classJob.Abbreviation.RawString))
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
Text = abbrv,
|
||||
};
|
||||
JobTags[classJob.Abbreviation.RawString] = new Tag()
|
||||
{
|
||||
IsSelected = false,
|
||||
IsExpanded = false,
|
||||
Text = classJob.Abbreviation.RawString,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
namespace PlayerTags.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public enum DefaultPluginDataTemplate
|
||||
namespace PlayerTags.Data
|
||||
{
|
||||
None,
|
||||
Basic,
|
||||
Simple,
|
||||
Full
|
||||
public enum DefaultPluginDataTemplate
|
||||
{
|
||||
None,
|
||||
Basic,
|
||||
Simple,
|
||||
Full
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
namespace PlayerTags.Data;
|
||||
|
||||
public enum DpsRole
|
||||
namespace PlayerTags.Data
|
||||
{
|
||||
Melee,
|
||||
Ranged
|
||||
public enum DpsRole
|
||||
{
|
||||
Melee,
|
||||
Ranged
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,99 +2,100 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PlayerTags.Data;
|
||||
|
||||
public class Identity : IComparable<Identity>, IEquatable<Identity>
|
||||
namespace PlayerTags.Data
|
||||
{
|
||||
public string Name { get; init; }
|
||||
public uint? WorldId { get; set; } = null;
|
||||
public List<Guid> CustomTagIds { get; init; } = [];
|
||||
|
||||
[JsonIgnore]
|
||||
public string? WorldName => WorldHelper.GetWorldName(WorldId);
|
||||
|
||||
public Identity(string name)
|
||||
public class Identity : IComparable<Identity>, IEquatable<Identity>
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
public string Name { get; init; }
|
||||
public uint? WorldId { get; set; } = null;
|
||||
public List<Guid> CustomTagIds { get; init; } = new List<Guid>();
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string str = Name;
|
||||
[JsonIgnore]
|
||||
public string? WorldName => WorldHelper.GetWorldName(WorldId);
|
||||
|
||||
if (WorldId != null)
|
||||
public Identity(string name)
|
||||
{
|
||||
str += $"@{WorldName}";
|
||||
Name = name;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
public int CompareTo(Identity? other)
|
||||
{
|
||||
string? otherToString = null;
|
||||
if (!(other is null))
|
||||
public override string ToString()
|
||||
{
|
||||
otherToString = other.ToString();
|
||||
string str = Name;
|
||||
|
||||
if (WorldId != null)
|
||||
{
|
||||
str += $"@{WorldName}";
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
return ToString().CompareTo(otherToString);
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is Identity identity && Equals(identity);
|
||||
}
|
||||
|
||||
public bool Equals(Identity? obj)
|
||||
{
|
||||
if (obj is null)
|
||||
public int CompareTo(Identity? other)
|
||||
{
|
||||
return false;
|
||||
string? otherToString = null;
|
||||
if (!(other is null))
|
||||
{
|
||||
otherToString = other.ToString();
|
||||
}
|
||||
|
||||
return ToString().CompareTo(otherToString);
|
||||
}
|
||||
|
||||
return this == obj;
|
||||
}
|
||||
|
||||
public static bool operator ==(Identity? first, Identity? second)
|
||||
{
|
||||
if (ReferenceEquals(first, second))
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return true;
|
||||
return obj is Identity identity && Equals(identity);
|
||||
}
|
||||
|
||||
if (first is null && second is null)
|
||||
public bool Equals(Identity? obj)
|
||||
{
|
||||
return true;
|
||||
if (obj is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return this == obj;
|
||||
}
|
||||
|
||||
if (first is null || second is null)
|
||||
public static bool operator ==(Identity? first, Identity? second)
|
||||
{
|
||||
return false;
|
||||
if (ReferenceEquals(first, second))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (first is null && second is null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (first is null || second is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool areNamesEqual = first.Name.ToLower().Trim() == second.Name.ToLower().Trim();
|
||||
|
||||
// If one of the worlds are null then it's technically equal as it could be promoted to the identity that does have a world
|
||||
bool areWorldsEqual = first.WorldId == null || second.WorldId == null || first.WorldId == second.WorldId;
|
||||
|
||||
return areNamesEqual && areWorldsEqual;
|
||||
}
|
||||
|
||||
bool areNamesEqual = first.Name.ToLower().Trim() == second.Name.ToLower().Trim();
|
||||
|
||||
// If one of the worlds are null then it's technically equal as it could be promoted to the identity that does have a world
|
||||
bool areWorldsEqual = first.WorldId == null || second.WorldId == null || first.WorldId == second.WorldId;
|
||||
|
||||
return areNamesEqual && areWorldsEqual;
|
||||
}
|
||||
|
||||
public static bool operator !=(Identity? first, Identity? second)
|
||||
{
|
||||
return !(first == second);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hashCode = Name.GetHashCode();
|
||||
|
||||
if (WorldName != null)
|
||||
public static bool operator !=(Identity? first, Identity? second)
|
||||
{
|
||||
hashCode *= 17 ^ WorldName.GetHashCode();
|
||||
return !(first == second);
|
||||
}
|
||||
|
||||
return hashCode;
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hashCode = Name.GetHashCode();
|
||||
|
||||
if (WorldName != null)
|
||||
{
|
||||
hashCode *= 17 ^ WorldName.GetHashCode();
|
||||
}
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace PlayerTags.Data;
|
||||
|
||||
public class InheritableCategoryAttribute : Attribute
|
||||
namespace PlayerTags.Data
|
||||
{
|
||||
public string CategoryId { get; private set; }
|
||||
|
||||
public InheritableCategoryAttribute(string categoryId)
|
||||
public class InheritableCategoryAttribute : Attribute
|
||||
{
|
||||
CategoryId = categoryId;
|
||||
public string CategoryId { get; private set; }
|
||||
|
||||
public InheritableCategoryAttribute(string categoryId)
|
||||
{
|
||||
CategoryId = categoryId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
namespace PlayerTags.Data;
|
||||
|
||||
public enum LandHandRole
|
||||
namespace PlayerTags.Data
|
||||
{
|
||||
Land,
|
||||
Hand
|
||||
public enum LandHandRole
|
||||
{
|
||||
Land,
|
||||
Hand
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
namespace PlayerTags.Data;
|
||||
|
||||
public enum NameplateElement
|
||||
namespace PlayerTags.Data
|
||||
{
|
||||
Name,
|
||||
Title,
|
||||
FreeCompany,
|
||||
public enum NameplateElement
|
||||
{
|
||||
Name,
|
||||
Title,
|
||||
FreeCompany
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PlayerTags.Data;
|
||||
|
||||
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
|
||||
public enum NameplateFreeCompanyVisibility
|
||||
namespace PlayerTags.Data
|
||||
{
|
||||
Default,
|
||||
Never
|
||||
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
|
||||
public enum NameplateFreeCompanyVisibility
|
||||
{
|
||||
Default,
|
||||
Never
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PlayerTags.Data;
|
||||
|
||||
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
|
||||
public enum NameplateTitlePosition
|
||||
namespace PlayerTags.Data
|
||||
{
|
||||
Default,
|
||||
AlwaysAboveName,
|
||||
AlwaysBelowName
|
||||
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
|
||||
public enum NameplateTitlePosition
|
||||
{
|
||||
Default,
|
||||
AlwaysAboveName,
|
||||
AlwaysBelowName
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PlayerTags.Data;
|
||||
|
||||
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
|
||||
public enum NameplateTitleVisibility
|
||||
namespace PlayerTags.Data
|
||||
{
|
||||
Default,
|
||||
Always,
|
||||
Never,
|
||||
WhenHasTags
|
||||
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
|
||||
public enum NameplateTitleVisibility
|
||||
{
|
||||
Default,
|
||||
Always,
|
||||
Never,
|
||||
WhenHasTags
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
namespace PlayerTags.Data;
|
||||
|
||||
public enum PlayerContext
|
||||
namespace PlayerTags.Data
|
||||
{
|
||||
None = 0x0,
|
||||
Self = 0x1,
|
||||
Party = 0x2,
|
||||
Alliance = 0x4,
|
||||
Enemy = 0x8,
|
||||
Friend = 0x10
|
||||
public enum PlayerContext
|
||||
{
|
||||
None = 0x0,
|
||||
Self = 0x1,
|
||||
Party = 0x2,
|
||||
Alliance = 0x4,
|
||||
Enemy = 0x8,
|
||||
Friend = 0x10
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,59 +1,81 @@
|
||||
using Dalamud.Game.ClientState.Objects.Enums;
|
||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||
|
||||
namespace PlayerTags.Data;
|
||||
|
||||
public static class PlayerContextHelper
|
||||
namespace PlayerTags.Data
|
||||
{
|
||||
public static PlayerContext GetPlayerContext(IPlayerCharacter playerCharacter)
|
||||
public static class PlayerContextHelper
|
||||
{
|
||||
var playerContext = PlayerContext.None;
|
||||
public static PlayerContext GetPlayerContext(PlayerCharacter playerCharacter)
|
||||
{
|
||||
PlayerContext playerContext = PlayerContext.None;
|
||||
|
||||
if (PluginServices.ClientState.LocalPlayer == playerCharacter)
|
||||
playerContext |= PlayerContext.Self;
|
||||
if (PluginServices.ClientState.LocalPlayer == playerCharacter)
|
||||
{
|
||||
playerContext |= PlayerContext.Self;
|
||||
}
|
||||
|
||||
if (playerCharacter.StatusFlags.HasFlag(StatusFlags.Friend))
|
||||
playerContext |= PlayerContext.Friend;
|
||||
if (playerCharacter.StatusFlags.HasFlag(StatusFlags.Friend))
|
||||
{
|
||||
playerContext |= PlayerContext.Friend;
|
||||
}
|
||||
|
||||
if (playerCharacter.StatusFlags.HasFlag(StatusFlags.PartyMember))
|
||||
playerContext |= PlayerContext.Party;
|
||||
if (playerCharacter.StatusFlags.HasFlag(StatusFlags.PartyMember))
|
||||
{
|
||||
playerContext |= PlayerContext.Party;
|
||||
}
|
||||
|
||||
if (playerCharacter.StatusFlags.HasFlag(StatusFlags.AllianceMember))
|
||||
playerContext |= PlayerContext.Alliance;
|
||||
if (playerCharacter.StatusFlags.HasFlag(StatusFlags.AllianceMember))
|
||||
{
|
||||
playerContext |= PlayerContext.Alliance;
|
||||
}
|
||||
|
||||
if (playerCharacter.StatusFlags.HasFlag(StatusFlags.Hostile))
|
||||
playerContext |= PlayerContext.Enemy;
|
||||
if (playerCharacter.StatusFlags.HasFlag(StatusFlags.Hostile))
|
||||
{
|
||||
playerContext |= PlayerContext.Enemy;
|
||||
}
|
||||
|
||||
return playerContext;
|
||||
}
|
||||
return playerContext;
|
||||
}
|
||||
|
||||
public static bool GetIsVisible(PlayerContext playerContext, bool desiredSelfVisibility, bool desiredFriendsVisibility, bool desiredPartyVisibility, bool desiredAllianceVisibility, bool desiredEnemiesVisibility, bool desiredOthersVisibility)
|
||||
{
|
||||
if (playerContext.HasFlag(PlayerContext.Self))
|
||||
return desiredSelfVisibility;
|
||||
public static bool GetIsVisible(PlayerContext playerContext, bool desiredSelfVisibility, bool desiredFriendsVisibility, bool desiredPartyVisibility, bool desiredAllianceVisibility, bool desiredEnemiesVisibility, bool desiredOthersVisibility)
|
||||
{
|
||||
if (playerContext.HasFlag(PlayerContext.Self))
|
||||
{
|
||||
return desiredSelfVisibility;
|
||||
}
|
||||
|
||||
var isVisible = false;
|
||||
bool isVisible = false;
|
||||
if (playerContext.HasFlag(PlayerContext.Friend))
|
||||
{
|
||||
isVisible |= desiredFriendsVisibility;
|
||||
}
|
||||
|
||||
if (playerContext.HasFlag(PlayerContext.Friend))
|
||||
isVisible |= desiredFriendsVisibility;
|
||||
if (playerContext.HasFlag(PlayerContext.Party))
|
||||
{
|
||||
isVisible |= desiredPartyVisibility;
|
||||
}
|
||||
|
||||
if (playerContext.HasFlag(PlayerContext.Party))
|
||||
isVisible |= desiredPartyVisibility;
|
||||
if (!playerContext.HasFlag(PlayerContext.Party) && playerContext.HasFlag(PlayerContext.Alliance))
|
||||
{
|
||||
isVisible |= desiredAllianceVisibility;
|
||||
}
|
||||
|
||||
if (!playerContext.HasFlag(PlayerContext.Party) && playerContext.HasFlag(PlayerContext.Alliance))
|
||||
isVisible |= desiredAllianceVisibility;
|
||||
if (playerContext.HasFlag(PlayerContext.Enemy))
|
||||
{
|
||||
isVisible |= desiredEnemiesVisibility;
|
||||
}
|
||||
|
||||
if (playerContext.HasFlag(PlayerContext.Enemy))
|
||||
isVisible |= desiredEnemiesVisibility;
|
||||
if (playerContext == PlayerContext.None)
|
||||
{
|
||||
isVisible |= desiredOthersVisibility;
|
||||
}
|
||||
|
||||
if (playerContext == PlayerContext.None)
|
||||
isVisible |= desiredOthersVisibility;
|
||||
return isVisible;
|
||||
}
|
||||
|
||||
return isVisible;
|
||||
}
|
||||
|
||||
public static bool GetIsVisible(IPlayerCharacter playerCharacter, bool desiredSelfVisibility, bool desiredFriendsVisibility, bool desiredPartyVisibility, bool desiredAllianceVisibility, bool desiredEnemiesVisibility, bool desiredOthersVisibility)
|
||||
{
|
||||
return GetIsVisible(GetPlayerContext(playerCharacter), desiredSelfVisibility, desiredFriendsVisibility, desiredPartyVisibility, desiredAllianceVisibility, desiredEnemiesVisibility, desiredOthersVisibility);
|
||||
public static bool GetIsVisible(PlayerCharacter playerCharacter, bool desiredSelfVisibility, bool desiredFriendsVisibility, bool desiredPartyVisibility, bool desiredAllianceVisibility, bool desiredEnemiesVisibility, bool desiredOthersVisibility)
|
||||
{
|
||||
return GetIsVisible(GetPlayerContext(playerCharacter), desiredSelfVisibility, desiredFriendsVisibility, desiredPartyVisibility, desiredAllianceVisibility, desiredEnemiesVisibility, desiredOthersVisibility);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,305 +1,60 @@
|
||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||
using Dalamud.ContextMenu;
|
||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||
using Dalamud.Game.ClientState.Party;
|
||||
using Dalamud.Game.Gui.ContextMenu;
|
||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||
using Dalamud.Logging;
|
||||
using Pilz.Dalamud.ActivityContexts;
|
||||
using PlayerTags.Configuration;
|
||||
using PlayerTags.PluginStrings;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace PlayerTags.Data;
|
||||
|
||||
public class PluginData
|
||||
namespace PlayerTags.Data
|
||||
{
|
||||
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 PluginData(PluginConfiguration pluginConfiguration)
|
||||
public class PluginData
|
||||
{
|
||||
this.pluginConfiguration = pluginConfiguration;
|
||||
ReloadDefault();
|
||||
}
|
||||
public Dictionary<ZoneType, PluginTagsData> TagsData = new();
|
||||
private readonly PluginConfiguration pluginConfiguration;
|
||||
|
||||
public void ReloadDefault()
|
||||
{
|
||||
Default = new DefaultPluginData(pluginConfiguration.DefaultPluginDataTemplate);
|
||||
|
||||
// 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 = [];
|
||||
foreach (var role in Enum.GetValues<Role>())
|
||||
public PluginData(PluginConfiguration pluginConfiguration)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
this.pluginConfiguration = pluginConfiguration;
|
||||
ReloadDefault();
|
||||
}
|
||||
|
||||
DpsRoleTags = [];
|
||||
foreach (var dpsRole in Enum.GetValues<DpsRole>())
|
||||
private void EnsureDataExists(ref ZoneType zoneType)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
if (pluginConfiguration.TagsConfigs.IsEverywhere)
|
||||
zoneType = ZoneType.Everywhere;
|
||||
|
||||
if (!TagsData.ContainsKey(zoneType))
|
||||
TagsData.Add(zoneType, new PluginTagsData(pluginConfiguration, pluginConfiguration.TagsConfigs.GetConfig(zoneType)));
|
||||
}
|
||||
|
||||
RangedDpsRoleTags = [];
|
||||
foreach (var rangedDpsRole in Enum.GetValues<RangedDpsRole>())
|
||||
public PluginTagsData GetTagsData(ZoneType zoneType)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
EnsureDataExists(ref zoneType);
|
||||
return TagsData[zoneType];
|
||||
}
|
||||
|
||||
LandHandRoleTags = [];
|
||||
foreach (var landHandRole in Enum.GetValues<LandHandRole>())
|
||||
public void ReloadDefault(ZoneType zoneType)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
EnsureDataExists(ref zoneType);
|
||||
|
||||
if (TagsData[zoneType].ReloadDefault())
|
||||
pluginConfiguration.Save(this);
|
||||
}
|
||||
|
||||
JobTags = [];
|
||||
foreach ((var jobAbbreviation, var role) in RoleHelper.RolesByJobAbbreviation)
|
||||
public void ReloadDefault()
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
var needToSave = false;
|
||||
|
||||
foreach (var tagsData in TagsData.Values)
|
||||
needToSave |= tagsData.ReloadDefault();
|
||||
|
||||
if (needToSave)
|
||||
pluginConfiguration.Save(this);
|
||||
}
|
||||
|
||||
AllCustomTags = new Tag(new LocalizedPluginString(nameof(AllCustomTags)), Default.AllCustomTags);
|
||||
AllCustomTags.SetChanges(pluginConfiguration.AllCustomTagsChanges);
|
||||
|
||||
CustomTags = [];
|
||||
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)
|
||||
{
|
||||
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(MenuTargetDefault taget)
|
||||
{
|
||||
if (string.IsNullOrEmpty(taget.TargetName)
|
||||
|| taget.TargetHomeWorld.RowId == 0
|
||||
|| taget.TargetHomeWorld.RowId == 65535)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return GetIdentity(taget.TargetName, taget.TargetHomeWorld.RowId);
|
||||
}
|
||||
|
||||
public Identity GetIdentity(IPlayerCharacter playerCharacter)
|
||||
{
|
||||
return GetIdentity(playerCharacter.Name.TextValue, playerCharacter.HomeWorld.RowId);
|
||||
}
|
||||
|
||||
public Identity GetIdentity(IPartyMember partyMember)
|
||||
{
|
||||
return GetIdentity(partyMember.Name.TextValue, partyMember.World.RowId);
|
||||
}
|
||||
|
||||
public Identity GetIdentity(PlayerPayload playerPayload)
|
||||
{
|
||||
return GetIdentity(playerPayload.PlayerName, playerPayload.World.RowId);
|
||||
}
|
||||
}
|
||||
|
||||
308
PlayerTags/Data/PluginTagsData.cs
Normal file
308
PlayerTags/Data/PluginTagsData.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
namespace PlayerTags.Data;
|
||||
|
||||
public enum RangedDpsRole
|
||||
namespace PlayerTags.Data
|
||||
{
|
||||
Magical,
|
||||
Physical,
|
||||
public enum RangedDpsRole
|
||||
{
|
||||
Magical,
|
||||
Physical,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
namespace PlayerTags.Data;
|
||||
|
||||
public enum Role
|
||||
namespace PlayerTags.Data
|
||||
{
|
||||
LandHand,
|
||||
Tank,
|
||||
Healer,
|
||||
Dps,
|
||||
public enum Role
|
||||
{
|
||||
LandHand,
|
||||
Tank,
|
||||
Healer,
|
||||
Dps,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,152 +1,153 @@
|
||||
using Lumina.Excel.Sheets;
|
||||
using Pilz.Dalamud;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace PlayerTags.Data;
|
||||
|
||||
public static class RoleHelper
|
||||
namespace PlayerTags.Data
|
||||
{
|
||||
public static Dictionary<byte, Role> RolesByRoleId { get; } = new Dictionary<byte, Role>()
|
||||
public static class RoleHelper
|
||||
{
|
||||
{ 0, Role.LandHand },
|
||||
{ 1, Role.Tank },
|
||||
{ 2, Role.Dps },
|
||||
{ 3, Role.Dps },
|
||||
{ 4, Role.Healer },
|
||||
};
|
||||
|
||||
public static Dictionary<byte, DpsRole> DpsRolesByRoleId { get; } = new Dictionary<byte, DpsRole>()
|
||||
{
|
||||
{ 2, DpsRole.Melee },
|
||||
{ 3, DpsRole.Ranged },
|
||||
};
|
||||
|
||||
public static Dictionary<byte, RangedDpsRole> RangedDpsRolesByPrimaryStat { get; } = new Dictionary<byte, RangedDpsRole>()
|
||||
{
|
||||
{ 4, RangedDpsRole.Magical },
|
||||
{ 2, RangedDpsRole.Physical },
|
||||
};
|
||||
|
||||
private static Dictionary<string, Role>? s_RolesByJobAbbreviation = null;
|
||||
public static Dictionary<string, Role> RolesByJobAbbreviation
|
||||
{
|
||||
get
|
||||
public static Dictionary<byte, Role> RolesByRoleId { get; } = new Dictionary<byte, Role>()
|
||||
{
|
||||
if (s_RolesByJobAbbreviation == null)
|
||||
{
|
||||
s_RolesByJobAbbreviation = [];
|
||||
{ 0, Role.LandHand },
|
||||
{ 1, Role.Tank },
|
||||
{ 2, Role.Dps },
|
||||
{ 3, Role.Dps },
|
||||
{ 4, Role.Healer },
|
||||
};
|
||||
|
||||
var classJobs = PluginServices.DataManager.GetExcelSheet<ClassJob>();
|
||||
if (classJobs != null)
|
||||
{
|
||||
foreach (var classJob in classJobs.Where(classJob => !string.IsNullOrEmpty(classJob.Abbreviation.ParseString())))
|
||||
{
|
||||
if (RolesByRoleId.TryGetValue(classJob.Role, out var role))
|
||||
{
|
||||
s_RolesByJobAbbreviation[classJob.Abbreviation.ParseString()] = role;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s_RolesByJobAbbreviation;
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, DpsRole>? s_DpsRolesByJobAbbreviation = null;
|
||||
public static Dictionary<string, DpsRole> DpsRolesByJobAbbreviation
|
||||
{
|
||||
get
|
||||
public static Dictionary<byte, DpsRole> DpsRolesByRoleId { get; } = new Dictionary<byte, DpsRole>()
|
||||
{
|
||||
if (s_DpsRolesByJobAbbreviation == null)
|
||||
{
|
||||
s_DpsRolesByJobAbbreviation = [];
|
||||
{ 2, DpsRole.Melee },
|
||||
{ 3, DpsRole.Ranged },
|
||||
};
|
||||
|
||||
var classJobs = PluginServices.DataManager.GetExcelSheet<ClassJob>();
|
||||
if (classJobs != null)
|
||||
{
|
||||
foreach (var classJob in classJobs.Where(classJob => !string.IsNullOrEmpty(classJob.Abbreviation.ParseString())))
|
||||
{
|
||||
if (DpsRolesByRoleId.TryGetValue(classJob.Role, out var dpsRole))
|
||||
{
|
||||
s_DpsRolesByJobAbbreviation[classJob.Abbreviation.ParseString()] = dpsRole;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s_DpsRolesByJobAbbreviation;
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, RangedDpsRole>? s_RangedDpsRolesByJobAbbreviation = null;
|
||||
public static Dictionary<string, RangedDpsRole> RangedDpsRolesByJobAbbreviation
|
||||
{
|
||||
get
|
||||
public static Dictionary<byte, RangedDpsRole> RangedDpsRolesByPrimaryStat { get; } = new Dictionary<byte, RangedDpsRole>()
|
||||
{
|
||||
if (s_RangedDpsRolesByJobAbbreviation == null)
|
||||
{
|
||||
s_RangedDpsRolesByJobAbbreviation = [];
|
||||
{ 4, RangedDpsRole.Magical },
|
||||
{ 2, RangedDpsRole.Physical },
|
||||
};
|
||||
|
||||
var classJobs = PluginServices.DataManager.GetExcelSheet<ClassJob>();
|
||||
if (classJobs != null)
|
||||
private static Dictionary<string, Role>? s_RolesByJobAbbreviation = null;
|
||||
public static Dictionary<string, Role> RolesByJobAbbreviation
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_RolesByJobAbbreviation == null)
|
||||
{
|
||||
foreach (var classJob in classJobs.Where(classJob => !string.IsNullOrEmpty(classJob.Abbreviation.ParseString())))
|
||||
s_RolesByJobAbbreviation = new Dictionary<string, Role>();
|
||||
|
||||
var classJobs = PluginServices.DataManager.GetExcelSheet<ClassJob>();
|
||||
if (classJobs != null)
|
||||
{
|
||||
if (DpsRolesByJobAbbreviation.TryGetValue(classJob.Abbreviation.ParseString(), out var dpsRole) && dpsRole == DpsRole.Ranged)
|
||||
foreach (var classJob in classJobs.Where(classJob => !string.IsNullOrEmpty(classJob.Abbreviation.RawString)))
|
||||
{
|
||||
if (RangedDpsRolesByPrimaryStat.TryGetValue(classJob.PrimaryStat, out var rangedDPSRole))
|
||||
if (RolesByRoleId.TryGetValue(classJob.Role, out var role))
|
||||
{
|
||||
s_RangedDpsRolesByJobAbbreviation[classJob.Abbreviation.ParseString()] = rangedDPSRole;
|
||||
s_RolesByJobAbbreviation[classJob.Abbreviation] = role;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s_RolesByJobAbbreviation;
|
||||
}
|
||||
|
||||
return s_RangedDpsRolesByJobAbbreviation;
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, LandHandRole>? s_LandHandRolesByJobAbbreviation = null;
|
||||
public static Dictionary<string, LandHandRole> LandHandRolesByJobAbbreviation
|
||||
{
|
||||
get
|
||||
private static Dictionary<string, DpsRole>? s_DpsRolesByJobAbbreviation = null;
|
||||
public static Dictionary<string, DpsRole> DpsRolesByJobAbbreviation
|
||||
{
|
||||
if (s_LandHandRolesByJobAbbreviation == null)
|
||||
get
|
||||
{
|
||||
s_LandHandRolesByJobAbbreviation = [];
|
||||
|
||||
var classJobs = PluginServices.DataManager.GetExcelSheet<ClassJob>();
|
||||
var gatheringSubCategories = PluginServices.DataManager.GetExcelSheet<GatheringSubCategory>();
|
||||
if (classJobs != null && gatheringSubCategories != null)
|
||||
if (s_DpsRolesByJobAbbreviation == null)
|
||||
{
|
||||
var gatheringJobAbbreviations = gatheringSubCategories
|
||||
.Select(gatheringSubCategory => gatheringSubCategory.ClassJob.Value)
|
||||
.Select(classJob => classJob!.Abbreviation).Distinct();
|
||||
s_DpsRolesByJobAbbreviation = new Dictionary<string, DpsRole>();
|
||||
|
||||
foreach (var classJob in classJobs.Where(classJob => !string.IsNullOrEmpty(classJob.Abbreviation.ParseString())))
|
||||
var classJobs = PluginServices.DataManager.GetExcelSheet<ClassJob>();
|
||||
if (classJobs != null)
|
||||
{
|
||||
if (RolesByRoleId.TryGetValue(classJob.Role, out var role))
|
||||
foreach (var classJob in classJobs.Where(classJob => !string.IsNullOrEmpty(classJob.Abbreviation.RawString)))
|
||||
{
|
||||
if (role == Role.LandHand)
|
||||
if (DpsRolesByRoleId.TryGetValue(classJob.Role, out var dpsRole))
|
||||
{
|
||||
if (gatheringJobAbbreviations.Contains(classJob.Abbreviation))
|
||||
s_DpsRolesByJobAbbreviation[classJob.Abbreviation] = dpsRole;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s_DpsRolesByJobAbbreviation;
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, RangedDpsRole>? s_RangedDpsRolesByJobAbbreviation = null;
|
||||
public static Dictionary<string, RangedDpsRole> RangedDpsRolesByJobAbbreviation
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_RangedDpsRolesByJobAbbreviation == null)
|
||||
{
|
||||
s_RangedDpsRolesByJobAbbreviation = new Dictionary<string, RangedDpsRole>();
|
||||
|
||||
var classJobs = PluginServices.DataManager.GetExcelSheet<ClassJob>();
|
||||
if (classJobs != null)
|
||||
{
|
||||
foreach (var classJob in classJobs.Where(classJob => !string.IsNullOrEmpty(classJob.Abbreviation.RawString)))
|
||||
{
|
||||
if (DpsRolesByJobAbbreviation.TryGetValue(classJob.Abbreviation, out var dpsRole) && dpsRole == DpsRole.Ranged)
|
||||
{
|
||||
if (RangedDpsRolesByPrimaryStat.TryGetValue(classJob.PrimaryStat, out var rangedDPSRole))
|
||||
{
|
||||
s_LandHandRolesByJobAbbreviation[classJob.Abbreviation.ParseString()] = LandHandRole.Land;
|
||||
}
|
||||
else
|
||||
{
|
||||
s_LandHandRolesByJobAbbreviation[classJob.Abbreviation.ParseString()] = LandHandRole.Hand;
|
||||
s_RangedDpsRolesByJobAbbreviation[classJob.Abbreviation] = rangedDPSRole;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s_LandHandRolesByJobAbbreviation;
|
||||
return s_RangedDpsRolesByJobAbbreviation;
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, LandHandRole>? s_LandHandRolesByJobAbbreviation = null;
|
||||
public static Dictionary<string, LandHandRole> LandHandRolesByJobAbbreviation
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_LandHandRolesByJobAbbreviation == null)
|
||||
{
|
||||
s_LandHandRolesByJobAbbreviation = new Dictionary<string, LandHandRole>();
|
||||
|
||||
var classJobs = PluginServices.DataManager.GetExcelSheet<ClassJob>();
|
||||
var gatheringSubCategories = PluginServices.DataManager.GetExcelSheet<GatheringSubCategory>();
|
||||
if (classJobs != null && gatheringSubCategories != null)
|
||||
{
|
||||
var gatheringJobAbbreviations = gatheringSubCategories
|
||||
.Select(gatheringSubCategory => gatheringSubCategory.ClassJob.Value)
|
||||
.Where(classJob => classJob != null)
|
||||
.Select(classJob => classJob!.Abbreviation).Distinct();
|
||||
|
||||
foreach (var classJob in classJobs.Where(classJob => !string.IsNullOrEmpty(classJob.Abbreviation.RawString)))
|
||||
{
|
||||
if (RolesByRoleId.TryGetValue(classJob.Role, out var role))
|
||||
{
|
||||
if (role == Role.LandHand)
|
||||
{
|
||||
if (gatheringJobAbbreviations.Contains(classJob.Abbreviation))
|
||||
{
|
||||
s_LandHandRolesByJobAbbreviation[classJob.Abbreviation] = LandHandRole.Land;
|
||||
}
|
||||
else
|
||||
{
|
||||
s_LandHandRolesByJobAbbreviation[classJob.Abbreviation] = LandHandRole.Hand;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s_LandHandRolesByJobAbbreviation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,320 +1,330 @@
|
||||
using Dalamud.Game.Text;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Pilz.Dalamud.Icons;
|
||||
using PlayerTags.Inheritables;
|
||||
using PlayerTags.PluginStrings;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PlayerTags.Data;
|
||||
|
||||
public class Tag
|
||||
namespace PlayerTags.Data
|
||||
{
|
||||
public IPluginString Name { get; init; }
|
||||
|
||||
[JsonProperty("Parent")]
|
||||
private Tag? m_Parent = null;
|
||||
|
||||
[JsonIgnore]
|
||||
public Tag? Parent
|
||||
public class Tag
|
||||
{
|
||||
get => m_Parent;
|
||||
set
|
||||
{
|
||||
if (m_Parent != value)
|
||||
{
|
||||
if (m_Parent != null)
|
||||
{
|
||||
if (m_Parent.Children.Contains(this))
|
||||
{
|
||||
m_Parent.Children.Remove(this);
|
||||
}
|
||||
}
|
||||
public IPluginString Name { get; init; }
|
||||
|
||||
m_Parent = value;
|
||||
if (m_Parent != null)
|
||||
[JsonProperty("Parent")]
|
||||
private Tag? m_Parent = null;
|
||||
|
||||
[JsonIgnore]
|
||||
public Tag? Parent
|
||||
{
|
||||
get => m_Parent;
|
||||
set
|
||||
{
|
||||
if (m_Parent != value)
|
||||
{
|
||||
m_Parent.Children.Add(this);
|
||||
foreach ((var name, IInheritable inheritable) in Inheritables)
|
||||
if (m_Parent != null)
|
||||
{
|
||||
inheritable.Parent = m_Parent.Inheritables[name];
|
||||
if (m_Parent.Children.Contains(this))
|
||||
{
|
||||
m_Parent.Children.Remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
m_Parent = value;
|
||||
if (m_Parent != null)
|
||||
{
|
||||
m_Parent.Children.Add(this);
|
||||
foreach ((var name, IInheritable inheritable) in Inheritables)
|
||||
{
|
||||
inheritable.Parent = m_Parent.Inheritables[name];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<Tag> Children { get; } = [];
|
||||
public List<Tag> Children { get; } = new List<Tag>();
|
||||
|
||||
[JsonIgnore]
|
||||
public IEnumerable<Tag> Descendents
|
||||
{
|
||||
get
|
||||
[JsonIgnore]
|
||||
public IEnumerable<Tag> Descendents
|
||||
{
|
||||
IEnumerable<Tag> descendents = Children.Prepend(this);
|
||||
|
||||
foreach (var child in Children)
|
||||
get
|
||||
{
|
||||
descendents = descendents.Union(child.Descendents);
|
||||
}
|
||||
IEnumerable<Tag> descendents = Children.Prepend(this);
|
||||
|
||||
return descendents.Distinct();
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
private Dictionary<string, IInheritable>? m_Inheritables = null;
|
||||
[JsonIgnore]
|
||||
public Dictionary<string, IInheritable> Inheritables
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Inheritables == null)
|
||||
{
|
||||
m_Inheritables = [];
|
||||
|
||||
var inheritableFields = GetType().GetFields().Where(field => typeof(IInheritable).IsAssignableFrom(field.FieldType));
|
||||
foreach (var inheritableField in inheritableFields)
|
||||
foreach (var child in Children)
|
||||
{
|
||||
IInheritable? inheritable = inheritableField.GetValue(this) as IInheritable;
|
||||
if (inheritable != null)
|
||||
descendents = descendents.Union(child.Descendents);
|
||||
}
|
||||
|
||||
return descendents.Distinct();
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
private Dictionary<string, IInheritable>? m_Inheritables = null;
|
||||
[JsonIgnore]
|
||||
public Dictionary<string, IInheritable> Inheritables
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Inheritables == null)
|
||||
{
|
||||
m_Inheritables = new Dictionary<string, IInheritable>();
|
||||
|
||||
var inheritableFields = GetType().GetFields().Where(field => typeof(IInheritable).IsAssignableFrom(field.FieldType));
|
||||
foreach (var inheritableField in inheritableFields)
|
||||
{
|
||||
Inheritables[inheritableField.Name] = inheritable;
|
||||
IInheritable? inheritable = inheritableField.GetValue(this) as IInheritable;
|
||||
if (inheritable != null)
|
||||
{
|
||||
Inheritables[inheritableField.Name] = inheritable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m_Inheritables!;
|
||||
}
|
||||
|
||||
return m_Inheritables!;
|
||||
}
|
||||
}
|
||||
|
||||
public InheritableValue<bool> IsSelected = new(false)
|
||||
{
|
||||
Behavior = InheritableBehavior.Enabled
|
||||
};
|
||||
|
||||
public InheritableValue<bool> IsExpanded = new(false)
|
||||
{
|
||||
Behavior = InheritableBehavior.Enabled
|
||||
};
|
||||
|
||||
// Deprecated
|
||||
public InheritableReference<string> GameObjectNamesToApplyTo = new("");
|
||||
|
||||
public InheritableValue<Guid> CustomId = new(Guid.Empty);
|
||||
|
||||
[JsonProperty, Obsolete]
|
||||
private InheritableValue<bool> IsIconVisibleInChat
|
||||
{
|
||||
set => IsRoleIconVisibleInChat = value;
|
||||
}
|
||||
|
||||
[JsonProperty, Obsolete]
|
||||
private InheritableValue<bool> IsIconVisibleInNameplate
|
||||
{
|
||||
set => IsRoleIconVisibleInNameplates = value;
|
||||
}
|
||||
|
||||
[InheritableCategory("IconCategory")]
|
||||
public InheritableValue<BitmapFontIcon> Icon = new(BitmapFontIcon.Aethernet);
|
||||
[InheritableCategory("IconCategory")]
|
||||
public InheritableValue<bool> IsRoleIconVisibleInChat = new(false);
|
||||
[InheritableCategory("IconCategory")]
|
||||
public InheritableValue<bool> IsRoleIconVisibleInNameplates = new(false);
|
||||
[InheritableCategory("IconCategory")]
|
||||
public InheritableValue<bool> IsJobIconVisibleInNameplates = new(false);
|
||||
[InheritableCategory("IconCategory")]
|
||||
public InheritableValue<JobIconSetName> JobIconSet = new(JobIconSetName.Framed);
|
||||
|
||||
[InheritableCategory("TextCategory")]
|
||||
public InheritableReference<string> Text = new("");
|
||||
[InheritableCategory("TextCategory")]
|
||||
public InheritableValue<ushort> TextColor = new(6);
|
||||
[InheritableCategory("TextCategory")]
|
||||
public InheritableValue<ushort> TextGlowColor = new(6);
|
||||
[InheritableCategory("TextCategory")]
|
||||
public InheritableValue<bool> IsTextItalic = new(false);
|
||||
[InheritableCategory("TextCategory")]
|
||||
public InheritableValue<bool> IsTextVisibleInChat = new(false);
|
||||
[InheritableCategory("TextCategory")]
|
||||
public InheritableValue<bool> IsTextVisibleInNameplates = new(false);
|
||||
[InheritableCategory("TextCategory")]
|
||||
public InheritableValue<bool> IsTextColorAppliedToChatName = new(false);
|
||||
[InheritableCategory("TextCategory")]
|
||||
public InheritableValue<bool> IsTextColorAppliedToNameplateName = new(false);
|
||||
[InheritableCategory("TextCategory")]
|
||||
public InheritableValue<bool> IsTextColorAppliedToNameplateTitle = new(false);
|
||||
[InheritableCategory("TextCategory")]
|
||||
public InheritableValue<bool> IsTextColorAppliedToNameplateFreeCompany = new(false);
|
||||
|
||||
[InheritableCategory("PositionCategory")]
|
||||
public InheritableValue<TagPosition> TagPositionInChat = new(TagPosition.Before);
|
||||
[InheritableCategory("PositionCategory")]
|
||||
public InheritableValue<bool> InsertBehindNumberPrefixInChat = new(true);
|
||||
[InheritableCategory("PositionCategory")]
|
||||
public InheritableValue<TagPosition> TagPositionInNameplates = new(TagPosition.Before);
|
||||
[InheritableCategory("PositionCategory")]
|
||||
public InheritableValue<NameplateElement> TagTargetInNameplates = new(NameplateElement.Name);
|
||||
|
||||
[InheritableCategory("ActivityCategory")]
|
||||
public InheritableValue<bool> IsVisibleInPveDuties = new(false);
|
||||
[InheritableCategory("ActivityCategory")]
|
||||
public InheritableValue<bool> IsVisibleInPvpDuties = new(false);
|
||||
[InheritableCategory("ActivityCategory")]
|
||||
public InheritableValue<bool> IsVisibleInOverworld = new(false);
|
||||
|
||||
[InheritableCategory("PlayerCategory")]
|
||||
public InheritableValue<bool> IsVisibleForSelf = new(false);
|
||||
[InheritableCategory("PlayerCategory")]
|
||||
public InheritableValue<bool> IsVisibleForFriendPlayers = new(false);
|
||||
[InheritableCategory("PlayerCategory")]
|
||||
public InheritableValue<bool> IsVisibleForPartyPlayers = new(false);
|
||||
[InheritableCategory("PlayerCategory")]
|
||||
public InheritableValue<bool> IsVisibleForAlliancePlayers = new(false);
|
||||
[InheritableCategory("PlayerCategory")]
|
||||
public InheritableValue<bool> IsVisibleForEnemyPlayers = new(false);
|
||||
[InheritableCategory("PlayerCategory")]
|
||||
public InheritableValue<bool> IsVisibleForOtherPlayers = new(false);
|
||||
|
||||
[InheritableCategory("ChatFeatureCategory")]
|
||||
public InheritableReference<List<XivChatType>> TargetChatTypes = new(new List<XivChatType>(Enum.GetValues<XivChatType>()));
|
||||
[InheritableCategory("ChatFeatureCategory")]
|
||||
public InheritableValue<bool> TargetChatTypesIncludeUndefined = new(true);
|
||||
|
||||
[JsonIgnore]
|
||||
public string[] IdentitiesToAddTo
|
||||
{
|
||||
get
|
||||
public InheritableValue<bool> IsSelected = new InheritableValue<bool>(false)
|
||||
{
|
||||
if (GameObjectNamesToApplyTo == null || GameObjectNamesToApplyTo.InheritedValue == null)
|
||||
Behavior = InheritableBehavior.Enabled
|
||||
};
|
||||
|
||||
public InheritableValue<bool> IsExpanded = new InheritableValue<bool>(false)
|
||||
{
|
||||
Behavior = InheritableBehavior.Enabled
|
||||
};
|
||||
|
||||
// Deprecated
|
||||
public InheritableReference<string> GameObjectNamesToApplyTo = new InheritableReference<string>("");
|
||||
|
||||
public InheritableValue<Guid> CustomId = new InheritableValue<Guid>(Guid.Empty);
|
||||
|
||||
[JsonProperty, Obsolete]
|
||||
private InheritableValue<bool> IsIconVisibleInChat
|
||||
{
|
||||
set => IsRoleIconVisibleInChat = value;
|
||||
}
|
||||
|
||||
[JsonProperty, Obsolete]
|
||||
private InheritableValue<bool> IsIconVisibleInNameplate
|
||||
{
|
||||
set => IsRoleIconVisibleInNameplates = value;
|
||||
}
|
||||
|
||||
[InheritableCategory("IconCategory")]
|
||||
public InheritableValue<BitmapFontIcon> Icon = new InheritableValue<BitmapFontIcon>(BitmapFontIcon.Aethernet);
|
||||
[InheritableCategory("IconCategory")]
|
||||
public InheritableValue<bool> IsRoleIconVisibleInChat = new InheritableValue<bool>(false);
|
||||
[InheritableCategory("IconCategory")]
|
||||
public InheritableValue<bool> IsRoleIconVisibleInNameplates = new InheritableValue<bool>(false);
|
||||
[InheritableCategory("IconCategory")]
|
||||
public InheritableValue<bool> IsJobIconVisibleInNameplates = new InheritableValue<bool>(false);
|
||||
[InheritableCategory("IconCategory")]
|
||||
public InheritableValue<JobIconSetName> JobIconSet = new InheritableValue<JobIconSetName>(JobIconSetName.Framed);
|
||||
|
||||
[InheritableCategory("TextCategory")]
|
||||
public InheritableReference<string> Text = new InheritableReference<string>("");
|
||||
[InheritableCategory("TextCategory")]
|
||||
public InheritableValue<ushort> TextColor = new InheritableValue<ushort>(6);
|
||||
[InheritableCategory("TextCategory")]
|
||||
public InheritableValue<ushort> TextGlowColor = new InheritableValue<ushort>(6);
|
||||
[InheritableCategory("TextCategory")]
|
||||
public InheritableValue<bool> IsTextItalic = new InheritableValue<bool>(false);
|
||||
[InheritableCategory("TextCategory")]
|
||||
public InheritableValue<bool> IsTextVisibleInChat = new InheritableValue<bool>(false);
|
||||
[InheritableCategory("TextCategory")]
|
||||
public InheritableValue<bool> IsTextVisibleInNameplates = new InheritableValue<bool>(false);
|
||||
[InheritableCategory("TextCategory")]
|
||||
public InheritableValue<bool> IsTextColorAppliedToChatName = new InheritableValue<bool>(false);
|
||||
[InheritableCategory("TextCategory")]
|
||||
public InheritableValue<bool> IsTextColorAppliedToNameplateName = new InheritableValue<bool>(false);
|
||||
[InheritableCategory("TextCategory")]
|
||||
public InheritableValue<bool> IsTextColorAppliedToNameplateTitle = new InheritableValue<bool>(false);
|
||||
[InheritableCategory("TextCategory")]
|
||||
public InheritableValue<bool> IsTextColorAppliedToNameplateFreeCompany = new InheritableValue<bool>(false);
|
||||
|
||||
//[InheritableCategory("NameplateCategory")]
|
||||
//public InheritableValue<NameplateFreeCompanyVisibility> NameplateFreeCompanyVisibility = new InheritableValue<NameplateFreeCompanyVisibility>(Data.NameplateFreeCompanyVisibility.Default);
|
||||
//[InheritableCategory("NameplateCategory")]
|
||||
//public InheritableValue<NameplateTitleVisibility> NameplateTitleVisibility = new InheritableValue<NameplateTitleVisibility>(Data.NameplateTitleVisibility.Default);
|
||||
//[InheritableCategory("NameplateCategory")]
|
||||
//public InheritableValue<NameplateTitlePosition> NameplateTitlePosition = new InheritableValue<NameplateTitlePosition>(Data.NameplateTitlePosition.Default);
|
||||
|
||||
[InheritableCategory("PositionCategory")]
|
||||
public InheritableValue<TagPosition> TagPositionInChat = new InheritableValue<TagPosition>(TagPosition.Before);
|
||||
[InheritableCategory("PositionCategory")]
|
||||
public InheritableValue<bool> InsertBehindNumberPrefixInChat = new InheritableValue<bool>(true);
|
||||
[InheritableCategory("PositionCategory")]
|
||||
public InheritableValue<TagPosition> TagPositionInNameplates = new InheritableValue<TagPosition>(TagPosition.Before);
|
||||
[InheritableCategory("PositionCategory")]
|
||||
public InheritableValue<NameplateElement> TagTargetInNameplates = new InheritableValue<NameplateElement>(NameplateElement.Name);
|
||||
|
||||
[InheritableCategory("ActivityCategory")]
|
||||
public InheritableValue<bool> IsVisibleInPveDuties = new InheritableValue<bool>(false);
|
||||
[InheritableCategory("ActivityCategory")]
|
||||
public InheritableValue<bool> IsVisibleInPvpDuties = new InheritableValue<bool>(false);
|
||||
[InheritableCategory("ActivityCategory")]
|
||||
public InheritableValue<bool> IsVisibleInOverworld = new InheritableValue<bool>(false);
|
||||
|
||||
[InheritableCategory("PlayerCategory")]
|
||||
public InheritableValue<bool> IsVisibleForSelf = new InheritableValue<bool>(false);
|
||||
[InheritableCategory("PlayerCategory")]
|
||||
public InheritableValue<bool> IsVisibleForFriendPlayers = new InheritableValue<bool>(false);
|
||||
[InheritableCategory("PlayerCategory")]
|
||||
public InheritableValue<bool> IsVisibleForPartyPlayers = new InheritableValue<bool>(false);
|
||||
[InheritableCategory("PlayerCategory")]
|
||||
public InheritableValue<bool> IsVisibleForAlliancePlayers = new InheritableValue<bool>(false);
|
||||
[InheritableCategory("PlayerCategory")]
|
||||
public InheritableValue<bool> IsVisibleForEnemyPlayers = new InheritableValue<bool>(false);
|
||||
[InheritableCategory("PlayerCategory")]
|
||||
public InheritableValue<bool> IsVisibleForOtherPlayers = new InheritableValue<bool>(false);
|
||||
|
||||
[InheritableCategory("ChatFeatureCategory")]
|
||||
public InheritableReference<List<XivChatType>> TargetChatTypes = new(new List<XivChatType>(Enum.GetValues<XivChatType>()));
|
||||
[InheritableCategory("ChatFeatureCategory")]
|
||||
public InheritableValue<bool> TargetChatTypesIncludeUndefined = new(true);
|
||||
|
||||
[JsonIgnore]
|
||||
public string[] IdentitiesToAddTo
|
||||
{
|
||||
get
|
||||
{
|
||||
return new string[] { };
|
||||
if (GameObjectNamesToApplyTo == null || GameObjectNamesToApplyTo.InheritedValue == null)
|
||||
{
|
||||
return new string[] { };
|
||||
}
|
||||
|
||||
return GameObjectNamesToApplyTo.InheritedValue.Split(';', ',').Where(item => !string.IsNullOrEmpty(item)).Select(item => item.Trim()).ToArray();
|
||||
}
|
||||
|
||||
return GameObjectNamesToApplyTo.InheritedValue.Split(';', ',').Where(item => !string.IsNullOrEmpty(item)).Select(item => item.Trim()).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
private Tag? m_Defaults;
|
||||
[JsonIgnore]
|
||||
public bool HasDefaults
|
||||
{
|
||||
get { return m_Defaults != null; }
|
||||
}
|
||||
|
||||
public Tag()
|
||||
{
|
||||
Name = new LiteralPluginString("");
|
||||
m_Defaults = null;
|
||||
}
|
||||
|
||||
public Tag(IPluginString name)
|
||||
{
|
||||
Name = name;
|
||||
m_Defaults = null;
|
||||
}
|
||||
|
||||
public Tag(IPluginString name, Tag defaults)
|
||||
{
|
||||
Name = name;
|
||||
m_Defaults = defaults;
|
||||
SetChanges(defaults.GetChanges());
|
||||
}
|
||||
|
||||
public Dictionary<string, InheritableData> GetChanges(Dictionary<string, InheritableData>? defaultChanges = null)
|
||||
{
|
||||
Dictionary<string, InheritableData> changes = [];
|
||||
|
||||
foreach ((var name, var inheritable) in Inheritables)
|
||||
private Tag? m_Defaults;
|
||||
[JsonIgnore]
|
||||
public bool HasDefaults
|
||||
{
|
||||
// If there's a default for this name, only set the value if it's different from the default
|
||||
if (defaultChanges != null && defaultChanges.TryGetValue(name, out var defaultInheritableData))
|
||||
get { return m_Defaults != null; }
|
||||
}
|
||||
|
||||
public Tag()
|
||||
{
|
||||
Name = new LiteralPluginString("");
|
||||
m_Defaults = null;
|
||||
}
|
||||
|
||||
public Tag(IPluginString name)
|
||||
{
|
||||
Name = name;
|
||||
m_Defaults = null;
|
||||
}
|
||||
|
||||
public Tag(IPluginString name, Tag defaults)
|
||||
{
|
||||
Name = name;
|
||||
m_Defaults = defaults;
|
||||
SetChanges(defaults.GetChanges());
|
||||
}
|
||||
|
||||
public Dictionary<string, InheritableData> GetChanges(Dictionary<string, InheritableData>? defaultChanges = null)
|
||||
{
|
||||
Dictionary<string, InheritableData> changes = new Dictionary<string, InheritableData>();
|
||||
|
||||
foreach ((var name, var inheritable) in Inheritables)
|
||||
{
|
||||
var inheritableData = inheritable.GetData();
|
||||
if (inheritableData.Behavior != defaultInheritableData.Behavior ||
|
||||
!EqualsInheritableData(inheritableData, defaultInheritableData))
|
||||
// If there's a default for this name, only set the value if it's different from the default
|
||||
if (defaultChanges != null && defaultChanges.TryGetValue(name, out var defaultInheritableData))
|
||||
{
|
||||
var inheritableData = inheritable.GetData();
|
||||
if (inheritableData.Behavior != defaultInheritableData.Behavior ||
|
||||
!EqualsInheritableData(inheritableData, defaultInheritableData))
|
||||
{
|
||||
changes[name] = inheritable.GetData();
|
||||
}
|
||||
}
|
||||
// If there's no default, then only set the value if it's not inherited
|
||||
else if (inheritable.Behavior != InheritableBehavior.Inherit)
|
||||
{
|
||||
changes[name] = inheritable.GetData();
|
||||
}
|
||||
}
|
||||
// If there's no default, then only set the value if it's not inherited
|
||||
else if (inheritable.Behavior != InheritableBehavior.Inherit)
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
private static bool EqualsInheritableData(InheritableData data1, InheritableData data2)
|
||||
{
|
||||
if (data1.Value is List<XivChatType>)
|
||||
return EqualsInheritableDataListXivChatType<XivChatType>(data1, data2);
|
||||
else
|
||||
return data1.Value.Equals(data2.Value);
|
||||
}
|
||||
|
||||
private static bool EqualsInheritableDataListXivChatType<TEnum>(InheritableData data1, InheritableData data2)
|
||||
{
|
||||
var list1 = data1.Value as List<TEnum>;
|
||||
var list2 = data2.Value as List<TEnum>;
|
||||
|
||||
if (list1 is null || list2 is null || list1.Count != list2.Count)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < list1.Count; i++)
|
||||
{
|
||||
changes[name] = inheritable.GetData();
|
||||
if (!list1[i].Equals(list2[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static readonly Dictionary<string, string> ObsulteInheritableStringMap = new()
|
||||
{
|
||||
{ "IsIconVisibleInChat", nameof(IsRoleIconVisibleInChat) },
|
||||
{ "IsIconVisibleInNameplate", nameof(IsRoleIconVisibleInNameplates) },
|
||||
{ "IsIconVisibleInNameplates", nameof(IsRoleIconVisibleInNameplates) }
|
||||
};
|
||||
private static string FixObsuleteInheritableStringName(string name)
|
||||
{
|
||||
if (ObsulteInheritableStringMap.ContainsKey(name))
|
||||
return ObsulteInheritableStringMap[name];
|
||||
else
|
||||
return name;
|
||||
}
|
||||
|
||||
public void SetChanges(IEnumerable<KeyValuePair<string, InheritableData>> changes)
|
||||
{
|
||||
foreach ((var name, var inheritableData) in changes)
|
||||
{
|
||||
var namefixed = FixObsuleteInheritableStringName(name);
|
||||
Inheritables[namefixed].SetData(inheritableData);
|
||||
}
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
private static bool EqualsInheritableData(InheritableData data1, InheritableData data2)
|
||||
{
|
||||
if (data1.Value is List<XivChatType>)
|
||||
return EqualsInheritableDataListXivChatType<XivChatType>(data1, data2);
|
||||
else
|
||||
return data1.Value.Equals(data2.Value);
|
||||
}
|
||||
|
||||
private static bool EqualsInheritableDataListXivChatType<TEnum>(InheritableData data1, InheritableData data2)
|
||||
{
|
||||
var list1 = data1.Value as List<TEnum>;
|
||||
var list2 = data2.Value as List<TEnum>;
|
||||
|
||||
if (list1 is null || list2 is null || list1.Count != list2.Count)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < list1.Count; i++)
|
||||
private Dictionary<string, InheritableData> GetAllAsChanges()
|
||||
{
|
||||
if (!list1[i].Equals(list2[i]))
|
||||
return false;
|
||||
Dictionary<string, InheritableData> changes = new Dictionary<string, InheritableData>();
|
||||
|
||||
foreach ((var name, var inheritable) in Inheritables)
|
||||
{
|
||||
changes[name] = inheritable.GetData();
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static readonly Dictionary<string, string> ObsulteInheritableStringMap = new()
|
||||
{
|
||||
{ "IsIconVisibleInChat", nameof(IsRoleIconVisibleInChat) },
|
||||
{ "IsIconVisibleInNameplate", nameof(IsRoleIconVisibleInNameplates) },
|
||||
{ "IsIconVisibleInNameplates", nameof(IsRoleIconVisibleInNameplates) }
|
||||
};
|
||||
private static string FixObsuleteInheritableStringName(string name)
|
||||
{
|
||||
if (ObsulteInheritableStringMap.ContainsKey(name))
|
||||
return ObsulteInheritableStringMap[name];
|
||||
else
|
||||
return name;
|
||||
}
|
||||
|
||||
public void SetChanges(IEnumerable<KeyValuePair<string, InheritableData>> changes)
|
||||
{
|
||||
foreach ((var name, var inheritableData) in changes)
|
||||
public void SetDefaults()
|
||||
{
|
||||
var namefixed = FixObsuleteInheritableStringName(name);
|
||||
Inheritables[namefixed].SetData(inheritableData);
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<string, InheritableData> GetAllAsChanges()
|
||||
{
|
||||
Dictionary<string, InheritableData> changes = [];
|
||||
|
||||
foreach ((var name, var inheritable) in Inheritables)
|
||||
{
|
||||
changes[name] = inheritable.GetData();
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
public void SetDefaults()
|
||||
{
|
||||
if (m_Defaults != null)
|
||||
{
|
||||
// Exclude IsSelected and IsExpanded for UX purposes
|
||||
SetChanges(m_Defaults.GetAllAsChanges().Where(change => change.Key != nameof(IsSelected) && change.Key != nameof(IsExpanded)));
|
||||
if (m_Defaults != null)
|
||||
{
|
||||
// Exclude IsSelected and IsExpanded for UX purposes
|
||||
SetChanges(m_Defaults.GetAllAsChanges().Where(change => change.Key != nameof(IsSelected) && change.Key != nameof(IsExpanded)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
namespace PlayerTags.Data;
|
||||
|
||||
public enum TagPosition
|
||||
namespace PlayerTags.Data
|
||||
{
|
||||
Before,
|
||||
After,
|
||||
Replace
|
||||
public enum TagPosition
|
||||
{
|
||||
Before,
|
||||
After,
|
||||
Replace
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
namespace PlayerTags.Data;
|
||||
|
||||
public enum TagTarget
|
||||
namespace PlayerTags.Data
|
||||
{
|
||||
Chat,
|
||||
Nameplate
|
||||
public enum TagTarget
|
||||
{
|
||||
Chat,
|
||||
Nameplate
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +1,41 @@
|
||||
using Lumina.Excel.Sheets;
|
||||
using Pilz.Dalamud;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PlayerTags.Data;
|
||||
|
||||
public static class WorldHelper
|
||||
namespace PlayerTags.Data
|
||||
{
|
||||
private static Dictionary<uint, string>? s_WorldNames = null;
|
||||
public static Dictionary<uint, string> WorldNames
|
||||
public static class WorldHelper
|
||||
{
|
||||
get
|
||||
private static Dictionary<uint, string>? s_WorldNames = null;
|
||||
public static Dictionary<uint, string> WorldNames
|
||||
{
|
||||
if (s_WorldNames == null)
|
||||
get
|
||||
{
|
||||
s_WorldNames = [];
|
||||
|
||||
var worlds = PluginServices.DataManager.GetExcelSheet<World>();
|
||||
if (worlds != null)
|
||||
if (s_WorldNames == null)
|
||||
{
|
||||
foreach (var world in worlds)
|
||||
s_WorldNames = new Dictionary<uint, string>();
|
||||
|
||||
var worlds = PluginServices.DataManager.GetExcelSheet<World>();
|
||||
if (worlds != null)
|
||||
{
|
||||
s_WorldNames[world.RowId] = world.Name.ParseString();
|
||||
foreach (var world in worlds)
|
||||
{
|
||||
s_WorldNames[world.RowId] = world.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s_WorldNames;
|
||||
}
|
||||
}
|
||||
|
||||
public static string? GetWorldName(uint? worldId)
|
||||
{
|
||||
if (worldId != null && WorldNames.TryGetValue(worldId.Value, out var name))
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
return s_WorldNames;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static string? GetWorldName(uint? worldId)
|
||||
{
|
||||
if (worldId != null && WorldNames.TryGetValue(worldId.Value, out var name))
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +1,40 @@
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PlayerTags;
|
||||
|
||||
internal static class Extensions
|
||||
namespace PlayerTags
|
||||
{
|
||||
/// <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)
|
||||
internal static class Extensions
|
||||
{
|
||||
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++)
|
||||
/// <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)
|
||||
{
|
||||
if (ReferenceEquals(payloads[i], 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++)
|
||||
{
|
||||
payloads.RemoveAt(i);
|
||||
break;
|
||||
if (ReferenceEquals(payloads[i], payload))
|
||||
{
|
||||
payloads.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,489 +3,443 @@ using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Dalamud.Game.Text;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||
using Lumina.Excel.Sheets;
|
||||
using Pilz.Dalamud;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Pilz.Dalamud.Tools.Strings;
|
||||
using PlayerTags.Configuration;
|
||||
using PlayerTags.Configuration.GameConfig;
|
||||
using PlayerTags.Data;
|
||||
using PlayerTags.Inheritables;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using Action = System.Action;
|
||||
|
||||
namespace PlayerTags.Features;
|
||||
|
||||
/// <summary>
|
||||
/// A feature that adds tags to chat messages.
|
||||
/// </summary>
|
||||
public class ChatTagTargetFeature : TagTargetFeature
|
||||
namespace PlayerTags.Features
|
||||
{
|
||||
/// <summary>
|
||||
/// A match found within a string.
|
||||
/// A feature that adds tags to chat messages.
|
||||
/// </summary>
|
||||
private class StringMatch(SeString seString)
|
||||
public class ChatTagTargetFeature : TagTargetFeature
|
||||
{
|
||||
/// <summary>
|
||||
/// The string that the match was found in.
|
||||
/// A match found within a string.
|
||||
/// </summary>
|
||||
public SeString SeString { get; init; } = seString;
|
||||
|
||||
public List<Payload> DisplayTextPayloads { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// The matching game object if one exists
|
||||
/// </summary>
|
||||
public IGameObject? GameObject { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// A matching player payload if one exists.
|
||||
/// </summary>
|
||||
public PlayerPayload? PlayerPayload { get; init; }
|
||||
|
||||
public RawPayload LinkTerminatorPayload { get; init; }
|
||||
|
||||
public Payload? PlayerNamePayload
|
||||
private class StringMatch
|
||||
{
|
||||
get
|
||||
/// <summary>
|
||||
/// The string that the match was found in.
|
||||
/// </summary>
|
||||
public SeString SeString { get; init; }
|
||||
|
||||
public List<Payload> DisplayTextPayloads { get; init; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// The matching game object if one exists
|
||||
/// </summary>
|
||||
public GameObject? GameObject { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// A matching player payload if one exists.
|
||||
/// </summary>
|
||||
public PlayerPayload? PlayerPayload { get; init; }
|
||||
|
||||
public RawPayload LinkTerminatorPayload { get; init; }
|
||||
|
||||
public Payload? PlayerNamePayload
|
||||
{
|
||||
Payload textPayload = null;
|
||||
string textMatch = GetMatchTextInternal();
|
||||
string textMatchShort = BuildPlayername(textMatch);
|
||||
|
||||
textPayload = DisplayTextPayloads.FirstOrDefault(n => n is TextPayload textPayload && (textPayload.Text.Contains(textMatch) || ((!string.IsNullOrEmpty(textMatchShort)) && textPayload.Text.Contains(textMatchShort))));
|
||||
textPayload ??= PlayerPayload;
|
||||
textPayload ??= DisplayTextPayloads.FirstOrDefault();
|
||||
|
||||
return textPayload;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsLocalPlayer
|
||||
{
|
||||
get => GetMatchTextInternal() == PluginServices.ClientState.LocalPlayer.Name.TextValue;
|
||||
}
|
||||
|
||||
private string GetMatchTextInternal()
|
||||
{
|
||||
if (GameObject != null)
|
||||
return GameObject.Name.TextValue;
|
||||
else if (PlayerPayload != null)
|
||||
return PlayerPayload.PlayerName;
|
||||
else
|
||||
return SeString.TextValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the matches text.
|
||||
/// </summary>
|
||||
/// <returns>The match text.</returns>
|
||||
public string GetMatchText()
|
||||
{
|
||||
var playerNamePayload = PlayerNamePayload;
|
||||
if (playerNamePayload is PlayerPayload pp)
|
||||
return pp.PlayerName;
|
||||
else if (playerNamePayload is TextPayload tp)
|
||||
return tp.Text;
|
||||
else
|
||||
return SeString.TextValue;
|
||||
}
|
||||
}
|
||||
|
||||
public ChatTagTargetFeature(PluginConfiguration pluginConfiguration, PluginData pluginData) : base(pluginConfiguration, pluginData)
|
||||
{
|
||||
PluginServices.ChatGui.ChatMessage += ChatGui_ChatMessage;
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
PluginServices.ChatGui.ChatMessage -= ChatGui_ChatMessage;
|
||||
base.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void ChatGui_ChatMessage(XivChatType type, int timestamp, ref SeString sender, ref SeString message, ref bool isHandled)
|
||||
{
|
||||
if (EnableGlobal && pluginConfiguration.GeneralOptions[ActivityContextManager.CurrentActivityContext.ActivityType].IsApplyTagsToAllChatMessagesEnabled)
|
||||
{
|
||||
AddTagsToChat(sender, type, true);
|
||||
AddTagsToChat(message, type, false);
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool IsIconVisible(Tag tag)
|
||||
{
|
||||
if (tag.IsRoleIconVisibleInChat.InheritedValue != null)
|
||||
return tag.IsRoleIconVisibleInChat.InheritedValue.Value;
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override bool IsTextVisible(Tag tag)
|
||||
{
|
||||
if (tag.IsTextVisibleInChat.InheritedValue != null)
|
||||
{
|
||||
return tag.IsTextVisibleInChat.InheritedValue.Value;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches the given string for game object matches.
|
||||
/// </summary>
|
||||
/// <param name="seString">The string to search.</param>
|
||||
/// <returns>A list of matched game objects.</returns>
|
||||
private List<StringMatch> GetStringMatches(SeString seString)
|
||||
{
|
||||
List<StringMatch> stringMatches = [];
|
||||
Stack<PlayerPayload> curPlayerPayload = new();
|
||||
Stack<List<Payload>> curRefPayloads = new();
|
||||
var defaultRawPayload = RawPayload.LinkTerminator.Data;
|
||||
|
||||
foreach (var payload in seString.Payloads)
|
||||
{
|
||||
|
||||
if (payload is PlayerPayload playerPayload)
|
||||
{
|
||||
curPlayerPayload.Push(playerPayload);
|
||||
curRefPayloads.Push([]);
|
||||
}
|
||||
else if (payload is RawPayload rawPayload)
|
||||
{
|
||||
if (defaultRawPayload.SequenceEqual(rawPayload.Data))
|
||||
finishCurrentMatch(rawPayload);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (curRefPayloads.TryPeek(out List<Payload> result))
|
||||
result.Add(payload);
|
||||
}
|
||||
}
|
||||
|
||||
// Finally finish, if not closed by RawPayload
|
||||
finishCurrentMatch(null);
|
||||
|
||||
void finishCurrentMatch(RawPayload linkTerminatorPayload)
|
||||
{
|
||||
if (curPlayerPayload.TryPop(out PlayerPayload playerPayload))
|
||||
{
|
||||
var gameObject = PluginServices.ObjectTable.FirstOrDefault(gameObject => gameObject.Name.TextValue == playerPayload.PlayerName);
|
||||
var stringMatch = new StringMatch(seString)
|
||||
get
|
||||
{
|
||||
GameObject = gameObject,
|
||||
PlayerPayload = playerPayload,
|
||||
LinkTerminatorPayload = linkTerminatorPayload,
|
||||
DisplayTextPayloads = curRefPayloads.Pop()
|
||||
};
|
||||
stringMatches.Add(stringMatch);
|
||||
Payload textPayload = null;
|
||||
string textMatch = GetMatchTextInternal();
|
||||
string textMatchShort = BuildPlayername(textMatch);
|
||||
|
||||
textPayload = DisplayTextPayloads.FirstOrDefault(n => n is TextPayload textPayload && (textPayload.Text.Contains(textMatch) || ((!string.IsNullOrEmpty(textMatchShort)) && textPayload.Text.Contains(textMatchShort))));
|
||||
textPayload ??= PlayerPayload;
|
||||
textPayload ??= DisplayTextPayloads.FirstOrDefault();
|
||||
|
||||
return textPayload;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsLocalPlayer
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetMatchTextInternal() == PluginServices.ClientState.LocalPlayer.Name.TextValue;
|
||||
}
|
||||
}
|
||||
|
||||
public StringMatch(SeString seString)
|
||||
{
|
||||
SeString = seString;
|
||||
}
|
||||
|
||||
private string GetMatchTextInternal()
|
||||
{
|
||||
if (GameObject != null)
|
||||
return GameObject.Name.TextValue;
|
||||
else if (PlayerPayload != null)
|
||||
return PlayerPayload.PlayerName;
|
||||
else
|
||||
return SeString.TextValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the matches text.
|
||||
/// </summary>
|
||||
/// <returns>The match text.</returns>
|
||||
public string GetMatchText()
|
||||
{
|
||||
var playerNamePayload = PlayerNamePayload;
|
||||
if (playerNamePayload is PlayerPayload pp)
|
||||
return pp.PlayerName;
|
||||
else if (playerNamePayload is TextPayload tp)
|
||||
return tp.Text;
|
||||
else
|
||||
return SeString.TextValue;
|
||||
}
|
||||
}
|
||||
|
||||
return stringMatches;
|
||||
}
|
||||
private PluginConfiguration m_PluginConfiguration;
|
||||
private PluginData m_PluginData;
|
||||
|
||||
private void SplitOffPartyNumberPrefix(SeString sender, XivChatType type)
|
||||
{
|
||||
if (type == XivChatType.Party || type == XivChatType.Alliance)
|
||||
public ChatTagTargetFeature(PluginConfiguration pluginConfiguration, PluginData pluginData)
|
||||
{
|
||||
PlayerPayload lastPlayerPayload = null;
|
||||
foreach (var payload in sender.Payloads.ToArray())
|
||||
m_PluginConfiguration = pluginConfiguration;
|
||||
m_PluginData = pluginData;
|
||||
|
||||
PluginServices.ChatGui.ChatMessage += Chat_ChatMessage;
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
PluginServices.ChatGui.ChatMessage -= Chat_ChatMessage;
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
private void Chat_ChatMessage(XivChatType type, uint senderId, ref SeString sender, ref SeString message, ref bool isHandled)
|
||||
{
|
||||
if (m_PluginConfiguration.GeneralConfigs.GetConfig(ActivityContextManager.CurrentActivityContext.ZoneType).IsApplyTagsToAllChatMessagesEnabled)
|
||||
{
|
||||
AddTagsToChat(sender, type, true);
|
||||
AddTagsToChat(message, type, false);
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool IsIconVisible(Tag tag)
|
||||
{
|
||||
if (tag.IsRoleIconVisibleInChat.InheritedValue != null)
|
||||
{
|
||||
return tag.IsRoleIconVisibleInChat.InheritedValue.Value;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override bool IsTextVisible(Tag tag)
|
||||
{
|
||||
if (tag.IsTextVisibleInChat.InheritedValue != null)
|
||||
{
|
||||
return tag.IsTextVisibleInChat.InheritedValue.Value;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches the given string for game object matches.
|
||||
/// </summary>
|
||||
/// <param name="seString">The string to search.</param>
|
||||
/// <returns>A list of matched game objects.</returns>
|
||||
private List<StringMatch> GetStringMatches(SeString seString)
|
||||
{
|
||||
List<StringMatch> stringMatches = new();
|
||||
Stack<PlayerPayload> curPlayerPayload = new();
|
||||
Stack<List<Payload>> curRefPayloads = new();
|
||||
var defaultRawPayload = RawPayload.LinkTerminator.Data;
|
||||
|
||||
foreach (var payload in seString.Payloads)
|
||||
{
|
||||
|
||||
if (payload is PlayerPayload playerPayload)
|
||||
lastPlayerPayload = playerPayload;
|
||||
else if (payload is TextPayload playerNamePayload && lastPlayerPayload != null)
|
||||
{
|
||||
// Get position of player name in payload
|
||||
var indexOfPlayerName = playerNamePayload.Text.IndexOf(BuildPlayername(lastPlayerPayload.PlayerName));
|
||||
curPlayerPayload.Push(playerPayload);
|
||||
curRefPayloads.Push(new List<Payload>());
|
||||
}
|
||||
else if (payload is RawPayload rawPayload)
|
||||
{
|
||||
if (defaultRawPayload.SequenceEqual(rawPayload.Data))
|
||||
finishCurrentMatch(rawPayload);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (curRefPayloads.TryPeek(out List<Payload> result))
|
||||
result.Add(payload);
|
||||
}
|
||||
}
|
||||
|
||||
if (indexOfPlayerName == -1)
|
||||
indexOfPlayerName = playerNamePayload.Text.IndexOf(lastPlayerPayload.PlayerName);
|
||||
// Finally finish, if not closed by RawPayload
|
||||
finishCurrentMatch(null);
|
||||
|
||||
if (indexOfPlayerName > 0)
|
||||
void finishCurrentMatch(RawPayload linkTerminatorPayload)
|
||||
{
|
||||
if (curPlayerPayload.TryPop(out PlayerPayload playerPayload))
|
||||
{
|
||||
var gameObject = PluginServices.ObjectTable.FirstOrDefault(gameObject => gameObject.Name.TextValue == playerPayload.PlayerName);
|
||||
var stringMatch = new StringMatch(seString)
|
||||
{
|
||||
// Split off the name from the prefix number
|
||||
var prefixPayload = new TextPayload(playerNamePayload.Text[..indexOfPlayerName]);
|
||||
playerNamePayload.Text = playerNamePayload.Text[indexOfPlayerName..];
|
||||
GameObject = gameObject,
|
||||
PlayerPayload = playerPayload,
|
||||
LinkTerminatorPayload = linkTerminatorPayload,
|
||||
DisplayTextPayloads = curRefPayloads.Pop()
|
||||
};
|
||||
stringMatches.Add(stringMatch);
|
||||
}
|
||||
}
|
||||
|
||||
// Add prefix number before the player name payload
|
||||
var playerNamePayloadIndex = sender.Payloads.IndexOf(playerNamePayload);
|
||||
sender.Payloads.Insert(playerNamePayloadIndex, prefixPayload);
|
||||
return stringMatches;
|
||||
}
|
||||
|
||||
private void SplitOffPartyNumberPrefix(SeString sender, XivChatType type)
|
||||
{
|
||||
if (type == XivChatType.Party || type == XivChatType.Alliance)
|
||||
{
|
||||
PlayerPayload lastPlayerPayload = null;
|
||||
foreach (var payload in sender.Payloads.ToArray())
|
||||
{
|
||||
if (payload is PlayerPayload playerPayload)
|
||||
lastPlayerPayload = playerPayload;
|
||||
else if (payload is TextPayload playerNamePayload && lastPlayerPayload != null)
|
||||
{
|
||||
// Get position of player name in payload
|
||||
var indexOfPlayerName = playerNamePayload.Text.IndexOf(BuildPlayername(lastPlayerPayload.PlayerName));
|
||||
|
||||
if (indexOfPlayerName == -1)
|
||||
indexOfPlayerName = playerNamePayload.Text.IndexOf(lastPlayerPayload.PlayerName);
|
||||
|
||||
if (indexOfPlayerName > 0)
|
||||
{
|
||||
// Split off the name from the prefix number
|
||||
var prefixPayload = new TextPayload(playerNamePayload.Text[..indexOfPlayerName]);
|
||||
playerNamePayload.Text = playerNamePayload.Text[indexOfPlayerName..];
|
||||
|
||||
// Add prefix number before the player name payload
|
||||
var playerNamePayloadIndex = sender.Payloads.IndexOf(playerNamePayload);
|
||||
sender.Payloads.Insert(playerNamePayloadIndex, prefixPayload);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ParsePayloadsForOwnPlayer(SeString seString, XivChatType chatType, bool isSender)
|
||||
{
|
||||
if (PluginServices.ClientState.LocalPlayer != null)
|
||||
private void ParsePayloadsForOwnPlayer(SeString seString, XivChatType chatType, bool isSender)
|
||||
{
|
||||
foreach (var payload in seString.Payloads.ToArray())
|
||||
if (PluginServices.ClientState.LocalPlayer != null)
|
||||
{
|
||||
if (payload is TextPayload textPayload)
|
||||
foreach (var payload in seString.Payloads.ToArray())
|
||||
{
|
||||
List<TextPayload> playerTextPayloads = [];
|
||||
|
||||
var playerName = PluginServices.ClientState.LocalPlayer.Name.TextValue;
|
||||
var playerNameShorted = BuildPlayername(playerName);
|
||||
|
||||
if (textPayload.Text == playerName || textPayload.Text == playerNameShorted)
|
||||
if (payload is TextPayload textPayload)
|
||||
{
|
||||
playerTextPayloads.Add(textPayload);
|
||||
}
|
||||
else
|
||||
{
|
||||
var usedPlayerName = chatType == XivChatType.Party || chatType == XivChatType.Alliance ? playerNameShorted : playerName;
|
||||
var textMatchIndex = textPayload.Text.IndexOf(usedPlayerName);
|
||||
List<TextPayload> playerTextPayloads = new List<TextPayload>();
|
||||
|
||||
while (textMatchIndex >= 0)
|
||||
var playerName = PluginServices.ClientState.LocalPlayer.Name.TextValue;
|
||||
var playerNameShorted = BuildPlayername(playerName);
|
||||
|
||||
if (textPayload.Text == playerName || textPayload.Text == playerNameShorted)
|
||||
{
|
||||
var textPayloadIndex = seString.Payloads.IndexOf(payload);
|
||||
playerTextPayloads.Add(textPayload);
|
||||
}
|
||||
else
|
||||
{
|
||||
var usedPlayerName = chatType == XivChatType.Party || chatType == XivChatType.Alliance ? playerNameShorted : playerName;
|
||||
var textMatchIndex = textPayload.Text.IndexOf(usedPlayerName);
|
||||
|
||||
// Chop text to the left and insert it as a new payload
|
||||
if (textMatchIndex > 0)
|
||||
while (textMatchIndex >= 0)
|
||||
{
|
||||
// Add the content before the player
|
||||
seString.Payloads.Insert(textPayloadIndex++, new TextPayload(textPayload.Text.Substring(0, textMatchIndex)));
|
||||
var textPayloadIndex = seString.Payloads.IndexOf(payload);
|
||||
|
||||
// Chop text to the left and insert it as a new payload
|
||||
if (textMatchIndex > 0)
|
||||
{
|
||||
// Add the content before the player
|
||||
seString.Payloads.Insert(textPayloadIndex++, new TextPayload(textPayload.Text.Substring(0, textMatchIndex)));
|
||||
|
||||
// Remove from the chopped text from the original payload
|
||||
textPayload.Text = textPayload.Text.Substring(textMatchIndex, textPayload.Text.Length - textMatchIndex);
|
||||
}
|
||||
|
||||
// This is the last reference to the local player in this payload
|
||||
if (textPayload.Text.Length == usedPlayerName.Length)
|
||||
{
|
||||
playerTextPayloads.Add(textPayload);
|
||||
break;
|
||||
}
|
||||
|
||||
// Create the new name payload and add it
|
||||
var playerTextPayload = new TextPayload(usedPlayerName);
|
||||
playerTextPayloads.Add(playerTextPayload);
|
||||
seString.Payloads.Insert(textPayloadIndex, playerTextPayload);
|
||||
|
||||
// Remove from the chopped text from the original payload
|
||||
textPayload.Text = textPayload.Text.Substring(textMatchIndex, textPayload.Text.Length - textMatchIndex);
|
||||
textPayload.Text = textPayload.Text.Substring(usedPlayerName.Length);
|
||||
|
||||
textMatchIndex = textPayload.Text.IndexOf(usedPlayerName);
|
||||
}
|
||||
|
||||
// This is the last reference to the local player in this payload
|
||||
if (textPayload.Text.Length == usedPlayerName.Length)
|
||||
{
|
||||
playerTextPayloads.Add(textPayload);
|
||||
break;
|
||||
}
|
||||
|
||||
// Create the new name payload and add it
|
||||
var playerTextPayload = new TextPayload(usedPlayerName);
|
||||
playerTextPayloads.Add(playerTextPayload);
|
||||
seString.Payloads.Insert(textPayloadIndex, playerTextPayload);
|
||||
|
||||
// Remove from the chopped text from the original payload
|
||||
textPayload.Text = textPayload.Text.Substring(usedPlayerName.Length);
|
||||
|
||||
textMatchIndex = textPayload.Text.IndexOf(usedPlayerName);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var playerTextPayload in playerTextPayloads)
|
||||
{
|
||||
// Fix displaying of abbreviated own player name as the game does this after the chat message handler
|
||||
playerTextPayload.Text = BuildPlayername(playerTextPayload.Text);
|
||||
foreach (var playerTextPayload in playerTextPayloads)
|
||||
{
|
||||
// Fix displaying of abbreviated own player name as the game does this after the chat message handler
|
||||
playerTextPayload.Text = BuildPlayername(playerTextPayload.Text);
|
||||
|
||||
var playerPayload = new PlayerPayload(playerName, PluginServices.ClientState.LocalPlayer.HomeWorld.RowId);
|
||||
int playerPayloadIndex = seString.Payloads.IndexOf(playerTextPayload);
|
||||
var hasNumberPrefix = isSender && (chatType == XivChatType.Party || chatType == XivChatType.Alliance);
|
||||
var playerPayload = new PlayerPayload(playerName, PluginServices.ClientState.LocalPlayer.HomeWorld.Id);
|
||||
int playerPayloadIndex = seString.Payloads.IndexOf(playerTextPayload);
|
||||
var hasNumberPrefix = isSender && (chatType == XivChatType.Party || chatType == XivChatType.Alliance);
|
||||
|
||||
// Ensure to include the group number prefix within the player link
|
||||
if (hasNumberPrefix)
|
||||
playerPayloadIndex--;
|
||||
// Ensure to include the group number prefix within the player link
|
||||
if (hasNumberPrefix)
|
||||
playerPayloadIndex--;
|
||||
|
||||
// Add the Player Link Payload
|
||||
seString.Payloads.Insert(playerPayloadIndex++, playerPayload);
|
||||
// Add the Player Link Payload
|
||||
seString.Payloads.Insert(playerPayloadIndex++, playerPayload);
|
||||
|
||||
// Same as above, but reverse
|
||||
if (hasNumberPrefix)
|
||||
playerPayloadIndex++;
|
||||
// Same as above, but reverse
|
||||
if (hasNumberPrefix)
|
||||
playerPayloadIndex++;
|
||||
|
||||
// Add the Link Terminator to end the Player Link. This should be done behind the Text Payload (display text).
|
||||
// Normally used to end PlayerPayload linking. But for the own player it has no affect. Anyway, use it, just because. Maybe it's needed in the future somewhere else.
|
||||
seString.Payloads.Insert(++playerPayloadIndex, RawPayload.LinkTerminator);
|
||||
// Add the Link Terminator to end the Player Link. This should be done behind the Text Payload (display text).
|
||||
// Normally used to end PlayerPayload linking. But for the own player it has no affect. Anyway, use it, just because. Maybe it's needed in the future somewhere else.
|
||||
seString.Payloads.Insert(++playerPayloadIndex, RawPayload.LinkTerminator);
|
||||
|
||||
// I M P O R T A N T N O T I C E:
|
||||
// The PlayerPayload is now just temporary. We keep the TextPayload.
|
||||
// The PayerPayload gets removed at the ChatTagTargetFeature at the end and the TextPayload will be keeped there.
|
||||
// I M P O R T A N T N O T I C E:
|
||||
// The PlayerPayload is now just temporary. We keep the TextPayload.
|
||||
// The PayerPayload gets removed at the ChatTagTargetFeature at the end and the TextPayload will be keeped there.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds all configured tags to a chat message.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to change.</param>
|
||||
private void AddTagsToChat(SeString message, XivChatType chatType, bool isSender)
|
||||
{
|
||||
// Parse Payloads for local player to be able to work with in the following code
|
||||
ParsePayloadsForOwnPlayer(message, chatType, isSender);
|
||||
|
||||
// Split out the party/alliance number from the PlayerPayload
|
||||
if (isSender)
|
||||
SplitOffPartyNumberPrefix(message, chatType);
|
||||
|
||||
var stringMatches = GetStringMatches(message);
|
||||
foreach (var stringMatch in stringMatches)
|
||||
/// <summary>
|
||||
/// Adds all configured tags to a chat message.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to change.</param>
|
||||
private void AddTagsToChat(SeString message, XivChatType chatType, bool isSender)
|
||||
{
|
||||
StringChanges stringChanges = new();
|
||||
// Parse Payloads for local player to be able to work with in the following code
|
||||
ParsePayloadsForOwnPlayer(message, chatType, isSender);
|
||||
|
||||
bool isTagEnabled(Tag tag)
|
||||
=> tag.TagPositionInChat.InheritedValue != null && tag.TargetChatTypes.InheritedValue != null &&
|
||||
(tag.TargetChatTypes.InheritedValue.Contains(chatType) || (!Enum.IsDefined(chatType) && (tag.TargetChatTypesIncludeUndefined?.InheritedValue ?? false)));
|
||||
// Split out the party/alliance number from the PlayerPayload
|
||||
if (isSender)
|
||||
SplitOffPartyNumberPrefix(message, chatType);
|
||||
|
||||
if (stringMatch.GameObject is IPlayerCharacter playerCharacter)
|
||||
var tagsData = m_PluginData.GetTagsData(ActivityContextManager.CurrentActivityContext.ZoneType);
|
||||
var stringMatches = GetStringMatches(message);
|
||||
|
||||
foreach (var stringMatch in stringMatches)
|
||||
{
|
||||
// Add the job tag
|
||||
if (playerCharacter.ClassJob.ValueNullable is ClassJob classJob && pluginData.JobTags.TryGetValue(classJob.Abbreviation.ParseString(), out var jobTag))
|
||||
{
|
||||
if (isTagEnabled(jobTag))
|
||||
{
|
||||
var payloads = GetPayloads(jobTag, playerCharacter);
|
||||
if (payloads.Any())
|
||||
{
|
||||
var insertBehindNumberPrefix = jobTag.InsertBehindNumberPrefixInChat?.Value ?? true;
|
||||
addPayloadChanges(jobTag, payloads);
|
||||
}
|
||||
}
|
||||
}
|
||||
StringChanges stringChanges = new();
|
||||
|
||||
// Add randomly generated name tag payload
|
||||
if (pluginConfiguration.IsPlayerNameRandomlyGenerated)
|
||||
{
|
||||
var playerName = stringMatch.GetMatchText();
|
||||
if (playerName != null)
|
||||
{
|
||||
var generatedName = BuildPlayername(RandomNameGenerator.Generate(playerName));
|
||||
if (generatedName != null)
|
||||
{
|
||||
AddPayloadChanges(StringPosition.Replace, Enumerable.Empty<Payload>().Append(new TextPayload(generatedName)), stringChanges, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bool isTagEnabled(Tag tag)
|
||||
=> tag.TagPositionInChat.InheritedValue != null && tag.TargetChatTypes.InheritedValue != null &&
|
||||
(tag.TargetChatTypes.InheritedValue.Contains(chatType) || (!Enum.IsDefined(chatType) && (tag.TargetChatTypesIncludeUndefined?.InheritedValue ?? false)));
|
||||
|
||||
// Add custom tags
|
||||
if (stringMatch.PlayerPayload != null)
|
||||
{
|
||||
Identity identity = pluginData.GetIdentity(stringMatch.PlayerPayload);
|
||||
foreach (var customTagId in identity.CustomTagIds)
|
||||
if (stringMatch.GameObject is PlayerCharacter playerCharacter)
|
||||
{
|
||||
var customTag = pluginData.CustomTags.FirstOrDefault(tag => tag.CustomId.Value == customTagId);
|
||||
if (customTag != null)
|
||||
// Add the job tag
|
||||
if (playerCharacter.ClassJob.GameData != null && tagsData.JobTags.TryGetValue(playerCharacter.ClassJob.GameData.Abbreviation, out var jobTag))
|
||||
{
|
||||
if (isTagEnabled(customTag))
|
||||
if (isTagEnabled(jobTag))
|
||||
{
|
||||
var customTagPayloads = GetPayloads(customTag, stringMatch.GameObject);
|
||||
if (customTagPayloads.Any())
|
||||
var payloads = GetPayloads(jobTag, stringMatch.GameObject);
|
||||
if (payloads.Any())
|
||||
{
|
||||
var insertBehindNumberPrefix = customTag.InsertBehindNumberPrefixInChat?.Value ?? true;
|
||||
addPayloadChanges(customTag, customTagPayloads);
|
||||
var insertBehindNumberPrefix = jobTag.InsertBehindNumberPrefixInChat?.Value ?? true;
|
||||
addPayloadChanges(jobTag, payloads);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add randomly generated name tag payload
|
||||
if (m_PluginConfiguration.IsPlayerNameRandomlyGenerated)
|
||||
{
|
||||
var playerName = stringMatch.GetMatchText();
|
||||
if (playerName != null)
|
||||
{
|
||||
var generatedName = BuildPlayername(RandomNameGenerator.Generate(playerName));
|
||||
if (generatedName != null)
|
||||
{
|
||||
AddPayloadChanges(StringPosition.Replace, Enumerable.Empty<Payload>().Append(new TextPayload(generatedName)), stringChanges, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addPayloadChanges(Tag tag, IEnumerable<Payload> payloads)
|
||||
{
|
||||
var insertBehindNumberPrefix = tag.InsertBehindNumberPrefixInChat?.Value ?? true;
|
||||
var insertPositionInChat = tag.TagPositionInChat.InheritedValue.Value;
|
||||
AddPayloadChanges((StringPosition)insertPositionInChat, payloads, stringChanges, insertBehindNumberPrefix);
|
||||
}
|
||||
|
||||
// An additional step to apply text color to additional locations
|
||||
if (stringMatch.PlayerPayload != null && stringMatch.DisplayTextPayloads.Any())
|
||||
{
|
||||
Identity identity = pluginData.GetIdentity(stringMatch.PlayerPayload);
|
||||
|
||||
if (stringMatch.GameObject is IPlayerCharacter playerCharacter1)
|
||||
{
|
||||
if (playerCharacter1.ClassJob.ValueNullable is ClassJob classJob && pluginData.JobTags.TryGetValue(classJob.Abbreviation.ParseString(), out var jobTag) && isTagEnabled(jobTag))
|
||||
applyTextFormatting(jobTag);
|
||||
}
|
||||
|
||||
foreach (var customTagId in identity.CustomTagIds)
|
||||
{
|
||||
var customTag = pluginData.CustomTags.FirstOrDefault(tag => tag.CustomId.Value == customTagId);
|
||||
if (customTag != null && isTagEnabled(customTag))
|
||||
applyTextFormatting(customTag);
|
||||
}
|
||||
|
||||
void applyTextFormatting(Tag tag)
|
||||
=> ApplyTextFormatting(stringMatch.GameObject, tag, new[] { message }, new[] { tag.IsTextColorAppliedToChatName }, stringMatch.DisplayTextPayloads);
|
||||
}
|
||||
|
||||
// Finally apply the all the changes to the message
|
||||
ApplyStringChanges(message, stringChanges, stringMatch.DisplayTextPayloads, stringMatch.PlayerNamePayload);
|
||||
|
||||
// Remove PlayerPayload and LinkTerminator if it's your own character (they just got added temporary)
|
||||
if (stringMatch.IsLocalPlayer)
|
||||
{
|
||||
// Add custom tags
|
||||
if (stringMatch.PlayerPayload != null)
|
||||
message.Remove(stringMatch.PlayerPayload);
|
||||
if (stringMatch.LinkTerminatorPayload != null)
|
||||
message.Remove(stringMatch.LinkTerminatorPayload);
|
||||
{
|
||||
Identity identity = tagsData.GetIdentity(stringMatch.PlayerPayload);
|
||||
foreach (var customTagId in identity.CustomTagIds)
|
||||
{
|
||||
var customTag = tagsData.CustomTags.FirstOrDefault(tag => tag.CustomId.Value == customTagId);
|
||||
if (customTag != null)
|
||||
{
|
||||
if (isTagEnabled(customTag))
|
||||
{
|
||||
var customTagPayloads = GetPayloads(customTag, stringMatch.GameObject);
|
||||
if (customTagPayloads.Any())
|
||||
{
|
||||
var insertBehindNumberPrefix = customTag.InsertBehindNumberPrefixInChat?.Value ?? true;
|
||||
addPayloadChanges(customTag, customTagPayloads);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addPayloadChanges(Tag tag, IEnumerable<Payload> payloads)
|
||||
{
|
||||
var insertBehindNumberPrefix = tag.InsertBehindNumberPrefixInChat?.Value ?? true;
|
||||
var insertPositionInChat = tag.TagPositionInChat.InheritedValue.Value;
|
||||
AddPayloadChanges((StringPosition)insertPositionInChat, payloads, stringChanges, insertBehindNumberPrefix);
|
||||
}
|
||||
|
||||
// An additional step to apply text color to additional locations
|
||||
if (stringMatch.PlayerPayload != null && stringMatch.DisplayTextPayloads.Any())
|
||||
{
|
||||
Identity identity = tagsData.GetIdentity(stringMatch.PlayerPayload);
|
||||
|
||||
if (stringMatch.GameObject is PlayerCharacter playerCharacter1)
|
||||
{
|
||||
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 = tagsData.CustomTags.FirstOrDefault(tag => tag.CustomId.Value == customTagId);
|
||||
if (customTag != null && isTagEnabled(customTag))
|
||||
applyTextFormatting(customTag);
|
||||
}
|
||||
|
||||
void applyTextFormatting(Tag tag)
|
||||
=> ApplyTextFormatting(stringMatch.GameObject, tag, new[] { message }, new[] { tag.IsTextColorAppliedToChatName }, stringMatch.DisplayTextPayloads);
|
||||
}
|
||||
|
||||
// Finally apply the all the changes to the message
|
||||
ApplyStringChanges(message, stringChanges, stringMatch.DisplayTextPayloads, stringMatch.PlayerNamePayload);
|
||||
|
||||
// Remove PlayerPayload and LinkTerminator if it's your own character (they just got added temporary)
|
||||
if (stringMatch.IsLocalPlayer)
|
||||
{
|
||||
if (stringMatch.PlayerPayload != null)
|
||||
message.Remove(stringMatch.PlayerPayload);
|
||||
if (stringMatch.LinkTerminatorPayload != null)
|
||||
message.Remove(stringMatch.LinkTerminatorPayload);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyTextFormatting(IGameObject? gameObject, Tag tag, SeString[] destStrings, InheritableValue<bool>[] textColorApplied, List<Payload> preferedPayloads, ushort? overwriteTextColor = null)
|
||||
{
|
||||
if (IsTagVisible(tag, gameObject))
|
||||
{
|
||||
for (int i = 0; i < destStrings.Length; i++)
|
||||
{
|
||||
var destString = destStrings[i];
|
||||
var isTextColorApplied = textColorApplied[i];
|
||||
applyTextColor(destString, isTextColorApplied, tag.TextColor);
|
||||
}
|
||||
}
|
||||
|
||||
void applyTextColor(SeString destPayload, InheritableValue<bool> enableFlag, InheritableValue<ushort> colorValue)
|
||||
{
|
||||
var colorToUse = overwriteTextColor ?? colorValue?.InheritedValue;
|
||||
if (shouldApplyFormattingPayloads(destPayload)
|
||||
&& enableFlag.InheritedValue != null
|
||||
&& enableFlag.InheritedValue.Value
|
||||
&& colorToUse != null)
|
||||
applyTextFormattingPayloads(destPayload, new UIForegroundPayload(colorToUse.Value), new UIForegroundPayload(0));
|
||||
}
|
||||
|
||||
bool shouldApplyFormattingPayloads(SeString destPayload)
|
||||
=> destPayload.Payloads.Any(payload => payload is TextPayload || payload is PlayerPayload);
|
||||
|
||||
void applyTextFormattingPayloads(SeString destPayload, Payload startPayload, Payload endPayload)
|
||||
{
|
||||
if (preferedPayloads == null || !preferedPayloads.Any())
|
||||
applyTextFormattingPayloadToStartAndEnd(destPayload, startPayload, endPayload);
|
||||
else
|
||||
applyTextFormattingPayloadsToSpecificPosition(destPayload, startPayload, endPayload, preferedPayloads);
|
||||
}
|
||||
|
||||
void applyTextFormattingPayloadToStartAndEnd(SeString destPayload, Payload startPayload, Payload endPayload)
|
||||
{
|
||||
destPayload.Payloads.Insert(0, startPayload);
|
||||
destPayload.Payloads.Add(endPayload);
|
||||
}
|
||||
|
||||
void applyTextFormattingPayloadsToSpecificPosition(SeString destPayload, Payload startPayload, Payload endPayload, List<Payload> preferedPayload)
|
||||
{
|
||||
int payloadStartIndex = destPayload.Payloads.IndexOf(preferedPayloads.First());
|
||||
destPayload.Payloads.Insert(payloadStartIndex, startPayload);
|
||||
|
||||
int payloadEndIndex = destPayload.Payloads.IndexOf(preferedPayloads.Last());
|
||||
destPayload.Payloads.Insert(payloadEndIndex + 1, endPayload);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies changes to the given string.
|
||||
/// </summary>
|
||||
/// <param name="seString">The string to apply changes to.</param>
|
||||
/// <param name="stringChanges">The changes to apply.</param>
|
||||
/// <param name="anchorPayload">The payload in the string that changes should be anchored to. If there is no anchor, the changes will be applied to the entire string.</param>
|
||||
protected void ApplyStringChanges(SeString seString, StringChanges stringChanges, List<Payload> anchorPayloads = null, Payload anchorReplacePayload = null)
|
||||
{
|
||||
var props = new StringChangesProps
|
||||
{
|
||||
Destination = seString,
|
||||
AnchorPayload = anchorReplacePayload,
|
||||
AnchorPayloads = anchorPayloads,
|
||||
StringChanges = stringChanges
|
||||
};
|
||||
|
||||
StringUpdateFactory.ApplyStringChanges(props);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using Dalamud.Game.Gui.ContextMenu;
|
||||
using Dalamud.ContextMenu;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Logging;
|
||||
using Pilz.Dalamud.ActivityContexts;
|
||||
using PlayerTags.Configuration;
|
||||
using PlayerTags.Data;
|
||||
using PlayerTags.Resources;
|
||||
@@ -6,81 +9,94 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace PlayerTags.Features;
|
||||
|
||||
/// <summary>
|
||||
/// A feature that adds options for the management of custom tags to context menus.
|
||||
/// </summary>
|
||||
public class CustomTagsContextMenuFeature : FeatureBase, IDisposable
|
||||
namespace PlayerTags.Features
|
||||
{
|
||||
private readonly string[] supportedAddonNames =
|
||||
[
|
||||
null,
|
||||
"_PartyList",
|
||||
"ChatLog",
|
||||
"ContactList",
|
||||
"ContentMemberList",
|
||||
"CrossWorldLinkshell",
|
||||
"FreeCompany",
|
||||
"FriendList",
|
||||
"LookingForGroup",
|
||||
"LinkShell",
|
||||
"PartyMemberList",
|
||||
"SocialList",
|
||||
];
|
||||
|
||||
public CustomTagsContextMenuFeature(PluginConfiguration pluginConfiguration, PluginData pluginData) : base(pluginConfiguration, pluginData)
|
||||
/// <summary>
|
||||
/// A feature that adds options for the management of custom tags to context menus.
|
||||
/// </summary>
|
||||
public class CustomTagsContextMenuFeature : FeatureBase
|
||||
{
|
||||
PluginServices.ContextMenu.OnMenuOpened += ContextMenu_OnMenuOpened; ;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
PluginServices.ContextMenu.OnMenuOpened -= ContextMenu_OnMenuOpened;
|
||||
}
|
||||
|
||||
private void ContextMenu_OnMenuOpened(IMenuOpenedArgs args)
|
||||
{
|
||||
if (!EnableGlobal || !pluginConfiguration.IsCustomTagsContextMenuEnabled
|
||||
|| args.MenuType != ContextMenuType.Default
|
||||
|| args.Target is not MenuTargetDefault menuTarget
|
||||
|| !supportedAddonNames.Contains(args.AddonName))
|
||||
return;
|
||||
|
||||
Identity? identity = pluginData.GetIdentity(menuTarget);
|
||||
if (identity != null)
|
||||
private string?[] SupportedAddonNames = new string?[]
|
||||
{
|
||||
var allTags = new Dictionary<Tag, bool>();
|
||||
foreach (var customTag in pluginData.CustomTags)
|
||||
null,
|
||||
"_PartyList",
|
||||
"ChatLog",
|
||||
"ContactList",
|
||||
"ContentMemberList",
|
||||
"CrossWorldLinkshell",
|
||||
"FreeCompany",
|
||||
"FriendList",
|
||||
"LookingForGroup",
|
||||
"LinkShell",
|
||||
"PartyMemberList",
|
||||
"SocialList",
|
||||
};
|
||||
|
||||
private PluginConfiguration m_PluginConfiguration;
|
||||
private PluginData m_PluginData;
|
||||
private DalamudContextMenu? m_ContextMenu;
|
||||
|
||||
public CustomTagsContextMenuFeature(PluginConfiguration pluginConfiguration, PluginData pluginData)
|
||||
{
|
||||
m_PluginConfiguration = pluginConfiguration;
|
||||
m_PluginData = pluginData;
|
||||
|
||||
m_ContextMenu = new DalamudContextMenu();
|
||||
m_ContextMenu.OnOpenGameObjectContextMenu += ContextMenuHooks_ContextMenuOpened;
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (m_ContextMenu != null)
|
||||
{
|
||||
var isAdded = identity.CustomTagIds.Contains(customTag.CustomId.Value);
|
||||
allTags.Add(customTag, isAdded);
|
||||
m_ContextMenu.OnOpenGameObjectContextMenu -= ContextMenuHooks_ContextMenuOpened;
|
||||
((IDisposable)m_ContextMenu).Dispose();
|
||||
m_ContextMenu = null;
|
||||
}
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
var sortedTags = allTags.OrderBy(n => n.Value);
|
||||
foreach (var tag in sortedTags)
|
||||
private void ContextMenuHooks_ContextMenuOpened(GameObjectContextMenuOpenArgs contextMenuOpenedArgs)
|
||||
{
|
||||
if (!m_PluginConfiguration.IsCustomTagsContextMenuEnabled
|
||||
|| !SupportedAddonNames.Contains(contextMenuOpenedArgs.ParentAddonName))
|
||||
return;
|
||||
|
||||
var tagsData = m_PluginData.GetTagsData(ActivityContextManager.CurrentActivityContext.ZoneType);
|
||||
Identity? identity = tagsData.GetIdentity(contextMenuOpenedArgs);
|
||||
|
||||
if (identity != null)
|
||||
{
|
||||
string menuItemText;
|
||||
if (tag.Value)
|
||||
menuItemText = Strings.Loc_Static_ContextMenu_RemoveTag;
|
||||
else
|
||||
menuItemText = Strings.Loc_Static_ContextMenu_AddTag;
|
||||
menuItemText = string.Format(menuItemText, tag.Key.Text.Value);
|
||||
|
||||
args.AddMenuItem(new()
|
||||
var allTags = new Dictionary<Tag, bool>();
|
||||
foreach (var customTag in tagsData.CustomTags)
|
||||
{
|
||||
IsSubmenu = false,
|
||||
IsEnabled = true,
|
||||
Name = menuItemText,
|
||||
OnClicked = openedEventArgs =>
|
||||
{
|
||||
if (tag.Value)
|
||||
pluginData.RemoveCustomTagFromIdentity(tag.Key, identity);
|
||||
else
|
||||
pluginData.AddCustomTagToIdentity(tag.Key, identity);
|
||||
pluginConfiguration.Save(pluginData);
|
||||
},
|
||||
});
|
||||
var isAdded = identity.CustomTagIds.Contains(customTag.CustomId.Value);
|
||||
allTags.Add(customTag, isAdded);
|
||||
}
|
||||
|
||||
var sortedTags = allTags.OrderBy(n => n.Value);
|
||||
foreach (var tag in sortedTags)
|
||||
{
|
||||
string menuItemText;
|
||||
if (tag.Value)
|
||||
menuItemText = Strings.Loc_Static_ContextMenu_RemoveTag;
|
||||
else
|
||||
menuItemText = Strings.Loc_Static_ContextMenu_AddTag;
|
||||
menuItemText = string.Format(menuItemText, tag.Key.Text.Value);
|
||||
|
||||
contextMenuOpenedArgs.AddCustomItem(
|
||||
new GameObjectContextMenuItem(menuItemText, openedEventArgs =>
|
||||
{
|
||||
if (tag.Value)
|
||||
tagsData.RemoveCustomTagFromIdentity(tag.Key, identity);
|
||||
else
|
||||
tagsData.AddCustomTagToIdentity(tag.Key, identity);
|
||||
m_PluginConfiguration.Save(m_PluginData);
|
||||
})
|
||||
{
|
||||
IsSubMenu = false
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
using PlayerTags.Configuration;
|
||||
using PlayerTags.Data;
|
||||
using Pilz.Dalamud.ActivityContexts;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PlayerTags.Features;
|
||||
|
||||
public class FeatureBase
|
||||
namespace PlayerTags.Features
|
||||
{
|
||||
protected readonly PluginConfiguration pluginConfiguration;
|
||||
protected readonly PluginData pluginData;
|
||||
|
||||
public virtual bool EnableGlobal => pluginConfiguration.EnabledGlobal;
|
||||
|
||||
protected FeatureBase(PluginConfiguration pluginConfiguration, PluginData pluginData)
|
||||
public abstract class FeatureBase : IDisposable
|
||||
{
|
||||
this.pluginConfiguration = pluginConfiguration;
|
||||
this.pluginData = pluginData;
|
||||
public ActivityContextManager ActivityContextManager { get; init; }
|
||||
|
||||
public FeatureBase()
|
||||
{
|
||||
ActivityContextManager = new();
|
||||
}
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
ActivityContextManager.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,223 +1,288 @@
|
||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||
using Dalamud.Game.Gui.NamePlate;
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||
using Lumina.Excel.Sheets;
|
||||
using Pilz.Dalamud;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Memory;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Pilz.Dalamud.Icons;
|
||||
using Pilz.Dalamud.Tools.NamePlates;
|
||||
using Pilz.Dalamud.Nameplates.Tools;
|
||||
using Pilz.Dalamud.Tools.Strings;
|
||||
using PlayerTags.Configuration;
|
||||
using PlayerTags.Data;
|
||||
using PlayerTags.GameInterface.Nameplates;
|
||||
using PlayerTags.Inheritables;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace PlayerTags.Features;
|
||||
|
||||
/// <summary>
|
||||
/// A feature that adds tags to nameplates.
|
||||
/// </summary>
|
||||
public class NameplateTagTargetFeature : TagTargetFeature
|
||||
namespace PlayerTags.Features
|
||||
{
|
||||
private readonly StatusIconPriorizer statusiconPriorizer;
|
||||
private readonly JobIconSets jobIconSets = new();
|
||||
|
||||
public NameplateTagTargetFeature(PluginConfiguration pluginConfiguration, PluginData pluginData) : base(pluginConfiguration, pluginData)
|
||||
/// <summary>
|
||||
/// A feature that adds tags to nameplates.
|
||||
/// </summary>
|
||||
public class NameplateTagTargetFeature : TagTargetFeature
|
||||
{
|
||||
statusiconPriorizer = new(pluginConfiguration.StatusIconPriorizerSettings);
|
||||
PluginServices.NamePlateGui.OnNamePlateUpdate += NamePlateGui_OnNamePlateUpdate;
|
||||
}
|
||||
private readonly PluginConfiguration m_PluginConfiguration;
|
||||
private readonly PluginData m_PluginData;
|
||||
private readonly StatusIconPriorizer statusiconPriorizer;
|
||||
private readonly JobIconSets jobIconSets = new();
|
||||
private Nameplate? m_Nameplate;
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
PluginServices.NamePlateGui.OnNamePlateUpdate -= NamePlateGui_OnNamePlateUpdate;
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
protected override bool IsIconVisible(Tag tag)
|
||||
{
|
||||
if (tag.IsRoleIconVisibleInNameplates.InheritedValue != null)
|
||||
return tag.IsRoleIconVisibleInNameplates.InheritedValue.Value;
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override bool IsTextVisible(Tag tag)
|
||||
{
|
||||
if (tag.IsTextVisibleInNameplates.InheritedValue != null)
|
||||
return tag.IsTextVisibleInNameplates.InheritedValue.Value;
|
||||
return false;
|
||||
}
|
||||
|
||||
private void NamePlateGui_OnNamePlateUpdate(INamePlateUpdateContext context, IReadOnlyList<INamePlateUpdateHandler> handlers)
|
||||
{
|
||||
if (!EnableGlobal)
|
||||
return;
|
||||
|
||||
foreach (var handler in handlers)
|
||||
public NameplateTagTargetFeature(PluginConfiguration pluginConfiguration, PluginData pluginData)
|
||||
{
|
||||
// Only handle player nameplates
|
||||
if (handler.NamePlateKind != NamePlateKind.PlayerCharacter || handler.PlayerCharacter == null)
|
||||
continue;
|
||||
m_PluginConfiguration = pluginConfiguration;
|
||||
m_PluginData = pluginData;
|
||||
statusiconPriorizer = new(pluginConfiguration.StatusIconPriorizerSettings);
|
||||
|
||||
var beforeTitleBytes = handler.InfoView.Title.Encode();
|
||||
var generalOptions = pluginConfiguration.GeneralOptions[ActivityContextManager.CurrentActivityContext.ActivityType];
|
||||
PluginServices.ClientState.Login += ClientState_Login;
|
||||
PluginServices.ClientState.Logout += ClientState_Logout;
|
||||
|
||||
AddTagsToNameplate(handler, generalOptions);
|
||||
Hook();
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
Unhook();
|
||||
|
||||
PluginServices.ClientState.Logout -= ClientState_Logout;
|
||||
PluginServices.ClientState.Login -= ClientState_Login;
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
private void Hook()
|
||||
{
|
||||
if (m_Nameplate == null)
|
||||
{
|
||||
m_Nameplate = new Nameplate();
|
||||
if (!m_Nameplate.IsValid)
|
||||
{
|
||||
m_Nameplate = null;
|
||||
}
|
||||
|
||||
if (m_Nameplate != null)
|
||||
{
|
||||
m_Nameplate.PlayerNameplateUpdated += Nameplate_PlayerNameplateUpdated;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Unhook()
|
||||
{
|
||||
if (m_Nameplate != null)
|
||||
{
|
||||
m_Nameplate.PlayerNameplateUpdated -= Nameplate_PlayerNameplateUpdated;
|
||||
m_Nameplate.Dispose();
|
||||
m_Nameplate = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void ClientState_Login(object? sender, EventArgs e)
|
||||
{
|
||||
Hook();
|
||||
}
|
||||
|
||||
private void ClientState_Logout(object? sender, EventArgs e)
|
||||
{
|
||||
Unhook();
|
||||
}
|
||||
|
||||
protected override bool IsIconVisible(Tag tag)
|
||||
{
|
||||
if (tag.IsRoleIconVisibleInNameplates.InheritedValue != null)
|
||||
{
|
||||
return tag.IsRoleIconVisibleInNameplates.InheritedValue.Value;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override bool IsTextVisible(Tag tag)
|
||||
{
|
||||
if (tag.IsTextVisibleInNameplates.InheritedValue != null)
|
||||
{
|
||||
return tag.IsTextVisibleInNameplates.InheritedValue.Value;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private unsafe void Nameplate_PlayerNameplateUpdated(PlayerNameplateUpdatedArgs args)
|
||||
{
|
||||
var beforeTitleBytes = args.Title.Encode();
|
||||
var iconID = args.IconId;
|
||||
var generalOptions = m_PluginConfiguration.GeneralConfigs.GetConfig(ActivityContextManager.CurrentActivityContext.ZoneType);
|
||||
|
||||
AddTagsToNameplate(args.PlayerCharacter, args.Name, args.Title, args.FreeCompany, ref iconID);
|
||||
|
||||
args.IconId = iconID;
|
||||
|
||||
if (generalOptions.NameplateTitlePosition == NameplateTitlePosition.AlwaysAboveName)
|
||||
handler.IsPrefixTitle = true;
|
||||
args.IsTitleAboveName = true;
|
||||
else if (generalOptions.NameplateTitlePosition == NameplateTitlePosition.AlwaysBelowName)
|
||||
handler.IsPrefixTitle = false;
|
||||
args.IsTitleAboveName = false;
|
||||
|
||||
if (generalOptions.NameplateTitleVisibility == NameplateTitleVisibility.Always)
|
||||
handler.DisplayTitle = true;
|
||||
args.IsTitleVisible = true;
|
||||
else if (generalOptions.NameplateTitleVisibility == NameplateTitleVisibility.Never)
|
||||
handler.DisplayTitle = false;
|
||||
args.IsTitleVisible = false;
|
||||
else if (generalOptions.NameplateTitleVisibility == NameplateTitleVisibility.WhenHasTags)
|
||||
handler.DisplayTitle = !beforeTitleBytes.SequenceEqual(handler.InfoView.Title.Encode());
|
||||
{
|
||||
bool hasTitleChanged = !beforeTitleBytes.SequenceEqual(args.Title.Encode());
|
||||
args.IsTitleVisible = hasTitleChanged;
|
||||
}
|
||||
|
||||
if (generalOptions.NameplateFreeCompanyVisibility == NameplateFreeCompanyVisibility.Never)
|
||||
handler.RemoveFreeCompanyTag();
|
||||
args.FreeCompany.Payloads.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given payload changes to the specified locations.
|
||||
/// </summary>
|
||||
/// <param name="nameplateElement">The nameplate element of the changes.</param>
|
||||
/// <param name="tagPosition">The position of the changes.</param>
|
||||
/// <param name="payloadChanges">The payload changes to add.</param>
|
||||
/// <param name="nameplateChanges">The dictionary to add changes to.</param>
|
||||
private void AddPayloadChanges(NameplateElement nameplateElement, TagPosition tagPosition, IEnumerable<Payload> payloadChanges, NameplateChanges nameplateChanges, bool forceUsingSingleAnchorPayload)
|
||||
{
|
||||
if (payloadChanges.Any())
|
||||
/// <summary>
|
||||
/// Adds the given payload changes to the specified locations.
|
||||
/// </summary>
|
||||
/// <param name="nameplateElement">The nameplate element of the changes.</param>
|
||||
/// <param name="tagPosition">The position of the changes.</param>
|
||||
/// <param name="payloadChanges">The payload changes to add.</param>
|
||||
/// <param name="nameplateChanges">The dictionary to add changes to.</param>
|
||||
private void AddPayloadChanges(NameplateElement nameplateElement, TagPosition tagPosition, IEnumerable<Payload> payloadChanges, NameplateChanges nameplateChanges, bool forceUsingSingleAnchorPayload)
|
||||
{
|
||||
var changes = nameplateChanges.GetChange((NameplateElements)nameplateElement);
|
||||
AddPayloadChanges((StringPosition)tagPosition, payloadChanges, changes.Changes, forceUsingSingleAnchorPayload);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds tags to the nameplate of a game object.
|
||||
/// </summary>
|
||||
/// <param name="playerCharacter">The game object context.</param>
|
||||
/// <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(INamePlateUpdateHandler handler, GeneralOptionsClass generalOptions)
|
||||
{
|
||||
int? newStatusIcon = null;
|
||||
var nameplateChanges = new NameplateChanges(handler);
|
||||
|
||||
if (handler.PlayerCharacter != null && (!handler.PlayerCharacter.IsDead || generalOptions.NameplateDeadPlayerHandling != DeadPlayerHandling.Ignore))
|
||||
{
|
||||
var classJob = handler.PlayerCharacter.ClassJob.ValueNullable;
|
||||
|
||||
// Add the job tags
|
||||
if (classJob.HasValue && pluginData.JobTags.TryGetValue(classJob.Value.Abbreviation.ParseString(), out var jobTag))
|
||||
if (payloadChanges.Any())
|
||||
{
|
||||
if (jobTag.TagTargetInNameplates.InheritedValue != null && jobTag.TagPositionInNameplates.InheritedValue != null)
|
||||
checkTag(jobTag);
|
||||
var changes = nameplateChanges.GetChanges((NameplateElements)nameplateElement);
|
||||
AddPayloadChanges((StringPosition)tagPosition, payloadChanges, changes, forceUsingSingleAnchorPayload);
|
||||
}
|
||||
}
|
||||
|
||||
// Add the randomly generated name tag payload
|
||||
if (pluginConfiguration.IsPlayerNameRandomlyGenerated)
|
||||
private NameplateChanges GenerateEmptyNameplateChanges(SeString name, SeString title, SeString freeCompany)
|
||||
{
|
||||
NameplateChanges nameplateChanges = new();
|
||||
|
||||
nameplateChanges.GetProps(NameplateElements.Name).Destination = name;
|
||||
nameplateChanges.GetProps(NameplateElements.Title).Destination = title;
|
||||
nameplateChanges.GetProps(NameplateElements.FreeCompany).Destination = freeCompany;
|
||||
|
||||
return nameplateChanges;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds tags to the nameplate of a game object.
|
||||
/// </summary>
|
||||
/// <param name="gameObject">The game object context.</param>
|
||||
/// <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)
|
||||
{
|
||||
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 || generalConfig.NameplateDeadPlayerHandling != DeadPlayerHandling.Ignore))
|
||||
{
|
||||
var characterName = handler.PlayerCharacter.Name.TextValue;
|
||||
if (characterName != null)
|
||||
var classJob = playerCharacter.ClassJob;
|
||||
var classJobGameData = classJob?.GameData;
|
||||
|
||||
// Add the job tags
|
||||
if (classJobGameData != null && tagsData.JobTags.TryGetValue(classJobGameData.Abbreviation, out var jobTag))
|
||||
{
|
||||
var generatedName = RandomNameGenerator.Generate(characterName);
|
||||
if (generatedName != null)
|
||||
AddPayloadChanges(NameplateElement.Name, TagPosition.Replace, Enumerable.Empty<Payload>().Append(new TextPayload(generatedName)), nameplateChanges, false);
|
||||
if (jobTag.TagTargetInNameplates.InheritedValue != null && jobTag.TagPositionInNameplates.InheritedValue != null)
|
||||
checkTag(jobTag);
|
||||
}
|
||||
|
||||
// Add the randomly generated name tag payload
|
||||
if (m_PluginConfiguration.IsPlayerNameRandomlyGenerated)
|
||||
{
|
||||
var characterName = playerCharacter.Name.TextValue;
|
||||
if (characterName != null)
|
||||
{
|
||||
var generatedName = RandomNameGenerator.Generate(characterName);
|
||||
if (generatedName != null)
|
||||
AddPayloadChanges(NameplateElement.Name, TagPosition.Replace, Enumerable.Empty<Payload>().Append(new TextPayload(generatedName)), nameplateChanges, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Add custom tags
|
||||
Identity identity = tagsData.GetIdentity(playerCharacter);
|
||||
foreach (var customTagId in identity.CustomTagIds)
|
||||
{
|
||||
var customTag = tagsData.CustomTags.FirstOrDefault(tag => tag.CustomId.Value == customTagId);
|
||||
if (customTag != null)
|
||||
checkTag(customTag);
|
||||
}
|
||||
|
||||
void checkTag(Tag tag)
|
||||
{
|
||||
if (tag.TagTargetInNameplates.InheritedValue != null && tag.TagPositionInNameplates.InheritedValue != null)
|
||||
{
|
||||
var payloads = GetPayloads(tag, gameObject);
|
||||
if (payloads.Any())
|
||||
AddPayloadChanges(tag.TagTargetInNameplates.InheritedValue.Value, tag.TagPositionInNameplates.InheritedValue.Value, payloads, nameplateChanges, false);
|
||||
}
|
||||
if (IsTagVisible(tag, gameObject) && newStatusIcon == null && classJob != null && (tag.IsJobIconVisibleInNameplates?.InheritedValue ?? false))
|
||||
newStatusIcon = jobIconSets.GetJobIcon(tag.JobIconSet?.InheritedValue ?? JobIconSetName.Framed, classJob.Id);
|
||||
}
|
||||
}
|
||||
|
||||
// Add custom tags
|
||||
Identity identity = pluginData.GetIdentity(handler.PlayerCharacter);
|
||||
foreach (var customTagId in identity.CustomTagIds)
|
||||
// Apply new status icon
|
||||
if (newStatusIcon != null)
|
||||
{
|
||||
var customTag = pluginData.CustomTags.FirstOrDefault(tag => tag.CustomId.Value == customTagId);
|
||||
if (customTag != null)
|
||||
checkTag(customTag);
|
||||
var change = nameplateChanges.GetChange(NameplateElements.Name, StringPosition.Before);
|
||||
NameplateUpdateFactory.ApplyStatusIconWithPrio(ref statusIcon, (int)newStatusIcon, change, ActivityContextManager.CurrentActivityContext, statusiconPriorizer, m_PluginConfiguration.MoveStatusIconToNameplateTextIfPossible);
|
||||
}
|
||||
|
||||
void checkTag(Tag tag)
|
||||
// Gray out the nameplate
|
||||
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 || generalConfig.NameplateDeadPlayerHandling == DeadPlayerHandling.Include))
|
||||
{
|
||||
if (tag.TagTargetInNameplates.InheritedValue != null && tag.TagPositionInNameplates.InheritedValue != null)
|
||||
// An additional step to apply text color to additional locations
|
||||
Identity identity = tagsData.GetIdentity(playerCharacter);
|
||||
foreach (var customTagId in identity.CustomTagIds)
|
||||
{
|
||||
var payloads = GetPayloads(tag, handler.PlayerCharacter);
|
||||
if (payloads.Length != 0)
|
||||
AddPayloadChanges(tag.TagTargetInNameplates.InheritedValue.Value, tag.TagPositionInNameplates.InheritedValue.Value, payloads, nameplateChanges, false);
|
||||
var customTag = tagsData.CustomTags.FirstOrDefault(tag => tag.CustomId.Value == customTagId);
|
||||
if (customTag != null)
|
||||
applyTextFormatting(customTag);
|
||||
}
|
||||
if (IsTagVisible(tag, handler.PlayerCharacter) && newStatusIcon == null && classJob != null && (tag.IsJobIconVisibleInNameplates?.InheritedValue ?? false))
|
||||
newStatusIcon = jobIconSets.GetJobIcon(tag.JobIconSet?.InheritedValue ?? JobIconSetName.Framed, classJob.Value.RowId);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply new status icon
|
||||
if (newStatusIcon != null)
|
||||
{
|
||||
NameplateUpdateFactory.ApplyStatusIconWithPrio(handler, (int)newStatusIcon, ActivityContextManager.CurrentActivityContext, statusiconPriorizer, pluginConfiguration.MoveStatusIconToNameplateTextIfPossible);
|
||||
}
|
||||
if (playerCharacter.ClassJob.GameData != null && tagsData.JobTags.TryGetValue(playerCharacter.ClassJob.GameData.Abbreviation, out var jobTag))
|
||||
applyTextFormatting(jobTag);
|
||||
|
||||
// Build the final strings out of the payloads
|
||||
NameplateUpdateFactory.ApplyNameplateChanges(new NameplateChangesProps
|
||||
{
|
||||
Changes = nameplateChanges
|
||||
});
|
||||
|
||||
// Gray out the nameplate
|
||||
if (handler.PlayerCharacter != null && handler.PlayerCharacter.IsDead && generalOptions.NameplateDeadPlayerHandling == DeadPlayerHandling.GrayOut)
|
||||
GrayOutNameplate(handler.PlayerCharacter, nameplateChanges);
|
||||
|
||||
// Apply text color
|
||||
if (handler.PlayerCharacter != null && (!handler.PlayerCharacter.IsDead || generalOptions.NameplateDeadPlayerHandling == DeadPlayerHandling.Include))
|
||||
{
|
||||
Identity identity = pluginData.GetIdentity(handler.PlayerCharacter);
|
||||
foreach (var customTagId in identity.CustomTagIds)
|
||||
{
|
||||
var customTag = pluginData.CustomTags.FirstOrDefault(tag => tag.CustomId.Value == customTagId);
|
||||
if (customTag != null)
|
||||
applyTextFormatting(customTag);
|
||||
}
|
||||
|
||||
if (handler.PlayerCharacter.ClassJob.ValueNullable is ClassJob classJob && pluginData.JobTags.TryGetValue(classJob.Abbreviation.ParseString(), out var jobTag))
|
||||
applyTextFormatting(jobTag);
|
||||
|
||||
void applyTextFormatting(Tag tag)
|
||||
{
|
||||
var dic = new Dictionary<NameplateElementChange, InheritableValue<bool>>
|
||||
void applyTextFormatting(Tag tag)
|
||||
{
|
||||
{ nameplateChanges.GetChange(NameplateElements.Name), tag.IsTextColorAppliedToNameplateName },
|
||||
{ nameplateChanges.GetChange(NameplateElements.Title), tag.IsTextColorAppliedToNameplateTitle },
|
||||
{ nameplateChanges.GetChange(NameplateElements.FreeCompany), tag.IsTextColorAppliedToNameplateFreeCompany },
|
||||
};
|
||||
ApplyTextFormatting(handler.PlayerCharacter, tag, dic, null);
|
||||
var destStrings = new[] { name, title, freeCompany };
|
||||
var isTextColorApplied = new[] { tag.IsTextColorAppliedToNameplateName, tag.IsTextColorAppliedToNameplateTitle, tag.IsTextColorAppliedToNameplateFreeCompany };
|
||||
ApplyTextFormatting(gameObject, tag, new[] { name, title, freeCompany }, isTextColorApplied, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void GrayOutNameplate(IPlayerCharacter playerCharacter, NameplateChanges nameplateChanges)
|
||||
{
|
||||
foreach (var element in Enum.GetValues<NameplateElements>())
|
||||
nameplateChanges.GetChange(element).ApplyFormatting(new SeString().Append(new UIForegroundPayload(3)), new SeString().Append(new UIForegroundPayload(0)));
|
||||
}
|
||||
|
||||
protected void ApplyTextFormatting(IPlayerCharacter? gameObject, Tag tag, IEnumerable<KeyValuePair<NameplateElementChange, InheritableValue<bool>>> changes, ushort? overwriteTextColor = null)
|
||||
{
|
||||
if (IsTagVisible(tag, gameObject))
|
||||
private void GrayOutNameplate(GameObject gameObject, NameplateChanges nameplateChanges)
|
||||
{
|
||||
foreach (var kvp in changes)
|
||||
if (gameObject is PlayerCharacter playerCharacter)
|
||||
{
|
||||
var change = kvp.Key;
|
||||
var enableFlag = kvp.Value;
|
||||
|
||||
if (enableFlag.InheritedValue != null && enableFlag.InheritedValue.Value && (overwriteTextColor ?? tag.TextColor?.InheritedValue) is ushort colorToUse)
|
||||
change.ApplyFormatting(new SeString().Append(new UIForegroundPayload(colorToUse)), new SeString().Append(new UIForegroundPayload(0)));
|
||||
foreach (NameplateElements element in Enum.GetValues<NameplateElements>())
|
||||
{
|
||||
nameplateChanges.GetChange(element, StringPosition.Before).Payloads.Add(new UIForegroundPayload(3));
|
||||
nameplateChanges.GetChange(element, StringPosition.After).Payloads.Add(new UIForegroundPayload(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void ApplyNameplateChanges(NameplateChanges nameplateChanges)
|
||||
{
|
||||
var props = new NameplateChangesProps
|
||||
{
|
||||
Changes = nameplateChanges
|
||||
};
|
||||
NameplateUpdateFactory.ApplyNameplateChanges(props);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,171 +2,291 @@
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||
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;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using GameObject = Dalamud.Game.ClientState.Objects.Types.GameObject;
|
||||
|
||||
namespace PlayerTags.Features;
|
||||
|
||||
/// <summary>
|
||||
/// The base of a feature that adds tags to UI elements.
|
||||
/// </summary>
|
||||
public abstract class TagTargetFeature(PluginConfiguration pluginConfiguration, PluginData pluginData) : FeatureBase(pluginConfiguration, pluginData), IDisposable
|
||||
namespace PlayerTags.Features
|
||||
{
|
||||
public ActivityContextManager ActivityContextManager { get; init; } = new();
|
||||
|
||||
public virtual void Dispose()
|
||||
/// <summary>
|
||||
/// The base of a feature that adds tags to UI elements.
|
||||
/// </summary>
|
||||
public abstract class TagTargetFeature : FeatureBase
|
||||
{
|
||||
ActivityContextManager.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected abstract bool IsIconVisible(Tag tag);
|
||||
|
||||
protected abstract bool IsTextVisible(Tag tag);
|
||||
|
||||
protected bool IsTagVisible(Tag tag, IGameObject? gameObject)
|
||||
{
|
||||
bool isVisibleForActivity = ActivityContextHelper.GetIsVisible(ActivityContextManager.CurrentActivityContext.ActivityType,
|
||||
tag.IsVisibleInPveDuties.InheritedValue ?? false,
|
||||
tag.IsVisibleInPvpDuties.InheritedValue ?? false,
|
||||
tag.IsVisibleInOverworld.InheritedValue ?? false);
|
||||
|
||||
if (!isVisibleForActivity)
|
||||
return false;
|
||||
|
||||
if (gameObject is IPlayerCharacter playerCharacter)
|
||||
public TagTargetFeature()
|
||||
{
|
||||
bool isVisibleForPlayer = PlayerContextHelper.GetIsVisible(playerCharacter,
|
||||
tag.IsVisibleForSelf.InheritedValue ?? false,
|
||||
tag.IsVisibleForFriendPlayers.InheritedValue ?? false,
|
||||
tag.IsVisibleForPartyPlayers.InheritedValue ?? false,
|
||||
tag.IsVisibleForAlliancePlayers.InheritedValue ?? false,
|
||||
tag.IsVisibleForEnemyPlayers.InheritedValue ?? false,
|
||||
tag.IsVisibleForOtherPlayers.InheritedValue ?? false);
|
||||
|
||||
if (!isVisibleForPlayer)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the payloads for the given tag and game object depending on visibility conditions.
|
||||
/// </summary>
|
||||
/// <param name="gameObject">The game object to get payloads for.</param>
|
||||
/// <param name="tag">The tag config to get payloads for.</param>
|
||||
/// <returns>A list of payloads for the given tag.</returns>
|
||||
protected Payload[] GetPayloads(Tag tag, IGameObject? playerCharacter)
|
||||
{
|
||||
if (!IsTagVisible(tag, playerCharacter))
|
||||
return [];
|
||||
return CreatePayloads(tag);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates payloads for the given tag.
|
||||
/// </summary>
|
||||
/// <param name="tag">The tag to create payloads for.</param>
|
||||
/// <returns>The payloads for the given tag.</returns>
|
||||
private Payload[] CreatePayloads(Tag tag)
|
||||
{
|
||||
List<Payload> newPayloads = [];
|
||||
BitmapFontIcon? icon = null;
|
||||
string? text = null;
|
||||
|
||||
if (IsIconVisible(tag))
|
||||
icon = tag.Icon.InheritedValue;
|
||||
|
||||
if (icon != null && icon.Value != BitmapFontIcon.None)
|
||||
newPayloads.Add(new IconPayload(icon.Value));
|
||||
|
||||
if (IsTextVisible(tag))
|
||||
text = tag.Text.InheritedValue;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(text))
|
||||
public override void Dispose()
|
||||
{
|
||||
if (tag.IsTextItalic.InheritedValue != null && tag.IsTextItalic.InheritedValue.Value)
|
||||
newPayloads.Add(new EmphasisItalicPayload(true));
|
||||
|
||||
if (tag.TextGlowColor.InheritedValue != null)
|
||||
newPayloads.Add(new UIGlowPayload(tag.TextGlowColor.InheritedValue.Value));
|
||||
|
||||
if (tag.TextColor.InheritedValue != null)
|
||||
newPayloads.Add(new UIForegroundPayload(tag.TextColor.InheritedValue.Value));
|
||||
|
||||
newPayloads.Add(new TextPayload(text));
|
||||
|
||||
if (tag.TextColor.InheritedValue != null)
|
||||
newPayloads.Add(new UIForegroundPayload(0));
|
||||
|
||||
if (tag.TextGlowColor.InheritedValue != null)
|
||||
newPayloads.Add(new UIGlowPayload(0));
|
||||
|
||||
if (tag.IsTextItalic.InheritedValue != null && tag.IsTextItalic.InheritedValue.Value)
|
||||
newPayloads.Add(new EmphasisItalicPayload(false));
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
return [.. newPayloads];
|
||||
}
|
||||
protected abstract bool IsIconVisible(Tag tag);
|
||||
|
||||
protected static string BuildPlayername(string name)
|
||||
{
|
||||
var logNameType = GameConfigHelper.Instance.GetLogNameType();
|
||||
var result = string.Empty;
|
||||
protected abstract bool IsTextVisible(Tag tag);
|
||||
|
||||
if (logNameType != null && !string.IsNullOrEmpty(name))
|
||||
protected bool IsTagVisible(Tag tag, GameObject? gameObject)
|
||||
{
|
||||
var nameSplitted = name.Split(' ');
|
||||
bool isVisibleForActivity = ActivityContextHelper.GetIsVisible(ActivityContextManager.CurrentActivityContext.ActivityType,
|
||||
tag.IsVisibleInPveDuties.InheritedValue ?? false,
|
||||
tag.IsVisibleInPvpDuties.InheritedValue ?? false,
|
||||
tag.IsVisibleInOverworld.InheritedValue ?? false);
|
||||
|
||||
if (nameSplitted.Length > 1)
|
||||
if (!isVisibleForActivity)
|
||||
{
|
||||
var firstName = nameSplitted[0];
|
||||
var lastName = nameSplitted[1];
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (logNameType)
|
||||
if (gameObject is PlayerCharacter playerCharacter)
|
||||
{
|
||||
bool isVisibleForPlayer = PlayerContextHelper.GetIsVisible(playerCharacter,
|
||||
tag.IsVisibleForSelf.InheritedValue ?? false,
|
||||
tag.IsVisibleForFriendPlayers.InheritedValue ?? false,
|
||||
tag.IsVisibleForPartyPlayers.InheritedValue ?? false,
|
||||
tag.IsVisibleForAlliancePlayers.InheritedValue ?? false,
|
||||
tag.IsVisibleForEnemyPlayers.InheritedValue ?? false,
|
||||
tag.IsVisibleForOtherPlayers.InheritedValue ?? false);
|
||||
|
||||
if (!isVisibleForPlayer)
|
||||
{
|
||||
case LogNameType.FullName:
|
||||
result = $"{firstName} {lastName}";
|
||||
break;
|
||||
case LogNameType.LastNameShorted:
|
||||
result = $"{firstName} {lastName[..1]}.";
|
||||
break;
|
||||
case LogNameType.FirstNameShorted:
|
||||
result = $"{firstName[..1]}. {lastName}";
|
||||
break;
|
||||
case LogNameType.Initials:
|
||||
result = $"{firstName[..1]}. {lastName[..1]}.";
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the payloads for the given tag and game object depending on visibility conditions.
|
||||
/// </summary>
|
||||
/// <param name="gameObject">The game object to get payloads for.</param>
|
||||
/// <param name="tag">The tag config to get payloads for.</param>
|
||||
/// <returns>A list of payloads for the given tag.</returns>
|
||||
protected Payload[] GetPayloads(Tag tag, GameObject? gameObject)
|
||||
{
|
||||
if (!IsTagVisible(tag, gameObject))
|
||||
{
|
||||
return Array.Empty<Payload>();
|
||||
}
|
||||
|
||||
return CreatePayloads(tag);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates payloads for the given tag.
|
||||
/// </summary>
|
||||
/// <param name="tag">The tag to create payloads for.</param>
|
||||
/// <returns>The payloads for the given tag.</returns>
|
||||
private Payload[] CreatePayloads(Tag tag)
|
||||
{
|
||||
List<Payload> newPayloads = new List<Payload>();
|
||||
|
||||
BitmapFontIcon? icon = null;
|
||||
if (IsIconVisible(tag))
|
||||
{
|
||||
icon = tag.Icon.InheritedValue;
|
||||
}
|
||||
|
||||
if (icon != null && icon.Value != BitmapFontIcon.None)
|
||||
{
|
||||
newPayloads.Add(new IconPayload(icon.Value));
|
||||
}
|
||||
|
||||
string? text = null;
|
||||
if (IsTextVisible(tag))
|
||||
{
|
||||
text = tag.Text.InheritedValue;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
if (tag.IsTextItalic.InheritedValue != null && tag.IsTextItalic.InheritedValue.Value)
|
||||
{
|
||||
newPayloads.Add(new EmphasisItalicPayload(true));
|
||||
}
|
||||
|
||||
if (tag.TextGlowColor.InheritedValue != null)
|
||||
{
|
||||
newPayloads.Add(new UIGlowPayload(tag.TextGlowColor.InheritedValue.Value));
|
||||
}
|
||||
|
||||
if (tag.TextColor.InheritedValue != null)
|
||||
{
|
||||
newPayloads.Add(new UIForegroundPayload(tag.TextColor.InheritedValue.Value));
|
||||
}
|
||||
|
||||
newPayloads.Add(new TextPayload(text));
|
||||
|
||||
if (tag.TextColor.InheritedValue != null)
|
||||
{
|
||||
newPayloads.Add(new UIForegroundPayload(0));
|
||||
}
|
||||
|
||||
if (tag.TextGlowColor.InheritedValue != null)
|
||||
{
|
||||
newPayloads.Add(new UIGlowPayload(0));
|
||||
}
|
||||
|
||||
if (tag.IsTextItalic.InheritedValue != null && tag.IsTextItalic.InheritedValue.Value)
|
||||
{
|
||||
newPayloads.Add(new EmphasisItalicPayload(false));
|
||||
}
|
||||
}
|
||||
|
||||
return newPayloads.ToArray();
|
||||
}
|
||||
|
||||
protected static string BuildPlayername(string name)
|
||||
{
|
||||
var logNameType = GameConfigHelper.Instance.GetLogNameType();
|
||||
var result = string.Empty;
|
||||
|
||||
if (logNameType != null && !string.IsNullOrEmpty(name))
|
||||
{
|
||||
var nameSplitted = name.Split(' ');
|
||||
|
||||
if (nameSplitted.Length > 1)
|
||||
{
|
||||
var firstName = nameSplitted[0];
|
||||
var lastName = nameSplitted[1];
|
||||
|
||||
switch (logNameType)
|
||||
{
|
||||
case LogNameType.FullName:
|
||||
result = $"{firstName} {lastName}";
|
||||
break;
|
||||
case LogNameType.LastNameShorted:
|
||||
result = $"{firstName} {lastName[..1]}.";
|
||||
break;
|
||||
case LogNameType.FirstNameShorted:
|
||||
result = $"{firstName[..1]}. {lastName}";
|
||||
break;
|
||||
case LogNameType.Initials:
|
||||
result = $"{firstName[..1]}. {lastName[..1]}.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(result))
|
||||
result = name;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given payload changes to the dictionary.
|
||||
/// </summary>
|
||||
/// <param name="tagPosition">The position to add changes to.</param>
|
||||
/// <param name="payloads">The payloads to add.</param>
|
||||
/// <param name="stringChanges">The dictionary to add the changes to.</param>
|
||||
protected void AddPayloadChanges(StringPosition tagPosition, IEnumerable<Payload> payloads, StringChanges stringChanges, bool forceUsingSingleAnchorPayload)
|
||||
{
|
||||
if (payloads != null && payloads.Any() && stringChanges != null)
|
||||
{
|
||||
var changes = stringChanges.GetChange(tagPosition);
|
||||
changes.Payloads.AddRange(payloads);
|
||||
changes.ForceUsingSingleAnchorPayload = forceUsingSingleAnchorPayload;
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(result))
|
||||
result = name;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given payload changes to the dictionary.
|
||||
/// </summary>
|
||||
/// <param name="tagPosition">The position to add changes to.</param>
|
||||
/// <param name="payloads">The payloads to add.</param>
|
||||
/// <param name="stringChanges">The dictionary to add the changes to.</param>
|
||||
protected void AddPayloadChanges(StringPosition tagPosition, IEnumerable<Payload> payloads, StringChanges stringChanges, bool forceUsingSingleAnchorPayload)
|
||||
{
|
||||
if (payloads != null && payloads.Any() && stringChanges != null)
|
||||
/// <summary>
|
||||
/// Applies changes to the given string.
|
||||
/// </summary>
|
||||
/// <param name="seString">The string to apply changes to.</param>
|
||||
/// <param name="stringChanges">The changes to apply.</param>
|
||||
/// <param name="anchorPayload">The payload in the string that changes should be anchored to. If there is no anchor, the changes will be applied to the entire string.</param>
|
||||
protected void ApplyStringChanges(SeString seString, StringChanges stringChanges, List<Payload> anchorPayloads = null, Payload anchorReplacePayload = null)
|
||||
{
|
||||
var changes = stringChanges.GetChange(tagPosition);
|
||||
changes.Payloads.AddRange(payloads);
|
||||
changes.ForceUsingSingleAnchorPayload = forceUsingSingleAnchorPayload;
|
||||
var props = new StringChangesProps
|
||||
{
|
||||
Destination = seString,
|
||||
AnchorPayload = anchorReplacePayload
|
||||
};
|
||||
|
||||
props.AnchorPayloads = anchorPayloads;
|
||||
props.StringChanges = stringChanges;
|
||||
|
||||
StringUpdateFactory.ApplyStringChanges(props);
|
||||
}
|
||||
|
||||
protected void ApplyTextFormatting(GameObject gameObject, Tag tag, SeString[] destStrings, InheritableValue<bool>[] textColorApplied, List<Payload> preferedPayloads, ushort? overwriteTextColor = null)
|
||||
{
|
||||
if (IsTagVisible(tag, gameObject))
|
||||
{
|
||||
for (int i = 0; i < destStrings.Length; i++)
|
||||
{
|
||||
var destString = destStrings[i];
|
||||
var isTextColorApplied = textColorApplied[i];
|
||||
applyTextColor(destString, isTextColorApplied, tag.TextColor);
|
||||
//applyTextGlowColor(destString, isTextColorApplied, tag.TextGlowColor);
|
||||
//applyTextItalicColor(destString, tag.IsTextItalic); // Disabled, because that is needed only for a few parts somewhere else.
|
||||
}
|
||||
}
|
||||
|
||||
void applyTextColor(SeString destPayload, InheritableValue<bool> enableFlag, InheritableValue<ushort> colorValue)
|
||||
{
|
||||
var colorToUse = overwriteTextColor ?? colorValue?.InheritedValue;
|
||||
if (shouldApplyFormattingPayloads(destPayload)
|
||||
&& enableFlag.InheritedValue != null
|
||||
&& enableFlag.InheritedValue.Value
|
||||
&& colorToUse != null)
|
||||
applyTextFormattingPayloads(destPayload, new UIForegroundPayload(colorToUse.Value), new UIForegroundPayload(0));
|
||||
}
|
||||
|
||||
//void applyTextGlowColor(SeString destPayload, InheritableValue<bool> enableFlag, InheritableValue<ushort> colorValue)
|
||||
//{
|
||||
// if (shouldApplyFormattingPayloads(destPayload)
|
||||
// && enableFlag.InheritedValue != null
|
||||
// && enableFlag.InheritedValue.Value
|
||||
// && colorValue.InheritedValue != null)
|
||||
// applyTextFormattingPayloads(destPayload, new UIGlowPayload(colorValue.InheritedValue.Value), new UIGlowPayload(0));
|
||||
//}
|
||||
|
||||
//void applyTextItalicColor(SeString destPayload, InheritableValue<bool> italicValue)
|
||||
//{
|
||||
// if (shouldApplyFormattingPayloads(destPayload)
|
||||
// && italicValue.InheritedValue != null
|
||||
// && italicValue.InheritedValue.Value)
|
||||
// applyTextFormattingPayloads(destPayload, new EmphasisItalicPayload(true), new EmphasisItalicPayload(false));
|
||||
//}
|
||||
|
||||
bool shouldApplyFormattingPayloads(SeString destPayload)
|
||||
=> destPayload.Payloads.Any(payload => payload is TextPayload || payload is PlayerPayload);
|
||||
|
||||
void applyTextFormattingPayloads(SeString destPayload, Payload startPayload, Payload endPayload)
|
||||
{
|
||||
if (preferedPayloads == null || !preferedPayloads.Any())
|
||||
applyTextFormattingPayloadToStartAndEnd(destPayload, startPayload, endPayload);
|
||||
else
|
||||
applyTextFormattingPayloadsToSpecificPosition(destPayload, startPayload, endPayload, preferedPayloads);
|
||||
}
|
||||
|
||||
void applyTextFormattingPayloadToStartAndEnd(SeString destPayload, Payload startPayload, Payload endPayload)
|
||||
{
|
||||
destPayload.Payloads.Insert(0, startPayload);
|
||||
destPayload.Payloads.Add(endPayload);
|
||||
}
|
||||
|
||||
void applyTextFormattingPayloadsToSpecificPosition(SeString destPayload, Payload startPayload, Payload endPayload, List<Payload> preferedPayload)
|
||||
{
|
||||
int payloadStartIndex = destPayload.Payloads.IndexOf(preferedPayloads.First());
|
||||
destPayload.Payloads.Insert(payloadStartIndex, startPayload);
|
||||
|
||||
int payloadEndIndex = destPayload.Payloads.IndexOf(preferedPayloads.Last());
|
||||
destPayload.Payloads.Insert(payloadEndIndex + 1, endPayload);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
145
PlayerTags/GameInterface/GameInterfaceHelper.cs
Normal file
145
PlayerTags/GameInterface/GameInterfaceHelper.cs
Normal file
@@ -0,0 +1,145 @@
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Memory;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace PlayerTags.GameInterface
|
||||
{
|
||||
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(ref IntPtr ptr)
|
||||
{
|
||||
Marshal.FreeHGlobal(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
65
PlayerTags/GameInterface/Nameplates/Nameplate.cs
Normal file
65
PlayerTags/GameInterface/Nameplates/Nameplate.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Logging;
|
||||
using Dalamud.Utility.Signatures;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
using Pilz.Dalamud.Nameplates;
|
||||
using System;
|
||||
using System.Diagnostics.Tracing;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace PlayerTags.GameInterface.Nameplates
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides an interface to modify nameplates.
|
||||
/// </summary>
|
||||
public class Nameplate : IDisposable
|
||||
{
|
||||
public NameplateManager NameplateManager { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a player nameplate is updated by the game.
|
||||
/// </summary>
|
||||
public event PlayerNameplateUpdatedDelegate? PlayerNameplateUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the required hooks are in place and this instance is valid.
|
||||
/// </summary>
|
||||
public bool IsValid
|
||||
{
|
||||
get => NameplateManager != null && NameplateManager.IsValid;
|
||||
}
|
||||
|
||||
public Nameplate()
|
||||
{
|
||||
NameplateManager = new();
|
||||
NameplateManager.Hooks.AddonNamePlate_SetPlayerNameManaged += Hooks_AddonNamePlate_SetPlayerNameManaged;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
NameplateManager.Hooks.AddonNamePlate_SetPlayerNameManaged -= Hooks_AddonNamePlate_SetPlayerNameManaged;
|
||||
NameplateManager.Dispose();
|
||||
}
|
||||
|
||||
private void Hooks_AddonNamePlate_SetPlayerNameManaged(Pilz.Dalamud.Nameplates.EventArgs.AddonNamePlate_SetPlayerNameManagedEventArgs eventArgs)
|
||||
{
|
||||
try
|
||||
{
|
||||
PlayerCharacter? playerCharacter = NameplateManager.GetNameplateGameObject<PlayerCharacter>(eventArgs.SafeNameplateObject);
|
||||
|
||||
if (playerCharacter != null)
|
||||
{
|
||||
var playerNameplateUpdatedArgs = new PlayerNameplateUpdatedArgs(playerCharacter, eventArgs);
|
||||
PlayerNameplateUpdated?.Invoke(playerNameplateUpdatedArgs);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, $"SetPlayerNameplateDetour");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Pilz.Dalamud.Nameplates.EventArgs;
|
||||
|
||||
namespace PlayerTags.GameInterface.Nameplates
|
||||
{
|
||||
public class PlayerNameplateUpdatedArgs
|
||||
{
|
||||
private readonly AddonNamePlate_SetPlayerNameManagedEventArgs eventArgs;
|
||||
|
||||
public PlayerCharacter PlayerCharacter { get; }
|
||||
|
||||
public SeString Name
|
||||
{
|
||||
get => eventArgs.Name;
|
||||
}
|
||||
|
||||
public SeString Title
|
||||
{
|
||||
get => eventArgs.Title;
|
||||
}
|
||||
|
||||
public SeString FreeCompany
|
||||
{
|
||||
get => eventArgs.FreeCompany;
|
||||
}
|
||||
|
||||
public bool IsTitleVisible
|
||||
{
|
||||
get => eventArgs.IsTitleVisible;
|
||||
set => eventArgs.IsTitleVisible = value;
|
||||
}
|
||||
|
||||
public bool IsTitleAboveName
|
||||
{
|
||||
get => eventArgs.IsTitleAboveName;
|
||||
set => eventArgs.IsTitleAboveName = value;
|
||||
}
|
||||
|
||||
public int IconId
|
||||
{
|
||||
get => eventArgs.IconID;
|
||||
set => eventArgs.IconID = value;
|
||||
}
|
||||
|
||||
public PlayerNameplateUpdatedArgs(PlayerCharacter playerCharacter, AddonNamePlate_SetPlayerNameManagedEventArgs eventArgs)
|
||||
{
|
||||
PlayerCharacter = playerCharacter;
|
||||
this.eventArgs = eventArgs;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
namespace PlayerTags.GameInterface.Nameplates
|
||||
{
|
||||
public delegate void PlayerNameplateUpdatedDelegate(PlayerNameplateUpdatedArgs args);
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
namespace PlayerTags.Inheritables;
|
||||
|
||||
public interface IInheritable
|
||||
namespace PlayerTags.Inheritables
|
||||
{
|
||||
public IInheritable? Parent { get; set; }
|
||||
public interface IInheritable
|
||||
{
|
||||
public IInheritable? Parent { get; set; }
|
||||
|
||||
public InheritableBehavior Behavior { get; set; }
|
||||
public InheritableBehavior Behavior { get; set; }
|
||||
|
||||
public abstract void SetData(InheritableData inheritableData);
|
||||
public abstract void SetData(InheritableData inheritableData);
|
||||
|
||||
public abstract InheritableData GetData();
|
||||
public abstract InheritableData GetData();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
namespace PlayerTags.Inheritables;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
public enum InheritableBehavior
|
||||
namespace PlayerTags.Inheritables
|
||||
{
|
||||
Inherit,
|
||||
Enabled,
|
||||
Disabled
|
||||
public enum InheritableBehavior
|
||||
{
|
||||
Inherit,
|
||||
Enabled,
|
||||
Disabled
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,16 @@
|
||||
using Newtonsoft.Json.Converters;
|
||||
using System;
|
||||
|
||||
namespace PlayerTags.Inheritables;
|
||||
|
||||
[Serializable]
|
||||
public struct InheritableData
|
||||
namespace PlayerTags.Inheritables
|
||||
{
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
[JsonProperty("Behavior")]
|
||||
public InheritableBehavior Behavior;
|
||||
[Serializable]
|
||||
public struct InheritableData
|
||||
{
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
[JsonProperty("Behavior")]
|
||||
public InheritableBehavior Behavior;
|
||||
|
||||
[JsonProperty("Value")]
|
||||
public object Value;
|
||||
[JsonProperty("Value")]
|
||||
public object Value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,63 +1,66 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PlayerTags.Inheritables;
|
||||
|
||||
public class InheritableReference<T> : IInheritable
|
||||
where T : class
|
||||
namespace PlayerTags.Inheritables
|
||||
{
|
||||
public IInheritable? Parent { get; set; }
|
||||
|
||||
public InheritableBehavior Behavior { get; set; }
|
||||
|
||||
[JsonProperty]
|
||||
public T Value;
|
||||
|
||||
[JsonIgnore]
|
||||
public T? InheritedValue
|
||||
public class InheritableReference<T> : IInheritable
|
||||
where T : class
|
||||
{
|
||||
get
|
||||
public IInheritable? Parent { get; set; }
|
||||
|
||||
public InheritableBehavior Behavior { get; set; }
|
||||
|
||||
[JsonProperty]
|
||||
public T Value;
|
||||
|
||||
[JsonIgnore]
|
||||
public T? InheritedValue
|
||||
{
|
||||
IInheritable? current = this;
|
||||
while (current != null)
|
||||
get
|
||||
{
|
||||
if (current.Behavior == InheritableBehavior.Enabled && current is InheritableReference<T> currentOfSameType)
|
||||
IInheritable? current = this;
|
||||
while (current != null)
|
||||
{
|
||||
return currentOfSameType.Value;
|
||||
}
|
||||
else if (current.Behavior == InheritableBehavior.Disabled)
|
||||
{
|
||||
return default;
|
||||
if (current.Behavior == InheritableBehavior.Enabled && current is InheritableReference<T> currentOfSameType)
|
||||
{
|
||||
return currentOfSameType.Value;
|
||||
}
|
||||
else if (current.Behavior == InheritableBehavior.Disabled)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
current = current.Parent;
|
||||
}
|
||||
|
||||
current = current.Parent;
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
return default;
|
||||
public static implicit operator InheritableReference<T>(T value) => new InheritableReference<T>(value)
|
||||
{
|
||||
Behavior = InheritableBehavior.Enabled
|
||||
};
|
||||
|
||||
public InheritableReference(T value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public void SetData(InheritableData inheritableData)
|
||||
{
|
||||
Behavior = inheritableData.Behavior;
|
||||
Value = (T)inheritableData.Value;
|
||||
}
|
||||
|
||||
public InheritableData GetData()
|
||||
{
|
||||
return new InheritableData
|
||||
{
|
||||
Behavior = Behavior,
|
||||
Value = Value
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static implicit operator InheritableReference<T>(T value) => new(value)
|
||||
{
|
||||
Behavior = InheritableBehavior.Enabled
|
||||
};
|
||||
|
||||
public InheritableReference(T value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public void SetData(InheritableData inheritableData)
|
||||
{
|
||||
Behavior = inheritableData.Behavior;
|
||||
Value = (T)inheritableData.Value;
|
||||
}
|
||||
|
||||
public InheritableData GetData()
|
||||
{
|
||||
return new InheritableData
|
||||
{
|
||||
Behavior = Behavior,
|
||||
Value = Value
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,95 +1,97 @@
|
||||
using Newtonsoft.Json;
|
||||
using Dalamud.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
|
||||
namespace PlayerTags.Inheritables;
|
||||
|
||||
public class InheritableValue<T> : IInheritable
|
||||
where T : struct
|
||||
namespace PlayerTags.Inheritables
|
||||
{
|
||||
public IInheritable? Parent { get; set; }
|
||||
|
||||
public InheritableBehavior Behavior { get; set; }
|
||||
|
||||
[JsonProperty]
|
||||
public T Value;
|
||||
|
||||
[JsonIgnore]
|
||||
public T? InheritedValue
|
||||
public class InheritableValue<T> : IInheritable
|
||||
where T : struct
|
||||
{
|
||||
get
|
||||
public IInheritable? Parent { get; set; }
|
||||
|
||||
public InheritableBehavior Behavior { get; set; }
|
||||
|
||||
[JsonProperty]
|
||||
public T Value;
|
||||
|
||||
[JsonIgnore]
|
||||
public T? InheritedValue
|
||||
{
|
||||
IInheritable? current = this;
|
||||
while (current != null)
|
||||
get
|
||||
{
|
||||
if (current.Behavior == InheritableBehavior.Enabled && current is InheritableValue<T> currentOfSameType)
|
||||
IInheritable? current = this;
|
||||
while (current != null)
|
||||
{
|
||||
return currentOfSameType.Value;
|
||||
}
|
||||
else if (current.Behavior == InheritableBehavior.Disabled)
|
||||
{
|
||||
return default;
|
||||
if (current.Behavior == InheritableBehavior.Enabled && current is InheritableValue<T> currentOfSameType)
|
||||
{
|
||||
return currentOfSameType.Value;
|
||||
}
|
||||
else if (current.Behavior == InheritableBehavior.Disabled)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
current = current.Parent;
|
||||
}
|
||||
|
||||
current = current.Parent;
|
||||
return default;
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public static implicit operator InheritableValue<T>(T value) => new(value)
|
||||
{
|
||||
Behavior = InheritableBehavior.Enabled
|
||||
};
|
||||
|
||||
public InheritableValue(T value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public void SetData(InheritableData inheritableData)
|
||||
{
|
||||
Behavior = inheritableData.Behavior;
|
||||
|
||||
try
|
||||
public static implicit operator InheritableValue<T>(T value) => new InheritableValue<T>(value)
|
||||
{
|
||||
if (typeof(T).IsEnum && inheritableData.Value != null)
|
||||
Behavior = InheritableBehavior.Enabled
|
||||
};
|
||||
|
||||
public InheritableValue(T value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public void SetData(InheritableData inheritableData)
|
||||
{
|
||||
Behavior = inheritableData.Behavior;
|
||||
|
||||
try
|
||||
{
|
||||
if (inheritableData.Value is string stringValue)
|
||||
if (typeof(T).IsEnum && inheritableData.Value != null)
|
||||
{
|
||||
Value = (T)Enum.Parse(typeof(T), stringValue);
|
||||
if (inheritableData.Value is string stringValue)
|
||||
{
|
||||
Value = (T)Enum.Parse(typeof(T), stringValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
Value = (T)Enum.ToObject(typeof(T), inheritableData.Value);
|
||||
}
|
||||
}
|
||||
else if (inheritableData.Value == null)
|
||||
{
|
||||
// This should never happen
|
||||
PluginLog.Error($"Expected value of type {Value.GetType()} but received null");
|
||||
}
|
||||
else if (typeof(T) == typeof(Guid) && inheritableData.Value is string strValue)
|
||||
{
|
||||
Value = (T)(object)Guid.Parse(strValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
Value = (T)Enum.ToObject(typeof(T), inheritableData.Value);
|
||||
Value = (T)Convert.ChangeType(inheritableData.Value, typeof(T));
|
||||
}
|
||||
}
|
||||
else if (inheritableData.Value == null)
|
||||
catch (Exception ex)
|
||||
{
|
||||
// This should never happen
|
||||
PluginServices.PluginLog.Error($"Expected value of type {Value.GetType()} but received null");
|
||||
}
|
||||
else if (typeof(T) == typeof(Guid) && inheritableData.Value is string strValue)
|
||||
{
|
||||
Value = (T)(object)Guid.Parse(strValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
Value = (T)Convert.ChangeType(inheritableData.Value, typeof(T));
|
||||
PluginLog.Error(ex, $"Failed to convert {inheritableData.Value.GetType()} value '{inheritableData.Value}' to {Value.GetType()}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginServices.PluginLog.Error(ex, $"Failed to convert {inheritableData.Value.GetType()} value '{inheritableData.Value}' to {Value.GetType()}");
|
||||
}
|
||||
}
|
||||
|
||||
public InheritableData GetData()
|
||||
{
|
||||
return new InheritableData
|
||||
public InheritableData GetData()
|
||||
{
|
||||
Behavior = Behavior,
|
||||
Value = Value
|
||||
};
|
||||
return new InheritableData
|
||||
{
|
||||
Behavior = Behavior,
|
||||
Value = Value
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,58 +1,62 @@
|
||||
using PlayerTags.Resources;
|
||||
using Dalamud.Logging;
|
||||
using Dalamud.Plugin;
|
||||
using PlayerTags.Resources;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
|
||||
namespace PlayerTags;
|
||||
|
||||
public static class Localizer
|
||||
namespace PlayerTags
|
||||
{
|
||||
public static void SetLanguage(string langCode)
|
||||
public static class Localizer
|
||||
{
|
||||
SetLanguage(new CultureInfo(langCode));
|
||||
}
|
||||
|
||||
public static void SetLanguage(CultureInfo cultureInfo)
|
||||
{
|
||||
Strings.Culture = cultureInfo;
|
||||
}
|
||||
|
||||
public static string GetName<TEnum>(TEnum value)
|
||||
{
|
||||
return $"{typeof(TEnum).Name}_{value}";
|
||||
}
|
||||
|
||||
public static string GetString<TEnum>(bool isDescription)
|
||||
where TEnum : Enum
|
||||
{
|
||||
return GetString(typeof(TEnum).Name, isDescription);
|
||||
}
|
||||
|
||||
public static string GetString<TEnum>(TEnum value, bool isDescription)
|
||||
where TEnum : Enum
|
||||
{
|
||||
return GetString(GetName(value), isDescription);
|
||||
}
|
||||
|
||||
public static string GetString(string localizedStringName, bool isDescription)
|
||||
{
|
||||
string localizedStringId = $"Loc_{localizedStringName}";
|
||||
|
||||
if (isDescription)
|
||||
public static void SetLanguage(string langCode)
|
||||
{
|
||||
localizedStringId += "_Description";
|
||||
SetLanguage(new CultureInfo(langCode));
|
||||
}
|
||||
|
||||
return GetString(localizedStringId);
|
||||
}
|
||||
public static void SetLanguage(CultureInfo cultureInfo)
|
||||
{
|
||||
Strings.Culture = cultureInfo;
|
||||
}
|
||||
|
||||
public static string GetString(string localizedStringId)
|
||||
{
|
||||
string? value = Strings.ResourceManager.GetString(localizedStringId, Strings.Culture);
|
||||
public static string GetName<TEnum>(TEnum value)
|
||||
{
|
||||
return $"{typeof(TEnum).Name}_{value}";
|
||||
}
|
||||
|
||||
if (value != null)
|
||||
return value;
|
||||
public static string GetString<TEnum>(bool isDescription)
|
||||
where TEnum : Enum
|
||||
{
|
||||
return GetString(typeof(TEnum).Name, isDescription);
|
||||
}
|
||||
|
||||
PluginServices.PluginLog.Error($"Failed to get localized string for id {localizedStringId}");
|
||||
return localizedStringId;
|
||||
public static string GetString<TEnum>(TEnum value, bool isDescription)
|
||||
where TEnum : Enum
|
||||
{
|
||||
return GetString(GetName(value), isDescription);
|
||||
}
|
||||
|
||||
public static string GetString(string localizedStringName, bool isDescription)
|
||||
{
|
||||
string localizedStringId = $"Loc_{localizedStringName}";
|
||||
|
||||
if (isDescription)
|
||||
{
|
||||
localizedStringId += "_Description";
|
||||
}
|
||||
|
||||
return GetString(localizedStringId);
|
||||
}
|
||||
|
||||
public static string GetString(string localizedStringId)
|
||||
{
|
||||
string? value = Strings.ResourceManager.GetString(localizedStringId, Strings.Culture);
|
||||
|
||||
if (value != null)
|
||||
return value;
|
||||
|
||||
PluginLog.Error($"Failed to get localized string for id {localizedStringId}");
|
||||
return localizedStringId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,33 @@
|
||||
using System.IO;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PlayerTags;
|
||||
|
||||
internal class MyPaths
|
||||
namespace PlayerTags
|
||||
{
|
||||
private static string? _PluginDirectoryPath = null;
|
||||
|
||||
public static string PluginDirectoryPath
|
||||
internal class MyPaths
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_PluginDirectoryPath is null)
|
||||
{
|
||||
var path = Path.GetDirectoryName(PluginServices.DalamudPluginInterface.AssemblyLocation.FullName);
|
||||
if (path is null)
|
||||
_PluginDirectoryPath = string.Empty;
|
||||
else
|
||||
_PluginDirectoryPath = path;
|
||||
}
|
||||
return _PluginDirectoryPath;
|
||||
}
|
||||
}
|
||||
private static string? _PluginDirectoryPath = null;
|
||||
|
||||
public static string ResourcePath
|
||||
=> Path.Combine(PluginDirectoryPath, "Resources");
|
||||
public static string PluginDirectoryPath
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_PluginDirectoryPath is null)
|
||||
{
|
||||
var path = Path.GetDirectoryName(PluginServices.DalamudPluginInterface.AssemblyLocation.FullName);
|
||||
if (path is null)
|
||||
_PluginDirectoryPath = string.Empty;
|
||||
else
|
||||
_PluginDirectoryPath = path;
|
||||
}
|
||||
return _PluginDirectoryPath;
|
||||
}
|
||||
}
|
||||
|
||||
public static string ResourcePath
|
||||
=> Path.Combine(PluginDirectoryPath, "Resources");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,49 +1,92 @@
|
||||
<Project Sdk="Dalamud.NET.Sdk/13.0.0">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Authors>r00telement;Pilzinsel64</Authors>
|
||||
<Version>1.9.0.0</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Authors>r00telement;Pilzinsel64</Authors>
|
||||
<Version>1.12.3.0</Version>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<Platforms>x64</Platforms>
|
||||
<Nullable>annotations</Nullable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Nullable>annotations</Nullable>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<DalamudLibPath>$(appdata)\XIVLauncher\addon\Hooks\dev\</DalamudLibPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Pilz.Dalamud\Pilz.Dalamud\Pilz.Dalamud.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Dalamud.ContextMenu" Version="1.2.1" />
|
||||
<PackageReference Include="DalamudPackager" Version="2.1.10" />
|
||||
<ProjectReference Include="..\Pilz.Dalamud\Pilz.Dalamud.csproj" />
|
||||
<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>
|
||||
<Compile Update="Resources\Paths.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Paths.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Resources\Strings.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Strings.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Resources\Paths.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Paths.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Resources\Strings.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Strings.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Resources\Paths.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Paths.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Update="Resources\Strings.resx">
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Strings.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Resources\Paths.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Paths.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Update="Resources\Strings.resx">
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Strings.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Resources\Words\Adjectives.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Resources\Words\Nouns.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
<ItemGroup>
|
||||
<None Update="Resources\Words\Adjectives.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Resources\Words\Nouns.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
"Punchline": "Lightweight job visibility in nameplates and chat.",
|
||||
"Tags": [ "Jobs", "UI" ],
|
||||
"CategoryTags": [ "jobs", "UI" ],
|
||||
"RepoUrl": "https://git.pilzinsel64.de/pilzinsel64/playertags"
|
||||
"RepoUrl": "https://github.com/Pilzinsel64/PlayerTags"
|
||||
}
|
||||
@@ -1,113 +1,80 @@
|
||||
using Dalamud.Game.Command;
|
||||
using Dalamud.Logging;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Plugin.Internal;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
|
||||
using PlayerTags.Configuration;
|
||||
using PlayerTags.Data;
|
||||
using PlayerTags.Features;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace PlayerTags;
|
||||
|
||||
public sealed class Plugin : IDalamudPlugin
|
||||
namespace PlayerTags
|
||||
{
|
||||
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 readonly PluginConfiguration pluginConfiguration = null;
|
||||
private readonly PluginData pluginData = null;
|
||||
private readonly PluginConfigurationUI pluginConfigurationUI = null;
|
||||
|
||||
private readonly CustomTagsContextMenuFeature customTagsContextMenuFeature;
|
||||
private readonly NameplateTagTargetFeature nameplatesTagTargetFeature;
|
||||
private readonly ChatTagTargetFeature chatTagTargetFeature;
|
||||
|
||||
public Plugin(IDalamudPluginInterface pluginInterface)
|
||||
public sealed class Plugin : IDalamudPlugin
|
||||
{
|
||||
PluginServices.Initialize(pluginInterface);
|
||||
Pilz.Dalamud.PluginServices.Initialize(pluginInterface);
|
||||
public string Name => "Player Tags";
|
||||
private const string c_CommandName = "/playertags";
|
||||
|
||||
pluginConfiguration = PluginConfiguration.LoadPluginConfig() ?? new();
|
||||
pluginData = new PluginData(pluginConfiguration);
|
||||
pluginConfigurationUI = new PluginConfigurationUI(pluginConfiguration, pluginData);
|
||||
private PluginConfiguration m_PluginConfiguration;
|
||||
private PluginData m_PluginData;
|
||||
private PluginConfigurationUI m_PluginConfigurationUI;
|
||||
|
||||
Localizer.SetLanguage(PluginServices.DalamudPluginInterface.UiLanguage);
|
||||
PluginServices.DalamudPluginInterface.LanguageChanged += DalamudPluginInterface_LanguageChanged;
|
||||
private CustomTagsContextMenuFeature m_CustomTagsContextMenuFeature;
|
||||
private NameplateTagTargetFeature m_NameplatesTagTargetFeature;
|
||||
private ChatTagTargetFeature m_ChatTagTargetFeature;
|
||||
|
||||
PluginServices.DalamudPluginInterface.UiBuilder.Draw += UiBuilder_Draw;
|
||||
PluginServices.DalamudPluginInterface.UiBuilder.OpenConfigUi += UiBuilder_OpenConfigUi;
|
||||
PluginServices.CommandManager.AddHandler(c_CommandName, new CommandInfo(CommandManager_Handler)
|
||||
public Plugin(DalamudPluginInterface pluginInterface)
|
||||
{
|
||||
HelpMessage = Resources.Strings.Loc_Command_playertags_v2
|
||||
});
|
||||
customTagsContextMenuFeature = new CustomTagsContextMenuFeature(pluginConfiguration, pluginData);
|
||||
nameplatesTagTargetFeature = new NameplateTagTargetFeature(pluginConfiguration, pluginData);
|
||||
chatTagTargetFeature = new ChatTagTargetFeature(pluginConfiguration, pluginData);
|
||||
}
|
||||
PluginServices.Initialize(pluginInterface);
|
||||
Pilz.Dalamud.PluginServices.Initialize(pluginInterface);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
chatTagTargetFeature.Dispose();
|
||||
nameplatesTagTargetFeature.Dispose();
|
||||
customTagsContextMenuFeature.Dispose();
|
||||
PluginServices.DalamudPluginInterface.LanguageChanged -= DalamudPluginInterface_LanguageChanged;
|
||||
PluginServices.CommandManager.RemoveHandler(c_CommandName);
|
||||
PluginServices.DalamudPluginInterface.UiBuilder.OpenConfigUi -= UiBuilder_OpenConfigUi;
|
||||
PluginServices.DalamudPluginInterface.UiBuilder.Draw -= UiBuilder_Draw;
|
||||
}
|
||||
m_PluginConfiguration = PluginConfiguration.LoadPluginConfig() ?? new PluginConfiguration();
|
||||
m_PluginData = new PluginData(m_PluginConfiguration);
|
||||
m_PluginConfigurationUI = new PluginConfigurationUI(m_PluginConfiguration, m_PluginData);
|
||||
|
||||
private void DalamudPluginInterface_LanguageChanged(string langCode)
|
||||
{
|
||||
Localizer.SetLanguage(langCode);
|
||||
}
|
||||
Localizer.SetLanguage(PluginServices.DalamudPluginInterface.UiLanguage);
|
||||
PluginServices.DalamudPluginInterface.LanguageChanged += DalamudPluginInterface_LanguageChanged;
|
||||
|
||||
private void CommandManager_Handler(string command, string arguments)
|
||||
{
|
||||
switch (command)
|
||||
PluginServices.DalamudPluginInterface.UiBuilder.Draw += UiBuilder_Draw;
|
||||
PluginServices.DalamudPluginInterface.UiBuilder.OpenConfigUi += UiBuilder_OpenConfigUi;
|
||||
PluginServices.CommandManager.AddHandler(c_CommandName, new CommandInfo((string command, string arguments) => UiBuilder_OpenConfigUi())
|
||||
{
|
||||
HelpMessage = Resources.Strings.Loc_Command_playertags
|
||||
});
|
||||
m_CustomTagsContextMenuFeature = new CustomTagsContextMenuFeature(m_PluginConfiguration, m_PluginData);
|
||||
m_NameplatesTagTargetFeature = new NameplateTagTargetFeature(m_PluginConfiguration, m_PluginData);
|
||||
m_ChatTagTargetFeature = new ChatTagTargetFeature(m_PluginConfiguration, m_PluginData);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
case c_CommandName:
|
||||
if (string.IsNullOrWhiteSpace(arguments))
|
||||
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:
|
||||
pluginConfiguration.EnabledGlobal = true;
|
||||
break;
|
||||
case c_CommandArg_Off:
|
||||
pluginConfiguration.EnabledGlobal = false;
|
||||
break;
|
||||
case c_CommandArg_toggle:
|
||||
pluginConfiguration.EnabledGlobal = !pluginConfiguration.EnabledGlobal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
m_ChatTagTargetFeature.Dispose();
|
||||
m_NameplatesTagTargetFeature.Dispose();
|
||||
m_CustomTagsContextMenuFeature.Dispose();
|
||||
PluginServices.DalamudPluginInterface.LanguageChanged -= DalamudPluginInterface_LanguageChanged;
|
||||
PluginServices.CommandManager.RemoveHandler(c_CommandName);
|
||||
PluginServices.DalamudPluginInterface.UiBuilder.OpenConfigUi -= UiBuilder_OpenConfigUi;
|
||||
PluginServices.DalamudPluginInterface.UiBuilder.Draw -= UiBuilder_Draw;
|
||||
}
|
||||
|
||||
private void DalamudPluginInterface_LanguageChanged(string langCode)
|
||||
{
|
||||
Localizer.SetLanguage(langCode);
|
||||
}
|
||||
|
||||
private void UiBuilder_Draw()
|
||||
{
|
||||
if (m_PluginConfiguration.IsVisible)
|
||||
m_PluginConfigurationUI.Draw();
|
||||
}
|
||||
|
||||
private void UiBuilder_OpenConfigUi()
|
||||
{
|
||||
m_PluginConfiguration.IsVisible = true;
|
||||
m_PluginConfiguration.Save(m_PluginData);
|
||||
}
|
||||
}
|
||||
|
||||
private void UiBuilder_Draw()
|
||||
{
|
||||
if (pluginConfiguration.IsVisible)
|
||||
pluginConfigurationUI.Draw();
|
||||
}
|
||||
|
||||
private void UiBuilder_OpenConfigUi()
|
||||
{
|
||||
pluginConfiguration.IsVisible = true;
|
||||
pluginConfiguration.Save(pluginData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,30 @@
|
||||
using Dalamud.IoC;
|
||||
using Dalamud.Data;
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Game.ClientState;
|
||||
using Dalamud.Game.ClientState.Objects;
|
||||
using Dalamud.Game.ClientState.Party;
|
||||
using Dalamud.Game.Command;
|
||||
using Dalamud.Game.Gui;
|
||||
using Dalamud.IoC;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Plugin.Services;
|
||||
|
||||
namespace PlayerTags;
|
||||
|
||||
public class PluginServices
|
||||
namespace PlayerTags
|
||||
{
|
||||
[PluginService] public static IDalamudPluginInterface DalamudPluginInterface { get; set; }
|
||||
[PluginService] public static IPluginLog PluginLog { get; set; }
|
||||
[PluginService] public static IGameConfig GameConfig { get; set; }
|
||||
[PluginService] public static IChatGui ChatGui { get; set; }
|
||||
[PluginService] public static IClientState ClientState { get; set; }
|
||||
[PluginService] public static ICommandManager CommandManager { get; set; }
|
||||
[PluginService] public static IDataManager DataManager { get; set; }
|
||||
[PluginService] public static IFramework Framework { get; set; }
|
||||
[PluginService] public static IGameGui GameGui { get; set; }
|
||||
[PluginService] public static IObjectTable ObjectTable { get; set; }
|
||||
[PluginService] public static IPartyList PartyList { get; set; }
|
||||
[PluginService] public static IGameInteropProvider GameInteropProvider { get; set; }
|
||||
[PluginService] public static IContextMenu ContextMenu { get; set; }
|
||||
[PluginService] public static INamePlateGui NamePlateGui { get; set; }
|
||||
|
||||
public static void Initialize(IDalamudPluginInterface pluginInterface)
|
||||
public class PluginServices
|
||||
{
|
||||
pluginInterface.Create<PluginServices>();
|
||||
[PluginService] public static ChatGui ChatGui { get; set; } = null!;
|
||||
[PluginService] public static ClientState ClientState { get; set; } = null!;
|
||||
[PluginService] public static CommandManager CommandManager { get; set; } = null!;
|
||||
[PluginService] public static DalamudPluginInterface DalamudPluginInterface { get; set; } = null!;
|
||||
[PluginService] public static DataManager DataManager { get; set; } = null!;
|
||||
[PluginService] public static Framework Framework { get; set; } = null!;
|
||||
[PluginService] public static GameGui GameGui { get; set; } = null!;
|
||||
[PluginService] public static ObjectTable ObjectTable { get; set; } = null!;
|
||||
[PluginService] public static PartyList PartyList { get; set; } = null!;
|
||||
|
||||
public static void Initialize(DalamudPluginInterface pluginInterface)
|
||||
{
|
||||
pluginInterface.Create<PluginServices>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
namespace PlayerTags.PluginStrings;
|
||||
|
||||
public interface IPluginString
|
||||
namespace PlayerTags.PluginStrings
|
||||
{
|
||||
public string Value { get; }
|
||||
public interface IPluginString
|
||||
{
|
||||
public string Value { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
namespace PlayerTags.PluginStrings;
|
||||
|
||||
public class LiteralPluginString : IPluginString
|
||||
namespace PlayerTags.PluginStrings
|
||||
{
|
||||
private string m_Value;
|
||||
public string Value => m_Value;
|
||||
|
||||
public LiteralPluginString(string value)
|
||||
public class LiteralPluginString : IPluginString
|
||||
{
|
||||
m_Value = value;
|
||||
}
|
||||
private string m_Value;
|
||||
public string Value => m_Value;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value;
|
||||
public LiteralPluginString(string value)
|
||||
{
|
||||
m_Value = value;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
namespace PlayerTags.PluginStrings;
|
||||
|
||||
public class LocalizedPluginString : IPluginString
|
||||
namespace PlayerTags.PluginStrings
|
||||
{
|
||||
public string Key { get; init; }
|
||||
public string Value => Localizer.GetString(Key, false);
|
||||
|
||||
public LocalizedPluginString(string key)
|
||||
public class LocalizedPluginString : IPluginString
|
||||
{
|
||||
Key = key;
|
||||
}
|
||||
public string Key { get; init; }
|
||||
public string Value => Localizer.GetString(Key, false);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value;
|
||||
public LocalizedPluginString(string key)
|
||||
{
|
||||
Key = key;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,113 +1,116 @@
|
||||
using System;
|
||||
using Dalamud.Logging;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace PlayerTags;
|
||||
|
||||
/// <summary>
|
||||
/// Generates names based on existing lists of words.
|
||||
/// </summary>
|
||||
public static class RandomNameGenerator
|
||||
namespace PlayerTags
|
||||
{
|
||||
private static string[]? s_Adjectives;
|
||||
private static string[] Adjectives
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_Adjectives == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
s_Adjectives = File.ReadAllLines(Path.Combine(MyPaths.ResourcePath, Resources.Paths.AdjectivesTxt));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginServices.PluginLog.Error(ex, $"RandomNameGenerator failed to read adjectives");
|
||||
}
|
||||
}
|
||||
|
||||
if (s_Adjectives != null)
|
||||
{
|
||||
return s_Adjectives;
|
||||
}
|
||||
|
||||
return new string[] { };
|
||||
}
|
||||
}
|
||||
|
||||
private static string[]? s_Nouns;
|
||||
private static string[] Nouns
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_Nouns == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
s_Nouns = File.ReadAllLines(Path.Combine(MyPaths.ResourcePath, Resources.Paths.NounsTxt));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginServices.PluginLog.Error(ex, $"RandomNameGenerator failed to read nouns");
|
||||
}
|
||||
}
|
||||
|
||||
if (s_Nouns != null)
|
||||
{
|
||||
return s_Nouns;
|
||||
}
|
||||
|
||||
return new string[] { };
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a name for the given string.
|
||||
/// Generates names based on existing lists of words.
|
||||
/// </summary>
|
||||
/// <param name="str">The string to generate a name for.</param>
|
||||
/// <returns>A generated name.</returns>
|
||||
public static string? Generate(string str)
|
||||
public static class RandomNameGenerator
|
||||
{
|
||||
if (Adjectives == null || Nouns == null)
|
||||
private static string[]? s_Adjectives;
|
||||
private static string[] Adjectives
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int hash = GetDeterministicHashCode(str);
|
||||
|
||||
// Use the seed as the hash so the same player always gets the same name
|
||||
Random random = new(hash);
|
||||
var adjective = Adjectives[random.Next(0, Adjectives.Length)];
|
||||
var noun = Nouns[random.Next(0, Nouns.Length)];
|
||||
var generatedName = $"{adjective} {noun}";
|
||||
|
||||
return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(generatedName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a deterministic hash code for the given string.
|
||||
/// </summary>
|
||||
/// <param name="str">The string to hash.</param>
|
||||
/// <returns>A deterministic hash code.</returns>
|
||||
private static int GetDeterministicHashCode(string str)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hash1 = (5381 << 16) + 5381;
|
||||
int hash2 = hash1;
|
||||
|
||||
for (int index = 0; index < str.Length; index += 2)
|
||||
get
|
||||
{
|
||||
hash1 = ((hash1 << 5) + hash1) ^ str[index];
|
||||
if (index == str.Length - 1)
|
||||
if (s_Adjectives == null)
|
||||
{
|
||||
break;
|
||||
try
|
||||
{
|
||||
s_Adjectives = File.ReadAllLines(Path.Combine(MyPaths.ResourcePath, Resources.Paths.AdjectivesTxt));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, $"RandomNameGenerator failed to read adjectives");
|
||||
}
|
||||
}
|
||||
|
||||
hash2 = ((hash2 << 5) + hash2) ^ str[index + 1];
|
||||
if (s_Adjectives != null)
|
||||
{
|
||||
return s_Adjectives;
|
||||
}
|
||||
|
||||
return new string[] { };
|
||||
}
|
||||
}
|
||||
|
||||
private static string[]? s_Nouns;
|
||||
private static string[] Nouns
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_Nouns == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
s_Nouns = File.ReadAllLines(Path.Combine(MyPaths.ResourcePath, Resources.Paths.NounsTxt));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, $"RandomNameGenerator failed to read nouns");
|
||||
}
|
||||
}
|
||||
|
||||
if (s_Nouns != null)
|
||||
{
|
||||
return s_Nouns;
|
||||
}
|
||||
|
||||
return new string[] { };
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a name for the given string.
|
||||
/// </summary>
|
||||
/// <param name="str">The string to generate a name for.</param>
|
||||
/// <returns>A generated name.</returns>
|
||||
public static string? Generate(string str)
|
||||
{
|
||||
if (Adjectives == null || Nouns == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return hash1 + (hash2 * 1566083941);
|
||||
int hash = GetDeterministicHashCode(str);
|
||||
|
||||
// Use the seed as the hash so the same player always gets the same name
|
||||
Random random = new Random(hash);
|
||||
var adjective = Adjectives[random.Next(0, Adjectives.Length)];
|
||||
var noun = Nouns[random.Next(0, Nouns.Length)];
|
||||
var generatedName = $"{adjective} {noun}";
|
||||
|
||||
return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(generatedName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a deterministic hash code for the given string.
|
||||
/// </summary>
|
||||
/// <param name="str">The string to hash.</param>
|
||||
/// <returns>A deterministic hash code.</returns>
|
||||
private static int GetDeterministicHashCode(string str)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hash1 = (5381 << 16) + 5381;
|
||||
int hash2 = hash1;
|
||||
|
||||
for (int index = 0; index < str.Length; index += 2)
|
||||
{
|
||||
hash1 = ((hash1 << 5) + hash1) ^ str[index];
|
||||
if (index == str.Length - 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
hash2 = ((hash2 << 5) + hash2) ^ str[index + 1];
|
||||
}
|
||||
|
||||
return hash1 + (hash2 * 1566083941);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
8
PlayerTags/Resources/Strings.Designer.cs
generated
8
PlayerTags/Resources/Strings.Designer.cs
generated
@@ -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 -> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 -> Setzt einen globalen Hauptschalter, welcher alle PluginFeaturues ein- bzw. ausschalten kann, ohne die Konfiguration selbst zu ändern.</value>
|
||||
</data>
|
||||
</root>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user