Updated for client version 0.15.5.1 33420

This commit is contained in:
IcyClawz 2024-12-02 15:59:14 +02:00
parent ec50b42dc7
commit 438b0fdb9e
17 changed files with 346 additions and 186 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();
},
};
// 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;
} }
return [];
}
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 = () =>
@ -96,25 +78,136 @@ internal class FireModeSubMenu : CustomSubInteractions
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,
})); };
}
} }
} }
@ -126,31 +219,103 @@ internal static class ComponentUtils
if (player is not null && player.HandsController is Player.FirearmController fc && component.Item == fc.Item) if (player is not null && player.HandsController is Player.FirearmController fc && component.Item == fc.Item)
{ {
if (fc.Item.MalfState.State is Weapon.EMalfunctionState.None) if (fc.Item.MalfState.State is not Weapon.EMalfunctionState.None)
fc.ChangeFireMode(fireMode);
else
{ {
fc.FirearmsAnimator.MisfireSlideUnknown(false); fc.FirearmsAnimator.MisfireSlideUnknown(false);
player.GetInventoryController().ExamineMalfunction(fc.Item, false); player.InventoryController.ExamineMalfunction(fc.Item, false);
return;
} }
fc.ChangeFireMode(fireMode);
return; return;
} }
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,7 +3,7 @@
<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>
@ -27,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;
@ -91,7 +91,7 @@ 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}"))
: ""; : "";
}, },
@ -111,20 +111,20 @@ 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 [];
if (item.Owner?.OwnerType is EOwnerType.RagFair or EOwnerType.Trader if (item.Owner?.OwnerType is EOwnerType.RagFair or EOwnerType.Trader
&& (item.StackObjectsCount > 1 || item.UnlimitedCount)) && (item.StackObjectsCount > 1 || item.UnlimitedCount))
{ {
@ -133,11 +133,12 @@ internal static class ItemExtensions
item.UnlimitedCount = false; item.UnlimitedCount = false;
} }
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;
@ -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

@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net472</TargetFramework> <TargetFramework>net472</TargetFramework>
<AssemblyName>IcyClawz.MunitionsExpert</AssemblyName> <AssemblyName>IcyClawz.MunitionsExpert</AssemblyName>
<Version>1.4.2</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.2")] [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,11 +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;
if (ColorPanelField.GetValue(__instance) is not Image image) if (ColorPanelField.GetValue(__instance) is not Image image)
return; return;
int armorClass = bullet.AmmoTemplate.GetPenetrationArmorClass(); int armorClass = ammo.AmmoTemplate.GetPenetrationArmorClass();
image.color = Plugin.GetArmorClassColor(armorClass); image.color = Plugin.GetArmorClassColor(armorClass);
} }
} }