using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using FishNet;
using FishNet.Broadcast;
using FishNet.Connection;
using FishNet.Object;
using FishNet.Serializing;
using FishNet.Transporting;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ArceDev.ExoticUpgrades")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.2.0.0")]
[assembly: AssemblyInformationalVersion("0.2.0")]
[assembly: AssemblyProduct("ArceDev.ExoticUpgrades")]
[assembly: AssemblyTitle("Exotic Upgrades")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.2.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
[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 BepInEx
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
[Conditional("CodeGeneration")]
[Embedded]
internal sealed class BepInAutoPluginAttribute : Attribute
{
public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
{
}
}
}
namespace BepInEx.Preloader.Core.Patching
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
[Conditional("CodeGeneration")]
[Embedded]
internal sealed class PatcherAutoPluginAttribute : Attribute
{
public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
{
}
}
}
namespace Microsoft.CodeAnalysis
{
[Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace ExoticUpgrades
{
internal static class ExoticGacha
{
private readonly record struct PendingReward(Player Player, byte WeaponId, int UpgradeIndex, byte SlotIndex);
[HarmonyPatch(typeof(SlotMachineManager), "SendRoll")]
private static class SendRollPatch
{
private static void Prefix(byte[] itemIDs, byte[] itemSkins)
{
float num = Mathf.Clamp01(_exoticChance.Value);
for (int i = 0; i < itemIDs.Length; i++)
{
Item val = ((itemIDs[i] == byte.MaxValue) ? null : GameInfo.IDToItem(itemIDs[i]));
Weapon val2 = (Weapon)(object)((val is Weapon) ? val : null);
if (val2 != null && UpgradeState.SupportsProjectileUpgrades(val2) && Random.value < num)
{
itemSkins[i] = Encode(Random.Range(0, UpgradeState.UpgradeCount));
}
}
}
}
[HarmonyPatch(typeof(SlotMachine), "Roll")]
private static class RollPatch
{
private static void Prefix(Player roller, byte[] itemIDs, byte[] itemSkins, byte rolled)
{
_pending = (TryDecode(itemSkins[rolled], out var upgradeIndex) ? new PendingReward?(new PendingReward(roller, itemIDs[rolled], upgradeIndex, rolled)) : ((PendingReward?)null));
}
private static void Postfix()
{
PendingReward? pending = _pending;
if (pending.HasValue)
{
RolledSkinField.SetValue(null, (byte)0);
}
}
}
[HarmonyPatch(typeof(MachineSlot), "SetIndex")]
private static class SetIndexPatch
{
private static void Prefix(MachineSlot __instance, ref byte skinIndex)
{
if (TryDecode(skinIndex, out var upgradeIndex))
{
SlotUpgrades[__instance] = upgradeIndex;
skinIndex = 0;
}
else
{
SlotUpgrades.Remove(__instance);
}
}
private static void Postfix(MachineSlot __instance, TextMeshProUGUI ____itemText, Image ____outline)
{
((TMP_Text)____itemText).text = "";
((Behaviour)____outline).enabled = true;
HideUpgradeIcon(__instance);
if (SlotUpgrades.TryGetValue(__instance, out var value))
{
ExoticUpgrade upgrade = UpgradeState.GetUpgrade(value);
((Behaviour)____outline).enabled = false;
ShowUpgradeIcon(__instance, ((Component)____outline).transform.parent, upgrade);
}
}
}
[HarmonyPatch(typeof(SlotMachine), "OnRollFinished")]
private static class RollFinishedPatch
{
private static void Prefix(MachineSlot[] ____machineSlots)
{
PendingReward? pending = _pending;
if (pending.HasValue)
{
PendingReward valueOrDefault = pending.GetValueOrDefault();
if (valueOrDefault.SlotIndex < ____machineSlots.Length && UpgradeIcons.TryGetValue(____machineSlots[valueOrDefault.SlotIndex], out Image value) && Object.op_Implicit((Object)(object)value))
{
SetWinnerIconLayout(((Graphic)value).rectTransform);
}
if (Object.op_Implicit((Object)(object)valueOrDefault.Player) && ((NetworkBehaviour)valueOrDefault.Player).Owner.IsLocalClient)
{
UpgradeState.Unlock(valueOrDefault.Player.SteamID, valueOrDefault.WeaponId, valueOrDefault.UpgradeIndex);
}
}
}
private static void Postfix(TextMeshProUGUI ____rollerText)
{
PendingReward? pending = _pending;
if (pending.HasValue)
{
PendingReward valueOrDefault = pending.GetValueOrDefault();
string name = GameInfo.IDToItem(valueOrDefault.WeaponId).GetName();
string displayName = UpgradeState.GetUpgrade(valueOrDefault.UpgradeIndex).DisplayName;
((TMP_Text)____rollerText).text = "EXOTIC: " + displayName + " — " + name;
}
_pending = null;
}
}
private const byte EncodedUpgradeStart = 128;
private const float DefaultExoticChance = 0.25f;
private static readonly FieldInfo RolledSkinField = AccessTools.Field(typeof(SlotMachine), "_skinIndex");
private static readonly Dictionary<MachineSlot, int> SlotUpgrades = new Dictionary<MachineSlot, int>();
private static readonly Dictionary<MachineSlot, Image> UpgradeIcons = new Dictionary<MachineSlot, Image>();
private static readonly Dictionary<string, Sprite> IconSprites = new Dictionary<string, Sprite>();
private static ConfigEntry<float> _exoticChance = null;
private static PendingReward? _pending;
internal static void Initialize(ConfigFile config)
{
_exoticChance = config.Bind<float>("Gacha", "ExoticChance", 0.25f, "Chance from 0 to 1 that each eligible weapon-skin slot becomes an exotic-upgrade slot.");
}
internal static void Validate()
{
for (int i = 0; i < UpgradeState.UpgradeCount; i++)
{
if (!TryDecode(Encode(i), out var upgradeIndex) || upgradeIndex != i)
{
throw new InvalidOperationException("Exotic gacha encoding validation failed.");
}
byte[] array = ReadIconPng(UpgradeState.GetUpgrade(i));
if (array.Length < 8 || array[0] != 137 || array[1] != 80 || array[2] != 78 || array[3] != 71)
{
throw new InvalidOperationException("Exotic gacha icon validation failed.");
}
}
float num = 0.54999995f;
float num2 = 0.54999995f;
if (Mathf.Abs(num - num2) > 0.001f)
{
throw new InvalidOperationException("Exotic winner layout must remain square at 2:1.");
}
}
private static void HideUpgradeIcon(MachineSlot slot)
{
if (UpgradeIcons.TryGetValue(slot, out Image value) && Object.op_Implicit((Object)(object)value))
{
((Component)value).gameObject.SetActive(false);
}
}
private static void ShowUpgradeIcon(MachineSlot slot, Transform parent, ExoticUpgrade upgrade)
{
//IL_004c: Unknown result type (might be due to invalid IL or missing references)
//IL_0052: Expected O, but got Unknown
//IL_0076: Unknown result type (might be due to invalid IL or missing references)
//IL_007c: Expected O, but got Unknown
//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
if (!UpgradeIcons.TryGetValue(slot, out Image value) || !Object.op_Implicit((Object)(object)value))
{
GameObject val = new GameObject("ExoticUpgradeIcon", new Type[3]
{
typeof(RectTransform),
typeof(CanvasRenderer),
typeof(Image)
});
val.layer = ((Component)parent).gameObject.layer;
val.transform.SetParent(parent, false);
RectTransform val2 = (RectTransform)val.transform;
((Transform)val2).SetAsLastSibling();
value = val.GetComponent<Image>();
value.preserveAspect = true;
((Graphic)value).raycastTarget = false;
UpgradeIcons[slot] = value;
}
SetPreviewIconLayout(((Graphic)value).rectTransform);
value.sprite = GetUpgradeIconSprite(upgrade);
((Graphic)value).color = upgrade.BadgeColor;
((Component)value).gameObject.SetActive(true);
((Component)value).transform.SetAsLastSibling();
}
private static void SetPreviewIconLayout(RectTransform rect)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: 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)
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
SetIconLayout(rect, Vector2.one / 6f, Vector2.one * 5f / 6f, 0.005f);
}
private static void SetWinnerIconLayout(RectTransform rect)
{
//IL_000b: 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)
SetIconLayout(rect, new Vector2(0.3625f, 0.225f), new Vector2(0.6375f, 0.775f), 0f);
}
private static void SetIconLayout(RectTransform rect, Vector2 anchorMin, Vector2 anchorMax, float inset)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0008: 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_0020: Unknown result type (might be due to invalid IL or missing references)
rect.anchorMin = anchorMin;
rect.anchorMax = anchorMax;
rect.offsetMin = new Vector2(inset, inset);
rect.offsetMax = new Vector2(0f - inset, 0f - inset);
}
private static Sprite GetUpgradeIconSprite(ExoticUpgrade upgrade)
{
//IL_0023: 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_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
//IL_004e: Expected O, but got Unknown
//IL_0098: Unknown result type (might be due to invalid IL or missing references)
//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
if (IconSprites.TryGetValue(upgrade.IconResource, out Sprite value) && Object.op_Implicit((Object)(object)value))
{
return value;
}
Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false)
{
name = "ExoticUpgrades_" + upgrade.Id,
filterMode = (FilterMode)1,
wrapMode = (TextureWrapMode)1
};
if (!ImageConversion.LoadImage(val, ReadIconPng(upgrade), true))
{
Object.Destroy((Object)(object)val);
throw new InvalidOperationException("Exotic icon '" + upgrade.IconResource + "' is not a valid PNG.");
}
value = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
((Object)value).name = "ExoticUpgrades_" + upgrade.Id;
IconSprites[upgrade.IconResource] = value;
return value;
}
private static byte[] ReadIconPng(ExoticUpgrade upgrade)
{
using Stream stream = typeof(ExoticGacha).Assembly.GetManifestResourceStream("ExoticUpgrades." + upgrade.IconResource) ?? throw new InvalidOperationException("Embedded exotic icon '" + upgrade.IconResource + "' was not found.");
using MemoryStream memoryStream = new MemoryStream();
stream.CopyTo(memoryStream);
return memoryStream.ToArray();
}
private static byte Encode(int upgradeIndex)
{
return (byte)(128 + upgradeIndex);
}
private static bool TryDecode(byte encoded, out int upgradeIndex)
{
upgradeIndex = encoded - 128;
if (encoded >= 128)
{
return upgradeIndex < UpgradeState.UpgradeCount;
}
return false;
}
}
internal static class ExplosiveBullets
{
private const int DefaultDamage = 50;
private const float DefaultRadius = 5f;
private const float DefaultItemForce = 800f;
private static readonly Dictionary<Projectile, ProjectileProfile> Tracked = new Dictionary<Projectile, ProjectileProfile>();
private static readonly MethodInfo ExplodeEffects = AccessTools.Method(typeof(ExplosionManager), "ExplodeEffects", (Type[])null, (Type[])null);
private static ConfigEntry<int> _damage = null;
private static ConfigEntry<float> _radius = null;
private static ConfigEntry<float> _itemForce = null;
private static ExplosionInfo? _nativeExplosion;
private static bool _warnedMissingEffects;
internal static int Damage => Mathf.Max(0, _damage.Value);
internal static float Radius => Mathf.Max(0f, _radius.Value);
internal static float ItemForce => Mathf.Max(0f, _itemForce.Value);
internal static void Initialize(ConfigFile config)
{
_damage = config.Bind<int>("Explosive Bullets", "Damage", 50, "Explosion damage dealt to each creature in range, in addition to the direct bullet hit.");
_radius = config.Bind<float>("Explosive Bullets", "Radius", 5f, "Damage and item-force radius in metres.");
_itemForce = config.Bind<float>("Explosive Bullets", "ItemForce", 800f, "Explosion force applied to nearby loose items.");
}
internal static void Track(Projectile projectile, ProjectileProfile profile)
{
if (profile.Effects.HasFlag(ProjectileEffects.Explosive))
{
Tracked[projectile] = profile;
}
}
internal static void Clear()
{
Tracked.Clear();
}
internal static void Explode(Projectile projectile, Vector3 position)
{
//IL_0017: Unknown result type (might be due to invalid IL or missing references)
if (Tracked.Remove(projectile) && projectile.IsLocal)
{
ProjectileNetwork.RequestExplosion(projectile, position);
}
}
internal static void RemoveExpired(ProjectileManager manager)
{
List<Projectile> list = null;
foreach (Projectile key in Tracked.Keys)
{
if (!manager.GetType(key.TypeId).Projectiles.Contains(key))
{
(list ?? (list = new List<Projectile>())).Add(key);
}
}
if (list == null)
{
return;
}
foreach (Projectile item in list)
{
Tracked.Remove(item);
}
}
internal static void DamageOnServer(Player owner, Vector3 position, ProjectileProfile profile)
{
//IL_0066: Unknown result type (might be due to invalid IL or missing references)
//IL_006b: Unknown result type (might be due to invalid IL or missing references)
//IL_006c: Unknown result type (might be due to invalid IL or missing references)
//IL_0071: 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)
float num = profile.ExplosionRadius * profile.ExplosionRadius;
HashSet<Creature> hashSet = new HashSet<Creature>();
foreach (Item value in ItemManager.Items.Values)
{
Creature val = (Object.op_Implicit((Object)(object)value) ? value.Creature : null);
if (Object.op_Implicit((Object)(object)val) && !val.IsDead && hashSet.Add(val))
{
Vector3 val2 = ((Item)val).Rig.worldCenterOfMass - position;
if (((Vector3)(ref val2)).sqrMagnitude <= num)
{
val.ServerChangeHp(profile.ExplosionDamage);
val.ObserverExplosionHit(owner, ((Vector3)(ref val2)).normalized, profile.ExplosionDamage);
}
}
}
}
internal static void PlayExplosion(Vector3 position, float radius, float force)
{
//IL_0000: 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)
PlayNativeEffects(position);
PushItems(position, radius, force);
}
internal static void Validate()
{
if ((object)ExplodeEffects == null)
{
throw new InvalidOperationException("Explosive Bullets validation failed.");
}
}
private static void PlayNativeEffects(Vector3 position)
{
//IL_0092: Unknown result type (might be due to invalid IL or missing references)
if (_nativeExplosion == null)
{
_nativeExplosion = (from explosive in Resources.FindObjectsOfTypeAll<Explosive>()
orderby ((Object)explosive).name.IndexOf("dynamite", StringComparison.OrdinalIgnoreCase) >= 0 descending
select ((Item)explosive).GetExplosionInfo()).FirstOrDefault((Func<ExplosionInfo, bool>)((ExplosionInfo info) => info != null));
}
if (_nativeExplosion != null)
{
ExplodeEffects.Invoke(null, new object[2] { position, _nativeExplosion });
}
else if (!_warnedMissingEffects)
{
_warnedMissingEffects = true;
Plugin.Log.LogWarning((object)"Could not find a native dynamite explosion for Explosive Bullets effects.");
}
}
private static void PushItems(Vector3 position, float radius, float force)
{
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
//IL_006c: Unknown result type (might be due to invalid IL or missing references)
//IL_0072: Unknown result type (might be due to invalid IL or missing references)
//IL_0076: Unknown result type (might be due to invalid IL or missing references)
//IL_007c: 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)
radius = Mathf.Max(0f, radius);
force = Mathf.Max(0f, force);
HashSet<Item> hashSet = new HashSet<Item>();
Collider[] array = Physics.OverlapSphere(position, radius, LayerMask.op_Implicit(GameInfo.AffectedByExplosionLayer));
foreach (Collider val in array)
{
Item val2 = ItemManager.Get(val);
if (Object.op_Implicit((Object)(object)val2) && !Object.op_Implicit((Object)(object)val2.Holder) && hashSet.Add(val2))
{
val2.RigidbodySync.StartSimulateLocal(default(Vector3), default(Quaternion));
val2.Rig.AddExplosionForce(force, position, radius);
}
}
}
}
internal static class HomingBullets
{
private readonly record struct HomingProjectile(Projectile Projectile, ProjectileProfile Profile, int TargetObjectId);
private const float AggressivePrototypeTurnDegreesPerSecond = 720f;
internal const float DefaultTurnDegreesPerSecond = 180f;
private const float DefaultRange = 60f;
private const float DefaultMinimumForwardDot = -0.25f;
private static readonly Dictionary<ProjectileKey, HomingProjectile> Tracked = new Dictionary<ProjectileKey, HomingProjectile>();
private static readonly Dictionary<ProjectileKey, int> Targets = new Dictionary<ProjectileKey, int>();
private static ConfigEntry<float> _range = null;
private static ConfigEntry<float> _minimumForwardDot = null;
private static ConfigEntry<float> _turnDegreesPerSecond = null;
internal static float Range => Mathf.Max(0f, _range.Value);
internal static float MinimumForwardDot => Mathf.Clamp(_minimumForwardDot.Value, -1f, 1f);
internal static float TurnDegreesPerSecond => Mathf.Max(0f, _turnDegreesPerSecond.Value);
internal static void Initialize(ConfigFile config)
{
_range = config.Bind<float>("Homing Bullets", "TargetRange", 60f, "Maximum target acquisition distance in metres.");
_minimumForwardDot = config.Bind<float>("Homing Bullets", "MinimumForwardDot", -0.25f, "Target cone: 1 is straight ahead, 0 includes side targets, and -1 includes targets behind.");
_turnDegreesPerSecond = config.Bind<float>("Homing Bullets", "TurnDegreesPerSecond", 180f, "Maximum projectile steering speed in degrees per second.");
if (Mathf.Approximately(_turnDegreesPerSecond.Value, 720f))
{
_turnDegreesPerSecond.Value = 180f;
}
}
internal static void Track(Projectile projectile, ProjectileKey key, ProjectileProfile profile)
{
if (profile.Effects.HasFlag(ProjectileEffects.Homing))
{
Tracked[key] = new HomingProjectile(projectile, profile, Targets.TryGetValue(key, out var value) ? value : (-1));
}
}
internal static void SetTarget(ProjectileKey key, int targetObjectId)
{
Targets[key] = targetObjectId;
if (Tracked.TryGetValue(key, out var value))
{
Tracked[key] = value with
{
TargetObjectId = targetObjectId
};
}
}
internal static void Clear()
{
Tracked.Clear();
Targets.Clear();
}
internal static void Steer(ProjectileManager manager)
{
if (Tracked.Count == 0)
{
return;
}
List<ProjectileKey> list = null;
List<(Projectile, Creature)> list2 = null;
foreach (var (item, tracked) in Tracked)
{
if (!UpdateProjectile(manager, tracked, out Creature acquired))
{
(list ?? (list = new List<ProjectileKey>())).Add(item);
}
else if (Object.op_Implicit((Object)(object)acquired))
{
(list2 ?? (list2 = new List<(Projectile, Creature)>())).Add((tracked.Projectile, acquired));
}
}
ShareTargets(list2);
RemoveExpired(list);
}
private static bool UpdateProjectile(ProjectileManager manager, HomingProjectile tracked, out Creature? acquired)
{
//IL_0062: Unknown result type (might be due to invalid IL or missing references)
//IL_0068: Unknown result type (might be due to invalid IL or missing references)
//IL_006d: Unknown result type (might be due to invalid IL or missing references)
//IL_0072: 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_0096: Unknown result type (might be due to invalid IL or missing references)
//IL_009b: Unknown result type (might be due to invalid IL or missing references)
Projectile projectile = tracked.Projectile;
acquired = null;
if (!manager.GetType(projectile.TypeId).Projectiles.Contains(projectile))
{
return false;
}
Creature val = CreatureFor(tracked.TargetObjectId);
if (!Object.op_Implicit((Object)(object)val) && projectile.IsLocal)
{
val = (acquired = NearestTarget(projectile, tracked.Profile));
}
if (Object.op_Implicit((Object)(object)val))
{
Vector3 desired = ((Item)val).Rig.worldCenterOfMass - projectile.Position;
projectile.Velocity = TurnTowards(projectile.Velocity, desired, tracked.Profile.HomingTurnSpeed * (MathF.PI / 180f) * Time.fixedDeltaTime);
}
return true;
}
private static void ShareTargets(List<(Projectile Projectile, Creature Target)>? acquired)
{
if (acquired == null)
{
return;
}
foreach (var (projectile, target) in acquired)
{
ProjectileNetwork.ShareTarget(projectile, target);
}
}
private static void RemoveExpired(List<ProjectileKey>? expired)
{
if (expired == null)
{
return;
}
foreach (ProjectileKey item in expired)
{
Tracked.Remove(item);
Targets.Remove(item);
}
}
internal static void Validate()
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0032: 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)
Vector3 val = TurnTowards(Vector3.forward * 10f, Vector3.right, 0.25f);
if (!Mathf.Approximately(((Vector3)(ref val)).magnitude, 10f) || val.x <= 0f || val.z <= 0f)
{
throw new InvalidOperationException("Homing steering validation failed.");
}
}
private static Creature? NearestTarget(Projectile projectile, ProjectileProfile profile)
{
//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_0091: 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_00a1: 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_00b5: Unknown result type (might be due to invalid IL or missing references)
Creature result = null;
float homingRange = profile.HomingRange;
float num = homingRange * homingRange;
float homingMinimumDot = profile.HomingMinimumDot;
Vector3 normalized = ((Vector3)(ref projectile.Velocity)).normalized;
HashSet<Creature> hashSet = new HashSet<Creature>();
foreach (Item value in ItemManager.Items.Values)
{
if (value == null || !Object.op_Implicit((Object)(object)value))
{
continue;
}
Creature creature = value.Creature;
if (Object.op_Implicit((Object)(object)creature) && !creature.IsDead && !Object.op_Implicit((Object)(object)value.Holder) && hashSet.Add(creature))
{
Vector3 val = ((Item)creature).Rig.worldCenterOfMass - projectile.Position;
float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
if (sqrMagnitude < num && Vector3.Dot(normalized, ((Vector3)(ref val)).normalized) >= homingMinimumDot)
{
result = creature;
num = sqrMagnitude;
}
}
}
return result;
}
private static Creature? CreatureFor(int objectId)
{
if (objectId < 0)
{
return null;
}
foreach (Item value in ItemManager.Items.Values)
{
if (Object.op_Implicit((Object)(object)value) && ((NetworkBehaviour)value).ObjectId == objectId && Object.op_Implicit((Object)(object)value.Creature) && !value.Creature.IsDead)
{
return value.Creature;
}
}
return null;
}
private static Vector3 TurnTowards(Vector3 velocity, Vector3 desired, float maxRadians)
{
//IL_0038: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: 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_0027: Unknown result type (might be due to invalid IL or missing references)
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
float magnitude = ((Vector3)(ref velocity)).magnitude;
if (!(magnitude <= Mathf.Epsilon) && !(((Vector3)(ref desired)).sqrMagnitude <= Mathf.Epsilon))
{
return Vector3.RotateTowards(velocity, ((Vector3)(ref desired)).normalized * magnitude, maxRadians, 0f);
}
return velocity;
}
}
[BepInPlugin("ArceDev.ExoticUpgrades", "Exotic Upgrades", "0.2.0")]
public class Plugin : BaseUnityPlugin
{
[HarmonyPatch(typeof(PlayerHolding), "ChangeSkinInput")]
private static class ChangeSkinInputPatch
{
private static bool Prefix(Player ____player, Item ____heldItem, ref CallbackContext context)
{
if (____player.BlockInputs || !InspectHeld() || !Object.op_Implicit((Object)(object)____heldItem) || !Object.op_Implicit((Object)(object)____heldItem.Weapon))
{
return true;
}
return !UpgradeState.TrySelect(____heldItem.Weapon, ((CallbackContext)(ref context)).ReadValue<float>() > 0f);
}
}
[HarmonyPatch(typeof(PlayerHolding), "ReloadInput")]
private static class ReloadInputPatch
{
private static bool Prefix(Player ____player, Item ____heldItem)
{
if (____player.BlockInputs || !InspectHeld() || !Object.op_Implicit((Object)(object)____heldItem) || !Object.op_Implicit((Object)(object)____heldItem.Weapon))
{
return true;
}
return !UpgradeState.TryToggleSelected(____heldItem.Weapon);
}
}
[HarmonyPatch(typeof(WeaponUI), "UpdateInspectedItemText")]
private static class InspectionTextPatch
{
private static void Prefix(Item ____inspectedItem, ref string ____extraDescription)
{
if (Object.op_Implicit((Object)(object)____inspectedItem) && Object.op_Implicit((Object)(object)____inspectedItem.Weapon))
{
____extraDescription = UpgradeState.WithInspection(____extraDescription, ____inspectedItem.Weapon);
}
}
}
[HarmonyPatch(typeof(ProjectileManager), "AddProjectile")]
private static class AddProjectilePatch
{
private static bool Prefix(ProjectileManager __instance, Player owner, WeaponInfo weaponInfo, bool isLocal, Vector3 pos, ref Vector3 velocity, uint catchingUpToDo, uint id, bool fromNpc, out int __state)
{
//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_00b0: Unknown result type (might be due to invalid IL or missing references)
//IL_007f: 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)
__state = __instance.GetType(weaponInfo.ProjectileType).Projectiles.Count;
if (!isLocal || fromNpc || !Object.op_Implicit((Object)(object)weaponInfo.Weapon))
{
return true;
}
ProjectileProfile profile = ProjectileProfile.Capture(weaponInfo.Weapon).Clamped();
if (profile.Effects == ProjectileEffects.None)
{
return true;
}
if (profile.Effects.HasFlag(ProjectileEffects.SplitShot) && profile.SplitCount > 1)
{
__state = ~__state;
__instance.AddProjectiles(owner, weaponInfo, true, pos, (Vector3[])(object)new Vector3[1] { velocity }, catchingUpToDo, id, false);
return false;
}
velocity = SlowMoBullets.ApplySpeed(velocity, profile);
ProjectileNetwork.SendShot(owner, ProjectileNetwork.NextId(__instance), 1, profile);
return true;
}
private static void Postfix(ProjectileManager __instance, WeaponInfo weaponInfo, int __state)
{
if (__state >= 0)
{
ProjectileNetwork.RegisterNew(__instance, weaponInfo, __state);
}
}
}
[HarmonyPatch(typeof(ProjectileManager), "AddProjectiles")]
private static class AddProjectilesPatch
{
private static void Prefix(ProjectileManager __instance, Player owner, WeaponInfo weaponInfo, bool isLocal, ref Vector3[] velocities, out int __state)
{
__state = __instance.GetType(weaponInfo.ProjectileType).Projectiles.Count;
if (isLocal && Object.op_Implicit((Object)(object)weaponInfo.Weapon))
{
ProjectileProfile profile = ProjectileProfile.Capture(weaponInfo.Weapon).Clamped();
if (profile.Effects != ProjectileEffects.None)
{
velocities = SplitShotBullets.Expand(velocities, profile);
ApplySlowSpeed(velocities, profile);
ProjectileNetwork.SendShot(owner, ProjectileNetwork.NextId(__instance), velocities.Length, profile);
}
}
}
private static void Postfix(ProjectileManager __instance, WeaponInfo weaponInfo, int __state)
{
ProjectileNetwork.RegisterNew(__instance, weaponInfo, __state);
}
}
[HarmonyPatch(typeof(ProjectileManager), "FixedUpdate")]
private static class ProjectileUpdatePatch
{
private static void Prefix(ProjectileManager __instance)
{
HomingBullets.Steer(__instance);
ExplosiveBullets.RemoveExpired(__instance);
RicochetBullets.RemoveExpired(__instance);
ProjectileNetwork.Cleanup();
}
}
[HarmonyPatch(typeof(ProjectileManager), "Hit")]
private static class ProjectileHitPatch
{
private static bool Prefix(Projectile projectile, ref RaycastHit hit, out bool __state)
{
//IL_0003: Unknown result type (might be due to invalid IL or missing references)
__state = RicochetBullets.TryBounce(projectile, hit);
return !__state;
}
private static void Postfix(Projectile projectile, ref RaycastHit hit, bool __state)
{
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
if (!__state)
{
ExplosiveBullets.Explode(projectile, ((RaycastHit)(ref hit)).point);
}
}
}
[HarmonyPatch(typeof(Player), "OnStartClient")]
private static class PlayerClientStartPatch
{
private static void Postfix()
{
ProjectileNetwork.RegisterClient();
ProjectileNetwork.RegisterServer();
}
}
[HarmonyPatch(typeof(Server), "OnStartClient")]
private static class NetworkStartPatch
{
private static void Postfix()
{
ProjectileNetwork.RegisterClient();
ProjectileNetwork.RegisterServer();
}
}
[HarmonyPatch(typeof(Server), "OnStopClient")]
private static class NetworkStopPatch
{
private static void Prefix()
{
ProjectileNetwork.ResetSession();
}
}
public const string Id = "ArceDev.ExoticUpgrades";
internal static ManualLogSource Log { get; private set; }
public static string Name => "Exotic Upgrades";
public static string Version => "0.2.0";
private void Awake()
{
Log = ((BaseUnityPlugin)this).Logger;
UpgradeState.Initialize(((BaseUnityPlugin)this).Config);
ProjectileNetwork.Initialize();
HomingBullets.Initialize(((BaseUnityPlugin)this).Config);
ExplosiveBullets.Initialize(((BaseUnityPlugin)this).Config);
RicochetBullets.Initialize(((BaseUnityPlugin)this).Config);
SlowMoBullets.Initialize(((BaseUnityPlugin)this).Config);
SplitShotBullets.Initialize(((BaseUnityPlugin)this).Config);
ExoticGacha.Initialize(((BaseUnityPlugin)this).Config);
ExoticGacha.Validate();
UpgradeState.Validate();
HomingBullets.Validate();
ExplosiveBullets.Validate();
RicochetBullets.Validate();
SlowMoBullets.Validate();
SplitShotBullets.Validate();
ProjectileNetwork.Validate();
Harmony.CreateAndPatchAll(typeof(Plugin).Assembly, "ArceDev.ExoticUpgrades");
Log.LogInfo((object)("Plugin " + Name + " is loaded!"));
Log.LogInfo((object)"Exotic upgrades coexist with native skins in each island's slot-machine weapon pool.");
}
private static bool InspectHeld()
{
if (Object.op_Implicit((Object)(object)GameInfo.Input))
{
return GameInfo.Input.actions["WeaponInspect"].IsPressed();
}
return false;
}
private static void ApplySlowSpeed(Vector3[] velocities, ProjectileProfile profile)
{
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_000e: Unknown result type (might be due to invalid IL or missing references)
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
for (int i = 0; i < velocities.Length; i++)
{
velocities[i] = SlowMoBullets.ApplySpeed(velocities[i], profile);
}
}
}
[Flags]
internal enum ProjectileEffects : byte
{
None = 0,
Homing = 1,
Explosive = 2,
Ricochet = 4,
SlowMo = 8,
SplitShot = 0x10
}
internal readonly record struct ProjectileKey(int OwnerObjectId, uint ProjectileId);
internal readonly record struct ProjectileProfile(ProjectileEffects Effects, float HomingRange, float HomingMinimumDot, float HomingTurnSpeed, int ExplosionDamage, float ExplosionRadius, float ExplosionForce, int RicochetBounces, float RicochetRetention, float RicochetOffset, float SlowSpeed, float SlowGravity, int SplitCount, float SplitSpread, float SplitDamage)
{
internal static ProjectileProfile Capture(Weapon weapon)
{
ProjectileEffects projectileEffects = ProjectileEffects.None;
if (UpgradeState.IsEnabled(weapon, UpgradeState.Homing))
{
projectileEffects |= ProjectileEffects.Homing;
}
if (UpgradeState.IsEnabled(weapon, UpgradeState.Explosive))
{
projectileEffects |= ProjectileEffects.Explosive;
}
if (UpgradeState.IsEnabled(weapon, UpgradeState.Ricochet))
{
projectileEffects |= ProjectileEffects.Ricochet;
}
if (UpgradeState.IsEnabled(weapon, UpgradeState.SlowMo))
{
projectileEffects |= ProjectileEffects.SlowMo;
}
if (UpgradeState.IsEnabled(weapon, UpgradeState.SplitShot))
{
projectileEffects |= ProjectileEffects.SplitShot;
}
return new ProjectileProfile(projectileEffects, HomingBullets.Range, HomingBullets.MinimumForwardDot, HomingBullets.TurnDegreesPerSecond, ExplosiveBullets.Damage, ExplosiveBullets.Radius, ExplosiveBullets.ItemForce, RicochetBullets.MaxBounces, RicochetBullets.SpeedRetention, RicochetBullets.SurfaceOffset, SlowMoBullets.ProjectileSpeed, SlowMoBullets.GravityMultiplier, SplitShotBullets.ProjectileCount, SplitShotBullets.SpreadDegrees, SplitShotBullets.DamageMultiplier);
}
internal ProjectileProfile Clamped()
{
return this with
{
Effects = (Effects & (ProjectileEffects.Homing | ProjectileEffects.Explosive | ProjectileEffects.Ricochet | ProjectileEffects.SlowMo | ProjectileEffects.SplitShot)),
HomingRange = Mathf.Clamp(HomingRange, 0f, 250f),
HomingMinimumDot = Mathf.Clamp(HomingMinimumDot, -1f, 1f),
HomingTurnSpeed = Mathf.Clamp(HomingTurnSpeed, 0f, 1440f),
ExplosionDamage = Mathf.Clamp(ExplosionDamage, 0, 1000),
ExplosionRadius = Mathf.Clamp(ExplosionRadius, 0f, 25f),
ExplosionForce = Mathf.Clamp(ExplosionForce, 0f, 10000f),
RicochetBounces = Mathf.Clamp(RicochetBounces, 0, 12),
RicochetRetention = Mathf.Clamp(RicochetRetention, 0f, 3f),
RicochetOffset = Mathf.Clamp(RicochetOffset, 0f, 1f),
SlowSpeed = Mathf.Clamp(SlowSpeed, 0.01f, 500f),
SlowGravity = Mathf.Clamp(SlowGravity, 0f, 5f),
SplitCount = Mathf.Clamp(SplitCount, 1, 12),
SplitSpread = Mathf.Clamp(SplitSpread, 0f, 180f),
SplitDamage = Mathf.Clamp(SplitDamage, 0f, 5f)
};
}
}
internal readonly record struct ShotBroadcast(int OwnerObjectId, uint FirstProjectileId, ushort ProjectileCount, ProjectileProfile Profile) : IBroadcast;
internal readonly record struct HomingTargetBroadcast(int OwnerObjectId, uint ProjectileId, int TargetObjectId) : IBroadcast;
internal readonly record struct ExplosionRequestBroadcast(int OwnerObjectId, uint ProjectileId, Vector3 Position) : IBroadcast;
internal readonly record struct ExplosionEventBroadcast(Vector3 Position, float Radius, float Force) : IBroadcast;
internal static class ProjectileNetwork
{
private readonly record struct ServerShot(NetworkConnection Connection, ProjectileProfile Profile);
private const int MaxProjectilesPerShot = 192;
private static readonly FieldRef<ProjectileManager, uint> NextIdRef = AccessTools.FieldRefAccess<ProjectileManager, uint>("_nextId");
private static readonly Action<ShotBroadcast, Channel> ClientShotHandler = OnClientShot;
private static readonly Action<HomingTargetBroadcast, Channel> ClientTargetHandler = OnClientTarget;
private static readonly Action<ExplosionEventBroadcast, Channel> ClientExplosionHandler = OnClientExplosion;
private static readonly Action<NetworkConnection, ShotBroadcast, Channel> ServerShotHandler = OnServerShot;
private static readonly Action<NetworkConnection, HomingTargetBroadcast, Channel> ServerTargetHandler = OnServerTarget;
private static readonly Action<NetworkConnection, ExplosionRequestBroadcast, Channel> ServerExplosionHandler = OnServerExplosion;
private static readonly Dictionary<ProjectileKey, ProjectileProfile> PendingProfiles = new Dictionary<ProjectileKey, ProjectileProfile>();
private static readonly Dictionary<ProjectileKey, Projectile> PendingProjectiles = new Dictionary<ProjectileKey, Projectile>();
private static readonly HashSet<ProjectileKey> Applied = new HashSet<ProjectileKey>();
private static readonly Dictionary<ProjectileKey, ServerShot> ServerShots = new Dictionary<ProjectileKey, ServerShot>();
private static readonly HashSet<ProjectileKey> Exploded = new HashSet<ProjectileKey>();
private static bool _clientRegistered;
private static bool _serverRegistered;
internal static void Initialize()
{
GenericWriter<ShotBroadcast>.SetWrite((Action<Writer, ShotBroadcast>)WriteShot);
GenericReader<ShotBroadcast>.SetRead((Func<Reader, ShotBroadcast>)ReadShot);
GenericWriter<HomingTargetBroadcast>.SetWrite((Action<Writer, HomingTargetBroadcast>)delegate(Writer writer, HomingTargetBroadcast message)
{
writer.WriteInt32(message.OwnerObjectId);
writer.WriteUInt32(message.ProjectileId);
writer.WriteInt32(message.TargetObjectId);
});
GenericReader<HomingTargetBroadcast>.SetRead((Func<Reader, HomingTargetBroadcast>)((Reader reader) => new HomingTargetBroadcast(reader.ReadInt32(), reader.ReadUInt32(), reader.ReadInt32())));
GenericWriter<ExplosionRequestBroadcast>.SetWrite((Action<Writer, ExplosionRequestBroadcast>)delegate(Writer writer, ExplosionRequestBroadcast message)
{
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
writer.WriteInt32(message.OwnerObjectId);
writer.WriteUInt32(message.ProjectileId);
writer.WriteVector3(message.Position);
});
GenericReader<ExplosionRequestBroadcast>.SetRead((Func<Reader, ExplosionRequestBroadcast>)((Reader reader) => new ExplosionRequestBroadcast(reader.ReadInt32(), reader.ReadUInt32(), reader.ReadVector3())));
GenericWriter<ExplosionEventBroadcast>.SetWrite((Action<Writer, ExplosionEventBroadcast>)delegate(Writer writer, ExplosionEventBroadcast message)
{
//IL_0003: Unknown result type (might be due to invalid IL or missing references)
writer.WriteVector3(message.Position);
writer.WriteSingle(message.Radius);
writer.WriteSingle(message.Force);
});
GenericReader<ExplosionEventBroadcast>.SetRead((Func<Reader, ExplosionEventBroadcast>)((Reader reader) => new ExplosionEventBroadcast(reader.ReadVector3(), reader.ReadSingle(), reader.ReadSingle())));
}
internal static void RegisterClient()
{
if (!_clientRegistered && Object.op_Implicit((Object)(object)InstanceFinder.ClientManager))
{
InstanceFinder.ClientManager.RegisterBroadcast<ShotBroadcast>(ClientShotHandler);
InstanceFinder.ClientManager.RegisterBroadcast<HomingTargetBroadcast>(ClientTargetHandler);
InstanceFinder.ClientManager.RegisterBroadcast<ExplosionEventBroadcast>(ClientExplosionHandler);
_clientRegistered = true;
Plugin.Log.LogInfo((object)"Exotic projectile multiplayer client handlers registered.");
}
}
internal static void RegisterServer()
{
if (!_serverRegistered && Object.op_Implicit((Object)(object)InstanceFinder.ServerManager))
{
InstanceFinder.ServerManager.RegisterBroadcast<ShotBroadcast>(ServerShotHandler, true);
InstanceFinder.ServerManager.RegisterBroadcast<HomingTargetBroadcast>(ServerTargetHandler, true);
InstanceFinder.ServerManager.RegisterBroadcast<ExplosionRequestBroadcast>(ServerExplosionHandler, true);
_serverRegistered = true;
Plugin.Log.LogInfo((object)"Exotic projectile multiplayer server handlers registered.");
}
}
internal static void ResetSession()
{
if (_clientRegistered && Object.op_Implicit((Object)(object)InstanceFinder.ClientManager))
{
InstanceFinder.ClientManager.UnregisterBroadcast<ShotBroadcast>(ClientShotHandler);
InstanceFinder.ClientManager.UnregisterBroadcast<HomingTargetBroadcast>(ClientTargetHandler);
InstanceFinder.ClientManager.UnregisterBroadcast<ExplosionEventBroadcast>(ClientExplosionHandler);
}
if (_serverRegistered && Object.op_Implicit((Object)(object)InstanceFinder.ServerManager))
{
InstanceFinder.ServerManager.UnregisterBroadcast<ShotBroadcast>(ServerShotHandler);
InstanceFinder.ServerManager.UnregisterBroadcast<HomingTargetBroadcast>(ServerTargetHandler);
InstanceFinder.ServerManager.UnregisterBroadcast<ExplosionRequestBroadcast>(ServerExplosionHandler);
}
_clientRegistered = false;
_serverRegistered = false;
PendingProfiles.Clear();
PendingProjectiles.Clear();
Applied.Clear();
ServerShots.Clear();
Exploded.Clear();
HomingBullets.Clear();
ExplosiveBullets.Clear();
RicochetBullets.Clear();
}
internal static uint NextId(ProjectileManager manager)
{
return NextIdRef.Invoke(manager);
}
internal static void SendShot(Player owner, uint firstId, int count, ProjectileProfile profile)
{
if (Object.op_Implicit((Object)(object)owner) && count > 0 && count <= 192 && Object.op_Implicit((Object)(object)InstanceFinder.ClientManager))
{
ShotBroadcast shotBroadcast = new ShotBroadcast(((NetworkBehaviour)owner).ObjectId, firstId, (ushort)count, profile.Clamped());
ApplyShot(shotBroadcast);
InstanceFinder.ClientManager.Broadcast<ShotBroadcast>(shotBroadcast, (Channel)0);
}
}
internal static void RegisterNew(ProjectileManager manager, WeaponInfo weaponInfo, int firstNewIndex)
{
List<Projectile> projectiles = manager.GetType(weaponInfo.ProjectileType).Projectiles;
for (int i = firstNewIndex; i < projectiles.Count; i++)
{
Projectile val = projectiles[i];
ProjectileKey key = Key(val);
PendingProjectiles[key] = val;
TryApply(key);
}
}
internal static void ShareTarget(Projectile projectile, Creature target)
{
if (projectile.IsLocal && Object.op_Implicit((Object)(object)target) && Object.op_Implicit((Object)(object)InstanceFinder.ClientManager))
{
HomingTargetBroadcast homingTargetBroadcast = new HomingTargetBroadcast(((NetworkBehaviour)projectile.Owner).ObjectId, projectile.Id, ((NetworkBehaviour)target).ObjectId);
HomingBullets.SetTarget(Key(projectile), ((NetworkBehaviour)target).ObjectId);
InstanceFinder.ClientManager.Broadcast<HomingTargetBroadcast>(homingTargetBroadcast, (Channel)0);
}
}
internal static void RequestExplosion(Projectile projectile, Vector3 position)
{
//IL_002a: Unknown result type (might be due to invalid IL or missing references)
if (projectile.IsLocal && Object.op_Implicit((Object)(object)InstanceFinder.ClientManager))
{
InstanceFinder.ClientManager.Broadcast<ExplosionRequestBroadcast>(new ExplosionRequestBroadcast(((NetworkBehaviour)projectile.Owner).ObjectId, projectile.Id, position), (Channel)0);
}
}
internal static void Cleanup()
{
List<ProjectileKey> list = null;
foreach (var (item, val2) in PendingProjectiles)
{
if (!Object.op_Implicit((Object)(object)val2.Owner) || !Object.op_Implicit((Object)(object)ProjectileManager.Instance) || !ProjectileManager.Instance.GetType(val2.TypeId).Projectiles.Contains(val2))
{
(list ?? (list = new List<ProjectileKey>())).Add(item);
}
}
if (list == null)
{
return;
}
foreach (ProjectileKey item2 in list)
{
PendingProjectiles.Remove(item2);
PendingProfiles.Remove(item2);
Applied.Remove(item2);
ServerShots.Remove(item2);
Exploded.Remove(item2);
}
}
internal static void Validate()
{
ProjectileProfile projectileProfile = new ProjectileProfile((ProjectileEffects)255, -1f, -2f, 9999f, -1, 999f, -4f, -2, 9f, -1f, 0f, -1f, 99, 999f, -1f).Clamped();
if (projectileProfile.Effects == ProjectileEffects.None || projectileProfile.HomingRange < 0f || projectileProfile.ExplosionDamage < 0 || projectileProfile.SplitCount != 12 || projectileProfile.SlowSpeed < 0.01f)
{
throw new InvalidOperationException("Projectile network profile validation failed.");
}
}
private static void OnServerShot(NetworkConnection connection, ShotBroadcast message, Channel channel)
{
Player player = PlayerFor(message.OwnerObjectId);
if (!Owns(connection, player) || message.ProjectileCount == 0 || message.ProjectileCount > 192)
{
Plugin.Log.LogWarning((object)"Rejected invalid exotic projectile metadata from a client.");
return;
}
ShotBroadcast message2 = message with
{
Profile = message.Profile.Clamped()
};
for (uint num = 0u; num < message2.ProjectileCount; num++)
{
ServerShots[new ProjectileKey(message2.OwnerObjectId, message2.FirstProjectileId + num)] = new ServerShot(connection, message2.Profile);
}
BroadcastAuthenticated<ShotBroadcast>(message2);
}
private static void OnServerTarget(NetworkConnection connection, HomingTargetBroadcast message, Channel channel)
{
ProjectileKey key = new ProjectileKey(message.OwnerObjectId, message.ProjectileId);
if (ServerShots.TryGetValue(key, out var value) && !(value.Connection != connection) && value.Profile.Effects.HasFlag(ProjectileEffects.Homing) && IsLivingCreature(message.TargetObjectId))
{
BroadcastAuthenticated<HomingTargetBroadcast>(message);
}
}
private static void OnServerExplosion(NetworkConnection connection, ExplosionRequestBroadcast message, Channel channel)
{
//IL_007b: Unknown result type (might be due to invalid IL or missing references)
//IL_008e: Unknown result type (might be due to invalid IL or missing references)
ProjectileKey projectileKey = new ProjectileKey(message.OwnerObjectId, message.ProjectileId);
if (ServerShots.TryGetValue(projectileKey, out var value) && !(value.Connection != connection) && value.Profile.Effects.HasFlag(ProjectileEffects.Explosive) && Exploded.Add(projectileKey))
{
Player val = PlayerFor(message.OwnerObjectId);
if (Object.op_Implicit((Object)(object)val))
{
ExplosiveBullets.DamageOnServer(val, message.Position, value.Profile);
BroadcastAuthenticated<ExplosionEventBroadcast>(new ExplosionEventBroadcast(message.Position, value.Profile.ExplosionRadius, value.Profile.ExplosionForce));
}
}
}
private static void OnClientShot(ShotBroadcast message, Channel channel)
{
ApplyShot(message);
}
private static void OnClientTarget(HomingTargetBroadcast message, Channel channel)
{
HomingBullets.SetTarget(new ProjectileKey(message.OwnerObjectId, message.ProjectileId), message.TargetObjectId);
}
private static void OnClientExplosion(ExplosionEventBroadcast message, Channel channel)
{
//IL_0002: Unknown result type (might be due to invalid IL or missing references)
ExplosiveBullets.PlayExplosion(message.Position, message.Radius, message.Force);
}
private static void ApplyShot(ShotBroadcast message)
{
ProjectileProfile value = message.Profile.Clamped();
for (uint num = 0u; num < message.ProjectileCount; num++)
{
ProjectileKey key = new ProjectileKey(message.OwnerObjectId, message.FirstProjectileId + num);
PendingProfiles[key] = value;
TryApply(key);
}
}
private static void TryApply(ProjectileKey key)
{
if (PendingProfiles.TryGetValue(key, out var value) && PendingProjectiles.TryGetValue(key, out Projectile value2))
{
if (!Applied.Add(key))
{
PendingProfiles.Remove(key);
return;
}
HomingBullets.Track(value2, key, value);
ExplosiveBullets.Track(value2, value);
RicochetBullets.Track(value2, value);
SlowMoBullets.Apply(value2, value);
SplitShotBullets.ApplyDamage(value2, value);
PendingProfiles.Remove(key);
}
}
private static ProjectileKey Key(Projectile projectile)
{
return new ProjectileKey(((NetworkBehaviour)projectile.Owner).ObjectId, projectile.Id);
}
private static Player? PlayerFor(int objectId)
{
foreach (Player player in PlayerManager.Players)
{
if (Object.op_Implicit((Object)(object)player) && ((NetworkBehaviour)player).ObjectId == objectId)
{
return player;
}
}
return null;
}
private static bool Owns(NetworkConnection connection, Player? player)
{
if (Object.op_Implicit((Object)(object)player))
{
return ((NetworkBehaviour)player).Owner == connection;
}
return false;
}
private static bool IsLivingCreature(int objectId)
{
foreach (Item value in ItemManager.Items.Values)
{
if (Object.op_Implicit((Object)(object)value) && ((NetworkBehaviour)value).ObjectId == objectId && Object.op_Implicit((Object)(object)value.Creature) && !value.Creature.IsDead)
{
return true;
}
}
return false;
}
private static void BroadcastAuthenticated<T>(T message) where T : struct, IBroadcast
{
foreach (NetworkConnection value in InstanceFinder.ServerManager.Clients.Values)
{
if (value.IsAuthenticated)
{
InstanceFinder.ServerManager.Broadcast<T>(value, message, true, (Channel)0);
}
}
}
private static void WriteShot(Writer writer, ShotBroadcast message)
{
writer.WriteInt32(message.OwnerObjectId);
writer.WriteUInt32(message.FirstProjectileId);
writer.WriteUInt16(message.ProjectileCount);
writer.WriteUInt8Unpacked((byte)message.Profile.Effects);
writer.WriteSingle(message.Profile.HomingRange);
writer.WriteSingle(message.Profile.HomingMinimumDot);
writer.WriteSingle(message.Profile.HomingTurnSpeed);
writer.WriteInt32(message.Profile.ExplosionDamage);
writer.WriteSingle(message.Profile.ExplosionRadius);
writer.WriteSingle(message.Profile.ExplosionForce);
writer.WriteInt32(message.Profile.RicochetBounces);
writer.WriteSingle(message.Profile.RicochetRetention);
writer.WriteSingle(message.Profile.RicochetOffset);
writer.WriteSingle(message.Profile.SlowSpeed);
writer.WriteSingle(message.Profile.SlowGravity);
writer.WriteInt32(message.Profile.SplitCount);
writer.WriteSingle(message.Profile.SplitSpread);
writer.WriteSingle(message.Profile.SplitDamage);
}
private static ShotBroadcast ReadShot(Reader reader)
{
return new ShotBroadcast(reader.ReadInt32(), reader.ReadUInt32(), reader.ReadUInt16(), new ProjectileProfile((ProjectileEffects)reader.ReadUInt8Unpacked(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadInt32(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadInt32(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadInt32(), reader.ReadSingle(), reader.ReadSingle()));
}
}
internal static class RicochetBullets
{
private readonly record struct RicochetState(int Remaining, ProjectileProfile Profile);
private const int DefaultMaxBounces = 3;
private const float DefaultSpeedRetention = 0.85f;
private const float DefaultSurfaceOffset = 0.08f;
private static readonly Dictionary<Projectile, RicochetState> Tracked = new Dictionary<Projectile, RicochetState>();
private static ConfigEntry<int> _maxBounces = null;
private static ConfigEntry<float> _speedRetention = null;
private static ConfigEntry<float> _surfaceOffset = null;
internal static int MaxBounces => Mathf.Max(0, _maxBounces.Value);
internal static float SpeedRetention => Mathf.Max(0f, _speedRetention.Value);
internal static float SurfaceOffset => Mathf.Max(0f, _surfaceOffset.Value);
internal static void Initialize(ConfigFile config)
{
_maxBounces = config.Bind<int>("Ricochet", "MaxBounces", 3, "Maximum number of level or boat surface bounces per projectile.");
_speedRetention = config.Bind<float>("Ricochet", "SpeedRetention", 0.85f, "Projectile speed multiplier after each bounce. Values above 1 accelerate it.");
_surfaceOffset = config.Bind<float>("Ricochet", "SurfaceOffset", 0.08f, "Distance used to move a reflected projectile away from the hit surface.");
}
internal static void Track(Projectile projectile, ProjectileProfile profile)
{
if (profile.Effects.HasFlag(ProjectileEffects.Ricochet))
{
Tracked[projectile] = new RicochetState(profile.RicochetBounces, profile);
}
}
internal static void Clear()
{
Tracked.Clear();
}
internal static bool TryBounce(Projectile projectile, RaycastHit hit)
{
//IL_0055: Unknown result type (might be due to invalid IL or missing references)
//IL_005c: Unknown result type (might be due to invalid IL or missing references)
//IL_0061: Unknown result type (might be due to invalid IL or missing references)
//IL_0076: Unknown result type (might be due to invalid IL or missing references)
//IL_007b: Unknown result type (might be due to invalid IL or missing references)
//IL_007e: Unknown result type (might be due to invalid IL or missing references)
//IL_0085: Unknown result type (might be due to invalid IL or missing references)
//IL_009a: Unknown result type (might be due to invalid IL or missing references)
//IL_009f: Unknown result type (might be due to invalid IL or missing references)
//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
if (!Tracked.TryGetValue(projectile, out var value))
{
return false;
}
if ((!((Component)((RaycastHit)(ref hit)).transform).CompareTag("Level") && !((Component)((RaycastHit)(ref hit)).transform).CompareTag("Boat")) || value.Remaining <= 0)
{
Tracked.Remove(projectile);
return false;
}
Vector3 val = Vector3.Reflect(projectile.Velocity, ((RaycastHit)(ref hit)).normal) * value.Profile.RicochetRetention;
projectile.Position = (projectile.PreviousPosition = ((RaycastHit)(ref hit)).point + ((RaycastHit)(ref hit)).normal * value.Profile.RicochetOffset);
projectile.PreviousVelocity = val;
projectile.Velocity = val;
Tracked[projectile] = value with
{
Remaining = value.Remaining - 1
};
return true;
}
internal static void RemoveExpired(ProjectileManager manager)
{
List<Projectile> list = null;
foreach (Projectile key in Tracked.Keys)
{
if (!manager.GetType(key.TypeId).Projectiles.Contains(key))
{
(list ?? (list = new List<Projectile>())).Add(key);
}
}
if (list == null)
{
return;
}
foreach (Projectile item in list)
{
Tracked.Remove(item);
}
}
internal static void Validate()
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: 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)
Vector3 val = Vector3.Reflect(Vector3.right, Vector3.left);
if (Vector3.Dot(val, Vector3.left) < 0.999f)
{
throw new InvalidOperationException("Ricochet validation failed.");
}
}
}
internal static class SlowMoBullets
{
private const float DefaultProjectileSpeed = 12f;
private const float DefaultGravityMultiplier = 0.0625f;
private static ConfigEntry<float> _projectileSpeed;
private static ConfigEntry<float> _gravityMultiplier;
internal static float ProjectileSpeed => Mathf.Max(0.01f, _projectileSpeed.Value);
internal static float GravityMultiplier => Mathf.Max(0f, _gravityMultiplier.Value);
internal static void Initialize(ConfigFile config)
{
_projectileSpeed = config.Bind<float>("Slow-Mo Bullets", "ProjectileSpeed", 12f, "Absolute speed in world units per second shared by every Slow-Mo projectile. Clamped to at least 0.01.");
_gravityMultiplier = config.Bind<float>("Slow-Mo Bullets", "GravityMultiplier", 0.0625f, "Projectile gravity multiplier. Lower values keep slow projectiles from dropping too quickly.");
}
internal static Vector3 ApplySpeed(Vector3 velocity, ProjectileProfile profile)
{
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_0023: Unknown result type (might be due to invalid IL or missing references)
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
if (!profile.Effects.HasFlag(ProjectileEffects.SlowMo))
{
return velocity;
}
return WithSpeed(velocity, profile.SlowSpeed);
}
internal static void Apply(Projectile projectile, ProjectileProfile profile)
{
//IL_001c: 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_002d: Unknown result type (might be due to invalid IL or missing references)
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_0039: Unknown result type (might be due to invalid IL or missing references)
if (profile.Effects.HasFlag(ProjectileEffects.SlowMo))
{
projectile.Velocity = WithSpeed(projectile.Velocity, profile.SlowSpeed);
projectile.PreviousVelocity = projectile.Velocity;
projectile.GravityForce *= profile.SlowGravity;
}
}
internal static void Validate()
{
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: 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_0038: 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_0066: Unknown result type (might be due to invalid IL or missing references)
//IL_006b: Unknown result type (might be due to invalid IL or missing references)
//IL_0079: Unknown result type (might be due to invalid IL or missing references)
//IL_007e: Unknown result type (might be due to invalid IL or missing references)
Vector3 val = WithSpeed(new Vector3(100f, 0f, 0f), 12f);
Vector3 val2 = WithSpeed(new Vector3(0f, 0f, 500f), 12f);
if (!Mathf.Approximately(((Vector3)(ref val)).magnitude, 12f) || !Mathf.Approximately(((Vector3)(ref val2)).magnitude, 12f) || ((Vector3)(ref val)).normalized != Vector3.right || ((Vector3)(ref val2)).normalized != Vector3.forward)
{
throw new InvalidOperationException("Slow-Mo Bullets validation failed.");
}
}
private static Vector3 WithSpeed(Vector3 velocity, float speed)
{
//IL_0002: Unknown result type (might be due to invalid IL or missing references)
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
return ((Vector3)(ref velocity)).normalized * speed;
}
}
internal static class SplitShotBullets
{
private const int MaxProjectilesPerShot = 192;
private const int DefaultProjectileCount = 3;
private const float DefaultSpreadDegrees = 12f;
private const float DefaultDamageMultiplier = 0.75f;
private static ConfigEntry<int> _projectileCount;
private static ConfigEntry<float> _spreadDegrees;
private static ConfigEntry<float> _damageMultiplier;
internal static int ProjectileCount => Mathf.Clamp(_projectileCount.Value, 1, 12);
internal static float SpreadDegrees => Mathf.Max(0f, _spreadDegrees.Value);
internal static float DamageMultiplier => Mathf.Max(0f, _damageMultiplier.Value);
internal static void Initialize(ConfigFile config)
{
_projectileCount = config.Bind<int>("Split Shot", "ProjectileCount", 3, "Total projectiles produced by each original projectile. Clamped from 1 to 12.");
_spreadDegrees = config.Bind<float>("Split Shot", "SpreadDegrees", 12f, "Total horizontal angle between the outermost projectiles.");
_damageMultiplier = config.Bind<float>("Split Shot", "DamageMultiplier", 0.75f, "Damage multiplier applied to every split projectile.");
}
internal static Vector3[] Expand(Vector3[] velocities, ProjectileProfile profile)
{
//IL_005f: Unknown result type (might be due to invalid IL or missing references)
//IL_0064: Unknown result type (might be due to invalid IL or missing references)
//IL_0071: Unknown result type (might be due to invalid IL or missing references)
//IL_0082: Unknown result type (might be due to invalid IL or missing references)
//IL_0087: Unknown result type (might be due to invalid IL or missing references)
if (!profile.Effects.HasFlag(ProjectileEffects.SplitShot) || profile.SplitCount <= 1)
{
return velocities;
}
int num = Mathf.Min(profile.SplitCount, 192 / Mathf.Max(1, velocities.Length));
if (num <= 1)
{
return velocities;
}
Vector3[] array = (Vector3[])(object)new Vector3[velocities.Length * num];
int num2 = 0;
foreach (Vector3 velocity in velocities)
{
for (int j = 0; j < num; j++)
{
array[num2++] = Rotate(velocity, Offset(j, num, profile.SplitSpread));
}
}
return array;
}
internal static void ApplyDamage(Projectile projectile, ProjectileProfile profile)
{
if (profile.Effects.HasFlag(ProjectileEffects.SplitShot))
{
projectile.Damage = Mathf.RoundToInt((float)projectile.Damage * profile.SplitDamage);
}
}
internal static void Validate()
{
if (!Mathf.Approximately(Offset(0, 3, 12f), -6f) || !Mathf.Approximately(Offset(1, 3, 12f), 0f) || !Mathf.Approximately(Offset(2, 3, 12f), 6f))
{
throw new InvalidOperationException("Split Shot validation failed.");
}
}
private static float Offset(int index, int count, float spread)
{
if (count > 1)
{
return (0f - spread) * 0.5f + spread * (float)index / (float)(count - 1);
}
return 0f;
}
private static Vector3 Rotate(Vector3 velocity, float degrees)
{
//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_000b: 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)
return Quaternion.AngleAxis(degrees, Vector3.up) * velocity;
}
}
internal readonly record struct ExoticUpgrade(string Id, string DisplayName, string IconResource, Color BadgeColor);
internal static class UpgradeState
{
internal static readonly ExoticUpgrade Homing = new ExoticUpgrade("homing-bullets", "Homing Bullets", "HOM.png", Color32.op_Implicit(new Color32((byte)182, (byte)108, byte.MaxValue, byte.MaxValue)));
internal static readonly ExoticUpgrade Explosive = new ExoticUpgrade("explosive-bullets", "Explosive Bullets", "EXP.png", Color32.op_Implicit(new Color32(byte.MaxValue, (byte)138, (byte)42, byte.MaxValue)));
internal static readonly ExoticUpgrade Ricochet = new ExoticUpgrade("ricochet-bullets", "Ricochet", "RICO.png", Color32.op_Implicit(new Color32(byte.MaxValue, (byte)75, (byte)75, byte.MaxValue)));
internal static readonly ExoticUpgrade SlowMo = new ExoticUpgrade("slow-mo-bullets", "Slow-Mo Bullets", "SLO.png", Color32.op_Implicit(new Color32((byte)77, (byte)166, byte.MaxValue, byte.MaxValue)));
internal static readonly ExoticUpgrade SplitShot = new ExoticUpgrade("split-shot", "Split Shot", "SPL.png", Color32.op_Implicit(new Color32(byte.MaxValue, (byte)216, (byte)77, byte.MaxValue)));
private const string Marker = "\n\n<color=#F4B942><b>EXOTIC UPGRADES</b></color>\n";
private static readonly ExoticUpgrade[] Upgrades = new ExoticUpgrade[5] { Homing, Explosive, Ricochet, SlowMo, SplitShot };
private static readonly FieldInfo WeaponInfoField = AccessTools.Field(typeof(Weapon), "_weaponInfo");
private static readonly Dictionary<(ulong Player, byte Weapon), int> Selections = new Dictionary<(ulong, byte), int>();
private static readonly Dictionary<(ulong Player, byte Weapon, string Upgrade), ConfigEntry<bool>> Unlocked = new Dictionary<(ulong, byte, string), ConfigEntry<bool>>();
private static readonly Dictionary<(ulong Player, byte Weapon, string Upgrade), ConfigEntry<bool>> Enabled = new Dictionary<(ulong, byte, string), ConfigEntry<bool>>();
private static ConfigFile _config = null;
internal static int UpgradeCount => Upgrades.Length;
internal static void Initialize(ConfigFile config)
{
_config = config;
}
internal static bool IsEnabled(Weapon weapon, ExoticUpgrade upgrade)
{
if (IsUnlocked(weapon, upgrade))
{
return Entry(Enabled, Key(weapon), upgrade, "Enabled", defaultValue: true).Value;
}
return false;
}
internal static ExoticUpgrade GetUpgrade(int index)
{
return Upgrades[index];
}
internal static void Unlock(ulong player, byte weapon, int upgradeIndex)
{
ExoticUpgrade upgrade = Upgrades[upgradeIndex];
ConfigEntry<bool> val = Entry(Unlocked, (Player: player, Weapon: weapon), upgrade, "Owned", defaultValue: false);
if (!val.Value)
{
val.Value = true;
_config.Save();
Plugin.Log.LogInfo((object)$"Unlocked {upgrade.DisplayName} for weapon model {weapon}.");
}
}
internal static bool TrySelect(Weapon weapon, bool next)
{
ExoticUpgrade[] array = Available(weapon);
if (array.Length == 0)
{
return false;
}
(ulong, byte) key = Key(weapon);
int value;
int num = (Selections.TryGetValue(key, out value) ? value : 0);
Selections[key] = Wrap(num + (next ? 1 : (-1)), array.Length);
return true;
}
internal static bool TryToggleSelected(Weapon weapon)
{
ExoticUpgrade[] array = Available(weapon);
if (array.Length == 0)
{
return false;
}
ExoticUpgrade upgrade = array[SelectedIndex(weapon, array.Length)];
ConfigEntry<bool> val = Entry(Enabled, Key(weapon), upgrade, "Enabled", defaultValue: true);
val.Value = !val.Value;
_config.Save();
Plugin.Log.LogInfo((object)(upgrade.DisplayName + " " + (val.Value ? "enabled" : "disabled") + " for " + ((Item)weapon).GetName() + "."));
return true;
}
internal static string WithInspection(string description, Weapon weapon)
{
int num = description.IndexOf("\n\n<color=#F4B942><b>EXOTIC UPGRADES</b></color>\n", StringComparison.Ordinal);
string text = ((num >= 0) ? description.Substring(0, num) : description);
ExoticUpgrade[] array = Available(weapon);
if (array.Length == 0)
{
return text;
}
int selected = SelectedIndex(weapon, array.Length);
string text2 = string.Join("\n", array.Select(delegate(ExoticUpgrade upgrade, int index)
{
string text6 = ((index == selected) ? ">" : " ");
string text7 = (IsEnabled(weapon, upgrade) ? "<color=#63E06B>ON</color>" : "<color=#999999>OFF</color>");
return text6 + " " + upgrade.DisplayName + " " + text7;
}));
string text3 = SpriteManager.InputToSpriteName(GameInfo.Input.actions["WeaponInspect"], (InputSchemeSelection)0, -1) ?? "Inspect";
string text4 = SpriteManager.InputToSpriteName(GameInfo.Input.actions["PlayerChangeSkin"], (InputSchemeSelection)0, -1) ?? "Change Skin";
string text5 = SpriteManager.InputToSpriteName(GameInfo.Input.actions["WeaponReload"], (InputSchemeSelection)0, -1) ?? "Reload";
return text + "\n\n<color=#F4B942><b>EXOTIC UPGRADES</b></color>\n" + text2 + "\n<color=#AFAFAF>" + text3 + " + " + text4 + ": Select " + text5 + ": Toggle</color>";
}
internal static void Validate()
{
if (Upgrades.Select((ExoticUpgrade upgrade) => upgrade.Id).Distinct().Count() != Upgrades.Length || Upgrades.Any((ExoticUpgrade upgrade) => string.IsNullOrWhiteSpace(upgrade.IconResource) || !upgrade.IconResource.EndsWith(".png", StringComparison.OrdinalIgnoreCase) || upgrade.BadgeColor.a < 0.99f) || Wrap(-1, 3) != 2 || Wrap(3, 3) != 0)
{
throw new InvalidOperationException("Exotic upgrade catalog validation failed.");
}
}
private static bool IsUnlocked(Weapon weapon, ExoticUpgrade upgrade)
{
return Entry(Unlocked, Key(weapon), upgrade, "Owned", defaultValue: false).Value;
}
private static ExoticUpgrade[] Available(Weapon weapon)
{
if (!SupportsProjectileUpgrades(weapon))
{
return Array.Empty<ExoticUpgrade>();
}
return Upgrades.Where((ExoticUpgrade upgrade) => IsUnlocked(weapon, upgrade)).ToArray();
}
internal static bool SupportsProjectileUpgrades(Weapon weapon)
{
if (!Object.op_Implicit((Object)(object)weapon) || !Object.op_Implicit((Object)(object)ProjectileManager.Instance))
{
return false;
}
object? value = WeaponInfoField.GetValue(weapon);
WeaponInfo val = (WeaponInfo)((value is WeaponInfo) ? value : null);
if (val != null)
{
return !ProjectileManager.Instance.GetType(val.ProjectileType).IsHitScan;
}
return false;
}
private static ConfigEntry<bool> Entry(Dictionary<(ulong Player, byte Weapon, string Upgrade), ConfigEntry<bool>> entries, (ulong Player, byte Weapon) model, ExoticUpgrade upgrade, string name, bool defaultValue)
{
(ulong, byte, string) key = (model.Player, model.Weapon, upgrade.Id);
if (!entries.TryGetValue(key, out ConfigEntry<bool> value))
{
value = _config.Bind<bool>($"Player.{model.Player}.Weapon.{model.Weapon}", upgrade.Id + "." + name, defaultValue, "Saved " + name.ToLowerInvariant() + " state for " + upgrade.DisplayName + " on this weapon model.");
entries.Add(key, value);
}
return value;
}
private static (ulong Player, byte Weapon) Key(Weapon weapon)
{
return (Player: Object.op_Implicit((Object)(object)((Item)weapon).Holder) ? ((Item)weapon).Holder.SteamID : Player.LocalPlayer.SteamID, Weapon: ((Item)weapon).ID);
}
private static int SelectedIndex(Weapon weapon, int count)
{
(ulong, byte) key = Key(weapon);
int value = (Selections.TryGetValue(key, out var value2) ? value2 : 0);
value = Wrap(value, count);
Selections[key] = value;
return value;
}
private static int Wrap(int value, int count)
{
return (value % count + count) % count;
}
}
}
namespace System.Diagnostics.CodeAnalysis
{
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class ConstantExpectedAttribute : Attribute
{
public object? Min { get; set; }
public object? Max { get; set; }
}
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class ExperimentalAttribute : Attribute
{
public string DiagnosticId { get; }
public string? UrlFormat { get; set; }
public ExperimentalAttribute(string diagnosticId)
{
DiagnosticId = diagnosticId;
}
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
[ExcludeFromCodeCoverage]
internal sealed class MemberNotNullAttribute : Attribute
{
public string[] Members { get; }
public MemberNotNullAttribute(string member)
{
Members = new string[1] { member };
}
public MemberNotNullAttribute(params string[] members)
{
Members = members;
}
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
[ExcludeFromCodeCoverage]
internal sealed class MemberNotNullWhenAttribute : Attribute
{
public bool ReturnValue { get; }
public string[] Members { get; }
public MemberNotNullWhenAttribute(bool returnValue, string member)
{
ReturnValue = returnValue;
Members = new string[1] { member };
}
public MemberNotNullWhenAttribute(bool returnValue, params string[] members)
{
ReturnValue = returnValue;
Members = members;
}
}
[AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class SetsRequiredMembersAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class StringSyntaxAttribute : Attribute
{
public const string CompositeFormat = "CompositeFormat";
public const string DateOnlyFormat = "DateOnlyFormat";
public const string DateTimeFormat = "DateTimeFormat";
public const string EnumFormat = "EnumFormat";
public const string GuidFormat = "GuidFormat";
public const string Json = "Json";
public const string NumericFormat = "NumericFormat";
public const string Regex = "Regex";
public const string TimeOnlyFormat = "TimeOnlyFormat";
public const string TimeSpanFormat = "TimeSpanFormat";
public const string Uri = "Uri";
public const string Xml = "Xml";
public string Syntax { get; }
public object?[] Arguments { get; }
public StringSyntaxAttribute(string syntax)
{
Syntax = syntax;
Arguments = new object[0];
}
public StringSyntaxAttribute(string syntax, params object?[] arguments)
{
Syntax = syntax;
Arguments = arguments;
}
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class UnscopedRefAttribute : Attribute
{
}
}
namespace System.Runtime.Versioning
{
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class RequiresPreviewFeaturesAttribute : Attribute
{
public string? Message { get; }
public string? Url { get; set; }
public RequiresPreviewFeaturesAttribute()
{
}
public RequiresPreviewFeaturesAttribute(string? message)
{
Message = message;
}
}
}
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal sealed class IgnoresAccessChecksToAttribute : Attribute
{
public IgnoresAccessChecksToAttribute(string assemblyName)
{
}
}
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class CallerArgumentExpressionAttribute : Attribute
{
public string ParameterName { get; }
public CallerArgumentExpressionAttribute(string parameterName)
{
ParameterName = parameterName;
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class CollectionBuilderAttribute : Attribute
{
public Type BuilderType { get; }
public string MethodName { get; }
public CollectionBuilderAttribute(Type builderType, string methodName)
{
BuilderType = builderType;
MethodName = methodName;
}
}
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class CompilerFeatureRequiredAttribute : Attribute
{
public const string RefStructs = "RefStructs";
public const string RequiredMembers = "RequiredMembers";
public string FeatureName { get; }
public bool IsOptional { get; set; }
public CompilerFeatureRequiredAttribute(string featureName)
{
FeatureName = featureName;
}
}
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class InterpolatedStringHandlerArgumentAttribute : Attribute
{
public string[] Arguments { get; }
public InterpolatedStringHandlerArgumentAttribute(string argument)
{
Arguments = new string[1] { argument };
}
public InterpolatedStringHandlerArgumentAttribute(params string[] arguments)
{
Arguments = arguments;
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class InterpolatedStringHandlerAttribute : Attribute
{
}
[EditorBrowsable(EditorBrowsableState.Never)]
[ExcludeFromCodeCoverage]
internal static class IsExternalInit
{
}
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class ModuleInitializerAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class OverloadResolutionPriorityAttribute : Attribute
{
public int Priority { get; }
public OverloadResolutionPriorityAttribute(int priority)
{
Priority = priority;
}
}
[AttributeUsage(AttributeTargets.Parameter, Inherited = true, AllowMultiple = false)]
[ExcludeFromCodeCoverage]
internal sealed class ParamCollectionAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class RequiredMemberAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[ExcludeFromCodeCoverage]
internal sealed class RequiresLocationAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event | AttributeTargets.Interface, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class SkipLocalsInitAttribute : Attribute
{
}
}