IEnumerator LoopRoutine() { MopSettings.IsModActive = true; FramerateRecorder rec = gameObject.AddComponent <FramerateRecorder>(); rec.Initialize(); while (MopSettings.IsModActive) { // Ticks make sure that MOP is still up and running. // If the ticks didn't update, that means this routine stopped. ++ticks; if (!itemInitializationDelayDone) { // We are slightly delaying the initialization, so all items have chance to set in place, because f**k MSC and its physics. waitTime++; if (waitTime >= WaitDone) { FinishLoading(); } } isPlayerAtYard = MOP.ActiveDistance.Value == 0 ? Vector3.Distance(player.position, placeManager[0].transform.position) < 100 : Vector3.Distance(player.position, placeManager[0].transform.position) < 100 * MopSettings.ActiveDistanceMultiplicationValue; // When player is in any of the sectors, MOP will act like the player is at yard. if (SectorManager.Instance.IsPlayerInSector()) { inSectorMode = true; isPlayerAtYard = true; } else { inSectorMode = false; } yield return(null); int i; long half = worldObjectManager.Count >> 1; // World Objects. for (i = 0; i < worldObjectManager.Count; ++i) { if (i == half) { yield return(null); } try { GenericObject worldObject = worldObjectManager[i]; // Check if object was destroyed (mostly intended for AI pedastrians). if (worldObject.GameObject == null) { worldObjectManager.Remove(worldObject); continue; } if (SectorManager.Instance.IsPlayerInSector() && SectorManager.Instance.SectorRulesContains(worldObject.GameObject.name)) { worldObject.GameObject.SetActive(true); continue; } // Should the object be disabled when the player leaves the house? if (worldObject.DisableOn.HasFlag(DisableOn.PlayerAwayFromHome) || worldObject.DisableOn.HasFlag(DisableOn.PlayerInHome)) { if (worldObject.GameObject.name == "NPC_CARS" && inSectorMode) { continue; } if (worldObject.GameObject.name == "COMPUTER" && worldObject.GameObject.transform.Find("SYSTEM").gameObject.activeSelf) { continue; } worldObject.Toggle(worldObject.DisableOn.HasFlag(DisableOn.PlayerAwayFromHome) ? isPlayerAtYard : !isPlayerAtYard); } else if (worldObject.DisableOn.HasFlag(DisableOn.Distance)) { // The object will be disabled, if the player is in the range of that object. worldObject.Toggle(IsEnabled(worldObject.transform, worldObject.Distance)); } } catch (Exception ex) { ExceptionManager.New(ex, false, "WORLD_OBJECT_TOGGLE_ERROR"); } } // Safe mode prevents toggling elemenets that MAY case some issues (vehicles, items, etc.) if (MopSettings.Mode == PerformanceMode.Safe) { yield return(new WaitForSeconds(.7f)); continue; } // So we create two separate lists - one is meant to enable, and second is ment to disable them, // Why? // If we enable items before enabling vehicle inside of which these items are supposed to be, they'll fall through to ground. // And the opposite happens if we disable vehicles before disabling items. // So if we are disabling items, we need to do that BEFORE we disable vehicles. // And we need to enable items AFTER we enable vehicles. itemsToEnable.Clear(); itemsToDisable.Clear(); half = ItemsManager.Instance.Count >> 1; for (i = 0; i < ItemsManager.Instance.Count; ++i) { if (i == half) { yield return(null); } // Safe check if somehow the i gets bigger than array length. if (i >= ItemsManager.Instance.Count) { break; } try { ItemBehaviour item = ItemsManager.Instance[i]; if (item == null || item.gameObject == null) { itemsToRemove.Add(item); continue; } // Check the mode in what MOP is supposed to run and adjust to it. bool toEnable = true; if (MopSettings.Mode == 0) { toEnable = IsEnabled(item.transform, FsmManager.IsPlayerInCar() && !isPlayerAtYard ? 20 : 150); } else { toEnable = IsEnabled(item.transform, 150); } if (toEnable) { item.ToggleChangeFix(); if (item.ActiveSelf) { continue; } itemsToEnable.Add(item); } else { if (!item.ActiveSelf) { continue; } itemsToDisable.Add(item); } if (item.rb != null && item.rb.IsSleeping()) { if (item.IsPartMagnetAttached()) { continue; } if (CompatibilityManager.IsInBackpack(item)) { continue; } item.rb.isKinematic = true; } } catch (Exception ex) { ExceptionManager.New(ex, false, "ITEM_TOGGLE_GATHER_ERROR"); } } // Items To Disable int full = itemsToDisable.Count; if (full > 0) { half = itemsToDisable.Count >> 1; for (i = 0; i < full; ++i) { if (half != 0 && i == half) { yield return(null); } try { itemsToDisable[i].Toggle(false); } catch (Exception ex) { ExceptionManager.New(ex, false, "ITEM_TOGGLE_DISABLE_ERROR - " + itemsToDisable[i] != null ? itemsToDisable[i].gameObject.name : "null"); } } } // Vehicles (new) half = vehicleManager.Count >> 1; for (i = 0; i < vehicleManager.Count; ++i) { if (half != 0 && i == half) { yield return(null); } try { if (vehicleManager[i] == null) { vehicleManager.RemoveAt(i); continue; } float distance = Vector3.Distance(player.transform.position, vehicleManager[i].transform.position); float toggleDistance = MOP.ActiveDistance.Value == 0 ? MopSettings.UnityCarActiveDistance : MopSettings.UnityCarActiveDistance * MopSettings.ActiveDistanceMultiplicationValue; switch (vehicleManager[i].VehicleType) { case VehiclesTypes.Satsuma: Satsuma.Instance.ToggleElements(distance); vehicleManager[i].ToggleEventSounds(distance < 3); break; case VehiclesTypes.Jonnez: vehicleManager[i].ToggleEventSounds(distance < 2); break; } vehicleManager[i].ToggleUnityCar(IsVehiclePhysicsEnabled(distance, toggleDistance)); vehicleManager[i].Toggle(IsVehicleEnabled(distance)); } catch (Exception ex) { ExceptionManager.New(ex, false, $"VEHICLE_TOGGLE_ERROR_{i}"); } } // Items To Enable full = itemsToEnable.Count; if (full > 0) { half = full >> 1; for (i = 0; i < full; ++i) { if (half != 0 && i == half) { yield return(null); } try { itemsToEnable[i].Toggle(true); } catch (Exception ex) { ExceptionManager.New(ex, false, "ITEM_TOGGLE_ENABLE_ERROR - " + itemsToEnable[i] != null ? itemsToDisable[i].gameObject.name : "null"); } } } // Places (New) full = placeManager.Count; half = full >> 1; for (i = 0; i < full; ++i) { if (i == half) { yield return(null); } try { if (SectorManager.Instance.IsPlayerInSector() && SectorManager.Instance.SectorRulesContains(placeManager[i].GetName())) { continue; } placeManager[i].ToggleActive(IsPlaceEnabled(placeManager[i].transform, placeManager[i].GetToggleDistance())); } catch (Exception ex) { ExceptionManager.New(ex, false, $"PLACE_TOGGLE_ERROR_{i}"); } } // Remove items that don't exist anymore. if (itemsToRemove.Count > 0) { for (i = itemsToRemove.Count - 1; i >= 0; --i) { ItemsManager.Instance.RemoveAt(i); } itemsToRemove.Clear(); } yield return(new WaitForSeconds(.7f)); if (retries > 0 && !restartSucceedMessaged) { restartSucceedMessaged = true; ModConsole.Log("<color=green>[MOP] Restart succeeded!</color>"); } } }