Initial Commit
This commit is contained in:
commit
432eeafd3f
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/.idea
|
||||
*.user
|
16
Gearsetter.sln
Normal file
16
Gearsetter.sln
Normal file
@ -0,0 +1,16 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gearsetter", "Gearsetter\Gearsetter.csproj", "{3E87693D-1FEE-486D-80E9-C6D95E68160F}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{3E87693D-1FEE-486D-80E9-C6D95E68160F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3E87693D-1FEE-486D-80E9-C6D95E68160F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3E87693D-1FEE-486D-80E9-C6D95E68160F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3E87693D-1FEE-486D-80E9-C6D95E68160F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
3
Gearsetter/.gitignore
vendored
Normal file
3
Gearsetter/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/dist
|
||||
/obj
|
||||
/bin
|
147
Gearsetter/CachedItem.cs
Normal file
147
Gearsetter/CachedItem.cs
Normal file
@ -0,0 +1,147 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
|
||||
namespace Gearsetter;
|
||||
|
||||
internal sealed class CachedItem : IEquatable<CachedItem>
|
||||
{
|
||||
public required Item Item { get; init; }
|
||||
public required uint ItemId { get; init; }
|
||||
public required bool Hq { get; init; }
|
||||
public required string Name { get; init; }
|
||||
public required byte Level { get; init; }
|
||||
public required uint ItemLevel { get; init; }
|
||||
public required byte Rarity { get; init; }
|
||||
public required uint EquipSlotCategory { get; init; }
|
||||
public required IReadOnlyList<ClassJob> ClassJobs { get; set; }
|
||||
|
||||
public int CalculateScore(ClassJob classJob, short level)
|
||||
{
|
||||
var stats = new Stats(Item, Hq);
|
||||
|
||||
int score = 0;
|
||||
if (classJob is >= ClassJob.Miner and <= ClassJob.Fisher)
|
||||
{
|
||||
score += stats.Get(BaseParam.Gathering) + stats.Get(BaseParam.Perception) + stats.Get(BaseParam.GP);
|
||||
}
|
||||
else if (classJob is >= ClassJob.Carpenter and <= ClassJob.Culinarian)
|
||||
{
|
||||
score += stats.Get(BaseParam.Craftsmanship) + stats.Get(BaseParam.Control) + stats.Get(BaseParam.CP);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ItemId == 33648 && level < 80)
|
||||
return int.MaxValue - 1;
|
||||
else if (ItemId == 24589 && level < 70)
|
||||
return int.MaxValue - 2;
|
||||
else if (ItemId == 16039 && level < 50)
|
||||
return int.MaxValue - 3;
|
||||
|
||||
if (classJob is ClassJob.Conjurer or ClassJob.WhiteMage or ClassJob.Scholar or ClassJob.Astrologian
|
||||
or ClassJob.Sage or ClassJob.Thaumaturge or ClassJob.BlackMage or ClassJob.Arcanist or ClassJob.Summoner
|
||||
or ClassJob.RedMage or ClassJob.BlueMage)
|
||||
{
|
||||
score += 1_000_000 * (Item.DamageMag + stats.Get(BaseParam.DamageMag));
|
||||
}
|
||||
else
|
||||
score += 1_000_000 * (Item.DamagePhys + stats.Get(BaseParam.DamagePhys));
|
||||
|
||||
score += Item.DefensePhys + stats.Get(BaseParam.DefensePhys);
|
||||
score += Item.DefenseMag + stats.Get(BaseParam.DefenseMag);
|
||||
|
||||
score += 100 * (stats.Get(BaseParam.Strength) + stats.Get(BaseParam.Dexterity) +
|
||||
stats.Get(BaseParam.Intelligence) + stats.Get(BaseParam.Mind));
|
||||
|
||||
score += Rarity;
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
public bool Equals(CachedItem? other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return ItemId == other.ItemId && Hq == other.Hq;
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return ReferenceEquals(this, obj) || obj is CachedItem other && Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(ItemId, Hq);
|
||||
}
|
||||
|
||||
public static bool operator ==(CachedItem? left, CachedItem? right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(CachedItem? left, CachedItem? right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
|
||||
public enum BaseParam : byte
|
||||
{
|
||||
Strength = 1,
|
||||
Dexterity = 2,
|
||||
Vitality = 3,
|
||||
Intelligence = 4,
|
||||
Mind = 5,
|
||||
Piety = 6,
|
||||
|
||||
GP = 10,
|
||||
CP = 11,
|
||||
|
||||
DamagePhys = 12,
|
||||
DamageMag = 13,
|
||||
|
||||
DefensePhys = 21,
|
||||
DefenseMag = 24,
|
||||
|
||||
Tenacity = 19,
|
||||
Crit = 27,
|
||||
DirectHit = 22,
|
||||
Determination = 44,
|
||||
SpellSpeed = 24,
|
||||
|
||||
Craftsmanship = 70,
|
||||
Control = 71,
|
||||
Gathering = 72,
|
||||
Perception = 73,
|
||||
}
|
||||
|
||||
private sealed class Stats
|
||||
{
|
||||
private readonly Dictionary<BaseParam, short> _values;
|
||||
|
||||
public Stats(Item item, bool hq)
|
||||
{
|
||||
_values = item.UnkData59.Where(x => x.BaseParam > 0)
|
||||
.ToDictionary(x => (BaseParam)x.BaseParam, x => x.BaseParamValue);
|
||||
if (hq)
|
||||
{
|
||||
foreach (var hqstat in item.UnkData73.Select(x =>
|
||||
((BaseParam)x.BaseParamSpecial, x.BaseParamValueSpecial)))
|
||||
{
|
||||
if (_values.TryGetValue(hqstat.Item1, out var stat))
|
||||
_values[hqstat.Item1] = (short)(stat + hqstat.BaseParamValueSpecial);
|
||||
else
|
||||
_values[hqstat.Item1] = hqstat.BaseParamValueSpecial;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public short Get(BaseParam param)
|
||||
{
|
||||
_values.TryGetValue(param, out short v);
|
||||
return v;
|
||||
}
|
||||
}
|
||||
}
|
46
Gearsetter/ClassJob.cs
Normal file
46
Gearsetter/ClassJob.cs
Normal file
@ -0,0 +1,46 @@
|
||||
namespace Gearsetter;
|
||||
|
||||
internal enum ClassJob
|
||||
{
|
||||
Adventurer = 0,
|
||||
Gladiator = 1,
|
||||
Pugilist = 2,
|
||||
Marauder = 3,
|
||||
Lancer = 4,
|
||||
Archer = 5,
|
||||
Conjurer = 6,
|
||||
Thaumaturge = 7,
|
||||
Carpenter = 8,
|
||||
Blacksmith = 9,
|
||||
Armorer = 10,
|
||||
Goldsmith = 11,
|
||||
Leatherworker = 12,
|
||||
Weaver = 13,
|
||||
Alchemist = 14,
|
||||
Culinarian = 15,
|
||||
Miner = 16,
|
||||
Botanist = 17,
|
||||
Fisher = 18,
|
||||
Paladin = 19,
|
||||
Monk = 20,
|
||||
Warrior = 21,
|
||||
Dragoon = 22,
|
||||
Bard = 23,
|
||||
WhiteMage = 24,
|
||||
BlackMage = 25,
|
||||
Arcanist = 26,
|
||||
Summoner = 27,
|
||||
Scholar = 28,
|
||||
Rogue = 29,
|
||||
Ninja = 30,
|
||||
Machinist = 31,
|
||||
DarkKnight = 32,
|
||||
Astrologian = 33,
|
||||
Samurai = 34,
|
||||
RedMage = 35,
|
||||
BlueMage = 36,
|
||||
Gunbreaker = 37,
|
||||
Dancer = 38,
|
||||
Reaper = 39,
|
||||
Sage = 40,
|
||||
}
|
21
Gearsetter/DalamudPackager.targets
Normal file
21
Gearsetter/DalamudPackager.targets
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project>
|
||||
<Target Name="PackagePlugin" AfterTargets="Build" Condition="'$(Configuration)' == 'Debug'">
|
||||
<DalamudPackager
|
||||
ProjectDir="$(ProjectDir)"
|
||||
OutputPath="$(OutputPath)"
|
||||
AssemblyName="$(AssemblyName)"
|
||||
MakeZip="false"
|
||||
VersionComponents="2"/>
|
||||
</Target>
|
||||
|
||||
<Target Name="PackagePlugin" AfterTargets="Build" Condition="'$(Configuration)' == 'Release'">
|
||||
<DalamudPackager
|
||||
ProjectDir="$(ProjectDir)"
|
||||
OutputPath="$(OutputPath)"
|
||||
AssemblyName="$(AssemblyName)"
|
||||
MakeZip="true"
|
||||
VersionComponents="2"
|
||||
Exclude="Gearsetter.deps.json"/>
|
||||
</Target>
|
||||
</Project>
|
60
Gearsetter/Gearsetter.csproj
Normal file
60
Gearsetter/Gearsetter.csproj
Normal file
@ -0,0 +1,60 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<Version>0.1</Version>
|
||||
<LangVersion>11.0</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<OutputPath>dist</OutputPath>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DebugType>portable</DebugType>
|
||||
<PathMap Condition="$(SolutionDir) != ''">$(SolutionDir)=X:\</PathMap>
|
||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||
<DebugType>portable</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<DalamudLibPath>$(appdata)\XIVLauncher\addon\Hooks\dev\</DalamudLibPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))'">
|
||||
<DalamudLibPath>$(DALAMUD_HOME)/</DalamudLibPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DalamudPackager" Version="2.1.12"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<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="Lumina">
|
||||
<HintPath>$(DalamudLibPath)Lumina.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="Lumina.Excel">
|
||||
<HintPath>$(DalamudLibPath)Lumina.Excel.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json">
|
||||
<HintPath>$(DalamudLibPath)Newtonsoft.Json.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="FFXIVClientStructs">
|
||||
<HintPath>$(DalamudLibPath)FFXIVClientStructs.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="RenameLatestZip" AfterTargets="PackagePlugin" Condition="'$(Configuration)' == 'Release'">
|
||||
<Exec Command="rename $(OutDir)$(AssemblyName)\latest.zip $(AssemblyName)-$(Version).zip"/>
|
||||
</Target>
|
||||
</Project>
|
8
Gearsetter/Gearsetter.json
Normal file
8
Gearsetter/Gearsetter.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"Name": "Gearsetter",
|
||||
"Author": "Liza Carvelli",
|
||||
"Punchline": "Find gear upgrades",
|
||||
"Description": "",
|
||||
"RepoUrl": "https://git.carvel.li/liza/Gearsetter",
|
||||
"IconUrl": "https://plugins.carvel.li/icons/Gearsetter.png"
|
||||
}
|
309
Gearsetter/GearsetterPlugin.cs
Normal file
309
Gearsetter/GearsetterPlugin.cs
Normal file
@ -0,0 +1,309 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Dalamud.Game.Command;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
|
||||
namespace Gearsetter;
|
||||
|
||||
[SuppressMessage("ReSharper", "UnusedType.Global")]
|
||||
public class GearsetterPlugin : IDalamudPlugin
|
||||
{
|
||||
private static readonly InventoryType[] DefaultInventoryTypes =
|
||||
{
|
||||
InventoryType.Inventory1,
|
||||
InventoryType.Inventory2,
|
||||
InventoryType.Inventory3,
|
||||
InventoryType.Inventory4,
|
||||
InventoryType.ArmoryMainHand,
|
||||
InventoryType.ArmoryOffHand,
|
||||
InventoryType.ArmoryHead,
|
||||
InventoryType.ArmoryBody,
|
||||
InventoryType.ArmoryHands,
|
||||
InventoryType.ArmoryLegs,
|
||||
InventoryType.ArmoryFeets,
|
||||
InventoryType.ArmoryEar,
|
||||
InventoryType.ArmoryNeck,
|
||||
InventoryType.ArmoryWrist,
|
||||
InventoryType.ArmoryRings,
|
||||
InventoryType.EquippedItems,
|
||||
};
|
||||
|
||||
private readonly DalamudPluginInterface _pluginInterface;
|
||||
private readonly ICommandManager _commandManager;
|
||||
private readonly IChatGui _chatGui;
|
||||
private readonly IDataManager _dataManager;
|
||||
private readonly IPluginLog _pluginLog;
|
||||
private readonly IClientState _clientState;
|
||||
|
||||
private readonly IReadOnlyDictionary<byte, DalamudLinkPayload> _linkPayloads;
|
||||
private readonly Dictionary<uint, List<ClassJob>> _classJobCategories;
|
||||
private readonly Dictionary<byte, byte> _classJobToArrayIndex;
|
||||
private readonly Dictionary<uint, CachedItem> _cachedItems = new();
|
||||
|
||||
public GearsetterPlugin(DalamudPluginInterface pluginInterface, ICommandManager commandManager, IChatGui chatGui,
|
||||
IDataManager dataManager, IPluginLog pluginLog, IClientState clientState)
|
||||
{
|
||||
_pluginInterface = pluginInterface;
|
||||
_commandManager = commandManager;
|
||||
_chatGui = chatGui;
|
||||
_dataManager = dataManager;
|
||||
_pluginLog = pluginLog;
|
||||
_clientState = clientState;
|
||||
|
||||
_commandManager.AddHandler("/gup", new CommandInfo(ProcessCommand));
|
||||
_linkPayloads = Enumerable.Range(0, 100)
|
||||
.ToDictionary(x => (byte)x, x => _pluginInterface.AddChatLinkHandler((byte)x, ChangeGearset)).AsReadOnly();
|
||||
_clientState.TerritoryChanged += TerritoryChanged;
|
||||
|
||||
_classJobToArrayIndex = dataManager.GetExcelSheet<Lumina.Excel.GeneratedSheets.ClassJob>()!
|
||||
.Where(x => x.RowId > 0)
|
||||
.ToDictionary(x => (byte)x.RowId, x => (byte)x.ExpArrayIndex);
|
||||
_classJobCategories = _dataManager.GetExcelSheet<ClassJobCategory>()!
|
||||
.ToDictionary(x => x.RowId, x =>
|
||||
new Dictionary<ClassJob, bool>
|
||||
{
|
||||
{ ClassJob.Adventurer, x.ADV },
|
||||
{ ClassJob.Gladiator, x.GLA },
|
||||
{ ClassJob.Pugilist, x.PGL },
|
||||
{ ClassJob.Marauder, x.MRD },
|
||||
{ ClassJob.Lancer, x.LNC },
|
||||
{ ClassJob.Archer, x.ARC },
|
||||
{ ClassJob.Conjurer, x.CNJ },
|
||||
{ ClassJob.Thaumaturge, x.THM },
|
||||
{ ClassJob.Carpenter, x.CRP },
|
||||
{ ClassJob.Blacksmith, x.BSM },
|
||||
{ ClassJob.Armorer, x.ARM },
|
||||
{ ClassJob.Goldsmith, x.GSM },
|
||||
{ ClassJob.Leatherworker, x.LTW },
|
||||
{ ClassJob.Weaver, x.WVR },
|
||||
{ ClassJob.Alchemist, x.ALC },
|
||||
{ ClassJob.Culinarian, x.CUL },
|
||||
{ ClassJob.Miner, x.MIN },
|
||||
{ ClassJob.Botanist, x.BTN },
|
||||
{ ClassJob.Fisher, x.FSH },
|
||||
{ ClassJob.Paladin, x.PLD },
|
||||
{ ClassJob.Monk, x.MNK },
|
||||
{ ClassJob.Warrior, x.WAR },
|
||||
{ ClassJob.Dragoon, x.DRG },
|
||||
{ ClassJob.Bard, x.BRD },
|
||||
{ ClassJob.WhiteMage, x.WHM },
|
||||
{ ClassJob.BlackMage, x.BLM },
|
||||
{ ClassJob.Arcanist, x.ACN },
|
||||
{ ClassJob.Summoner, x.SMN },
|
||||
{ ClassJob.Scholar, x.SCH },
|
||||
{ ClassJob.Rogue, x.ROG },
|
||||
{ ClassJob.Ninja, x.NIN },
|
||||
{ ClassJob.Machinist, x.MCH },
|
||||
{ ClassJob.DarkKnight, x.DRK },
|
||||
{ ClassJob.Astrologian, x.AST },
|
||||
{ ClassJob.Samurai, x.SAM },
|
||||
{ ClassJob.RedMage, x.RDM },
|
||||
{ ClassJob.BlueMage, x.BLU },
|
||||
{ ClassJob.Gunbreaker, x.GNB },
|
||||
{ ClassJob.Dancer, x.DNC },
|
||||
{ ClassJob.Reaper, x.RPR },
|
||||
{ ClassJob.Sage, x.SGE },
|
||||
}
|
||||
.Where(y => y.Value)
|
||||
.Select(y => y.Key)
|
||||
.ToList());
|
||||
}
|
||||
|
||||
private void TerritoryChanged(ushort territory)
|
||||
{
|
||||
if (territory == 128)
|
||||
ShowUpgrades();
|
||||
}
|
||||
|
||||
private void ProcessCommand(string command, string arguments) => ShowUpgrades();
|
||||
|
||||
private unsafe void ShowUpgrades()
|
||||
{
|
||||
var inventoryManager = InventoryManager.Instance();
|
||||
List<CachedItem> inventoryItems = new();
|
||||
foreach (var inventoryType in DefaultInventoryTypes)
|
||||
{
|
||||
var container = inventoryManager->GetInventoryContainer(inventoryType);
|
||||
for (int i = 0; i < container->Size; ++i)
|
||||
{
|
||||
var item = container->GetInventorySlot(i);
|
||||
if (item != null && item->ItemID != 0)
|
||||
{
|
||||
CachedItem? cachedItem = LookupItem(item->ItemID, item->Flags.HasFlag(InventoryItem.ItemFlags.HQ));
|
||||
if (cachedItem != null)
|
||||
inventoryItems.Add(cachedItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var gearsetModule = RaptureGearsetModule.Instance();
|
||||
if (gearsetModule == null)
|
||||
return;
|
||||
|
||||
bool anyUpgrade = false;
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
var gearset = gearsetModule->GetGearset(i);
|
||||
if (gearset != null && gearset->Flags.HasFlag(RaptureGearsetModule.GearsetFlag.Exists))
|
||||
{
|
||||
anyUpgrade |= HandleGearset(gearset, inventoryItems);
|
||||
}
|
||||
}
|
||||
|
||||
if (!anyUpgrade)
|
||||
_chatGui.Print("All your gearsets are OK.");
|
||||
}
|
||||
|
||||
private unsafe bool HandleGearset(RaptureGearsetModule.GearsetEntry* gearset, List<CachedItem> inventoryItems)
|
||||
{
|
||||
string name = GetGearsetName(gearset);
|
||||
if (name.Contains('_') || name.Contains("Eureka") || name.Contains("Bozja"))
|
||||
return false;
|
||||
|
||||
List<List<SeString>> upgrades = new()
|
||||
{
|
||||
HandleGearsetItem("Main Hand", gearset, gearset->MainHand, inventoryItems),
|
||||
HandleGearsetItem("Off Hand", gearset, gearset->OffHand, inventoryItems),
|
||||
|
||||
HandleGearsetItem("Head", gearset, gearset->Head, inventoryItems),
|
||||
HandleGearsetItem("Body", gearset, gearset->Body, inventoryItems),
|
||||
HandleGearsetItem("Hands", gearset, gearset->Hands, inventoryItems),
|
||||
HandleGearsetItem("Legs", gearset, gearset->Legs, inventoryItems),
|
||||
HandleGearsetItem("Feet", gearset, gearset->Feet, inventoryItems),
|
||||
|
||||
HandleGearsetItem("Ears", gearset, gearset->Ears, inventoryItems),
|
||||
HandleGearsetItem("Neck", gearset, gearset->Neck, inventoryItems),
|
||||
HandleGearsetItem("Wrists", gearset, gearset->Wrists, inventoryItems),
|
||||
HandleGearsetItem("Rings", gearset, new[] { gearset->RingRight, gearset->RingLeft }, inventoryItems),
|
||||
};
|
||||
|
||||
List<SeString> flatUpgrades = upgrades.SelectMany(x => x).ToList();
|
||||
if (flatUpgrades.Count == 0)
|
||||
return false;
|
||||
|
||||
_chatGui.Print(
|
||||
new SeStringBuilder()
|
||||
.Append("Gearset ")
|
||||
.AddUiForeground(1)
|
||||
.Add(_linkPayloads[gearset->ID])
|
||||
.Append($"#{gearset->ID + 1}: ")
|
||||
.Append(name)
|
||||
.Add(RawPayload.LinkTerminator)
|
||||
.AddUiForegroundOff()
|
||||
.Build());
|
||||
|
||||
foreach (var upgrade in flatUpgrades)
|
||||
_chatGui.Print(new SeString(new TextPayload(" - ")).Append(upgrade));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This probably includes the ilvl; at the very minimum attempting to print this directly to chat will act as if
|
||||
/// the string ends after the name (and not render ANY text on the same line after the name).
|
||||
/// </summary>
|
||||
private unsafe string GetGearsetName(RaptureGearsetModule.GearsetEntry* gearset)
|
||||
=> Encoding.UTF8.GetString(gearset->Name, 0x2F).Split((char)0)[0];
|
||||
|
||||
private unsafe List<SeString> HandleGearsetItem(string label, RaptureGearsetModule.GearsetEntry* gearset,
|
||||
RaptureGearsetModule.GearsetItem gearsetItem, List<CachedItem> inventoryItems)
|
||||
=> HandleGearsetItem(label, gearset, new[] { gearsetItem }, inventoryItems);
|
||||
|
||||
private unsafe List<SeString> HandleGearsetItem(string label, RaptureGearsetModule.GearsetEntry* gearset,
|
||||
RaptureGearsetModule.GearsetItem[] gearsetItem, List<CachedItem> inventoryItems)
|
||||
{
|
||||
gearsetItem = gearsetItem.Where(x => x.ItemID != 0).ToArray();
|
||||
if (gearsetItem.Length > 0)
|
||||
{
|
||||
ClassJob classJob = (ClassJob)gearset->ClassJob;
|
||||
CachedItem[] currentItems = gearsetItem.Select(x => LookupItem(x.ItemID)).Where(x => x != null)
|
||||
.Select(x => x!).ToArray();
|
||||
if (currentItems.Length == 0)
|
||||
{
|
||||
_pluginLog.Information($"Unable to find gearset items");
|
||||
return new List<SeString>();
|
||||
}
|
||||
|
||||
var level = PlayerState.Instance()->ClassJobLevelArray[
|
||||
_classJobToArrayIndex[gearset->ClassJob]];
|
||||
|
||||
var bestItems = inventoryItems
|
||||
.Where(x => x.EquipSlotCategory == currentItems[0].EquipSlotCategory)
|
||||
.Where(x => x.Level <= level)
|
||||
.Where(x => x.ClassJobs.Contains(classJob))
|
||||
.Where(x => x.CalculateScore(classJob, level) > 0)
|
||||
.OrderByDescending(x => x.CalculateScore(classJob, level))
|
||||
.Take(gearsetItem.Length)
|
||||
.ToList();
|
||||
foreach (var currentItem in currentItems)
|
||||
{
|
||||
if (bestItems.Contains(currentItem))
|
||||
bestItems.Remove(currentItem);
|
||||
}
|
||||
|
||||
// don't make suggestions for equal scores
|
||||
bestItems.RemoveAll(x =>
|
||||
x.CalculateScore(classJob, level) ==
|
||||
currentItems.Select(y => y.CalculateScore(classJob, level)).Max());
|
||||
|
||||
return bestItems
|
||||
.Select(x => new SeString(new TextPayload($"{label}: "))
|
||||
.Append(SeString.CreateItemLink(x.ItemId, x.Hq))).ToList();
|
||||
}
|
||||
|
||||
return new List<SeString>();
|
||||
}
|
||||
|
||||
private CachedItem? LookupItem(uint itemId)
|
||||
{
|
||||
if (_cachedItems.TryGetValue(itemId, out CachedItem? cachedItem))
|
||||
return cachedItem;
|
||||
|
||||
try
|
||||
{
|
||||
var item = _dataManager.GetExcelSheet<Item>()!.GetRow(itemId % 1_000_000)!;
|
||||
cachedItem = new CachedItem
|
||||
{
|
||||
Item = item,
|
||||
ItemId = item.RowId,
|
||||
Hq = itemId > 1_000_000,
|
||||
Name = item.Name.ToString(),
|
||||
Level = item.LevelEquip,
|
||||
ItemLevel = item.LevelItem.Row,
|
||||
Rarity = item.Rarity,
|
||||
EquipSlotCategory = item.EquipSlotCategory.Row,
|
||||
ClassJobs = _classJobCategories[item.ClassJobCategory.Row],
|
||||
};
|
||||
_cachedItems[itemId] = cachedItem;
|
||||
return cachedItem;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_pluginLog.Information($"Unable to lookup item {itemId}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private CachedItem? LookupItem(uint itemId, bool hq)
|
||||
=> LookupItem(itemId + (hq ? 1_000_000u : 0));
|
||||
|
||||
private unsafe void ChangeGearset(uint commandId, SeString seString)
|
||||
=> RaptureGearsetModule.Instance()->EquipGearset((byte)commandId);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_clientState.TerritoryChanged -= TerritoryChanged;
|
||||
_pluginInterface.RemoveChatLinkHandler();
|
||||
_commandManager.RemoveHandler("/gup");
|
||||
}
|
||||
}
|
13
Gearsetter/packages.lock.json
Normal file
13
Gearsetter/packages.lock.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"version": 1,
|
||||
"dependencies": {
|
||||
"net7.0-windows7.0": {
|
||||
"DalamudPackager": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.1.12, )",
|
||||
"resolved": "2.1.12",
|
||||
"contentHash": "Sc0PVxvgg4NQjcI8n10/VfUQBAS4O+Fw2pZrAqBdRMbthYGeogzu5+xmIGCGmsEZ/ukMOBuAqiNiB5qA3MRalg=="
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
7
global.json
Normal file
7
global.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "7.0.0",
|
||||
"rollForward": "latestMinor",
|
||||
"allowPrerelease": false
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user