Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of Rain Better Skateboards MONO v1.0.0
RainsBetterSkateboard.dll
Decompiled a month agousing System; using System.Collections.Generic; using System.Diagnostics; 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 MelonLoader; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using RainsBetterSkateboard; using RainsBetterSkateboard.Logging; using ScheduleOne.Skating; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: MelonInfo(typeof(BetterSkateboardTweaks), "RainsBetterSkateboard", "1.0.0", "RainingDeath", null)] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: IgnoresAccessChecksTo("FishNet.Runtime")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("RainsBetterSkateboard")] [assembly: AssemblyConfiguration("DebugMelon")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("RainsBetterSkateboard")] [assembly: AssemblyTitle("RainsBetterSkateboard")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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; } } } public class SkateConfigRoot { public List<SkateboardBoard> Boards { get; set; } = new List<SkateboardBoard>(); } public class SkateboardBoard { public string Name; public float TopSpeed_Kmh; public float ReverseTopSpeed_Kmh; public float TurnForce; public float TurnChangeRate; public float TurnReturnToRestRate; public float TurnSpeedBoost; public float Gravity; public float BrakeForce; public float RotationClampForce; public float HoverForce; public float HoverHeight; public float HoverRayLength; public float Hover_P; public float Hover_I; public float Hover_D; public bool SlowOnTerrain; public bool FrictionEnabled; public float LongitudinalFrictionMultiplier; public float LateralFrictionForceMultiplier; public float JumpForce; public float JumpDuration_Min; public float JumpDuration_Max; public float JumpForwardBoost; public float PushForceMultiplier; public float PushForceDuration; public float PushDelay; public bool AirMovementEnabled; public float AirMovementForce; public float AirMovementJumpReductionDuration; public static SkateboardBoard FromPrefab(string name, Skateboard s) { return new SkateboardBoard { Name = name, TopSpeed_Kmh = s.TopSpeed_Kmh, ReverseTopSpeed_Kmh = s.ReverseTopSpeed_Kmh, TurnForce = s.TurnForce, TurnChangeRate = s.TurnChangeRate, TurnReturnToRestRate = s.TurnReturnToRestRate, TurnSpeedBoost = s.TurnSpeedBoost, Gravity = s.Gravity, BrakeForce = s.BrakeForce, RotationClampForce = s.RotationClampForce, HoverForce = s.HoverForce, HoverHeight = s.HoverHeight, HoverRayLength = s.HoverRayLength, Hover_P = s.Hover_P, Hover_I = s.Hover_I, Hover_D = s.Hover_D, SlowOnTerrain = s.SlowOnTerrain, FrictionEnabled = s.FrictionEnabled, LongitudinalFrictionMultiplier = s.LongitudinalFrictionMultiplier, LateralFrictionForceMultiplier = s.LateralFrictionForceMultiplier, JumpForce = s.JumpForce, JumpDuration_Min = s.JumpDuration_Min, JumpDuration_Max = s.JumpDuration_Max, JumpForwardBoost = s.JumpForwardBoost, PushForceMultiplier = s.PushForceMultiplier, PushForceDuration = s.PushForceDuration, PushDelay = s.PushDelay, AirMovementEnabled = s.AirMovementEnabled, AirMovementForce = s.AirMovementForce, AirMovementJumpReductionDuration = s.AirMovementJumpReductionDuration }; } } namespace RainsBetterSkateboard { public class Core : MelonMod { public override void OnInitializeMelon() { Log.LogInfo("Initializing RainsBetterSkateboard..."); } } public sealed class BetterSkateboardTweaks : MelonMod { private const string ConfigPath = "Mods/BetterSkateboardConfig.json"; private static SkateConfigRoot _config = new SkateConfigRoot(); private static bool _hadConfigAtStart; public override void OnInitializeMelon() { _config = LoadConfig(out _hadConfigAtStart) ?? new SkateConfigRoot(); MelonLogger.Msg(_hadConfigAtStart ? $"[BetterSkateboard] Loaded config for {_config.Boards.Count} boards at startup." : "[BetterSkateboard] No valid config at startup; will write defaults after prefabs are loaded in Main."); } public override void OnSceneWasLoaded(int buildIndex, string sceneName) { if (!sceneName.Equals("Main", StringComparison.OrdinalIgnoreCase)) { return; } Dictionary<string, Skateboard> dictionary = (from s in Resources.FindObjectsOfTypeAll<Skateboard>() where Object.op_Implicit((Object)(object)s) && Object.op_Implicit((Object)(object)((Component)s).gameObject) group s by ((Object)((Component)s).gameObject).name).ToDictionary((IGrouping<string, Skateboard> g) => g.Key, (IGrouping<string, Skateboard> g) => g.First()); if (dictionary.Count == 0) { MelonLogger.Warning("[BetterSkateboard] No Skateboard prefabs were found via Resources."); return; } string[] value = dictionary.Keys.OrderBy((string n) => n).ToArray(); MelonLogger.Msg("[BetterSkateboard] Prefabs discovered: " + string.Join(", ", value)); if (!_hadConfigAtStart) { _config.Boards = dictionary.Select((KeyValuePair<string, Skateboard> kv) => SkateboardBoard.FromPrefab(kv.Key, kv.Value)).ToList(); SafeWrite("Mods/BetterSkateboardConfig.json", JsonConvert.SerializeObject((object)_config, (Formatting)1)); _hadConfigAtStart = true; MelonLogger.Msg("[BetterSkateboard] Wrote defaults → Mods/BetterSkateboardConfig.json"); return; } int num = 0; int num2 = 0; foreach (SkateboardBoard board in _config.Boards) { if (board == null || string.IsNullOrEmpty(board.Name)) { num2++; continue; } if (!dictionary.TryGetValue(board.Name, out var value2)) { num2++; MelonLogger.Warning("[BetterSkateboard] Prefab not found for '" + board.Name + "' (exact match)."); continue; } value2.TopSpeed_Kmh = board.TopSpeed_Kmh; value2.ReverseTopSpeed_Kmh = board.ReverseTopSpeed_Kmh; value2.TurnForce = board.TurnForce; value2.TurnChangeRate = board.TurnChangeRate; value2.TurnReturnToRestRate = board.TurnReturnToRestRate; value2.TurnSpeedBoost = board.TurnSpeedBoost; value2.Gravity = board.Gravity; value2.BrakeForce = board.BrakeForce; value2.RotationClampForce = board.RotationClampForce; value2.HoverForce = board.HoverForce; value2.HoverHeight = board.HoverHeight; value2.HoverRayLength = board.HoverRayLength; value2.Hover_P = board.Hover_P; value2.Hover_I = board.Hover_I; value2.Hover_D = board.Hover_D; value2.SlowOnTerrain = board.SlowOnTerrain; value2.FrictionEnabled = board.FrictionEnabled; value2.LongitudinalFrictionMultiplier = board.LongitudinalFrictionMultiplier; value2.LateralFrictionForceMultiplier = board.LateralFrictionForceMultiplier; value2.JumpForce = board.JumpForce; value2.JumpDuration_Min = board.JumpDuration_Min; value2.JumpDuration_Max = board.JumpDuration_Max; value2.JumpForwardBoost = board.JumpForwardBoost; value2.PushForceMultiplier = board.PushForceMultiplier; value2.PushForceDuration = board.PushForceDuration; value2.PushDelay = board.PushDelay; value2.AirMovementEnabled = board.AirMovementEnabled; value2.AirMovementForce = board.AirMovementForce; value2.AirMovementJumpReductionDuration = board.AirMovementJumpReductionDuration; num++; MelonLogger.Msg($"[BetterSkateboard] Patched prefab '{board.Name}' (HoverHeight={value2.HoverHeight}, HoverForce={value2.HoverForce}, TopSpeed_Kmh={value2.TopSpeed_Kmh})"); } MelonLogger.Msg($"[BetterSkateboard] Applied {num} prefab(s) from config. Missing: {num2}."); } private static SkateConfigRoot LoadConfig(out bool had) { had = false; try { Directory.CreateDirectory(Path.GetDirectoryName("Mods/BetterSkateboardConfig.json")); if (!File.Exists("Mods/BetterSkateboardConfig.json")) { return null; } string text = File.ReadAllText("Mods/BetterSkateboardConfig.json"); SkateConfigRoot skateConfigRoot = TryParse(text); if (skateConfigRoot?.Boards != null && skateConfigRoot.Boards.Count > 0) { had = true; return skateConfigRoot; } } catch { } return null; } private static SkateConfigRoot TryParse(string text) { try { SkateConfigRoot skateConfigRoot = JsonConvert.DeserializeObject<SkateConfigRoot>(text); if (skateConfigRoot != null && skateConfigRoot.Boards?.Count > 0) { return skateConfigRoot; } } catch { } try { List<SkateboardBoard> list = JsonConvert.DeserializeObject<List<SkateboardBoard>>(text); if (list != null && list.Count > 0) { return new SkateConfigRoot { Boards = list }; } } catch { } try { SkateboardBoard skateboardBoard = JsonConvert.DeserializeObject<SkateboardBoard>(text); if (skateboardBoard != null && !string.IsNullOrEmpty(skateboardBoard.Name)) { return new SkateConfigRoot { Boards = new List<SkateboardBoard> { skateboardBoard } }; } } catch { } return null; } private static void SafeWrite(string path, string json) { try { if (File.Exists(path)) { File.Copy(path, path + ".bak", overwrite: true); } } catch { } File.WriteAllText(path, json); } } } namespace RainsBetterSkateboard.Logging { public static class Log { public static void LogInfo(string message) { Melon<Core>.Logger.Msg(message); } public static void LogWarning(string message) { Melon<Core>.Logger.Warning(message); } public static void LogError(string message) { Melon<Core>.Logger.Error(message); } public static void LogFatal(string message) { Melon<Core>.Logger.BigError(message); } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }