This commit is contained in:
warawanaidene
2023-09-13 15:16:25 -04:00
parent 22e6f9e76c
commit 19b9199721
21 changed files with 703 additions and 0 deletions

62
Globals.cs Normal file
View File

@@ -0,0 +1,62 @@
using EFT.InventoryLogic;
using EFT.UI;
using System.Collections.Generic;
namespace ShowMeTheStats
{
public static class Globals
{
public static bool isWeaponModding = false;
public static Item mod = null;
public static List<Slot> allSlots = new List<Slot>();
public static SimpleTooltip simpleTooltip = null;
//public static Slot slotType = null;
public static Item dropDownCurrentItem = null;
public static bool isKeyPressed = false;
//some stats are not very interesting to see and will clog up the ui more than anything, so we blacklist them
public static string[] statBlacklist = {
EItemAttributeId.CompatibleWith.ToString(),
EItemAttributeId.Weight.ToString(),
EItemAttributeId.Size.ToString(),
//EItemAttributeId.Caliber.ToString(),
//EItemAttributeId.BulletSpeed.ToString(),
EItemAttributeId.RaidModdable.ToString(),
EItemAttributeId.OpticCrate.ToString(),
//EItemAttributeId.EffectiveDist.ToString(),
//EItemAttributeId.EffectiveDistance.ToString(),
//EItemAttributeId.Velocity.ToString(),
EItemAttributeId.SightingRange.ToString(),
//EItemAttributeId.AmmoCaliber.ToString(),
EItemAttributeId.SingleFireRate.ToString(),
EItemAttributeId.FireRate.ToString(),
EItemAttributeId.DurabilityBurn.ToString(),
EItemAttributeId.HeatFactor.ToString(),
EItemAttributeId.CoolFactor.ToString(),
EItemAttributeId.MalfFeedChance.ToString(),
EItemAttributeId.MalfMisfireChance.ToString(),
EItemAttributeId.LoadUnloadSpeed.ToString(),
EItemAttributeId.CheckTimeSpeed.ToString(),
"AutoROF",
"SemiROF",
};
public static void ClearAllGlobals()
{
isWeaponModding = false;
mod = null;
allSlots.Clear();
simpleTooltip = null;
//slotType = null;
dropDownCurrentItem = null;
isKeyPressed = false;
}
}
}

44
Plugin.cs Normal file
View File

@@ -0,0 +1,44 @@
using BepInEx;
using ShowMeTheStats;
using UnityEngine;
namespace Plugin
{
[BepInPlugin("com.moddingstatshelper.aki", "Modding Stats Helper", "1.0")]
public class Plugin : BaseUnityPlugin
{
void Awake()
{
new ItemShowTooltipPatch().Enable();
new ShowTooltipPatch().Enable();
new WeaponUpdatePatch().Enable();
new DropDownSlotContextPatch().Enable();
new SlotViewPatch().Enable();
new ScreenTypePatch().Enable();
new DropDownSlotContextClosePatch().Enable();
}
void Update()
{
if (Globals.isWeaponModding)
{
bool isKeyDown = Input.GetKey(KeyCode.LeftControl);
if (isKeyDown && !Globals.isKeyPressed)
{
Globals.isKeyPressed = true;
Globals.simpleTooltip.Show("", null, 0.1f, null);
}
else if (!isKeyDown && Globals.isKeyPressed)
{
Globals.isKeyPressed = false;
Globals.simpleTooltip.Show("", null, 0.1f, null);
}
}
}
}
}

61
ShowMeTheStats.csproj Normal file
View File

@@ -0,0 +1,61 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<AssemblyName>Wara-ModdingStatsHelper</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<Optimize>False</Optimize>
</PropertyGroup>
<ItemGroup>
<Reference Include="Aki.Common">
<HintPath>dependencies\Aki.Common.dll</HintPath>
</Reference>
<Reference Include="Aki.Reflection">
<HintPath>dependencies\Aki.Reflection.dll</HintPath>
</Reference>
<Reference Include="Assembly-CSharp">
<HintPath>dependencies\Assembly-CSharp.dll</HintPath>
</Reference>
<Reference Include="BepInEx">
<HintPath>dependencies\BepInEx.dll</HintPath>
</Reference>
<Reference Include="BepInEx.Harmony">
<HintPath>dependencies\BepInEx.Harmony.dll</HintPath>
</Reference>
<Reference Include="Comfort">
<HintPath>dependencies\Comfort.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>dependencies\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="Sirenix.Serialization">
<HintPath>dependencies\Sirenix.Serialization.dll</HintPath>
</Reference>
<Reference Include="System.Windows.Forms" />
<Reference Include="UnityEngine">
<HintPath>dependencies\Unity.TextMeshPro.dll</HintPath>
</Reference>
<Reference Include="UnityEngine">
<HintPath>dependencies\UnityEngine.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.CoreModule">
<HintPath>dependencies\UnityEngine.CoreModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.AudioModule">
<HintPath>dependencies\UnityEngine.AudioModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.InputModule">
<HintPath>dependencies\UnityEngine.InputModule.dll</HintPath>
</Reference>
<Reference Include="UnityEngine.InputLegacyModule">
<HintPath>dependencies\UnityEngine.InputLegacyModule.dll</HintPath>
</Reference>
<Reference Include="System.IO">
<HintPath>dependencies\System.Runtime.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

30
ShowMeTheStats.sln Normal file
View File

@@ -0,0 +1,30 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.6.33723.286
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ShowMeTheStats", "ShowMeTheStats.csproj", "{06EE05FF-852D-47D1-882B-E8D3044A382E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{4CC091D9-5745-4098-AC8B-1CB99EE9A1DA}"
ProjectSection(SolutionItems) = preProject
Complete Gameplay Overhaul.sln = Complete Gameplay Overhaul.sln
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{06EE05FF-852D-47D1-882B-E8D3044A382E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{06EE05FF-852D-47D1-882B-E8D3044A382E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{06EE05FF-852D-47D1-882B-E8D3044A382E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{06EE05FF-852D-47D1-882B-E8D3044A382E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DF3EE3DB-0374-47BD-BC99-2ED85C439AC8}
EndGlobalSection
EndGlobal

302
ShowMeTheStatsPatches.cs Normal file
View File

@@ -0,0 +1,302 @@
using Aki.Reflection.Patching;
using EFT.InventoryLogic;
using System.Reflection;
using EFT.UI;
using EFT.UI.Screens;
using System.Collections.Generic;
using static ShowMeTheStats.Utils;
using EFT.UI.DragAndDrop;
using System;
using System.Linq;
using EFT.UI.WeaponModding;
namespace ShowMeTheStats
{
public class ItemShowTooltipPatch : ModulePatch
{
// we set the item we are hovering in the globals
protected override MethodBase GetTargetMethod()
{
return typeof(GridItemView).GetMethod("ShowTooltip", BindingFlags.Instance | BindingFlags.NonPublic);
}
[PatchPrefix]
static void Prefix(GridItemView __instance)
{
if (Globals.isWeaponModding)
{
Globals.mod = __instance.Item;
}
}
}
public class ShowTooltipPatch : ModulePatch
{
// the spaghetti starts here.
protected override MethodBase GetTargetMethod()
{
return typeof(SimpleTooltip).GetMethod("Show", BindingFlags.Instance | BindingFlags.Public);
}
[PatchPrefix]
static void Prefix(ref string text, ref float delay, SimpleTooltip __instance)
{
if (Globals.isWeaponModding)
{
// checks for a bug
if (text.Contains("EQUIPPED") || text.Contains("STASH"))
{
return;
}
if (Globals.mod.Attributes != null)
{
delay = 0.1f;
Globals.simpleTooltip = __instance;
string firstString = "";
string finalString = "";
bool isSameStats = true;
bool hoveringSlottedMod = Globals.allSlots.Any(a => a.ContainedItem == Globals.mod);
if (Globals.isKeyPressed && !hoveringSlottedMod)
{
firstString = "<mspace=0.55em><color=#52ffd9>STATS</color></mspace> → <size=65%><mspace=0.4875em><color=#fc7b03>COMPARISON</color></mspace> (CTRL)</size><br>";
}
else if (!hoveringSlottedMod && Globals.dropDownCurrentItem != null)
{
firstString = "<mspace=0.55em><color=#fc7b03>COMPARISON</color></mspace> → <size=65%><mspace=0.4475em><color=#52ffd9>STATS</color></mspace> (CTRL)</size><br>";
}
// IF WE ARE NOT COMPARING
if (hoveringSlottedMod || Globals.isKeyPressed || Globals.dropDownCurrentItem == null)
{
List<ItemAttributeClass> attributes = GetAllAttributesNotInBlacklist(Globals.mod.Attributes);
foreach (var attribute in attributes)
{
if (attribute.Base() != 0)
{
string stringColor = "#ffffff";
string stringValue = attribute.StringValue();
string stringDisplayname = AlignTextToWidth(attribute.DisplayName.Trim() + ":");
stringValue = AddOperatorToStringValue(attribute.StringValue(), attribute.Base(), false);
stringColor = GetValueColor(attribute.Base(), attribute.LessIsGood, attribute.LabelVariations, false);
if (!stringValue.Contains("MOA")) // MOA is annoying to deal with
{
string attributeLine = $"<mspace=0.55em>{stringDisplayname}</mspace><color={stringColor}>{stringValue}</color><br>";
finalString += attributeLine;
isSameStats = false;
}
}
}
}
// IF WE ARE COMPARING
else if (!hoveringSlottedMod)
{
List<ItemAttributeClass> replacingAttributes = GetAllAttributesNotInBlacklist(Globals.mod.Attributes);
List<ItemAttributeClass> slottedAttributes = GetAllAttributesNotInBlacklist(Globals.dropDownCurrentItem.Attributes);
List<string> replacingAttributesDisplayed = new List<string>();
foreach (var slottedAttribute in slottedAttributes)
{
if (slottedAttribute.Base() != 0)
{
//if (slottedAttribute.Id.ToString() == EItemAttributeId.MalfMisfireChance.ToString())
//{
// break;
//}
string stringDisplayname = AlignTextToWidth(slottedAttribute.DisplayName.Trim() + ":");
ItemAttributeClass replacingAttribute = replacingAttributes.Where(a => a.Id.ToString() == slottedAttribute.Id.ToString()).SingleOrDefault();
if (replacingAttribute != null && replacingAttribute.Base() != 0)
{
// check if there's a difference in comparison or same stats
float substractedBases = slottedAttribute.Base() - replacingAttribute.Base();
bool isZero = Math.Abs(substractedBases) < float.Epsilon;
if (!isZero)
{
// we do the substract stuff here (this is the wrong way to do it. I should use Base(), but w/e.)
string substractedAttributeStringValue = SubstractStringValue(slottedAttribute.StringValue(), replacingAttribute.StringValue());
substractedAttributeStringValue = SpaghettiLastStringValueOperatorCheck(substractedAttributeStringValue, substractedBases);
string stringColor = GetValueColor(substractedBases, slottedAttribute.LessIsGood, slottedAttribute.LabelVariations, true);
if (!substractedAttributeStringValue.Contains("MOA")) // MOA is annoying to deal with
{
string attributeLine = $"<mspace=0.55em>{stringDisplayname}</mspace><color={stringColor}>{substractedAttributeStringValue}</color><br>";
finalString += attributeLine;
isSameStats = false;
}
}
}
else
{
string stringValue = AddOperatorToStringValue(slottedAttribute.StringValue(), slottedAttribute.Base(), false);
string stringColor = GetValueColor(slottedAttribute.Base(), slottedAttribute.LessIsGood, slottedAttribute.LabelVariations, true);
// should use reverse bool on AddOperatorToStringValue, but there was a bug IIRC, so I use this patchy method instead
stringValue = ReverseOperator(stringValue);
if (!stringValue.Contains("MOA")) // MOA is annoying to deal with
{
string attributeLine = $"<mspace=0.55em>{stringDisplayname}</mspace><color={stringColor}>{stringValue}</color><br>";
finalString += attributeLine;
isSameStats = false;
}
}
}
replacingAttributesDisplayed.Add(slottedAttribute.Id.ToString());
}
// for attributes that are not compared, just added or removed by changing the part.
foreach (var attribute in replacingAttributes)
{
if (!replacingAttributesDisplayed.Contains(attribute.Id.ToString()))
{
string stringDisplayname = AlignTextToWidth(attribute.DisplayName.Trim() + ":");
string stringValue = AddOperatorToStringValue(attribute.StringValue(), attribute.Base(), false);
string stringColor = GetValueColor(attribute.Base(), attribute.LessIsGood, attribute.LabelVariations, false);
if (!stringValue.Contains("MOA")) // MOA is annoying to deal with
{
string attributeLine = $"<mspace=0.55em>{stringDisplayname}</mspace><color={stringColor}>{stringValue}</color><br>";
finalString += attributeLine;
isSameStats = false;
}
}
}
}
if (finalString != "" || firstString != "")
{
if (firstString == "")
{
firstString = "<mspace=0.55em><color=#52ffd9>STATS</color></mspace><br>";
}
if (isSameStats && firstString.Contains("COMPARISON") && !Globals.isKeyPressed)
{
finalString += "<color=#39ff2b>SAME STATS</color><br>";
}
text = firstString + finalString;
}
}
}
}
}
public class WeaponUpdatePatch : ModulePatch
{
protected override MethodBase GetTargetMethod()
{
return typeof(EditBuildScreen).GetMethod("WeaponUpdate", BindingFlags.Instance | BindingFlags.NonPublic);
}
[PatchPrefix]
static void Prefix()
{
Globals.allSlots.Clear();
}
}
public class DropDownSlotContextPatch : ModulePatch
{
protected override MethodBase GetTargetMethod()
{
return typeof(DropDownMenu).GetMethod("Show", BindingFlags.Instance | BindingFlags.Public);
}
[PatchPrefix]
static void Prefix(ModdingScreenSlotView slotView)
{
FieldInfo fieldInfo = typeof(ModdingScreenSlotView).GetField("slot_0", BindingFlags.Instance | BindingFlags.NonPublic);
if (fieldInfo != null)
{
Slot slot_0 = (Slot)fieldInfo.GetValue(slotView);
if (slot_0.ContainedItem != null)
{
Globals.dropDownCurrentItem = slot_0.ContainedItem;
//Globals.slotType = slot_0;
}
}
}
}
public class DropDownSlotContextClosePatch : ModulePatch
{
protected override MethodBase GetTargetMethod()
{
return typeof(DropDownMenu).GetMethod("Close", BindingFlags.Instance | BindingFlags.Public);
}
[PatchPrefix]
static void Prefix()
{
if (Globals.isWeaponModding)
{
Globals.dropDownCurrentItem = null;
}
}
}
public class SlotViewPatch : ModulePatch
{
protected override MethodBase GetTargetMethod()
{
return typeof(ModdingScreenSlotView).GetMethod("Show", BindingFlags.Instance | BindingFlags.Public);
}
[PatchPrefix]
static void Prefix(Slot slot)
{
if (slot.ContainedItem != null)
{
Globals.allSlots.Add(slot);
}
}
}
public class ScreenTypePatch : ModulePatch
{
protected override MethodBase GetTargetMethod()
{
return typeof(MenuTaskBar).GetMethod("OnScreenChanged", BindingFlags.Instance | BindingFlags.Public);
}
[PatchPrefix]
static void Prefix(EEftScreenType eftScreenType)
{
if (eftScreenType == EEftScreenType.EditBuild || eftScreenType == EEftScreenType.WeaponModding)
{
Globals.isWeaponModding = true;
return;
}
if (Globals.isWeaponModding)
{
if (eftScreenType != EEftScreenType.EditBuild || eftScreenType != EEftScreenType.WeaponModding)
{
Globals.ClearAllGlobals();
}
}
}
}
}

204
Utils.cs Normal file
View File

@@ -0,0 +1,204 @@
using EFT.InventoryLogic;
using System.Collections.Generic;
using System.Linq;
namespace ShowMeTheStats
{
public static class Utils
{
public static string AlignTextToWidth(string text)
{
int spaces = 20;
int currentLength = text.Length;
int newLength = spaces - currentLength;
if (currentLength < spaces)
{
for (int i = 0; i < newLength; i++)
{
text += " ";
}
}
return text;
}
public static string[] getDigitsFromStringValues(string attributeStringValue)
{
string extractedDigits = "";
string extractedOperator = "";
string extractedType = "";
foreach (char c in attributeStringValue)
{
if (char.IsDigit(c) || c == '.')
{
extractedDigits += c.ToString();
}
else if (c == '-' || c == '+')
{
extractedOperator = c.ToString();
}
else if (c == '%' || c == 'M' || c == 'O' || c == 'A') // lazy ahh MOA extraction
{
extractedType += c.ToString();
}
}
if (extractedType == "MOA")
{
extractedType = " " + extractedType;
}
string[] final = { extractedOperator, extractedDigits, extractedType };
return final;
}
public static string SubstractStringValue(string slottedAttributeStringValue, string replacingAttributeStringValue)
{
//[0] = operator
//[1] = float
//[2] = "%" string
string[] slottingAttributeExtracted = getDigitsFromStringValues(slottedAttributeStringValue);
string[] replacingAttributeExtracted = getDigitsFromStringValues(replacingAttributeStringValue);
string subtracted = (float.Parse(replacingAttributeExtracted[0] + replacingAttributeExtracted[1]) - float.Parse(slottingAttributeExtracted[0] + slottingAttributeExtracted[1])).ToString(); //"F1"
return subtracted + replacingAttributeExtracted[2];
}
public static string GetValueColor(float numBase, bool LessIsGood, EItemAttributeLabelVariations labelVariation, bool reversed)
{
string blueColor = "#54c1ff";
string redColor = "#c40000";
string textColor = "";
if (labelVariation == EItemAttributeLabelVariations.Colored)
{
if (numBase < 0f)
{
if (LessIsGood)
{
textColor = blueColor;
}
else if (!LessIsGood)
{
textColor = redColor;
}
}
else if (numBase > 0f)
{
if (LessIsGood)
{
textColor = redColor;
}
else if (!LessIsGood)
{
textColor = blueColor;
}
}
if (reversed)
{
if (textColor == blueColor)
{
textColor = redColor;
}
else if (textColor == redColor)
{
textColor = blueColor;
}
}
}
else
{
textColor = "#ffffff"; // white
}
return textColor;
}
public static string ReverseOperator(string stringValue)
{
if (stringValue.Contains("-"))
{
stringValue = stringValue.Replace("-", "+");
}
else if (stringValue.Contains("+"))
{
stringValue = stringValue.Replace("+", "-");
}
return stringValue;
}
public static string SpaghettiLastStringValueOperatorCheck(string attributeStringValue, float attributeBase)
{
if (attributeStringValue.Trim().ToUpper().Contains("LOUDNESS") || attributeStringValue.Trim().ToUpper().Contains("MOA") || attributeStringValue.Trim().ToUpper().Contains("MAX COUNT"))
{
if (attributeStringValue.Contains("+"))
{
attributeStringValue = attributeStringValue.Replace("+", "");
}
return attributeStringValue;
}
else if (!attributeStringValue.Contains("+") && !attributeStringValue.Contains("-"))
{
return "+" + attributeStringValue;
}
return attributeStringValue;
}
public static string AddOperatorToStringValue(string attributeStringValue, float attributeBase, bool reversed)
{
if (attributeBase < 0f)
{
if (!attributeStringValue.Contains("-"))
{
if (!reversed)
{
attributeStringValue = "-" + attributeStringValue;
}
else if (reversed)
{
attributeStringValue = "+" + attributeStringValue;
}
}
}
else
{
if (!reversed)
{
attributeStringValue = "+" + attributeStringValue;
}
else if (reversed)
{
attributeStringValue = "-" + attributeStringValue;
}
}
return attributeStringValue;
}
public static List<ItemAttributeClass> GetAllAttributesNotInBlacklist(List<ItemAttributeClass> attributes)
{
List<ItemAttributeClass> attributesResult = new List<ItemAttributeClass>();
foreach (var attribute in attributes)
{
if (!Globals.statBlacklist.Any(x => x == attribute.Id.ToString()))
{
attributesResult.Add(attribute);
}
}
return attributesResult;
}
}
}

BIN
dependencies/Aki.Common.dll vendored Normal file

Binary file not shown.

BIN
dependencies/Aki.Reflection.dll vendored Normal file

Binary file not shown.

BIN
dependencies/Assembly-CSharp.dll vendored Normal file

Binary file not shown.

BIN
dependencies/BepInEx.Harmony.dll vendored Normal file

Binary file not shown.

BIN
dependencies/BepInEx.dll vendored Normal file

Binary file not shown.

BIN
dependencies/Comfort.dll vendored Normal file

Binary file not shown.

BIN
dependencies/EFTApi.dll vendored Normal file

Binary file not shown.

BIN
dependencies/Newtonsoft.Json.dll vendored Normal file

Binary file not shown.

BIN
dependencies/Sirenix.Serialization.dll vendored Normal file

Binary file not shown.

BIN
dependencies/Unity.TextMeshPro.dll vendored Normal file

Binary file not shown.

BIN
dependencies/UnityEngine.AudioModule.dll vendored Normal file

Binary file not shown.

BIN
dependencies/UnityEngine.CoreModule.dll vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
dependencies/UnityEngine.InputModule.dll vendored Normal file

Binary file not shown.

BIN
dependencies/UnityEngine.dll vendored Normal file

Binary file not shown.