refactor: use new Dalamud PF event
This commit is contained in:
parent
a83bb75e21
commit
a3ce973dda
|
@ -8,7 +8,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Dalamud, Version=5.2.3.3, Culture=neutral, PublicKeyToken=null">
|
||||
<Reference Include="Dalamud, Version=5.2.3.5, Culture=neutral, PublicKeyToken=null">
|
||||
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\Dalamud.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
|
@ -35,7 +35,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DalamudPackager" Version="1.2.0"/>
|
||||
<PackageReference Include="DalamudPackager" Version="1.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Dalamud.Configuration;
|
||||
using Dalamud.Game.Internal.Gui.Structs;
|
||||
|
||||
namespace BetterPartyFinder {
|
||||
public class Configuration : IPluginConfiguration {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Dalamud.Game.Internal.Gui;
|
||||
using Dalamud.Game.Internal.Gui.Structs;
|
||||
|
||||
namespace BetterPartyFinder {
|
||||
public class Filter : IDisposable {
|
||||
|
@ -9,7 +11,11 @@ namespace BetterPartyFinder {
|
|||
internal Filter(Plugin plugin) {
|
||||
this.Plugin = plugin;
|
||||
|
||||
this.Plugin.Functions.ReceivePartyFinderListing += this.ReceiveListing;
|
||||
this.Plugin.Interface.Framework.Gui.PartyFinder.ReceiveListing += this.ReceiveListing;
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
this.Plugin.Interface.Framework.Gui.PartyFinder.ReceiveListing -= this.ReceiveListing;
|
||||
}
|
||||
|
||||
private void ReceiveListing(PartyFinderListing listing, PartyFinderListingEventArgs args) {
|
||||
|
@ -142,16 +148,12 @@ namespace BetterPartyFinder {
|
|||
|
||||
// filter based on player
|
||||
if (filter.Players.Count > 0) {
|
||||
if (filter.Players.Any(info => info.Name == listing.Name && info.World == listing.HomeWorld.Value.RowId)) {
|
||||
if (filter.Players.Any(info => info.Name == listing.Name.TextValue && info.World == listing.HomeWorld.Value.RowId)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
this.Plugin.Functions.ReceivePartyFinderListing -= this.ReceiveListing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Plugin;
|
||||
|
||||
namespace BetterPartyFinder {
|
||||
public class GameFunctions : IDisposable {
|
||||
|
@ -14,18 +13,6 @@ namespace BetterPartyFinder {
|
|||
|
||||
#endregion
|
||||
|
||||
#region PF Listings events
|
||||
|
||||
internal delegate void PartyFinderListingEventDelegate(PartyFinderListing listing, PartyFinderListingEventArgs args);
|
||||
|
||||
internal event PartyFinderListingEventDelegate? ReceivePartyFinderListing;
|
||||
|
||||
private delegate void HandlePfPacketDelegate(IntPtr param1, IntPtr data);
|
||||
|
||||
private readonly Hook<HandlePfPacketDelegate> _handlePacketHook;
|
||||
|
||||
#endregion
|
||||
|
||||
private Plugin Plugin { get; }
|
||||
private IntPtr PartyFinderAgent { get; set; } = IntPtr.Zero;
|
||||
|
||||
|
@ -33,19 +20,14 @@ namespace BetterPartyFinder {
|
|||
this.Plugin = plugin;
|
||||
|
||||
var requestPfPtr = this.Plugin.Interface.TargetModuleScanner.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 40 0F 10 81 ?? ?? ?? ??");
|
||||
var listingPtr = this.Plugin.Interface.TargetModuleScanner.ScanText("40 53 41 57 48 83 EC 28 48 8B D9");
|
||||
|
||||
this._requestPartyFinderListings = Marshal.GetDelegateForFunctionPointer<RequestPartyFinderListingsDelegate>(requestPfPtr);
|
||||
this._requestPfListingsHook = new Hook<RequestPartyFinderListingsDelegate>(requestPfPtr, new RequestPartyFinderListingsDelegate(this.OnRequestPartyFinderListings));
|
||||
this._requestPfListingsHook.Enable();
|
||||
|
||||
this._handlePacketHook = new Hook<HandlePfPacketDelegate>(listingPtr, new HandlePfPacketDelegate(this.PacketDetour));
|
||||
this._handlePacketHook.Enable();
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
this._requestPfListingsHook.Dispose();
|
||||
this._handlePacketHook.Dispose();
|
||||
}
|
||||
|
||||
private byte OnRequestPartyFinderListings(IntPtr agent, byte categoryIdx) {
|
||||
|
@ -69,71 +51,5 @@ namespace BetterPartyFinder {
|
|||
var categoryIdx = Marshal.ReadByte(this.PartyFinderAgent + categoryOffset);
|
||||
this._requestPartyFinderListings(this.PartyFinderAgent, categoryIdx);
|
||||
}
|
||||
|
||||
|
||||
private void PacketDetour(IntPtr param1, IntPtr data) {
|
||||
if (data == IntPtr.Zero) {
|
||||
goto Return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.OnPacket(data);
|
||||
} catch (Exception ex) {
|
||||
PluginLog.Error(ex, "Unhandled exception in PF packet detour");
|
||||
}
|
||||
|
||||
Return:
|
||||
this._handlePacketHook!.Original(param1, data);
|
||||
}
|
||||
|
||||
private void OnPacket(IntPtr data) {
|
||||
var dataPtr = data + 0x10;
|
||||
|
||||
// parse the packet into a struct
|
||||
var packet = Marshal.PtrToStructure<PfPacket>(dataPtr);
|
||||
|
||||
var needToRewrite = false;
|
||||
|
||||
for (var i = 0; i < packet.listings.Length; i++) {
|
||||
if (packet.listings[i].IsNull()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// invoke event for each non-null listing
|
||||
var listing = new PartyFinderListing(packet.listings[i], this.Plugin.Interface.Data);
|
||||
var args = new PartyFinderListingEventArgs();
|
||||
this.ReceivePartyFinderListing?.Invoke(listing, args);
|
||||
|
||||
if (args.Visible) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// zero the listing if it shouldn't be visible
|
||||
packet.listings[i] = new PfListing();
|
||||
needToRewrite = true;
|
||||
}
|
||||
|
||||
if (!needToRewrite) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get some memory for writing to
|
||||
var newPacket = new byte[PacketInfo.PacketSize];
|
||||
var pinnedArray = GCHandle.Alloc(newPacket, GCHandleType.Pinned);
|
||||
var pointer = pinnedArray.AddrOfPinnedObject();
|
||||
|
||||
// write our struct into the memory (doing this directly crashes the game)
|
||||
Marshal.StructureToPtr(packet, pointer, false);
|
||||
|
||||
// copy our new memory over the game's
|
||||
Marshal.Copy(newPacket, 0, dataPtr, PacketInfo.PacketSize);
|
||||
|
||||
// free memory
|
||||
pinnedArray.Free();
|
||||
}
|
||||
}
|
||||
|
||||
internal class PartyFinderListingEventArgs {
|
||||
public bool Visible { get; set; } = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,242 +0,0 @@
|
|||
using Dalamud.Data;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace BetterPartyFinder {
|
||||
public class PartyFinderListing {
|
||||
public uint Id { get; }
|
||||
public string Name { get; }
|
||||
public string Description { get; }
|
||||
public Lazy<World> World { get; }
|
||||
public Lazy<World> HomeWorld { get; }
|
||||
public Lazy<World> CurrentWorld { get; }
|
||||
public Category Category { get; }
|
||||
public ushort RawDuty { get; }
|
||||
public Lazy<ContentFinderCondition> Duty { get; }
|
||||
public DutyType DutyType { get; }
|
||||
public bool BeginnersWelcome { get; }
|
||||
public ushort SecondsRemaining { get; }
|
||||
public ushort MinimumItemLevel { get; }
|
||||
public byte Parties { get; }
|
||||
public byte SlotsAvailable { get; }
|
||||
public IEnumerable<PartyFinderSlot> Slots => this._slots;
|
||||
|
||||
private readonly byte _objective;
|
||||
public ObjectiveFlags Objective => (ObjectiveFlags) this._objective;
|
||||
|
||||
private readonly byte _conditions;
|
||||
public ConditionFlags Conditions => (ConditionFlags) this._conditions;
|
||||
|
||||
private readonly byte _dutyFinderSettings;
|
||||
public DutyFinderSettingsFlags DutyFinderSettings => (DutyFinderSettingsFlags) this._dutyFinderSettings;
|
||||
|
||||
private readonly byte _lootRules;
|
||||
public LootRuleFlags LootRules => (LootRuleFlags) this._lootRules;
|
||||
|
||||
private readonly byte _searchArea;
|
||||
public SearchAreaFlags SearchArea => (SearchAreaFlags) this._searchArea;
|
||||
|
||||
private readonly PartyFinderSlot[] _slots;
|
||||
|
||||
private readonly byte[] _jobsPresent;
|
||||
internal IEnumerable<byte> RawJobsPresent => this._jobsPresent;
|
||||
internal IReadOnlyCollection<Lazy<ClassJob?>> JobsPresent { get; }
|
||||
|
||||
public bool this[ObjectiveFlags flag] => this._objective == 0 || (this._objective & (uint) flag) > 0;
|
||||
|
||||
|
||||
public bool this[ConditionFlags flag] => this._conditions == 0 || (this._conditions & (uint) flag) > 0;
|
||||
|
||||
public bool this[DutyFinderSettingsFlags flag] => this._dutyFinderSettings == 0 || (this._dutyFinderSettings & (uint) flag) > 0;
|
||||
|
||||
public bool this[LootRuleFlags flag] => this._lootRules == 0 || (this._lootRules & (uint) flag) > 0;
|
||||
|
||||
public bool this[SearchAreaFlags flag] => this._searchArea == 0 || (this._searchArea & (uint) flag) > 0;
|
||||
|
||||
internal PartyFinderListing(PfListing listing, DataManager dataManager) {
|
||||
this.Id = listing.id;
|
||||
this.Name = listing.Name();
|
||||
this.Description = listing.Description();
|
||||
this.World = new Lazy<World>(() => dataManager.GetExcelSheet<World>().GetRow(listing.world));
|
||||
this.HomeWorld = new Lazy<World>(() => dataManager.GetExcelSheet<World>().GetRow(listing.homeWorld));
|
||||
this.CurrentWorld = new Lazy<World>(() => dataManager.GetExcelSheet<World>().GetRow(listing.currentWorld));
|
||||
this.Category = (Category) listing.category;
|
||||
this.RawDuty = listing.duty;
|
||||
this.Duty = new Lazy<ContentFinderCondition>(() => dataManager.GetExcelSheet<ContentFinderCondition>().GetRow(listing.duty));
|
||||
this.DutyType = (DutyType) listing.dutyType;
|
||||
this.BeginnersWelcome = listing.beginnersWelcome == 1;
|
||||
this.SecondsRemaining = listing.secondsRemaining;
|
||||
this.MinimumItemLevel = listing.minimumItemLevel;
|
||||
this.Parties = listing.numParties;
|
||||
this.SlotsAvailable = listing.numSlots;
|
||||
|
||||
this._objective = listing.objective;
|
||||
this._conditions = listing.conditions;
|
||||
this._dutyFinderSettings = listing.dutyFinderSettings;
|
||||
this._lootRules = listing.lootRules;
|
||||
this._searchArea = listing.searchArea;
|
||||
|
||||
this._slots = listing.slots.Select(accepting => new PartyFinderSlot(accepting)).ToArray();
|
||||
this._jobsPresent = listing.jobsPresent;
|
||||
this.JobsPresent = this._jobsPresent
|
||||
.Select(id => new Lazy<ClassJob?>(() => id == 0
|
||||
? null
|
||||
: dataManager.GetExcelSheet<ClassJob>().GetRow(id)))
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public class PartyFinderSlot {
|
||||
private readonly uint _accepting;
|
||||
private JobFlags[]? _listAccepting;
|
||||
|
||||
public IReadOnlyCollection<JobFlags> Accepting {
|
||||
get {
|
||||
if (this._listAccepting != null) {
|
||||
return this._listAccepting;
|
||||
}
|
||||
|
||||
this._listAccepting = Enum.GetValues(typeof(JobFlags))
|
||||
.Cast<JobFlags>()
|
||||
.Where(flag => this[flag])
|
||||
.ToArray();
|
||||
|
||||
return this._listAccepting;
|
||||
}
|
||||
}
|
||||
|
||||
public bool this[JobFlags flag] => (this._accepting & (uint) flag) > 0;
|
||||
|
||||
internal PartyFinderSlot(uint accepting) {
|
||||
this._accepting = accepting;
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum SearchAreaFlags : uint {
|
||||
DataCentre = 1 << 0,
|
||||
Private = 1 << 1,
|
||||
AllianceRaid = 1 << 2,
|
||||
World = 1 << 3,
|
||||
OnePlayerPerJob = 1 << 5,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum JobFlags {
|
||||
Gladiator = 1 << 1,
|
||||
Pugilist = 1 << 2,
|
||||
Marauder = 1 << 3,
|
||||
Lancer = 1 << 4,
|
||||
Archer = 1 << 5,
|
||||
Conjurer = 1 << 6,
|
||||
Thaumaturge = 1 << 7,
|
||||
Paladin = 1 << 8,
|
||||
Monk = 1 << 9,
|
||||
Warrior = 1 << 10,
|
||||
Dragoon = 1 << 11,
|
||||
Bard = 1 << 12,
|
||||
WhiteMage = 1 << 13,
|
||||
BlackMage = 1 << 14,
|
||||
Arcanist = 1 << 15,
|
||||
Summoner = 1 << 16,
|
||||
Scholar = 1 << 17,
|
||||
Rogue = 1 << 18,
|
||||
Ninja = 1 << 19,
|
||||
Machinist = 1 << 20,
|
||||
DarkKnight = 1 << 21,
|
||||
Astrologian = 1 << 22,
|
||||
Samurai = 1 << 23,
|
||||
RedMage = 1 << 24,
|
||||
BlueMage = 1 << 25,
|
||||
Gunbreaker = 1 << 26,
|
||||
Dancer = 1 << 27,
|
||||
}
|
||||
|
||||
internal static class JobFlagsExt {
|
||||
internal static ClassJob? ClassJob(this JobFlags job, DataManager data) {
|
||||
var jobs = data.GetExcelSheet<ClassJob>();
|
||||
|
||||
uint? row = job switch {
|
||||
JobFlags.Gladiator => 1,
|
||||
JobFlags.Pugilist => 2,
|
||||
JobFlags.Marauder => 3,
|
||||
JobFlags.Lancer => 4,
|
||||
JobFlags.Archer => 5,
|
||||
JobFlags.Conjurer => 6,
|
||||
JobFlags.Thaumaturge => 7,
|
||||
JobFlags.Paladin => 19,
|
||||
JobFlags.Monk => 20,
|
||||
JobFlags.Warrior => 21,
|
||||
JobFlags.Dragoon => 22,
|
||||
JobFlags.Bard => 23,
|
||||
JobFlags.WhiteMage => 24,
|
||||
JobFlags.BlackMage => 25,
|
||||
JobFlags.Arcanist => 26,
|
||||
JobFlags.Summoner => 27,
|
||||
JobFlags.Scholar => 28,
|
||||
JobFlags.Rogue => 29,
|
||||
JobFlags.Ninja => 30,
|
||||
JobFlags.Machinist => 31,
|
||||
JobFlags.DarkKnight => 32,
|
||||
JobFlags.Astrologian => 33,
|
||||
JobFlags.Samurai => 34,
|
||||
JobFlags.RedMage => 35,
|
||||
JobFlags.BlueMage => 36,
|
||||
JobFlags.Gunbreaker => 37,
|
||||
JobFlags.Dancer => 38,
|
||||
_ => null,
|
||||
};
|
||||
|
||||
return row == null ? null : jobs.GetRow((uint) row);
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum ObjectiveFlags : uint {
|
||||
None = 0,
|
||||
DutyCompletion = 1,
|
||||
Practice = 2,
|
||||
Loot = 4,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum ConditionFlags : uint {
|
||||
None = 1,
|
||||
DutyComplete = 2,
|
||||
DutyIncomplete = 4,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum DutyFinderSettingsFlags : uint {
|
||||
None = 0,
|
||||
UndersizedParty = 1 << 0,
|
||||
MinimumItemLevel = 1 << 1,
|
||||
SilenceEcho = 1 << 2,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum LootRuleFlags : uint {
|
||||
None = 0,
|
||||
GreedOnly = 1,
|
||||
Lootmaster = 2,
|
||||
}
|
||||
|
||||
public enum Category {
|
||||
Duty = 0,
|
||||
QuestBattles = 1 << 0,
|
||||
Fates = 1 << 1,
|
||||
TreasureHunt = 1 << 2,
|
||||
TheHunt = 1 << 3,
|
||||
GatheringForays = 1 << 4,
|
||||
DeepDungeons = 1 << 5,
|
||||
AdventuringForays = 1 << 6,
|
||||
}
|
||||
|
||||
public enum DutyType {
|
||||
Other = 0,
|
||||
Roulette = 1 << 0,
|
||||
Normal = 1 << 1,
|
||||
}
|
||||
}
|
|
@ -1,125 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace BetterPartyFinder {
|
||||
public static class PacketInfo {
|
||||
public static readonly int PacketSize = Marshal.SizeOf<PfPacket>();
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public readonly struct PfPacket {
|
||||
private readonly int unk0;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
|
||||
private readonly byte[] padding1;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
public readonly PfListing[] listings;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public readonly struct PfListing {
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
private readonly byte[] header1;
|
||||
|
||||
internal readonly uint id;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
private readonly byte[] header2;
|
||||
|
||||
private readonly uint unknownInt1;
|
||||
private readonly ushort unknownShort1;
|
||||
private readonly ushort unknownShort2;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
|
||||
private readonly byte[] header3;
|
||||
|
||||
internal readonly byte category;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
|
||||
private readonly byte[] header4;
|
||||
|
||||
internal readonly ushort duty;
|
||||
internal readonly byte dutyType;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)]
|
||||
private readonly byte[] header5;
|
||||
|
||||
internal readonly ushort world;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
|
||||
private readonly byte[] header6;
|
||||
|
||||
internal readonly byte objective;
|
||||
internal readonly byte beginnersWelcome;
|
||||
internal readonly byte conditions;
|
||||
internal readonly byte dutyFinderSettings;
|
||||
internal readonly byte lootRules;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
|
||||
private readonly byte[] header7; // all zero in every pf I've examined
|
||||
|
||||
private readonly uint lastPatchHotfixTimestamp; // last time the servers were restarted?
|
||||
internal readonly ushort secondsRemaining;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
|
||||
private readonly byte[] header8; // 00 00 01 00 00 00 in every pf I've examined
|
||||
|
||||
internal readonly ushort minimumItemLevel;
|
||||
internal readonly ushort homeWorld;
|
||||
internal readonly ushort currentWorld;
|
||||
|
||||
private readonly byte header9;
|
||||
|
||||
internal readonly byte numSlots;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
|
||||
private readonly byte[] header10;
|
||||
|
||||
internal readonly byte searchArea;
|
||||
|
||||
private readonly byte header11;
|
||||
|
||||
internal readonly byte numParties;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
|
||||
private readonly byte[] header12; // 00 00 00 always. maybe numParties is a u32?
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
|
||||
internal readonly uint[] slots;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
|
||||
internal readonly byte[] jobsPresent;
|
||||
|
||||
// Note that ByValTStr will not work here because the strings are UTF-8 and there's only a CharSet for UTF-16 in C#.
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
|
||||
private readonly byte[] name;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 192)]
|
||||
private readonly byte[] description;
|
||||
|
||||
// 128 (0x80) before name and desc
|
||||
// 160 (0xA0) with name (32 bytes/0x20)
|
||||
// 352 (0x160) with both (192 bytes/0xC0)
|
||||
|
||||
private static string HandleString(IEnumerable<byte> bytes) {
|
||||
var nonNull = bytes.TakeWhile(b => b != 0).ToArray();
|
||||
return Encoding.UTF8.GetString(nonNull);
|
||||
}
|
||||
|
||||
internal string Name() {
|
||||
return HandleString(this.name);
|
||||
}
|
||||
|
||||
internal string Description() {
|
||||
return HandleString(this.description);
|
||||
}
|
||||
|
||||
internal bool IsNull() {
|
||||
// a valid party finder must have at least one slot set
|
||||
return this.slots.All(slot => slot == 0);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,9 +3,11 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Dalamud.Data;
|
||||
using Dalamud.Game.Internal.Gui.Structs;
|
||||
using Dalamud.Interface;
|
||||
using ImGuiNET;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Addon = Lumina.Excel.GeneratedSheets.Addon;
|
||||
using GameAddon = Dalamud.Game.Internal.Gui.Addon.Addon;
|
||||
|
||||
namespace BetterPartyFinder {
|
||||
|
|
Loading…
Reference in New Issue