Decompiled source of MoreCards v1.1.1
BepInEx/plugins/SlipSkid.MoreCards/SlipSkid.MoreCards.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; 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 System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Core.Logging.Interpolation; using BepInEx.Logging; using BepInEx.Unity.Mono; using Effects; using Game; using Game.GameMode; using Game.UnlockableContent; using HarmonyLib; using Menus; using Menus.UI; using Microsoft.CodeAnalysis; using Obstacles; using Obstacles.ObstacleAffixes; using Obstacles.ObstacleGameModifiers; using Player; using SlipSkid.MoreCards.Cards; using SlipSkid.MoreCards.Runtime; using SlipSkid.MoreCards.Util; using UI; using UI.NotificationSystem; using Unity.Netcode; using UnityEngine; using UnityEngine.Events; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using Utils; using Utils.Colors; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("SlipSkid.MoreCards")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.1.0")] [assembly: AssemblyInformationalVersion("1.1.1+630028d86b9d3da140b17829af15fd81ebd52620")] [assembly: AssemblyProduct("SlipSkid.MoreCards")] [assembly: AssemblyTitle("SlipSkid.MoreCards")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace SlipSkid.MoreCards { internal static class GameLogForwarder { private static ManualLogSource _gameLog; private static Harmony _harmony; private static bool _installed; [ThreadStatic] private static bool _reentrant; public static void Install() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown if (!_installed) { _installed = true; _gameLog = Logger.CreateLogSource("Game"); _harmony = new Harmony("slipskid.morecards.logforwarder"); Type? typeFromHandle = typeof(GameLogForwarder); HarmonyMethod postfix = new HarmonyMethod(typeFromHandle.GetMethod("OnInfo", BindingFlags.Static | BindingFlags.NonPublic)); HarmonyMethod postfix2 = new HarmonyMethod(typeFromHandle.GetMethod("OnWarning", BindingFlags.Static | BindingFlags.NonPublic)); HarmonyMethod postfix3 = new HarmonyMethod(typeFromHandle.GetMethod("OnError", BindingFlags.Static | BindingFlags.NonPublic)); HarmonyMethod postfix4 = new HarmonyMethod(typeFromHandle.GetMethod("OnException", BindingFlags.Static | BindingFlags.NonPublic)); TryPatch("Log", new Type[1] { typeof(object) }, postfix); TryPatch("Log", new Type[2] { typeof(object), typeof(Object) }, postfix); TryPatch("LogWarning", new Type[1] { typeof(object) }, postfix2); TryPatch("LogWarning", new Type[2] { typeof(object), typeof(Object) }, postfix2); TryPatch("LogError", new Type[1] { typeof(object) }, postfix3); TryPatch("LogError", new Type[2] { typeof(object), typeof(Object) }, postfix3); TryPatch("LogException", new Type[1] { typeof(Exception) }, postfix4); Plugin.Log.LogInfo((object)"Game log forwarding installed."); } } public static void Uninstall() { if (_installed) { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } _installed = false; } } private static void TryPatch(string name, Type[] args, HarmonyMethod postfix) { //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Expected O, but got Unknown //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.Method(typeof(Debug), name, args, (Type[])null); bool flag = default(bool); if (methodInfo == null) { ManualLogSource log = Plugin.Log; BepInExDebugLogInterpolatedStringHandler val = new BepInExDebugLogInterpolatedStringHandler(31, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Debug."); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(name); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("("); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(string.Join(", ", Array.ConvertAll(args, (Type a) => a.Name))); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(") not present; skipping."); } log.LogDebug(val); return; } try { _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, postfix, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(26, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Could not forward Debug."); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(name); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message); } log2.LogWarning(val2); } } private static void Write(LogLevel level, object message) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (_reentrant) { return; } _reentrant = true; try { _gameLog.Log(level, message ?? "null"); } catch { } finally { _reentrant = false; } } private static void OnInfo(object message) { Write((LogLevel)16, message); } private static void OnWarning(object message) { Write((LogLevel)4, message); } private static void OnError(object message) { Write((LogLevel)2, message); } private static void OnException(Exception exception) { Write((LogLevel)2, exception); } } internal static class ModConfig { public static ConfigEntry<bool> Enabled; public static ConfigEntry<bool> ForwardGameLogs; public static ConfigEntry<bool> Verbose; public static ConfigEntry<bool> ShowStateChanges; public static ConfigEntry<Key> DebugKey; public static ConfigEntry<Key> ReloadConfigKey; public static ConfigEntry<bool> OverrideCarStats; public static ConfigEntry<float> MaxSpeedMultiplier; public static ConfigEntry<int> ExtraJumps; public static ConfigEntry<bool> EnableCustomCards; public static ConfigEntry<float> SpeedCardBoostPercent; public static ConfigEntry<float> SizeCardGrowthPercent; public static ConfigEntry<float> CarCardScale; public static ConfigEntry<float> CarCardLength; public static ConfigEntry<float> TreeScale; public static ConfigEntry<float> TreeHeight; public static ConfigEntry<float> SwatterScale; public static ConfigEntry<float> SwatterWidth; public static ConfigEntry<float> SwatterReach; public static ConfigEntry<string> PropCarrierName; public static ConfigEntry<bool> ForceCustomCardInDraft; public static ConfigEntry<float> GunFireInterval; public static ConfigEntry<bool> GunFiresOnlyWhileRacing; public static ConfigEntry<float> GunLength; public static ConfigEntry<float> GunOffsetRight; public static ConfigEntry<float> GunOffsetUp; public static ConfigEntry<float> GunOffsetForward; public static ConfigEntry<float> GunYaw; public static ConfigEntry<float> GunPitch; public static ConfigEntry<float> GunRoll; public static ConfigEntry<float> BulletLength; public static ConfigEntry<float> BulletSpeed; public static ConfigEntry<float> BulletLifetime; public static ConfigEntry<bool> BulletsKill; public static ConfigEntry<float> BulletSlowPercent; public static ConfigEntry<float> BulletSlowSeconds; public static ConfigEntry<float> BulletHopSpeed; public static ConfigEntry<float> BulletHitPadding; public static ConfigEntry<float> BoinkSlowPercent; public static ConfigEntry<float> BoinkSlowSeconds; public static ConfigEntry<float> BoinkBounceDepth; public static ConfigEntry<float> BoinkBounceSeconds; public static ConfigEntry<float> BoinkMinHeight; public static ConfigEntry<float> BoinkCooldownSeconds; public static ConfigEntry<float> RingSize; public static ConfigEntry<float> RingHeight; public static ConfigEntry<float> RingYaw; public static ConfigEntry<float> BouncePush; public static ConfigEntry<float> BounceLift; public static ConfigEntry<float> BounceRecoil; public static ConfigEntry<float> BounceCooldownSeconds; public static ConfigEntry<bool> BounceOnlyWhileRacing; public static ConfigEntry<float> RingWobbleSeconds; public static ConfigEntry<float> RingWobbleDepth; public static ConfigEntry<float> GravityReductionPercent; public static ConfigEntry<float> SwoopSeconds; public static ConfigEntry<float> LiftSeconds; public static ConfigEntry<float> RestSeconds; public static ConfigEntry<float> SwatPush; public static ConfigEntry<float> SwatLift; public static ConfigEntry<float> SwatCooldownSeconds; public static ConfigEntry<bool> SwatOnlyWhileRacing; public static ConfigEntry<float> BollardScale; public static ConfigEntry<float> BollardHeight; public static ConfigEntry<float> DownSeconds; public static ConfigEntry<float> UpSeconds; public static ConfigEntry<float> RiseSeconds; public static void Bind(ConfigFile cfg) { //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Expected O, but got Unknown //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Expected O, but got Unknown //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Expected O, but got Unknown //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Expected O, but got Unknown //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Expected O, but got Unknown //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Expected O, but got Unknown //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Expected O, but got Unknown //IL_0280: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Expected O, but got Unknown //IL_02b8: Unknown result type (might be due to invalid IL or missing references) //IL_02c2: Expected O, but got Unknown //IL_02f0: Unknown result type (might be due to invalid IL or missing references) //IL_02fa: Expected O, but got Unknown //IL_0328: Unknown result type (might be due to invalid IL or missing references) //IL_0332: Expected O, but got Unknown //IL_03a0: Unknown result type (might be due to invalid IL or missing references) //IL_03aa: Expected O, but got Unknown //IL_03f3: Unknown result type (might be due to invalid IL or missing references) //IL_03fd: Expected O, but got Unknown //IL_042b: Unknown result type (might be due to invalid IL or missing references) //IL_0435: Expected O, but got Unknown //IL_0463: Unknown result type (might be due to invalid IL or missing references) //IL_046d: Expected O, but got Unknown //IL_049b: Unknown result type (might be due to invalid IL or missing references) //IL_04a5: Expected O, but got Unknown //IL_04d3: Unknown result type (might be due to invalid IL or missing references) //IL_04dd: Expected O, but got Unknown //IL_050b: Unknown result type (might be due to invalid IL or missing references) //IL_0515: Expected O, but got Unknown //IL_0543: Unknown result type (might be due to invalid IL or missing references) //IL_054d: Expected O, but got Unknown //IL_057b: Unknown result type (might be due to invalid IL or missing references) //IL_0585: Expected O, but got Unknown //IL_05b3: Unknown result type (might be due to invalid IL or missing references) //IL_05bd: Expected O, but got Unknown //IL_05eb: Unknown result type (might be due to invalid IL or missing references) //IL_05f5: Expected O, but got Unknown //IL_063e: Unknown result type (might be due to invalid IL or missing references) //IL_0648: Expected O, but got Unknown //IL_0676: Unknown result type (might be due to invalid IL or missing references) //IL_0680: Expected O, but got Unknown //IL_06ae: Unknown result type (might be due to invalid IL or missing references) //IL_06b8: Expected O, but got Unknown //IL_06e6: Unknown result type (might be due to invalid IL or missing references) //IL_06f0: Expected O, but got Unknown //IL_071e: Unknown result type (might be due to invalid IL or missing references) //IL_0728: Expected O, but got Unknown //IL_0756: Unknown result type (might be due to invalid IL or missing references) //IL_0760: Expected O, but got Unknown //IL_078e: Unknown result type (might be due to invalid IL or missing references) //IL_0798: Expected O, but got Unknown //IL_07c6: Unknown result type (might be due to invalid IL or missing references) //IL_07d0: Expected O, but got Unknown //IL_07fe: Unknown result type (might be due to invalid IL or missing references) //IL_0808: Expected O, but got Unknown //IL_0836: Unknown result type (might be due to invalid IL or missing references) //IL_0840: Expected O, but got Unknown //IL_086e: Unknown result type (might be due to invalid IL or missing references) //IL_0878: Expected O, but got Unknown //IL_08a6: Unknown result type (might be due to invalid IL or missing references) //IL_08b0: Expected O, but got Unknown //IL_08de: Unknown result type (might be due to invalid IL or missing references) //IL_08e8: Expected O, but got Unknown //IL_0916: Unknown result type (might be due to invalid IL or missing references) //IL_0920: Expected O, but got Unknown //IL_094e: Unknown result type (might be due to invalid IL or missing references) //IL_0958: Expected O, but got Unknown //IL_0986: Unknown result type (might be due to invalid IL or missing references) //IL_0990: Expected O, but got Unknown //IL_09be: Unknown result type (might be due to invalid IL or missing references) //IL_09c8: Expected O, but got Unknown //IL_0a11: Unknown result type (might be due to invalid IL or missing references) //IL_0a1b: Expected O, but got Unknown //IL_0a49: Unknown result type (might be due to invalid IL or missing references) //IL_0a53: Expected O, but got Unknown //IL_0a81: Unknown result type (might be due to invalid IL or missing references) //IL_0a8b: Expected O, but got Unknown //IL_0ab9: Unknown result type (might be due to invalid IL or missing references) //IL_0ac3: Expected O, but got Unknown //IL_0af1: Unknown result type (might be due to invalid IL or missing references) //IL_0afb: Expected O, but got Unknown //IL_0b29: Unknown result type (might be due to invalid IL or missing references) //IL_0b33: Expected O, but got Unknown //IL_0b61: Unknown result type (might be due to invalid IL or missing references) //IL_0b6b: Expected O, but got Unknown //IL_0b99: Unknown result type (might be due to invalid IL or missing references) //IL_0ba3: Expected O, but got Unknown //IL_0bd1: Unknown result type (might be due to invalid IL or missing references) //IL_0bdb: Expected O, but got Unknown //IL_0c24: Unknown result type (might be due to invalid IL or missing references) //IL_0c2e: Expected O, but got Unknown //IL_0c5c: Unknown result type (might be due to invalid IL or missing references) //IL_0c66: Expected O, but got Unknown //IL_0c94: Unknown result type (might be due to invalid IL or missing references) //IL_0c9e: Expected O, but got Unknown //IL_0ccc: Unknown result type (might be due to invalid IL or missing references) //IL_0cd6: Expected O, but got Unknown //IL_0d04: Unknown result type (might be due to invalid IL or missing references) //IL_0d0e: Expected O, but got Unknown Enabled = cfg.Bind<bool>("General", "Enabled", true, "Turn the whole mod off without uninstalling it. Takes effect on restart."); ForwardGameLogs = cfg.Bind<bool>("Debug", "ForwardGameLogs", false, "Mirror the game's own Debug.Log output into the BepInEx log. This build is stripped, so BepInEx cannot capture it by itself. Leave off unless you are debugging — it is chatty. Turn it off if you also run the Custom Map Loader, which already forwards logs."); Verbose = cfg.Bind<bool>("Debug", "Verbose", false, "Log every hook as it fires. Useful when a patch silently is not running."); ShowStateChanges = cfg.Bind<bool>("Debug", "ShowStateChanges", false, "Log each race phase transition (Racing, ScoreBoard, SelectingObstacles, and so on)."); DebugKey = cfg.Bind<Key>("Debug", "DebugKey", (Key)101, "Prints a snapshot of game state to the log. Safe to press at any time."); ReloadConfigKey = cfg.Bind<Key>("Debug", "ReloadConfigKey", (Key)102, "Re-reads this file from disk, so you can tune values — CarCardScale in particular — with the game still running. Settings marked as taking effect on restart still do; the rest apply the next time they are read, which for the \"Wreck\" card means the next one placed."); OverrideCarStats = cfg.Bind<bool>("Car", "OverrideCarStats", false, "Re-applies the multipliers below on top of the host's rules every time the car reads them. Single player and private lobbies only: your car will not match what other players see, because each client simulates its own physics."); MaxSpeedMultiplier = cfg.Bind<float>("Car", "MaxSpeedMultiplier", 1f, new ConfigDescription("Multiplier applied to top speed when OverrideCarStats is on.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 4f), Array.Empty<object>())); ExtraJumps = cfg.Bind<int>("Car", "ExtraJumps", 0, new ConfigDescription("Extra mid-air jumps granted when OverrideCarStats is on.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10), Array.Empty<object>())); EnableCustomCards = cfg.Bind<bool>("Cards", "EnableCustomCards", true, "Adds this mod's cards to the post-round draft. Because cards cross the network as indices into a shared catalog, EVERY player must run this mod with the same cards, or the draft desyncs and players draw different cards from the same roll. Takes effect on restart."); SpeedCardBoostPercent = cfg.Bind<float>("Cards", "SpeedCardBoostPercent", 10f, new ConfigDescription("How much top speed the \"Overdrive\" card grants for the round, as a percent. The effect is applied the same way the game's own modifier cards apply theirs, so it replicates correctly as long as every peer has the mod.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>())); SizeCardGrowthPercent = cfg.Bind<float>("Cards", "SizeCardGrowthPercent", 10f, new ConfigDescription("How much bigger the \"Supersize\" card makes every car, as a percent. Like the game's own upgrade cards this stacks and lasts for the rest of the match, so the value compounds: at 10%, four picks is roughly a 1.5x car. Large values get silly fast — 100 means each pick doubles the car, which is wider than a lane after two.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 300f), Array.Empty<object>())); CarCardScale = cfg.Bind<float>("Cards", "CarCardScale", 1f, new ConfigDescription("Size of the car placed by the \"Wreck\" card, measured against a real player car. 1 makes the wreck exactly as long as the car you drive; 0.5 makes it half that; 2 makes it double. The player car is measured live, so this stays right whatever the map's scale is. Press ReloadConfigKey after editing to apply it without restarting the game. Every player must use the same value or the wreck will be a different size on each screen.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 5f), Array.Empty<object>())); CarCardLength = cfg.Bind<float>("Cards", "CarCardLength", 0f, new ConfigDescription("Exact length of the \"Wreck\" car in world units, ignoring CarCardScale and the player car entirely. Leave at 0 to size against the player car, which is what you want on an unfamiliar map. Set a number here when you want one specific size and do not care how big the cars are: the log prints \"wreck sized\" with the measured player-car length every time one is placed, so start from that number. Every player must use the same value.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>())); TreeScale = cfg.Bind<float>("Cards", "TreeScale", 2.5f, new ConfigDescription("How tall the \"Timber!\" tree stands, measured in player-car lengths. At the default 2.5 it is two and a half cars tall, which reads as a tree without walling off the track — its canopy is about as wide as it is tall, so raising this widens the obstacle as fast as it heightens it. The player car is measured live, so this stays right whatever the map's scale is. Press ReloadConfigKey after editing to apply it to the next tree placed. Every player must use the same value or the tree will be a different size, and block different things, on each screen.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 15f), Array.Empty<object>())); TreeHeight = cfg.Bind<float>("Cards", "TreeHeight", 0f, new ConfigDescription("Exact height of the \"Timber!\" tree in world units, ignoring TreeScale and the player car entirely. Leave at 0 to size against the player car, which is what you want on an unfamiliar map. The log prints \"tree sized\" with the measured player-car length every time one is placed, so start from that number. Every player must use the same value.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 300f), Array.Empty<object>())); SwatterScale = cfg.Bind<float>("Swatter", "SwatterScale", 0.3f, new ConfigDescription("How big the \"SWAT!\" flyswatter's pad is across, measured in player-car lengths. The pad sweeps across the track standing on its edge, so this is also how tall a wall it presents to a car: at the default 0.3 it is about a third of a car wide and the same again high. The handle is scaled with it, and the handle is most of the model, so the whole swatter comes out about three times this long — use SwatterReach if you want a shorter sweep without a smaller pad. The player car is measured live, so this stays right whatever the map's scale is. Press ReloadConfigKey after editing and the swatter already on the track resizes itself; you do not have to draft another one. Every player must use the same value or the swatter will be a different size, and swat different things, on each screen.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 10f), Array.Empty<object>())); SwatterWidth = cfg.Bind<float>("Swatter", "SwatterWidth", 0f, new ConfigDescription("Exact size of the \"SWAT!\" pad across, in world units, ignoring SwatterScale and the player car entirely. Leave at 0 to size against the player car, which is what you want on an unfamiliar map. The log prints \"swatter sized\" with the measured player-car length every time one is placed, so start from that number. Every player must use the same value.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 200f), Array.Empty<object>())); SwatterReach = cfg.Bind<float>("Swatter", "SwatterReach", 1f, new ConfigDescription("How far out the \"SWAT!\" pad sweeps, as a fraction of the swatter's own length. This is the knob for \"the arc is too wide\" — it shortens the reach without shrinking the pad, where SwatterScale changes both together. At 1 the swatter is pinned at the very end of its handle and sweeps its whole length; at 0.5 it is held halfway down the handle, so the arc is half as wide and the tail of the handle pokes out past the post, the way it would in somebody's hand. The pad itself is not stretched or shrunk by this — only how far from the post it travels, which is also how hard the tip of a swat lands. Press ReloadConfigKey and the swatter on the track re-hangs itself. Every player must use the same value or the pad will sweep a different part of the road on each screen.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 1f), Array.Empty<object>())); PropCarrierName = cfg.Bind<string>("Cards", "PropCarrierName", "", "Which vanilla obstacle the \"Wreck\" card borrows to exist as a real, placeable object. Leave empty to let the mod pick the most inert prop it can find. The names it found are written to the log every launch, as \"prop carrier candidates\" — set one of those here if the automatic choice behaves oddly, since the borrowed prop's collision shape is inherited and cannot be changed. EVERY player must use the same value, or the same card will be a different object on each screen."); ForceCustomCardInDraft = cfg.Bind<bool>("Cards", "ForceCustomCardInDraft", false, "Testing aid: force this mod's cards into every obstacle draft so you do not have to wait for the weighted roll to offer it. Leave off for normal rarity-weighted behaviour. Host only — the roll happens server-side."); BindCardRarities(cfg); GunFireInterval = cfg.Bind<float>("Gun", "GunFireInterval", 6f, new ConfigDescription("Seconds between shots from the \"GUN!\" card. Shown on the card itself, so the draft screen always matches what you set here.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 60f), Array.Empty<object>())); GunFiresOnlyWhileRacing = cfg.Bind<bool>("Gun", "GunFiresOnlyWhileRacing", true, "Hold fire outside the race itself. The card is drafted between rounds, so with this off the gun starts shooting over the scoreboard and while the next round's obstacles are being placed."); GunLength = cfg.Bind<float>("Gun", "GunLength", 0.1f, new ConfigDescription("Length of the gun as a fraction of the car it is mounted on. 0.1 is a tenth of the car, which reads as a sidearm bolted to the bodywork; much above that and it stops looking mounted and starts looking parked alongside. Because it is a fraction it stays right on any map, and it grows with a car the Supersize card has enlarged.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.02f, 3f), Array.Empty<object>())); GunOffsetRight = cfg.Bind<float>("Gun", "GunOffsetRight", 0.1f, new ConfigDescription("How far to the right of the car's centre the gun sits, in fractions of car length. 0.1 puts it on the flank of a stock car; 0 buries it in the middle of the bodywork and negative values move it across to the driver's side. This is measured out from the centre of the car rather than in from its edge, so raising it moves the gun by a predictable amount.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-2f, 2f), Array.Empty<object>())); GunOffsetUp = cfg.Bind<float>("Gun", "GunOffsetUp", 0.05f, new ConfigDescription("How far above the car's mid-height the gun sits, in fractions of car length.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-2f, 2f), Array.Empty<object>())); GunOffsetForward = cfg.Bind<float>("Gun", "GunOffsetForward", 0f, new ConfigDescription("How far toward the nose of the car the gun sits, in fractions of car length. 0 is level with the middle of the car.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-2f, 2f), Array.Empty<object>())); GunYaw = cfg.Bind<float>("Gun", "GunYaw", 0f, new ConfigDescription("Turn the gun, in degrees, from straight ahead. Bullets follow the barrel, so this aims the shots too: 90 fires out the passenger side and 180 fires backwards. If a gun you swap in comes out pointing the wrong way down the car, this is the fix — the barrel's axis is worked out from the model's shape, and a bounding box cannot tell which end of that axis is the muzzle.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-180f, 180f), Array.Empty<object>())); GunPitch = cfg.Bind<float>("Gun", "GunPitch", 0f, new ConfigDescription("Tilt the gun's nose down (positive) or up (negative), in degrees.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-180f, 180f), Array.Empty<object>())); GunRoll = cfg.Bind<float>("Gun", "GunRoll", 180f, new ConfigDescription("Roll the gun about its own barrel, in degrees — which turns it over without changing where it points. It defaults to 180 because the bundled pistol is authored grip-up: the model's vertical axis is worked out from its shape, and that cannot tell a grip from a sight, so it is a coin toss which way up any given model lands.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-180f, 180f), Array.Empty<object>())); BulletLength = cfg.Bind<float>("Gun", "BulletLength", 0.15f, new ConfigDescription("Length of each bullet as a fraction of the firing car.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.005f, 2f), Array.Empty<object>())); BulletSpeed = cfg.Bind<float>("Gun", "BulletSpeed", 8f, new ConfigDescription("How fast bullets travel, in car lengths per second. Expressed against the car rather than in world units so a shot looks equally quick whatever scale the map is built at.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 200f), Array.Empty<object>())); BulletLifetime = cfg.Bind<float>("Gun", "BulletLifetime", 2f, new ConfigDescription("Seconds before a bullet disappears. Bullets pass through walls and props — they only test against cars — so this is what stops a miss flying off forever.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 60f), Array.Empty<object>())); BulletsKill = cfg.Bind<bool>("Gun", "BulletsKill", false, "Kill a player whose car a bullet touches, exactly as the game's own kill zones do: it costs a life, plays the usual explosion, and respawns them. You are immune to your own gun. It is off by default, which leaves bullets as harmless scenery. Every player should use the same value — each machine decides only for its own car, so with this off you would survive shots that everyone else sees kill you."); BulletSlowPercent = cfg.Bind<float>("Gun", "BulletSlowPercent", 50f, new ConfigDescription("How much speed a bullet knocks off the car it hits while BulletsKill is off, as a percentage of the speed they were carrying. This and the hop below are what a harmless gun does instead of killing: a shot has to be worth taking without costing a life. 100 stops them where they stand; 0 leaves only the hop. Ignored while BulletsKill is on — there is nothing to slow down.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>())); BulletSlowSeconds = cfg.Bind<float>("Gun", "BulletSlowSeconds", 0.1f, new ConfigDescription("How long that slow is held, in seconds. Only their speed along the ground is capped, so the hop still rises and falls normally underneath it. The default is a stumble rather than a handbrake — long enough to cost a car length, short enough that being shot is not the end of your lap.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>())); BulletHopSpeed = cfg.Bind<float>("Gun", "BulletHopSpeed", 1.5f, new ConfigDescription("How hard a bullet pops the car it hits off the road while BulletsKill is off, as the upward speed it leaves with in car lengths per second. Expressed against the car rather than in world units so the hop reads the same whatever scale the map is built at. It is set rather than added, so a hit is the same little jump whatever the car was already doing. 0 leaves only the slow.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f), Array.Empty<object>())); BulletHitPadding = cfg.Bind<float>("Gun", "BulletHitPadding", 0.05f, new ConfigDescription("Extra hit radius around a car, in fractions of car length, on top of the car's own physics radius and the bullet's length. A little padding makes hits feel fair, because a bullet's position is only as fresh as the last frame. 0 is exact; large values make shots near-impossible to dodge.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); BoinkSlowPercent = cfg.Bind<float>("Boink", "BoinkSlowPercent", 50f, new ConfigDescription("How much speed a car loses when someone lands on it, as a percent of the speed it was doing at that moment. 50 halves it. It is capped there for BoinkSlowSeconds rather than just knocked down once, so the victim cannot floor it straight back up to speed.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>())); BoinkSlowSeconds = cfg.Bind<float>("Boink", "BoinkSlowSeconds", 0.5f, new ConfigDescription("How long the slow lasts, in seconds.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>())); BoinkBounceDepth = cfg.Bind<float>("Boink", "BoinkBounceDepth", 0.35f, new ConfigDescription("How far the squashed car dips, as a fraction of its own height. The bounce overshoots on the way back up and settles, so this is the depth of the first dip rather than the whole travel. 0 turns the animation off and leaves only the slow.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>())); BoinkBounceSeconds = cfg.Bind<float>("Boink", "BoinkBounceSeconds", 0.35f, new ConfigDescription("How long the bounce animation runs, in seconds. Independent of the slow — a short springy bounce under a longer slow reads well.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 5f), Array.Empty<object>())); BoinkMinHeight = cfg.Bind<float>("Boink", "BoinkMinHeight", 0.6f, new ConfigDescription("How far above a car you must be for the hit to count as landing on it, measured in the victim's own car radii. This is what separates a jump from a side-swipe: at 0 any contact from fractionally above counts, and high values demand you come down squarely on their roof.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), Array.Empty<object>())); BoinkCooldownSeconds = cfg.Bind<float>("Boink", "BoinkCooldownSeconds", 0.4f, new ConfigDescription("Shortest gap between two boinks on the same car. Two cars settling against each other generate a stream of contacts, and without this one landing would read as a burst of them and pin the car to the floor.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>())); RingSize = cfg.Bind<float>("Bouncy", "RingSize", 0.5f, new ConfigDescription("How big the swim ring is, as a multiple of the car wearing it. At the default 0.5 it rides on the car like a float; around 1.5 the hole is wide enough for the car to sit down inside it. Because it is a fraction it stays right on any map, and it grows with a car the Supersize card has enlarged. It also sets how far out the ring bumps people: below roughly 1.2 that reach is inside the car's own body, so bumps land on contact rather than early. Every player should use the same value.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 4f), Array.Empty<object>())); RingHeight = cfg.Bind<float>("Bouncy", "RingHeight", 0f, new ConfigDescription("How far above the middle of the car the ring floats, in fractions of car length. 0 puts it around the car's waist; negative values drop it towards the wheels.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-2f, 2f), Array.Empty<object>())); RingYaw = cfg.Bind<float>("Bouncy", "RingYaw", 0f, new ConfigDescription("Turn the ring, in degrees. The duck's head faces the way the car does at 0; use 180 if you would rather it looked back at whoever you just bounced.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-180f, 180f), Array.Empty<object>())); BouncePush = cfg.Bind<float>("Bouncy", "BouncePush", 1f, new ConfigDescription("How hard the ring throws the player it touches, in car lengths per second. Their momentum into the ring is cancelled first, so this is the speed they actually leave with rather than something added to what they had. A normal car-on-car bump is worth well under one car length per second, so the default already reads as a shove; 6 or so launches people off the track. Expressed against the car rather than in world units so it feels the same whatever scale the map is built at.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f), Array.Empty<object>())); BounceLift = cfg.Bind<float>("Bouncy", "BounceLift", 0.2f, new ConfigDescription("How much of the push goes upwards, as a fraction of it. A little lift makes the bump read as bouncy rather than as a shunt; 0 keeps it flat along the ground.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>())); BounceRecoil = cfg.Bind<float>("Bouncy", "BounceRecoil", 0.2f, new ConfigDescription("How hard the ring's own wearer is pushed back, as a fraction of BouncePush. This is what makes a bump a bounce rather than a battering ram: at 0 you plough through everyone unmoved. Unlike the player you hit, your recoil is added to the speed you already had rather than replacing it, so a bump knocks you off line without stopping you dead.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>())); BounceCooldownSeconds = cfg.Bind<float>("Bouncy", "BounceCooldownSeconds", 0.4f, new ConfigDescription("Shortest gap between two bounces of the same player. The ring keeps touching them for as long as they are inside it, so without this one bump would be a stream of them and the pair would never separate.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>())); BounceOnlyWhileRacing = cfg.Bind<bool>("Bouncy", "BounceOnlyWhileRacing", true, "Keep the ring limp outside the race itself. Cars sit close together on a start grid and between rounds, and a ring that bumps whatever is near it would spend that time shoving the neighbours around. The ring is still worn and still visible — it just does nothing until the race starts. Every player should use the same value."); RingWobbleSeconds = cfg.Bind<float>("Bouncy", "RingWobbleSeconds", 0.45f, new ConfigDescription("How long the ring wobbles after a bump, in seconds.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 5f), Array.Empty<object>())); RingWobbleDepth = cfg.Bind<float>("Bouncy", "RingWobbleDepth", 0.25f, new ConfigDescription("How far the ring squashes as it wobbles, as a fraction of its own size. It flattens and spreads, then springs back through and settles, so this is the depth of the first squash rather than the whole travel. 0 turns the animation off and leaves only the push.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.9f), Array.Empty<object>())); GravityReductionPercent = cfg.Bind<float>("Floaty", "GravityReductionPercent", 30f, new ConfigDescription("How much gravity the \"Floaty\" card takes off the drafting player's car, as a percent. It weakens the fall without touching the jump, so the height you gain runs ahead of the number: taking 30% off gravity is about 43% more air, and the hang time grows with it. Like the game's own upgrade cards it lasts the rest of the match and stacks, and it compounds rather than adding — a second pick takes 30% off what is left, so three picks is two thirds of gravity gone and no number of picks ever reaches weightlessness. Every player must use the same value — each client simulates its own car, so a mismatch means your jumps land somewhere different on your screen than on theirs.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 90f), Array.Empty<object>())); SwoopSeconds = cfg.Bind<float>("Swatter", "SwoopSeconds", 0.35f, new ConfigDescription("How long the swat itself takes, in seconds — the 90 degrees the pad sweeps across the track, from parked out to one side to pointing straight ahead. The pad accelerates the whole way round, so halving this doubles the speed of the sweep and halves the time anybody has to get clear of the arc; how hard a landed swat throws you is SwatPush, not this. Short and mean is the point — much above a second and it stops reading as a swat and starts reading as a barrier arm.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 5f), Array.Empty<object>())); LiftSeconds = cfg.Bind<float>("Swatter", "LiftSeconds", 1.2f, new ConfigDescription("How long the swatter takes to draw itself back to where it started afterwards, in seconds. Deliberately slower than the swat: the return is the window players use to get past, and it does not hit anybody — the pad only pushes while it is moving towards you.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 10f), Array.Empty<object>())); RestSeconds = cfg.Bind<float>("Swatter", "RestSeconds", 1.5f, new ConfigDescription("How long the swatter waits, parked out to one side, between one swat and the next. This plus SwoopSeconds plus LiftSeconds is the full cycle, which at the defaults is a swat roughly every three seconds.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f), Array.Empty<object>())); SwatPush = cfg.Bind<float>("Swatter", "SwatPush", 9f, new ConfigDescription("How hard a clean swat throws a car, in car lengths per second. This is the figure for the middle of the pad catching a car square at the end of the sweep; catching the tip is worth more, and being clipped early in the sweep or brushed by an edge on the way past is worth much less, because the push follows how fast the pad is actually closing on you and in which direction. A normal car-on-car bump is worth well under one car length per second, so the default of 9 is a proper launch. Expressed against the car rather than in world units so it feels the same whatever scale the map is built at.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f), Array.Empty<object>())); SwatLift = cfg.Bind<float>("Swatter", "SwatLift", 0.25f, new ConfigDescription("How much of the push goes upwards, as a fraction of it. The sweep is level with the track, so the push is too, and a little lift is what makes a swat send cars tumbling rather than sliding them sideways along the road. 0 keeps it flat.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>())); SwatCooldownSeconds = cfg.Bind<float>("Swatter", "SwatCooldownSeconds", 0.5f, new ConfigDescription("Shortest gap between two swats landing on the same car. The pad keeps touching a car for as long as it is sweeping past it, so without this one swing would read as a burst of hits and pin the car under the pad.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>())); SwatOnlyWhileRacing = cfg.Bind<bool>("Swatter", "SwatOnlyWhileRacing", false, "Hold the launch until the race itself starts. Off by default, so a swat throws cars around whenever the swatter is swinging. With it on, the pad still swings and is still solid to drive into between rounds — it simply stops flinging anyone until the race is running. Every player should use the same value: each machine decides only for its own car, so with this on for you and off for them you would take swats nobody else sees land."); BollardScale = cfg.Bind<float>("Bollard", "BollardScale", 0.35f, new ConfigDescription("How tall the bollard stands when it is up, measured in player-car lengths. At the default 0.35 it comes to about the height of a car's waist — high enough to stop one, low enough to see over. The player car is measured live, so this stays right whatever the map's scale is. How far the post travels is derived from this, since a bollard retracts until its lid is flush with the road. Every player must use the same value or it will be a different size, and block different things, on each screen.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 5f), Array.Empty<object>())); BollardHeight = cfg.Bind<float>("Bollard", "BollardHeight", 0f, new ConfigDescription("Exact standing height of the bollard in world units, ignoring BollardScale and the player car entirely. Leave at 0 to size against the player car, which is what you want on an unfamiliar map. The log prints \"bollard sized\" with the measured player-car length every time one is placed, so start from that number. Every player must use the same value.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>())); DownSeconds = cfg.Bind<float>("Bollard", "DownSeconds", 3f, new ConfigDescription("How long the bollard stays down, flush with the road, between one rise and the next. This is the window cars can cross it in.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 60f), Array.Empty<object>())); UpSeconds = cfg.Bind<float>("Bollard", "UpSeconds", 3f, new ConfigDescription("How long the bollard stands there once it is up, before dropping again.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 60f), Array.Empty<object>())); RiseSeconds = cfg.Bind<float>("Bollard", "RiseSeconds", 0.6f, new ConfigDescription("How long the post takes to travel, in seconds — used for the rise and for the drop, since they are the same movement in opposite directions. Eased at both ends, so it reads as hydraulics rather than as a lift. The model's own baked animation took about 1.7 seconds over each; 0.6 is meaner, and it is the difference between a bollard you can beat and one you cannot. DownSeconds plus UpSeconds plus twice this is the full cycle — 7.2 seconds at the defaults.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 10f), Array.Empty<object>())); } private static void BindCardRarities(ConfigFile cfg) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown try { BindEach(cfg); } catch (Exception ex) { ManualLogSource log = Plugin.Log; bool flag = default(bool); BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(87, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Could not bind the per-card rarity settings, so every card keeps its "); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("authored default: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex); } log.LogError(val); } } private static void BindEach(ConfigFile cfg) { foreach (CustomCard item in CustomCards.All) { item.RarityConfig = cfg.Bind<CardRarity>("Rarity", item.Key + "Rarity", item.Rarity, "How often the \"" + item.Name + "\" card is offered in the draft. These are the game's own rarity levels and they set the draw weight exactly as they do for a shipped card — Common 110, Uncommon 60, Rare 37, UltraRare 25, Legendary 12, Unique 2, against a pool holding every card in the game — so a Common turns up about three times as often as a Rare. It cuts the other way late on, though: a match that raises its minimum rarity drops the commonest cards from the pool first, so a card set to Common is the one that stops appearing. Off never offers the card, but leaves it registered: that is the only safe way to retire one, because cards cross the network as positions in a shared list and dropping one would renumber the rest. The level also colours the card's frame. Read live, so the ReloadConfigKey applies a change to the next draft. The roll happens server-side, so it is the host's settings that decide what the lobby is offered."); } } } [BepInPlugin("slipskid.morecards", "MoreCards", "1.1.1")] public class Plugin : BaseUnityPlugin { private Harmony _harmony; internal static Plugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } internal static Harmony Harmony { get; private set; } private void Awake() { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Expected O, but got Unknown //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; ModConfig.Bind(((BaseUnityPlugin)this).Config); if (!ModConfig.Enabled.Value) { Log.LogInfo((object)"Disabled in config; not patching anything."); return; } if (ModConfig.ForwardGameLogs.Value) { GameLogForwarder.Install(); } _harmony = new Harmony("slipskid.morecards"); Harmony = _harmony; ApplyPatchClasses(Assembly.GetExecutingAssembly()); bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val; foreach (MethodBase patchedMethod in _harmony.GetPatchedMethods()) { ManualLogSource log = Log; val = new BepInExInfoLogInterpolatedStringHandler(10, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Patched: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(patchedMethod.DeclaringType?.FullName); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("."); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(patchedMethod.Name); } log.LogInfo(val); } ((MonoBehaviour)this).StartCoroutine(Startup()); ManualLogSource log2 = Log; val = new BepInExInfoLogInterpolatedStringHandler(9, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("MoreCards"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" v"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("1.1.1"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" ready."); } log2.LogInfo(val); } private void ApplyPatchClasses(Assembly assembly) { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Expected O, but got Unknown Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(assembly); bool flag = default(bool); foreach (Type type in typesFromAssembly) { if (type.GetCustomAttributes(typeof(HarmonyPatch), inherit: true).Length == 0) { if (type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Any((MethodInfo m) => m.GetCustomAttributes(typeof(HarmonyPatch), inherit: true).Length != 0)) { Log.LogError((object)(type.Name + " declares [HarmonyPatch] methods but has no [HarmonyPatch] on the class, so Harmony ignores all of them. Add [HarmonyPatch] above the class declaration.")); } continue; } try { _harmony.CreateClassProcessor(type).Patch(); } catch (Exception ex) { ManualLogSource log = Log; BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(44, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Patching "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(type.Name); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" failed; its patches are inactive: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex); } log.LogError(val); } } } private IEnumerator Startup() { yield return GameHooks.WaitForGameManager(); GameHooks.Install(); ((Component)this).gameObject.AddComponent<ModBehaviour>(); Log.LogInfo((object)"Startup complete."); } private void OnDestroy() { GameHooks.Uninstall(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } internal static class PluginInfo { public const string Guid = "slipskid.morecards"; public const string Name = "MoreCards"; public const string Version = "1.1.1"; public const string HarmonyId = "slipskid.morecards"; } } namespace SlipSkid.MoreCards.Util { internal static class CardArt { private static readonly Dictionary<string, Texture2D> _cache = new Dictionary<string, Texture2D>(StringComparer.Ordinal); public static Texture2D Load(string resource) { //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Expected O, but got Unknown //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected O, but got Unknown //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Expected O, but got Unknown if (string.IsNullOrEmpty(resource)) { return null; } if (_cache.TryGetValue(resource, out var value)) { return value; } Texture2D val = null; bool flag = default(bool); try { byte[] array = ReadResource(resource); if (array == null) { Plugin.Log.LogWarning((object)("[art] embedded resource '" + resource + "' not found; the card keeps the carrier's thumbnail. Check the LogicalName in the .csproj.")); } else { val = new Texture2D(2, 2, (TextureFormat)4, false) { name = resource, wrapMode = (TextureWrapMode)1 }; if (!ImageConversion.LoadImage(val, array)) { Object.Destroy((Object)(object)val); val = null; ManualLogSource log = Plugin.Log; BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(44, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[art] '"); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(resource); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("' is not a PNG this build can decode."); } log.LogWarning(val2); } else if (ModConfig.Verbose.Value) { ManualLogSource log2 = Plugin.Log; BepInExInfoLogInterpolatedStringHandler val3 = new BepInExInfoLogInterpolatedStringHandler(16, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("[art] loaded "); ((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<string>(resource); ((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<int>(((Texture)val).width); ((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("x"); ((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<int>(((Texture)val).height); } log2.LogInfo(val3); } } } catch (Exception ex) { ManualLogSource log3 = Plugin.Log; BepInExErrorLogInterpolatedStringHandler val4 = new BepInExErrorLogInterpolatedStringHandler(25, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("[art] could not load '"); ((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<string>(resource); ((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("': "); ((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<Exception>(ex); } log3.LogError(val4); val = null; } _cache[resource] = val; return val; } private static byte[] ReadResource(string name) { using Stream stream = typeof(CardArt).Assembly.GetManifestResourceStream(name); if (stream == null) { return null; } byte[] array = new byte[stream.Length]; int num; for (int i = 0; i < array.Length; i += num) { num = stream.Read(array, i, array.Length - i); if (num <= 0) { break; } } return array; } } internal static class CarMeasure { public const float FallbackLength = 4.5f; public static float ReferenceLength(out bool measured) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) List<PlayerData> players = GameManager.Players; if (players != null) { for (int i = 0; i < players.Count; i++) { Transform val = players[i]?.controller?.car; if (!((Object)(object)val == (Object)null) && TryMeasureBody(val, out var localBounds)) { measured = true; return WorldLength(val, localBounds); } } } measured = false; return 4.5f; } public static bool TryMeasureBody(Transform car, out Bounds localBounds) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015b: 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_0081: 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_008a: 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) //IL_0093: 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_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) localBounds = default(Bounds); if ((Object)(object)car == (Object)null) { return false; } bool flag = false; MeshFilter[] componentsInChildren = ((Component)car).GetComponentsInChildren<MeshFilter>(false); Vector3 val3 = default(Vector3); foreach (MeshFilter val in componentsInChildren) { Mesh val2 = (((Object)(object)val != (Object)null) ? val.sharedMesh : null); if ((Object)(object)val2 == (Object)null) { continue; } MeshRenderer component = ((Component)val).GetComponent<MeshRenderer>(); if ((Object)(object)component == (Object)null || !((Renderer)component).enabled || (Object)(object)((Component)val).GetComponentInParent<ModAttachment>() != (Object)null) { continue; } Bounds bounds = val2.bounds; Vector3 center = ((Bounds)(ref bounds)).center; Vector3 extents = ((Bounds)(ref bounds)).extents; for (int j = 0; j < 8; j++) { ((Vector3)(ref val3))..ctor(center.x + (((j & 1) == 0) ? (0f - extents.x) : extents.x), center.y + (((j & 2) == 0) ? (0f - extents.y) : extents.y), center.z + (((j & 4) == 0) ? (0f - extents.z) : extents.z)); val3 = car.InverseTransformPoint(((Component)val).transform.TransformPoint(val3)); if (!flag) { localBounds = new Bounds(val3, Vector3.zero); flag = true; } else { ((Bounds)(ref localBounds)).Encapsulate(val3); } } } if (flag) { Vector3 size = ((Bounds)(ref localBounds)).size; return ((Vector3)(ref size)).sqrMagnitude > 1E-06f; } return false; } public static float WorldLength(Transform car, Bounds localBounds) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: 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_000f: 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_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) Vector3 size = ((Bounds)(ref localBounds)).size; Vector3 lossyScale = car.lossyScale; return Mathf.Max(size.x * Mathf.Abs(lossyScale.x), size.z * Mathf.Abs(lossyScale.z)); } public static float LocalLength(Bounds localBounds) { //IL_0002: 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) return Mathf.Max(((Bounds)(ref localBounds)).size.x, ((Bounds)(ref localBounds)).size.z); } public static Transform CarOf(PlayerData player) { return player?.controller?.car; } } internal static class ModelOrientation { public static Quaternion LongestAxisForward(Bounds bounds) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: 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_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: 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_0070: 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_0077: 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_007b: Unknown result type (might be due to invalid IL or missing references) //IL_007d: 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_0098: Unknown result type (might be due to invalid IL or missing references) //IL_0099: 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) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_008f: 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_0096: Unknown result type (might be due to invalid IL or missing references) Vector3 size = ((Bounds)(ref bounds)).size; int num = 0; if (size.y > ((Vector3)(ref size))[num]) { num = 1; } if (size.z > ((Vector3)(ref size))[num]) { num = 2; } int num2 = 0; if (size.y < ((Vector3)(ref size))[num2]) { num2 = 1; } if (size.z < ((Vector3)(ref size))[num2]) { num2 = 2; } if (num2 == num) { num2 = (num + 1) % 3; } int index = 3 - num - num2; Vector3 val = Axis(num); Vector3 val2 = Axis(num2); Vector3 val3 = Axis(index); if (Vector3.Dot(Vector3.Cross(val2, val3), val) < 0f) { val2 = -val2; } return Quaternion.Inverse(Quaternion.LookRotation(val, val3)); } public static float LongestExtent(Bounds bounds) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: 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_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) Vector3 size = ((Bounds)(ref bounds)).size; return Mathf.Max(size.x, Mathf.Max(size.y, size.z)); } private static Vector3 Axis(int index) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) return (Vector3)(index switch { 1 => Vector3.up, 0 => Vector3.right, _ => Vector3.forward, }); } } internal static class ModUi { public static void Toast(string message, float duration = 2f, Transform parent = null, Vector3 position = default(Vector3)) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Expected O, but got Unknown //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) bool flag = default(bool); if ((Object)(object)FloatingTextLabelGenerator.instance == (Object)null) { ManualLogSource log = Plugin.Log; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(8, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[toast] "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(message); } log.LogInfo(val); return; } try { FloatingTextLabelGenerator.Generate(message, position, parent, default(FloatingTextLabelCallBacks), 0.3f, duration, 0.3f); } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(30, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Toast failed ("); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("); message was: "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(message); } log2.LogWarning(val2); } } public static bool Notify(string header, string body, Texture thumbnail = null, float duration = 5f) { //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Expected O, but got Unknown //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) NotificationManager instance = NotificationManager.instance; if ((Object)(object)instance == (Object)null) { ManualLogSource log = Plugin.Log; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(11, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[notify] "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(header); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(body); } log.LogInfo(val); return false; } try { Type type = AccessTools.Inner(typeof(NotificationManager), "NotificationStyle"); Type type2 = AccessTools.Inner(typeof(NotificationManager), "NotificationContent"); MethodInfo methodInfo = AccessTools.Method(typeof(NotificationManager), "ShowNotification", new Type[3] { type, type2, typeof(float) }, (Type[])null); if (type == null || type2 == null || methodInfo == null) { Plugin.Log.LogWarning((object)"NotificationManager internals not found; falling back to a toast."); Toast(header + ": " + body); return false; } object value = AccessTools.Field(typeof(NotificationManager), "unlockableUnlockStyle").GetValue(instance); object obj = Activator.CreateInstance(type2); AccessTools.Field(type2, "headerText").SetValue(obj, null); AccessTools.Field(type2, "descriptionText").SetValue(obj, body ?? string.Empty); AccessTools.Field(type2, "thumbnail").SetValue(obj, thumbnail); methodInfo.Invoke(null, new object[3] { value, obj, duration }); if (!string.IsNullOrEmpty(header) && (Object)(object)instance.headerLabel != (Object)null) { instance.headerLabel.text = header; } return true; } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; bool flag2 = default(bool); BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(42, 1, ref flag2); if (flag2) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Notify failed ("); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("); falling back to a toast."); } log2.LogWarning(val2); Toast(header + ": " + body); return false; } } } internal static class ObjMaterials { private static readonly Dictionary<string, Material> _cache = new Dictionary<string, Material>(StringComparer.Ordinal); private static readonly int PlayerPrimaryColor = Shader.PropertyToID("_PlayerPrimaryColor"); private static readonly int PlayerSecondaryColor = Shader.PropertyToID("_PlayerSecondaryColor"); private static readonly int BaseColor = Shader.PropertyToID("_BaseColor"); private static readonly int LegacyColor = Shader.PropertyToID("_Color"); private static readonly string[] CommonMaps = new string[9] { "_BaseMap", "_MainTex", "_BumpMap", "_NormalMap", "_EmissionMap", "_MetallicGlossMap", "_SpecGlossMap", "_OcclusionMap", "_DetailAlbedoMap" }; public static Material[] Build(ObjModel model, Material template, string cacheKey, Color? overrideColor = null, bool untextured = false, bool opaque = false, bool doubleSided = false, Color? multiplyColor = null) { //IL_007a: 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_0093: Expected O, but got Unknown //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) int subMeshCount = model.Mesh.subMeshCount; Material[] array = (Material[])(object)new Material[subMeshCount]; for (int i = 0; i < subMeshCount; i++) { string text = ((i < model.MaterialNames.Length) ? model.MaterialNames[i] : string.Empty); string key = cacheKey + "/" + text; if (_cache.TryGetValue(key, out var value) && (Object)(object)value != (Object)null) { array[i] = value; continue; } if ((Object)(object)template == (Object)null) { array[i] = null; continue; } Material val = new Material(template) { name = cacheKey + "_" + text }; if (untextured) { ClearTextures(val); } if (opaque) { MakeOpaque(val); } if (doubleSided) { SetIfPresent(val, "_Cull", 0f); } bool flag = true; Color val2; Color value2; if (overrideColor.HasValue) { val2 = overrideColor.Value; } else if (model.MaterialColors.TryGetValue(text, out value2)) { val2 = value2; } else { val2 = Color.white; flag = multiplyColor.HasValue; } if (multiplyColor.HasValue) { Color value3 = multiplyColor.Value; ((Color)(ref val2))..ctor(val2.r * value3.r, val2.g * value3.g, val2.b * value3.b, 1f); } if (flag) { Tint(val, val2); } _cache[key] = val; array[i] = val; } return array; } public static void ClearTextures(Material mat) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Invalid comparison between Unknown and I4 Shader val = (((Object)(object)mat != (Object)null) ? mat.shader : null); if ((Object)(object)val == (Object)null) { return; } try { int propertyCount = val.GetPropertyCount(); for (int i = 0; i < propertyCount; i++) { if ((int)val.GetPropertyType(i) == 4) { mat.SetTexture(val.GetPropertyNameId(i), (Texture)null); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[obj] could not enumerate '" + ((Object)val).name + "' texture properties (" + ex.GetType().Name + "); clearing the usual ones instead.")); string[] commonMaps = CommonMaps; foreach (string text in commonMaps) { if (mat.HasProperty(text)) { mat.SetTexture(text, (Texture)null); } } } } public static void Tint(Material mat, Color color) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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) if (!((Object)(object)mat == (Object)null)) { color.a = 1f; int num = 0; if (mat.HasProperty(PlayerPrimaryColor)) { mat.SetColor(PlayerPrimaryColor, color); num++; } if (mat.HasProperty(PlayerSecondaryColor)) { mat.SetColor(PlayerSecondaryColor, color); num++; } if (mat.HasProperty(BaseColor)) { mat.SetColor(BaseColor, color); num++; } if (mat.HasProperty(LegacyColor)) { mat.SetColor(LegacyColor, color); num++; } if (num == 0) { Plugin.Log.LogWarning((object)("[obj] none of the colour properties exist on " + Describe(mat) + ", so it kept the template's colour. The shader names its colour something we do not write.")); } } } public static void MakeOpaque(Material mat) { if ((Object)(object)mat == (Object)null) { return; } try { SetIfPresent(mat, "_Surface", 0f); SetIfPresent(mat, "_Blend", 0f); SetIfPresent(mat, "_AlphaClip", 0f); SetIfPresent(mat, "_SrcBlend", 1f); SetIfPresent(mat, "_DstBlend", 0f); SetIfPresent(mat, "_ZWrite", 1f); SetIfPresent(mat, "_Cutoff", 0f); mat.DisableKeyword("_SURFACE_TYPE_TRANSPARENT"); mat.DisableKeyword("_ALPHAPREMULTIPLY_ON"); mat.DisableKeyword("_ALPHABLEND_ON"); mat.DisableKeyword("_ALPHATEST_ON"); mat.SetOverrideTag("RenderType", "Opaque"); mat.renderQueue = 2000; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[obj] could not force '" + ((Object)mat).name + "' opaque (" + ex.GetType().Name + "); it will keep the borrowed prop's blend state.")); } } private static void SetIfPresent(Material mat, string property, float value) { if (mat.HasProperty(property)) { mat.SetFloat(property, value); } } public static string Describe(Material mat) { if ((Object)(object)mat == (Object)null) { return "<none>"; } string arg = ((mat.renderQueue >= 3000) ? " TRANSPARENT-QUEUE" : string.Empty); return "'" + ((Object)mat).name + "' shader '" + (((Object)(object)mat.shader != (Object)null) ? ((Object)mat.shader).name : "<null>") + "' " + $"queue {mat.renderQueue}{arg}"; } public static bool TryReadColor(Material mat, out Color color) { //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_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0066: 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_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) color = Color.white; if ((Object)(object)mat == (Object)null) { return false; } int[] array = new int[4] { PlayerPrimaryColor, BaseColor, LegacyColor, PlayerSecondaryColor }; foreach (int num in array) { if (!mat.HasProperty(num)) { continue; } Color color2 = mat.GetColor(num); if (!(color2.a <= 0.001f)) { float num2 = Mathf.Max(color2.r, Mathf.Max(color2.g, color2.b)); float num3 = Mathf.Min(color2.r, Mathf.Min(color2.g, color2.b)); if (!(num2 <= 0.001f) && (!(num3 >= 0.99f) || !(num2 >= 0.99f))) { color = color2; return true; } } } return false; } public static Material TemplateFrom(Transform root) { if ((Object)(object)root == (Object)null) { return null; } MeshRenderer[] componentsInChildren = ((Component)root).GetComponentsInChildren<MeshRenderer>(false); foreach (MeshRenderer val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && ((Renderer)val).enabled && (Object)(object)((Renderer)val).sharedMaterial != (Object)null) { return ((Renderer)val).sharedMaterial; } } return null; } } internal sealed class ObjModel { private readonly struct VertexKey : IEquatable<VertexKey> { private readonly int _p; private readonly int _uv; private readonly int _n; public VertexKey(int p, int uv, int n) { _p = p; _uv = uv; _n = n; } public bool Equals(VertexKey o) { if (_p == o._p && _uv == o._uv) { return _n == o._n; } return false; } public override bool Equals(object o) { if (o is VertexKey o2) { return Equals(o2); } return false; } public override int GetHashCode() { return (((_p * 397) ^ _uv) * 397) ^ _n; } } private static readonly char[] Whitespace = new char[3] { ' ', '\t', '\r' }; public Mesh Mesh { get; private set; } public string[] MaterialNames { get; private set; } public Dictionary<string, Color> MaterialColors { get; } = new Dictionary<string, Color>(StringComparer.Ordinal); public static ObjModel Parse(string objText, string mtlText, string meshName) { if (string.IsNullOrEmpty(objText)) { throw new ArgumentException("OBJ text is empty.", "objText"); } ObjModel objModel = new ObjModel(); if (!string.IsNullOrEmpty(mtlText)) { objModel.ParseMtl(mtlText); } objModel.ParseObj(objText, meshName); return objModel; } public static ObjModel FromEmbeddedResource(string objResource, string mtlResource, string meshName) { Assembly assembly = typeof(ObjModel).Assembly; string objText = ReadResource(assembly, objResource) ?? throw new InvalidOperationException("Embedded resource '" + objResource + "' not found. Available: " + string.Join(", ", assembly.GetManifestResourceNames())); string mtlText = ReadResource(assembly, mtlResource); return Parse(objText, mtlText, meshName); } private static string ReadResource(Assembly asm, string name) { if (string.IsNullOrEmpty(name)) { return null; } using Stream stream = asm.GetManifestResourceStream(name); if (stream == null) { return null; } using StreamReader streamReader = new StreamReader(stream); return streamReader.ReadToEnd(); } private void ParseMtl(string text) { //IL_0072: Unknown result type (might be due to invalid IL or missing references) string text2 = null; foreach (string item in EnumerateLines(text)) { string[] array = Tokenize(item); if (array.Length != 0) { if (array[0] == "newmtl" && array.Length >= 2) { text2 = array[1]; } else if (array[0] == "Kd" && array.Length >= 4 && text2 != null) { MaterialColors[text2] = new Color(F(array[1]), F(array[2]), F(array[3])); } } } } private void ParseObj(string text, string meshName) { //IL_028f: Unknown result type (might be due to invalid IL or missing references) //IL_0294: Unknown result type (might be due to invalid IL or missing references) //IL_029d: Expected O, but got Unknown //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) List<Vector3> list = new List<Vector3>(); List<Vector3> list2 = new List<Vector3>(); List<Vector2> list3 = new List<Vector2>(); List<string> list4 = new List<string>(); List<List<int>> list5 = new List<List<int>>(); int num = -1; Dictionary<VertexKey, int> emitted = new Dictionary<VertexKey, int>(); List<Vector3> list6 = new List<Vector3>(); List<Vector3> list7 = new List<Vector3>(); List<Vector2> list8 = new List<Vector2>(); List<int> list9 = new List<int>(8); foreach (string item2 in EnumerateLines(text)) { string[] array = Tokenize(item2); if (array.Length == 0) { continue; } switch (array[0]) { case "v": if (array.Length >= 4) { list.Add(ToUnity(F(array[1]), F(array[2]), F(array[3]))); } break; case "vn": if (array.Length >= 4) { list2.Add(ToUnity(F(array[1]), F(array[2]), F(array[3]))); } break; case "vt": if (array.Length >= 3) { list3.Add(new Vector2(F(array[1]), F(array[2]))); } break; case "usemtl": { string item = ((array.Length >= 2) ? array[1] : string.Empty); num = list4.IndexOf(item); if (num < 0) { list4.Add(item); list5.Add(new List<int>()); num = list4.Count - 1; } break; } case "f": { if (array.Length < 4) { break; } if (num < 0) { list4.Add(string.Empty); list5.Add(new List<int>()); num = 0; } list9.Clear(); for (int i = 1; i < array.Length; i++) { int num2 = EmitVertex(array[i], list, list3, list2, emitted, list6, list8, list7); if (num2 >= 0) { list9.Add(num2); } } List<int> list10 = list5[num]; for (int j = 2; j < list9.Count; j++) { list10.Add(list9[0]); list10.Add(list9[j]); list10.Add(list9[j - 1]); } break; } } } if (list6.Count == 0) { throw new InvalidOperationException("OBJ contained no usable geometry."); } Mesh val = new Mesh { name = meshName }; if (list6.Count > 65535) { val.indexFormat = (IndexFormat)1; } val.SetVertices(list6); if (list7.Count == list6.Count) { val.SetNormals(list7); } if (list8.Count == list6.Count) { val.SetUVs(0, list8); } val.subMeshCount = list5.Count; for (int k = 0; k < list5.Count; k++) { val.SetTriangles(list5[k], k, false); } if (list7.Count != list6.Count) { val.RecalculateNormals(); } val.RecalculateBounds(); val.RecalculateTangents(); Mesh = val; MaterialNames = list4.ToArray(); } private static int EmitVertex(string token, List<Vector3> positions, List<Vector2> uvs, List<Vector3> normals, Dictionary<VertexKey, int> emitted, List<Vector3> outPositions, List<Vector2> outUvs, List<Vector3> outNormals) { //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_008c: 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_00a5: Unknown result type (might be due to invalid IL or missing references) string[] array = token.Split(new char[1] { '/' }); int num = ResolveIndex((array.Length != 0) ? array[0] : null, positions.Count); if (num < 0) { return -1; } int num2 = ((array.Length > 1) ? ResolveIndex(array[1], uvs.Count) : (-1)); int num3 = ((array.Length > 2) ? ResolveIndex(array[2], normals.Count) : (-1)); VertexKey key = new VertexKey(num, num2, num3); if (emitted.TryGetValue(key, out var value)) { return value; } outPositions.Add(positions[num]); outUvs.Add((num2 >= 0) ? uvs[num2] : Vector2.zero); outNormals.Add((num3 >= 0) ? normals[num3] : Vector3.zero); return emitted[key] = outPositions.Count - 1; } private static int ResolveIndex(string field, int count) { if (string.IsNullOrEmpty(field)) { return -1; } if (!int.TryParse(field, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { return -1; } int num = ((result > 0) ? (result - 1) : (count + result)); if (num < 0 || num >= count) { return -1; } return num; } private static Vector3 ToUnity(float x, float y, float z) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) return new Vector3(0f - x, y, z); } private static IEnumerable<string> EnumerateLines(string text) { using StringReader reader = new StringReader(text); string text2; while ((text2 = reader.ReadLine()) != null) { if (text2.Length != 0 && text2[0] != '#') { yield return text2; } } } private static string[] Tokenize(string line) { return line.Split(Whitespace, StringSplitOptions.RemoveEmptyEntries); } private static float F(string s) { if (!float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return 0f; } return result; } } internal static class PlayerPaint { private const float MinBrightness = 0.28f; private static readonly Dictionary<int, Color> _announced = new Dictionary<int, Color>(); public static void Announce(int obstacleId, Color colour) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (_announced.Count > 128) { _announced.Clear(); } _announced[obstacleId] = colour; } public static bool TryAnnounced(int obstacleId, out Color colour) { return _announced.TryGetValue(obstacleId, out colour); } public static bool TryIdentityColour(PlayerData player, out Color colour, out string source) { //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_001f: 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) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0094: 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_008b: 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_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) colour = Color.white; source = "none"; if ((Object)(object)player == (Object)null) { return false; } bool flag = false; Color val = Color.white; if (Usable(player.primaryColor?.Value, out var colour2)) { val = colour2; flag = true; source = "primaryColor"; } if (Usable(player.secondaryColor?.Value, out var colour3) && (!flag || Brightness(colour3) > Brightness(val))) { val = colour3; flag = true; source = "secondaryColor"; } if (!flag && TryCarEffectsColour(player, out var colour4)) { val = colour4; flag = true; source = "CarEffects"; } if (!flag && TryPaletteColour(player, out var colour5, out var source2)) { val = colour5; flag = true; source = source2; } if (!flag && TryCarMaterialColour(player, out var colour6, out var source3)) { val = colour6; flag = true; source = source3; } if (!flag) { return false; } colour = Legible(val); return true; } public static bool TryPickIdentity(Color32 primary, Color32 secondary, out Color colour) { //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_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0026: 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_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: 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_0047: 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_003e: Unknown result type (might be due to invalid IL or missing references) colour = Color.white; bool flag = false; Color val = Color.white; if (Usable(primary, out var colour2)) { val = colour2; flag = true; } if (Usable(secondary, out var colour3) && (!flag || Brightness(colour3) > Brightness(val))) { val = colour3; flag = true; } if (!flag) { return false; } colour = Legible(val); return true; } public static string Describe(PlayerData player) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null) { return "no player"; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("primaryColor=").Append((player.primaryColor != null) ? ((object)player.primaryColor.Value/*cast due to .constrained prefix*/).ToString() : "<null>"); stringBuilder.Append(" secondaryColor=").Append((player.secondaryColor != null) ? ((object)player.secondaryColor.Value/*cast due to .constrained prefix*/).ToString() : "<null>"); stringBuilder.Append(" primaryIndex=").Append((player.primaryColorIndex != null) ? player.primaryColorIndex.Value.ToString() : "<null>"); stringBuilder.Append(" secondaryIndex=").Append((player.secondaryColorIndex != null) ? player.secondaryColorIndex.Value.ToString() : "<null>"); stringBuilder.Append(" paintIndex=").Append((player.carPaintIndex != null) ? player.carPaintIndex.Value.ToString() : "<null>"); SphereCarController controller = player.controller; CarEffects val = (((Object)(object)controller != (Object)null) ? (((Component)controller).GetComponentInChildren<CarEffects>(true) ?? ((Component)controller).GetComponentInParent<CarEffects>()) : null); if ((Object)(object)val == (Object)null) { stringBuilder.Append(" CarEffects=<none>"); return stringBuilder.ToString(); } Traverse val2 = Traverse.Create((object)val); stringBuilder.Append(" CarEffects._primaryColor=").Append(val2.Field("_primaryColor").GetValue<Color32>()); stringBuilder.Append(" ._secondaryColor=").Append(val2.Field("_secondaryColor").GetValue<Color32>()); stringBuilder.Append(" paintMaterial=").Append(ObjMaterials.Describe(val.currentCarPaintMaterial)); return stringBuilder.ToString(); } private static bool Usable(Color32? value, out Color colour) { //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_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: 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_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0030: 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) colour = Color.white; if (!value.HasValue) { return false; } Color32 value2 = value.Value; if (value2.a == 0) { return false; } if (value2.r == 0 && value2.g == 0 && value2.b == 0) { return false; } colour = Color32.op_Implicit(value2); return true; } private static bool TryCarEffectsColour(PlayerData player, out Color colour) { //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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: 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) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_009d: 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) colour = Color.white; SphereCarController controller = player.controller; if ((Object)(object)controller == (Object)null) { return false; } CarEffects val = ((Component)controller).GetComponentInChildren<CarEffects>(true) ?? ((Component)controller).GetComponentInParent<CarEffects>(); if ((Object)(object)val == (Object)null) { return false; } Traverse val2 = Traverse.Create((object)val); bool flag = false; Color val3 = Color.white; string[] array = new string[2] { "_primaryColor", "_secondaryColor" }; foreach (string text in array) { Traverse val4 = val2.Field(text); if (val4 != null && val4.FieldExists() && Usable(val4.GetValue<Color32>(), out var colour2) && (!flag || Brightness(colour2) > Brightness(val3))) { val3 = colour2; flag = true; } } colour = val3; return flag; } private static bool TryPaletteColour(PlayerData player, out Color colour, out string source) { //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_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown