You are viewing a potentially older version of this package. View all versions.
4902-Cruiser_Additions-1.2.1 icon

Cruiser Additions

fixed magnet rotation, move scrap to the ship automatically, storage light, mute engine/radio audio in orbit, cruiser scrap items, speedometer,

By 4902
Date uploaded 3 weeks ago
Version 1.2.1
Download link 4902-Cruiser_Additions-1.2.1.zip
Downloads 11019
Dependency string 4902-Cruiser_Additions-1.2.1

This mod requires the following mods to function

BepInEx-BepInExPack-5.4.2100 icon
BepInEx-BepInExPack

BepInEx pack for Mono Unity games. Preconfigured and ready to use.

Preferred version: 5.4.2100

README

Additions for the cruiser. all features are client-sided and have configs

  • Screenshots

    • Storage light

    • Scrap items

    • Speedometer

  • Magnet rotation
    • fixes the cruiser rotation when magneted to only be 90 or 270 (parallel to the ship). otherwise at certain angles (~225 or 315-360) it can be magneted to 180 or 360 and be partially inside the ship
    • host must have this enabled for it to apply. all clients will see the fixed rotation if host has this enabled
  • Move items
    • move items from the cruiser to the ship when going into orbit. the items are only moved if they were collected
    • this is client sided so items are only shown to have been moved to players with this enabled
    • if one player has this enabled and another player has this disabled (or doesn't have the mod), the item positions will be different, however picking up the item/s will resync the position
    • in vanilla any collected items in the cruiser will be moved to the ship when leaving and rejoining after the game saved, so this feature is the same as vanilla except without having to leave and rejoin
  • Only move scrap items
    • only moves scrap and leaves other items in the cruiser
    • if the host has this mod this will be set to what the host has this set to
  • Mute engine/radio audio
    • mute engine/radio audio while in orbit
  • Storage light
    • adds a light to the storage area of the cruiser
  • Scrap items
    • adds a cruiser item and an alternate cruiser item
    • items are a percentage based retexture of v-type engine, and are synced with other players if the config percentages are the same
    • item types are saved to the save file if the save/load config is enabled
  • Speedometer
    • adds a speedometer
  • Cruiser position/rotation when joining
    • when joining a lobby with a cruiser, it will be on the magnet instead of on the opposite side of the ship
    • also fixes the 'rotation quaternions must be unit length' error from the cruiser after joining
  • Cruiser 1up
    • allows purchasing another cruiser in the same day if the current cruiser is destroyed

cs in zip. discord@4902

CHANGELOG

1.2.2
-fixed speedometer text not being visible with certain mods

1.2.1
-fixed cruiser items rotation when using mods that change engine restingRotation/verticalOffset

1.2.0
-added config for purchasing another cruiser if the current cruiser is destroyed
-added cruiser items being visible on the scrap found display

1.1.1
-added QueryTriggerInteraction config to attempt to fix some modded items (and teeth) not being collected by the magnet

1.1.0
-added saving/loading of item types
-added screenshots to the readme

1.0.0
Source code

1.2.2

using UInt32 = System.UInt32; using UInt64 = System.UInt64; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Collections.Generic; using System.Threading.Tasks; using BepInEx; using BepInEx.Logging; using BepInEx.Configuration; using HarmonyLib; using GameNetcodeStuff; using UnityEngine; using UnityEngine.InputSystem; using Unity.Netcode; namespace kirby { [BepInPlugin("4902.Cruiser_Additions", "Cruiser_Additions", "1.0.0")] public class ca : BaseUnityPlugin { public static readonly Harmony harmony = new Harmony("4902.Cruiser_Additions"); public static ManualLogSource mls; public static ConfigEntry<bool> cfg1_magnet; //true public static ConfigEntry<int> cfg2_moveitems; //2 public static ConfigEntry<bool> cfg2_scraponly; //true public static ConfigEntry<bool> cfg3_engine; //true public static ConfigEntry<bool> cfg3_radio; //true public static ConfigEntry<bool> cfg4_light; //true public static ConfigEntry<bool> cfg5_scrap; //true public static ConfigEntry<int> cfg5_cruiser; //40 public static ConfigEntry<int> cfg5_cruiseralt; //40 public static ConfigEntry<bool> cfg5_saveseeds; //true public static ConfigEntry<int> cfg5_millisecond; //100 public static ConfigEntry<bool> cfg6_speedometer; //true public static ConfigEntry<bool> cfg7_unlockables; //true public static ConfigEntry<bool> cfg8_collide; //true public static ConfigEntry<bool> cfg9_1up; //false private void Awake() { cfg1_magnet = Config.Bind("Cruiser+", "magnet_rotation", true, "[Magnet rotation]\nfixes the cruiser rotation when magneted to only be 90 or 270 (parallel to the ship). otherwise at certain angles (~225 or 315-360) it can be magneted to 180 or 360 and be partially inside the ship.\nhost must have this enabled for it to apply. all clients will see the fixed rotation if host has this enabled"); cfg2_moveitems = Config.Bind("Cruiser+", "move_items", 2, "[Move items]\nmove items from the cruiser to the ship when going into orbit. the items are only moved if they were collected.\nthis is client sided so items are only shown to have been moved to players with this enabled.\nif one player has this enabled and another player has this disabled (or doesn't have the mod), the item positions will be different, however picking up the item/s will resync the position.\nin vanilla any collected items in the cruiser will be moved to the ship when leaving and rejoining after the game saved, so this feature is the same as vanilla except without having to leave and rejoin.\n1 = disabled\n2 = only enabled when you're host, or if the host has this enabled\n3 = always enabled"); cfg2_scraponly = Config.Bind("Cruiser+", "only_scrap", true, "[Only move scrap items]\nonly moves scrap and leaves other items in the cruiser.\nif the host has this mod this will be set to what the host has this set to"); cfg3_engine = Config.Bind("Cruiser+", "mute_engine", true, "[Mute engine audio]\nmute engine audio while in orbit"); cfg3_radio = Config.Bind("Cruiser+", "mute_radio", true, "[Mute radio audio]\nmute radio audio while in orbit"); cfg4_light = Config.Bind("Cruiser+", "light", true, "[Storage light]\nadds a light to the storage area of the cruiser"); cfg5_scrap = Config.Bind("Cruiser+", "scrap", true, "[Scrap items]\nwhether cruiser scrap items should be enabled or disabled.\nitems are a percentage based retexture of v-type engine, and are synced with other players if the config percentages are the same"); cfg5_cruiser = Config.Bind("Cruiser+", "cruiser_percentage", 40, "[Cruiser item percentage]\npercentage that the cruiser item will replace v-type engine.\ninput = percentage, 40 is 40%"); cfg5_cruiseralt = Config.Bind("Cruiser+", "cruiser_alt_percentage", 40, "[Cruiser alt item percentage]\npercentage that the cruiser item will be an alternate version.\ninput = percentage, 40 is 40%"); cfg5_saveseeds = Config.Bind("Cruiser+", "save/load", true, "[Save/load seeds]\nwhether the engine/cruiser item types should be saved to the save file. this saves the seed that determines the items type, so it will be the same when rejoining.\nloading a save file that has saved seeds without having this enabled isn't recommended as the saved seeds can be reset if the number of items is changed.\nsaved item types are synced with other players if the config percentages are the same"); cfg5_millisecond = Config.Bind("Cruiser+", "timer", 100, "[Client wait_timer]\nwhen joining a lobby as non-host if save/load is enabled then engine items will wait to set their item type until the seeds sent by the host have been received or the time spent waiting reached the maximum amount set by this config.\nthere will be a log message specifying if the seeds were received first or the timer ended first. if the timer is often ending before the message from the host is being received then this should be increased. if the host doesn't have this mod then save/load can be disabled since there won't be a received message from the host so the items would always wait the full timer.\nmin is 20, max is 4000. 100 is before the player spawn animation ends, 500 is about 10 seconds, 1000 is about 15 seconds"); cfg6_speedometer = Config.Bind("Cruiser+", "speedometer", true, "[Speedometer]\nadds a speedometer"); cfg7_unlockables = Config.Bind("Cruiser+", "fix", true, "[Cruiser position/rotation after joining]\nwhen joining a lobby with a cruiser, it will be on the magnet instead of on the opposite side of the ship.\nalso fixes the 'rotation quaternions must be unit length' error from the cruiser after joining"); cfg8_collide = Config.Bind("Cruiser+", "collide", true, "[QueryTriggerInteraction]\nchanges the Physics.OverlapSphere QueryTriggerInteraction in VehicleController.CollectItemsInTruck from Ignore to Collide.\nattempts to fix some modded items (and teeth) not being collected by the magnet and not having isInShipRoom set to true, so the move items script would skip them"); cfg9_1up = Config.Bind("Cruiser+", "1up", false, "[Cruiser 1up]\nallows purchasing another cruiser in the same day if the current cruiser is destroyed"); mls = BepInEx.Logging.Logger.CreateLogSource("Cruiser+"); mls.LogInfo(":red_car:"); harmony.PatchAll(typeof(cruiser_additions)); } } public class cruiser_additions { // // magnet rotation // private static Vector3 cruiser_rotation; [HarmonyPatch(typeof(VehicleController), "StartMagneting"), HarmonyPrefix] private static void pre1(VehicleController __instance) { cruiser_rotation = __instance.transform.eulerAngles; } [HarmonyPatch(typeof(VehicleController), "StartMagneting"), HarmonyTranspiler] private static IEnumerable<CodeInstruction> trn1(IEnumerable<CodeInstruction> Instrs) { var l = new List<CodeInstruction>(Instrs); for (int n = 0; n < l.Count; n = n + 1) { if (n >= 11 && l[n - 11].ToString() == "call static UnityEngine.Color UnityEngine.Color::get_white()") { yield return new CodeInstruction(OpCodes.Call, typeof(cruiser_additions).GetMethod("return_rotation")); } yield return l[n]; //ca.mls.LogInfo(l[n].ToString()); } } public static float return_rotation(float r) { if (ca.cfg1_magnet.Value == true) { if (cruiser_rotation.y < 180f) { ca.mls.LogInfo("eulerAngles.y " + cruiser_rotation.y + " < 180, returning 90"); return 90f; } ca.mls.LogInfo("eulerAngles.y " + cruiser_rotation.y + " > 180, returning 270"); return 270f; } return r; } [HarmonyPatch(typeof(StartOfRound), "EndOfGame"), HarmonyPrefix] private static void pre2(StartOfRound __instance) { VehicleController car = __instance.attachedVehicle; if (car != null) { // // move items // if (ca.cfg2_moveitems.Value == 3 || (ca.cfg2_moveitems.Value == 2 && (hostmoveitems == "true" || GameNetworkManager.Instance.isHostingGame == true))) { ca.mls.LogInfo("moving items"); ca.mls.LogInfo(("host:" + hostscraponly + " client:" + ca.cfg2_scraponly.Value).ToLower()); Collider[] objects = Physics.OverlapBox(car.transform.position, new Vector3(4f, 11f, 11f) / 2, new Quaternion(0f, 0.7071068f, 0f, 0.7071068f), 64, QueryTriggerInteraction.Collide); ca.mls.LogInfo("total items found in cruiser: " + objects.Length); for (int n = 0; n < objects.Length; n = n + 1) { GrabbableObject item = objects[n].GetComponent<GrabbableObject>(); if (item != null && (hostscraponly == "false" || (hostscraponly == "true" && item.itemProperties.isScrap == true) || (hostscraponly == "nil" && ca.cfg2_scraponly.Value == false) || (hostscraponly == "nil" && ca.cfg2_scraponly.Value == true && item.itemProperties.isScrap == true)) && item.isHeld == false && item.isHeldByEnemy == false && item.isInShipRoom == true) //objects[n].transform.parent == car.transform) { ca.mls.LogInfo("moving item " + (n + 1) + " " + item.itemProperties.itemName + " / " + item.itemProperties.name + " / " + item.name); item.transform.SetParent(StartOfRound.Instance.elevatorTransform); Shion cr = (GameNetworkManager.Instance.disableSteam == false && lobbyid != 0uL && item.GetComponent<NetworkObject>() != null ? new Shion(lobbyid + item.GetComponent<NetworkObject>().NetworkObjectId) : new Shion()); var angle = cr.next01() * System.Math.PI * 2; var radius = cr.next01() * 1; var random_x = -0.33f + radius * System.Math.Cos(angle); var random_z = -14.4f + radius * System.Math.Sin(angle); Vector3 pos = item.transform.parent.InverseTransformPoint(new Vector3((float)random_x, item.itemProperties.verticalOffset + 0.2362f, (float)random_z)); item.transform.eulerAngles = new Vector3(item.itemProperties.restingRotation.x, (float)cr.next32mm(0, 360), item.itemProperties.restingRotation.z); item.transform.position = pos; item.startFallingPosition = pos; item.targetFloorPosition = pos; if (item.itemProperties.spawnPrefab != null) { item.transform.localScale = item.itemProperties.spawnPrefab.transform.localScale; } else { item.transform.localScale = item.originalScale; } } else if (item != null) { ca.mls.LogInfo("skipped item " + (n + 1) + " " + item.itemProperties.itemName + " / " + item.itemProperties.name + " / " + item.name); if (item.isInShipRoom == false) { ca.mls.LogInfo("skipped item " + (n + 1) + " wasn't collected! isInShipRoom was false. it will be in the cruiser"); } } } } // // mute engine/radio // if (ca.cfg3_engine.Value == true) { ca.mls.LogInfo("muting engine"); car.engineAudio1.mute = true; car.engineAudio2.mute = true; } if (ca.cfg3_radio.Value == true) { ca.mls.LogInfo("muting radio"); car.radioAudio.mute = true; car.radioInterference.mute = true; } } } // // unmute engine/radio // [HarmonyPatch(typeof(StartOfRound), "openingDoorsSequence", MethodType.Enumerator), HarmonyTranspiler] private static IEnumerable<CodeInstruction> trn2(IEnumerable<CodeInstruction> Instrs) { var l = new List<CodeInstruction>(Instrs); for (int n = 0; n < l.Count; n = n + 1) { yield return l[n]; if (n >= 2 && l[n - 2].ToString() == "ldstr \"Closed\"") { yield return new CodeInstruction(OpCodes.Call, typeof(cruiser_additions).GetMethod("unmute_audio")); } //ca.mls.LogInfo(l[n].ToString()); } } public static void unmute_audio() { VehicleController car = StartOfRound.Instance.attachedVehicle; if (car != null) { if (ca.cfg3_engine.Value == true) { ca.mls.LogInfo("unmuting engine"); car.engineAudio1.mute = false; car.engineAudio2.mute = false; } if (ca.cfg3_radio.Value == true) { ca.mls.LogInfo("unmuting radio"); car.radioAudio.mute = false; car.radioInterference.mute = false; } } } private static Transform light_tree = null; private static Transform meter_cube = null; private static Transform meter_text = null; [HarmonyPatch(typeof(VehicleController), "Start"), HarmonyPrefix] private static void pre3(VehicleController __instance) { // // storage light // if (ca.cfg4_light.Value == true) { ca.mls.LogInfo("adding light"); if (light_tree == null) { Transform light_object = Object.Instantiate<Transform>(__instance.frontCabinLightContainer.GetComponentsInChildren<Transform>()[1]); light_object.name = "cruiser_storage_light"; Light light = light_object.GetComponentInChildren<Light>(); light.type = LightType.Spot; light.spotAngle = 120f; light.range = 10f; light_tree = light_object; light_tree.gameObject.SetActive(false); Object.DontDestroyOnLoad(light_tree); } if (light_tree != null) { Transform light_object = Object.Instantiate<Transform>(light_tree); light_object.SetParent(__instance.GetComponentsInChildren<Transform>().First(_ => _.name == "Meshes")); light_object.localPosition = new Vector3(0f, 3f, -2f); light_object.localEulerAngles = new Vector3(90f, 0f, 0f); light_object.localScale = new Vector3(1f, 1f, 1f); light_object.gameObject.SetActive(__instance.frontCabinLightContainer.activeSelf); ca.mls.LogInfo("added light"); } } // // speedometer // if (ca.cfg6_speedometer.Value == true) { ca.mls.LogInfo("adding speedometer"); if (meter_cube == null) { //cube Transform screen = Object.Instantiate<Transform>(GameObject.Find("Environment/HangarShip/damageTrigger/Cube").transform); Transform border = Object.Instantiate<Transform>(screen); screen.GetComponent<MeshRenderer>().materials = new Material[] {GameObject.Find("Environment/HangarShip/SmallDetails/Cameras/TinyCamera").GetComponent<MeshRenderer>().materials[0]}; screen.GetComponent<MeshRenderer>().materials[0].color = new Color(0f, 0f, 0f ,0f); border.GetComponent<MeshRenderer>().materials = new Material[] {GameObject.Find("Environment/HangarShip/ShipModels2b/MonitorWall/Cube").GetComponent<MeshRenderer>().materials[1]}; border.SetParent(screen); border.localPosition = new Vector3(0f, 0f, 0f); border.localEulerAngles = new Vector3(0f, 0f, 0f); border.localScale = new Vector3(0.98f, 1.2f, 1.2f); screen.name = "cruiser_speedometer_screen"; border.name = "cruiser_speedometer_border"; meter_cube = screen; meter_cube.gameObject.SetActive(false); Object.DontDestroyOnLoad(meter_cube); ca.mls.LogInfo("saved speedometer object"); //text Transform upper = Object.Instantiate<Transform>(GameObject.Find("Environment/HangarShip/ShipModels2b/MonitorWall/Cube/Canvas (1)").transform); Transform lower = upper.GetComponentsInChildren<Transform>(true).First(_ => _.name == "HeaderText"); lower.SetParent(upper); lower.gameObject.SetActive(true); Transform temp = upper.GetComponentsInChildren<Transform>(true).First(_ => _.name == "MainContainer"); Object.DestroyImmediate(temp.GetComponent<UnityEngine.UI.Image>()); Object.DestroyImmediate(temp.gameObject); var text = lower.GetComponent<TMPro.TextMeshProUGUI>(); text.enabled = true; text.alignment = TMPro.TextAlignmentOptions.Center; text.color = new Color(1f, 1f, 1f, 1f); text.fontStyle = (TMPro.FontStyles)FontStyle.Bold; text.horizontalAlignment = TMPro.HorizontalAlignmentOptions.Center; text.text = "444"; upper.name = "cruiser_speedometer_text1"; lower.name = "cruiser_speedometer_text2"; meter_text = upper; meter_text.gameObject.SetActive(false); Object.DontDestroyOnLoad(meter_text); ca.mls.LogInfo("saved speedometer text"); } if (meter_cube != null && meter_text != null) { Transform cube = Object.Instantiate<Transform>(meter_cube); cube.SetParent(__instance.GetComponentsInChildren<Transform>().First(_ => _.name == "Meshes")); cube.localPosition = new Vector3(-1f, 0.115f, 2.65f); cube.localEulerAngles = new Vector3(0f, 90f, 30f); cube.localScale = new Vector3(0.2f, 0.08f, 0.13f); cube.gameObject.SetActive(true); Transform text = Object.Instantiate<Transform>(meter_text); text.SetParent(__instance.GetComponentsInChildren<Transform>().First(_ => _.name == "Meshes")); text.localPosition = new Vector3(-0.995f, 0.157f, 2.558f); text.localEulerAngles = new Vector3(30f, 0f, 0f); text.localScale = new Vector3(0.001f, 0.001f, 0.001f); Transform lower = text.GetChild(0); lower.localPosition = new Vector3(-4.7f, 7f, 0f); lower.localEulerAngles = new Vector3(0f, 0f, 0f); lower.localScale = new Vector3(1f, 1f, 1f); var txt = lower.GetComponent<TMPro.TextMeshProUGUI>(); txt.enabled = true; txt.enableWordWrapping = false; txt.autoSizeTextContainer = false; txt.enableAutoSizing = false; txt.text = "444"; txt.autoSizeTextContainer = true; txt.enableAutoSizing = true; text.gameObject.SetActive(__instance.frontCabinLightContainer.activeSelf); ca.mls.LogInfo("added speedometer"); } } } [HarmonyPatch(typeof(VehicleController), "SetFrontCabinLightOn"), HarmonyPostfix] private static void pst1(VehicleController __instance, ref bool setOn) { if (ca.cfg4_light.Value == true && light_tree != null) { __instance.GetComponentsInChildren<Transform>(true).First(_ => _.name == "cruiser_storage_light(Clone)").gameObject.SetActive(setOn); } if (ca.cfg6_speedometer.Value == true && meter_cube != null && meter_text != null) { RectTransform rt = __instance.GetComponentsInChildren<RectTransform>(true).First(_ => _.name == "cruiser_speedometer_text1(Clone)"); if (rt.GetChild(0).GetComponent<TMPro.TextMeshProUGUI>().text == "444") { rt.GetChild(0).GetComponent<TMPro.TextMeshProUGUI>().text = "0"; } rt.gameObject.SetActive(setOn); } } [HarmonyPatch(typeof(VehicleController), "DestroyCar"), HarmonyPostfix] private static void pst2(VehicleController __instance) { if (ca.cfg4_light.Value == true && light_tree != null) { __instance.GetComponentsInChildren<Transform>(true).First(_ => _.name == "cruiser_storage_light(Clone)").gameObject.SetActive(false); } if (ca.cfg6_speedometer.Value == true && meter_cube != null && meter_text != null) { __instance.GetComponentsInChildren<Transform>(true).First(_ => _.name == "cruiser_speedometer_screen(Clone)").gameObject.SetActive(false); __instance.GetComponentsInChildren<RectTransform>(true).First(_ => _.name == "cruiser_speedometer_text1(Clone)").gameObject.SetActive(false); } } [HarmonyPatch(typeof(VehicleController), "FixedUpdate"), HarmonyPostfix] private static void pst3(VehicleController __instance) { if (ca.cfg6_speedometer.Value == true && meter_cube != null && meter_text != null && __instance.ignitionStarted == true) { RectTransform rt = __instance.GetComponentsInChildren<RectTransform>(true).First(_ => _.name == "cruiser_speedometer_text1(Clone)"); if (rt.gameObject.activeSelf == true) { var text = rt.GetChild(0).GetComponent<TMPro.TextMeshProUGUI>(); string velocity = Mathf.Round(__instance.mainRigidbody.velocity.magnitude * 2).ToString(); if (velocity != text.text) { if (velocity.Length > 5) { velocity = "Error"; } text.text = velocity; } } } } // // scrap items // private static string[] item = new string[2]; private static Transform[] tree = new Transform[2]; private static AudioClip[] take = new AudioClip[2]; private static AudioClip[] give = new AudioClip[2]; private static Vector3[] position = new Vector3[3]; private static Vector3[] rotation = new Vector3[2]; private static bool first_pending = false; private static UInt64 lobbyid = 0uL; private static bool[] first_item = new bool[] {true, true, true}; [HarmonyPatch(typeof(GrabbableObject), "Start"), HarmonyPostfix] private static void pst4(GrabbableObject __instance) { pst4async(__instance); } private static async void pst4async(GrabbableObject __instance) { if (ca.cfg5_scrap.Value == true && __instance.itemProperties.name == "EnginePart1") { if (first_item[0] == true) { Item r = StartOfRound.Instance.allItemsList.itemsList.First(_ => _.name == "EnginePart1"); r.restingRotation = new Vector3(0f, 0f, 90f); r.verticalOffset = 0.4f; } if (ca.cfg5_saveseeds.Value == true && GameNetworkManager.Instance.disableSteam == false && seeds == "nil") { if (GameNetworkManager.Instance.isHostingGame == false) { if (first_item[0] == true) { ca.mls.LogInfo("await wait_timer"); first_item[0] = false; } await wait_timer(ca.cfg5_millisecond.Value >= 20 && ca.cfg5_millisecond.Value <= 4000 ? ca.cfg5_millisecond.Value : 100); if (disconnected[0] == true) return; } if (seeds == "nil") seeds = "?"; } if (first_pending == true) { await wait_frames(2); } if (item[0] == null && first_pending == false) { ca.mls.LogInfo("first engine"); first_pending = true; Transform tr = Object.Instantiate<Transform>(StartOfRound.Instance.VehiclesList[0].GetComponentsInChildren<Transform>().First(_ => _.name == "Meshes")); var sl = new List<string>() { "Headlights", "Cube.002", "Cube.003", "HoodTrigger", "CarHoodFire", "DoorTrigger", "Collider", "DoorTrigger", "Collider", "Links", "ClosedCollider", "ClosedTrigger", "OpenCollider", "OpenTrigger", "DoorLeft", "ShiftToReverseTrigger", "ShiftToDriveTrigger", "ShiftToParkTrigger", "CarKeyTurnedPos", "CarKeyNotTurnedPos", "StartIgnition", "StopIgnition", "InteractionBlockers", "Cube", "Cube", "WindshieldInteractBlocker", "Spring.001", "FrontCabinLight", "BackLights", "SparkParticle (1)", }; foreach (Transform ctr in tr.GetComponentsInChildren<Transform>(true)) { foreach (string s in sl) { if (ctr.name == s) { Object.Destroy(ctr.gameObject); sl.Remove(s); break; } } if (ctr.name.Contains("Audio") == true) { Object.Destroy(ctr.gameObject); } else if (ctr.name == "MainBodyDestroyed") { tree[0] = Object.Instantiate<Transform>(ctr); tree[0].gameObject.SetActive(false); tree[0].name = "ToyCar(Exploded)"; Object.DontDestroyOnLoad(tree[0]); Object.Destroy(ctr.gameObject); } else if (ctr.name == "CarHoodMesh") { Object.Destroy(ctr.GetComponent<Animator>()); Object.Destroy(ctr.GetComponent<PlayAudioAnimationEvent>()); } } await wait_frames(1); tree[1] = tr; tree[1].gameObject.SetActive(false); tree[1].name = "ToyCar"; Object.DontDestroyOnLoad(tree[1]); ca.mls.LogInfo("saved cruiser scrap transforms"); //engine item[0] = "V-type engine"; take[0] = __instance.itemProperties.grabSFX; give[0] = __instance.itemProperties.dropSFX; position[0] = __instance.itemProperties.positionOffset; rotation[0] = __instance.itemProperties.rotationOffset; //cruiser Item temp = StartOfRound.Instance.allItemsList.itemsList.First(_ => _.name == "ToyCube"); item[1] = "Toy car"; take[1] = temp.grabSFX; give[1] = temp.dropSFX; position[1] = new Vector3(-0.035f, 0.2f, 0.02f); position[2] = new Vector3(-0.02f, 0.31f, -0.008f); //-0.035 0.295 -0.0075 rotation[1] = new Vector3(0f, 16f, 76f); first_pending = false; ca.mls.LogInfo("saved engine/cruiser scrap variables"); } if (item[0] != null) { Shion cr; if (GameNetworkManager.Instance.disableSteam == false && lobbyid != 0uL && __instance.GetComponent<NetworkObject>() != null) { UInt64 n64; if (ca.cfg5_saveseeds.Value == true && seeds != "nil" && seeds != "?" && seeds.Contains(__instance.GetComponent<NetworkObject>().NetworkObjectId + ".") == true) { if (first_item[2] == true) ca.mls.LogInfo("custom_random = new Shion(saved seed)"); int start = seeds.IndexOf(__instance.GetComponent<NetworkObject>().NetworkObjectId + "."); int n = __instance.GetComponent<NetworkObject>().NetworkObjectId.ToString().Length + 1; n64 = UInt64.Parse(seeds.Substring(start + n, seeds.IndexOf("/", start) - (start + n))); } else { if (first_item[2] == true) ca.mls.LogInfo("custom_random = new Shion(lobbyid+networkobjectid)"); n64 = lobbyid + __instance.GetComponent<NetworkObject>().NetworkObjectId; } cr = new Shion(n64); if (ca.cfg5_saveseeds.Value == true) { __instance.gameObject.AddComponent<ItemTypeSeed>(); __instance.gameObject.GetComponent<ItemTypeSeed>().seed = n64.ToString(); } } else { if (first_item[2] == true) ca.mls.LogInfo("custom_random = new Shion()"); cr = new Shion(); } first_item[2] = false; int num = (cr.next32mm(0, 100) < ca.cfg5_cruiser.Value ? 1 : 0); if (num == 1) { BoxCollider box = __instance.GetComponent<BoxCollider>(); if (cr.next32mm(0, 100) < ca.cfg5_cruiseralt.Value) { Object.Instantiate<Transform>(tree[0]).SetParent(__instance.transform); Transform tr = __instance.GetComponentsInChildren<Transform>(true)[2]; tr.localPosition = new Vector3(21.5f, 0f, 0f); tr.localEulerAngles = new Vector3(0f, 90f, 180f); tr.localScale = new Vector3(-6f, -6f, -6f); tr.gameObject.SetActive(true); box.center = new Vector3(5f, 0f, 0f); box.size = new Vector3(27.5f, 23f, 60f); __instance.GetComponentInChildren<ScanNodeProperties>().transform.localPosition = new Vector3(5f, 0f, 0f); __instance.itemProperties.positionOffset = position[2]; } else { Object.Instantiate<Transform>(tree[1]).SetParent(__instance.transform); Transform tr = __instance.GetComponentsInChildren<Transform>(true)[2]; tr.localPosition = new Vector3(3.5f, 0f, 0f); tr.localEulerAngles = new Vector3(0f, 180f, 90f); tr.localScale = new Vector3(-6f, -6f, -6f); tr.gameObject.SetActive(true); box.center = new Vector3(2.5f, 0f, 0f); box.size = new Vector3(33f, 23f, 60f); __instance.GetComponentInChildren<ScanNodeProperties>().transform.localPosition = new Vector3(2.5f, 0f, 0f); __instance.itemProperties.positionOffset = position[1]; } __instance.GetComponent<MeshFilter>().mesh = null; } else { __instance.itemProperties.positionOffset = position[0]; } __instance.itemProperties.itemName = item[num]; __instance.GetComponentInChildren<ScanNodeProperties>().headerText = item[num]; __instance.itemProperties.grabSFX = take[num]; __instance.itemProperties.dropSFX = give[num]; __instance.itemProperties.rotationOffset = rotation[num]; } } } private static async Task wait_frames(int frames) { int previous_frames = Time.frameCount; while ((Time.frameCount > (previous_frames + frames)) == false) { await Task.Delay(4); } } private static async Task wait_timer(int maxwell) { int n = 0; while (seeds == "nil" && n < maxwell && disconnected[0] == false) { n = n + 1; await Task.Delay(4); } if (disconnected[0] == true) { if (disconnected[1] == false) { ca.mls.LogMessage("disconnected before wait_timer ended"); disconnected[1] = true; } return; } if (first_item[1] == true) { ca.mls.LogMessage(seeds == "nil" ? "timer ended before receiving seeds (" + n + "/" + ca.cfg5_millisecond.Value + ")" : "received seeds before timer ended (" + n + "/" + ca.cfg5_millisecond.Value + ")"); first_item[1] = false; } } [HarmonyPatch(typeof(GrabbableObject), "PlayDropSFX"), HarmonyPrefix] private static void pre4(ref GrabbableObject __instance) { if (item[0] != null && __instance != null && __instance.itemProperties.name == "EnginePart1" && __instance.GetComponentInChildren<ScanNodeProperties>() != null) { __instance.itemProperties.dropSFX = (__instance.GetComponentInChildren<ScanNodeProperties>().headerText == item[1] ? give[1] : give[0]); } } [HarmonyPatch(typeof(PlayerControllerB), "BeginGrabObject"), HarmonyPostfix] private static void pst5(ref GrabbableObject ___currentlyGrabbingObject) { if (item[0] != null && ___currentlyGrabbingObject != null && ___currentlyGrabbingObject.itemProperties.name == "EnginePart1" && ___currentlyGrabbingObject.GetComponentInChildren<ScanNodeProperties>() != null) { int num = (___currentlyGrabbingObject.GetComponentInChildren<ScanNodeProperties>().headerText == item[1] ? 1 : 0); ___currentlyGrabbingObject.itemProperties.grabSFX = take[num]; ___currentlyGrabbingObject.itemProperties.positionOffset = (((___currentlyGrabbingObject.GetComponentsInChildren<Transform>().Length > 2) && ___currentlyGrabbingObject.GetComponentsInChildren<Transform>()[2].name.Contains("Exploded") == true) ? position[2] : position[num]); ___currentlyGrabbingObject.itemProperties.rotationOffset = rotation[num]; ___currentlyGrabbingObject.itemProperties.itemName = ___currentlyGrabbingObject.GetComponentInChildren<ScanNodeProperties>().headerText; } } [HarmonyPatch(typeof(PlayerControllerB), "ScrollMouse_performed"), HarmonyPrefix] private static void pre5(ref InputAction.CallbackContext context, ref int ___currentItemSlot, ref GrabbableObject[] ___ItemSlots) { if (item[0] != null) { bool foward = false; if (context.ReadValue<float>() > 0f) foward = true; int next = ___currentItemSlot - 1; if (foward == true) next = (___currentItemSlot + 1) % ___ItemSlots.Length; else if (___currentItemSlot == 0) next = ___ItemSlots.Length - 1; if (next >= 0 && next < ___ItemSlots.Length && ___ItemSlots[next] != null && ___ItemSlots[next].itemProperties.name == "EnginePart1" && ___ItemSlots[next].GetComponentInChildren<ScanNodeProperties>() != null) { ___ItemSlots[next].itemProperties.grabSFX = (___ItemSlots[next].GetComponentInChildren<ScanNodeProperties>().headerText == item[1] ? take[1] : take[0]); ___ItemSlots[next].itemProperties.itemName = ___ItemSlots[next].GetComponentInChildren<ScanNodeProperties>().headerText; } } } [HarmonyPatch(typeof(PlayerControllerB), "ScrollMouse_performed"), HarmonyPostfix] private static void pst6(ref int ___currentItemSlot, ref GrabbableObject[] ___ItemSlots) { if (item[0] != null && ___ItemSlots[___currentItemSlot] != null && ___ItemSlots[___currentItemSlot].itemProperties.name == "EnginePart1" && ___ItemSlots[___currentItemSlot].GetComponentInChildren<ScanNodeProperties>() != null) { int num = (___ItemSlots[___currentItemSlot].GetComponentInChildren<ScanNodeProperties>().headerText == item[1] ? 1 : 0); ___ItemSlots[___currentItemSlot].itemProperties.positionOffset = (((___ItemSlots[___currentItemSlot].GetComponentsInChildren<Transform>().Length > 2) && ___ItemSlots[___currentItemSlot].GetComponentsInChildren<Transform>()[2].name.Contains("Exploded") == true) ? position[2] : position[num]); ___ItemSlots[___currentItemSlot].itemProperties.rotationOffset = rotation[num]; } } [HarmonyPatch(typeof(HUDManager), "DisplayNewScrapFound"), HarmonyPrefix] private static void pre6(ref List<GrabbableObject> ___itemsToBeDisplayed) { if (item[0] != null && ___itemsToBeDisplayed[0] != null && ___itemsToBeDisplayed[0].itemProperties.name == "EnginePart1" && ___itemsToBeDisplayed[0].GetComponentInChildren<ScanNodeProperties>() != null) { ___itemsToBeDisplayed[0].itemProperties.itemName = ___itemsToBeDisplayed[0].GetComponentInChildren<ScanNodeProperties>().headerText; ___itemsToBeDisplayed[0].itemProperties.spawnPrefab.GetComponentInChildren<ScanNodeProperties>().headerText = (___itemsToBeDisplayed[0].GetComponentsInChildren<Transform>(true).Length >= 3 && ___itemsToBeDisplayed[0].GetComponentsInChildren<Transform>(true)[2] != null && (___itemsToBeDisplayed[0].GetComponentsInChildren<Transform>(true)[2].name == "ToyCar(Clone)" || ___itemsToBeDisplayed[0].GetComponentsInChildren<Transform>(true)[2].name == "ToyCar(Exploded)(Clone)") ? ___itemsToBeDisplayed[0].GetComponentsInChildren<Transform>(true)[2].name : ___itemsToBeDisplayed[0].GetComponentInChildren<ScanNodeProperties>().headerText); } } [HarmonyPatch(typeof(HUDManager), "DisplayNewScrapFound"), HarmonyTranspiler] private static IEnumerable<CodeInstruction> trn3(IEnumerable<CodeInstruction> Instrs) { var l = new List<CodeInstruction>(Instrs); for (int n = 0; n < l.Count; n = n + 1) { if (n < (l.Count - 1) && l[n + 1].ToString() == "callvirt UnityEngine.Renderer[] UnityEngine.GameObject::GetComponentsInChildren()") { yield return new CodeInstruction(OpCodes.Ldloc_0); yield return new CodeInstruction(OpCodes.Call, typeof(cruiser_additions).GetMethod("display_cruiser")); } yield return l[n]; //ca.mls.LogInfo(l[n].ToString()); } } public static void display_cruiser(GameObject displayingObject) { if (item[0] != null && displayingObject.name == "EnginePart(Clone)" && displayingObject.GetComponentInChildren<ScanNodeProperties>() != null) { if (displayingObject.GetComponentInChildren<ScanNodeProperties>().headerText == item[1]) { displayingObject.GetComponent<MeshFilter>().mesh = null; } else if (displayingObject.GetComponentInChildren<ScanNodeProperties>().headerText == "ToyCar(Clone)") { Object.Instantiate<Transform>(tree[1]).SetParent(displayingObject.transform); Transform tr = displayingObject.GetComponentsInChildren<Transform>(true)[2]; tr.localPosition = new Vector3(3.5f, 0f, 0f); tr.localEulerAngles = new Vector3(0f, 180f, 90f); tr.localScale = new Vector3(-6f, -6f, -6f); tr.gameObject.SetActive(true); displayingObject.GetComponent<MeshFilter>().mesh = null; } else if (displayingObject.GetComponentInChildren<ScanNodeProperties>().headerText == "ToyCar(Exploded)(Clone)") { Object.Instantiate<Transform>(tree[0]).SetParent(displayingObject.transform); Transform tr = displayingObject.GetComponentsInChildren<Transform>(true)[2]; tr.localPosition = new Vector3(21.5f, 0f, 0f); tr.localEulerAngles = new Vector3(0f, 90f, 180f); tr.localScale = new Vector3(-6f, -6f, -6f); tr.gameObject.SetActive(true); displayingObject.GetComponent<MeshFilter>().mesh = null; } } } [HarmonyPatch(typeof(StartOfRound), "Awake"), HarmonyPostfix] private static void pst7() { if (GameNetworkManager.Instance.disableSteam == false) { disconnected = new bool[] {false, false}; if (GameNetworkManager.Instance.currentLobby.HasValue == true) { lobbyid = (GameNetworkManager.Instance.currentLobby.Value.Id % 1000000000); ca.mls.LogInfo(lobbyid); } else { ca.mls.LogError("current lobby id is null"); } } } // // rotation quaternions must be unit length + start position/rotation // [HarmonyPatch(typeof(StartOfRound), "SyncShipUnlockablesClientRpc"), HarmonyPostfix] private static void pst8(StartOfRound __instance) { if (ca.cfg7_unlockables.Value == true && __instance.attachedVehicle != null && GameNetworkManager.Instance.isHostingGame == false) { typeof(VehicleController).GetField("magnetTargetRotation", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(__instance.attachedVehicle, new Quaternion(0f, 0.7071068f, 0f, 0.7071068f)); typeof(VehicleController).GetField("magnetStartRotation", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(__instance.attachedVehicle, new Quaternion(0f, 0.7071068f, 0f, 0.7071068f)); typeof(VehicleController).GetField("magnetTargetPosition", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(__instance.attachedVehicle, (__instance.magnetPoint.position - __instance.magnetPoint.forward * 5.5f)); typeof(VehicleController).GetField("magnetStartPosition", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(__instance.attachedVehicle, (__instance.magnetPoint.position + __instance.magnetPoint.forward * 5f)); ca.mls.LogInfo("attached cruiser to the magnet"); } } // // QueryTriggerInteraction // [HarmonyPatch(typeof(VehicleController), "CollectItemsInTruck"), HarmonyTranspiler] private static IEnumerable<CodeInstruction> trn4(IEnumerable<CodeInstruction> Instrs) { var l = new List<CodeInstruction>(Instrs); for (int n = 0; n < l.Count; n = n + 1) { if (ca.cfg8_collide.Value == true && n < (l.Count - 1) && l[n + 1].ToString() == "call static UnityEngine.Collider[] UnityEngine.Physics::OverlapSphere(UnityEngine.Vector3 position, float radius, int layerMask, UnityEngine.QueryTriggerInteraction queryTriggerInteraction)") { yield return new CodeInstruction(OpCodes.Ldc_I4_2); } else { yield return l[n]; } //ca.mls.LogInfo(l[n].ToString()); } } // // cruiser 1up // [HarmonyPatch(typeof(Terminal), "LoadNewNodeIfAffordable"), HarmonyTranspiler] private static IEnumerable<CodeInstruction> trn5(IEnumerable<CodeInstruction> Instrs) { var l = new List<CodeInstruction>(Instrs); for (int n = 0; n < l.Count; n = n + 1) { if (ca.cfg9_1up.Value == true && l[n].ToString() == "call static VehicleController UnityEngine.Object::FindObjectOfType()") { yield return new CodeInstruction(OpCodes.Call, typeof(cruiser_additions).GetMethod("return_exploded")); n = n + 1; } else { yield return l[n]; } //ca.mls.LogInfo(l[n].ToString()); } } public static bool return_exploded() { return (bool)Object.FindObjectsOfType<VehicleController>().FirstOrDefault(_ => (_ != null && _.carDestroyed == false)); } // // network syncing // private static bool sync = false; private static string hostmoveitems = "nil"; private static string hostscraponly = "nil"; private static string seeds = "nil"; private static bool[] disconnected = new bool[] {false, false}; [HarmonyPatch(typeof(PlayerControllerB), "ConnectClientToPlayerObject"), HarmonyPostfix] private static void pst9() { if (sync == false) { if (NetworkManager.Singleton.IsHost == true) { NetworkManager.Singleton.CustomMessagingManager.RegisterNamedMessageHandler("4902.Cruiser_Additions-Host", host_receive); } else { ca.mls.LogInfo("requesting message from host"); NetworkManager.Singleton.CustomMessagingManager.RegisterNamedMessageHandler("4902.Cruiser_Additions-Client", client_receive); FastBufferWriter w = new FastBufferWriter(0, Unity.Collections.Allocator.Temp); NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("4902.Cruiser_Additions-Host", NetworkManager.ServerClientId, w); w.Dispose(); } sync = true; } } private static void host_receive(ulong id, FastBufferReader r) { if (NetworkManager.Singleton.IsHost == true) { try { ca.mls.LogInfo("received request from client"); string message = ((ca.cfg2_moveitems.Value == 2 || ca.cfg2_moveitems.Value == 3) ? "true" : "false") + "^" + ca.cfg2_scraponly.Value.ToString().ToLower() + "^" + seeds; ca.mls.LogInfo("sending message " + message); FastBufferWriter w = new FastBufferWriter(FastBufferWriter.GetWriteSize(message), Unity.Collections.Allocator.Temp); w.WriteValueSafe(message); NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("4902.Cruiser_Additions-Client", id, w, NetworkDelivery.ReliableFragmentedSequenced); w.Dispose(); } catch (System.Exception error) { ca.mls.LogError("Error writing strings while syncing: " + error); } } } private static void client_receive(ulong id, FastBufferReader r) { if (NetworkManager.Singleton.IsClient == true) { try { string message; r.ReadValueSafe(out message, false); ca.mls.LogInfo("client received message " + message); string[] s = message.Split(new char[]{'^'}, 3); hostmoveitems = s[0]; hostscraponly = s[1]; seeds = s[2]; } catch (System.Exception error) { ca.mls.LogError("Error reading strings while syncing: " + error); } } } [HarmonyPatch(typeof(GameNetworkManager), "Disconnect"), HarmonyPrefix] private static void pre8() { disconnected[0] = true; } [HarmonyPatch(typeof(GameNetworkManager), "Disconnect"), HarmonyPostfix] private static void pst10() { sync = false; hostmoveitems = "nil"; hostscraponly = "nil"; seeds = "nil"; lobbyid = 0uL; first_item = new bool[] {true, true, true}; saved_engine = ""; loaded_engine = new List<string>(); if (StartOfRound.Instance != null) { NetworkManager.Singleton.CustomMessagingManager.UnregisterNamedMessageHandler("4902.Cruiser_Additions-Host"); NetworkManager.Singleton.CustomMessagingManager.UnregisterNamedMessageHandler("4902.Cruiser_Additions-Client"); } } // // saving/loading // private static string saved_engine = ""; private static List<string> loaded_engine = new List<string>(); [HarmonyPatch(typeof(GameNetworkManager), "SaveItemsInShip"), HarmonyTranspiler] private static IEnumerable<CodeInstruction> trn6(IEnumerable<CodeInstruction> Instrs) { var l = new List<CodeInstruction>(Instrs); for (int n = 0; n < l.Count; n = n + 1) { yield return l[n]; if (l[n].ToString() == "callvirt virtual void System.Collections.Generic.List<UnityEngine.Vector3>::Add(UnityEngine.Vector3 item)") { yield return new CodeInstruction(OpCodes.Ldloc_0); yield return new CodeInstruction(OpCodes.Ldloc, 6); yield return new CodeInstruction(OpCodes.Call, typeof(cruiser_additions).GetMethod("save_engine")); } //ca.mls.LogInfo(l[n].ToString()); } } public static void save_engine(GrabbableObject[] items, int index) { if (ca.cfg5_saveseeds.Value == true && GameNetworkManager.Instance.isHostingGame == true && GameNetworkManager.Instance.disableSteam == false && lobbyid != 0uL && items[index].itemProperties.name == "EnginePart1") { if (items[index].GetComponent<ItemTypeSeed>() != null) { ItemTypeSeed component = items[index].GetComponent<ItemTypeSeed>(); saved_engine = saved_engine + component.seed + "/"; } else if (items[index].GetComponent<NetworkObject>() != null) { saved_engine = saved_engine + (lobbyid + items[index].GetComponent<NetworkObject>().NetworkObjectId).ToString() + "/"; ca.mls.LogInfo("ItemTypeSeed is null! saving seed for this item as lobbyid+networkobjectid"); } else { saved_engine = saved_engine + new Shion().next32mm(1, 101).ToString() + "/"; ca.mls.LogInfo("ItemTypeSeed and NetworkObject are null! saving seed for this item as a random number from 1 to 100"); } } } [HarmonyPatch(typeof(GameNetworkManager), "SaveGame"), HarmonyPostfix] private static void pst11() { if (ca.cfg5_saveseeds.Value == true && GameNetworkManager.Instance.isHostingGame == true && GameNetworkManager.Instance.disableSteam == false && StartOfRound.Instance.inShipPhase == true && StartOfRound.Instance.isChallengeFile == false) { try { if (saved_engine != "" && saved_engine.EndsWith("/") == true) saved_engine = saved_engine.Substring(0, saved_engine.Length - 1); ca.mls.LogInfo("saving " + saved_engine); ES3.Save("4902.Cruiser_Additions-1", saved_engine, GameNetworkManager.Instance.currentSaveFileName); saved_engine = ""; } catch (System.Exception error) { ca.mls.LogError("Error saving item types: " + error); } } } [HarmonyPatch(typeof(StartOfRound), "LoadShipGrabbableItems"), HarmonyTranspiler] private static IEnumerable<CodeInstruction> trn7(IEnumerable<CodeInstruction> Instrs) { var l = new List<CodeInstruction>(Instrs); for (int n = 0; n < l.Count; n = n + 1) { yield return l[n]; if (l[n].ToString() == "callvirt void Unity.Netcode.NetworkObject::Spawn(bool destroyWithScene)") { yield return new CodeInstruction(OpCodes.Ldloc_0); yield return new CodeInstruction(OpCodes.Call, typeof(cruiser_additions).GetMethod("load_engine")); } //ca.mls.LogInfo(l[n].ToString()); } } public static void load_engine(GrabbableObject item) { if (ca.cfg5_saveseeds.Value == true && GameNetworkManager.Instance.isHostingGame == true && GameNetworkManager.Instance.disableSteam == false && lobbyid != 0uL && item.itemProperties.name == "EnginePart1") { if (item.GetComponent<NetworkObject>() != null) { loaded_engine.Add(item.GetComponent<NetworkObject>().NetworkObjectId.ToString()); } else { loaded_engine.Add("nil"); ca.mls.LogInfo("NetworkObject is null! the seed can't be loaded for this item"); } } } [HarmonyPatch(typeof(StartOfRound), "Start"), HarmonyPostfix] private static void pst12() { if (ca.cfg5_saveseeds.Value == true && GameNetworkManager.Instance.isHostingGame == true && GameNetworkManager.Instance.disableSteam == false) { try { string temp = ES3.Load("4902.Cruiser_Additions-1", GameNetworkManager.Instance.currentSaveFileName, "nil"); ca.mls.LogInfo("loaded " + temp); string[] s = temp.Split(new char[]{'/'}); if (s[0] != "nil" && s[0] != "" && s.Length == loaded_engine.Count) { seeds = ""; for (int n = 0; n < loaded_engine.Count; n = n + 1) { seeds = seeds + loaded_engine[n] + "." + s[n] + "/"; } ca.mls.LogInfo("current networkobjectids + saved seeds " + seeds); } loaded_engine = new List<string>(); } catch (System.Exception error) { ca.mls.LogError("Error loading item type seeds: " + error); } } } } // // custom component // public class ItemTypeSeed : MonoBehaviour { public string guid = ca.harmony.Id; public string seed; } // // custom random (better than seeded System.Random) // public class Shion { private UInt64[] state; public Shion() { System.Security.Cryptography.RandomNumberGenerator rand = System.Security.Cryptography.RandomNumberGenerator.Create(); byte[] randBytes = new byte[8]; rand.GetBytes(randBytes, 0, 8); UInt64 seed = System.BitConverter.ToUInt64(randBytes, 0); xorshift256_init(seed); } public Shion(UInt64 seed) { xorshift256_init(seed); } //next public int next32mm(int min, int max) { uint value = next32(); double scale = ((double)(max - min)) / UInt32.MaxValue; return (int)(min + (value * scale)); } public byte[] next8() { UInt64 nextInt64 = xoshiro256ss(); return System.BitConverter.GetBytes(nextInt64); } public UInt32 next32() { byte[] randBytes = next8(); return System.BitConverter.ToUInt32(randBytes, 0); } public UInt64 next64() { return xoshiro256ss(); } public double next01() { UInt64 nextInt64 = xoshiro256ss(); //0 inclusive, 1 exclusive return (double)nextInt64 / (double)(UInt64.MaxValue); } //misc private UInt64 splitmix64(UInt64 partialstate) { partialstate = partialstate + 0x9E3779B97f4A7C15; partialstate = (partialstate ^ (partialstate >> 30)) * 0xBF58476D1CE4E5B9; partialstate = (partialstate ^ (partialstate >> 27)) * 0x94D049BB133111EB; return partialstate ^ (partialstate >> 31); } private void xorshift256_init(UInt64 seed) { UInt64[] result = new UInt64[4]; result[0] = splitmix64(seed); result[1] = splitmix64(result[0]); result[2] = splitmix64(result[1]); result[3] = splitmix64(result[2]); state = result; } private UInt64 rotl64(UInt64 x, int k) { return (x << k) | (x >> (64 - k)); } private UInt64 xoshiro256ss() { UInt64 result = rotl64(state[1] * 5, 7) * 9; UInt64 t = state[1] << 17; state[2] ^= state[0]; state[3] ^= state[1]; state[1] ^= state[2]; state[0] ^= state[3]; state[2] ^= t; state[3] = rotl64(state[3], 45); return result; } } }