using OWML.Common; using QSB.OrbSync.TransformSync; using QSB.OrbSync.WorldObjects; using QSB.Utility; using System; using System.Collections.Generic; using System.Linq; using UnityEngine; namespace QSB.WorldSync { public static class QSBWorldSync { public static List OldOrbList { get; set; } = new(); public static List OldDialogueTrees { get; set; } = new(); public static Dictionary DialogueConditions { get; } = new(); public static List ShipLogFacts { get; } = new(); private static readonly List WorldObjects = new(); private static readonly Dictionary WorldObjectsToUnityObjects = new(); public static IEnumerable GetWorldObjects() where TWorldObject : IWorldObject => WorldObjects.OfType(); public static TWorldObject GetWorldFromId(int id) where TWorldObject : IWorldObject { var worldObjects = GetWorldObjects().ToList(); if (id < 0 || id >= worldObjects.Count) { DebugLog.ToConsole($"Warning - Tried to find {typeof(TWorldObject).Name} id {id}. Count is {worldObjects.Count}.", MessageType.Warning); return default; } return worldObjects[id]; } public static TWorldObject GetWorldFromUnity(MonoBehaviour unityObject) where TWorldObject : IWorldObject { if (unityObject == null) { DebugLog.ToConsole($"Error - Trying to run GetWorldFromUnity with a null unity object! TWorldObject:{typeof(TWorldObject).Name}, TUnityObject:NULL, Stacktrace:\r\n{Environment.StackTrace}", MessageType.Error); return default; } if (!QSBCore.IsInMultiplayer) { DebugLog.ToConsole($"Warning - Trying to run GetWorldFromUnity while not in multiplayer! TWorldObject:{typeof(TWorldObject).Name}, TUnityObject:{unityObject.GetType().Name}, Stacktrace:\r\n{Environment.StackTrace}", MessageType.Warning); return default; } if (!WorldObjectsToUnityObjects.TryGetValue(unityObject, out var returnObject)) { DebugLog.ToConsole($"Error - WorldObjectsToUnityObjects does not contain \"{unityObject.name}\"! TWorldObject:{typeof(TWorldObject).Name}, TUnityObject:{unityObject.GetType().Name}, Stacktrace:\r\n{Environment.StackTrace}", MessageType.Error); return default; } if (returnObject == null) { DebugLog.ToConsole($"Error - World object for unity object {unityObject.name} is null! TWorldObject:{typeof(TWorldObject).Name}, TUnityObject:{unityObject.GetType().Name}, Stacktrace:\r\n{Environment.StackTrace}", MessageType.Error); return default; } return (TWorldObject)returnObject; } public static int GetIdFromUnity(MonoBehaviour unityObject) where TWorldObject : IWorldObject => GetWorldFromUnity(unityObject).ObjectId; public static int GetIdFromTypeSubset(TTypeSubset typeSubset) where TTypeSubset : IWorldObjectTypeSubset { var index = GetWorldObjects().ToList().IndexOf(typeSubset); if (index == -1) { DebugLog.ToConsole($"Warning - {typeSubset.Name} doesn't exist in list of {typeof(TTypeSubset).Name} !", MessageType.Warning); } return index; } public static void RemoveWorldObjects() where TWorldObject : IWorldObject { if (WorldObjects.Count == 0) { DebugLog.ToConsole($"Warning - Trying to remove WorldObjects of type {typeof(TWorldObject).Name}, but there are no WorldObjects!", MessageType.Warning); return; } var itemsToRemove = WorldObjects.Where(x => x is TWorldObject); foreach (var item in itemsToRemove) { try { WorldObjectsToUnityObjects.Remove(item.ReturnObject()); item.OnRemoval(); } catch (Exception e) { DebugLog.ToConsole($"Error - Exception in OnRemoval() for {item.GetType()}. Message : {e.Message}, Stack trace : {e.StackTrace}", MessageType.Error); } } WorldObjects.RemoveAll(x => x is TWorldObject); } public static IEnumerable GetUnityObjects() where TUnityObject : MonoBehaviour => Resources.FindObjectsOfTypeAll() .Where(x => x.gameObject.scene.name != null); public static void Init() where TWorldObject : WorldObject, new() where TUnityObject : MonoBehaviour { RemoveWorldObjects(); var list = GetUnityObjects().ToList(); //DebugLog.DebugWrite($"{typeof(TWorldObject).Name} init : {list.Count} instances.", MessageType.Info); for (var id = 0; id < list.Count; id++) { var obj = new TWorldObject { AttachedObject = list[id], ObjectId = id }; obj.Init(); WorldObjects.Add(obj); WorldObjectsToUnityObjects.Add(list[id], obj); } } public static void HandleSlotStateChange(NomaiInterfaceSlot slot, NomaiInterfaceOrb affectingOrb, bool state) { var slotList = GetWorldObjects().ToList(); if (!slotList.Any()) { return; } var qsbSlot = slotList.FirstOrDefault(x => x.AttachedObject == slot); if (qsbSlot == null) { DebugLog.ToConsole($"Error - No QSBOrbSlot found for {slot.name}!", MessageType.Error); return; } var orbSync = NomaiOrbTransformSync.OrbTransformSyncs.Where(x => x != null).FirstOrDefault(x => x.AttachedObject == affectingOrb.transform); if (orbSync == null) { DebugLog.ToConsole($"Error - No NomaiOrbTransformSync found for {affectingOrb.name} (For slot {slot.name})!", MessageType.Error); return; } if (orbSync.HasAuthority) { qsbSlot.HandleEvent(state, OldOrbList.IndexOf(affectingOrb)); } } public static void SetDialogueCondition(string name, bool state) { if (!QSBCore.IsHost) { DebugLog.ToConsole("Warning - Cannot write to condition dict when not server!", MessageType.Warning); return; } DialogueConditions[name] = state; } public static void AddFactReveal(string id, bool saveGame, bool showNotification) { if (!QSBCore.IsHost) { DebugLog.ToConsole("Warning - Cannot write to fact list when not server!", MessageType.Warning); return; } if (ShipLogFacts.Any(x => x.Id == id)) { return; } ShipLogFacts.Add(new FactReveal { Id = id, SaveGame = saveGame, ShowNotification = showNotification }); } } }