Compare commits

..

No commits in common. "master" and "v1.0" have entirely different histories.
master ... v1.0

16 changed files with 166 additions and 1235 deletions

2
LLib

@ -1 +1 @@
Subproject commit e206782c1106e1a5292a06af61783faef1ac0c42
Subproject commit 865a6080319f8ccbcd5fd5b0004404822b6e60d4

File diff suppressed because it is too large Load Diff

View File

@ -37,9 +37,4 @@ internal class Attributes : IEquatable<Attributes>
{
return !Equals(left, right);
}
public override string ToString()
{
return $"{PhysicalAbility} / {MentalAbility} / {TacticalAbility}";
}
}

View File

@ -54,7 +54,7 @@ internal sealed class BonusAttributes : Attributes, IEquatable<BonusAttributes>
}
}
private static void Fix(ref int mainStat, int mainGained, ref int otherStatA, int otherGainedA, ref int otherStatB, int otherGainedB)
private void Fix(ref int mainStat, int mainGained, ref int otherStatA, int otherGainedA, ref int otherStatB, int otherGainedB)
{
if (mainStat >= 0 || mainGained > 0)
return;

View File

@ -10,12 +10,12 @@ internal sealed class SquadronMember : IEquatable<SquadronMember>
{
public required string Name { get; init; }
public required int Level { get; init; }
public required uint ClassJob { get; init; }
// TODO
public required Race Race { get; init; }
public required int EnlistmentTimestamp { get; init; }
public uint Experience { get; init; }
// TODO
public int Experience { get; init; }
public int PhysicalAbility => GrowthParams[Level].PhysicalAbility;
public int MentalAbility => GrowthParams[Level].MentalAbility;
public int TacticalAbility => GrowthParams[Level].TacticalAbility;
@ -34,7 +34,6 @@ internal sealed class SquadronMember : IEquatable<SquadronMember>
{
growthAsList.Add((growth.Physical[i], growth.Mental[i], growth.Tactical[i]));
}
growthAsList.Add((growth.Unknown123, growth.Unknown184, growth.Unknown245));
GrowthParams = growthAsList;
return this;
@ -44,8 +43,7 @@ internal sealed class SquadronMember : IEquatable<SquadronMember>
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Name == other.Name && Level == other.Level && ClassJob == other.ClassJob && Race == other.Race &&
Experience == other.Experience && PhysicalAbility == other.PhysicalAbility && MentalAbility == other.MentalAbility && TacticalAbility == other.TacticalAbility;
return Name == other.Name && Level == other.Level && ClassJob == other.ClassJob && Race == other.Race && Experience == other.Experience;
}
public override bool Equals(object? obj)
@ -67,11 +65,4 @@ internal sealed class SquadronMember : IEquatable<SquadronMember>
{
return !Equals(left, right);
}
public Attributes ToAttributes() => new()
{
PhysicalAbility = PhysicalAbility,
MentalAbility = MentalAbility,
TacticalAbility = TacticalAbility,
};
}

View File

@ -3,7 +3,7 @@
namespace Squadronista.Solver;
[Flags]
public enum SquadronMemberUiInfo : int
public enum SquadronMemberUiFlags : int
{
Unknown1 = 1,
IsPartOfMission = 2,

View File

@ -38,11 +38,11 @@ internal sealed class SquadronSolver
_newTrainings = _calculatedTrainings.Keys.ToList();
}
public IEnumerable<CalculationResult> SolveFor(SquadronMission mission, Attributes missionAttributes, int requiredMatchingStats)
public IEnumerable<CalculationResult> SolveFor(SquadronMission mission, int requiredMatchingStats)
{
int minPhysical = missionAttributes.PhysicalAbility;
int minMental = missionAttributes.MentalAbility;
int minTactical = missionAttributes.TacticalAbility;
int minPhysical = mission.CurrentAttributes!.PhysicalAbility;
int minMental = mission.CurrentAttributes!.MentalAbility;
int minTactical = mission.CurrentAttributes!.TacticalAbility;
bool foundWithoutTraining = false;
List<CalculationResult> intermediates = CalculateForAllMemberCombinations(mission.Level, _state.Bonus);
@ -78,7 +78,7 @@ internal sealed class SquadronSolver
}
}
private static int CountStatMatches(CalculationResult x, int minPhysical, int minMental, int minTactical)
private int CountStatMatches(CalculationResult x, int minPhysical, int minMental, int minTactical)
{
return (x.PhysicalAbility >= minPhysical ? 1 : 0) +
(x.MentalAbility >= minMental ? 1 : 0) +

View File

@ -5,20 +5,15 @@ namespace Squadronista.Solver;
internal sealed class SquadronState
{
private readonly Dictionary<(int id, Attributes attributes), Task<SquadronSolver.CalculationResults>> _calculationResults = new();
private readonly Dictionary<SquadronMission, Task<SquadronSolver.CalculationResults>> _calculationResults = new();
public required byte Rank { get; init; }
public required IReadOnlyList<SquadronMember> Members { get; init; }
public required BonusAttributes Bonus { get; set; }
public required uint CurrentTraining { get; set; }
public Task<SquadronSolver.CalculationResults>? GetCalculation(SquadronMission mission, Attributes? attributes)
{
if (attributes == null)
return null;
public Task<SquadronSolver.CalculationResults>? GetCalculation(SquadronMission mission)
=> _calculationResults.TryGetValue(mission, out var task) ? task : null;
return _calculationResults.GetValueOrDefault((mission.Id, attributes));
}
public void SetCalculation(SquadronMission mission, Attributes attributes, Task<SquadronSolver.CalculationResults> task)
=> _calculationResults[(mission.Id, attributes)] = task;
public void SetCalculation(SquadronMission mission, Task<SquadronSolver.CalculationResults> task)
=> _calculationResults[mission] = task;
}

View File

@ -12,7 +12,7 @@ public class Training
public int CappedMentalGained => CalculateCapped(MentalGained, PhysicalGained, TacticalGained);
public int CappedTacticalGained => CalculateCapped(TacticalGained, PhysicalGained, MentalGained);
private static int CalculateCapped(int mainStat, int otherA, int otherB)
private int CalculateCapped(int mainStat, int otherA, int otherB)
{
if (mainStat > 0)
return mainStat;

View File

@ -11,4 +11,5 @@ internal sealed class SquadronMission
public required bool IsFlaggedMission { get; init; }
public required IReadOnlyList<Attributes> PossibleAttributes { get; init; }
public Attributes? CurrentAttributes { get; set; }
}

View File

@ -1,13 +1,64 @@
<Project Sdk="Dalamud.NET.Sdk/9.0.2">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version>4.0</Version>
<TargetFramework>net7.0-windows</TargetFramework>
<Version>1.0</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>
<Import Project="..\LLib\LLib.targets"/>
<Import Project="..\LLib\RenameZip.targets"/>
<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>
<ProjectReference Include="..\LLib\LLib.csproj" />
</ItemGroup>
<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>

View File

@ -4,5 +4,5 @@
"Punchline": "Simplified Squadron Calculator, heavily inspired by https://ffxivsquadron.com/",
"Description": "",
"RepoUrl": "https://git.carvel.li/liza/Squadronista",
"IconUrl": "https://plugins.carvel.li/icons/Squadronista.png"
"IconUrl": "https://git.carvel.li/liza/plugin-repo/raw/branch/master/dist/Squadronista.png"
}

View File

@ -1,14 +1,15 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.InteropServices;
using Dalamud.Game.Addon.Lifecycle;
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
using Dalamud.Interface.Windowing;
using Dalamud.Memory;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.UI;
using FFXIVClientStructs.FFXIV.Component.GUI;
using Lumina.Excel.GeneratedSheets;
using Squadronista.Solver;
using Squadronista.Windows;
@ -16,29 +17,34 @@ using Race = Squadronista.Solver.Race;
namespace Squadronista;
public sealed class SquadronistaPlugin : IDalamudPlugin
public class SquadronistaPlugin : IDalamudPlugin
{
private readonly WindowSystem _windowSystem = new(nameof(SquadronistaPlugin));
private readonly IDalamudPluginInterface _pluginInterface;
private readonly DalamudPluginInterface _pluginInterface;
private readonly IClientState _clientState;
private readonly IPluginLog _pluginLog;
private readonly IDataManager _dataManager;
private readonly IAddonLifecycle _addonLifecycle;
private readonly IReadOnlyDictionary<string, uint> _classNamesToId;
private readonly IReadOnlyList<SquadronMission> _allMissions;
private readonly MainWindow _mainWindow;
public SquadronistaPlugin(IDalamudPluginInterface pluginInterface, IClientState clientState, IPluginLog pluginLog,
IDataManager dataManager, IAddonLifecycle addonLifecycle, IGameGui gameGui)
public SquadronistaPlugin(DalamudPluginInterface pluginInterface, IClientState clientState, IPluginLog pluginLog,
IDataManager dataManager, IAddonLifecycle addonLifecycle, IGameGui gameGui, IFramework framework)
{
ArgumentNullException.ThrowIfNull(dataManager);
_pluginInterface = pluginInterface;
_clientState = clientState;
_pluginLog = pluginLog;
_dataManager = dataManager;
_addonLifecycle = addonLifecycle;
_classNamesToId = dataManager.GetExcelSheet<ClassJob>()!
.Where(x => x.RowId > 0)
.Where(x => x.Name.ToString().Length > 0)
.ToDictionary(x => x.Name.ToString().ToLower(), x => x.RowId)
.AsReadOnly();
_allMissions = dataManager.GetExcelSheet<GcArmyExpedition>()!
.Where(x => x.RowId > 0)
.Select(x => new SquadronMission
@ -92,81 +98,68 @@ public sealed class SquadronistaPlugin : IDalamudPlugin
private unsafe void UpdateSquadronState(AddonEvent type, AddonArgs args)
{
_pluginLog.Information("Updating squadron state from member list");
var gcArmyManager = GcArmyManager.Instance();
if (gcArmyManager == null)
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
if (addon->AtkValuesCount != 133)
{
_pluginLog.Warning("No GcArmyManager");
_pluginLog.Error("Unexpected AddonGcArmyMemberList atkvalues count");
ResetCharacterSpecificData();
return;
}
if (gcArmyManager->Data == null)
var atkValues = addon->AtkValues;
uint memberCount = atkValues[4].UInt;
// can't do any missions like this...
if (memberCount < 4)
{
_pluginLog.Warning("No GcArmyManager->Data");
ResetCharacterSpecificData();
return;
}
if (gcArmyManager->GetMemberCount() < 4)
{
_pluginLog.Warning($"Not enough squadron members to send on missions, only got {gcArmyManager->GetMemberCount()} members");
ResetCharacterSpecificData();
return;
}
IReadOnlyList<SquadronMember> members = Enumerable.Range(0, (int)gcArmyManager->GetMemberCount())
.Select(i =>
IReadOnlyList<SquadronMember> members = Enumerable.Range(0, (int)memberCount)
.Select(i => new SquadronMember
{
var member = gcArmyManager->GetMember((uint)i);
if (member == null)
return null;
return new SquadronMember
{
Name = _dataManager.GetExcelSheet<ENpcResident>()!.GetRow(member->ENpcResidentId)!
.Singular.ToString(),
Level = member->Level,
ClassJob = member->ClassJob,
Race = (Race)member->Race,
Experience = member->Experience,
EnlistmentTimestamp = member->EnlistmentTimestamp,
}.InitializeFrom(_dataManager);
})
.Where(x => x != null)
.Cast<SquadronMember>()
.OrderBy(x => x.EnlistmentTimestamp)
Name = ReadAtkString(atkValues[6 + 15 * i])!,
Level = atkValues[10 + 15 * i].Int,
ClassJob = _classNamesToId[ReadAtkString(atkValues[7 + 15 * i])!.ToLower()],
Race = Race.Lalafell, // TODO
Experience = 0, // TODO
}.InitializeFrom(_dataManager))
.ToList()
.AsReadOnly();
byte rank = byte.Parse(ReadAtkString(atkValues[0])!.Split(":")[1].Trim());
int[] attributes = ReadAtkString(atkValues[1])!.Split(":")[1].Trim()
.Split("/")
.Select(int.Parse)
.ToArray();
var bonus = new BonusAttributes
{
PhysicalAbility = gcArmyManager->Data->BonusPhysical,
MentalAbility = gcArmyManager->Data->BonusMental,
TacticalAbility = gcArmyManager->Data->BonusTactical,
Cap = gcArmyManager->Data->BonusPhysical + gcArmyManager->Data->BonusMental + gcArmyManager->Data->BonusTactical,
PhysicalAbility = attributes[0],
MentalAbility = attributes[1],
TacticalAbility = attributes[2],
Cap = attributes.Sum()
};
if (SquadronState != null &&
members.SequenceEqual(SquadronState.Members) &&
rank == SquadronState.Rank &&
bonus == SquadronState.Bonus)
{
// nothing changed...
_pluginLog.Verbose("Not updating SquadronState, not changed");
return;
}
SquadronState = new SquadronState
{
Members = members,
Rank = rank,
Bonus = bonus,
CurrentTraining = ((ExtendedGcArmyData*)gcArmyManager->Data)->CurrentTraining,
};
_pluginLog.Verbose(
$"Bonus stats: {bonus} (Cap: {bonus.Cap})");
foreach (var member in members)
_pluginLog.Verbose(
$"Squadron Member {member.Name}: ClassJob {member.ClassJob}, Lv{member.Level} → {member.ToAttributes()}");
_pluginLog.Verbose($"Squadron Member {member.Name}: ClassJob {member.ClassJob}, Lv{member.Level}");
}
private unsafe void UpdateExpeditionState(AddonEvent type, AddonArgs args)
@ -191,9 +184,18 @@ public sealed class SquadronistaPlugin : IDalamudPlugin
.ToList();
}
private unsafe string? ReadAtkString(AtkValue atkValue)
{
if (atkValue.String != null)
return MemoryHelper.ReadSeStringNullTerminated(new nint(atkValue.String)).ToString();
return null;
}
private void ResetCharacterSpecificData()
{
SquadronState = null;
foreach (var mission in _allMissions)
mission.CurrentAttributes = null;
}
public void Dispose()
@ -205,9 +207,4 @@ public sealed class SquadronistaPlugin : IDalamudPlugin
_clientState.Logout -= ResetCharacterSpecificData;
_pluginInterface.UiBuilder.Draw -= _windowSystem.Draw;
}
[StructLayout(LayoutKind.Explicit, Size = 0xB28)]
private struct ExtendedGcArmyData {
[FieldOffset(0x2C4)] public ushort CurrentTraining;
}
}

View File

@ -1,10 +1,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Numerics;
using System.Threading;
using System.Threading.Tasks;
using Dalamud.Game.Addon.Lifecycle;
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
using Dalamud.Game.Text;
@ -15,15 +12,15 @@ using FFXIVClientStructs.FFXIV.Client.UI;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using FFXIVClientStructs.FFXIV.Component.GUI;
using ImGuiNET;
using LLib;
using LLib.GameUI;
using LLib.ImGui;
using Squadronista.Solver;
using Task = System.Threading.Tasks.Task;
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
namespace Squadronista.Windows;
internal sealed class MainWindow : LWindow, IDisposable
internal sealed class MainWindow : LImGui.LWindow, IDisposable
{
private readonly SquadronistaPlugin _plugin;
private readonly IPluginLog _pluginLog;
@ -95,29 +92,21 @@ internal sealed class MainWindow : LWindow, IDisposable
var agentExpedition = AgentGcArmyExpedition.Instance();
if (agentExpedition == null || agentExpedition->SelectedRow >= _plugin.AvailableMissions.Count)
{
ImGui.Text($"Could not find mission... ({(agentExpedition != null ? agentExpedition->SelectedRow.ToString(CultureInfo.InvariantCulture) : "null")}; {_plugin.AvailableMissions.Count})");
ImGui.Text($"Could not find mission... ({(agentExpedition != null ? agentExpedition->SelectedRow.ToString() : "null")}; {_plugin.AvailableMissions.Count})");
return;
}
var selectedMission = _plugin.AvailableMissions[agentExpedition->SelectedRow];
var missionAttributes = FindCurrentAttributeIndex(agentExpedition, selectedMission);
if (missionAttributes != null)
ImGui.Text($"{selectedMission.Name} ({missionAttributes})");
else
ImGui.Text($"{selectedMission.Name}");
ImGui.Text($"{selectedMission.Name}");
var state = _plugin.SquadronState;
if (state == null)
{
ImGui.TextColored(ImGuiColors.DalamudYellow, "Open Squadron Member list to continue.");
}
else if (state.CurrentTraining != 0)
{
ImGui.TextColored(ImGuiColors.DalamudRed, "Your squadron is currently in training.");
}
else
{
var task = state.GetCalculation(selectedMission, missionAttributes);
var task = state.GetCalculation(selectedMission);
if (task != null)
{
if (task.IsCompletedSuccessfully)
@ -132,7 +121,7 @@ internal sealed class MainWindow : LWindow, IDisposable
{
var atkValues = addonMemberList->AtkValues;
activeMembers = Enumerable.Range(0, (int)atkValues[4].UInt)
.Where(i => ((SquadronMemberUiInfo)atkValues[5 + i * 15].Int).HasFlag(SquadronMemberUiInfo.IsPartOfMission))
.Where(i => ((SquadronMemberUiFlags)atkValues[5 + i * 15].Int).HasFlag(SquadronMemberUiFlags.IsPartOfMission))
.Select(i => atkValues[6 + i * 15].ReadAtkString())
.Where(x => !string.IsNullOrEmpty(x))
.Cast<string>()
@ -176,21 +165,21 @@ internal sealed class MainWindow : LWindow, IDisposable
ImGui.Text("Final Stats:");
ImGui.SameLine(0);
ImGui.TextColored(
result.PhysicalAbility >= missionAttributes!.PhysicalAbility
result.PhysicalAbility >= selectedMission.CurrentAttributes!.PhysicalAbility
? ImGuiColors.HealerGreen
: ImGuiColors.DalamudYellow, $"{result.PhysicalAbility}");
ImGui.SameLine(0);
ImGui.Text("/");
ImGui.SameLine(0);
ImGui.TextColored(
result.MentalAbility >= missionAttributes.MentalAbility
result.MentalAbility >= selectedMission.CurrentAttributes!.MentalAbility
? ImGuiColors.HealerGreen
: ImGuiColors.DalamudYellow, $"{result.MentalAbility}");
ImGui.SameLine(0);
ImGui.Text("/");
ImGui.SameLine(0);
ImGui.TextColored(
result.TacticalAbility >= missionAttributes.TacticalAbility
result.TacticalAbility >= selectedMission.CurrentAttributes!.TacticalAbility
? ImGuiColors.HealerGreen
: ImGuiColors.DalamudYellow, $"{result.TacticalAbility}");
@ -199,7 +188,7 @@ internal sealed class MainWindow : LWindow, IDisposable
}
else
{
ImGui.TextColored(ImGuiColors.DalamudRed, $"No combination of members/trainings can achieve\n{(selectedMission.IsFlaggedMission ? "all" : "2 out of 3")} attributes for {missionAttributes}.");
ImGui.TextColored(ImGuiColors.DalamudRed, $"No combination of members/trainings can achieve\n{(selectedMission.IsFlaggedMission ? "all" : "2 out of 3")} attributes for {selectedMission.CurrentAttributes!.PhysicalAbility} / {selectedMission.CurrentAttributes!.MentalAbility} / {selectedMission.CurrentAttributes!.TacticalAbility}.");
ImGui.Text("Level the squadron further and check again.");
}
}
@ -210,13 +199,16 @@ internal sealed class MainWindow : LWindow, IDisposable
}
else
{
if (missionAttributes == null)
if (selectedMission.CurrentAttributes == null)
FindCurrentAttributeIndex(agentExpedition, selectedMission);
if (selectedMission.CurrentAttributes == null)
{
ImGui.Text("No matching mission found...?");
return;
}
state.SetCalculation(selectedMission, missionAttributes, Task.Factory.StartNew(() =>
state.SetCalculation(selectedMission, Task.Factory.StartNew(() =>
{
var solver = new SquadronSolver(_pluginLog, state, _plugin.Trainings);
@ -228,7 +220,7 @@ internal sealed class MainWindow : LWindow, IDisposable
if (selectedMission.IsFlaggedMission)
{
// only relevant when all 3 stats match
var perfectMatches = solver.SolveFor(selectedMission, missionAttributes, 3).ToList();
var perfectMatches = solver.SolveFor(selectedMission, 3).ToList();
if (perfectMatches.Count > 0)
{
results.Results.Add(perfectMatches
@ -239,7 +231,7 @@ internal sealed class MainWindow : LWindow, IDisposable
}
else
{
var matches = solver.SolveFor(selectedMission, missionAttributes, 2)
var matches = solver.SolveFor(selectedMission, 2)
.OrderByDescending(x => x.MatchingAttributes)
.ThenBy(x => x.Trainings.Count)
.ThenBy(x => x.Members.Sum(y => y.Level))
@ -269,7 +261,7 @@ internal sealed class MainWindow : LWindow, IDisposable
if (matches.Count > 0)
{
var suboptimalMatch = matches.First();
if (suboptimalMatch.Trainings.Count == 0 && perfectMatchesWithTraining.Count != 0)
if (suboptimalMatch.Trainings.Count == 0 && perfectMatchesWithTraining.Any())
results.Results.Insert(results.Results.Count - 1, suboptimalMatch);
else
results.Results.Add(suboptimalMatch);
@ -277,12 +269,12 @@ internal sealed class MainWindow : LWindow, IDisposable
}
return results;
}, default, TaskCreationOptions.LongRunning, TaskScheduler.Default));
}));
}
}
}
private unsafe Attributes? FindCurrentAttributeIndex(AgentGcArmyExpedition* agentExpedition,
private unsafe void FindCurrentAttributeIndex(AgentGcArmyExpedition* agentExpedition,
SquadronMission selectedMission)
{
AddonGcArmyExpedition* addonExpedition =
@ -290,11 +282,11 @@ internal sealed class MainWindow : LWindow, IDisposable
// should never happen
if (addonExpedition == null || !LAddon.IsAddonReady(&addonExpedition->AtkUnitBase))
return null;
return;
AtkComponentBase* requiredAttribComponent = addonExpedition->RequiredAttributesComponentNode;
if (requiredAttribComponent == null)
return null;
return;
AtkComponentNode* physicalComponent = GetNodeById<AtkComponentNode>(requiredAttribComponent->UldManager, 2);
AtkComponentNode* mentalComponent = GetNodeById<AtkComponentNode>(requiredAttribComponent->UldManager, 4);
@ -302,7 +294,7 @@ internal sealed class MainWindow : LWindow, IDisposable
if (physicalComponent == null || mentalComponent == null || tacticalComponent == null)
{
_pluginLog.Warning("Could not parse required attribute children");
return null;
return;
}
AtkTextNode* physicalText = GetNodeById<AtkTextNode>(physicalComponent->Component->UldManager, 2);
@ -311,12 +303,12 @@ internal sealed class MainWindow : LWindow, IDisposable
if (physicalText == null || mentalText == null || tacticalText == null)
{
_pluginLog.Warning("Could not parse required attribute texts");
return null;
return;
}
int physical = int.Parse(physicalText->NodeText.ToString(), CultureInfo.InvariantCulture);
int mental = int.Parse(mentalText->NodeText.ToString(), CultureInfo.InvariantCulture);
int tactical = int.Parse(tacticalText->NodeText.ToString(), CultureInfo.InvariantCulture);
int physical = int.Parse(physicalText->NodeText.ToString());
int mental = int.Parse(mentalText->NodeText.ToString());
int tactical = int.Parse(tacticalText->NodeText.ToString());
var newAttributes = new Attributes
{
@ -325,12 +317,9 @@ internal sealed class MainWindow : LWindow, IDisposable
TacticalAbility = tactical,
};
if (selectedMission.PossibleAttributes.Contains(newAttributes))
return newAttributes;
selectedMission.CurrentAttributes = newAttributes;
else
{
_pluginLog.Warning($"Wrong attributes for {selectedMission.Name}: {newAttributes}");
return null;
}
_pluginLog.Warning($"Wrong attributes for {selectedMission.Name}: {physical} / {mental} / {tactical}");
}
private static unsafe T* GetNodeById<T>(AtkUldManager uldManager, uint nodeId, NodeType? type = null)
@ -339,8 +328,8 @@ internal sealed class MainWindow : LWindow, IDisposable
for (var i = 0; i < uldManager.NodeListCount; i++)
{
var n = uldManager.NodeList[i];
if (n->NodeId != nodeId || type != null && n->Type != type.Value) continue;
if (!n->IsVisible()) continue;
if (n->NodeID != nodeId || type != null && n->Type != type.Value) continue;
if (!n->IsVisible) continue;
return (T*)n;
}

View File

@ -1,86 +1,15 @@
{
"version": 1,
"dependencies": {
"net8.0-windows7.0": {
"net7.0-windows7.0": {
"DalamudPackager": {
"type": "Direct",
"requested": "[2.1.13, )",
"resolved": "2.1.13",
"contentHash": "rMN1omGe8536f4xLMvx9NwfvpAc9YFFfeXJ1t4P4PE6Gu8WCIoFliR1sh07hM+bfODmesk/dvMbji7vNI+B/pQ=="
},
"DotNet.ReproducibleBuilds": {
"type": "Direct",
"requested": "[1.1.1, )",
"resolved": "1.1.1",
"contentHash": "+H2t/t34h6mhEoUvHi8yGXyuZ2GjSovcGYehJrS2MDm2XgmPfZL2Sdxg+uL2lKgZ4M6tTwKHIlxOob2bgh0NRQ==",
"dependencies": {
"Microsoft.SourceLink.AzureRepos.Git": "1.1.1",
"Microsoft.SourceLink.Bitbucket.Git": "1.1.1",
"Microsoft.SourceLink.GitHub": "1.1.1",
"Microsoft.SourceLink.GitLab": "1.1.1"
}
},
"Microsoft.SourceLink.Gitea": {
"type": "Direct",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "KOBodmDnlWGIqZt2hT47Q69TIoGhIApDVLCyyj9TT5ct8ju16AbHYcB4XeknoHX562wO1pMS/1DfBIZK+V+sxg==",
"dependencies": {
"Microsoft.Build.Tasks.Git": "8.0.0",
"Microsoft.SourceLink.Common": "8.0.0"
}
},
"Microsoft.Build.Tasks.Git": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
},
"Microsoft.SourceLink.AzureRepos.Git": {
"type": "Transitive",
"resolved": "1.1.1",
"contentHash": "qB5urvw9LO2bG3eVAkuL+2ughxz2rR7aYgm2iyrB8Rlk9cp2ndvGRCvehk3rNIhRuNtQaeKwctOl1KvWiklv5w==",
"dependencies": {
"Microsoft.Build.Tasks.Git": "1.1.1",
"Microsoft.SourceLink.Common": "1.1.1"
}
},
"Microsoft.SourceLink.Bitbucket.Git": {
"type": "Transitive",
"resolved": "1.1.1",
"contentHash": "cDzxXwlyWpLWaH0em4Idj0H3AmVo3L/6xRXKssYemx+7W52iNskj/SQ4FOmfCb8YQt39otTDNMveCZzYtMoucQ==",
"dependencies": {
"Microsoft.Build.Tasks.Git": "1.1.1",
"Microsoft.SourceLink.Common": "1.1.1"
}
},
"Microsoft.SourceLink.Common": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
"Microsoft.SourceLink.GitHub": {
"type": "Transitive",
"resolved": "1.1.1",
"contentHash": "IaJGnOv/M7UQjRJks7B6p7pbPnOwisYGOIzqCz5ilGFTApZ3ktOR+6zJ12ZRPInulBmdAf1SrGdDG2MU8g6XTw==",
"dependencies": {
"Microsoft.Build.Tasks.Git": "1.1.1",
"Microsoft.SourceLink.Common": "1.1.1"
}
},
"Microsoft.SourceLink.GitLab": {
"type": "Transitive",
"resolved": "1.1.1",
"contentHash": "tvsg47DDLqqedlPeYVE2lmiTpND8F0hkrealQ5hYltSmvruy/Gr5nHAKSsjyw5L3NeM/HLMI5ORv7on/M4qyZw==",
"dependencies": {
"Microsoft.Build.Tasks.Git": "1.1.1",
"Microsoft.SourceLink.Common": "1.1.1"
}
"requested": "[2.1.12, )",
"resolved": "2.1.12",
"contentHash": "Sc0PVxvgg4NQjcI8n10/VfUQBAS4O+Fw2pZrAqBdRMbthYGeogzu5+xmIGCGmsEZ/ukMOBuAqiNiB5qA3MRalg=="
},
"llib": {
"type": "Project",
"dependencies": {
"DalamudPackager": "[2.1.13, )"
}
"type": "Project"
}
}
}

View File

@ -1,6 +1,6 @@
{
"sdk": {
"version": "8.0.0",
"version": "7.0.0",
"rollForward": "latestMinor",
"allowPrerelease": false
}