Compare commits

...

5 Commits
3.9.0 ... main

Author SHA1 Message Date
438b0fdb9e Updated for client version 0.15.5.1 33420 2024-12-02 15:59:14 +02:00
ec50b42dc7 style 2024-10-25 20:55:26 +03:00
7183b28e8b Fix potential issue with max values 2024-10-23 17:07:49 +03:00
ccad3d23d0 Add missing ammo attributes 2024-10-23 00:49:39 +03:00
0f7ee0b1e3 VS junk 2024-07-13 03:51:11 +03:00
18 changed files with 426 additions and 210 deletions

View File

@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net472</TargetFramework> <TargetFramework>net472</TargetFramework>
<AssemblyName>IcyClawz.CustomInteractions.Prepatch</AssemblyName> <AssemblyName>IcyClawz.CustomInteractions.Prepatch</AssemblyName>
<Version>1.5.0</Version> <Version>1.6.0</Version>
<RootNamespace>IcyClawz.CustomInteractions</RootNamespace> <RootNamespace>IcyClawz.CustomInteractions</RootNamespace>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
</PropertyGroup> </PropertyGroup>

View File

@ -4,21 +4,17 @@ using EFT.UI;
using JetBrains.Annotations; using JetBrains.Annotations;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using UnityEngine; using UnityEngine;
using EmptyInteractionsAbstractClass = GClass3061; using EmptyInteractionsAbstractClass = GClass3423;
namespace IcyClawz.CustomInteractions; namespace IcyClawz.CustomInteractions;
[EditorBrowsable(EditorBrowsableState.Never)] public interface ICustomInteractionsProvider
public interface ICustomInteractionsProvider { } // Do not implement this directly
public interface IItemCustomInteractionsProvider : ICustomInteractionsProvider
{ {
IEnumerable<CustomInteraction> GetCustomInteractions(ItemUiContext uiContext, EItemViewType viewType, Item item); IEnumerable<CustomInteraction> GetCustomInteractions(ItemUiContext context, EItemViewType viewType, Item item);
} }
public static class CustomInteractionsManager public static class CustomInteractionsManager
@ -32,52 +28,35 @@ public static class CustomInteractionsManager
} }
} }
public class CustomInteraction() public class CustomInteraction(ItemUiContext context)
{ {
internal readonly CustomInteractionImpl Impl = new(UnityEngine.Random.Range(0, int.MaxValue).ToString("x4")); internal readonly CustomInteractionImpl Impl = new(context, UnityEngine.Random.Range(0, int.MaxValue).ToString("x4"));
public Func<string> Caption { get => Impl.Caption; set => Impl.Caption = value; } public Func<string> Caption { get => Impl.Caption; set => Impl.Caption = value; }
public Func<Sprite> Icon { get => Impl.Icon; set => Impl.Icon = value; } public Func<Sprite> Icon { get => Impl.Icon; set => Impl.Icon = value; }
public Action Action { get => Impl.Action; set => Impl.Action = value; } public Action Action { get => Impl.Action; set => Impl.Action = value; }
public Func<CustomSubInteractions> SubMenu { get => Impl.SubMenu; set => Impl.SubMenu = value; } public Func<IEnumerable<CustomInteraction>> SubMenu { get => Impl.SubMenu; set => Impl.SubMenu = value; }
public Func<bool> Enabled { get => Impl.Enabled; set => Impl.Enabled = value; } public Func<bool> Enabled { get => Impl.Enabled; set => Impl.Enabled = value; }
public Func<string> Error { get => Impl.Error; set => Impl.Error = value; } public Func<string> Error { get => Impl.Error; set => Impl.Error = value; }
} }
internal sealed class CustomInteractionImpl(string id) : DynamicInteractionClass(id, id) internal sealed class CustomInteractionImpl(ItemUiContext context, string id) : DynamicInteractionClass(id, id)
{ {
internal readonly ItemUiContext Context = context;
public Func<string> Caption { get; set; } public Func<string> Caption { get; set; }
public new Func<Sprite> Icon { get; set; } public new Func<Sprite> Icon { get; set; }
public Action Action { get => action_0; set => action_0 = value; } public Action Action { get => action_0; set => action_0 = value; }
public Func<CustomSubInteractions> SubMenu { get; set; } public Func<IEnumerable<CustomInteraction>> SubMenu { get; set; }
public Func<bool> Enabled { get; set; } public Func<bool> Enabled { get; set; }
public Func<string> Error { get; set; } public Func<string> Error { get; set; }
public bool IsInteractive() => Enabled?.Invoke() ?? true; public bool IsInteractive() => Enabled?.Invoke() ?? true;
} }
public abstract class CustomSubInteractions(ItemUiContext uiContext) internal sealed class CustomInteractionsImpl(ItemUiContext context) : EmptyInteractionsAbstractClass(context)
{
internal readonly CustomSubInteractionsImpl Impl = new(uiContext);
public bool ExaminationRequired
{
get => Impl.ExaminationRequiredInternal;
set => Impl.ExaminationRequiredInternal = value;
}
public void Add(CustomInteraction interaction) => Impl.AddCustomInteraction(interaction);
public void AddRange(IEnumerable<CustomInteraction> interactions) => interactions.ExecuteForEach(Impl.AddCustomInteraction);
public void Remove(CustomInteraction interaction) => Impl.RemoveCustomInteraction(interaction);
public void RemoveRange(IEnumerable<CustomInteraction> interactions) => interactions.ExecuteForEach(Impl.RemoveCustomInteraction);
}
internal sealed class CustomSubInteractionsImpl(ItemUiContext uiContext) : EmptyInteractionsAbstractClass(uiContext)
{ {
public IEnumerable<CustomInteractionImpl> CustomInteractions => DynamicInteractions.OfType<CustomInteractionImpl>(); public IEnumerable<CustomInteractionImpl> CustomInteractions => DynamicInteractions.OfType<CustomInteractionImpl>();
public bool ExaminationRequiredInternal { get; set; } = true;
public override bool ExaminationRequired => ExaminationRequiredInternal;
public override bool HasIcons => CustomInteractions.Any(interaction => interaction.Icon is not null); public override bool HasIcons => CustomInteractions.Any(interaction => interaction.Icon is not null);
} }
@ -87,11 +66,8 @@ internal static class AbstractInteractionsExtensions
typeof(ItemInfoInteractionsAbstractClass<T>).GetField("dictionary_0", BindingFlags.NonPublic | BindingFlags.Instance) typeof(ItemInfoInteractionsAbstractClass<T>).GetField("dictionary_0", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(instance) as Dictionary<string, DynamicInteractionClass>; .GetValue(instance) as Dictionary<string, DynamicInteractionClass>;
public static void AddCustomInteraction<T>(this ItemInfoInteractionsAbstractClass<T> instance, CustomInteraction interaction) where T : struct, Enum => public static void AddCustomInteraction<T>(this ItemInfoInteractionsAbstractClass<T> instance, CustomInteractionImpl impl) where T : struct, Enum =>
instance.GetDynamicInteractions()[interaction.Impl.Key] = interaction.Impl; instance.GetDynamicInteractions()[impl.Key] = impl;
public static void RemoveCustomInteraction<T>(this ItemInfoInteractionsAbstractClass<T> instance, CustomInteraction interaction) where T : struct, Enum =>
instance.GetDynamicInteractions().Remove(interaction.Impl.Key);
} }
internal static class InteractionButtonsContainerExtensions internal static class InteractionButtonsContainerExtensions
@ -140,6 +116,7 @@ internal static class InteractionButtonsContainerExtensions
public static void AddCustomButton(this InteractionButtonsContainer instance, CustomInteractionImpl impl) public static void AddCustomButton(this InteractionButtonsContainer instance, CustomInteractionImpl impl)
{ {
bool isInteractive = impl.IsInteractive(); bool isInteractive = impl.IsInteractive();
IEnumerable<CustomInteraction> subMenu = impl.SubMenu?.Invoke();
SimpleContextMenuButton button = null; SimpleContextMenuButton button = null;
button = instance.CreateButton( button = instance.CreateButton(
impl.Key, impl.Key,
@ -156,14 +133,15 @@ internal static class InteractionButtonsContainerExtensions
{ {
instance.SetCurrentButton(button); instance.SetCurrentButton(button);
instance.CloseSubMenu(); instance.CloseSubMenu();
if (isInteractive) if (isInteractive && subMenu != null)
{ {
CustomSubInteractions subMenu = impl.SubMenu?.Invoke(); CustomInteractionsImpl subInteractions = new CustomInteractionsImpl(impl.Context);
if (subMenu is not null) foreach (CustomInteractionImpl subImpl in subMenu.Select(item => item.Impl))
instance.SetSubInteractions(subMenu.Impl); subInteractions.AddCustomInteraction(subImpl);
instance.SetSubInteractions(subInteractions);
} }
}, },
impl.SubMenu is not null, subMenu?.Any() ?? false,
false false
); );
button.SetButtonInteraction( button.SetButtonInteraction(

View File

@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net472</TargetFramework> <TargetFramework>net472</TargetFramework>
<AssemblyName>IcyClawz.CustomInteractions</AssemblyName> <AssemblyName>IcyClawz.CustomInteractions</AssemblyName>
<Version>1.5.0</Version> <Version>1.6.0</Version>
<RootNamespace>IcyClawz.CustomInteractions</RootNamespace> <RootNamespace>IcyClawz.CustomInteractions</RootNamespace>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
</PropertyGroup> </PropertyGroup>

View File

@ -6,7 +6,7 @@ using System.Reflection;
namespace IcyClawz.CustomInteractions; namespace IcyClawz.CustomInteractions;
[BepInPlugin("com.IcyClawz.CustomInteractions", "IcyClawz.CustomInteractions", "1.5.0")] [BepInPlugin("com.IcyClawz.CustomInteractions", "IcyClawz.CustomInteractions", "1.6.0")]
public class Plugin : BaseUnityPlugin public class Plugin : BaseUnityPlugin
{ {
private void Awake() private void Awake()
@ -25,13 +25,13 @@ internal class ItemUiContextPatch : ModulePatch
private static void Postfix(ref ItemInfoInteractionsAbstractClass<EFT.InventoryLogic.EItemInfoButton> __result, private static void Postfix(ref ItemInfoInteractionsAbstractClass<EFT.InventoryLogic.EItemInfoButton> __result,
ref ItemUiContext __instance, ItemContextClass itemContext) ref ItemUiContext __instance, ItemContextClass itemContext)
{ {
foreach (var provider in CustomInteractionsManager.Providers.OfType<IItemCustomInteractionsProvider>()) foreach (var provider in CustomInteractionsManager.Providers)
{ {
var interactions = provider.GetCustomInteractions(__instance, itemContext.ViewType, itemContext.Item); var interactions = provider.GetCustomInteractions(__instance, itemContext.ViewType, itemContext.Item);
if (interactions is null) if (interactions is null)
continue; continue;
foreach (CustomInteraction interaction in interactions) foreach (CustomInteractionImpl impl in interactions.Select(interaction => interaction.Impl))
__result.AddCustomInteraction(interaction); __result.AddCustomInteraction(impl);
} }
} }
} }

View File

@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net472</TargetFramework> <TargetFramework>net472</TargetFramework>
<AssemblyName>IcyClawz.ItemAttributeFix</AssemblyName> <AssemblyName>IcyClawz.ItemAttributeFix</AssemblyName>
<Version>1.4.0</Version> <Version>1.5.0</Version>
<RootNamespace>IcyClawz.ItemAttributeFix</RootNamespace> <RootNamespace>IcyClawz.ItemAttributeFix</RootNamespace>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
</PropertyGroup> </PropertyGroup>

View File

@ -5,7 +5,7 @@ using System.Reflection;
namespace IcyClawz.ItemAttributeFix; namespace IcyClawz.ItemAttributeFix;
[BepInPlugin("com.IcyClawz.ItemAttributeFix", "IcyClawz.ItemAttributeFix", "1.4.0")] [BepInPlugin("com.IcyClawz.ItemAttributeFix", "IcyClawz.ItemAttributeFix", "1.5.0")]
public class Plugin : BaseUnityPlugin public class Plugin : BaseUnityPlugin
{ {
private void Awake() => private void Awake() =>

View File

@ -5,89 +5,71 @@ using EFT.UI;
using IcyClawz.CustomInteractions; using IcyClawz.CustomInteractions;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection;
using UnityEngine; using UnityEngine;
using ILightTemplate = GInterface310; using ILightTemplate = GInterface357;
using GlobalEvents = GClass3040; using ISightTemplate = GInterface365;
using GlobalEvents = GClass3400;
namespace IcyClawz.ItemContextMenuExt; namespace IcyClawz.ItemContextMenuExt;
internal static class PlayerExtensions internal static class SightComponentExtensions
{ {
private static readonly FieldInfo InventoryControllerField = public static int[] GetScopeCalibrationDistances(this SightComponent instance, int scopeIndex) =>
typeof(Player).GetField("_inventoryController", BindingFlags.NonPublic | BindingFlags.Instance); ((ISightTemplate)instance.Item.Template).CalibrationDistances[scopeIndex];
public static InventoryControllerClass GetInventoryController(this Player player) => public static float[] GetScopeZooms(this SightComponent instance, int scopeIndex) =>
InventoryControllerField.GetValue(player) as InventoryControllerClass; ((ISightTemplate)instance.Item.Template).Zooms[scopeIndex];
} }
internal static class LightComponentExtensions internal static class LightComponentExtensions
{ {
public static int GetModesCount(this LightComponent component) => public static int GetModesCount(this LightComponent instance) =>
((ILightTemplate)component.Item.Template).ModesCount; ((ILightTemplate)instance.Item.Template).ModesCount;
} }
internal sealed class CustomInteractionsProvider : IItemCustomInteractionsProvider internal sealed class CustomInteractionsProvider : ICustomInteractionsProvider
{ {
internal const string IconsPrefix = "Characteristics/Icons/"; private const string IconsPrefix = "Characteristics/Icons/";
internal static StaticIcons StaticIcons => EFTHardSettings.Instance.StaticIcons; private static StaticIcons StaticIcons => EFTHardSettings.Instance.StaticIcons;
public IEnumerable<CustomInteraction> GetCustomInteractions(ItemUiContext uiContext, EItemViewType viewType, Item item) public IEnumerable<CustomInteraction> GetCustomInteractions(ItemUiContext context, EItemViewType viewType, Item item)
{
if (viewType is EItemViewType.Inventory)
{ {
if (viewType is not EItemViewType.Inventory)
yield break;
FireModeComponent fireModeComponent = item.GetItemComponent<FireModeComponent>(); FireModeComponent fireModeComponent = item.GetItemComponent<FireModeComponent>();
if (fireModeComponent is not null) if (fireModeComponent is not null)
{ return GetFireModeInteractions(context, fireModeComponent);
// Firing mode
yield return new() SightComponent sightComponent = item.GetItemComponent<SightComponent>();
{ if (sightComponent is not null)
Caption = () => "Firing mode", return GetSightInteractions(context, sightComponent);
Icon = () => StaticIcons.GetAttributeIcon(EItemAttributeId.Weapon),
SubMenu = () => new FireModeSubMenu(uiContext, fireModeComponent),
Enabled = () => fireModeComponent.AvailableEFireModes.Length > 1,
Error = () => "This weapon is incapable of selective fire"
};
yield break;
}
LightComponent lightComponent = item.GetItemComponent<LightComponent>(); LightComponent lightComponent = item.GetItemComponent<LightComponent>();
if (lightComponent is not null) if (lightComponent is not null)
{ return GetLightInteractions(context, lightComponent);
// Turn on/off
yield return new()
{
Caption = () => (lightComponent.IsActive ? "TurnOff" : "TurnOn").Localized(),
Icon = () => CacheResourcesPopAbstractClass.Pop<Sprite>(IconsPrefix + (lightComponent.IsActive ? "TurnOff" : "TurnOn")),
Action = () =>
{
Singleton<GUISounds>.Instance.PlayUISound(EUISoundType.MenuContextMenu);
ComponentUtils.SetLightState(lightComponent, !lightComponent.IsActive, lightComponent.SelectedMode);
GlobalEvents.RequestGlobalRedraw();
} }
}; return [];
// Switch mode
yield return new()
{
Caption = () => "Switch mode",
Icon = () => StaticIcons.GetAttributeIcon(EItemAttributeId.EncodeState),
SubMenu = () => new LightModeSubMenu(uiContext, lightComponent),
Enabled = () => lightComponent.GetModesCount() > 1,
Error = () => "This device has no alternative modes"
};
yield break;
} }
private IEnumerable<CustomInteraction> GetFireModeInteractions(ItemUiContext context, FireModeComponent component)
{
if (component.AvailableEFireModes.Length > 1)
{
yield return new(context)
{
Caption = () => "Firing mode",
Icon = () => StaticIcons.GetAttributeIcon(EItemAttributeId.Weapon),
SubMenu = () => GetFireModeSubInteractions(context, component),
};
} }
} }
internal class FireModeSubMenu : CustomSubInteractions private IEnumerable<CustomInteraction> GetFireModeSubInteractions(ItemUiContext context, FireModeComponent component)
{ {
public FireModeSubMenu(ItemUiContext uiContext, FireModeComponent component) foreach (Weapon.EFireMode fireMode in component.AvailableEFireModes)
: base(uiContext)
{ {
AddRange(component.AvailableEFireModes.Select(fireMode => new CustomInteraction() yield return new(context)
{ {
Caption = () => fireMode.ToString().Localized(), Caption = () => fireMode.ToString().Localized(),
Action = () => Action = () =>
@ -95,26 +77,137 @@ internal class FireModeSubMenu : CustomSubInteractions
Singleton<GUISounds>.Instance.PlayUISound(EUISoundType.MenuContextMenu); Singleton<GUISounds>.Instance.PlayUISound(EUISoundType.MenuContextMenu);
ComponentUtils.SetFireMode(component, fireMode); ComponentUtils.SetFireMode(component, fireMode);
}, },
Enabled = () => fireMode != component.FireMode Enabled = () => fireMode != component.FireMode,
})); };
} }
} }
internal class LightModeSubMenu : CustomSubInteractions private IEnumerable<CustomInteraction> GetSightInteractions(ItemUiContext context, SightComponent component)
{ {
public LightModeSubMenu(ItemUiContext uiContext, LightComponent component) if (component.ScopesCount > 1)
: base(uiContext)
{ {
AddRange(Enumerable.Range(0, component.GetModesCount()).Select(lightMode => new CustomInteraction() yield return new(context)
{
Caption = () => "Active scope",
Icon = () => StaticIcons.GetAttributeIcon(EItemAttributeId.EncodeState),
SubMenu = () => GetScopeIndexSubInteractions(context, component),
};
}
if (component.GetScopeModesCount(component.SelectedScope) > 1)
{
yield return new(context)
{
Caption = () => "Active mode",
Icon = () => StaticIcons.GetAttributeIcon(EItemAttributeId.EncodeState),
SubMenu = () => GetScopeModeSubInteractions(context, component),
};
}
if (component.GetScopeCalibrationDistances(component.SelectedScope).Length > 1)
{
yield return new(context)
{
Caption = () => "Zero distance",
Icon = () => StaticIcons.GetAttributeIcon(EItemAttributeId.EncodeState),
SubMenu = () => GetScopeCalibSubInteractions(context, component),
};
}
}
private IEnumerable<CustomInteraction> GetScopeIndexSubInteractions(ItemUiContext context, SightComponent component)
{
foreach (int scopeIndex in Enumerable.Range(0, component.ScopesCount))
{
yield return new(context)
{
Caption = () => $"Scope {scopeIndex + 1}",
Action = () =>
{
Singleton<GUISounds>.Instance.PlayUISound(EUISoundType.MenuContextMenu);
ComponentUtils.SetScopeIndex(component, scopeIndex);
},
Enabled = () => scopeIndex != component.SelectedScope,
};
}
}
private IEnumerable<CustomInteraction> GetScopeModeSubInteractions(ItemUiContext context, SightComponent component)
{
int scopeIndex = component.SelectedScope;
float[] scopeZooms = component.GetScopeZooms(scopeIndex);
foreach (int scopeMode in Enumerable.Range(0, component.GetScopeModesCount(scopeIndex)))
{
float scopeZoom = scopeZooms.Length > 0 ? scopeZooms[scopeMode] : 0f;
yield return new(context)
{
Caption = () => $"Mode {scopeMode + 1} ({scopeZoom}x)",
Action = () =>
{
Singleton<GUISounds>.Instance.PlayUISound(EUISoundType.MenuContextMenu);
ComponentUtils.SetScopeMode(component, scopeMode);
},
Enabled = () => scopeMode != component.ScopesSelectedModes[scopeIndex],
};
}
}
private IEnumerable<CustomInteraction> GetScopeCalibSubInteractions(ItemUiContext context, SightComponent component)
{
int scopeIndex = component.SelectedScope;
int[] scopeCalibDistances = component.GetScopeCalibrationDistances(scopeIndex);
foreach (int scopeCalibIndex in Enumerable.Range(0, scopeCalibDistances.Length))
{
yield return new(context)
{
Caption = () => $"{scopeCalibDistances[scopeCalibIndex]}",
Action = () =>
{
Singleton<GUISounds>.Instance.PlayUISound(EUISoundType.MenuContextMenu);
ComponentUtils.SetScopeCalibIndex(component, scopeCalibIndex);
},
Enabled = () => scopeCalibIndex != component.ScopesCurrentCalibPointIndexes[scopeIndex],
};
}
}
private IEnumerable<CustomInteraction> GetLightInteractions(ItemUiContext context, LightComponent component)
{
yield return new(context)
{
Caption = () => component.IsActive ? "Turn off" : "Turn on",
Icon = () => CacheResourcesPopAbstractClass.Pop<Sprite>(IconsPrefix + (component.IsActive ? "TurnOff" : "TurnOn")),
Action = () =>
{
Singleton<GUISounds>.Instance.PlayUISound(EUISoundType.MenuContextMenu);
ComponentUtils.SetLightActive(component, !component.IsActive);
GlobalEvents.RequestGlobalRedraw();
},
};
if (component.GetModesCount() > 1)
{
yield return new(context)
{
Caption = () => "Active mode",
Icon = () => StaticIcons.GetAttributeIcon(EItemAttributeId.EncodeState),
SubMenu = () => GetLightModeSubInteractions(context, component),
};
}
}
private IEnumerable<CustomInteraction> GetLightModeSubInteractions(ItemUiContext context, LightComponent component)
{
foreach (int lightMode in Enumerable.Range(0, component.GetModesCount()))
{
yield return new(context)
{ {
Caption = () => $"Mode {lightMode + 1}", Caption = () => $"Mode {lightMode + 1}",
Action = () => Action = () =>
{ {
Singleton<GUISounds>.Instance.PlayUISound(EUISoundType.MenuContextMenu); Singleton<GUISounds>.Instance.PlayUISound(EUISoundType.MenuContextMenu);
ComponentUtils.SetLightState(component, component.IsActive, lightMode); ComponentUtils.SetLightMode(component, lightMode);
}, },
Enabled = () => lightMode != component.SelectedMode Enabled = () => lightMode != component.SelectedMode,
})); };
}
} }
} }
@ -129,7 +222,7 @@ internal static class ComponentUtils
if (fc.Item.MalfState.State is not Weapon.EMalfunctionState.None) if (fc.Item.MalfState.State is not Weapon.EMalfunctionState.None)
{ {
fc.FirearmsAnimator.MisfireSlideUnknown(false); fc.FirearmsAnimator.MisfireSlideUnknown(false);
player.GetInventoryController().ExamineMalfunction(fc.Item, false); player.InventoryController.ExamineMalfunction(fc.Item, false);
return; return;
} }
@ -140,18 +233,89 @@ internal static class ComponentUtils
component.SetFireMode(fireMode); component.SetFireMode(fireMode);
} }
public static void SetLightState(LightComponent component, bool isActive, int lightMode) public static void SetScopeIndex(SightComponent component, int scopeIndex)
{
SetScopeState(component, new()
{
Id = component.Item.Id,
ScopeIndexInsideSight = scopeIndex,
ScopeMode = component.ScopesSelectedModes[scopeIndex],
ScopeCalibrationIndex = component.ScopesCurrentCalibPointIndexes[scopeIndex],
});
}
public static void SetScopeMode(SightComponent component, int scopeMode)
{
int scopeIndex = component.SelectedScope;
SetScopeState(component, new()
{
Id = component.Item.Id,
ScopeIndexInsideSight = scopeIndex,
ScopeMode = scopeMode,
ScopeCalibrationIndex = component.ScopesCurrentCalibPointIndexes[scopeIndex],
});
}
public static void SetScopeCalibIndex(SightComponent component, int scopeCalibIndex)
{
int scopeIndex = component.SelectedScope;
SetScopeState(component, new()
{
Id = component.Item.Id,
ScopeIndexInsideSight = scopeIndex,
ScopeMode = component.ScopesSelectedModes[scopeIndex],
ScopeCalibrationIndex = scopeCalibIndex,
});
}
private static void SetScopeState(SightComponent component, FirearmScopeStateStruct scopeState)
{ {
Player player = GamePlayerOwner.MyPlayer; Player player = GamePlayerOwner.MyPlayer;
if (player is not null && player.HandsController is Player.FirearmController fc && component.Item.IsChildOf(fc.Item)) if (player is not null && player.HandsController is Player.FirearmController fc && component.Item.IsChildOf(fc.Item))
{ {
fc.SetLightsState([new() { Id = component.Item.Id, IsActive = isActive, LightMode = lightMode }]); fc.SetScopeMode([scopeState]);
return; return;
} }
component.IsActive = isActive; int scopeIndex = scopeState.ScopeIndexInsideSight;
component.SelectedMode = lightMode; component.SelectedScope = scopeIndex;
component.ScopesSelectedModes[scopeIndex] = scopeState.ScopeMode;
component.ScopesCurrentCalibPointIndexes[scopeIndex] = scopeState.ScopeCalibrationIndex;
}
public static void SetLightActive(LightComponent component, bool isActive)
{
SetLightState(component, new()
{
Id = component.Item.Id,
IsActive = isActive,
LightMode = component.SelectedMode,
});
}
public static void SetLightMode(LightComponent component, int lightMode)
{
SetLightState(component, new()
{
Id = component.Item.Id,
IsActive = component.IsActive,
LightMode = lightMode,
});
}
private static void SetLightState(LightComponent component, FirearmLightStateStruct lightState)
{
Player player = GamePlayerOwner.MyPlayer;
if (player is not null && player.HandsController is Player.FirearmController fc && component.Item.IsChildOf(fc.Item))
{
fc.SetLightsState([lightState]);
return;
}
component.IsActive = lightState.IsActive;
component.SelectedMode = lightState.LightMode;
if (player is not null) if (player is not null)
{ {

View File

@ -3,15 +3,11 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net472</TargetFramework> <TargetFramework>net472</TargetFramework>
<AssemblyName>IcyClawz.ItemContextMenuExt</AssemblyName> <AssemblyName>IcyClawz.ItemContextMenuExt</AssemblyName>
<Version>1.5.0</Version> <Version>1.6.0</Version>
<RootNamespace>IcyClawz.ItemContextMenuExt</RootNamespace> <RootNamespace>IcyClawz.ItemContextMenuExt</RootNamespace>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<None Remove="ItemContextMenuExt.cs~RF18496b2.TMP" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Assembly-CSharp"> <Reference Include="Assembly-CSharp">
<HintPath>..\Shared\Assembly-CSharp.dll</HintPath> <HintPath>..\Shared\Assembly-CSharp.dll</HintPath>
@ -31,6 +27,9 @@
<Reference Include="ItemTemplate.Types"> <Reference Include="ItemTemplate.Types">
<HintPath>..\Shared\ItemTemplate.Types.dll</HintPath> <HintPath>..\Shared\ItemTemplate.Types.dll</HintPath>
</Reference> </Reference>
<Reference Include="SPT.Reflection">
<HintPath>..\Shared\spt-reflection.dll</HintPath>
</Reference>
<Reference Include="UnityEngine"> <Reference Include="UnityEngine">
<HintPath>..\Shared\UnityEngine.dll</HintPath> <HintPath>..\Shared\UnityEngine.dll</HintPath>
</Reference> </Reference>

View File

@ -1,12 +1,35 @@
using BepInEx; using BepInEx;
using BepInEx.Configuration;
using EFT.UI;
using IcyClawz.CustomInteractions; using IcyClawz.CustomInteractions;
using SPT.Reflection.Patching;
using System.Reflection;
namespace IcyClawz.ItemContextMenuExt; namespace IcyClawz.ItemContextMenuExt;
[BepInPlugin("com.IcyClawz.ItemContextMenuExt", "IcyClawz.ItemContextMenuExt", "1.5.0")] [BepInPlugin("com.IcyClawz.ItemContextMenuExt", "IcyClawz.ItemContextMenuExt", "1.6.0")]
[BepInDependency("com.IcyClawz.CustomInteractions")] [BepInDependency("com.IcyClawz.CustomInteractions")]
public class Plugin : BaseUnityPlugin public class Plugin : BaseUnityPlugin
{ {
private void Awake() => private static ConfigEntry<float> ScaleConfig { get; set; }
private void Awake()
{
const string SECTION = "Item Context Menu";
ScaleConfig = Config.Bind(SECTION, "Scale", 1f);
new ItemUiContextPatch().Enable();
CustomInteractionsManager.Register(new CustomInteractionsProvider()); CustomInteractionsManager.Register(new CustomInteractionsProvider());
} }
internal static float Scale => ScaleConfig.Value;
}
internal class ItemUiContextPatch : ModulePatch
{
protected override MethodBase GetTargetMethod() =>
typeof(ItemUiContext).GetMethod("ShowContextMenu", BindingFlags.Public | BindingFlags.Instance);
[PatchPostfix]
private static void Postfix(ref ItemUiContext __instance) =>
__instance.ContextMenu.Transform.localScale = new(Plugin.Scale, Plugin.Scale);
}

View File

@ -8,7 +8,7 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using UnityEngine; using UnityEngine;
using CurrencyUtil = GClass2531; using CurrencyUtil = GClass2867;
namespace IcyClawz.ItemSellPrice; namespace IcyClawz.ItemSellPrice;
@ -55,7 +55,7 @@ internal static class ItemExtensions
["po"] = ["Preço de venda ({0})", "Não pode ser vendido a comerciantes"], ["po"] = ["Preço de venda ({0})", "Não pode ser vendido a comerciantes"],
["ru"] = ["Цена продажи ({0})", "Невозможно продать торговцам"], ["ru"] = ["Цена продажи ({0})", "Невозможно продать торговцам"],
["sk"] = ["Predajná cena ({0})", "Nedá sa predať obchodníkom"], ["sk"] = ["Predajná cena ({0})", "Nedá sa predať obchodníkom"],
["tu"] = ["Satış fiyatı ({0})", "Tüccarlara satılamaz"] ["tu"] = ["Satış fiyatı ({0})", "Tüccarlara satılamaz"],
}; };
private static ISession _Session; private static ISession _Session;
@ -91,11 +91,11 @@ internal static class ItemExtensions
FullStringValue = () => FullStringValue = () =>
{ {
IEnumerable<TraderOffer> offers = GetAllTraderOffers(item); IEnumerable<TraderOffer> offers = GetAllTraderOffers(item);
return offers is not null return offers.Any()
? string.Join(Environment.NewLine, offers.Select(offer => $"{offer.Name}: {offer.Currency} {offer.Price}")) ? string.Join(Environment.NewLine, offers.Select(offer => $"{offer.Name}: {offer.Currency} {offer.Price}"))
: ""; : "";
}, },
DisplayType = () => EItemAttributeDisplayType.Compact DisplayType = () => EItemAttributeDisplayType.Compact,
}; };
item.Attributes = [attribute, .. item.Attributes]; item.Attributes = [attribute, .. item.Attributes];
} }
@ -111,38 +111,34 @@ internal static class ItemExtensions
private static TraderOffer GetTraderOffer(Item item, TraderClass trader) private static TraderOffer GetTraderOffer(Item item, TraderClass trader)
{ {
var result = trader.GetUserItemPrice(item); var price = trader.GetUserItemPrice(item);
return result is null ? null : new( return price.HasValue ? new(
trader.LocalizedName, trader.LocalizedName,
result.Value.Amount, price.Value.Amount,
CurrencyUtil.GetCurrencyCharById(result.Value.CurrencyId), CurrencyUtil.GetCurrencyCharById(price.Value.CurrencyId.Value),
trader.GetSupplyData().CurrencyCourses[result.Value.CurrencyId], trader.GetSupplyData().CurrencyCourses[price.Value.CurrencyId.Value],
item.StackObjectsCount item.StackObjectsCount
); ) : null;
} }
private static IEnumerable<TraderOffer> GetAllTraderOffers(Item item) private static IEnumerable<TraderOffer> GetAllTraderOffers(Item item)
{ {
if (!Session.Profile.Examined(item)) if (!Session.Profile.Examined(item))
return null; return [];
switch (item.Owner?.OwnerType) if (item.Owner?.OwnerType is EOwnerType.RagFair or EOwnerType.Trader
{ && (item.StackObjectsCount > 1 || item.UnlimitedCount))
case EOwnerType.RagFair:
case EOwnerType.Trader:
if (item.StackObjectsCount > 1 || item.UnlimitedCount)
{ {
item = item.CloneItem(); item = item.CloneItem();
item.StackObjectsCount = 1; item.StackObjectsCount = 1;
item.UnlimitedCount = false; item.UnlimitedCount = false;
} }
break;
}
return Session.Traders return Session.Traders
.Where(trader => !trader.Settings.AvailableInRaid)
.Select(trader => GetTraderOffer(item, trader)) .Select(trader => GetTraderOffer(item, trader))
.Where(offer => offer is not null) .Where(offer => offer is not null)
.OrderByDescending(offer => offer.Price * offer.Course); .OrderByDescending(offer => offer.Price * offer.Course);
} }
private static TraderOffer GetBestTraderOffer(Item item) => private static TraderOffer GetBestTraderOffer(Item item) =>
GetAllTraderOffers(item)?.FirstOrDefault() ?? null; GetAllTraderOffers(item).FirstOrDefault();
} }

View File

@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net472</TargetFramework> <TargetFramework>net472</TargetFramework>
<AssemblyName>IcyClawz.ItemSellPrice</AssemblyName> <AssemblyName>IcyClawz.ItemSellPrice</AssemblyName>
<Version>1.4.0</Version> <Version>1.5.0</Version>
<RootNamespace>IcyClawz.ItemSellPrice</RootNamespace> <RootNamespace>IcyClawz.ItemSellPrice</RootNamespace>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
</PropertyGroup> </PropertyGroup>

View File

@ -1,20 +1,20 @@
using BepInEx; using BepInEx;
using EFT;
using EFT.InventoryLogic; using EFT.InventoryLogic;
using SPT.Reflection.Patching; using SPT.Reflection.Patching;
using System.Reflection; using System.Reflection;
namespace IcyClawz.ItemSellPrice; namespace IcyClawz.ItemSellPrice;
[BepInPlugin("com.IcyClawz.ItemSellPrice", "IcyClawz.ItemSellPrice", "1.4.0")] [BepInPlugin("com.IcyClawz.ItemSellPrice", "IcyClawz.ItemSellPrice", "1.5.0")]
public class Plugin : BaseUnityPlugin public class Plugin : BaseUnityPlugin
{ {
private void Awake() private void Awake()
{ {
new TraderPatch().Enable(); new TraderPatch().Enable();
new ItemPatch().Enable(); new ItemPatch().Enable();
new AmmoPatch().Enable(); new AmmoItemPatch().Enable();
new GrenadePatch().Enable(); new ThrowWeapItemPatch().Enable();
new SecureContainerPatch().Enable();
} }
} }
@ -38,32 +38,22 @@ internal class ItemPatch : ModulePatch
__instance.AddTraderOfferAttribute(); __instance.AddTraderOfferAttribute();
} }
internal class AmmoPatch : ModulePatch internal class AmmoItemPatch : ModulePatch
{ {
protected override MethodBase GetTargetMethod() => protected override MethodBase GetTargetMethod() =>
typeof(BulletClass).GetConstructors()[0]; typeof(AmmoItemClass).GetConstructors()[0];
[PatchPostfix] [PatchPostfix]
private static void PatchPostfix(ref BulletClass __instance) => private static void PatchPostfix(ref AmmoItemClass __instance) =>
__instance.AddTraderOfferAttribute(); __instance.AddTraderOfferAttribute();
} }
internal class GrenadePatch : ModulePatch internal class ThrowWeapItemPatch : ModulePatch
{ {
protected override MethodBase GetTargetMethod() => protected override MethodBase GetTargetMethod() =>
typeof(GrenadeClass).GetConstructors()[0]; typeof(ThrowWeapItemClass).GetConstructors()[0];
[PatchPostfix] [PatchPostfix]
private static void PatchPostfix(ref GrenadeClass __instance) => private static void PatchPostfix(ref ThrowWeapItemClass __instance) =>
__instance.AddTraderOfferAttribute();
}
internal class SecureContainerPatch : ModulePatch
{
protected override MethodBase GetTargetMethod() =>
typeof(ItemContainerClass).GetConstructors()[0];
[PatchPostfix]
private static void PatchPostfix(ref ItemContainerClass __instance) =>
__instance.AddTraderOfferAttribute(); __instance.AddTraderOfferAttribute();
} }

View File

@ -7,7 +7,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using UnityEngine; using UnityEngine;
using InGameStatus = GClass1864; using InGameStatus = GClass2064;
namespace IcyClawz.MagazineInspector; namespace IcyClawz.MagazineInspector;
@ -30,7 +30,7 @@ internal static class MagazineClassExtensions
["po"] = "Munição carregada", ["po"] = "Munição carregada",
["ru"] = "Заряженные боеприпасы", ["ru"] = "Заряженные боеприпасы",
["sk"] = "Nabitá munícia", ["sk"] = "Nabitá munícia",
["tu"] = "Yüklü mühimmat" ["tu"] = "Yüklü mühimmat",
}; };
private static ISession _Session; private static ISession _Session;
@ -38,7 +38,7 @@ internal static class MagazineClassExtensions
private static Profile ActiveProfile => InGameStatus.InRaid ? GamePlayerOwner.MyPlayer.Profile : Session.Profile; private static Profile ActiveProfile => InGameStatus.InRaid ? GamePlayerOwner.MyPlayer.Profile : Session.Profile;
public static void AddAmmoCountAttribute(this MagazineClass magazine) public static void AddAmmoCountAttribute(this MagazineItemClass magazine)
{ {
ItemAttributeClass attribute = magazine.Attributes.Find(attr => attr.Id is EItemAttributeId.MaxCount); ItemAttributeClass attribute = magazine.Attributes.Find(attr => attr.Id is EItemAttributeId.MaxCount);
if (attribute is null) if (attribute is null)
@ -74,7 +74,7 @@ internal static class MagazineClassExtensions
}; };
} }
private static int? GetAmmoCount(MagazineClass magazine, Profile profile, out bool magChecked) private static int? GetAmmoCount(MagazineItemClass magazine, Profile profile, out bool magChecked)
{ {
if (!InGameStatus.InRaid || magazine.Count == 0) if (!InGameStatus.InRaid || magazine.Count == 0)
{ {

View File

@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net472</TargetFramework> <TargetFramework>net472</TargetFramework>
<AssemblyName>IcyClawz.MagazineInspector</AssemblyName> <AssemblyName>IcyClawz.MagazineInspector</AssemblyName>
<Version>1.4.0</Version> <Version>1.5.0</Version>
<RootNamespace>IcyClawz.MagazineInspector</RootNamespace> <RootNamespace>IcyClawz.MagazineInspector</RootNamespace>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
</PropertyGroup> </PropertyGroup>

View File

@ -4,7 +4,7 @@ using System.Reflection;
namespace IcyClawz.MagazineInspector; namespace IcyClawz.MagazineInspector;
[BepInPlugin("com.IcyClawz.MagazineInspector", "IcyClawz.MagazineInspector", "1.4.0")] [BepInPlugin("com.IcyClawz.MagazineInspector", "IcyClawz.MagazineInspector", "1.5.0")]
public class Plugin : BaseUnityPlugin public class Plugin : BaseUnityPlugin
{ {
private void Awake() => private void Awake() =>
@ -14,9 +14,9 @@ public class Plugin : BaseUnityPlugin
internal class MagazinePatch : ModulePatch internal class MagazinePatch : ModulePatch
{ {
protected override MethodBase GetTargetMethod() => protected override MethodBase GetTargetMethod() =>
typeof(MagazineClass).GetConstructors()[0]; typeof(MagazineItemClass).GetConstructors()[0];
[PatchPostfix] [PatchPostfix]
private static void PatchPostfix(ref MagazineClass __instance) => private static void PatchPostfix(ref MagazineItemClass __instance) =>
__instance.AddAmmoCountAttribute(); __instance.AddAmmoCountAttribute();
} }

View File

@ -31,7 +31,7 @@ internal static class ColorCache
[ColorName.Silver] = new Color32(150, 150, 150, ALPHA), [ColorName.Silver] = new Color32(150, 150, 150, ALPHA),
[ColorName.Tan] = new Color32(175, 145, 100, ALPHA), [ColorName.Tan] = new Color32(175, 145, 100, ALPHA),
[ColorName.Violet] = new Color32(80, 50, 180, ALPHA), [ColorName.Violet] = new Color32(80, 50, 180, ALPHA),
[ColorName.Yellow] = new Color32(170, 170, 0, ALPHA) [ColorName.Yellow] = new Color32(170, 170, 0, ALPHA),
}; };
public static Color Get(ColorName name) => Cache.TryGetValue(name, out Color color) ? color : default; public static Color Get(ColorName name) => Cache.TryGetValue(name, out Color color) ? color : default;
@ -60,7 +60,7 @@ internal static class IconCache
{ {
[EAmmoExtraAttributeId.ArmorDamage] = Properties.Resources.ArmorDamage.ToSprite(), [EAmmoExtraAttributeId.ArmorDamage] = Properties.Resources.ArmorDamage.ToSprite(),
[EAmmoExtraAttributeId.FragmentationChance] = Properties.Resources.FragmentationChance.ToSprite(), [EAmmoExtraAttributeId.FragmentationChance] = Properties.Resources.FragmentationChance.ToSprite(),
[EAmmoExtraAttributeId.RicochetChance] = Properties.Resources.RicochetChance.ToSprite() [EAmmoExtraAttributeId.RicochetChance] = Properties.Resources.RicochetChance.ToSprite(),
}; };
public static Sprite Get(Enum id) => Cache.TryGetValue(id, out Sprite sprite) ? sprite : default; public static Sprite Get(Enum id) => Cache.TryGetValue(id, out Sprite sprite) ? sprite : default;
@ -68,6 +68,15 @@ internal static class IconCache
internal static class AmmoTemplateExtensions internal static class AmmoTemplateExtensions
{ {
private static readonly string[] MalfChancesKeys = (string[])
typeof(AmmoTemplate).GetField("MalfChancesKeys", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
private static readonly FieldInfo MaxMalfMisfireChance =
typeof(AmmoTemplate).GetField("MaxMalfMisfireChance", BindingFlags.NonPublic | BindingFlags.Static);
private static readonly FieldInfo MaxMalfFeedChance =
typeof(AmmoTemplate).GetField("MaxMalfFeedChance", BindingFlags.NonPublic | BindingFlags.Static);
private static readonly FieldInfo CachedQualitiesField = private static readonly FieldInfo CachedQualitiesField =
typeof(AmmoTemplate).GetField("_cachedQualities", BindingFlags.NonPublic | BindingFlags.Instance); typeof(AmmoTemplate).GetField("_cachedQualities", BindingFlags.NonPublic | BindingFlags.Instance);
@ -82,7 +91,27 @@ internal static class AmmoTemplateExtensions
DisplayNameFunc = () => "Armor damage", DisplayNameFunc = () => "Armor damage",
Base = () => instance.ArmorDamage, Base = () => instance.ArmorDamage,
StringValue = () => $"{instance.ArmorDamage}%", StringValue = () => $"{instance.ArmorDamage}%",
DisplayType = () => EItemAttributeDisplayType.Compact DisplayType = () => EItemAttributeDisplayType.Compact,
});
instance.SafelyAddQualityToList(new ItemAttributeClass(EItemAttributeId.DurabilityBurn)
{
Name = EItemAttributeId.DurabilityBurn.GetName(),
Base = () => instance.DurabilityBurnModificator - 1f,
StringValue = () => $"{(instance.DurabilityBurnModificator - 1f) * 100f:F1}%",
DisplayType = () => EItemAttributeDisplayType.Compact,
LabelVariations = EItemAttributeLabelVariations.Colored,
LessIsGood = true,
});
instance.SafelyAddQualityToList(new ItemAttributeClass(EItemAttributeId.HeatFactor)
{
Name = EItemAttributeId.HeatFactor.GetName(),
Base = () => instance.HeatFactor - 1f,
StringValue = () => $"{(instance.HeatFactor - 1f) * 100f:F1}%",
DisplayType = () => EItemAttributeDisplayType.Compact,
LabelVariations = EItemAttributeLabelVariations.Colored,
LessIsGood = true,
}); });
instance.SafelyAddQualityToList(new ItemAttributeClass(EAmmoExtraAttributeId.FragmentationChance) instance.SafelyAddQualityToList(new ItemAttributeClass(EAmmoExtraAttributeId.FragmentationChance)
@ -90,8 +119,8 @@ internal static class AmmoTemplateExtensions
Name = EAmmoExtraAttributeId.FragmentationChance.ToString(), Name = EAmmoExtraAttributeId.FragmentationChance.ToString(),
DisplayNameFunc = () => "Fragmentation chance", DisplayNameFunc = () => "Fragmentation chance",
Base = () => instance.FragmentationChance, Base = () => instance.FragmentationChance,
StringValue = () => $"{Math.Round(instance.FragmentationChance * 100, 1)}%", StringValue = () => $"{instance.FragmentationChance * 100f:F1}%",
DisplayType = () => EItemAttributeDisplayType.Compact DisplayType = () => EItemAttributeDisplayType.Compact,
}); });
instance.SafelyAddQualityToList(new ItemAttributeClass(EAmmoExtraAttributeId.RicochetChance) instance.SafelyAddQualityToList(new ItemAttributeClass(EAmmoExtraAttributeId.RicochetChance)
@ -99,8 +128,44 @@ internal static class AmmoTemplateExtensions
Name = EAmmoExtraAttributeId.RicochetChance.ToString(), Name = EAmmoExtraAttributeId.RicochetChance.ToString(),
DisplayNameFunc = () => "Ricochet chance", DisplayNameFunc = () => "Ricochet chance",
Base = () => instance.RicochetChance, Base = () => instance.RicochetChance,
StringValue = () => $"{Math.Round(instance.RicochetChance * 100, 1)}%", StringValue = () => $"{(instance.RicochetChance * 100f):F1}%",
DisplayType = () => EItemAttributeDisplayType.Compact DisplayType = () => EItemAttributeDisplayType.Compact,
});
instance.SafelyAddQualityToList(new ItemAttributeClass(EItemAttributeId.MalfMisfireChance)
{
Name = EItemAttributeId.MalfMisfireChance.GetName(),
Base = () => instance.MalfMisfireChance,
StringValue = () =>
{
float maxMalfMisfireChance = (float)MaxMalfMisfireChance.GetValue(null);
int index = instance.MalfMisfireChance <= 0f ? 0
: instance.MalfMisfireChance < 3f * maxMalfMisfireChance / 7f ? 1
: instance.MalfMisfireChance < 4f * maxMalfMisfireChance / 7f ? 2
: instance.MalfMisfireChance < 5f * maxMalfMisfireChance / 7f ? 3
: instance.MalfMisfireChance < 6f * maxMalfMisfireChance / 7f ? 4
: 5;
return MalfChancesKeys[index].Localized();
},
DisplayType = () => EItemAttributeDisplayType.Compact,
});
instance.SafelyAddQualityToList(new ItemAttributeClass(EItemAttributeId.MalfFeedChance)
{
Name = EItemAttributeId.MalfFeedChance.GetName(),
Base = () => instance.MalfFeedChance,
StringValue = () =>
{
float maxMalfFeedChance = (float)MaxMalfFeedChance.GetValue(null);
int index = instance.MalfFeedChance <= 0f ? 0
: instance.MalfFeedChance < 1f * maxMalfFeedChance / 7f ? 1
: instance.MalfFeedChance < 3f * maxMalfFeedChance / 7f ? 2
: instance.MalfFeedChance < 5f * maxMalfFeedChance / 7f ? 3
: instance.MalfFeedChance < 6f * maxMalfFeedChance / 7f ? 4
: 5;
return MalfChancesKeys[index].Localized();
},
DisplayType = () => EItemAttributeDisplayType.Compact,
}); });
} }

View File

@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net472</TargetFramework> <TargetFramework>net472</TargetFramework>
<AssemblyName>IcyClawz.MunitionsExpert</AssemblyName> <AssemblyName>IcyClawz.MunitionsExpert</AssemblyName>
<Version>1.4.0</Version> <Version>1.5.0</Version>
<RootNamespace>IcyClawz.MunitionsExpert</RootNamespace> <RootNamespace>IcyClawz.MunitionsExpert</RootNamespace>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
</PropertyGroup> </PropertyGroup>

View File

@ -13,24 +13,24 @@ using UnityEngine.UI;
namespace IcyClawz.MunitionsExpert; namespace IcyClawz.MunitionsExpert;
[BepInPlugin("com.IcyClawz.MunitionsExpert", "IcyClawz.MunitionsExpert", "1.4.0")] [BepInPlugin("com.IcyClawz.MunitionsExpert", "IcyClawz.MunitionsExpert", "1.5.0")]
public class Plugin : BaseUnityPlugin public class Plugin : BaseUnityPlugin
{ {
private static ConfigEntry<bool> ColorizeSwitch { get; set; } private static ConfigEntry<bool> ColorizeConfig { get; set; }
private static ConfigEntry<ColorName>[] ArmorClassColors { get; set; } private static ConfigEntry<ColorName>[] ArmorClassColorConfigs { get; set; }
private void Awake() private void Awake()
{ {
const string COLORIZE = "Colorize Icon Backgrounds"; const string SECTION = "Colorize Icon Backgrounds";
ColorizeSwitch = Config.Bind(COLORIZE, "", true, new ConfigurationManagerAttributes { Order = 7 }); ColorizeConfig = Config.Bind(SECTION, "", true, new ConfigurationManagerAttributes { Order = 7 });
ArmorClassColors = [ ArmorClassColorConfigs = [
Config.Bind(COLORIZE, "Unarmored", ColorName.Purple, new ConfigurationManagerAttributes { Order = 6 }), Config.Bind(SECTION, "Unarmored", ColorName.Purple, new ConfigurationManagerAttributes { Order = 6 }),
Config.Bind(COLORIZE, "Class 1", ColorName.Blue, new ConfigurationManagerAttributes { Order = 5 }), Config.Bind(SECTION, "Class 1", ColorName.Blue, new ConfigurationManagerAttributes { Order = 5 }),
Config.Bind(COLORIZE, "Class 2", ColorName.Cyan, new ConfigurationManagerAttributes { Order = 4 }), Config.Bind(SECTION, "Class 2", ColorName.Cyan, new ConfigurationManagerAttributes { Order = 4 }),
Config.Bind(COLORIZE, "Class 3", ColorName.Green, new ConfigurationManagerAttributes { Order = 3 }), Config.Bind(SECTION, "Class 3", ColorName.Green, new ConfigurationManagerAttributes { Order = 3 }),
Config.Bind(COLORIZE, "Class 4", ColorName.Yellow, new ConfigurationManagerAttributes { Order = 2 }), Config.Bind(SECTION, "Class 4", ColorName.Yellow, new ConfigurationManagerAttributes { Order = 2 }),
Config.Bind(COLORIZE, "Class 5", ColorName.Orange, new ConfigurationManagerAttributes { Order = 1 }), Config.Bind(SECTION, "Class 5", ColorName.Orange, new ConfigurationManagerAttributes { Order = 1 }),
Config.Bind(COLORIZE, "Class 6+", ColorName.Red, new ConfigurationManagerAttributes { Order = 0 }) Config.Bind(SECTION, "Class 6+", ColorName.Red, new ConfigurationManagerAttributes { Order = 0 }),
]; ];
new StaticIconsPatch().Enable(); new StaticIconsPatch().Enable();
new AmmoTemplatePatch().Enable(); new AmmoTemplatePatch().Enable();
@ -38,12 +38,12 @@ public class Plugin : BaseUnityPlugin
new EntityIconPatch().Enable(); new EntityIconPatch().Enable();
} }
internal static bool Colorize => ColorizeSwitch.Value; internal static bool Colorize => ColorizeConfig.Value;
internal static Color GetArmorClassColor(int index) internal static Color GetArmorClassColor(int index)
{ {
index = Mathf.Clamp(index, 0, ArmorClassColors.Length - 1); index = Mathf.Clamp(index, 0, ArmorClassColorConfigs.Length - 1);
return ColorCache.Get(ArmorClassColors[index].Value); return ColorCache.Get(ArmorClassColorConfigs[index].Value);
} }
} }
@ -87,9 +87,9 @@ internal class ItemViewPatch : ModulePatch
[PatchPrefix] [PatchPrefix]
private static void PatchPrefix(ref ItemView __instance) private static void PatchPrefix(ref ItemView __instance)
{ {
if (!Plugin.Colorize || __instance.Item is not BulletClass bullet || bullet.PenetrationPower <= 0) if (!Plugin.Colorize || __instance.Item is not AmmoItemClass ammo || ammo.PenetrationPower <= 0)
return; return;
int armorClass = bullet.AmmoTemplate.GetPenetrationArmorClass(); int armorClass = ammo.AmmoTemplate.GetPenetrationArmorClass();
BackgroundColorField.SetValue(__instance, Plugin.GetArmorClassColor(armorClass)); BackgroundColorField.SetValue(__instance, Plugin.GetArmorClassColor(armorClass));
} }
} }
@ -105,10 +105,11 @@ internal class EntityIconPatch : ModulePatch
[PatchPostfix] [PatchPostfix]
private static void PatchPostfix(ref EntityIcon __instance, Item item) private static void PatchPostfix(ref EntityIcon __instance, Item item)
{ {
if (!Plugin.Colorize || item is not BulletClass bullet || bullet.PenetrationPower <= 0) if (!Plugin.Colorize || item is not AmmoItemClass ammo || ammo.PenetrationPower <= 0)
return; return;
int armorClass = bullet.AmmoTemplate.GetPenetrationArmorClass(); if (ColorPanelField.GetValue(__instance) is not Image image)
if (ColorPanelField.GetValue(__instance) is Image image) return;
int armorClass = ammo.AmmoTemplate.GetPenetrationArmorClass();
image.color = Plugin.GetArmorClassColor(armorClass); image.color = Plugin.GetArmorClassColor(armorClass);
} }
} }