using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using Bifrost.Cooked;
using DooDesch;
using DooDesch.Weapons;
using DoubleBarrelPlus;
using HarmonyLib;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using Mimic;
using Mimic.Actors;
using Mimic.Animation;
using ReluProtocol;
using ReluProtocol.Enum;
using Steamworks;
using TMPro;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Core), "DoubleBarrelPlus", "1.1.1", "DooDesch", null)]
[assembly: MelonGame("ReLUGames", "MIMESIS")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("DooDesch")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © DooDesch")]
[assembly: AssemblyFileVersion("1.1.1.0")]
[assembly: AssemblyInformationalVersion("1.1.1+84e39077a211c16706ca561236a1f7d07ae8d9e3")]
[assembly: AssemblyProduct("DoubleBarrelPlus")]
[assembly: AssemblyTitle("DoubleBarrelPlus")]
[assembly: AssemblyMetadata("AI_Assisted_Creation", "This assembly was partially created with the assistance of Generative AI.")]
[assembly: AssemblyMetadata("AI_Model_Vendor", "Anthropic/OpenAI")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace DooDesch
{
internal sealed class HostSettings
{
[HarmonyPatch(typeof(SteamInviteDispatcher), "OnLobbyCreated")]
private static class CreatedPatch
{
private static void Postfix(LobbyCreated_t callback)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0007: Invalid comparison between Unknown and I4
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
if ((int)callback.m_eResult != 1)
{
return;
}
foreach (HostSettings instance in Instances)
{
instance.Begin(new CSteamID(callback.m_ulSteamIDLobby), host: true);
}
}
}
[HarmonyPatch(typeof(SteamInviteDispatcher), "OnLobbyEntered")]
private static class EnteredPatch
{
private static void Prefix(LobbyEnter_t callback)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
//IL_0043: Unknown result type (might be due to invalid IL or missing references)
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
//IL_003a: Unknown result type (might be due to invalid IL or missing references)
if (callback.m_EChatRoomEnterResponse != 1)
{
return;
}
CSteamID val = default(CSteamID);
((CSteamID)(ref val))..ctor(callback.m_ulSteamIDLobby);
foreach (HostSettings instance in Instances)
{
if (!instance.isHost || !(instance.lobby == val))
{
instance.Begin(val, host: false);
}
}
}
}
[HarmonyPatch(typeof(SteamMatchmaking), "LeaveLobby")]
private static class LeavePatch
{
private static void Prefix(CSteamID steamIDLobby)
{
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
foreach (HostSettings instance in Instances)
{
if (instance.lobby == steamIDLobby)
{
instance.Reset();
}
}
}
}
[HarmonyPatch(typeof(MainMenu), "Start")]
private static class MenuPatch
{
private static void Prefix()
{
foreach (HostSettings instance in Instances)
{
instance.Reset();
}
}
}
private static readonly List<HostSettings> Instances = new List<HostSettings>();
private static Callback<LobbyDataUpdate_t> dataCallback;
private static float nextPoll;
private readonly string modId;
private readonly string lobbyKey;
private readonly int schemaVersion;
private readonly List<ISetting> settings = new List<ISetting>();
private readonly string modernKey;
private string modernPayload;
private string lastModernPayload;
private long modernRevision;
private bool legacyHost;
private Dictionary<string, object> snapshot;
private CSteamID lobby;
private CSteamID owner;
private bool isHost;
private bool hostCreated;
private bool authorityValid;
private bool ownerChanged;
private bool published;
private string hostPayload;
private string warningReason;
private int warningRepeats;
internal bool IsInSession => lobby != CSteamID.Nil;
internal bool IsHost
{
get
{
if (isHost)
{
return authorityValid;
}
return false;
}
}
internal bool UsingLegacyHost
{
get
{
if (!isHost && authorityValid && legacyHost)
{
return modernRevision == 0;
}
return false;
}
}
internal event Action EffectiveValuesChanged;
internal event Action LiveValuesChanged;
internal HostSettings(string modId, int schemaVersion = 1)
{
if (string.IsNullOrWhiteSpace(modId))
{
throw new ArgumentException("A mod ID is required.", "modId");
}
if (schemaVersion < 1)
{
throw new ArgumentOutOfRangeException("schemaVersion");
}
this.modId = modId;
this.schemaVersion = schemaVersion;
lobbyKey = "doodesch." + modId.ToLowerInvariant() + ".settings.v" + schemaVersion;
modernKey = lobbyKey + ".extended";
if (Encoding.UTF8.GetByteCount(modernKey) >= 255)
{
throw new ArgumentException("The mod ID exceeds the Steam lobby key limit.", "modId");
}
foreach (HostSettings instance in Instances)
{
if (instance.lobbyKey == lobbyKey)
{
throw new ArgumentException("The mod already has a settings snapshot.", "modId");
}
}
Instances.Add(this);
}
internal HostSetting<T> Register<T>(MelonPreferences_Entry<T> entry, T vanillaValue, Func<T, bool> validate = null, bool live = false, bool legacy = true, Func<T> localValue = null)
{
//IL_005b: Unknown result type (might be due to invalid IL or missing references)
//IL_0060: Unknown result type (might be due to invalid IL or missing references)
if (entry == null)
{
throw new ArgumentNullException("entry");
}
if (string.IsNullOrWhiteSpace(((MelonPreferences_Entry)entry).Identifier))
{
throw new ArgumentException("A setting identifier is required.", "entry");
}
if (((MelonPreferences_Entry)entry).Identifier.IndexOfAny(new char[3] { '\n', '\r', '\t' }) >= 0)
{
throw new ArgumentException("Setting identifiers cannot contain wire delimiters.", "entry");
}
if (lobby != CSteamID.Nil)
{
throw new InvalidOperationException("Register settings before entering a lobby.");
}
if (typeof(T) != typeof(bool) && typeof(T) != typeof(int) && typeof(T) != typeof(float) && typeof(T) != typeof(string))
{
throw new NotSupportedException("Host settings support bool, int, float and string values.");
}
if (legacy && typeof(T) == typeof(string))
{
throw new ArgumentException("String settings must use the extended snapshot.", "legacy");
}
foreach (ISetting setting in settings)
{
if (setting.Key == ((MelonPreferences_Entry)entry).Identifier)
{
throw new ArgumentException("Duplicate setting: " + ((MelonPreferences_Entry)entry).Identifier, "entry");
}
}
HostSetting<T> hostSetting = new HostSetting<T>(this, entry, vanillaValue, validate, live, legacy, localValue);
if (!hostSetting.IsValid(vanillaValue))
{
throw new ArgumentException("The vanilla value is invalid: " + ((MelonPreferences_Entry)entry).Identifier, "vanillaValue");
}
if (!hostSetting.IsValid(entry.DefaultValue))
{
throw new ArgumentException("The configured default is invalid: " + ((MelonPreferences_Entry)entry).Identifier, "entry");
}
settings.Add(hostSetting);
return hostSetting;
}
internal T GetValue<T>(string key, T localValue, T vanillaValue, bool live)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
if (lobby == CSteamID.Nil)
{
return localValue;
}
if (live && !authorityValid)
{
return vanillaValue;
}
if (snapshot == null || !snapshot.TryGetValue(key, out var value))
{
return vanillaValue;
}
return (T)value;
}
internal static void Update()
{
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
//IL_0038: Unknown result type (might be due to invalid IL or missing references)
if (Time.realtimeSinceStartup < nextPoll)
{
return;
}
nextPoll = Time.realtimeSinceStartup + 1f;
foreach (HostSettings instance in Instances)
{
if (instance.lobby != CSteamID.Nil)
{
instance.Refresh();
}
}
}
private void Begin(CSteamID newLobby, bool host)
{
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
//IL_002e: Unknown result type (might be due to invalid IL or missing references)
//IL_003d: Unknown result type (might be due to invalid IL or missing references)
//IL_003e: Unknown result type (might be due to invalid IL or missing references)
//IL_0075: Unknown result type (might be due to invalid IL or missing references)
//IL_007a: Unknown result type (might be due to invalid IL or missing references)
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
//IL_0086: Unknown result type (might be due to invalid IL or missing references)
//IL_008b: Unknown result type (might be due to invalid IL or missing references)
//IL_0090: Unknown result type (might be due to invalid IL or missing references)
//IL_0095: Unknown result type (might be due to invalid IL or missing references)
//IL_0097: Unknown result type (might be due to invalid IL or missing references)
//IL_009c: Unknown result type (might be due to invalid IL or missing references)
//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
if (settings.Count == 0 || (lobby == newLobby && (!host || hostCreated)))
{
return;
}
if (lobby != newLobby)
{
Reset();
}
lobby = newLobby;
isHost = false;
hostCreated = host;
authorityValid = false;
ownerChanged = false;
snapshot = null;
published = false;
hostPayload = null;
owner = CSteamID.Nil;
try
{
owner = SteamMatchmaking.GetLobbyOwner(lobby);
CSteamID steamID = SteamUser.GetSteamID();
if (owner == CSteamID.Nil || (host ? (owner != steamID) : (owner == steamID)))
{
owner = CSteamID.Nil;
Warn("Lobby owner does not identify the expected host; using vanilla settings.");
Changed();
return;
}
isHost = host;
authorityValid = true;
if (host)
{
CaptureHostSnapshot();
}
else
{
CaptureClientSnapshot();
}
}
catch (Exception ex)
{
Warn("Initial lobby settings failed; vanilla settings are frozen for this session: " + ex.GetType().Name + ": " + ex.Message);
}
Refresh();
Changed();
}
private static void EnsureCallback()
{
if (dataCallback == null)
{
dataCallback = Callback<LobbyDataUpdate_t>.Create((DispatchDelegate<LobbyDataUpdate_t>)OnLobbyDataUpdated);
}
}
private static void OnLobbyDataUpdated(LobbyDataUpdate_t callback)
{
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
//IL_002e: Unknown result type (might be due to invalid IL or missing references)
//IL_0036: Unknown result type (might be due to invalid IL or missing references)
foreach (HostSettings instance in Instances)
{
if (instance.lobby.m_SteamID == callback.m_ulSteamIDLobby && callback.m_ulSteamIDMember == callback.m_ulSteamIDLobby)
{
if (callback.m_bSuccess == 0)
{
instance.Warn("Steam lobby data update failed; frozen session settings are retained.");
}
else
{
instance.Refresh();
}
}
}
}
private void CaptureHostSnapshot()
{
Dictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.Ordinal);
StringBuilder stringBuilder = new StringBuilder(schemaVersion.ToString(CultureInfo.InvariantCulture));
for (int i = 0; i < settings.Count; i++)
{
ISetting setting = settings[i];
object localValue = setting.LocalValue;
dictionary.Add(setting.Key, localValue);
if (setting.Legacy)
{
stringBuilder.Append('\n').Append(setting.Key).Append('\t')
.Append(Format(localValue));
}
}
string s = stringBuilder.ToString();
if (Encoding.UTF8.GetByteCount(s) + 1 > 8192)
{
throw new FormatException("Settings snapshot exceeds the Steam metadata limit.");
}
hostPayload = s;
snapshot = dictionary;
modernRevision = 1L;
modernPayload = BuildModern(dictionary, modernRevision);
}
private unsafe void CaptureClientSnapshot()
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0050: Unknown result type (might be due to invalid IL or missing references)
//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
string lobbyData = SteamMatchmaking.GetLobbyData(lobby, modernKey);
if (!string.IsNullOrEmpty(lobbyData))
{
snapshot = ParseModern(lobbyData, out modernRevision);
lastModernPayload = lobbyData;
MelonLogger.Msg("[" + modId + "] Host settings captured; live values follow the host, lobby values remain fixed.");
return;
}
string lobbyData2 = SteamMatchmaking.GetLobbyData(lobby, lobbyKey);
if (string.IsNullOrEmpty(lobbyData2))
{
Warn("Host snapshot is absent; vanilla settings are frozen for this session.");
return;
}
snapshot = ParseSnapshot(lobbyData2);
legacyHost = true;
string[] obj = new string[5] { "[", modId, "] Host settings snapshot frozen for lobby ", null, null };
CSteamID val = lobby;
obj[3] = ((object)(*(CSteamID*)(&val))/*cast due to .constrained prefix*/).ToString();
obj[4] = ".";
MelonLogger.Msg(string.Concat(obj));
}
private void Refresh()
{
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_0042: Unknown result type (might be due to invalid IL or missing references)
//IL_0043: Unknown result type (might be due to invalid IL or missing references)
//IL_001c: Unknown result type (might be due to invalid IL or missing references)
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_004f: Unknown result type (might be due to invalid IL or missing references)
//IL_0051: Unknown result type (might be due to invalid IL or missing references)
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
bool flag = false;
try
{
EnsureCallback();
CSteamID lobbyOwner = SteamMatchmaking.GetLobbyOwner(lobby);
if (ownerChanged || owner == CSteamID.Nil || lobbyOwner != owner)
{
authorityValid = false;
if (lobbyOwner != CSteamID.Nil && lobbyOwner != owner)
{
ownerChanged = true;
}
Warn("Lobby owner changed or is unavailable; authority revoked and frozen session settings retained.");
return;
}
bool num = !authorityValid;
authorityValid = true;
flag = true;
if (num)
{
MelonLogger.Msg("[" + modId + "] Pinned lobby authority recovered; frozen settings retained.");
}
if (isHost)
{
if (hostPayload != null)
{
if (published)
{
RefreshHostLive();
}
else
{
Publish();
}
}
}
else
{
RefreshClientLive();
}
}
catch (Exception ex)
{
authorityValid = flag;
Warn("Refresh lobby settings failed; frozen session settings retained: " + ex.GetType().Name + ": " + ex.Message);
}
}
private unsafe void Publish()
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0062: Unknown result type (might be due to invalid IL or missing references)
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0086: Unknown result type (might be due to invalid IL or missing references)
//IL_008b: Unknown result type (might be due to invalid IL or missing references)
//IL_003e: Unknown result type (might be due to invalid IL or missing references)
//IL_0043: Unknown result type (might be due to invalid IL or missing references)
//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
if (SteamMatchmaking.GetLobbyData(lobby, lobbyKey) != hostPayload && !SteamMatchmaking.SetLobbyData(lobby, lobbyKey, hostPayload))
{
CSteamID val = lobby;
Warn("SetLobbyData rejected the settings snapshot for lobby " + ((object)(*(CSteamID*)(&val))/*cast due to .constrained prefix*/).ToString() + ".");
}
else if (SteamMatchmaking.GetLobbyData(lobby, lobbyKey) != hostPayload)
{
CSteamID val = lobby;
Warn("Settings snapshot readback differs for lobby " + ((object)(*(CSteamID*)(&val))/*cast due to .constrained prefix*/).ToString() + ".");
}
else if (PublishModern(modernPayload))
{
published = true;
Recover();
string[] obj = new string[5] { "[", modId, "] Settings snapshot confirmed by local lobby readback for ", null, null };
CSteamID val = lobby;
obj[3] = ((object)(*(CSteamID*)(&val))/*cast due to .constrained prefix*/).ToString();
obj[4] = ".";
MelonLogger.Msg(string.Concat(obj));
}
}
private Dictionary<string, object> ParseSnapshot(string payload)
{
if (Encoding.UTF8.GetByteCount(payload) + 1 > 8192)
{
throw new FormatException("Settings snapshot exceeds the Steam metadata limit.");
}
string[] array = payload.Split('\n');
if (array[0] != schemaVersion.ToString(CultureInfo.InvariantCulture))
{
throw new FormatException("Settings snapshot schema mismatch.");
}
if (array.Length != settings.FindAll((ISetting s) => s.Legacy).Count + 1)
{
throw new FormatException("Settings snapshot has missing or extra keys.");
}
Dictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.Ordinal);
for (int num = 1; num < array.Length; num++)
{
string text = array[num];
int num2 = text.IndexOf('\t');
if (num2 <= 0 || num2 != text.LastIndexOf('\t') || text.IndexOf('\r') >= 0)
{
throw new FormatException("Settings snapshot has an invalid entry delimiter.");
}
string key = text.Substring(0, num2);
if (dictionary.ContainsKey(key))
{
throw new FormatException("Settings snapshot has a duplicate or missing key.");
}
ISetting setting = settings.Find((ISetting candidate) => candidate.Key == key && candidate.Legacy);
if (setting == null)
{
throw new FormatException("Settings snapshot has an unknown key: " + key);
}
dictionary.Add(key, setting.Parse(text.Substring(num2 + 1)));
}
return dictionary;
}
private string BuildModern(Dictionary<string, object> values, long revision)
{
StringBuilder stringBuilder = new StringBuilder("2\t" + revision.ToString(CultureInfo.InvariantCulture));
foreach (ISetting setting in settings)
{
stringBuilder.Append('\n').Append(setting.Key).Append('\t')
.Append(Format(values[setting.Key]));
}
string text = stringBuilder.ToString();
if (Encoding.UTF8.GetByteCount(text) + 1 > 8192)
{
throw new FormatException("Extended settings exceed the Steam metadata limit.");
}
return text;
}
private Dictionary<string, object> ParseModern(string payload, out long revision)
{
if (Encoding.UTF8.GetByteCount(payload) + 1 > 8192)
{
throw new FormatException("Extended settings exceed the Steam metadata limit.");
}
string[] array = payload.Split('\n');
string[] array2 = array[0].Split('\t');
if (array2.Length != 2 || array2[0] != "2" || !long.TryParse(array2[1], NumberStyles.None, CultureInfo.InvariantCulture, out revision) || revision < 1)
{
throw new FormatException("Invalid extended settings schema or revision.");
}
Dictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.Ordinal);
HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
for (int i = 1; i < array.Length; i++)
{
int num = array[i].IndexOf('\t');
if (num <= 0 || num != array[i].LastIndexOf('\t') || array[i].IndexOf('\r') >= 0)
{
throw new FormatException("Invalid extended settings delimiter.");
}
string key = array[i].Substring(0, num);
if (!hashSet.Add(key))
{
throw new FormatException("Duplicate extended setting: " + key);
}
ISetting setting = settings.Find((ISetting s) => s.Key == key);
if (setting != null)
{
dictionary.Add(key, setting.Parse(array[i].Substring(num + 1)));
}
}
return dictionary;
}
private bool PublishModern(string payload)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
if (SteamMatchmaking.GetLobbyData(lobby, modernKey) != payload && !SteamMatchmaking.SetLobbyData(lobby, modernKey, payload))
{
Warn("SetLobbyData rejected the extended settings; last confirmed values retained.");
return false;
}
if (SteamMatchmaking.GetLobbyData(lobby, modernKey) != payload)
{
Warn("Extended settings readback differs; last confirmed values retained.");
return false;
}
return true;
}
private void RefreshHostLive()
{
if (snapshot == null)
{
return;
}
Dictionary<string, object> dictionary = new Dictionary<string, object>(snapshot, StringComparer.Ordinal);
bool flag = false;
foreach (ISetting setting in settings)
{
if (setting.Live)
{
object localValue = setting.LocalValue;
if (!dictionary.TryGetValue(setting.Key, out var value) || !object.Equals(value, localValue))
{
dictionary[setting.Key] = localValue;
flag = true;
}
}
}
if (!flag)
{
if (PublishModern(modernPayload))
{
Recover();
}
return;
}
long revision = checked(modernRevision + 1);
string payload = BuildModern(dictionary, revision);
if (PublishModern(payload))
{
snapshot = dictionary;
modernRevision = revision;
modernPayload = payload;
Recover();
Changed(this.LiveValuesChanged);
}
}
private void RefreshClientLive()
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
string lobbyData = SteamMatchmaking.GetLobbyData(lobby, modernKey);
if (string.IsNullOrEmpty(lobbyData) || lobbyData == lastModernPayload)
{
return;
}
long revision;
Dictionary<string, object> dictionary = ParseModern(lobbyData, out revision);
if (revision <= modernRevision)
{
Warn("Extended settings revision did not advance; last confirmed values retained.");
return;
}
Dictionary<string, object> dictionary2 = ((snapshot == null) ? new Dictionary<string, object>(StringComparer.Ordinal) : new Dictionary<string, object>(snapshot, StringComparer.Ordinal));
bool flag = false;
foreach (ISetting setting in settings)
{
if (setting.Live && dictionary.TryGetValue(setting.Key, out var value) && (!dictionary2.TryGetValue(setting.Key, out var value2) || !object.Equals(value2, value)))
{
dictionary2[setting.Key] = value;
flag = true;
}
}
snapshot = dictionary2;
modernRevision = revision;
lastModernPayload = lobbyData;
Recover();
if (flag)
{
Changed(this.LiveValuesChanged);
}
}
private static string Format(object value)
{
if (value is string s)
{
return "s:" + Convert.ToBase64String(Encoding.UTF8.GetBytes(s));
}
if (value is float num)
{
return num.ToString("R", CultureInfo.InvariantCulture);
}
if (value is bool)
{
if (!(bool)value)
{
return "false";
}
return "true";
}
return ((int)value).ToString(CultureInfo.InvariantCulture);
}
private void Warn(string reason)
{
if (warningReason == reason)
{
warningRepeats++;
return;
}
if (warningRepeats > 0)
{
MelonLogger.Warning("[" + modId + "] Previous settings warning repeated " + warningRepeats + " times.");
}
warningReason = reason;
warningRepeats = 0;
MelonLogger.Warning("[" + modId + "] " + reason);
}
private void Recover()
{
if (warningReason != null)
{
MelonLogger.Msg("[" + modId + "] Lobby settings recovered after " + (warningRepeats + 1) + " warning occurrences.");
warningReason = null;
warningRepeats = 0;
}
}
private void Changed()
{
Changed(this.EffectiveValuesChanged);
}
private void Changed(Action handlers)
{
if (handlers == null)
{
return;
}
Delegate[] invocationList = handlers.GetInvocationList();
for (int i = 0; i < invocationList.Length; i++)
{
Action action = (Action)invocationList[i];
try
{
action();
}
catch (Exception ex)
{
MelonLogger.Error("[" + modId + "] Apply effective settings failed.", ex);
}
}
}
private void Reset()
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_001c: Unknown result type (might be due to invalid IL or missing references)
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
bool num = lobby != CSteamID.Nil;
lobby = CSteamID.Nil;
owner = CSteamID.Nil;
isHost = false;
hostCreated = false;
authorityValid = false;
ownerChanged = false;
published = false;
snapshot = null;
hostPayload = null;
modernPayload = null;
lastModernPayload = null;
modernRevision = 0L;
legacyHost = false;
warningReason = null;
warningRepeats = 0;
if (num)
{
MelonLogger.Msg("[" + modId + "] Session settings cleared; local settings restored.");
Changed();
}
}
}
internal interface ISetting
{
string Key { get; }
object LocalValue { get; }
bool Live { get; }
bool Legacy { get; }
bool IsValid(object value);
object Parse(string value);
}
internal sealed class HostSetting<T> : ISetting
{
private readonly HostSettings owner;
private readonly MelonPreferences_Entry<T> entry;
private readonly T vanillaValue;
private readonly Func<T, bool> validate;
private readonly Func<T> localValue;
private bool invalidLocal;
public bool Live { get; }
public bool Legacy { get; }
internal T Value => owner.GetValue(Key, (T)LocalValue, vanillaValue, Live);
public string Key => ((MelonPreferences_Entry)entry).Identifier;
public object LocalValue
{
get
{
T val = ((localValue == null) ? entry.Value : localValue());
if (IsValid(val))
{
if (invalidLocal)
{
MelonLogger.Msg("Local setting recovered: " + Key);
}
invalidLocal = false;
return val;
}
if (!invalidLocal)
{
MelonLogger.Warning("Invalid local setting " + Key + "; using its configured default without overwriting the file.");
}
invalidLocal = true;
return entry.DefaultValue;
}
}
internal HostSetting(HostSettings owner, MelonPreferences_Entry<T> entry, T vanillaValue, Func<T, bool> validate, bool live, bool legacy, Func<T> localValue)
{
this.owner = owner;
this.entry = entry;
this.vanillaValue = vanillaValue;
this.validate = validate;
this.localValue = localValue;
Live = live;
Legacy = legacy;
}
public bool IsValid(object value)
{
if (!(value is T arg))
{
return false;
}
if (value is float f && (float.IsNaN(f) || float.IsInfinity(f)))
{
return false;
}
if (value is string text && (Encoding.UTF8.GetByteCount(text) > 1024 || text.IndexOf('\0') >= 0))
{
return false;
}
if (validate != null)
{
return validate(arg);
}
return true;
}
public object Parse(string value)
{
object obj;
int result2;
float result3;
if (typeof(T) == typeof(bool) && bool.TryParse(value, out var result))
{
obj = result;
}
else if (typeof(T) == typeof(int) && int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result2))
{
obj = result2;
}
else if (typeof(T) == typeof(float) && float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out result3))
{
obj = result3;
}
else
{
if (!(typeof(T) == typeof(string)) || !value.StartsWith("s:", StringComparison.Ordinal))
{
throw new FormatException("Invalid settings value: " + Key);
}
obj = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true).GetString(Convert.FromBase64String(value.Substring(2)));
}
if (!IsValid(obj))
{
throw new FormatException("Settings value is out of range: " + Key);
}
return obj;
}
}
internal static class ModVersion
{
internal const string Current = "1.1.1";
}
}
namespace DooDesch.Weapons
{
internal static class WeaponDefinition
{
internal const string Name = "DoubleBarrelPlus";
internal static readonly int ItemId = 1500;
internal static bool IsGun => true;
internal static bool IsBat => false;
internal static bool IsReload(int skill)
{
return skill == 10902;
}
internal static bool HasSkill(int skill)
{
if (skill != 10900)
{
return skill == 10902;
}
return true;
}
}
[HarmonyPatch]
internal static class CombatConsumptionScope
{
[ThreadStatic]
internal static EquipmentItemElement Active;
private static IEnumerable<MethodBase> TargetMethods()
{
yield return AccessTools.DeclaredMethod(typeof(InventoryController), "OnUseSkill", (Type[])null, (Type[])null);
yield return AccessTools.DeclaredMethod(typeof(InventoryController), "OnHitSkill", (Type[])null, (Type[])null);
yield return AccessTools.DeclaredMethod(typeof(InventoryController), "OnUseSkill_SpawnProjectile", (Type[])null, (Type[])null);
}
private static void Prefix(InventoryController __instance, VCreature ____self, out EquipmentItemElement __state)
{
__state = Active;
Active = ((____self is VPlayer) ? WeaponRuntime.Current(__instance) : null);
}
private static Exception Finalizer(EquipmentItemElement __state, Exception __exception)
{
Active = __state;
return __exception;
}
}
[HarmonyPatch(typeof(EquipmentItemElement), "SetDurability")]
internal static class CombatWearPatch
{
private static void Prefix(EquipmentItemElement __instance, ref int durability)
{
if (CombatConsumptionScope.Active == __instance && WeaponPreferences.Unbreakable && durability < __instance.RemainDurability)
{
durability = __instance.RemainDurability;
}
}
}
[HarmonyPatch(typeof(EquipmentItemElement), "SetAmount")]
internal static class CombatEnergyPatch
{
private static void Prefix(EquipmentItemElement __instance, ref int amount)
{
if (CombatConsumptionScope.Active == __instance && WeaponPreferences.InfiniteGauge && amount < __instance.RemainAmount)
{
amount = __instance.RemainAmount;
}
}
}
[HarmonyPatch(typeof(InventoryController), "CanUseSkill")]
internal static class WeaponValidationPatch
{
private static void Prefix(InventoryController __instance, VCreature ____self, int skillMasterID)
{
if (!(____self is VPlayer) || !WeaponPreferences.InfiniteGauge || !WeaponDefinition.HasSkill(skillMasterID))
{
return;
}
EquipmentItemElement val = WeaponRuntime.Current(__instance);
if (val != null)
{
ItemEquipmentInfo val2 = WeaponRuntime.Info(val);
if (val2 != null && val.RemainAmount < val2.MaxGauge)
{
val.SetAmount(val2.MaxGauge);
}
}
}
private static void Postfix(InventoryController __instance, VCreature ____self, int skillMasterID, ref MsgErrorCode __result)
{
//IL_0039: Unknown result type (might be due to invalid IL or missing references)
//IL_003e: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
//IL_0087: Invalid comparison between Unknown and I4
//IL_004c: Unknown result type (might be due to invalid IL or missing references)
//IL_0052: Invalid comparison between Unknown and I4
//IL_0045: Unknown result type (might be due to invalid IL or missing references)
//IL_004b: Expected I4, but got Unknown
//IL_008a: Unknown result type (might be due to invalid IL or missing references)
//IL_0090: Invalid comparison between Unknown and I4
//IL_0055: Unknown result type (might be due to invalid IL or missing references)
//IL_0057: Expected I4, but got Unknown
if (!(____self is VPlayer) || !WeaponDefinition.IsGun)
{
return;
}
if (WeaponPreferences.FiniteMagazine && (int)__result == 20000016 && WeaponDefinition.HasSkill(skillMasterID) && !WeaponDefinition.IsReload(skillMasterID) && WeaponRuntime.Current(__instance) != null)
{
MsgErrorCode val = __instance.HandleReloadWeapon();
if ((int)val == 0)
{
__result = (MsgErrorCode)(int)__instance.CanUseSkill(skillMasterID);
}
else if ((int)val != 20000013)
{
__result = (MsgErrorCode)(int)val;
}
}
else if (WeaponPreferences.InfiniteReserve && (int)__result == 20000013)
{
EquipmentItemElement val2 = WeaponRuntime.Current(__instance);
ItemEquipmentInfo val3 = ((val2 == null) ? null : WeaponRuntime.Info(val2));
if (val3 != null && (int)val2.PartsType == 1 && (int)val3.WeaponType == 1 && skillMasterID == val3.ReloadSkillMasterID && WeaponRuntime.Data.ExcelDataManager.GetSkillInfo(skillMasterID) != null)
{
__result = (MsgErrorCode)0;
}
}
}
}
[HarmonyPatch(typeof(InventoryController), "HandleReloadWeapon")]
internal static class InfiniteReserveReloadPatch
{
private static bool Prefix(InventoryController __instance, VCreature ____self, ref MsgErrorCode __result)
{
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Invalid comparison between Unknown and I4
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_0038: Invalid comparison between Unknown and I4
//IL_007f: Unknown result type (might be due to invalid IL or missing references)
//IL_0084: Unknown result type (might be due to invalid IL or missing references)
//IL_0090: Unknown result type (might be due to invalid IL or missing references)
//IL_009c: Unknown result type (might be due to invalid IL or missing references)
//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
//IL_00ba: Expected O, but got Unknown
if (!(____self is VPlayer) || !WeaponPreferences.InfiniteReserve)
{
return true;
}
EquipmentItemElement val = WeaponRuntime.Current(__instance);
ItemEquipmentInfo val2 = ((val == null) ? null : WeaponRuntime.Info(val));
if (val2 == null || (int)val.PartsType != 1 || (int)val2.WeaponType != 1 || val2.ReloadSkillMasterID == 0 || WeaponRuntime.Data.ExcelDataManager.GetSkillInfo(val2.ReloadSkillMasterID) == null)
{
return true;
}
if (val.RemainAmount >= val2.MaxGauge)
{
__result = (MsgErrorCode)20000013;
return false;
}
val.SetAmount(val2.MaxGauge);
((VActor)____self).SendInSight((IMsg)new ReloadWeaponSig
{
actorID = ((VActor)____self).ObjectID,
currentHandEquipmentInfo = ((ItemElement)val).toItemInfo(),
inventoryInfos = __instance.GetInventoryInfos(),
currentInvenSlotIndex = __instance.CurrentInventorySlot
}, true);
__result = (MsgErrorCode)0;
return false;
}
}
[HarmonyPatch(typeof(ProtoActor), "TryUseEquipSkill")]
internal static class InfiniteGaugeInputPatch
{
private static bool Prefix(ProtoActor __instance, SkillHelper ___skillHelper, ItemEquipmentInfo equipMasterInfo, InventoryItem selectedItem, int skillIndex)
{
if ((!WeaponPreferences.InfiniteGauge && !WeaponPreferences.FiniteMagazine) || !__instance.AmIAvatar() || selectedItem == null || selectedItem.IsFake || selectedItem.ItemMasterID != WeaponDefinition.ItemId || skillIndex < 0 || skillIndex >= equipMasterInfo.SkillList.Length)
{
return true;
}
if (WeaponPreferences.FiniteMagazine && WeaponRuntime.RemainingAmmo(selectedItem, __instance.GetInventoryItems()).GetValueOrDefault() == 0L)
{
return true;
}
int skillMasterIDWithGauge = equipMasterInfo.SkillList[skillIndex].SkillMasterIDWithGauge;
if (skillMasterIDWithGauge <= 0 || !WeaponDefinition.HasSkill(skillMasterIDWithGauge))
{
return true;
}
___skillHelper.UseSkill(skillMasterIDWithGauge, (ProtoActor)null);
return false;
}
}
[HarmonyPatch(typeof(UIPrefab_Inventory), "UpdateSlot")]
internal static class WeaponDisplay
{
private static readonly FieldInfo InventoryUi = AccessTools.Field(typeof(GameMainBase), "inventoryui") ?? throw new MissingFieldException("GameMainBase", "inventoryui");
private static bool refresh;
internal static void RequestRefresh()
{
refresh = true;
}
internal static void Update()
{
if (refresh)
{
GameMainBase main = Hub.Main;
ProtoActor val = ((main != null) ? main.GetMyAvatar() : null);
if (!((Object)(object)val == (Object)null) && InventoryUi.GetValue(main) != null)
{
refresh = false;
main.UpdateInventoryUI(val);
}
}
}
private static void Postfix(List<InventoryItem> inventoryItems, List<int> visibleSlotIndices, List<Slot> ___inventorySlots)
{
if (!WeaponPreferences.ShowInfinity || !WeaponPreferences.Enabled || inventoryItems == null || visibleSlotIndices == null)
{
return;
}
foreach (int visibleSlotIndex in visibleSlotIndices)
{
if (visibleSlotIndex < 0 || visibleSlotIndex >= inventoryItems.Count || visibleSlotIndex >= ___inventorySlots.Count)
{
continue;
}
InventoryItem val = inventoryItems[visibleSlotIndex];
TMP_Text val2 = ___inventorySlots[visibleSlotIndex]?.stackCount;
if (val != null && !val.IsFake && val.ItemMasterID == WeaponDefinition.ItemId && !((Object)(object)val2 == (Object)null))
{
if (WeaponPreferences.FiniteMagazine)
{
long? num = WeaponRuntime.RemainingAmmo(val, inventoryItems);
val2.text = (num.HasValue ? ("∞/" + num.Value) : "∞");
}
else if (WeaponDefinition.IsGun && WeaponPreferences.AmmoMode == 1)
{
val2.text = val.RemainGauge + "/∞";
}
else if (WeaponPreferences.InfiniteGauge || (WeaponPreferences.Unbreakable && WeaponDefinition.IsBat))
{
val2.text = "∞";
}
}
}
}
}
internal static class WeaponPreferences
{
private static HostSettings settings;
private static HostSetting<bool> enabled;
private static HostSetting<bool> unbreakable;
private static HostSetting<bool> infiniteEnergy;
private static HostSetting<int> ammoMode;
private static MelonPreferences_Entry<bool> showInfinity;
private static HostSetting<float> damage;
private static HostSetting<float> range;
private static HostSetting<float> cooldown;
private static HostSetting<float> smashDamage;
private static HostSetting<float> smashCooldown;
private static HostSetting<float> shockDuration;
private static HostSetting<float> knockback;
private static HostSetting<float> throwSpeed;
private static HostSetting<float> throwDamage;
private static HostSetting<float> spread;
private static HostSetting<float> reloadTime;
internal static bool Enabled => enabled?.Value ?? false;
internal static bool Unbreakable
{
get
{
if (Enabled)
{
return unbreakable?.Value ?? false;
}
return false;
}
}
internal static int AmmoMode
{
get
{
if (!Enabled)
{
return 0;
}
return ammoMode?.Value ?? 0;
}
}
internal static bool InfiniteReserve
{
get
{
if (WeaponDefinition.IsGun)
{
if (AmmoMode != 1)
{
return AmmoMode == 2;
}
return true;
}
return false;
}
}
internal static bool FiniteMagazine
{
get
{
if (WeaponDefinition.IsGun)
{
return AmmoMode == 3;
}
return false;
}
}
internal static bool InfiniteGauge
{
get
{
if (Enabled)
{
if (!WeaponDefinition.IsGun)
{
return infiniteEnergy?.Value ?? false;
}
return AmmoMode == 2;
}
return false;
}
}
internal static bool ShowInfinity => showInfinity?.Value ?? true;
internal static float Range
{
get
{
if (!Enabled)
{
return 1f;
}
return range?.Value ?? 1f;
}
}
internal static float ShockDuration
{
get
{
if (!Enabled)
{
return 1f;
}
return shockDuration?.Value ?? 1f;
}
}
internal static float Knockback
{
get
{
if (!Enabled)
{
return 1f;
}
return knockback?.Value ?? 1f;
}
}
internal static float ThrowSpeed
{
get
{
if (!Enabled)
{
return 1f;
}
return throwSpeed?.Value ?? 1f;
}
}
internal static float ThrowDamage
{
get
{
if (!Enabled)
{
return 1f;
}
return throwDamage?.Value ?? 1f;
}
}
internal static float Spread
{
get
{
if (!Enabled)
{
return 1f;
}
return spread?.Value ?? 1f;
}
}
internal static float ReloadTime
{
get
{
if (!Enabled)
{
return 1f;
}
return reloadTime?.Value ?? 1f;
}
}
internal static float Damage(int skill)
{
if (!Enabled)
{
return 1f;
}
return ((skill == 10870) ? smashDamage : damage)?.Value ?? 1f;
}
internal static float Cooldown(int skill)
{
if (!WeaponDefinition.IsReload(skill))
{
if (!Enabled)
{
return 1f;
}
return ((skill == 10870) ? smashCooldown : cooldown)?.Value ?? 1f;
}
return ReloadTime;
}
internal static void Initialize()
{
if (settings != null)
{
return;
}
MelonPreferences_Category category = MelonPreferences.CreateCategory("DoubleBarrelPlus");
settings = new HostSettings("DoubleBarrelPlus");
enabled = Flag("Enabled", initial: true, "Enable this weapon's changes. Clients use native behavior when the host does not have this mod.");
unbreakable = Flag("Unbreakable", WeaponDefinition.IsBat, "Prevent combat durability loss for this weapon. Repairs and save values remain native.");
if (WeaponDefinition.IsGun)
{
ammoMode = settings.Register(category.CreateEntry<int>("AmmoMode", 1, (string)null, "Host setting, live. 0 = native ammo, 1 = infinite reserve with normal reload, 2 = infinite magazine without reload, 3 = no reload with finite loaded and inventory ammo.", false, false, (ValueValidator)null, (string)null), 0, (int value) => value >= 0 && value <= 3, live: true, legacy: false);
spread = Factor("SpreadMultiplier", "Multiply the native shotgun hit-cone angle. Range 0.1-4.", 0.1f, 4f);
reloadTime = Factor("ReloadTimeMultiplier", "Multiply reload animation, ammo insertion timing and reload cooldown together. Smaller = faster. Range 0.25-4.", 0.25f, 4f);
}
if (WeaponDefinition.ItemId == 2040)
{
infiniteEnergy = Flag("InfiniteEnergy", initial: true, "Keep charged attacks available without consuming energy. Durability is controlled separately.");
}
damage = Factor("DamageMultiplier", "Multiply this weapon's attack damage. Range 0-10.");
range = Factor("RangeMultiplier", "Multiply attack reach. Range 0.25-4.", 0.25f, 4f);
cooldown = Factor("CooldownMultiplier", "Multiply the native skill cooldown, independently of animation length. Range 0.1-5.", 0.1f, 5f);
if (WeaponDefinition.ItemId == 1600)
{
smashDamage = Factor("SmashDamageMultiplier", "Multiply Smash damage independently of the normal swing. Range 0-10.");
smashCooldown = Factor("SmashCooldownMultiplier", "Multiply the Smash cooldown. Range 0.1-5.", 0.1f, 5f);
}
if (WeaponDefinition.ItemId == 2040)
{
shockDuration = Factor("ShockDurationMultiplier", "Multiply charged electric-shock duration. Range 0.1-5.", 0.1f, 5f);
}
if (WeaponDefinition.ItemId == 2900)
{
knockback = Factor("KnockbackMultiplier", "Multiply native swing knockback distance. Range 0-4.", 0f, 4f);
throwSpeed = Factor("ThrowSpeedMultiplier", "Multiply initial throw speed. Native gravity and item recovery remain. Range 0.25-4.", 0.25f, 4f);
throwDamage = Factor("ThrowDamageMultiplier", "Multiply thrown crowbar damage independently of the swing. Range 0-10.");
}
showInfinity = category.CreateEntry<bool>("ShowInfinity", true, (string)null, "Local display only. Show native weapon counts with an infinity symbol when the host enables infinite ammo or energy.", false, false, (ValueValidator)null, (string)null);
settings.EffectiveValuesChanged += WeaponDisplay.RequestRefresh;
settings.LiveValuesChanged += WeaponDisplay.RequestRefresh;
((MelonEventBase<LemonAction<bool, bool>>)(object)showInfinity.OnEntryValueChanged).Subscribe((LemonAction<bool, bool>)delegate
{
WeaponDisplay.RequestRefresh();
}, 0, false);
HostSetting<float> Factor(string key, string description, float minimum = 0f, float maximum = 10f)
{
return settings.Register(category.CreateEntry<float>(key, 1f, (string)null, "Host setting, live for the next attack. 1 = native. " + description, false, false, (ValueValidator)null, (string)null), 1f, (float value) => !float.IsNaN(value) && !float.IsInfinity(value) && value >= minimum && value <= maximum, live: true, legacy: false);
}
HostSetting<bool> Flag(string key, bool initial, string description)
{
return settings.Register(category.CreateEntry<bool>(key, initial, (string)null, "Host setting, live. " + description, false, false, (ValueValidator)null, (string)null), vanillaValue: false, null, live: true, legacy: false);
}
}
}
internal static class WeaponReloadAnimation
{
private sealed class Track
{
internal Animator Animator;
internal ProtoActor Actor;
internal SkillHelper Helper;
internal int Skill;
internal float Before;
internal float Applied;
}
private static readonly List<Track> Tracks = new List<Track>();
private static readonly FieldInfo HelperField = AccessTools.Field(typeof(ProtoActor), "skillHelper") ?? throw new MissingFieldException("ProtoActor", "skillHelper");
internal static void Release(Animator animator)
{
for (int num = Tracks.Count - 1; num >= 0; num--)
{
if ((Object)(object)Tracks[num].Animator == (Object)(object)animator)
{
Restore(Tracks[num]);
Tracks.RemoveAt(num);
}
}
}
internal static void Begin(Animator animator, ProtoActor actor, int skill)
{
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
//IL_0019: Invalid comparison between Unknown and I4
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_0041: Expected O, but got Unknown
if (!((Object)(object)animator == (Object)null) && !((Object)(object)actor == (Object)null) && (int)actor.ActorType == 1 && WeaponDefinition.IsReload(skill) && WeaponPreferences.ReloadTime != 1f)
{
SkillHelper val = (SkillHelper)HelperField.GetValue(actor);
if (val == null)
{
throw new InvalidOperationException("Reload animation: player SkillHelper is missing.");
}
Release(animator);
Track track = new Track
{
Animator = animator,
Actor = actor,
Helper = val,
Skill = skill,
Before = animator.speed,
Applied = animator.speed / WeaponPreferences.ReloadTime
};
animator.speed = track.Applied;
Tracks.Add(track);
}
}
internal static void Update()
{
for (int num = Tracks.Count - 1; num >= 0; num--)
{
Track track = Tracks[num];
InventoryItem val = (((Object)(object)track.Actor == (Object)null) ? null : track.Actor.GetSelectedInventoryItem());
if (!((Object)(object)track.Animator != (Object)null) || !((Object)(object)track.Actor != (Object)null) || ((val != null) ? new int?(val.ItemMasterID) : ((int?)null)) != WeaponDefinition.ItemId || track.Helper.currentCastingSkillMasterIdCached != track.Skill || !track.Helper.IsUsingSkill())
{
Restore(track);
Tracks.RemoveAt(num);
}
}
}
private static void Restore(Track track)
{
if (!((Object)(object)track.Animator == (Object)null))
{
if (Mathf.Approximately(track.Animator.speed, track.Applied))
{
track.Animator.speed = track.Before;
}
else
{
MelonLogger.Warning(string.Format("[{0}] Reload animation speed changed externally; preserving current speed {1}.", "DoubleBarrelPlus", track.Animator.speed));
}
}
}
private static ImmutableArray<T> Events<T>(ImmutableArray<T> original, float factor) where T : AnimNotifyInfo
{
ImmutableArray<T>.Builder builder = ImmutableArray.CreateBuilder<T>(original.Length);
ImmutableArray<T>.Enumerator enumerator = original.GetEnumerator();
while (enumerator.MoveNext())
{
T current = enumerator.Current;
T val = WeaponCopies.Copy<T>(current);
WeaponCopies.Set<AnimNotifyInfo>((AnimNotifyInfo)(object)val, "Start", ((AnimNotifyInfo)current).Start * (double)factor);
object obj = val;
AnimNotifyHitCheck val2 = (AnimNotifyHitCheck)((obj is AnimNotifyHitCheck) ? obj : null);
if (val2 != null)
{
WeaponCopies.Set<AnimNotifyHitCheck>(val2, "Duration", val2.Duration * (double)factor);
}
else
{
object obj2 = val;
AnimNotifyPickedHit val3 = (AnimNotifyPickedHit)((obj2 is AnimNotifyPickedHit) ? obj2 : null);
if (val3 != null)
{
WeaponCopies.Set<AnimNotifyPickedHit>(val3, "Duration", val3.Duration * (double)factor);
}
else
{
object obj3 = val;
AnimNotifyNextSkill val4 = (AnimNotifyNextSkill)((obj3 is AnimNotifyNextSkill) ? obj3 : null);
if (val4 != null)
{
WeaponCopies.Set<AnimNotifyNextSkill>(val4, "Duration", val4.Duration * (double)factor);
}
}
}
builder.Add(val);
}
return builder.ToImmutable();
}
internal static SkillAnimInfo Scale(SkillAnimInfo original, float factor)
{
SkillAnimInfo obj = WeaponCopies.Copy<SkillAnimInfo>(original);
WeaponCopies.Set<SkillAnimInfo>(obj, "Length", original.Length * (double)factor);
WeaponCopies.Set<SkillAnimInfo>(obj, "ReloadWeapons", Events<AnimNotifyReloadWeapon>(original.ReloadWeapons, factor));
WeaponCopies.Set<SkillAnimInfo>(obj, "AllowMoves", Events<AnimNotifyAllowMove>(original.AllowMoves, factor));
WeaponCopies.Set<SkillAnimInfo>(obj, "NextSkills", Events<AnimNotifyNextSkill>(original.NextSkills, factor));
WeaponCopies.Set<SkillAnimInfo>(obj, "HitChecks", Events<AnimNotifyHitCheck>(original.HitChecks, factor));
WeaponCopies.Set<SkillAnimInfo>(obj, "PickedHits", Events<AnimNotifyPickedHit>(original.PickedHits, factor));
WeaponCopies.Set<SkillAnimInfo>(obj, "Projectiles", Events<AnimNotifyProjectile>(original.Projectiles, factor));
WeaponCopies.Set<SkillAnimInfo>(obj, "FieldSkills", Events<AnimNotifyFieldSkill>(original.FieldSkills, factor));
WeaponCopies.Set<SkillAnimInfo>(obj, "WeaponDestroyes", Events<AnimNotifyDestroyWeapon>(original.WeaponDestroyes, factor));
WeaponCopies.Set<SkillAnimInfo>(obj, "ImmuneAppliers", Events<AnimNotifyImmuneApplier>(original.ImmuneAppliers, factor));
WeaponCopies.Set<SkillAnimInfo>(obj, "ActivateAuras", Events<AnimNotifyHandleAura>(original.ActivateAuras, factor));
WeaponCopies.Set<SkillAnimInfo>(obj, "DestroyLootingObjects", Events<AnimNotifyDestroyLootingObject>(original.DestroyLootingObjects, factor));
return obj;
}
}
[HarmonyPatch(typeof(SkillHelper), "OnNetUseSkillSig")]
internal static class ReloadClientDurationPatch
{
private static void Postfix(ProtoActor ___owner, UseSkillSig sig, ref double ____usingSkillEndTime)
{
InventoryItem selectedInventoryItem = ___owner.GetSelectedInventoryItem();
if (((selectedInventoryItem != null) ? new int?(selectedInventoryItem.ItemMasterID) : ((int?)null)) == WeaponDefinition.ItemId && !selectedInventoryItem.IsFake && WeaponDefinition.IsReload(sig.skillMasterID))
{
____usingSkillEndTime = Time.timeAsDouble + (____usingSkillEndTime - Time.timeAsDouble) * (double)WeaponPreferences.ReloadTime;
}
}
}
[HarmonyPatch(typeof(PuppetScript), "UseSkill")]
internal static class ReloadPuppetAnimationPatch
{
private static void Prefix(Animator ___anim)
{
WeaponReloadAnimation.Release(___anim);
}
private static void Postfix(PuppetScript __instance, Animator ___anim, int skillMasterID)
{
ProtoActor owner = __instance.Owner;
InventoryItem val = ((owner != null) ? owner.GetSelectedInventoryItem() : null);
if (((val != null) ? new int?(val.ItemMasterID) : ((int?)null)) == WeaponDefinition.ItemId && !val.IsFake)
{
WeaponReloadAnimation.Begin(___anim, __instance.Owner, skillMasterID);
}
}
}
[HarmonyPatch(typeof(SocketAttachable), "OnReload")]
internal static class ReloadWeaponAnimationPatch
{
private static readonly PropertyInfo Owner = AccessTools.Property(typeof(SocketAttachable), "owner") ?? throw new MissingMemberException("SocketAttachable", "owner");
private static void Postfix(SocketAttachable __instance, Animator ___animator)
{
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
//IL_0019: Expected O, but got Unknown
if (WeaponDefinition.IsGun)
{
ProtoActor val = (ProtoActor)Owner.GetValue(__instance);
InventoryItem val2 = ((val != null) ? val.GetSelectedInventoryItem() : null);
if (((val2 != null) ? new int?(val2.ItemMasterID) : ((int?)null)) == WeaponDefinition.ItemId && !val2.IsFake)
{
ItemMasterInfo masterInfo = val2.MasterInfo;
WeaponReloadAnimation.Begin(___animator, val, ((ItemEquipmentInfo)(((masterInfo is ItemEquipmentInfo) ? masterInfo : null)?)).ReloadSkillMasterID ?? 0);
}
}
}
}
internal static class WeaponRuntime
{
private static readonly PropertyInfo DataManagerProperty = AccessTools.Property(typeof(Hub), "dataman") ?? throw new MissingMemberException("Hub", "dataman");
internal static DataManager Data
{
get
{
//IL_001c: Unknown result type (might be due to invalid IL or missing references)
//IL_0022: Expected O, but got Unknown
if (!((Object)(object)Hub.s == (Object)null))
{
return (DataManager)DataManagerProperty.GetValue(Hub.s);
}
return null;
}
}
internal static EquipmentItemElement Current(InventoryController inventory)
{
if (!WeaponPreferences.Enabled || inventory == null)
{
return null;
}
if (inventory.GetAllItemElements().TryGetValue(inventory.CurrentInventorySlot, out var value))
{
EquipmentItemElement val = (EquipmentItemElement)(object)((value is EquipmentItemElement) ? value : null);
if (val != null && !((ItemElement)val).IsFake && ((ItemElement)val).ItemMasterID == WeaponDefinition.ItemId)
{
return val;
}
}
return null;
}
internal static bool Matches(VCreature actor, int skill)
{
if (actor is VPlayer && Current(((VActor)actor).InventoryControlUnit) != null)
{
return WeaponDefinition.HasSkill(skill);
}
return false;
}
internal static ItemEquipmentInfo Info(EquipmentItemElement item)
{
DataManager data = Data;
ItemMasterInfo obj = ((data != null) ? data.ExcelDataManager.GetItemInfo(((ItemElement)item).ItemMasterID) : null);
return (ItemEquipmentInfo)(object)((obj is ItemEquipmentInfo) ? obj : null);
}
internal static long? RemainingAmmo(InventoryItem weapon, List<InventoryItem> inventory)
{
//IL_0088: Unknown result type (might be due to invalid IL or missing references)
//IL_008e: Invalid comparison between Unknown and I4
//IL_0092: Unknown result type (might be due to invalid IL or missing references)
//IL_0098: Unknown result type (might be due to invalid IL or missing references)
ItemMasterInfo obj = ((weapon != null) ? weapon.MasterInfo : null);
ItemEquipmentInfo val = (ItemEquipmentInfo)(object)((obj is ItemEquipmentInfo) ? obj : null);
if (val == null || inventory == null)
{
return null;
}
DataManager data = Data;
SkillInfo val2 = ((data != null) ? data.ExcelDataManager.GetSkillInfo(val.ReloadSkillMasterID) : null);
if (val2 == null)
{
return null;
}
long num = Math.Max(0, weapon.RemainGauge);
foreach (InventoryItem item in inventory)
{
ItemMasterInfo obj2 = ((item != null) ? item.MasterInfo : null);
ItemConsumableInfo val3 = (ItemConsumableInfo)(object)((obj2 is ItemConsumableInfo) ? obj2 : null);
if (val3 != null && (int)val3.ConsumeType == 1 && val3.BulletType == val2.BulletType)
{
num += Math.Max(0, item.StackCount);
}
}
return num;
}
}
internal static class WeaponCopies
{
private static readonly MethodInfo MemberwiseCopy = AccessTools.Method(typeof(object), "MemberwiseClone", (Type[])null, (Type[])null);
private static readonly Dictionary<(Type, string), FieldInfo> Fields = new Dictionary<(Type, string), FieldInfo>();
internal static T Copy<T>(T source) where T : class
{
return (T)MemberwiseCopy.Invoke(source, null);
}
internal static void Set<T>(T target, string name, object value) where T : class
{
(Type, string) key = (typeof(T), name);
if (!Fields.TryGetValue(key, out var value2))
{
value2 = AccessTools.Field(typeof(T), name) ?? throw new MissingFieldException(typeof(T).FullName, name);
Fields.Add(key, value2);
}
value2.SetValue(target, value);
}
internal static long Scale(long value, float factor)
{
return checked((long)Math.Round((double)value * (double)factor, MidpointRounding.AwayFromZero));
}
}
[HarmonyPatch(/*Could not decode attribute arguments.*/)]
internal static class WeaponAttackContextPatch
{
internal sealed class AttackValues
{
internal float Range;
internal float Spread;
internal float Damage;
}
private static readonly ConditionalWeakTable<SkillContextBaseInfo, AttackValues> Attacks = new ConditionalWeakTable<SkillContextBaseInfo, AttackValues>();
internal static AttackValues For(SkillContextBaseInfo context)
{
if (!Attacks.TryGetValue(context, out var value))
{
return null;
}
return value;
}
internal static void CopyValues(SkillContextBaseInfo source, SkillContextBaseInfo copy)
{
AttackValues attackValues = For(source);
if (attackValues != null)
{
Attacks.Add(copy, attackValues);
}
}
private static void Prefix(VCreature actor, ref SkillInfo skillInfo, ref SkillAnimInfo animInfo)
{
if (skillInfo == null || !WeaponRuntime.Matches(actor, skillInfo.MasterID))
{
return;
}
if (WeaponDefinition.IsReload(skillInfo.MasterID))
{
if (animInfo != null && WeaponPreferences.ReloadTime != 1f)
{
animInfo = WeaponReloadAnimation.Scale(animInfo, WeaponPreferences.ReloadTime);
}
}
else if (WeaponPreferences.Range != 1f)
{
SkillInfo val = WeaponCopies.Copy<SkillInfo>(skillInfo);
WeaponCopies.Set<SkillInfo>(val, "MaxRange", skillInfo.MaxRange * WeaponPreferences.Range);
WeaponCopies.Set<SkillInfo>(val, "AllowRange", skillInfo.AllowRange * WeaponPreferences.Range);
skillInfo = val;
}
}
private static void Postfix(SkillContextBaseInfo __instance)
{
if (WeaponRuntime.Matches(__instance.Creature, __instance.SkillInfo.MasterID))
{
Attacks.Add(__instance, new AttackValues
{
Range = WeaponPreferences.Range,
Spread = (WeaponDefinition.IsGun ? WeaponPreferences.Spread : 1f),
Damage = WeaponPreferences.Damage(__instance.SkillInfo.MasterID)
});
}
}
}
[HarmonyPatch(/*Could not decode attribute arguments.*/)]
internal static class WeaponAttackContextCopyPatch
{
private static void Postfix(SkillContextBaseInfo __instance, SkillContextBaseInfo other)
{
WeaponAttackContextPatch.CopyValues(other, __instance);
}
}
[HarmonyPatch(typeof(SkillContext<UseSkillSig>), "OnHit")]
internal static class WeaponDamageScope
{
[ThreadStatic]
internal static VActor Actor;
[ThreadStatic]
internal static float Factor;
[ThreadStatic]
internal static int Sequence;
private static void Prefix(ISkillContext __instance, HitInputData targetPack, out (VActor, float, int) __state)
{
__state = (Actor, Factor, Sequence);
WeaponAttackContextPatch.AttackValues attackValues = WeaponAttackContextPatch.For(__instance.ContextInfo);
Actor = (VActor)(object)((attackValues == null) ? null : __instance.ContextInfo.Creature);
Factor = attackValues?.Damage ?? 1f;
Sequence = targetPack.SkillSequenceInfo.MasterID;
}
private static Exception Finalizer((VActor, float, int) __state, Exception __exception)
{
(Actor, Factor, Sequence) = __state;
return __exception;
}
}
[HarmonyPatch(typeof(VWorldCombatUtil), "CalculateDamage")]
internal static class WeaponDamagePatch
{
private static void Postfix(VActor attacker, int skillSeqMasterID, ref DamageAttribute __result)
{
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_004a: Expected O, but got Unknown
if (attacker != null && attacker == WeaponDamageScope.Actor && skillSeqMasterID == WeaponDamageScope.Sequence && WeaponDamageScope.Factor != 1f && __result != null)
{
__result = new DamageAttribute(WeaponCopies.Scale(__result.Damage, WeaponDamageScope.Factor), __result.GroggyValue, __result.Critical);
}
}
}
[HarmonyPatch(typeof(CooltimeController), "AddCooltime")]
internal static class WeaponCooldownPatch
{
private static void Prefix(VActor ____self, CooltimeType type, int masterID, ref long idCooltime)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
if ((int)type == 0)
{
VCreature val = (VCreature)(object)((____self is VCreature) ? ____self : null);
if (val != null && WeaponRuntime.Matches(val, masterID))
{
idCooltime = WeaponCopies.Scale(idCooltime, WeaponPreferences.Cooldown(masterID));
}
}
}
}
[HarmonyPatch(typeof(SkillHelper), "ReserveEndSkillCoolTime")]
internal static class WeaponClientCooldownPatch
{
private static void Prefix(ProtoActor ___owner, int skillMasterID, ref float durationSec)
{
InventoryItem selectedInventoryItem = ___owner.GetSelectedInventoryItem();
if (((selectedInventoryItem != null) ? new int?(selectedInventoryItem.ItemMasterID) : ((int?)null)) == WeaponDefinition.ItemId && !selectedInventoryItem.IsFake && WeaponDefinition.HasSkill(skillMasterID))
{
durationSec *= WeaponPreferences.Cooldown(skillMasterID);
}
}
}
[HarmonyPatch]
internal static class WeaponHitboxScope
{
[ThreadStatic]
internal static float Range;
[ThreadStatic]
internal static float Spread;
private static IEnumerable<MethodBase> TargetMethods()
{
Type type = typeof(SkillContext<UseSkillSig>);
yield return AccessTools.DeclaredMethod(type, "CollectSkillTargetInfoByHit", (Type[])null, (Type[])null);
yield return AccessTools.DeclaredMethod(type, "DestroyHitDestructibleProjectiles", (Type[])null, (Type[])null);
}
private static void Prefix(ISkillContext __instance, out (float, float) __state)
{
__state = (Range, Spread);
WeaponAttackContextPatch.AttackValues attackValues = WeaponAttackContextPatch.For(__instance.ContextInfo);
Range = attackValues?.Range ?? 1f;
Spread = attackValues?.Spread ?? 1f;
}
private static Exception Finalizer((float, float) __state, Exception __exception)
{
(Range, Spread) = __state;
return __exception;
}
}
[HarmonyPatch(typeof(AnimNotifyDataManager), "GetHitBox")]
internal static class WeaponHitboxPatch
{
private static void Postfix(ref IHitCheck __result)
{
//IL_003e: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_004a: Unknown result type (might be due to invalid IL or missing references)
//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
//IL_00ce: Expected O, but got Unknown
//IL_00df: Unknown result type (might be due to invalid IL or missing references)
//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
//IL_0100: Expected O, but got Unknown
//IL_0091: Unknown result type (might be due to invalid IL or missing references)
//IL_0097: Expected O, but got Unknown
//IL_0123: Unknown result type (might be due to invalid IL or missing references)
//IL_0129: Unknown result type (might be due to invalid IL or missing references)
//IL_0130: Unknown result type (might be due to invalid IL or missing references)
//IL_013c: Unknown result type (might be due to invalid IL or missing references)
//IL_0142: Expected O, but got Unknown
//IL_0153: Unknown result type (might be due to invalid IL or missing references)
//IL_0159: Unknown result type (might be due to invalid IL or missing references)
//IL_017e: Unknown result type (might be due to invalid IL or missing references)
//IL_0184: Expected O, but got Unknown
float range = WeaponHitboxScope.Range;
float spread = WeaponHitboxScope.Spread;
if (range == 0f || (range == 1f && (spread == 0f || spread == 1f)) || __result == null)
{
return;
}
IHitCheck obj = __result;
FanHitCheck val = (FanHitCheck)(object)((obj is FanHitCheck) ? obj : null);
if (val != null)
{
__result = (IHitCheck)new FanHitCheck(val.Center * range, val.Rotation, val.Height, val.InnerRad * range, val.OuterRad * range, Mathf.Clamp(val.Angle * ((spread == 0f) ? 1f : spread), 1f, 360f), val.Key);
return;
}
IHitCheck obj2 = __result;
CubeHitCheck val2 = (CubeHitCheck)(object)((obj2 is CubeHitCheck) ? obj2 : null);
if (val2 != null)
{
__result = (IHitCheck)new CubeHitCheck(val2.Center * range, val2.Extent * range, val2.Rotation, val2.Key);
return;
}
IHitCheck obj3 = __result;
SphereHitCheck val3 = (SphereHitCheck)(object)((obj3 is SphereHitCheck) ? obj3 : null);
if (val3 != null)
{
__result = (IHitCheck)new SphereHitCheck(val3.Center * range, val3.Rad * range, val3.Key);
return;
}
IHitCheck obj4 = __result;
CapsuleHitCheck val4 = (CapsuleHitCheck)(object)((obj4 is CapsuleHitCheck) ? obj4 : null);
if (val4 != null)
{
__result = (IHitCheck)new CapsuleHitCheck(val4.Rad * range, val4.Length * range, val4.Center * range, val4.Rotation, val4.Key);
return;
}
IHitCheck obj5 = __result;
TorusHitCheck val5 = (TorusHitCheck)(object)((obj5 is TorusHitCheck) ? obj5 : null);
if (val5 != null)
{
__result = (IHitCheck)new TorusHitCheck(val5.Center * range, val5.Height, val5.OuterRad * range, val5.InnerRad * range, val5.Key);
return;
}
throw new NotSupportedException("Weapon reach: unsupported hitbox " + ((object)__result).GetType().FullName);
}
}
[HarmonyPatch(typeof(AbnormalController), "ApplyAbnormalInternal")]
internal static class WeaponAbnormalPatch
{
private static void Prefix(AbnormalController __instance, int casterObjectID, int skillMasterID, ref AbnormalInfo info, ref int duration)
{
//IL_015b: Unknown result type (might be due to invalid IL or missing references)
//IL_0161: Invalid comparison between Unknown and I4
VActor obj = ((VActor)__instance.Self).VRoom.FindActorByObjectID(casterObjectID);
if (!WeaponRuntime.Matches((VCreature)(object)((obj is VCreature) ? obj : null), skillMasterID))
{
return;
}
if (WeaponDefinition.ItemId == 2040 && skillMasterID == 10850 && info.MasterID == 24001 && WeaponPreferences.ShockDuration != 1f)
{
duration = checked((int)WeaponCopies.Scale((duration > 0) ? duration : info.MaxDuration, WeaponPreferences.ShockDuration));
}
if (WeaponDefinition.ItemId != 2900 || skillMasterID != 10806 || info.MasterID != 18006 || (WeaponPreferences.Knockback == 1f && WeaponPreferences.Damage(skillMasterID) == 1f))
{
return;
}
ImmutableDictionary<int, IAbnormalElementInfo>.Builder builder = info.ElementList.ToBuilder();
foreach (KeyValuePair<int, IAbnormalElementInfo> element in info.ElementList)
{
IAbnormalElementInfo value = element.Value;
CCElementInfo val = (CCElementInfo)(object)((value is CCElementInfo) ? value : null);
if (val != null && val.BattleActionInfo != null && WeaponPreferences.Knockback != 1f)
{
BattleActionInfo val2 = WeaponCopies.Copy<BattleActionInfo>(val.BattleActionInfo);
WeaponCopies.Set<BattleActionInfo>(val2, "Distance", val2.Distance * WeaponPreferences.Knockback);
CCElementInfo val3 = WeaponCopies.Copy<CCElementInfo>(val);
WeaponCopies.Set<CCElementInfo>(val3, "BattleActionInfo", val2);
builder[element.Key] = (IAbnormalElementInfo)(object)val3;
continue;
}
IAbnormalElementInfo value2 = element.Value;
AbnormalStatsElementInfo val4 = (AbnormalStatsElementInfo)(object)((value2 is AbnormalStatsElementInfo) ? value2 : null);
if (val4 != null && (int)val4.MutableStatType == 1 && val4.Value < 0)
{
AbnormalStatsElementInfo val5 = WeaponCopies.Copy<AbnormalStatsElementInfo>(val4);
WeaponCopies.Set<AbnormalStatsElementInfo>(val5, "Value", WeaponCopies.Scale(val4.Value, WeaponPreferences.Damage(skillMasterID)));
builder[element.Key] = (IAbnormalElementInfo)(object)val5;
}
}
info = WeaponCopies.Copy<AbnormalInfo>(info);
WeaponCopies.Set<AbnormalInfo>(info, "ElementList", builder.ToImmutable());
}
}
[HarmonyPatch(/*Could not decode attribute arguments.*/)]
internal static class CrowbarProjectilePatch
{
private static void Postfix(int masterID, ISkillContext skillContext, ref ProjectileInfo ____projectileInfo, ref float ____speed, ref Vector3 ____initialVector)
{
//IL_004a: Unknown result type (might be due to invalid IL or missing references)
//IL_0054: Unknown result type (might be due to invalid IL or missing references)
//IL_0059: Unknown result type (might be due to invalid IL or missing references)
if (WeaponDefinition.ItemId == 2900 && masterID == 52900 && skillContext != null && WeaponRuntime.Matches(skillContext.ContextInfo.Creature, skillContext.SkillMasterID))
{
if (WeaponPreferences.ThrowSpeed != 1f)
{
____speed *= WeaponPreferences.ThrowSpeed;
____initialVector *= WeaponPreferences.ThrowSpeed;
}
if (WeaponPreferences.ThrowDamage != 1f)
{
ProjectileInfo val = WeaponCopies.Copy<ProjectileInfo>(____projectileInfo);
WeaponCopies.Set<ProjectileInfo>(val, "MutableValue", WeaponCopies.Scale(val.MutableValue, WeaponPreferences.ThrowDamage));
____projectileInfo = val;
}
}
}
}
}
namespace DoubleBarrelPlus
{
public sealed class Core : MelonMod
{
public override void OnInitializeMelon()
{
WeaponPreferences.Initialize();
MelonLogger.Msg("DoubleBarrelPlus initialized for item 1500. Gameplay follows the host.");
}
public override void OnUpdate()
{
HostSettings.Update();
WeaponDisplay.Update();
WeaponReloadAnimation.Update();
}
}
}