From 6113c04093c1723f46afe8bb31fb1847867730ce Mon Sep 17 00:00:00 2001 From: Anna Clemens Date: Tue, 24 Aug 2021 14:00:42 -0400 Subject: [PATCH] refactor: move to net5 --- BetterPartyFinder/BetterPartyFinder.csproj | 23 ++-- BetterPartyFinder/BetterPartyFinder.yaml | 1 + BetterPartyFinder/Commands.cs | 8 +- BetterPartyFinder/Configuration.cs | 4 +- BetterPartyFinder/Filter.cs | 11 +- BetterPartyFinder/FodyWeavers.xml | 3 - BetterPartyFinder/JoinHandler.cs | 6 +- BetterPartyFinder/Plugin.cs | 52 +++++++-- BetterPartyFinder/PluginUi.cs | 127 ++++++++++----------- BetterPartyFinder/Util.cs | 8 +- icon.png | Bin 0 -> 13610 bytes icon.svg | 94 +++++++++++++++ 12 files changed, 230 insertions(+), 107 deletions(-) delete mode 100755 BetterPartyFinder/FodyWeavers.xml create mode 100644 icon.png create mode 100755 icon.svg diff --git a/BetterPartyFinder/BetterPartyFinder.csproj b/BetterPartyFinder/BetterPartyFinder.csproj index 33d954a..60b8f8e 100755 --- a/BetterPartyFinder/BetterPartyFinder.csproj +++ b/BetterPartyFinder/BetterPartyFinder.csproj @@ -1,10 +1,13 @@ - net48 + net5-windows 1.2.0 latest enable + false + true + true @@ -12,6 +15,10 @@ $(AppData)\XIVLauncher\addon\Hooks\dev\Dalamud.dll False + + $(AppData)\XIVLauncher\addon\Hooks\dev\FFXIVClientStructs.dll + False + $(AppData)\XIVLauncher\addon\Hooks\dev\ImGui.NET.dll False @@ -28,17 +35,15 @@ $(AppData)\XIVLauncher\addon\Hooks\dev\Lumina.Excel.dll False - - $(AppData)\XIVLauncher\addon\Hooks\dev\System.Memory.dll - False - - - - - + + + + + + diff --git a/BetterPartyFinder/BetterPartyFinder.yaml b/BetterPartyFinder/BetterPartyFinder.yaml index 8418f93..55b5519 100755 --- a/BetterPartyFinder/BetterPartyFinder.yaml +++ b/BetterPartyFinder/BetterPartyFinder.yaml @@ -1,5 +1,6 @@ author: ascclemens name: Better Party Finder +punchline: Use advanced Party Finder filter presets. description: |- Filter the party finder better. diff --git a/BetterPartyFinder/Commands.cs b/BetterPartyFinder/Commands.cs index e162ba4..4e854a4 100755 --- a/BetterPartyFinder/Commands.cs +++ b/BetterPartyFinder/Commands.cs @@ -14,16 +14,16 @@ namespace BetterPartyFinder { internal Commands(Plugin plugin) { this.Plugin = plugin; - foreach (var name in CommandNames) { - this.Plugin.Interface.CommandManager.AddHandler(name.Key, new CommandInfo(this.OnCommand) { - HelpMessage = name.Value, + foreach (var (name, help) in CommandNames) { + this.Plugin.CommandManager.AddHandler(name, new CommandInfo(this.OnCommand) { + HelpMessage = help, }); } } public void Dispose() { foreach (var name in CommandNames.Keys) { - this.Plugin.Interface.CommandManager.RemoveHandler(name); + this.Plugin.CommandManager.RemoveHandler(name); } } diff --git a/BetterPartyFinder/Configuration.cs b/BetterPartyFinder/Configuration.cs index 969c4e8..aa2db2e 100755 --- a/BetterPartyFinder/Configuration.cs +++ b/BetterPartyFinder/Configuration.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using Dalamud.Configuration; -using Dalamud.Game.Internal.Gui.Structs; +using Dalamud.Game.Gui.PartyFinder.Types; namespace BetterPartyFinder { public class Configuration : IPluginConfiguration { @@ -120,7 +120,7 @@ namespace BetterPartyFinder { Conditions = this.Conditions, Duties = duties, Jobs = jobs, - Name = string.Copy(this.Name), + Name = new string(this.Name), Objectives = this.Objectives, DutiesMode = this.DutiesMode, LootRule = this.LootRule, diff --git a/BetterPartyFinder/Filter.cs b/BetterPartyFinder/Filter.cs index 610e341..3c09e40 100755 --- a/BetterPartyFinder/Filter.cs +++ b/BetterPartyFinder/Filter.cs @@ -1,8 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using Dalamud.Game.Internal.Gui; -using Dalamud.Game.Internal.Gui.Structs; +using Dalamud.Game.Gui.PartyFinder.Types; namespace BetterPartyFinder { public class Filter : IDisposable { @@ -11,11 +10,11 @@ namespace BetterPartyFinder { internal Filter(Plugin plugin) { this.Plugin = plugin; - this.Plugin.Interface.Framework.Gui.PartyFinder.ReceiveListing += this.ReceiveListing; + this.Plugin.PartyFinderGui.ReceiveListing += this.ReceiveListing; } public void Dispose() { - this.Plugin.Interface.Framework.Gui.PartyFinder.ReceiveListing -= this.ReceiveListing; + this.Plugin.PartyFinderGui.ReceiveListing -= this.ReceiveListing; } private void ReceiveListing(PartyFinderListing listing, PartyFinderListingEventArgs args) { @@ -77,7 +76,7 @@ namespace BetterPartyFinder { } // filter based on category (slow) - if (!filter.Categories.Any(category => category.ListingMatches(this.Plugin.Interface.Data, listing))) { + if (!filter.Categories.Any(category => category.ListingMatches(this.Plugin.DataManager, listing))) { return false; } @@ -109,7 +108,7 @@ namespace BetterPartyFinder { continue; } - var job = possibleJob.ClassJob(this.Plugin.Interface.Data); + var job = possibleJob.ClassJob(this.Plugin.DataManager); if (present.Contains((byte) job.RowId)) { continue; } diff --git a/BetterPartyFinder/FodyWeavers.xml b/BetterPartyFinder/FodyWeavers.xml deleted file mode 100755 index 2dfb1f4..0000000 --- a/BetterPartyFinder/FodyWeavers.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/BetterPartyFinder/JoinHandler.cs b/BetterPartyFinder/JoinHandler.cs index f2135a8..58ad9d9 100755 --- a/BetterPartyFinder/JoinHandler.cs +++ b/BetterPartyFinder/JoinHandler.cs @@ -1,5 +1,5 @@ using System; -using Dalamud.Game.Internal.Gui.Structs; +using Dalamud.Game.Gui.PartyFinder.Types; using Dalamud.Game.Text; using Dalamud.Game.Text.SeStringHandling; @@ -25,10 +25,10 @@ namespace BetterPartyFinder { SeString msg = "Party description: "; msg.Payloads.AddRange(listing.Description.Payloads); - this.Plugin.Interface.Framework.Gui.Chat.PrintChat(new XivChatEntry { + this.Plugin.ChatGui.PrintChat(new XivChatEntry { Name = "Better Party Finder", Type = XivChatType.SystemMessage, - MessageBytes = msg.Encode(), + Message = msg, }); } } diff --git a/BetterPartyFinder/Plugin.cs b/BetterPartyFinder/Plugin.cs index 9911025..4d83326 100755 --- a/BetterPartyFinder/Plugin.cs +++ b/BetterPartyFinder/Plugin.cs @@ -1,4 +1,11 @@ -using Dalamud.Plugin; +using Dalamud.Data; +using Dalamud.Game.ClientState; +using Dalamud.Game.Command; +using Dalamud.Game.Gui; +using Dalamud.Game.Gui.PartyFinder; +using Dalamud.Game.Text.SeStringHandling; +using Dalamud.IoC; +using Dalamud.Plugin; using XivCommon; namespace BetterPartyFinder { @@ -6,28 +13,49 @@ namespace BetterPartyFinder { public class Plugin : IDalamudPlugin { public string Name => "Better Party Finder"; - internal DalamudPluginInterface Interface { get; private set; } = null!; - internal Configuration Config { get; private set; } = null!; - private Filter Filter { get; set; } = null!; - internal PluginUi Ui { get; private set; } = null!; - private Commands Commands { get; set; } = null!; - internal XivCommonBase Common { get; private set; } = null!; - private JoinHandler JoinHandler { get; set; } = null!; + [PluginService] + internal DalamudPluginInterface Interface { get; init; } = null!; - public void Initialize(DalamudPluginInterface pluginInterface) { - this.Interface = pluginInterface; + [PluginService] + internal ChatGui ChatGui { get; init; } = null!; + [PluginService] + internal ClientState ClientState { get; init; } = null!; + + [PluginService] + internal CommandManager CommandManager { get; init; } = null!; + + [PluginService] + internal DataManager DataManager { get; init; } = null!; + + [PluginService] + internal GameGui GameGui { get; init; } = null!; + + [PluginService] + internal PartyFinderGui PartyFinderGui { get; init; } = null!; + + [PluginService] + internal SeStringManager SeStringManager { get; init; } = null!; + + internal Configuration Config { get; } + private Filter Filter { get; } + internal PluginUi Ui { get; } + private Commands Commands { get; } + internal XivCommonBase Common { get; } + private JoinHandler JoinHandler { get; } + + public Plugin() { this.Config = Configuration.Load(this) ?? new Configuration(); this.Config.Initialise(this); - this.Common = new XivCommonBase(this.Interface, Hooks.PartyFinder); + this.Common = new XivCommonBase(Hooks.PartyFinder); this.Filter = new Filter(this); this.JoinHandler = new JoinHandler(this); this.Ui = new PluginUi(this); this.Commands = new Commands(this); // start task to determine maximum item level (based on max chestpiece) - Util.CalculateMaxItemLevel(this.Interface.Data); + Util.CalculateMaxItemLevel(this.DataManager); } public void Dispose() { diff --git a/BetterPartyFinder/PluginUi.cs b/BetterPartyFinder/PluginUi.cs index 8e1910c..63e1f9e 100755 --- a/BetterPartyFinder/PluginUi.cs +++ b/BetterPartyFinder/PluginUi.cs @@ -3,12 +3,12 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; using Dalamud.Data; -using Dalamud.Game.Internal.Gui.Structs; +using Dalamud.Game.Gui.PartyFinder.Types; using Dalamud.Interface; +using FFXIVClientStructs.FFXIV.Component.GUI; using ImGuiNET; using Lumina.Excel.GeneratedSheets; using Addon = Lumina.Excel.GeneratedSheets.Addon; -using GameAddon = Dalamud.Game.Internal.Gui.Addon.Addon; namespace BetterPartyFinder { public class PluginUi : IDisposable { @@ -47,16 +47,16 @@ namespace BetterPartyFinder { internal PluginUi(Plugin plugin) { this.Plugin = plugin; - this.Plugin.Interface.UiBuilder.OnBuildUi += this.Draw; - this.Plugin.Interface.UiBuilder.OnOpenConfigUi += this.OnOpenConfig; + this.Plugin.Interface.UiBuilder.Draw += this.Draw; + this.Plugin.Interface.UiBuilder.OpenConfigUi += this.OnOpenConfig; } public void Dispose() { - this.Plugin.Interface.UiBuilder.OnBuildUi -= this.Draw; - this.Plugin.Interface.UiBuilder.OnOpenConfigUi -= this.OnOpenConfig; + this.Plugin.Interface.UiBuilder.Draw -= this.Draw; + this.Plugin.Interface.UiBuilder.OpenConfigUi -= this.OnOpenConfig; } - private void OnOpenConfig(object sender, EventArgs e) { + private void OnOpenConfig() { this.Visible = !this.Visible; } @@ -75,8 +75,8 @@ namespace BetterPartyFinder { return result; } - private GameAddon? PartyFinderAddon() { - return this.Plugin.Interface.Framework.Gui.GetAddonByName("LookingForGroup", 1); + private IntPtr PartyFinderAddon() { + return this.Plugin.GameGui.GetAddonByName("LookingForGroup", 1); } private void Draw() { @@ -125,27 +125,30 @@ namespace BetterPartyFinder { ImGui.End(); } - private void DrawFiltersWindow() { + private unsafe void DrawFiltersWindow() { ImGui.SetNextWindowSize(new Vector2(550f, 510f), ImGuiCond.FirstUseEver); - var addon = this.Plugin.Config.ShowWhenPfOpen ? this.PartyFinderAddon() : null; + AtkUnitBase* addon = null; + var addonPtr = this.PartyFinderAddon(); + if (this.Plugin.Config.ShowWhenPfOpen && addonPtr != IntPtr.Zero) { + addon = (AtkUnitBase*) addonPtr; + } - var showWindow = this.Visible || addon?.Visible == true; + var showWindow = this.Visible || addon != null && addon->IsVisible; if (!showWindow) { return; } if (!ImGui.Begin(this.Plugin.Name, ref this._visible, ImGuiWindowFlags.NoDocking)) { - if (ImGui.IsWindowCollapsed() && addon is {Visible: true}) { + if (ImGui.IsWindowCollapsed() && addon != null && addon->IsVisible) { // wait until addon is initialised to show - try { - _ = addon.Width; - } catch (NullReferenceException) { + var rootNode = addon->RootNode; + if (rootNode == null) { return; } - ImGui.SetWindowPos(ImGuiHelpers.MainViewport.Pos + new Vector2(addon.X, addon.Y - ImGui.GetFrameHeight())); + ImGui.SetWindowPos(ImGuiHelpers.MainViewport.Pos + new Vector2(addon->X, addon->Y - ImGui.GetFrameHeight())); } ImGui.End(); @@ -153,10 +156,9 @@ namespace BetterPartyFinder { } if (addon != null && this.Plugin.Config.WindowSide == WindowSide.Right) { - try { - ImGui.SetWindowPos(ImGuiHelpers.MainViewport.Pos + new Vector2(addon.X + addon.Width, addon.Y)); - } catch (NullReferenceException) { - // ignore + var rootNode = addon->RootNode; + if (rootNode != null) { + ImGui.SetWindowPos(ImGuiHelpers.MainViewport.Pos + new Vector2(addon->X + rootNode->Width, addon->Y)); } } @@ -272,13 +274,10 @@ namespace BetterPartyFinder { } if (addon != null && this.Plugin.Config.WindowSide == WindowSide.Left) { - try { - _ = addon.Width; - // only continue if width is set, meaning addon is initialised + var rootNode = addon->RootNode; + if (rootNode != null) { var currentWidth = ImGui.GetWindowWidth(); - ImGui.SetWindowPos(ImGuiHelpers.MainViewport.Pos + new Vector2(addon.X - currentWidth, addon.Y)); - } catch (NullReferenceException) { - // ignore + ImGui.SetWindowPos(ImGuiHelpers.MainViewport.Pos + new Vector2(addon->X - currentWidth, addon->Y)); } } @@ -312,7 +311,7 @@ namespace BetterPartyFinder { foreach (var category in (UiCategory[]) Enum.GetValues(typeof(UiCategory))) { var selected = filter.Categories.Contains(category); - if (!ImGui.Selectable(category.Name(this.Plugin.Interface.Data), ref selected)) { + if (!ImGui.Selectable(category.Name(this.Plugin.DataManager), ref selected)) { continue; } @@ -361,23 +360,23 @@ namespace BetterPartyFinder { } if (ImGui.BeginChild("duty-selection", new Vector2(-1f, -1f))) { - var duties = this.Plugin.Interface.Data.GetExcelSheet() + var duties = this.Plugin.DataManager.GetExcelSheet()! .Where(cf => cf.Unknown29) .Where(cf => AllowedContentTypes.Contains(cf.ContentType.Row)); var searchQuery = this.DutySearchQuery.Trim(); if (searchQuery.Trim() != "") { duties = duties.Where(duty => { - var sestring = this.Plugin.Interface.SeStringManager.Parse(duty.Name.RawData.ToArray()); + var sestring = this.Plugin.SeStringManager.Parse(duty.Name.RawData.ToArray()); return sestring.TextValue.ContainsIgnoreCase(searchQuery); }); } foreach (var cf in duties) { - var sestring = this.Plugin.Interface.SeStringManager.Parse(cf.Name.RawData.ToArray()); + var sestring = this.Plugin.SeStringManager.Parse(cf.Name.RawData.ToArray()); var selected = filter.Duties.Contains(cf.RowId); var name = sestring.TextValue; - name = char.ToUpperInvariant(name[0]) + name.Substring(1); + name = char.ToUpperInvariant(name[0]) + name[1..]; if (!ImGui.Selectable(name, ref selected)) { continue; } @@ -472,7 +471,7 @@ namespace BetterPartyFinder { foreach (var job in (JobFlags[]) Enum.GetValues(typeof(JobFlags))) { var selected = (slot & job) > 0; - if (!ImGui.Selectable(job.ClassJob(this.Plugin.Interface.Data)?.Name ?? "???", ref selected)) { + if (!ImGui.Selectable(job.ClassJob(this.Plugin.DataManager)?.Name ?? "???", ref selected)) { continue; } @@ -603,7 +602,7 @@ namespace BetterPartyFinder { private string _playerName = string.Empty; private void DrawPlayersTab(ConfigurationFilter filter) { - var player = this.Plugin.Interface.ClientState.LocalPlayer; + var player = this.Plugin.ClientState.LocalPlayer; if (player == null || !ImGui.BeginTabItem("Players")) { return; @@ -615,7 +614,7 @@ namespace BetterPartyFinder { ImGui.SameLine(); - var worlds = Util.WorldsOnDataCentre(this.Plugin.Interface.Data, player) + var worlds = Util.WorldsOnDataCentre(this.Plugin.DataManager, player) .OrderBy(world => world.Name.RawString) .ToList(); @@ -642,7 +641,7 @@ namespace BetterPartyFinder { PlayerInfo? deleting = null; foreach (var info in filter.Players) { - var world = this.Plugin.Interface.Data.GetExcelSheet().GetRow(info.World); + var world = this.Plugin.DataManager.GetExcelSheet()!.GetRow(info.World); ImGui.TextUnformatted($"{info.Name}@{world?.Name}"); ImGui.SameLine(); if (IconButton(FontAwesomeIcon.Trash, $"delete-player-{info.GetHashCode()}")) { @@ -679,54 +678,54 @@ namespace BetterPartyFinder { internal static class UiCategoryExt { internal static string? Name(this UiCategory category, DataManager data) { - var ct = data.GetExcelSheet(); - var addon = data.GetExcelSheet(); + var ct = data.GetExcelSheet()!; + var addon = data.GetExcelSheet()!; return category switch { - UiCategory.None => addon.GetRow(1_562).Text.ToString(), // best guess - UiCategory.DutyRoulette => ct.GetRow((uint) ContentType2.DutyRoulette).Name.ToString(), - UiCategory.Dungeons => ct.GetRow((uint) ContentType2.Dungeons).Name.ToString(), - UiCategory.Guildhests => ct.GetRow((uint) ContentType2.Guildhests).Name.ToString(), - UiCategory.Trials => ct.GetRow((uint) ContentType2.Trials).Name.ToString(), - UiCategory.Raids => ct.GetRow((uint) ContentType2.Raids).Name.ToString(), - UiCategory.HighEndDuty => addon.GetRow(10_822).Text.ToString(), // best guess - UiCategory.Pvp => ct.GetRow((uint) ContentType2.Pvp).Name.ToString(), - UiCategory.QuestBattles => ct.GetRow((uint) ContentType2.QuestBattles).Name.ToString(), - UiCategory.Fates => ct.GetRow((uint) ContentType2.Fates).Name.ToString(), - UiCategory.TreasureHunt => ct.GetRow((uint) ContentType2.TreasureHunt).Name.ToString(), - UiCategory.TheHunt => addon.GetRow(8_613).Text.ToString(), - UiCategory.GatheringForays => addon.GetRow(2_306).Text.ToString(), - UiCategory.DeepDungeons => ct.GetRow((uint) ContentType2.DeepDungeons).Name.ToString(), - UiCategory.AdventuringForays => addon.GetRow(2_307).Text.ToString(), + UiCategory.None => addon.GetRow(1_562)?.Text.ToString(), // best guess + UiCategory.DutyRoulette => ct.GetRow((uint) ContentType2.DutyRoulette)?.Name.ToString(), + UiCategory.Dungeons => ct.GetRow((uint) ContentType2.Dungeons)?.Name.ToString(), + UiCategory.Guildhests => ct.GetRow((uint) ContentType2.Guildhests)?.Name.ToString(), + UiCategory.Trials => ct.GetRow((uint) ContentType2.Trials)?.Name.ToString(), + UiCategory.Raids => ct.GetRow((uint) ContentType2.Raids)?.Name.ToString(), + UiCategory.HighEndDuty => addon.GetRow(10_822)?.Text.ToString(), // best guess + UiCategory.Pvp => ct.GetRow((uint) ContentType2.Pvp)?.Name.ToString(), + UiCategory.QuestBattles => ct.GetRow((uint) ContentType2.QuestBattles)?.Name.ToString(), + UiCategory.Fates => ct.GetRow((uint) ContentType2.Fates)?.Name.ToString(), + UiCategory.TreasureHunt => ct.GetRow((uint) ContentType2.TreasureHunt)?.Name.ToString(), + UiCategory.TheHunt => addon.GetRow(8_613)?.Text.ToString(), + UiCategory.GatheringForays => addon.GetRow(2_306)?.Text.ToString(), + UiCategory.DeepDungeons => ct.GetRow((uint) ContentType2.DeepDungeons)?.Name.ToString(), + UiCategory.AdventuringForays => addon.GetRow(2_307)?.Text.ToString(), _ => null, }; } internal static bool ListingMatches(this UiCategory category, DataManager data, PartyFinderListing listing) { - var cr = data.GetExcelSheet(); + var cr = data.GetExcelSheet()!; - var isDuty = listing.Category == Category.Duty; + var isDuty = listing.Category == DutyCategory.Duty; var isNormal = listing.DutyType == DutyType.Normal; var isOther = listing.DutyType == DutyType.Other; var isNormalDuty = isNormal && isDuty; return category switch { UiCategory.None => isOther && isDuty && listing.RawDuty == 0, - UiCategory.DutyRoulette => listing.DutyType == DutyType.Roulette && isDuty && !cr.GetRow(listing.RawDuty).Unknown10, + UiCategory.DutyRoulette => listing.DutyType == DutyType.Roulette && isDuty && (!cr.GetRow(listing.RawDuty)?.Unknown10 ?? false), UiCategory.Dungeons => isNormalDuty && listing.Duty.Value.ContentType.Row == (uint) ContentType2.Dungeons, UiCategory.Guildhests => isNormalDuty && listing.Duty.Value.ContentType.Row == (uint) ContentType2.Guildhests, UiCategory.Trials => isNormalDuty && !listing.Duty.Value.HighEndDuty && listing.Duty.Value.ContentType.Row == (uint) ContentType2.Trials, UiCategory.Raids => isNormalDuty && !listing.Duty.Value.HighEndDuty && listing.Duty.Value.ContentType.Row == (uint) ContentType2.Raids, UiCategory.HighEndDuty => isNormalDuty && listing.Duty.Value.HighEndDuty, - UiCategory.Pvp => listing.DutyType == DutyType.Roulette && isDuty && cr.GetRow(listing.RawDuty).Unknown10 + UiCategory.Pvp => listing.DutyType == DutyType.Roulette && isDuty && (cr.GetRow(listing.RawDuty)?.Unknown10 ?? false) || isNormalDuty && listing.Duty.Value.ContentType.Row == (uint) ContentType2.Pvp, - UiCategory.QuestBattles => isOther && listing.Category == Category.QuestBattles, - UiCategory.Fates => isOther && listing.Category == Category.Fates, - UiCategory.TreasureHunt => isOther && listing.Category == Category.TreasureHunt, - UiCategory.TheHunt => isOther && listing.Category == Category.TheHunt, - UiCategory.GatheringForays => isNormal && listing.Category == Category.GatheringForays, - UiCategory.DeepDungeons => isOther && listing.Category == Category.DeepDungeons, - UiCategory.AdventuringForays => isNormal && listing.Category == Category.AdventuringForays, + UiCategory.QuestBattles => isOther && listing.Category == DutyCategory.QuestBattles, + UiCategory.Fates => isOther && listing.Category == DutyCategory.Fates, + UiCategory.TreasureHunt => isOther && listing.Category == DutyCategory.TreasureHunt, + UiCategory.TheHunt => isOther && listing.Category == DutyCategory.TheHunt, + UiCategory.GatheringForays => isNormal && listing.Category == DutyCategory.GatheringForays, + UiCategory.DeepDungeons => isOther && listing.Category == DutyCategory.DeepDungeons, + UiCategory.AdventuringForays => isNormal && listing.Category == DutyCategory.AdventuringForays, _ => false, }; } diff --git a/BetterPartyFinder/Util.cs b/BetterPartyFinder/Util.cs index 71948de..031c30e 100755 --- a/BetterPartyFinder/Util.cs +++ b/BetterPartyFinder/Util.cs @@ -2,7 +2,7 @@ using System.Globalization; using System.Linq; using Dalamud.Data; -using Dalamud.Game.ClientState.Actors.Types; +using Dalamud.Game.ClientState.Objects.SubKinds; using Lumina.Excel.GeneratedSheets; namespace BetterPartyFinder { @@ -14,8 +14,8 @@ namespace BetterPartyFinder { return; } - var max = data.GetExcelSheet() - .Where(item => item.EquipSlotCategory.Value.Body != 0) + var max = data.GetExcelSheet()! + .Where(item => item.EquipSlotCategory.Value!.Body != 0) .Select(item => item.LevelItem.Value?.RowId) .Where(level => level != null) .Cast() @@ -30,7 +30,7 @@ namespace BetterPartyFinder { internal static IEnumerable WorldsOnDataCentre(DataManager data, PlayerCharacter character) { var dcRow = character.HomeWorld.GameData.DataCenter.Row; - return data.GetExcelSheet() + return data.GetExcelSheet()! .Where(world => world.IsPublic && world.DataCenter.Row == dcRow); } } diff --git a/icon.png b/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..afe44a5186980e73acddabef56b0c7a649d0b2f7 GIT binary patch literal 13610 zcmeHtXIPWL6Xz!Mrda48hzLqo2)zkPk*ZRI^d>=?^d1Y?06_$luJjraLx&(plPVC9 z-n(=No!pE6|8sX=@3{~6<=*FkWZ&$}?#}Ga{AOlf>*=V`QL|D50O&N-Z{G(14t<3K zN^ygOm7hItYrCz&+JFG2^sB5iUzenAN;>8&G`s)*{NG4GzOcXSk!cz3qbABkHmFss ze_LeSfYF3WjUAxRd_{GCTK%LW)M%HEY?5oJXfAt$^Aegz?W|SQn#kDy%wtYE2f#NL zz#Z3fvx`3%Ul!Lf^%sC{G1x@0T?{gKarPHxMzFD2?5@Q|M2~A0at5!xG;GJHcnND} zW4{49xqz)AkSi^>Uqp@@S5i0#L_X`^cbwTMpIJ>UZye?6NWU#FVDB~&QOUlnWL_$sVt3Z7@(4n~VOG;mS z&iM70nsUir(c!z)GEg{;>)n9IVOzc#?nk>9@)6-=2X z1YnQ|c3@nBl#!@3#tHFGF6c$nQ=lh8dsh}7elO7?-4 zh>?;o^UTuO5VPJ2)#H68Y#__RQiowCh@Rxea`SPzpR1M(5 z8W+v7e<^w`lIvXd{MI+E=0sbi0*d+{-%ci2JMC&`lcXf{{9!)4pvIKMQby(X@Qk2K z^Nsut^KZ#c7vJfT`F}2Uc^e~aQ)E*4Q!?q;y6Bd4BjRnmOGS<)qTt5HeXDQd&Yt%| z2TfD-L|GemwNAEvEJWXHUW#{r9PcTqo!7nFKDB|8b-mxb9?gh(`_1)3r;mdaiH1d2 zXWG_>h3UDZ(e>Tip{7(~O92PMSjAMMwufDUL9%A> z{03iYxoXFvQz1C>c6k^1v9M`sA_!vNUhpPXU%DMxwM{lUp{gepp%FD3)L4 za~DJ@aj%%L1`P25NbQC4+RTj@r^me<8m`Ud_X5pDX{-;a^W6Ou?}CK_Fp=2lltFQq ztqgZ928ERc;h!2w5|Z2B0%)(K?A^W*(CRHihkuW1*)hIlCv}MOzN6*;iCOYxiY8bH z2VC;!o?YTt8GqT+(*D^2dj+%v1bZp&p=jwipuGv3a3>pZWF7W!dM^h1#iODvzVKdo z@@&tqLF}7U1IgyIk)Jw;>q|-aNQv6iiVYQN9#QEX7G6?L*&=@Au(TRjT^GaIvtx~t zrA64DfLqAz!!VD$N`*)oh30Fu53;_W!R&IX=M#Ae)ArZHduf47$PFfM$M8cuISee! z0(u3r!gqh-5cd(OBNBq6>BUXKnWljyo?2HT2pM=^-z|$gvyaG5JYB!#!%K|(rov}Y zq>&7!`=jLXQ+%Z(MPDtBY!z{a(^BD0C1#x?su-F9R{vrdVPd~?1%|Li-gS+W7lEu? z{@4lqwg`pYe557?dxJG3zEG;Sl;Ew?6PN+q5rhU(=29c_Z+OM5o`X}5U zc29J0+4DFec*kch+Min|55*Xsqtog3|1GB_71Wk!znM{^S_h{d> zWQ@s!??s=CZfldGiQ*4E?f^gu9xxo;+;}&~CAypu;{F9epXHI&{~&(yA>!M7%Nh4V zql1%Wud4!M_@(@_aHZ+`bC=SqT**hbQN7l3ylRepuds5L!gRnRHxvHnrmdFxw+d4# zeageOo=~-a)4e(L*>f^B`fNkA*uZ&!rlkPa%QFt&%W;I39GiVUVgFtI`A8FK&gaK& z`}1bUZWS^9_92Ovirh8k?W=ii5^vP#K~Fk$ei^FNXD`*ZpR=0(P2jBP&|15TE4UV+ zp7`#D%_qEm%QyZicS&*?1jtb?|Ls#~<#h7e&18HeBGWgyT1>YZ+Jf?6dfN`>e)FVkYLQ|KsmXpRbv9#{v5757&dMqf5_#N)eDT*3sI*a8_rU zx%@bj1W`KA`6ONIbKPqcU2So!!UQQgjC$;Xc*>Ehy1z2t;%fG9UytfRd*K5iQIWv59Bvn&O81C6qVB#Nr|cas z$p@Dw{ozVI!w&+zSk2Zw@oJgjM=441Zr~3W3AlQL69yfBRhpw7*D1j?Px4Ef9m3fnW*N-neH$2r!Uk~OkRH~=?pV~aTU(m z=iYSn9&czeVB^jkR{V4lQM*STA#vW72k>|v(aq0la1i?=?=7a^8%ChU93V@ztqh{z z2c?j8jh}PbZoDF6^=nc{BXxCQx2y9tDf&feqnpmVrPtBkJ2=8lf&lJ409<3ByIx6Y z^FwV`$bQkc7T@_zol(SH3?;eAjid`1oLH@o=OYo_?Z&V&NwJ$2qUk2L)3X_RjpPt3 z9_QQD=s5yAq3M(g(Qub$jzq4Zr0CrBJAVEq1%TrMU=8@?ZeHY4H|}aV6`M8QilRMv z5G-DvQPGm~=r`g*BMlXxa@O27rp2M!yguJB4?Q?Z(t@0DENlWNIcfQ4BA?ZtBTC@X z*hEBaUm-@pmfZ`kbYv^ak$m}+Y*~D`2R{rb8Bw5{^W^E~YbJASWTuNnemj$KoY~%V zV;Wxw(E_hhY2A7|oUP^3x5?lDtwoCVr@U5c5+Drgxb9&W{M4oPcNK(U;7aF*Z*1oY zyIu@ngm~J+(NUk8zPq3eH_DKo>yZ*~ffDeY^7<@!2<-?+IaeWXcQN9J$unW3%qZN< zX`zu6!0Rr&(-k#9z|g0*Tjnb0qj8>Rb4QSWXbqwSq-YIzojcSE;X=QMTGwAJZYB4|S+p)#C}sueo_gCpKlU&DTjLJAz&E~$Ptx~u~Ocj1t4R60O_-A5w@aZZe3u`SBkIPvTOI2xLxmo;D( zOD?-w!(mWTS%VzN$|=e!)Kbwt9skiYSD6M@v(Kw|LE4bQ4c3VdDfuwbyO@QaAT4;# z>^et!i0Y;EhZ~mvM!0+Gl<~Qx0~~aE$AA4;4_$W6>68}JbZ9}5fs90ElwVEL?qoi+ z%1t!kt2E4;{bCP z0-^rmK*|T@>{L|kL!z4!(zDl|+2h1@0aoWGJdgvEl37`KfB7-O92UqM;Y=0qs+`qX zy9h!AT!80T-~QrwuJk4U++x129*6BL7H@@*p<9Jwsn1-c`ARQ?!29X+2lpMwt~$g} zCt`U15!nVFbep5tRClPCdYD)%_wg1a1-*NZyRiIKUWfaJWGkOo5r8SfqADFhPOR@M zl6G3iOnAEGvM2*VQYG+e@Dw398XZ%R`FZVz(cEdMxQlUqx>bd=a7M~@#V1ktAzf;D z8>K5Qs8wzW%?q&_tTyEK$9?RyKny2696GTy3M`XxRf8b4EjQS!*wm`nc%&QcBt8Df zUfyc$8J)ivos6Q3S^)agI^a)32io~_Ug^Z(UfqD>*#W1&^G&Kl_kMa6Asqa*3g3R| ztOkjpF=E3T$7U4+L0$UGxG%xSOp2P&`NzZ3#>N#NZQx3uR&j}Kc9)+bV-F+xn!LPUC!>{BjkAmBc?XP0xoetvHkgnxO zRzGWS8Hy|>fZm$5!KhhN;;u(yLr}?{)qtSc7wOin{*>xk7EPG#hqJx@GAFK-zCRI3 zZK?%t&N)1thJ2~>i7k^4SdW+*KovmeFVaQo%i@v7>n>;A#w`@qQc^B-5 z20L!!84m9lvSY)zz@)Vq{5)hC*(7p~4?gi5k6(#kWUJZ#Rla%idqKEl+CKG{OpsRU zHBL@g3C1SPv1#tUx|Wd@LHEI&OOZ_?C5CUOgiV%!A7v|7n8`h=&dGMZF1Nfw^M=z% zk@)g!VKW0AQ=R=rC2!H&mzf>v-EnC@Os>vq;KoG1Z96AdP(jl>PYXCIj&dX*jO%{s z-9bmKryb4k_j_Tl4cd;lw2U*~wbGaND4QPpz2~RbF$hsqWAG%~Are0$uDZ9wxM*c0 zC5v6p&z#Ul#uA_VTkQ8J@9g4U=+>ArvN}jsnib7H99{Qj>7>STl;!Mf!wXQwL)>uXwl7kUN4A|;_aclSLS4C8pjMr^FK20?Ou+wH}4QX2WDcQ ze^*rBV>bUi<71Z{r>s;@^N&=icyJdN(NFrKq1WY?HK4R0dpPC@Errj)8)_B39n>t1 zL6ZM7d;Xg1!b2>cDevbtqCai)`5W2a059-EW!^{-CBcDwunF0T$BZcoe=H^O7vV}C z9#`p4?QyoS&F1ZEg=NED;m^oTcHH6pW&6fQCY9s|C2{idML~%zu|8+mn!U6D{rT;B z@ADii;KlYy)Q!tS4>frrSVKOSPPf=M+<-xL%Cj>je6iqtljThz=ONvr2MRlMAIKX@ zVaq+TmooD_Y1pACB=OI3r1aZVc5$t6>51>>L45-7qua{Ot*ph00XdYM(U^t*Q}JFm z%-|i4_<&4~@b;MBO9aTP!Ig&IjtH7n6sxz*TgJ)kcnSo4k3o$vF)c1J(KcC2g=j8h z5qA#))~6I4SK~e7-wI5qzk2IL-e`IAT3dy>LxxhJkw)Hx)=Xs-D~zScq)gUfTK7Ow zQ{=o)m>B)M_XU8ybTuNu_I6bU6N{-id06!ClZ?Ca0?oSil=@Z^#s*43l7XowO3x(; zh+C2}JBK$TmDqJX3-jKISjY%3e6yF?z4QH7NR9&1YGeNXgU`=K4x1yUw%${^Z!W2D zWc%QoYtNEL`b$|Sq2D8eg-Gsblz)h(>t)R|YT}E!Ra74q=OaE+^204~(Vc5pc$)Gj zBB$FBg-p4)a~!*Wv)mMpHl+K}{TAOlk(fcmRldgQ9{5u{7|RP8P-7Qd?QL%lUEwAf zRQIJJKX?D_iwac=`MWd5Ytr9TM3>TJ#_sF2dV5W|)0sOkYwoUl%*N-W?am!|-IFN| zHv14sgNaeG70u+K2>}0ZA6RdRGgj;8_wbxdTR(#J&b$z+;`&~oWXS8qSw&aB(DWxK zN1uYQLlW$M01UB#;LmZ$Uu`Ic^Dg>NCv4^xim>+Ng14f>OGpw6 znLy8?iB2Sh+2Pe`6ABaCR2xckCj?IuMKuiInd-AR*VLb%xS?jx8BiM(gsT$G#_kc= z8?Y8B9$OTN<|k6vBqu)td`BA3s9fjY(8c+4@U;7VS`g-H!O&cJpvKf*l;|lk;ZDKKHlFy%aT&_rI(ksbsotLDC&w2Di78C7q76k1W!BO%dzuAS>&$ZQfOLnrKcAbTB{fXQl z>xzXd@sS$4Od9`teVV=+kE=h6U?b_AZa-Zm3l)&=3eL2bMcWk@l^YJ*hbg-1n&4)K z){Ih#ul;=*>mTmyx+WF{(!;T#g4o} zL)Rw2j=*!H8@Fgho0G5&Q9AI6jf9u~$qa9;nRR)626J$O=(7?!Pgxn)1Jfp3`H zE1Z`*ayl&MxZ8}U+qqU-Osyy=u5+!`(s1QSfq<#7H%eYfs;v*^_YnbuRA6UiKUU|n z?8M-6q-3Al`sId_kSyv2gXs&Wrl%R`_ixw!Udhg>i8 z!yMhtrUa9!r0MDrlaD`$VrB3uP{;`;L}pPxlq5#zoB=<)_xCZ2+czDDoj7t~9xJoI z4-KWFWtq@_@nDbe2`T9-LR=E20zU+3+60m=JJZPsgB<7kMB*OfJZd2lh{|+*7@%cE zNLirTeD0ZR-*FwzlUEBaCGimkpJ+gwQ-~p^u6Vrr4vgy|^}hYEFwZ$a>-wYi79xb5 z%!P>*?KT{ow{dtyBPqMZ311eHzF{VpTE&QdOb)tE@;d2i$2&Ox1i`p`pH*dHya_Qw ztN``46meW{QaLSb%MZ%;Rd!Gxsns&~<@JYUHNz?+Q9ReN%p+J}dlQffEn=yY$V8TY zL77rP#oi(y+K{YBzis>~B(;(oyuir4VNET*Ho9gt2~T#csIY;xxPb2bFNL$bf0J#_ zfX!`#xYpNwz2nkLyE-?H>I3kpFLQ|m*2ROY6Czt?qJ`)gH2?au6tWcgvD+}Lj;ex| zEKvWPGuUyn3YI4Fh<5LK^77}9*yAJdqW#gPBL4*BctVSZlnX9W_H>Epp+ViAzj^bE z!7~?O6PK|?<0io;Z+o(XN*&hzj7SWWMh{UkPzxKT?xVe7vx77}wBT0x>muvOtDvQv zv?n#V?M@|)Dz+~5?{0!g7U~*b&5bCsJa!1@=JOxrYbHRON*hrCP%N;IWrXBKWy6~Mct!V zBRb%@6|XKE_Vvfmf-TJQjZ>kGDqMV|-cE)@4#7doc6ertVbq(4cy84Y+++j#w>*mG z9)i>XVfLDOmLGbx_k)ln*_rp=em)+$oV99ygA7e!U_}htF?rdNCSAE4ckk>h!|P6C zisXx+uafXvR;`t>dg&lAw#mC7Zh&;8@KlquMV0=1qq~dRXPpw`hQ2t{b)6(OIiVX@ z&H$7wQAi%1fs_k%Dome)>opT~S1x(!@|M0ib2-%2JsTc12)HDrJCySU!fwSredDQ` zocLJqSR9Tnkj?4b{X6=gx?hGLd<;GrMbhcWAI54FjIT4Te!kJSsk&J4Bbi!>mjpe@ z7}@QdmK98x5tb>{yt3=~_y;VP0*u#oFsBl$CD`9ZQiBpFf_Gw>&BrOKqajD zh_8oxHQr>7alnMHn%$E+WzbFroJN9k4~}jeqn3}Zn|VDRJyH{oT*Aq4EUQq7S0@^mZz;b7v>X^NQBAG8saCN&2z8XtN+deA=ZB!2$*n$I=xi4yemJWQEN{OGs(BhqzW@pY1g)WdYm z=?YRbfgG?feZl`GV>`H1@GTYdd(O_8o$fY_YX@-Y?n{T9Xzf(Fo?(+6y-53X*w+Z* zmEO)d(Auo$(npd%*U=I&DDokA^!~c3%SljNJWkyvtnMFEv}G_DF#qB%G1jN2wrJg!UFpWM$dF z7?9Wu$Tz!ft$o<$uux0=@%hHcu4} zx)WhsBTeH@NoB){`;QIEnZ(J%4tRX7f|~QdkS%y}dGm}yZctKfe{fiTh5#u#WvzY4!6$ALn*{R}KlZ+^ONARIxJeh%v}q`B+6!yesWe?g z%nb!@*#1U!`&t5RQX)n-t=`BeA)urgR!1za`yo~)ISHcm9U zh%ahNJXhW(Ek5Z$R6RNE{U{p3H+=~XmXA-`-j6ZDl^S4{Dtu;|n@Dx%-WN?T4Cb|J zaZu>*$yW2BiXOnB;J|t36ID9Au3o$pnui<6(TJ~zJP0lib#;8GYjbLt-W@jjtMnam zQ(R;%v1l=)ggk-)_RetLN|4lMn7pAc+rMg24~Yr_6($>^yOFZ zk*84FaH`5gt(K^}oGbuK+W?{-GeZtu6FxKXtaNvYd*$OBE6a$^)^q>5T3haI?eQvELh3H(k*p%RzsxCh!B5k0n0Dhrm@+2 zaHZiQY}FMf2W$3M*Cq1O&b)jUnb5j6H4*3=>LEUNeoxMa6Of|IAq*2DiU7%MAR|Ez zCHwqyyzEB66r)16yYr}`6iU(bcu}?E%GZHk%PFm+6adCm1aA_mR?g1zZqo>$1dCBD zTvaC5*MsD*kXb*~k@WHq-O7woi`SFkbl3%wdL87a}$&$;T&cYBQ zYQ=EJxb~p(EhrBCiCiFGLk&1yycvJ>U`Eku*R=N7w5C4o3_+ z8|ctN6h|A!Oj7B?p7La=ZMAVl6M+QoJ-oRP=VSngUf&n-}SyDaw!Kapwn&uh)L z{mQ>8166;hTK2abPz*OGCxjprjD=gByu~{lSMO)nk5Pfi9q+P!_l=b`#4*28=*x_- zixT-pE|?-VTq{^7Ta|+7H54)(ze|h6+buXkj8Kh}v8H9^a$(+owC;~r*JLCK8bqZ*mf(W) z`QaH&1am#UD@2q1H<@2k1RR^v+lV+MXu%v4cIB7^N%eMEaY_@ly<@McMS;Jeb z%_zE55Dqg7Lo0=K4Vu@UeI0Ekc}H_rgB<K=Pn2yy^Wz;R|efVRLrS3+~lB+BsnPb=7l1T)tit;R>lX??Jg0RX%dFD zMM6gU$$c(^g+Y%Q2U*fl60-n|){g@rUcSYydgxZK5@Z@O_sMa|6 zu8U<3K1pjQO<9T^HVr;eS!z1k&7x)+uQj4}PBt8U7s(`ww~iE&N}J7oBlt3z1^Zs8AM@r7qrGngv>cB%F&m+$9SGyo-vgvm}$ zz0eM-y!CSNq-wPx>JORj@!MRYfrpYyR3Ry|44tCQ)ULxv#|rl;ozl13Ppzx|{J`Nc zlY(>Q{yX);w{nxFtIwErpulP1dzq#xi~jIdSBvFM38G!n9RO}BX9r}{n*WT311^;% zXGQLO;&}6ELm(8_evs44``Le%z%B$`Avq7k!@%myB_;sUZuJZ+Gpw^+$vO(UO`baS z2nM2JvqL^bi~9HD{MfHUY^@KFhb*Q#($%p`L?gHF0JKmxl;F~S@2#DQya+|6mt*!H z=~x8HsnI~UDx#jre_I+a34#C?h$;kb*{ahx59#|*_W5>a2WP>?Hj#1*ibSIdvxydu zNYQXLYKZ7-V1Qv?o#M{xkWI|o=?u<=nU-r!FA`H~kkMm_)y=l`zf3BD33d_y zL;s^Xtu}>{K$C~cPyZqLI%q9q$$t7htr?DkZHymQ%)1s1H&9_y6gDXsB_@7kc@>ni&q?Q3)=1xxRenY+r+|E%U*oWy5fhw(-$7 zUIj+Q=Gb2>Be}zbHVcX6#p&6R6BOrIc3fAb-IgYb6UQC0o8Pa2!i^ye5oe8ytq&fQ zoCg_Ii+{daMZ_LjU^=HM`uvWQEQ)Kd&G%sv9LVO&tdr6rf3Q{e)TJHlJMWQ0(-QY+ zFuNI0m2(4lQZsX0m>uc7x7@SOF5Q{8F581)Hp-n=_#!PcrHUQNrH&RGojtCI47zU= ziqz{iXg4*uwfkzUwzZ38H^*i(2=kbocm8`K9KGDmOi4}1+xNh7II*(tJW&?H>EjNm zyn=SJu^s2HA(}cLT~BIRYu-O5fvST?59|j6eP(J6I<~B@kMPgoxcjjRQ(3ZR^7}y# zjz@gos$6x|YYX)2`TOx14pn>4xtAOJ_>I-I1*)B&ftmc>yUTq*&s1){^}g)1QTwnp zJKQvK6q6xwZDY&e#@ef)6s$m&qRQc=2gTko375P$j+Bm9SD@Qptqyr^wo>2T@$&@h z!Vx|WkPlb{^dszTb5~s&k96BTc;#|Z5)_B#o`rB?kEDyGFZ1`i;x}Y(XQydH_eCBQ z4vkOa3AB82oK4)=agn7Nz=e?=v6|-KFF59=&7@mNC$(Pf1Td}`SXyb0tEtxoV!L5V zn6>H18+Vf-6%J_k(hObC3DR+VmZJ@Yiu~d2_m?ZA8#Rji5ACM=c!sE;w6JYJpLdPg zaeI#;;oWI<^d<{16j}=jHfBh;Yc*-uuGuRyw`p!vxRn)|c(3nBkHHVHyu+1jHZ9JKXdTpm!3sD3oCoQ|wf?L01S2_qKgIdidxiVR?J4$Xa~ zpJNw`G4I4k#aY3#uzWiY7cqllQfT*flfdeHc^;GEi=It==3yg`lzHAo$e*i8Hs0f7 z0QJh4LF6Z5Ux1(bS&A*;o%uKZRS~?q7*sHZ17=x}6kru}I4y5+$|x$66>S(tVeKfO zCxOwNl4|R9pjpGs=DN4!8%y3DQ1gRsXdGWL;9~5EpbP_|?_odH(S zBHr*wz-V=+y)dZgV!@KbSWzdw48Qh)4XK8gQ|JI(%Q2Rpej;>F4=)tkG(jf`iyRXX zs*%JD#o{z?8HNm?lD<&C9+xyeu68UCUw`%|^@TuFXvB(*9@{rP%YQv4bYPy3X{x!M{-p%Y>|($hN-GF^@m5S5X5)<7)Ierl`k9357X*j$OLDK!;v47khpB_XFI)_`ryx>b`4> zkx5isp&+gHg9YVFac!^Yr{*_V!JD2S!J^<1D=WDSli~FxU&5e_iigEOWkXY2Ol4Rv zE0rj7ht7N}t}n#${Gkb_RrgVTlMj^fBS%x6Ji1c% z3)uo9)EfQs?cZ*cAkB=rDYaZ5AMR`>aJ4!~ML@U9Ji}2R$~2Ufzl(7Gqq9QuTj`Y!kVZWc|LA)yQ7d8Gl;IkY`J9vy4?Fl}W3UBQ(@w|jz6`VMTTCtV zc$jbCamYx(M=BKcPi<2-ydY6(-1G0t34TDg8l%@Ot9#|c-#C3RVWxQMO+1Z3)HAaZ zobQ8B717nI{+t(0R3=p5p4f2X@2oGck*^yNog0_6S6$nBVI*9T9l3u7OqOchW_-oq zWau%VDY~vZQ#C1``YMJNOx7vh8|Zd-3~|Ml=36Hz1JLacU79W^X3Vbr&;i9w8w$n! zYNYKo85LdqEF|Lx4-y94-ouns9s0_{2pToymV*V^PDw94AajE}1HwuFXv4Vjo_;i8 z!x)P=FO{^-d9gNckF=Ab7g&fA-+NNNWUjdcss70AY&b2w{yRDK#r-i<0I5Pms@d}b z{dNo0h(JCu=2DyJG4^xA_D~s&9{A^x*Hzwl4fG&G+WRR%@-+*^Ff)kr_GbHZJw6q> z)dO`A0D|gJ(Vdu7oZX3iPq-~iMS(u7k^)Bwr#PV7Gy<&LhNGL%Qa3Nv_Fc5S416{1 z*ZwqyLM880ZO}geISCkix#6nrp&Vr8Yzs)?;IzUAu~q=S4NZySm2ZpMp+d^C6J&5s zHDN0U${U(w#fxI?AO+ksJ`b^hxdiGqx_cBkUlgoW) zN5)A1!2_jb49J^fLlnhs6p%s|xytG9nsNUs1TE12*vp;xo?)Z7Dvx*LBSXJ_;^Gjo zK_LFS8v+dtiJmjrDHC!w!-Aw}WT7kDPSshjp>m4<@*YBffQ5$w(8+6229$~c*Pf)l zj0mFo2XoNvDAevb^{=+0_t^T9o?|A+$0Q|gJ(cUVQ(9K42R#$aEY z`TNYo6Zk2@fpPW1Ag8JwjR1CA@7w#p!b6p%kpWbYE;slmtZ!`K*BsIDp@_<-T$r0Z z?LwfU?MK`H(n85-)TKg?nsB3zdHc)YMJi6qhH?f9kszUwLeoMUbWV6CFEmY zzCzn4<1)bi(UcA_ESn`9R*wnIpK%#L;2?aa;oK02vp#uox}%`*906WnLCC;kn)Sjz zsRz$5`xm}jr0r!h0#Po(=8b!aa|f5SWJz0YP+@kX-3(R#yM}164qK_0Y(i~pyx)*X z_BnB=9RUFn@1I+a+j%o%l@U6!cRyaM#|=Lkz%t0=c)wxc&2Hf6TeU+~?T^*>Gf z+$)elbH@+E; zIXzrF4B%&8PaW-!pVJh7P7HDiDmhTTbqx8hjCi1w zT5aCGD#lu_C#(LLx9qX7hAbc*FltQ5Bn&UM)IrYrA8QWq%*G6>^QPpNxy>LC$$2~~ zsQ3LPDmVVqPy|2YME@U6r5%ze8DDU#7WdVxMT;3e9_y-2hAF9=zVbIEpC0Ep51KNH zul`rEcLBZ4?8!iW7pHd4M96I+rXmPhJTAsL(}8zpt|*S4It+eyx8o99ne`Z#4g9;z z0r!853nl0XMfcQs^vMKY6Psx?9t}x87-4#B7b43ILI!c6<_lq*D*9hO8On| z)Z<&3F`RJFP1ARw0cQ2|#_ao}&4;)iAa#H}70g{Iu- z`S0SQAARXrBnYu5YgV`Lw#&ZDUveD1@m)z5usEl<-L(Y54&O1sU@|ZVNhCvEZ`yVV zG3Myaw{?ND>?(eqIKs9pJGB1gs0e_AEFRsrx16;Gt8WHYCRHm23_fHDCm12R9_W5| zjk@LY&6_=AMOn1}R%5vy>r(D)shdQ@lf9*khim5_2^oeSMWFU;Wq4C#U}*iPiplv@ zpc|X&ln# + + + + + + + + + + + + Better + Party + Finder + + +