using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using CMRLib.Core;
using ComputerysModdingUtilities;
using FishNet;
using FishNet.Broadcast;
using FishNet.Connection;
using FishNet.Managing.Object;
using FishNet.Object;
using FishNet.Serializing;
using FishNet.Transporting;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using StraftatCMR.Features;
using StraftatCMR.Tweaks;
using TMPro;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.SceneManagement;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: StraftatMod(false)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("StraftatCMR")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("3.0.0.0")]
[assembly: AssemblyInformationalVersion("3.0.0+035ee42632884b38ea937935eb423d7c7e886381")]
[assembly: AssemblyProduct("StraftatCMR")]
[assembly: AssemblyTitle("StraftatCMR")]
[assembly: AssemblyVersion("3.0.0.0")]
[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 StraftatCMR
{
internal static class CmrRebalanceSettings
{
public static ConfigEntry<bool> HkVerbose { get; private set; }
public static ConfigEntry<bool> WardenVerbose { get; private set; }
public static void Bind(ConfigFile cfg)
{
HkVerbose = cfg.Bind<bool>("HK_G11", "VerboseLog", false, "Debug logging.");
WardenVerbose = cfg.Bind<bool>("Warden", "VerboseLog", false, "Debug logging.");
}
}
[BepInPlugin("cmr.rebalance", "StraftatCMR", "3.0.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public sealed class StraftatCMRPlugin : BaseUnityPlugin
{
public const string ModGuid = "cmr.rebalance";
public const string ModName = "StraftatCMR";
public const string ModVersion = "3.0.0";
private readonly Harmony _harmony = new Harmony("cmr.rebalance");
public static StraftatCMRPlugin Instance { get; private set; }
public static ManualLogSource Log { get; private set; }
private void Awake()
{
Instance = this;
Log = ((BaseUnityPlugin)this).Logger;
Log.LogInfo((object)"StraftatCMR 3.0.0 - starting");
CmrRebalanceSettings.Bind(((BaseUnityPlugin)this).Config);
ApplyTweaks();
InstallFeatures();
Install("Menu text", delegate
{
MenuText.Apply(Log, "3.0.0");
});
Install("Mod menu (optional)", delegate
{
ModMenuDecoration.Apply(typeof(StraftatCMRPlugin), "StraftatCMR.Resources.icon.png", "Weapon rebalance.", Log, (ConfigEntryBase[])(object)new ConfigEntryBase[2]
{
(ConfigEntryBase)CmrRebalanceSettings.HkVerbose,
(ConfigEntryBase)CmrRebalanceSettings.WardenVerbose
});
});
Log.LogInfo((object)"StraftatCMR 3.0.0 - ready");
}
private void InstallFeatures()
{
Install("HK_G11", delegate
{
HKG11FireMode.Init(Log, CmrRebalanceSettings.HkVerbose.Value);
_harmony.PatchAll(typeof(HKG11FireMode));
});
Install("Gamma: projectile split", delegate
{
GammaProjectileSplit.Apply(Log);
_harmony.PatchAll(typeof(GammaSpawnPatch));
});
Install("Mines: sounds", delegate
{
MineSounds.Apply(Log);
_harmony.PatchAll(typeof(MineSounds));
});
Install("Claymore: arming sound", delegate
{
ClaymoreSounds.Apply(Log);
_harmony.PatchAll(typeof(ClaymoreSounds));
});
Install("Weapon sounds (Bat / BlankState / Phoenix)", delegate
{
WeaponSounds.Apply(Log);
_harmony.PatchAll(typeof(WeaponSoundsShootPatch));
_harmony.PatchAll(typeof(WeaponSoundsMeleePatch));
_harmony.PatchAll(typeof(WeaponSoundsAwakePatch));
_harmony.PatchAll(typeof(MeleeSoundsAwakePatch));
});
Install("Warden flashlight", delegate
{
WardenFlashlight.Init(Log, CmrRebalanceSettings.WardenVerbose.Value);
_harmony.PatchAll(typeof(WardenFlashlight));
});
}
private void Install(string name, Action install)
{
try
{
install();
Log.LogInfo((object)("[Features] " + name + ": installed"));
}
catch (Exception ex)
{
Log.LogError((object)("[Features] " + name + ": NOT installed - " + ex.GetType().Name + ": " + ex.Message));
}
}
private void ApplyTweaks()
{
(string, Func<IEnumerable<Tweak>>)[] obj = new(string, Func<IEnumerable<Tweak>>)[2]
{
("Weapons", WeaponTweaks.All),
("Projectiles and explosives", ProjectileTweaks.All)
};
List<string> list = new List<string>();
int num = 0;
(string, Func<IEnumerable<Tweak>>)[] array = obj;
for (int i = 0; i < array.Length; i++)
{
(string, Func<IEnumerable<Tweak>>) tuple = array[i];
IEnumerable<Tweak> enumerable;
try
{
enumerable = tuple.Item2();
}
catch (Exception ex)
{
Log.LogError((object)("[Tweaks] group '" + tuple.Item1 + "' failed to build: " + ex.Message));
list.Add(tuple.Item1 + ": failed to build");
continue;
}
Report val = TweakRunner.Run(tuple.Item1, enumerable, Log);
num += val.Applied;
list.AddRange(val.Problems);
}
if (list.Count == 0)
{
Log.LogInfo((object)$"[Tweaks] TOTAL: {num} applied, no problems");
return;
}
Log.LogWarning((object)$"[Tweaks] TOTAL: {num} applied, {list.Count} problems:");
foreach (string item in list)
{
Log.LogWarning((object)("[Tweaks] " + item));
}
}
}
}
namespace StraftatCMR.Tweaks
{
internal static class ProjectileTweaks
{
public static IEnumerable<Tweak> All()
{
yield return Tweak.Spawned("Shrapnel").Private<ShrapnelBallistic>("damage", (object)1.5f);
yield return Tweak.Spawned("GlandGrenade").Private<PhysicsGrenade>("explosionRadius", (object)16f).Private<PhysicsGrenade>("numberOfRays", (object)400f);
yield return Tweak.Spawned("OBUSGROS").OnLayer("Grenade").Private<Obus>("explosionRadius", (object)4f)
.Private<Obus>("explosionTimer", (object)0.9f)
.Private<Obus>("timeBeforeExplosion", (object)0.7f);
yield return Tweak.Spawned("Kanye").OnLayer("Default").Private<PredictedProjectile>("damage", (object)1.4f)
.Private<PredictedProjectile>("MOVE_RATE", (object)100f);
yield return Tweak.Spawned("BeamLoadBullet").OnLayer("Default").Private<PredictedProjectile>("damage", (object)999f)
.Private<PredictedProjectile>("MOVE_RATE", (object)0.5f)
.Private<PredictedProjectile>("radius", (object)0.5f);
yield return Tweak.Spawned("ProphetProj").OnLayer("Default").Private<PredictedProjectile>("damage", (object)0.22f)
.Private<PredictedProjectile>("MOVE_RATE", (object)31f)
.Private<PredictedProjectile>("maxDistance", (object)67f);
}
}
internal static class WeaponTweaks
{
public static IEnumerable<Tweak> All()
{
yield return Tweak.Pickup("AAA12").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.currentAmmo = 90;
}).Rename("AA12");
yield return Tweak.Pickup("AK-K").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.headMultiplier = 3f;
}).Rename("Kaka-K3");
yield return Tweak.Pickup("APMine").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.currentAmmo = 5;
}).Rename("ghep mine");
yield return Tweak.Pickup("AR15").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.needsAmmo = false;
w.headMultiplier = 2f;
w.damage = 0.5f;
}).Rename("BLEP GEN 1");
yield return Tweak.Pickup("BaseballBat").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.movementFactor = 1.2f;
w.damage = 2f;
}).Private<MeleeWeapon>("baseAttackDamage", (object)1f)
.Private<MeleeWeapon>("secondAttackDamage", (object)1f)
.Rename("tung tung sahur");
yield return Tweak.Pickup("BeamLoad").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.timeBetweenFire = 1.5f;
}).Rename("dreamloader");
yield return Tweak.Pickup("Bender").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.damage = 2f;
}).Pin("our Bender damage is 2, regardless of the developers' balancing");
yield return Tweak.Pickup("BigFattyBro").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.damage = 2.8f;
w.headMultiplier = 1.57f;
w.movementFactor = 1.5f;
}).Private<MeleeWeapon>("baseAttackDamage", (object)2.8f)
.Private<MeleeWeapon>("secondAttackDamage", (object)2.8f)
.Rename("buvala");
yield return Tweak.Pickup("BlankState").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.damage = 1.99f;
w.currentAmmo = 3;
w.headMultiplier = 1.1f;
w.timeBetweenFire = 0.7f;
w.movementFactor = 1.4f;
w.jumpFactor = 1.2f;
}).Rename("Blep gen 2 (State)");
yield return Tweak.Pickup("Claymore").Rename("S.L.A.M.");
yield return Tweak.Pickup("Couperet").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.timeBetweenFire = 0.125f;
w.damage = 0.3125f;
w.onePressShoot = false;
w.movementFactor = 0.4f;
}).Private<MeleeWeapon>("baseAttackDamage", (object)0.375f)
.Private<MeleeWeapon>("secondAttackDamage", (object)0.625f)
.Private<MeleeWeapon>("timeBetweenBaseAttack", (object)0.125f)
.Private<MeleeWeapon>("timeBetweenSecondAttack", (object)0.15f)
.Rename("COQUETA");
yield return Tweak.Pickup("Crisis").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.currentAmmo = 60;
w.damage = 0.3f;
w.timeBetweenFire = 0.05f;
w.timeBetweenBullets = 0.025f;
}).Rename("kulana gen 1");
yield return Tweak.Pickup("DF_Torrent").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.damage = 0.8f;
w.bulletsAmount = 5;
w.currentAmmo = 30;
w.timeBetweenBullets = 0.13f;
}).Rename("qBitTorrent");
yield return Tweak.Pickup("Dispenser").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.damage = 0.9f;
w.currentAmmo = 32;
}).Rename("Decimort");
yield return Tweak.Pickup("DualLauncher").Rename("duck launcher");
yield return Tweak.Pickup("Elephant").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.onePressShoot = false;
w.timeBetweenFire = 0.55f;
w.ammoCharge = 0;
w.currentAmmo = 10;
w.damage = 2f;
w.reloadWeapon = false;
}).Rename("KSS-ZGETCH");
yield return Tweak.Pickup("FG42").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.damage = 2f;
}).Rename("kazn gen 2");
yield return Tweak.Pickup("Gamma").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.movementFactor = 0.7f;
w.timeBetweenFire = 0.135f;
}).Rename("Ghep gen 1");
yield return Tweak.Pickup("GammaGen2").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.timeBetweenFire = 0.07f;
w.movementFactor = 1.1f;
}).Rename("Ghep gen 2");
yield return Tweak.Pickup("GlandGrenade").Rename("Blep Grenade");
yield return Tweak.Pickup("Glock").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.damage = 0.8f;
w.currentAmmo = 27;
w.timeBetweenFire = 0.099f;
}).Rename("de_glock");
yield return Tweak.Pickup("Gun").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.damage = 1.2f;
w.timeBetweenFire = 0.1595f;
}).Rename("pistolerus");
yield return Tweak.Pickup("Gust").Rename("kazn gen 3");
yield return Tweak.Pickup("HandCanon").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.movementFactor = 0.1f;
}).Rename("BFG9000");
yield return Tweak.Pickup("Hill_H15").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.damage = 0.4f;
}).Rename("Hill Coleman");
yield return Tweak.Pickup("HK_G11").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.onePressShoot = false;
w.aimBurstGun = false;
w.timeBetweenBullets = 0.135f;
w.timeBetweenFire = 0.135f;
w.damage = 1f;
}).Rename("ghepk_g11");
yield return Tweak.Pickup("Kanye").Rename("M.O.M.");
yield return Tweak.Pickup("Katana").Rename("kuz-yakich");
yield return Tweak.Pickup("Keso").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.timeBetweenFire = 0.12f;
}).Rename("machine gun Keso");
yield return Tweak.Pickup("M2000").Rename("WLEL2000");
yield return Tweak.Pickup("Mac10").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.currentAmmo = 112;
w.damage = 0.2f;
w.timeBetweenFire = 0.01656f;
}).Rename("kulana gen 2 (firework)");
yield return Tweak.Pickup("Minigun").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.maxSpread = 0.1f;
w.minSpread = 0.025f;
w.damage = 0.6f;
w.headMultiplier = 1.5f;
}).PrivateOn("Minigun", "timeBeforeShooting", (object)0.5f)
.Rename("Bear eating shift");
yield return Tweak.Pickup("Nizeh").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.damage = 2f;
}).Private<MeleeWeapon>("baseAttackDamage", (object)2f)
.Private<MeleeWeapon>("secondAttackDamage", (object)4f)
.Rename("nizehzao");
yield return Tweak.Pickup("Nugget").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.currentAmmo = 12;
w.jumpFactor = 3f;
}).Rename("SERUM");
yield return Tweak.Pickup("Phoenix").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.damage = 0.067f;
w.currentAmmo = 500;
w.ammoCharge = 100;
w.maxWallJumps = 2;
w.jumpFactor = 1.5f;
w.movementFactor = 1.05f;
w.wallJumpFactor = 1.7f;
w.timeBetweenFire = 0.05f;
}).Rename("Flamethrower-PHNX223");
yield return Tweak.Pickup("Prophet").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.timeBetweenFire = 0.48f;
w.currentAmmo = 20;
w.onePressShoot = false;
}).Rename("Natures Prophet");
yield return Tweak.Pickup("ProximityMine").Rename("set and plant!");
yield return Tweak.Pickup("QCW05").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.damage = 1f;
}).Pin("our QCW05 damage is 1, regardless of the developers' balancing");
yield return Tweak.Pickup("Repulsar").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.movementFactor = 1.2f;
w.jumpFactor = 3f;
}).Rename("RAILPULSAR");
yield return Tweak.Pickup("Revolver").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.damage = 2.4f;
}).Rename("revolvstrel");
yield return Tweak.Pickup("RocketLauncher").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.movementFactor = 0.8f;
}).Rename("Quadicabus Launcher");
yield return Tweak.Pickup("SMG").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.currentAmmo = 29;
w.damage = 0.6f;
}).Rename("NG-SMG");
yield return Tweak.Pickup("SawedOff").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.currentAmmo = 48;
});
yield return Tweak.Pickup("Shotgun").PrivateOn("Shotgun", "spread", (object)0.1325f).Rename("gunshot");
yield return Tweak.Pickup("Silenzzio").Rename("salozao");
yield return Tweak.Pickup("SmithCarbine").Rename("kazn gen 1");
yield return Tweak.Pickup("Stylus").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.movementFactor = 1.35f;
w.jumpFactor = 1.35f;
}).Rename("Shtylus");
yield return Tweak.Pickup("Taser").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.jumpFactor = 1.5f;
w.maxWallJumps = 20;
w.wallJumpFactor = 3f;
}).PrivateOn("Taser", "stunTime", (object)1.25f)
.Rename("RETARZER");
yield return Tweak.Pickup("Yangtse").Weapon((Action<Weapon>)delegate(Weapon w)
{
w.timeBetweenFire = 0.035f;
}).Rename("Yousuke Yukimatsu");
}
}
}
namespace StraftatCMR.Features
{
[HarmonyPatch(typeof(Claymore))]
internal static class ClaymoreSounds
{
private static readonly string[] BipResources = new string[3] { "StraftatCMR.Audio.ClaymoreBip1.wav", "StraftatCMR.Audio.ClaymoreBip2.wav", "StraftatCMR.Audio.ClaymoreBip3.wav" };
private static ManualLogSource _log;
private static readonly List<AudioClip> _bips = new List<AudioClip>();
private static readonly Random _rng = new Random();
private static bool _ready;
private static bool _firstTrapLogged;
public static void Apply(ManualLogSource log)
{
_log = log;
string[] bipResources = BipResources;
foreach (string text in bipResources)
{
AudioClip val = EmbeddedAudio.Load(Assembly.GetExecutingAssembly(), text, log);
if ((Object)(object)val != (Object)null)
{
_bips.Add(val);
}
}
_ready = _bips.Count > 0;
_log.LogInfo((object)($"[Claymore] beeps loaded {_bips.Count}/{BipResources.Length}" + (_ready ? "" : " - random sound disabled")));
}
[HarmonyPatch("ActivateTrap")]
[HarmonyPrefix]
private static void ActivateTrapPrefix(Claymore __instance)
{
try
{
if ((Object)(object)__instance == (Object)null)
{
return;
}
if (!_firstTrapLogged)
{
_firstTrapLogged = true;
_log.LogInfo((object)("[Claymore] ActivateTrap fired for the first time on " + ((Object)((Component)__instance).gameObject).name + " - the hook is live"));
}
if (_ready)
{
AudioClip val = _bips[_rng.Next(_bips.Count)];
AudioClip val2 = Read(__instance);
if (val2 != val && Write(__instance, val))
{
_log.LogInfo((object)("[Claymore] " + ((Object)((Component)__instance).gameObject).name + ": sound " + Name(val2) + " -> " + Name(val)));
}
}
}
catch (Exception ex)
{
_log.LogError((object)("[Claymore] ActivateTrap failed: " + ex.GetType().Name + ": " + ex.Message));
}
}
private static AudioClip Read(Claymore c)
{
FieldInfo fieldInfo = AccessTools.Field(typeof(Claymore), "activationSound");
if (fieldInfo == null)
{
return null;
}
try
{
object? value = fieldInfo.GetValue(c);
return (AudioClip)((value is AudioClip) ? value : null);
}
catch
{
return null;
}
}
private static bool Write(Claymore c, AudioClip clip)
{
FieldInfo fieldInfo = AccessTools.Field(typeof(Claymore), "activationSound");
if (fieldInfo == null)
{
_log.LogError((object)"[Claymore] Claymore.activationSound field is missing - did the game update?");
return false;
}
try
{
fieldInfo.SetValue(c, clip);
return true;
}
catch (Exception ex)
{
_log.LogError((object)("[Claymore] could not write activationSound: " + ex.Message));
return false;
}
}
private static string Name(AudioClip c)
{
if (!((Object)(object)c == (Object)null))
{
return ((Object)c).name;
}
return "<none>";
}
}
internal static class GammaProjectileSplit
{
private const string Gen1Weapon = "Gamma";
private const string Gen2Weapon = "GammaGen2";
private const float Gen1Damage = 1.5f;
private const float Gen2Damage = 0.5f;
private static readonly Dictionary<string, float> _damageByWeapon = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase)
{
{ "Gamma", 1.5f },
{ "GammaGen2", 0.5f }
};
private static ManualLogSource _log;
private static FieldInfo _projectileField;
private static FieldInfo _damageField;
private static bool _ready;
private static readonly HashSet<string> _seen = new HashSet<string>();
public static void Apply(ManualLogSource log)
{
_log = log;
_projectileField = AccessTools.Field(AccessTools.TypeByName("DualLauncher"), "_projectile");
_damageField = AccessTools.Field(typeof(PredictedProjectile), "damage");
if (_projectileField == null || _damageField == null)
{
_log.LogError((object)"[Gamma] DualLauncher._projectile or PredictedProjectile.damage is missing - both gammas will keep sharing one damage value");
return;
}
GameObject val = Single("Gamma");
GameObject val2 = Single("GammaGen2");
if ((Object)(object)val != (Object)null && (Object)(object)val2 != (Object)null)
{
PrefabRebinder.ReportSharing((IEnumerable<GameObject>)(object)new GameObject[2] { val, val2 }, log);
}
ApplyToPrefab(val, 1.5f, "Gamma");
_ready = true;
_log.LogInfo((object)string.Format("[Gamma] per-shot damage armed: {0}={1}, {2}={3}", "Gamma", 1.5f, "GammaGen2", 0.5f));
}
internal static void BeforeSpawn(object launcher)
{
if (!_ready || launcher == null)
{
return;
}
Component val = (Component)((launcher is Component) ? launcher : null);
if ((Object)(object)val == (Object)null)
{
return;
}
string text = StripClone(((Object)val.gameObject).name);
if (!_damageByWeapon.TryGetValue(text, out var value))
{
return;
}
object value2;
try
{
value2 = _projectileField.GetValue(launcher);
}
catch
{
return;
}
if (value2 != null)
{
try
{
_damageField.SetValue(value2, value);
}
catch (Exception ex)
{
_log.LogError((object)("[Gamma] could not set damage: " + ex.Message));
return;
}
if (_seen.Add(text))
{
_log.LogInfo((object)("[Gamma] " + text + " fired: projectile damage set to " + FieldSnapshot.Fmt((object)value)));
}
}
}
private static void ApplyToPrefab(GameObject weapon, float damage, string label)
{
if ((Object)(object)weapon == (Object)null)
{
return;
}
foreach (PrefabRef item in PrefabRebinder.FindExternalRefs(weapon))
{
PredictedProjectile componentInChildren = item.TargetRoot.GetComponentInChildren<PredictedProjectile>(true);
if (!((Object)(object)componentInChildren == (Object)null))
{
Dictionary<string, object> dictionary = FieldSnapshot.Take((Component)(object)componentInChildren);
_damageField.SetValue(componentInChildren, damage);
var (flag, text) = FieldSnapshot.Diff(dictionary, (Component)(object)componentInChildren);
_log.LogInfo((object)("[Gamma] " + label + " baseline: " + ((Object)item.TargetRoot).name + " damage -> " + FieldSnapshot.Fmt((object)damage) + " [" + (flag ? text : "pin, value already ours") + "]"));
break;
}
}
}
private static GameObject Single(string name)
{
//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_000c: 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)
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_001f: Expected O, but got Unknown
GameObject[] array = PrefabRegistry.Resolve(new PrefabQuery
{
Name = name,
Exact = true,
Scope = (PrefabScope)1
});
if (array.Length == 1)
{
return array[0];
}
_log.LogWarning((object)$"[Gamma] {name}: {array.Length} matches, expected 1");
return null;
}
private static string StripClone(string name)
{
if (!name.EndsWith("(Clone)", StringComparison.Ordinal))
{
return name;
}
return name.Substring(0, name.Length - "(Clone)".Length);
}
}
[HarmonyPatch]
internal static class GammaSpawnPatch
{
[HarmonyPatch(typeof(DualLauncher), "SpawnProjectile")]
[HarmonyPrefix]
private static void Prefix(DualLauncher __instance)
{
try
{
GammaProjectileSplit.BeforeSpawn(__instance);
}
catch (Exception ex)
{
StraftatCMRPlugin.Log.LogError((object)("[Gamma] SpawnProjectile prefix failed: " + ex.Message));
}
}
}
[HarmonyPatch(typeof(FirstPersonController))]
internal static class HKG11FireMode
{
private sealed class State
{
public NetworkBehaviour Net;
public Transform PlayerRoot;
public Weapon Weapon;
public int LastLookupFrame = -999;
public bool HasHk;
public bool IsAiming;
public bool FastMode;
public bool Initialised;
public string LastLoggedSituation;
}
public const string WeaponNameFragment = "HK_G11";
private const string SwitchSoundResource = "StraftatCMR.Audio.SwitchMode.wav";
private const string PlayerRootName = "PlayerIK(Clone)";
private const int CacheRefreshFrames = 15;
private static ManualLogSource _log;
private static AudioClip _switchSound;
private static bool _verbose;
private static readonly Dictionary<int, State> _states = new Dictionary<int, State>();
public static void Init(ManualLogSource log, bool verbose)
{
_log = log;
_verbose = verbose;
ModeTextHud.Init(log);
_switchSound = EmbeddedAudio.Load(Assembly.GetExecutingAssembly(), "StraftatCMR.Audio.SwitchMode.wav", log);
if ((Object)(object)_switchSound == (Object)null)
{
_log.LogWarning((object)"[HK] switch sound failed to load - mode changes will be silent");
}
_log.LogInfo((object)("[HK] initialised, verbose log: " + (verbose ? "on" : "off")));
}
[HarmonyPatch("Update")]
[HarmonyPostfix]
private static void UpdatePostfix(FirstPersonController __instance)
{
try
{
Tick(__instance);
}
catch (Exception ex)
{
_log.LogError((object)("[HK] Update failed: " + ex.GetType().Name + ": " + ex.Message + "\n" + ex.StackTrace));
}
}
private static void Tick(FirstPersonController fpc)
{
//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)fpc == (Object)null)
{
return;
}
int instanceID = ((Object)fpc).GetInstanceID();
if (!_states.TryGetValue(instanceID, out var value))
{
value = new State();
_states[instanceID] = value;
if (_verbose)
{
_log.LogInfo((object)$"[HK] new FirstPersonController #{instanceID}");
}
}
if ((Object)(object)value.Net == (Object)null)
{
value.Net = ((Component)fpc).GetComponent<NetworkBehaviour>();
}
if (!((Object)(object)value.Net != (Object)null) || !value.Net.IsClient || !value.Net.IsOwner)
{
LogSituationOnce(value, "not our player");
return;
}
RefreshWeaponIfNeeded(fpc, value);
bool flag = (Object)(object)value.Weapon != (Object)null;
if (flag != value.HasHk)
{
value.HasHk = flag;
_log.LogInfo((object)(flag ? ("[HK] picked up HK_G11 (" + ((Object)value.Weapon).name + "), mode: " + ModeName(value.FastMode)) : "[HK] HK_G11 put away"));
}
if (!flag)
{
ModeTextHud.Hide();
LogSituationOnce(value, "holding something other than the HK");
return;
}
bool isScopeAiming = fpc.isScopeAiming;
if (!value.Initialised)
{
value.Initialised = true;
value.IsAiming = isScopeAiming;
ApplyStats(value, isScopeAiming, value.FastMode, "initial application");
}
if (isScopeAiming != value.IsAiming)
{
value.IsAiming = isScopeAiming;
ApplyStats(value, isScopeAiming, value.FastMode, isScopeAiming ? "entered aim" : "left aim");
}
if (!isScopeAiming && Keyboard.current != null && ((ButtonControl)Keyboard.current.xKey).wasPressedThisFrame)
{
value.FastMode = !value.FastMode;
_log.LogInfo((object)("[HK] === fire mode switched -> " + ModeName(value.FastMode) + " ==="));
if ((Object)(object)_switchSound != (Object)null)
{
AudioSource.PlayClipAtPoint(_switchSound, ((Component)fpc).transform.position, 1f);
}
ApplyStats(value, isScopeAiming, value.FastMode, "mode switch");
}
ModeTextHud.Set(visible: true, value.FastMode ? "Mode: Single" : "Mode: Auto");
LogSituationOnce(value, "HK in hand, " + (isScopeAiming ? "aiming" : "hip") + ", " + ModeName(value.FastMode));
}
private static void RefreshWeaponIfNeeded(FirstPersonController fpc, State st)
{
bool num = (Object)(object)st.Weapon != (Object)null && (Object)(object)((Component)st.Weapon).gameObject != (Object)null && ((Component)st.Weapon).gameObject.activeInHierarchy;
bool flag = Time.frameCount - st.LastLookupFrame >= 15;
if (num && !flag)
{
return;
}
st.LastLookupFrame = Time.frameCount;
if ((Object)(object)st.PlayerRoot == (Object)null)
{
Transform val = ((Component)fpc).transform;
while ((Object)(object)val != (Object)null && ((Object)val).name != "PlayerIK(Clone)")
{
val = val.parent;
}
st.PlayerRoot = val;
if ((Object)(object)val == (Object)null)
{
if (_verbose)
{
LogSituationOnce(st, "PlayerIK(Clone) not found");
}
st.Weapon = null;
return;
}
}
Weapon componentInChildren = ((Component)st.PlayerRoot).GetComponentInChildren<Weapon>(true);
if ((Object)(object)componentInChildren == (Object)null || ((Object)componentInChildren).name.IndexOf("HK_G11", StringComparison.OrdinalIgnoreCase) < 0)
{
st.Weapon = null;
return;
}
NetworkBehaviour component = ((Component)componentInChildren).GetComponent<NetworkBehaviour>();
if ((Object)(object)component == (Object)null || !component.IsClient || !component.IsOwner)
{
if (_verbose)
{
LogSituationOnce(st, "HK found but it is not our instance");
}
st.Weapon = null;
}
else if (st.Weapon != componentInChildren)
{
st.Weapon = componentInChildren;
st.Initialised = false;
if (_verbose)
{
_log.LogInfo((object)$"[HK] cached instance {((Object)componentInChildren).name} #{((Object)componentInChildren).GetInstanceID()}");
}
}
}
private static void ApplyStats(State st, bool aiming, bool fastMode, string reason)
{
Weapon weapon = st.Weapon;
if ((Object)(object)weapon == (Object)null)
{
_log.LogWarning((object)("[HK] " + reason + ": no weapon, stats not applied"));
return;
}
Dictionary<string, object> dictionary = FieldSnapshot.Take((Component)(object)weapon);
string text;
if (aiming)
{
text = "AIMING";
weapon.onePressShoot = false;
weapon.aimBurstGun = false;
weapon.timeBetweenBullets = 2f;
weapon.timeBetweenFire = 2f;
weapon.damage = 100f;
}
else if (fastMode)
{
text = "HIP / Single";
weapon.onePressShoot = true;
weapon.aimBurstGun = false;
weapon.timeBetweenBullets = 0.07f;
weapon.timeBetweenFire = 0.07f;
weapon.damage = 1.4f;
}
else
{
text = "HIP / Auto";
weapon.onePressShoot = false;
weapon.aimBurstGun = false;
weapon.timeBetweenBullets = 0.135f;
weapon.timeBetweenFire = 0.135f;
weapon.damage = 1f;
}
var (flag, text2) = FieldSnapshot.Diff(dictionary, (Component)(object)weapon);
_log.LogInfo((object)("[HK] " + reason + " -> set " + text + ": " + text2));
if (!flag)
{
_log.LogInfo((object)("[HK] (values already matched set " + text + ")"));
}
_log.LogInfo((object)("[HK] result: damage=" + FieldSnapshot.Fmt((object)weapon.damage) + ", tbf=" + FieldSnapshot.Fmt((object)weapon.timeBetweenFire) + ", tbb=" + FieldSnapshot.Fmt((object)weapon.timeBetweenBullets) + ", onePressShoot=" + FieldSnapshot.Fmt((object)weapon.onePressShoot) + ", aimBurstGun=" + FieldSnapshot.Fmt((object)weapon.aimBurstGun)));
}
private static string ModeName(bool fast)
{
if (!fast)
{
return "Auto (default)";
}
return "Single (fast)";
}
private static void LogSituationOnce(State st, string situation)
{
if (_verbose && !(st.LastLoggedSituation == situation))
{
st.LastLoggedSituation = situation;
_log.LogInfo((object)("[HK] state: " + situation));
}
}
}
internal static class MenuText
{
private const string StartupMenu = "---USER INTERFACE---/--MAIN MENU--/--STARTUP MENU--";
public static void Apply(ManualLogSource log, string modVersion)
{
TextPatch.At("---USER INTERFACE---/--MAIN MENU--/--STARTUP MENU--/StraftatText").Set("STRAFTAT: CMR");
TextPatch.At("---USER INTERFACE---/--MAIN MENU--/--STARTUP MENU--/StraftatText (2)").Set("--- press any key to blep ---");
TextPatch.At("---USER INTERFACE---/--MAIN MENU--/--STARTUP MENU--/version").Transform((Func<string, string>)((string orig) => orig.TrimEnd(Array.Empty<char>()) + "\r\nCMR " + modVersion));
log.LogInfo((object)("[Menu] text patches queued, mod version " + modVersion));
log.LogInfo((object)("[Menu] game version per SteamLobby: " + (ReadGameVersion(log) ?? "<unreadable>")));
}
public static string ReadGameVersion(ManualLogSource log)
{
try
{
Type type = AccessTools.TypeByName("SteamLobby");
if (type == null)
{
return null;
}
FieldInfo fieldInfo = AccessTools.Field(type, "GameVersion");
if (fieldInfo == null)
{
return null;
}
return fieldInfo.GetValue(null) as string;
}
catch (Exception ex)
{
log.LogWarning((object)("[Menu] could not read the game version: " + ex.Message));
return null;
}
}
}
[HarmonyPatch(typeof(ProximityMine))]
internal static class MineSounds
{
private const string GhepResource = "StraftatCMR.Audio.Ghep.wav";
private static readonly string[] BipResources = new string[3] { "StraftatCMR.Audio.ClaymoreBip1.wav", "StraftatCMR.Audio.ClaymoreBip2.wav", "StraftatCMR.Audio.ClaymoreBip3.wav" };
private const string ApMinePrefabName = "APMineObject";
private static ManualLogSource _log;
private static AudioClip _ghep;
private static readonly List<AudioClip> _bips = new List<AudioClip>();
private static readonly Random _rng = new Random();
private static readonly HashSet<string> _randomBipPrefabs = new HashSet<string>();
private static readonly HashSet<string> _seenAtStart = new HashSet<string>();
private static bool _ready;
public static void Apply(ManualLogSource log)
{
_log = log;
LoadClips();
ClassifyPrefabs();
ApplyGhepToApMinePrefab();
_ready = _bips.Count > 0;
if (!_ready)
{
_log.LogWarning((object)"[Mines] no beeps loaded - random sounds disabled");
}
}
private static void LoadClips()
{
_ghep = EmbeddedAudio.Load(Assembly.GetExecutingAssembly(), "StraftatCMR.Audio.Ghep.wav", _log);
string[] bipResources = BipResources;
foreach (string text in bipResources)
{
AudioClip val = EmbeddedAudio.Load(Assembly.GetExecutingAssembly(), text, _log);
if ((Object)(object)val != (Object)null)
{
_bips.Add(val);
}
}
_log.LogInfo((object)("[Mines] sounds: Ghep=" + (((Object)(object)_ghep != (Object)null) ? "ok" : "MISSING") + ", " + $"beeps {_bips.Count}/{BipResources.Length}"));
}
private static void ClassifyPrefabs()
{
GameObject[] array = PrefabRegistry.Spawned.Where((GameObject go) => (Object)(object)go != (Object)null && (Object)(object)go.GetComponentInChildren<ProximityMine>(true) != (Object)null).ToArray();
GameObject[] array2 = array;
foreach (GameObject val in array2)
{
bool flag = string.Equals(((Object)val).name, "APMineObject", StringComparison.OrdinalIgnoreCase);
if (!flag)
{
_randomBipPrefabs.Add(((Object)val).name);
}
_log.LogInfo((object)($"[Mines] {((Object)val).name}#{((Object)val).GetInstanceID()} tag='{val.tag}' -> " + (flag ? "own Ghep sound" : "random beep")));
}
if (array.Length == 0)
{
_log.LogWarning((object)"[Mines] no prefabs with ProximityMine found");
}
}
private static void ApplyGhepToApMinePrefab()
{
//IL_001d: 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_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_0036: Unknown result type (might be due to invalid IL or missing references)
//IL_0040: Expected O, but got Unknown
if ((Object)(object)_ghep == (Object)null)
{
_log.LogWarning((object)"[Mines] Ghep.wav not loaded - the AP mine keeps the game's sound");
return;
}
GameObject[] array = PrefabRegistry.Resolve(new PrefabQuery
{
Name = "APMineObject",
Exact = true,
Scope = (PrefabScope)2
});
if (array.Length != 1)
{
_log.LogWarning((object)string.Format("[Mines] {0}: {1} matches, expected 1 - sound not applied", "APMineObject", array.Length));
return;
}
ProximityMine componentInChildren = array[0].GetComponentInChildren<ProximityMine>(true);
if ((Object)(object)componentInChildren == (Object)null)
{
_log.LogWarning((object)"[Mines] APMineObject has no ProximityMine");
return;
}
AudioClip c = ReadSound(componentInChildren);
if (WriteSound(componentInChildren, _ghep))
{
_log.LogInfo((object)("[Mines] APMineObject: activationSound " + Name(c) + " -> " + Name(_ghep) + " (into the prefab, once)"));
}
}
[HarmonyPatch("Start")]
[HarmonyPrefix]
private static void StartPrefix(ProximityMine __instance)
{
try
{
if ((Object)(object)__instance == (Object)null)
{
return;
}
string text = StripClone(((Object)((Component)__instance).gameObject).name);
if (_seenAtStart.Add(text))
{
_log.LogInfo((object)("[Mines] Start fired on " + ((Object)((Component)__instance).gameObject).name + " " + $"(prefab {text}), in the random list: {_randomBipPrefabs.Contains(text)}"));
}
if (_ready && _randomBipPrefabs.Contains(text))
{
AudioClip val = _bips[_rng.Next(_bips.Count)];
AudioClip val2 = ReadSound(__instance);
if (val2 != val && WriteSound(__instance, val))
{
_log.LogInfo((object)("[Mines] " + ((Object)((Component)__instance).gameObject).name + ": beep " + Name(val2) + " -> " + Name(val)));
}
}
}
catch (Exception ex)
{
_log.LogError((object)("[Mines] Start failed: " + ex.GetType().Name + ": " + ex.Message));
}
}
private static AudioClip ReadSound(ProximityMine mine)
{
FieldInfo fieldInfo = AccessTools.Field(typeof(ProximityMine), "activationSound");
if (fieldInfo == null)
{
return null;
}
try
{
object? value = fieldInfo.GetValue(mine);
return (AudioClip)((value is AudioClip) ? value : null);
}
catch
{
return null;
}
}
private static bool WriteSound(ProximityMine mine, AudioClip clip)
{
FieldInfo fieldInfo = AccessTools.Field(typeof(ProximityMine), "activationSound");
if (fieldInfo == null)
{
_log.LogError((object)"[Mines] ProximityMine.activationSound field is missing - did the game update?");
return false;
}
try
{
fieldInfo.SetValue(mine, clip);
return true;
}
catch (Exception ex)
{
_log.LogError((object)("[Mines] could not write activationSound: " + ex.Message));
return false;
}
}
private static string StripClone(string name)
{
if (!name.EndsWith("(Clone)", StringComparison.Ordinal))
{
return name;
}
return name.Substring(0, name.Length - "(Clone)".Length);
}
private static string Name(AudioClip c)
{
if (!((Object)(object)c == (Object)null))
{
return ((Object)c).name;
}
return "<none>";
}
}
internal static class ModeTextHud
{
private const string UiRootName = "---USER INTERFACE---";
private const string MinimalUiName = "---MINIMAL UI---";
private const string SourceName = "HealthDisplay";
private const string HudName = "CMR_ModeText";
private static ManualLogSource _log;
private static GameObject _root;
private static TMP_Text _text;
private static string _lastText;
private static bool _lastActive;
private static int _lastAttemptFrame = -1;
public static void Init(ManualLogSource log)
{
_log = log;
}
public static void Set(bool visible, string text)
{
if (!EnsureCreated())
{
return;
}
if (_root.activeSelf != visible)
{
_root.SetActive(visible);
if (_lastActive != visible)
{
_lastActive = visible;
_log.LogInfo((object)("[HUD] indicator " + (visible ? "shown" : "hidden")));
}
}
if (visible && !((Object)(object)_text == (Object)null) && _text.text != text)
{
_text.text = text;
if (_lastText != text)
{
_lastText = text;
_log.LogInfo((object)("[HUD] text: \"" + text + "\""));
}
}
}
public static void Hide()
{
if (!((Object)(object)_root == (Object)null) && _root.activeSelf)
{
_root.SetActive(false);
_lastActive = false;
_log.LogInfo((object)"[HUD] indicator hidden");
}
}
private static bool EnsureCreated()
{
//IL_00d6: 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)
//IL_0102: Unknown result type (might be due to invalid IL or missing references)
//IL_0118: Unknown result type (might be due to invalid IL or missing references)
//IL_012e: Unknown result type (might be due to invalid IL or missing references)
//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)_root != (Object)null)
{
return true;
}
if (_lastAttemptFrame >= 0 && Time.frameCount - _lastAttemptFrame < 30)
{
return false;
}
_lastAttemptFrame = Time.frameCount;
Transform val = FindRoot("---USER INTERFACE---");
if ((Object)(object)val == (Object)null)
{
return false;
}
Transform val2 = FindDescendant(val, "---MINIMAL UI---");
if ((Object)(object)val2 == (Object)null)
{
_log.LogWarning((object)"[HUD] ---MINIMAL UI--- not found - there will be no indicator");
return false;
}
Transform val3 = FindDescendant(val2, "HealthDisplay");
if ((Object)(object)val3 == (Object)null)
{
_log.LogWarning((object)"[HUD] HealthDisplay not found, nothing to clone");
return false;
}
GameObject val4 = Object.Instantiate<GameObject>(((Component)val3).gameObject);
((Object)val4).name = "CMR_ModeText";
val4.transform.SetParent(val, false);
RectTransform component = val4.GetComponent<RectTransform>();
if ((Object)(object)component != (Object)null)
{
component.anchorMin = new Vector2(1f, 1f);
component.anchorMax = new Vector2(1f, 1f);
component.pivot = new Vector2(1f, 1f);
component.anchoredPosition = new Vector2(-1613.105f, -442.4104f);
component.sizeDelta = new Vector2(200f, 30f);
}
else
{
_log.LogWarning((object)"[HUD] clone has no RectTransform - position stays as in the original");
}
Transform val5 = FindDescendant(val4.transform, "text");
_text = (((Object)(object)val5 != (Object)null) ? ((Component)val5).GetComponent<TMP_Text>() : null);
if ((Object)(object)_text == (Object)null)
{
_text = val4.GetComponentInChildren<TMP_Text>(true);
if ((Object)(object)_text != (Object)null)
{
_log.LogInfo((object)"[HUD] child 'text' not found, using the first TMP_Text in the clone");
}
}
if ((Object)(object)_text == (Object)null)
{
_log.LogWarning((object)"[HUD] no TMP_Text found - the indicator will be blank");
}
val4.SetActive(false);
_root = val4;
_lastActive = false;
_log.LogInfo((object)("[HUD] CMR_ModeText created (clone of HealthDisplay), position " + (((Object)(object)component != (Object)null) ? ((object)component.anchoredPosition/*cast due to .constrained prefix*/).ToString() : "as in the original")));
return true;
}
private static Transform FindRoot(string name)
{
//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)
for (int i = 0; i < SceneManager.sceneCount; i++)
{
Scene sceneAt = SceneManager.GetSceneAt(i);
if (!((Scene)(ref sceneAt)).isLoaded)
{
continue;
}
GameObject[] rootGameObjects = ((Scene)(ref sceneAt)).GetRootGameObjects();
foreach (GameObject val in rootGameObjects)
{
if (((Object)val).name == name)
{
return val.transform;
}
}
}
GameObject val2 = GameObject.Find(name);
if (!((Object)(object)val2 != (Object)null))
{
return null;
}
return val2.transform;
}
private static Transform FindDescendant(Transform parent, string name)
{
if ((Object)(object)parent == (Object)null)
{
return null;
}
Queue<Transform> queue = new Queue<Transform>();
queue.Enqueue(parent);
while (queue.Count > 0)
{
Transform val = queue.Dequeue();
if (((Object)val).name == name)
{
return val;
}
for (int i = 0; i < val.childCount; i++)
{
queue.Enqueue(val.GetChild(i));
}
}
return null;
}
}
public struct FlashlightToggleBroadcast : IBroadcast
{
public int ObjectId;
public bool IsOn;
}
[HarmonyPatch(typeof(FirstPersonController))]
internal static class WardenFlashlight
{
private sealed class State
{
public NetworkBehaviour Net;
public Weapon Warden;
public Transform Light;
public int LastLookupFrame = -999;
public bool HadWarden;
}
private const string WeaponNameFragment = "warden";
private const string LightObjectName = "Spot Light";
private const int CacheRefreshFrames = 15;
private static ManualLogSource _log;
private static bool _verbose;
private static bool _registered;
private static bool _serializersSet;
private static int _lastRegisterAttempt = -999;
private static readonly Dictionary<int, State> _states = new Dictionary<int, State>();
public static void Init(ManualLogSource log, bool verbose)
{
_log = log;
_verbose = verbose;
GenericWriter<FlashlightToggleBroadcast>.Write = Write;
GenericReader<FlashlightToggleBroadcast>.Read = Read;
_serializersSet = true;
_log.LogInfo((object)"[Flashlight] serializers assigned, waiting for the network managers");
}
private static void TryRegister()
{
if (!_registered && _serializersSet && Time.frameCount - _lastRegisterAttempt >= 60)
{
_lastRegisterAttempt = Time.frameCount;
bool flag = false;
if ((Object)(object)InstanceFinder.ClientManager != (Object)null)
{
InstanceFinder.ClientManager.RegisterBroadcast<FlashlightToggleBroadcast>((Action<FlashlightToggleBroadcast>)OnClientToggle);
_log.LogInfo((object)"[Flashlight] client broadcast registered");
flag = true;
}
if ((Object)(object)InstanceFinder.ServerManager != (Object)null)
{
InstanceFinder.ServerManager.RegisterBroadcast<FlashlightToggleBroadcast>((Action<NetworkConnection, FlashlightToggleBroadcast>)OnServerToggle, true);
_log.LogInfo((object)"[Flashlight] server broadcast registered");
flag = true;
}
if (flag)
{
_registered = true;
}
}
}
private static void OnServerToggle(NetworkConnection conn, FlashlightToggleBroadcast msg)
{
if (!((Object)(object)InstanceFinder.ServerManager == (Object)null))
{
if (conn != (NetworkConnection)null && !OwnsObject(conn, msg.ObjectId))
{
_log.LogWarning((object)$"[Flashlight] client {conn.ClientId} is toggling a foreign object {msg.ObjectId}");
}
InstanceFinder.ServerManager.Broadcast<FlashlightToggleBroadcast>(msg, true, (Channel)0);
}
}
private static bool OwnsObject(NetworkConnection conn, int objectId)
{
try
{
if (((ManagedObjects)InstanceFinder.ServerManager.Objects).Spawned.TryGetValue(objectId, out var value))
{
return (Object)(object)value != (Object)null && value.Owner == conn;
}
}
catch
{
}
return true;
}
private static void OnClientToggle(FlashlightToggleBroadcast msg)
{
try
{
ApplyRemote(msg.ObjectId, msg.IsOn);
}
catch (Exception ex)
{
_log.LogError((object)("[Flashlight] receive failed: " + ex.GetType().Name + ": " + ex.Message));
}
}
private static void ApplyRemote(int objectId, bool isOn)
{
if ((Object)(object)InstanceFinder.ClientManager == (Object)null)
{
return;
}
if (!((ManagedObjects)InstanceFinder.ClientManager.Objects).Spawned.TryGetValue(objectId, out var value) || (Object)(object)value == (Object)null)
{
if (_verbose)
{
_log.LogInfo((object)$"[Flashlight] object {objectId} not found on this client");
}
return;
}
Transform val = FindLight(((Component)value).transform);
if ((Object)(object)val == (Object)null)
{
if (_verbose)
{
_log.LogInfo((object)string.Format("[Flashlight] object {0} has no '{1}'", objectId, "Spot Light"));
}
return;
}
((Component)val).gameObject.SetActive(isOn);
if (_verbose)
{
_log.LogInfo((object)string.Format("[Flashlight] remote {0} -> {1}", objectId, isOn ? "on" : "off"));
}
}
[HarmonyPatch("Update")]
[HarmonyPostfix]
private static void UpdatePostfix(FirstPersonController __instance)
{
try
{
Tick(__instance);
}
catch (Exception ex)
{
_log.LogError((object)("[Flashlight] Update failed: " + ex.GetType().Name + ": " + ex.Message));
}
}
private static void Tick(FirstPersonController fpc)
{
if ((Object)(object)fpc == (Object)null)
{
return;
}
TryRegister();
int instanceID = ((Object)fpc).GetInstanceID();
if (!_states.TryGetValue(instanceID, out var value))
{
value = (_states[instanceID] = new State());
}
if ((Object)(object)value.Net == (Object)null)
{
value.Net = ((Component)fpc).GetComponent<NetworkBehaviour>();
}
if (!((Object)(object)value.Net == (Object)null) && value.Net.IsClient && value.Net.IsOwner)
{
RefreshCache(fpc, value);
bool flag = (Object)(object)value.Warden != (Object)null;
if (flag != value.HadWarden)
{
value.HadWarden = flag;
_log.LogInfo((object)(flag ? ("[Flashlight] picked up Warden (" + ((Object)value.Warden).name + "), light: " + (((Object)(object)value.Light != (Object)null) ? "found" : "NOT found")) : "[Flashlight] Warden put away"));
}
if (flag && !((Object)(object)value.Light == (Object)null) && Keyboard.current != null && ((ButtonControl)Keyboard.current.xKey).wasPressedThisFrame)
{
bool flag2 = !((Component)value.Light).gameObject.activeSelf;
((Component)value.Light).gameObject.SetActive(flag2);
_log.LogInfo((object)("[Flashlight] === toggled -> " + (flag2 ? "ON" : "OFF") + " ==="));
Send(value.Net.ObjectId, flag2);
}
}
}
private static void Send(int objectId, bool isOn)
{
if ((Object)(object)InstanceFinder.ClientManager == (Object)null || !InstanceFinder.ClientManager.Connection.IsActive)
{
_log.LogWarning((object)"[Flashlight] no client connection - the state stayed local");
return;
}
InstanceFinder.ClientManager.Broadcast<FlashlightToggleBroadcast>(new FlashlightToggleBroadcast
{
ObjectId = objectId,
IsOn = isOn
}, (Channel)0);
if (_verbose)
{
_log.LogInfo((object)$"[Flashlight] sent: object {objectId} -> {isOn}");
}
}
private static void RefreshCache(FirstPersonController fpc, State st)
{
if ((Object)(object)st.Warden != (Object)null && (Object)(object)((Component)st.Warden).gameObject != (Object)null && ((Component)st.Warden).gameObject.activeInHierarchy && (Object)(object)st.Light != (Object)null && Time.frameCount - st.LastLookupFrame < 15)
{
return;
}
st.LastLookupFrame = Time.frameCount;
st.Warden = null;
st.Light = null;
Weapon[] componentsInChildren = ((Component)fpc).GetComponentsInChildren<Weapon>(true);
foreach (Weapon val in componentsInChildren)
{
if (!((Object)(object)val == (Object)null) && ((Component)val).gameObject.activeInHierarchy && ((Object)val).name.IndexOf("warden", StringComparison.OrdinalIgnoreCase) >= 0)
{
st.Warden = val;
st.Light = FindLight(((Component)val).transform);
break;
}
}
}
private static Transform FindLight(Transform root)
{
Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren<Transform>(true);
foreach (Transform val in componentsInChildren)
{
if (((Object)val).name == "Spot Light")
{
return val;
}
}
return null;
}
public static void Write(Writer writer, FlashlightToggleBroadcast value)
{
writer.WriteInt32(value.ObjectId, (AutoPackType)1);
writer.WriteBoolean(value.IsOn);
}
public static FlashlightToggleBroadcast Read(Reader reader)
{
return new FlashlightToggleBroadcast
{
ObjectId = reader.ReadInt32((AutoPackType)1),
IsOn = reader.ReadBoolean()
};
}
}
internal static class WeaponSounds
{
private sealed class SoundSet
{
public string Prefab;
public string[] FireClips;
public string[] MeleeSecondClips;
public bool IsFixed
{
get
{
if (FireClips != null)
{
return FireClips.Length == 1;
}
return false;
}
}
}
private const string Res = "StraftatCMR.Audio.";
private static readonly SoundSet[] Sets = new SoundSet[3]
{
new SoundSet
{
Prefab = "Phoenix",
FireClips = new string[1] { "StraftatCMR.Audio.PhoenixNew.wav" }
},
new SoundSet
{
Prefab = "BlankState",
FireClips = (from i in Enumerable.Range(1, 4)
select string.Format("{0}BlepState{1}.wav", "StraftatCMR.Audio.", i)).ToArray()
},
new SoundSet
{
Prefab = "BaseballBat",
FireClips = (from i in Enumerable.Range(1, 8)
select string.Format("{0}Tung{1}.wav", "StraftatCMR.Audio.", i)).ToArray(),
MeleeSecondClips = (from i in Enumerable.Range(1, 8)
select string.Format("{0}Tung{1}.wav", "StraftatCMR.Audio.", i)).ToArray()
}
};
private static ManualLogSource _log;
private static readonly Random _rng = new Random();
private static readonly Dictionary<string, List<AudioClip>> _fire = new Dictionary<string, List<AudioClip>>();
private static readonly Dictionary<string, List<AudioClip>> _melee = new Dictionary<string, List<AudioClip>>();
private static readonly HashSet<string> _seenShoot = new HashSet<string>();
private static readonly HashSet<string> _seenMelee = new HashSet<string>();
private static readonly HashSet<string> _seenAwake = new HashSet<string>();
public static void Apply(ManualLogSource log)
{
_log = log;
SoundSet[] sets = Sets;
foreach (SoundSet soundSet in sets)
{
List<AudioClip> list = LoadAll(soundSet.FireClips);
if (list.Count == 0)
{
_log.LogWarning((object)("[Sounds] " + soundSet.Prefab + ": no clips loaded - skipped"));
continue;
}
if (soundSet.IsFixed)
{
AssignToPrefab(soundSet.Prefab, list[0]);
}
else
{
_fire[soundSet.Prefab] = list;
_log.LogInfo((object)$"[Sounds] {soundSet.Prefab}: fireClip rotation, {list.Count} clips");
AssignToPrefab(soundSet.Prefab, list[0]);
}
if (soundSet.MeleeSecondClips != null)
{
List<AudioClip> list2 = LoadAll(soundSet.MeleeSecondClips);
if (list2.Count > 0)
{
_melee[soundSet.Prefab] = list2;
_log.LogInfo((object)$"[Sounds] {soundSet.Prefab}: secondAttackClip rotation, {list2.Count} clips");
}
}
}
}
private static List<AudioClip> LoadAll(string[] resources)
{
List<AudioClip> list = new List<AudioClip>();
if (resources == null)
{
return list;
}
foreach (string text in resources)
{
AudioClip val = EmbeddedAudio.Load(Assembly.GetExecutingAssembly(), text, _log);
if ((Object)(object)val != (Object)null)
{
list.Add(val);
}
}
return list;
}
private static void AssignToPrefab(string prefabName, AudioClip clip)
{
//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_000c: 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)
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_001f: Expected O, but got Unknown
GameObject[] array = PrefabRegistry.Resolve(new PrefabQuery
{
Name = prefabName,
Exact = true,
Scope = (PrefabScope)1
});
if (array.Length != 1)
{
_log.LogWarning((object)$"[Sounds] {prefabName}: {array.Length} matches, expected 1 - sound not applied");
return;
}
Weapon componentInChildren = array[0].GetComponentInChildren<Weapon>(true);
if ((Object)(object)componentInChildren == (Object)null)
{
_log.LogWarning((object)("[Sounds] " + prefabName + ": no Weapon component"));
return;
}
AudioClip fireClip = componentInChildren.fireClip;
componentInChildren.fireClip = clip;
_log.LogInfo((object)("[Sounds] " + prefabName + ": fireClip " + Name(fireClip) + " -> " + Name(clip) + " (into the prefab, once)"));
}
internal static void OnWeaponAwake(Weapon weapon)
{
if ((Object)(object)weapon == (Object)null)
{
return;
}
string text = StripClone(((Object)((Component)weapon).gameObject).name);
if (_fire.TryGetValue(text, out var value))
{
weapon.fireClip = value[_rng.Next(value.Count)];
}
if (_melee.TryGetValue(text, out var value2))
{
MeleeWeapon val = (MeleeWeapon)(object)((weapon is MeleeWeapon) ? weapon : null);
if (val != null)
{
SetPrivateClip(val, "secondAttackClip", value2[_rng.Next(value2.Count)]);
}
}
if (_seenAwake.Add(text) && (_fire.ContainsKey(text) || _melee.ContainsKey(text)))
{
_log.LogInfo((object)("[Sounds] Awake: " + ((Object)((Component)weapon).gameObject).name + " received clips (fireClip=" + Name(weapon.fireClip) + ")"));
}
}
internal static void OnShoot(Weapon weapon)
{
if ((Object)(object)weapon == (Object)null)
{
return;
}
string text = StripClone(((Object)((Component)weapon).gameObject).name);
if (_seenShoot.Add(text))
{
_log.LogInfo((object)("[Sounds] first OnShoot: " + ((Object)((Component)weapon).gameObject).name + " " + $"(prefab {text}), rotation: {_fire.ContainsKey(text)}"));
}
if (_fire.TryGetValue(text, out var value))
{
AudioClip val = value[_rng.Next(value.Count)];
if (weapon.fireClip != val)
{
weapon.fireClip = val;
}
}
}
internal static void OnMeleeFire(MeleeWeapon melee)
{
if ((Object)(object)melee == (Object)null)
{
return;
}
string text = StripClone(((Object)((Component)melee).gameObject).name);
if (_seenMelee.Add(text))
{
_log.LogInfo((object)("[Sounds] first MeleeWeapon.Fire: " + ((Object)((Component)melee).gameObject).name + " " + $"(prefab {text}), rotation: {_melee.ContainsKey(text)}"));
DumpMeleeClips(melee);
}
if (_melee.TryGetValue(text, out var value))
{
AudioClip val = value[_rng.Next(value.Count)];
if (((Weapon)melee).fireClip != val)
{
((Weapon)melee).fireClip = val;
}
SetPrivateClip(melee, "secondAttackClip", value[_rng.Next(value.Count)]);
}
}
private static void DumpMeleeClips(MeleeWeapon melee)
{
string[] obj = new string[3] { "secondAttackClip", "firstAttackStartClip", "secondAttackStartClip" };
_log.LogInfo((object)("[Sounds] fireClip = " + Name(((Weapon)melee).fireClip)));
string[] array = obj;
foreach (string text in array)
{
FieldInfo fieldInfo = AccessTools.Field(typeof(MeleeWeapon), text);
if (fieldInfo == null)
{
_log.LogInfo((object)("[Sounds] " + text + " = <no such field>"));
continue;
}
try
{
? val = _log;
object? value = fieldInfo.GetValue(melee);
((ManualLogSource)val).LogInfo((object)("[Sounds] " + text + " = " + Name((AudioClip)((value is AudioClip) ? value : null))));
}
catch
{
_log.LogInfo((object)("[Sounds] " + text + " = <unreadable>"));
}
}
}
private static void SetPrivateClip(MeleeWeapon melee, string field, AudioClip clip)
{
FieldInfo fieldInfo = AccessTools.Field(typeof(MeleeWeapon), field);
if (fieldInfo == null)
{
_log.LogError((object)("[Sounds] MeleeWeapon." + field + " field is missing"));
return;
}
try
{
fieldInfo.SetValue(melee, clip);
}
catch (Exception ex)
{
_log.LogError((object)("[Sounds] " + field + ": " + ex.Message));
}
}
private static string StripClone(string name)
{
if (!name.EndsWith("(Clone)", StringComparison.Ordinal))
{
return name;
}
return name.Substring(0, name.Length - "(Clone)".Length);
}
private static string Name(AudioClip c)
{
if (!((Object)(object)c == (Object)null))
{
return ((Object)c).name;
}
return "<none>";
}
}
[HarmonyPatch(typeof(Weapon))]
internal static class WeaponSoundsShootPatch
{
[HarmonyPatch("OnShoot")]
[HarmonyPrefix]
private static void Prefix(Weapon __instance)
{
try
{
WeaponSounds.OnShoot(__instance);
}
catch (Exception ex)
{
StraftatCMRPlugin.Log.LogError((object)("[Sounds] OnShoot failed: " + ex.GetType().Name + ": " + ex.Message));
}
}
}
[HarmonyPatch(typeof(Weapon))]
internal static class WeaponSoundsAwakePatch
{
[HarmonyPatch("Awake")]
[HarmonyPostfix]
private static void Postfix(Weapon __instance)
{
try
{
WeaponSounds.OnWeaponAwake(__instance);
}
catch (Exception ex)
{
StraftatCMRPlugin.Log.LogError((object)("[Sounds] Weapon.Awake failed: " + ex.GetType().Name + ": " + ex.Message));
}
}
}
[HarmonyPatch(typeof(MeleeWeapon))]
internal static class MeleeSoundsAwakePatch
{
[HarmonyPatch("Awake")]
[HarmonyPostfix]
private static void Postfix(MeleeWeapon __instance)
{
try
{
WeaponSounds.OnWeaponAwake((Weapon)(object)__instance);
}
catch (Exception ex)
{
StraftatCMRPlugin.Log.LogError((object)("[Sounds] MeleeWeapon.Awake failed: " + ex.GetType().Name + ": " + ex.Message));
}
}
}
[HarmonyPatch(typeof(MeleeWeapon))]
internal static class WeaponSoundsMeleePatch
{
[HarmonyPatch("Fire")]
[HarmonyPrefix]
private static void Prefix(MeleeWeapon __instance)
{
try
{
WeaponSounds.OnMeleeFire(__instance);
}
catch (Exception ex)
{
StraftatCMRPlugin.Log.LogError((object)("[Sounds] MeleeWeapon.Fire failed: " + ex.GetType().Name + ": " + ex.Message));
}
}
}
}