Reset cached mission attributes when changing characters; improve detection of who is part of the mission

master v0.4
Liza 2023-10-15 08:56:49 +02:00
parent 821311e4cc
commit c859e2553c
Signed by: liza
GPG Key ID: 7199F8D727D55F67
7 changed files with 78 additions and 18 deletions

View File

@ -1,8 +1,40 @@
using System;
namespace Squadronista.Solver; namespace Squadronista.Solver;
internal class Attributes internal class Attributes : IEquatable<Attributes>
{ {
public required int PhysicalAbility { get; init; } public required int PhysicalAbility { get; init; }
public required int MentalAbility { get; init; } public required int MentalAbility { get; init; }
public required int TacticalAbility { get; init; } public required int TacticalAbility { get; init; }
public bool Equals(Attributes? other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return PhysicalAbility == other.PhysicalAbility && MentalAbility == other.MentalAbility && TacticalAbility == other.TacticalAbility;
}
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Attributes)obj);
}
public override int GetHashCode()
{
return HashCode.Combine(PhysicalAbility, MentalAbility, TacticalAbility);
}
public static bool operator ==(Attributes? left, Attributes? right)
{
return Equals(left, right);
}
public static bool operator !=(Attributes? left, Attributes? right)
{
return !Equals(left, right);
}
} }

View File

@ -0,0 +1,11 @@
using System;
namespace Squadronista.Solver;
[Flags]
public enum SquadronMemberUiFlags : int
{
Unknown1 = 1,
IsPartOfMission = 2,
NewChemistryAvailable = 8192,
}

View File

@ -40,9 +40,9 @@ internal sealed class SquadronSolver
public IEnumerable<CalculationResult> SolveFor(SquadronMission mission, int requiredMatchingStats) public IEnumerable<CalculationResult> SolveFor(SquadronMission mission, int requiredMatchingStats)
{ {
int minPhysical = mission.CurrentAttributes.PhysicalAbility; int minPhysical = mission.CurrentAttributes!.PhysicalAbility;
int minMental = mission.CurrentAttributes.MentalAbility; int minMental = mission.CurrentAttributes!.MentalAbility;
int minTactical = mission.CurrentAttributes.TacticalAbility; int minTactical = mission.CurrentAttributes!.TacticalAbility;
bool foundWithoutTraining = false; bool foundWithoutTraining = false;
List<CalculationResult> intermediates = CalculateForAllMemberCombinations(mission.Level, _state.Bonus); List<CalculationResult> intermediates = CalculateForAllMemberCombinations(mission.Level, _state.Bonus);

View File

@ -1,4 +1,5 @@
using Squadronista.Solver; using System.Collections.Generic;
using Squadronista.Solver;
namespace Squadronista; namespace Squadronista;
@ -9,5 +10,6 @@ internal sealed class SquadronMission
public required byte Level { get; init; } public required byte Level { get; init; }
public required bool IsFlaggedMission { get; init; } public required bool IsFlaggedMission { get; init; }
public required IReadOnlyList<Attributes> PossibleAttributes { get; init; }
public Attributes? CurrentAttributes { get; set; } public Attributes? CurrentAttributes { get; set; }
} }

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework> <TargetFramework>net7.0-windows</TargetFramework>
<Version>0.3</Version> <Version>0.4</Version>
<LangVersion>11.0</LangVersion> <LangVersion>11.0</LangVersion>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

View File

@ -55,6 +55,15 @@ public class SquadronistaPlugin : IDalamudPlugin
// 13 and 14 seems to be a duplicate // 13 and 14 seems to be a duplicate
IsFlaggedMission = x.RowId is 7 or 14 or 15 or 34, IsFlaggedMission = x.RowId is 7 or 14 or 15 or 34,
PossibleAttributes = Enumerable.Range(0, x.RequiredPhysical.Length)
.Select(i => new Attributes
{
PhysicalAbility = x.RequiredPhysical[i],
MentalAbility = x.RequiredMental[i],
TacticalAbility = x.RequiredTactical[i],
})
.ToList()
.AsReadOnly(),
}) })
.ToList() .ToList()
.AsReadOnly(); .AsReadOnly();
@ -73,7 +82,7 @@ public class SquadronistaPlugin : IDalamudPlugin
.AsReadOnly(); .AsReadOnly();
_pluginInterface.UiBuilder.Draw += _windowSystem.Draw; _pluginInterface.UiBuilder.Draw += _windowSystem.Draw;
_clientState.Logout += Logout; _clientState.Logout += ResetCharacterSpecificData;
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "GcArmyMemberList", UpdateSquadronState); _addonLifecycle.RegisterListener(AddonEvent.PostSetup, "GcArmyMemberList", UpdateSquadronState);
_addonLifecycle.RegisterListener(AddonEvent.PostRefresh, "GcArmyExpedition", UpdateExpeditionState); _addonLifecycle.RegisterListener(AddonEvent.PostRefresh, "GcArmyExpedition", UpdateExpeditionState);
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "GcArmyExpedition", UpdateExpeditionState); _addonLifecycle.RegisterListener(AddonEvent.PostSetup, "GcArmyExpedition", UpdateExpeditionState);
@ -94,7 +103,7 @@ public class SquadronistaPlugin : IDalamudPlugin
if (addon->AtkValuesCount != 133) if (addon->AtkValuesCount != 133)
{ {
_pluginLog.Error("Unexpected AddonGcArmyMemberList atkvalues count"); _pluginLog.Error("Unexpected AddonGcArmyMemberList atkvalues count");
SquadronState = null; ResetCharacterSpecificData();
return; return;
} }
@ -104,7 +113,7 @@ public class SquadronistaPlugin : IDalamudPlugin
// can't do any missions like this... // can't do any missions like this...
if (memberCount < 4) if (memberCount < 4)
{ {
SquadronState = null; ResetCharacterSpecificData();
return; return;
} }
@ -157,7 +166,7 @@ public class SquadronistaPlugin : IDalamudPlugin
{ {
AvailableMissions.Clear(); AvailableMissions.Clear();
if (type == AddonEvent.PostSetup) if (type == AddonEvent.PostSetup)
SquadronState = null; ResetCharacterSpecificData();
AddonGcArmyExpedition* addonExpedition = (AddonGcArmyExpedition*)args.Addon; AddonGcArmyExpedition* addonExpedition = (AddonGcArmyExpedition*)args.Addon;
if (addonExpedition->AtkUnitBase.AtkValuesCount != 216) if (addonExpedition->AtkUnitBase.AtkValuesCount != 216)
@ -182,9 +191,11 @@ public class SquadronistaPlugin : IDalamudPlugin
return null; return null;
} }
private void Logout() private void ResetCharacterSpecificData()
{ {
SquadronState = null; SquadronState = null;
foreach (var mission in _allMissions)
mission.CurrentAttributes = null;
} }
public void Dispose() public void Dispose()
@ -193,7 +204,7 @@ public class SquadronistaPlugin : IDalamudPlugin
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "GcArmyExpedition", UpdateExpeditionState); _addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "GcArmyExpedition", UpdateExpeditionState);
_addonLifecycle.UnregisterListener(AddonEvent.PostRefresh, "GcArmyExpedition", UpdateExpeditionState); _addonLifecycle.UnregisterListener(AddonEvent.PostRefresh, "GcArmyExpedition", UpdateExpeditionState);
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "GcArmyMemberList", UpdateSquadronState); _addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "GcArmyMemberList", UpdateSquadronState);
_clientState.Logout -= Logout; _clientState.Logout -= ResetCharacterSpecificData;
_pluginInterface.UiBuilder.Draw -= _windowSystem.Draw; _pluginInterface.UiBuilder.Draw -= _windowSystem.Draw;
} }
} }

View File

@ -126,7 +126,7 @@ internal sealed class MainWindow : Window, IDisposable
{ {
var atkValues = addonMemberList->AtkValues; var atkValues = addonMemberList->AtkValues;
activeMembers = Enumerable.Range(0, (int)atkValues[4].UInt) activeMembers = Enumerable.Range(0, (int)atkValues[4].UInt)
.Where(i => atkValues[5 + i * 15].UInt == 3) .Where(i => ((SquadronMemberUiFlags)atkValues[5 + i * 15].Int).HasFlag(SquadronMemberUiFlags.IsPartOfMission))
.Select(i => atkValues[6 + i * 15].ReadAtkString()) .Select(i => atkValues[6 + i * 15].ReadAtkString())
.Where(x => !string.IsNullOrEmpty(x)) .Where(x => !string.IsNullOrEmpty(x))
.Cast<string>() .Cast<string>()
@ -170,21 +170,21 @@ internal sealed class MainWindow : Window, IDisposable
ImGui.Text("Final Stats:"); ImGui.Text("Final Stats:");
ImGui.SameLine(0); ImGui.SameLine(0);
ImGui.TextColored( ImGui.TextColored(
result.PhysicalAbility >= selectedMission.CurrentAttributes.PhysicalAbility result.PhysicalAbility >= selectedMission.CurrentAttributes!.PhysicalAbility
? ImGuiColors.HealerGreen ? ImGuiColors.HealerGreen
: ImGuiColors.DalamudYellow, $"{result.PhysicalAbility}"); : ImGuiColors.DalamudYellow, $"{result.PhysicalAbility}");
ImGui.SameLine(0); ImGui.SameLine(0);
ImGui.Text("/"); ImGui.Text("/");
ImGui.SameLine(0); ImGui.SameLine(0);
ImGui.TextColored( ImGui.TextColored(
result.MentalAbility >= selectedMission.CurrentAttributes.MentalAbility result.MentalAbility >= selectedMission.CurrentAttributes!.MentalAbility
? ImGuiColors.HealerGreen ? ImGuiColors.HealerGreen
: ImGuiColors.DalamudYellow, $"{result.MentalAbility}"); : ImGuiColors.DalamudYellow, $"{result.MentalAbility}");
ImGui.SameLine(0); ImGui.SameLine(0);
ImGui.Text("/"); ImGui.Text("/");
ImGui.SameLine(0); ImGui.SameLine(0);
ImGui.TextColored( ImGui.TextColored(
result.TacticalAbility >= selectedMission.CurrentAttributes.TacticalAbility result.TacticalAbility >= selectedMission.CurrentAttributes!.TacticalAbility
? ImGuiColors.HealerGreen ? ImGuiColors.HealerGreen
: ImGuiColors.DalamudYellow, $"{result.TacticalAbility}"); : ImGuiColors.DalamudYellow, $"{result.TacticalAbility}");
@ -193,7 +193,7 @@ internal sealed class MainWindow : Window, IDisposable
} }
else else
{ {
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.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."); ImGui.Text("Level the squadron further and check again.");
} }
} }
@ -315,12 +315,16 @@ internal sealed class MainWindow : Window, IDisposable
int mental = int.Parse(mentalText->NodeText.ToString()); int mental = int.Parse(mentalText->NodeText.ToString());
int tactical = int.Parse(tacticalText->NodeText.ToString()); int tactical = int.Parse(tacticalText->NodeText.ToString());
selectedMission.CurrentAttributes = new Attributes var newAttributes = new Attributes
{ {
PhysicalAbility = physical, PhysicalAbility = physical,
MentalAbility = mental, MentalAbility = mental,
TacticalAbility = tactical, TacticalAbility = tactical,
}; };
if (selectedMission.PossibleAttributes.Contains(newAttributes))
selectedMission.CurrentAttributes = newAttributes;
else
_pluginLog.Warning($"Wrong attributes for {selectedMission.Name}: {physical} / {mental} / {tactical}");
} }
private static unsafe T* GetNodeById<T>(AtkUldManager uldManager, uint nodeId, NodeType? type = null) private static unsafe T* GetNodeById<T>(AtkUldManager uldManager, uint nodeId, NodeType? type = null)