punish v6.8.18.0
This commit is contained in:
commit
cfb4dea47e
316 changed files with 554088 additions and 0 deletions
28
LLib/LLib.Gear/EBaseParam.cs
Normal file
28
LLib/LLib.Gear/EBaseParam.cs
Normal file
|
@ -0,0 +1,28 @@
|
|||
namespace LLib.Gear;
|
||||
|
||||
public enum EBaseParam : byte
|
||||
{
|
||||
None = 0,
|
||||
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 = 46,
|
||||
SkillSpeed = 45,
|
||||
Craftsmanship = 70,
|
||||
Control = 71,
|
||||
Gathering = 72,
|
||||
Perception = 73
|
||||
}
|
72
LLib/LLib.Gear/EquipmentStats.cs
Normal file
72
LLib/LLib.Gear/EquipmentStats.cs
Normal file
|
@ -0,0 +1,72 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace LLib.Gear;
|
||||
|
||||
public sealed record EquipmentStats(Dictionary<EBaseParam, StatInfo> Stats, byte MateriaCount)
|
||||
{
|
||||
private sealed class KeyValuePairComparer : IEqualityComparer<KeyValuePair<EBaseParam, StatInfo>>
|
||||
{
|
||||
public bool Equals(KeyValuePair<EBaseParam, StatInfo> x, KeyValuePair<EBaseParam, StatInfo> y)
|
||||
{
|
||||
if (x.Key == y.Key)
|
||||
{
|
||||
return object.Equals(x.Value, y.Value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int GetHashCode(KeyValuePair<EBaseParam, StatInfo> obj)
|
||||
{
|
||||
return HashCode.Combine((int)obj.Key, obj.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public short Get(EBaseParam param)
|
||||
{
|
||||
return (short)(GetEquipment(param) + GetMateria(param));
|
||||
}
|
||||
|
||||
public short GetEquipment(EBaseParam param)
|
||||
{
|
||||
Stats.TryGetValue(param, out StatInfo value);
|
||||
return value?.EquipmentValue ?? 0;
|
||||
}
|
||||
|
||||
public short GetMateria(EBaseParam param)
|
||||
{
|
||||
Stats.TryGetValue(param, out StatInfo value);
|
||||
return value?.MateriaValue ?? 0;
|
||||
}
|
||||
|
||||
public bool IsOvercapped(EBaseParam param)
|
||||
{
|
||||
Stats.TryGetValue(param, out StatInfo value);
|
||||
return value?.Overcapped ?? false;
|
||||
}
|
||||
|
||||
public bool Has(EBaseParam substat)
|
||||
{
|
||||
return Stats.ContainsKey(substat);
|
||||
}
|
||||
|
||||
public bool HasMateria()
|
||||
{
|
||||
return Stats.Values.Any((StatInfo x) => x.MateriaValue > 0);
|
||||
}
|
||||
|
||||
public bool Equals(EquipmentStats? other)
|
||||
{
|
||||
if (other != null && MateriaCount == other.MateriaCount)
|
||||
{
|
||||
return Stats.SequenceEqual(other.Stats, new KeyValuePairComparer());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(MateriaCount, Stats);
|
||||
}
|
||||
}
|
37
LLib/LLib.Gear/ExtendedBaseParam.cs
Normal file
37
LLib/LLib.Gear/ExtendedBaseParam.cs
Normal file
|
@ -0,0 +1,37 @@
|
|||
using Lumina.Excel;
|
||||
using Lumina.Excel.Sheets;
|
||||
|
||||
namespace LLib.Gear;
|
||||
|
||||
[Sheet("BaseParam")]
|
||||
public readonly struct ExtendedBaseParam : IExcelRow<ExtendedBaseParam>
|
||||
{
|
||||
private const int ParamCount = 23;
|
||||
|
||||
public uint RowId => _003Crow_003EP;
|
||||
|
||||
public BaseParam BaseParam => new BaseParam(_003Cpage_003EP, _003Coffset_003EP, _003Crow_003EP);
|
||||
|
||||
public unsafe Collection<ushort> EquipSlotCategoryPct => new Collection<ushort>(_003Cpage_003EP, _003Coffset_003EP, _003Coffset_003EP, (delegate*<ExcelPage, uint, uint, uint, ushort>)(&EquipSlotCategoryPctCtor), 23);
|
||||
|
||||
public ExtendedBaseParam(ExcelPage page, uint offset, uint row)
|
||||
{
|
||||
_003Cpage_003EP = page;
|
||||
_003Coffset_003EP = offset;
|
||||
_003Crow_003EP = row;
|
||||
}
|
||||
|
||||
private static ushort EquipSlotCategoryPctCtor(ExcelPage page, uint parentOffset, uint offset, uint i)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
return page.ReadUInt16(offset + 8 + (i - 1) * 2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static ExtendedBaseParam Create(ExcelPage page, uint offset, uint row)
|
||||
{
|
||||
return new ExtendedBaseParam(page, offset, row);
|
||||
}
|
||||
}
|
219
LLib/LLib.Gear/GearStatsCalculator.cs
Normal file
219
LLib/LLib.Gear/GearStatsCalculator.cs
Normal file
|
@ -0,0 +1,219 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||
using Lumina.Excel;
|
||||
using Lumina.Excel.Sheets;
|
||||
|
||||
namespace LLib.Gear;
|
||||
|
||||
public sealed class GearStatsCalculator
|
||||
{
|
||||
private sealed record MateriaInfo(EBaseParam BaseParam, Collection<short> Values, bool HasItem);
|
||||
|
||||
private const uint EternityRingItemId = 8575u;
|
||||
|
||||
private static readonly uint[] CanHaveOffhand = new uint[14]
|
||||
{
|
||||
2u, 6u, 8u, 12u, 14u, 16u, 18u, 20u, 22u, 24u,
|
||||
26u, 28u, 30u, 32u
|
||||
};
|
||||
|
||||
private readonly ExcelSheet<Item> _itemSheet;
|
||||
|
||||
private readonly Dictionary<(uint ItemLevel, EBaseParam BaseParam), ushort> _itemLevelStatCaps = new Dictionary<(uint, EBaseParam), ushort>();
|
||||
|
||||
private readonly Dictionary<(EBaseParam BaseParam, int EquipSlotCategory), ushort> _equipSlotCategoryPct;
|
||||
|
||||
private readonly Dictionary<uint, MateriaInfo> _materiaStats;
|
||||
|
||||
public GearStatsCalculator(IDataManager? dataManager)
|
||||
: this(dataManager?.GetExcelSheet<ItemLevel>() ?? throw new ArgumentNullException("dataManager"), dataManager.GetExcelSheet<ExtendedBaseParam>(), dataManager.GetExcelSheet<Materia>(), dataManager.GetExcelSheet<Item>())
|
||||
{
|
||||
}
|
||||
|
||||
public GearStatsCalculator(ExcelSheet<ItemLevel> itemLevelSheet, ExcelSheet<ExtendedBaseParam> baseParamSheet, ExcelSheet<Materia> materiaSheet, ExcelSheet<Item> itemSheet)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(itemLevelSheet, "itemLevelSheet");
|
||||
ArgumentNullException.ThrowIfNull(baseParamSheet, "baseParamSheet");
|
||||
ArgumentNullException.ThrowIfNull(materiaSheet, "materiaSheet");
|
||||
ArgumentNullException.ThrowIfNull(itemSheet, "itemSheet");
|
||||
_itemSheet = itemSheet;
|
||||
foreach (ItemLevel item in itemLevelSheet)
|
||||
{
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.Strength)] = item.Strength;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.Dexterity)] = item.Dexterity;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.Vitality)] = item.Vitality;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.Intelligence)] = item.Intelligence;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.Mind)] = item.Mind;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.Piety)] = item.Piety;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.GP)] = item.GP;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.CP)] = item.CP;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.DamagePhys)] = item.PhysicalDamage;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.DamageMag)] = item.MagicalDamage;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.DefensePhys)] = item.Defense;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.DefenseMag)] = item.MagicDefense;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.Tenacity)] = item.Tenacity;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.Crit)] = item.CriticalHit;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.DirectHit)] = item.DirectHitRate;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.Determination)] = item.Determination;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.SpellSpeed)] = item.SpellSpeed;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.SkillSpeed)] = item.SkillSpeed;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.Gathering)] = item.Gathering;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.Perception)] = item.Perception;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.Craftsmanship)] = item.Craftsmanship;
|
||||
_itemLevelStatCaps[(item.RowId, EBaseParam.Control)] = item.Control;
|
||||
}
|
||||
_equipSlotCategoryPct = baseParamSheet.SelectMany((ExtendedBaseParam x) => from y in Enumerable.Range(0, x.EquipSlotCategoryPct.Count)
|
||||
select ((EBaseParam)x.RowId, y: y, x.EquipSlotCategoryPct[y])).ToDictionary(((EBaseParam, int y, ushort) x) => (x.Item1, x.y), ((EBaseParam, int y, ushort) x) => x.Item3);
|
||||
_materiaStats = materiaSheet.Where((Materia x) => x.RowId != 0 && x.BaseParam.RowId != 0).ToDictionary((Materia x) => x.RowId, (Materia x) => new MateriaInfo((EBaseParam)x.BaseParam.RowId, x.Value, x.Item[0].RowId != 0));
|
||||
}
|
||||
|
||||
public unsafe EquipmentStats CalculateGearStats(InventoryItem* item)
|
||||
{
|
||||
List<(uint, byte)> list = new List<(uint, byte)>();
|
||||
byte b = 0;
|
||||
if (item->ItemId != 8575)
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
ushort num = item->Materia[i];
|
||||
if (num != 0)
|
||||
{
|
||||
b++;
|
||||
list.Add((num, item->MateriaGrades[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
return CalculateGearStats(_itemSheet.GetRow(item->ItemId), item->Flags.HasFlag(InventoryItem.ItemFlags.HighQuality), list)with
|
||||
{
|
||||
MateriaCount = b
|
||||
};
|
||||
}
|
||||
|
||||
public EquipmentStats CalculateGearStats(Item item, bool highQuality, IReadOnlyList<(uint MateriaId, byte Grade)> materias)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(materias, "materias");
|
||||
Dictionary<EBaseParam, StatInfo> dictionary = new Dictionary<EBaseParam, StatInfo>();
|
||||
for (int i = 0; i < item.BaseParam.Count; i++)
|
||||
{
|
||||
AddEquipmentStat(dictionary, item.BaseParam[i], item.BaseParamValue[i]);
|
||||
}
|
||||
if (highQuality)
|
||||
{
|
||||
for (int j = 0; j < item.BaseParamSpecial.Count; j++)
|
||||
{
|
||||
AddEquipmentStat(dictionary, item.BaseParamSpecial[j], item.BaseParamValueSpecial[j]);
|
||||
}
|
||||
}
|
||||
foreach (var materia in materias)
|
||||
{
|
||||
if (_materiaStats.TryGetValue(materia.MateriaId, out MateriaInfo value))
|
||||
{
|
||||
AddMateriaStat(item, dictionary, value, materia.Grade);
|
||||
}
|
||||
}
|
||||
return new EquipmentStats(dictionary, 0);
|
||||
}
|
||||
|
||||
private static void AddEquipmentStat(Dictionary<EBaseParam, StatInfo> result, RowRef<BaseParam> baseParam, short value)
|
||||
{
|
||||
if (baseParam.RowId != 0)
|
||||
{
|
||||
if (result.TryGetValue((EBaseParam)baseParam.RowId, out StatInfo value2))
|
||||
{
|
||||
result[(EBaseParam)baseParam.RowId] = value2 with
|
||||
{
|
||||
EquipmentValue = (short)(value2.EquipmentValue + value)
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
result[(EBaseParam)baseParam.RowId] = new StatInfo(value, 0, Overcapped: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddMateriaStat(Item item, Dictionary<EBaseParam, StatInfo> result, MateriaInfo materiaInfo, short grade)
|
||||
{
|
||||
if (!result.TryGetValue(materiaInfo.BaseParam, out StatInfo value))
|
||||
{
|
||||
value = (result[materiaInfo.BaseParam] = new StatInfo(0, 0, Overcapped: false));
|
||||
}
|
||||
if (materiaInfo.HasItem)
|
||||
{
|
||||
short num = (short)(GetMaximumStatValue(item, materiaInfo.BaseParam) - value.EquipmentValue);
|
||||
if (value.MateriaValue + materiaInfo.Values[grade] > num)
|
||||
{
|
||||
result[materiaInfo.BaseParam] = value with
|
||||
{
|
||||
MateriaValue = num,
|
||||
Overcapped = true
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
result[materiaInfo.BaseParam] = value with
|
||||
{
|
||||
MateriaValue = (short)(value.MateriaValue + materiaInfo.Values[grade])
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result[materiaInfo.BaseParam] = value with
|
||||
{
|
||||
MateriaValue = (short)(value.MateriaValue + materiaInfo.Values[grade])
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public short GetMaximumStatValue(Item item, EBaseParam baseParamValue)
|
||||
{
|
||||
if (_itemLevelStatCaps.TryGetValue((item.LevelItem.RowId, baseParamValue), out var value))
|
||||
{
|
||||
return (short)Math.Round((float)(value * _equipSlotCategoryPct[(baseParamValue, (int)item.EquipSlotCategory.RowId)]) / 1000f, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public unsafe short CalculateAverageItemLevel(InventoryContainer* container)
|
||||
{
|
||||
uint num = 0u;
|
||||
int num2 = 12;
|
||||
for (int i = 0; i < 13; i++)
|
||||
{
|
||||
if (i == 5)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
InventoryItem* inventorySlot = container->GetInventorySlot(i);
|
||||
if (inventorySlot == null || inventorySlot->ItemId == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Item? rowOrDefault = _itemSheet.GetRowOrDefault(inventorySlot->ItemId);
|
||||
if (!rowOrDefault.HasValue)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (rowOrDefault.Value.ItemUICategory.RowId == 105)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
num2--;
|
||||
}
|
||||
num2--;
|
||||
continue;
|
||||
}
|
||||
if (i == 0 && !CanHaveOffhand.Contains(rowOrDefault.Value.ItemUICategory.RowId))
|
||||
{
|
||||
num += rowOrDefault.Value.LevelItem.RowId;
|
||||
i++;
|
||||
}
|
||||
num += rowOrDefault.Value.LevelItem.RowId;
|
||||
}
|
||||
return (short)(num / num2);
|
||||
}
|
||||
}
|
6
LLib/LLib.Gear/StatInfo.cs
Normal file
6
LLib/LLib.Gear/StatInfo.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace LLib.Gear;
|
||||
|
||||
public sealed record StatInfo(short EquipmentValue, short MateriaValue, bool Overcapped)
|
||||
{
|
||||
public short TotalValue => (short)(EquipmentValue + MateriaValue);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue