using HarmonyLib;
namespace SCore.Features.Quality.Harmony
{
///
/// Patches XUiM_ItemStack.GetCustomDisplayValueForItem to fix display_value tier lookups
/// when using extended quality ranges (1-600 instead of vanilla 1-6).
///
/// The vanilla method calls GetValue((int)itemValue.Quality), which uses quality as a
/// direct array index (quality - 1). With raw quality 1-600 this produces an out-of-bounds
/// index on a 6-element value array and always returns 0, so item tooltips show
/// the wrong value for any display_value that uses a tier list.
///
/// The fix mirrors what MinEventModifyCVar.cs already does for triggered effects:
/// convert the raw quality to a tier (1-6) via QualityUtils.CalculateTier() before
/// passing it to GetValue().
///
[HarmonyPatch(typeof(XUiM_ItemStack))]
[HarmonyPatch(nameof(XUiM_ItemStack.GetCustomDisplayValueForItem))]
public class XUiMItemStackGetCustomDisplayValueForItem
{
private static readonly string AdvFeatureClass = "AdvancedItemFeatures";
private static readonly string Feature = "CustomQualityLevels";
public static bool Prefix(ref float __result, ItemValue itemValue, DisplayInfoEntry entry)
{
if (!Configuration.CheckFeatureStatus(AdvFeatureClass, Feature)) return true;
// Guard: let vanilla handle null/empty items and items without a class.
if (itemValue == null || itemValue.IsEmpty() || itemValue.ItemClass == null) return true;
// Convert raw quality (e.g. 350) to a vanilla-compatible tier (1-6).
// GetValue() treats the argument as an array index (tier - 1), so passing
// the raw quality would read far out of bounds and return 0.
int tier = QualityUtils.CalculateTier((int)itemValue.Quality);
// For non-quality items quality == 0 → tier == 0; GetValue(0) would be index -1.
// Let vanilla handle those.
if (tier == 0) return true;
float newValue = 0f;
MinEffectController effects = itemValue.ItemClass.Effects;
var list = effects?.EffectGroups;
if (list == null)
{
__result = newValue;
return false;
}
for (int i = 0; i < list.Count; i++)
{
MinEffectGroup minEffectGroup = list[i];
MinEventParams.CachedEventParam.ItemValue = itemValue;
MinEventParams.CachedEventParam.Seed = (int)itemValue.Seed;
if (minEffectGroup.EffectDisplayValues.ContainsKey(entry.CustomName) &&
minEffectGroup.EffectDisplayValues[entry.CustomName].IsValid(MinEventParams.CachedEventParam))
{
// Use tier (1-6) instead of raw quality (1-600)
newValue += minEffectGroup.EffectDisplayValues[entry.CustomName].GetValue(tier);
}
var primaryEffects = minEffectGroup.GetTriggeredEffects(MinEventTypes.onSelfPrimaryActionEnd);
if (primaryEffects != null)
foreach (var action in primaryEffects)
AddValueIfValid(action, entry, ref newValue);
var secondaryEffects = minEffectGroup.GetTriggeredEffects(MinEventTypes.onSelfSecondaryActionEnd);
if (secondaryEffects != null)
foreach (var action in secondaryEffects)
AddValueIfValid(action, entry, ref newValue);
}
__result = newValue;
return false;
}
// Replicates the vanilla compiler-generated local function
// g__AddValueForDisplayIfValid|15_0
private static void AddValueIfValid(MinEventActionBase actionBase, DisplayInfoEntry entry, ref float newValue)
{
if (!(actionBase is MinEventActionModifyCVar modifyCVar)) return;
if (!actionBase.CanExecute(actionBase.EventType, MinEventParams.CachedEventParam)) return;
if (modifyCVar.cvarName == entry.CustomName &&
modifyCVar.targetType == MinEventActionTargetedBase.TargetTypes.self)
{
newValue += modifyCVar.GetValueForDisplay();
}
}
}
}