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 List(); public static List OldDialogueTrees { get; set; } = new List(); public static Dictionary DialogueConditions { get; } = new Dictionary(); public static List ShipLogFacts { get; } = new List(); private static readonly List WorldObjects = new List(); private static readonly Dictionary WorldObjectsToUnityObjects = new Dictionary(); public static IEnumerable GetWorldObjects() => WorldObjects.OfType(); public static TWorldObject GetWorldFromId(int id) { if (id < 0 || id >= GetWorldObjects().Count()) { DebugLog.ToConsole($"Warning - Tried to find {typeof(TWorldObject).Name} id {id}. Count is {GetWorldObjects().Count()}.", MessageType.Warning); return default; } return GetWorldObjects().ToList()[id]; } public static IWorldObject GetWorldFromUnity(MonoBehaviour unityObject) { if (!WorldObjectManager.AllReady) { return default; } if (unityObject == null) { DebugLog.ToConsole($"Error - Trying to run GetWorldFromUnity with a null unity object! TUnityObject:NULL", MessageType.Error); return default; } if (!QSBCore.IsInMultiplayer) { DebugLog.ToConsole($"Warning - Trying to run GetWorldFromUnity while not in multiplayer! TUnityObject:{unityObject.GetType().Name}", MessageType.Warning); return default; } if (!WorldObjectsToUnityObjects.ContainsKey(unityObject)) { DebugLog.ToConsole($"Error - WorldObjectsToUnityObjects does not contain \"{unityObject.name}\"! TUnityObject:{unityObject.GetType().Name}", MessageType.Error); return default; } var returnObject = WorldObjectsToUnityObjects[unityObject]; if (returnObject == null) { DebugLog.ToConsole($"Error - World object for unity object {unityObject.name} is null! TUnityObject:{unityObject.GetType().Name}", MessageType.Error); return default; } return returnObject; } public static TWorldObject GetWorldFromUnity(MonoBehaviour unityObject) where TWorldObject : IWorldObject { if (!WorldObjectManager.AllReady) { return default; } if (unityObject == null) { DebugLog.ToConsole($"Error - Trying to run GetWorldFromUnity with a null unity object! TWorldObject:{typeof(TWorldObject).Name}, TUnityObject:NULL", 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}", MessageType.Warning); return default; } if (!WorldObjectsToUnityObjects.ContainsKey(unityObject)) { DebugLog.ToConsole($"Error - WorldObjectsToUnityObjects does not contain \"{unityObject.name}\"! TWorldObject:{typeof(TWorldObject).Name}, TUnityObject:{unityObject.GetType().Name}", MessageType.Error); return default; } var returnObject = (TWorldObject)WorldObjectsToUnityObjects[unityObject]; if (returnObject == null) { DebugLog.ToConsole($"Error - World object for unity object {unityObject.name} is null! TWorldObject:{typeof(TWorldObject).Name}, TUnityObject:{unityObject.GetType().Name}", MessageType.Error); return default; } return returnObject; } public static int GetIdFromUnity(MonoBehaviour unityObject) where TWorldObject : IWorldObject => GetWorldFromUnity(unityObject).ObjectId; public static int GetIdFromTypeSubset(TTypeSubset typeSubset) { var index = GetWorldObjects().ToList().IndexOf(typeSubset); if (index == -1) { DebugLog.ToConsole($"Warning - {(typeSubset as IWorldObject).Name} doesn't exist in list of {typeof(TTypeSubset).Name} !", MessageType.Warning); } return index; } public static void RemoveWorldObjects() { if (WorldObjects == null || WorldObjects.Count == 0) { DebugLog.ToConsole($"Warning - Trying to remove WorldObjects of type {typeof(TWorldObject).Name}, but there are no WorldObjects!"); } var itemsToRemove = WorldObjects.Where(x => x is TWorldObject); foreach (var item in itemsToRemove) { if (item is null) { DebugLog.ToConsole($"Error - Trying to remove a null WorldObject of type {typeof(TWorldObject).Name}.", MessageType.Error); continue; } 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 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 = CreateWorldObject(); obj.Init(list[id], id); WorldObjectsToUnityObjects.Add(list[id], obj); } } private static TWorldObject CreateWorldObject() where TWorldObject : IWorldObject { var worldObject = (TWorldObject)Activator.CreateInstance(typeof(TWorldObject)); WorldObjects.Add(worldObject); if (worldObject == null) { // if this happens, god help you DebugLog.ToConsole($"Error - CreateWorldObject is returning a null value! This is very bad!", MessageType.Error); } return worldObject; } 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 }); } } }