diff --git a/NewHorizons/Builder/Props/DetailBuilder.cs b/NewHorizons/Builder/Props/DetailBuilder.cs index 6cf3dd86..d2243713 100644 --- a/NewHorizons/Builder/Props/DetailBuilder.cs +++ b/NewHorizons/Builder/Props/DetailBuilder.cs @@ -19,7 +19,6 @@ namespace NewHorizons.Builder.Props { public static class DetailBuilder { - private static readonly Dictionary _detailInfoToCorrespondingSpawnedGameObject = new(); private static readonly Dictionary<(Sector, string), (GameObject prefab, bool isItem)> _fixedPrefabCache = new(); private static GameObject _emptyPrefab; @@ -48,20 +47,6 @@ namespace NewHorizons.Builder.Props UnityEngine.Object.Destroy(prefab.prefab); } _fixedPrefabCache.Clear(); - _detailInfoToCorrespondingSpawnedGameObject.Clear(); - } - - // i dont like how this is just a random collection in this class but quantum guy uses it :( - public static GameObject GetSpawnedGameObjectByDetailInfo(DetailInfo detail) - { - if (!_detailInfoToCorrespondingSpawnedGameObject.ContainsKey(detail)) - { - return null; - } - else - { - return _detailInfoToCorrespondingSpawnedGameObject[detail]; - } } /// @@ -301,8 +286,6 @@ namespace NewHorizons.Builder.Props ConditionalObjectActivation.SetUp(prop, detail.deactivationCondition, detail.blinkWhenActiveChanged, false); } - _detailInfoToCorrespondingSpawnedGameObject[detail] = prop; - return prop; } diff --git a/NewHorizons/Builder/Props/PropBuildManager.cs b/NewHorizons/Builder/Props/PropBuildManager.cs index ada91088..43a10ab4 100644 --- a/NewHorizons/Builder/Props/PropBuildManager.cs +++ b/NewHorizons/Builder/Props/PropBuildManager.cs @@ -165,7 +165,9 @@ namespace NewHorizons.Builder.Props } } - QuantumBuilder.TryMake(go, sector, config); + if (config.Props.lightningQuantumGroups != null) QuantumBuilder.Make(go, sector, mod, config.Props.lightningQuantumGroups); + if (config.Props.socketQuantumGroups != null) QuantumBuilder.Make(go, sector, mod, config.Props.socketQuantumGroups); + if (config.Props.stateQuantumGroups != null) QuantumBuilder.Make(go, sector, mod, config.Props.stateQuantumGroups); } private static bool _ignoreParent; diff --git a/NewHorizons/Builder/Props/QuantumBuilder.cs b/NewHorizons/Builder/Props/QuantumBuilder.cs index 8935f54d..cb2d2d2f 100644 --- a/NewHorizons/Builder/Props/QuantumBuilder.cs +++ b/NewHorizons/Builder/Props/QuantumBuilder.cs @@ -10,81 +10,46 @@ using System.Collections.Generic; using System.Linq; using NewHorizons.External.Modules.Props; using UnityEngine; +using OWML.Common; namespace NewHorizons.Builder.Props { public static class QuantumBuilder { - public static void TryMake(GameObject go, Sector sector, PlanetConfig config) + public static void Make(GameObject planetGO, Sector sector, IModBehaviour mod, BaseQuantumGroupInfo[] quantumGroups) { - if (config.Props != null && config.Props.details != null) + foreach (var group in quantumGroups) { - var quantumPropsByGroup = new Dictionary>(); - foreach (var detail in config.Props?.details) - { - if (detail.quantumGroupID != null) - { - if (!quantumPropsByGroup.ContainsKey(detail.quantumGroupID)) - { - quantumPropsByGroup[detail.quantumGroupID] = new(); - } - quantumPropsByGroup[detail.quantumGroupID].Add((DetailBuilder.GetSpawnedGameObjectByDetailInfo(detail), detail)); - } - } - - void MakeQuantumGroup(BaseQuantumGroupInfo[] group) - { - foreach (var quantumGroup in group) - { - if (!quantumPropsByGroup.ContainsKey(quantumGroup.id)) continue; - var propsInGroup = quantumPropsByGroup[quantumGroup.id]; - - try - { - QuantumBuilder.Make(go, sector, quantumGroup, propsInGroup.ToArray()); - } - catch (Exception ex) - { - NHLogger.LogError($"Couldn't make quantum group [{quantumGroup.id}] for [{go.name}]:\n{ex}"); - } - } - } - - if (config.Props.socketQuantumGroups != null) - { - MakeQuantumGroup(config.Props.socketQuantumGroups); - } - - if (config.Props.stateQuantumGroups != null) - { - MakeQuantumGroup(config.Props.stateQuantumGroups); - } - - if (config.Props.lightningQuantumGroups != null) - { - MakeQuantumGroup(config.Props.lightningQuantumGroups); - } + Make(planetGO, sector, mod, group); } } - public static void Make(GameObject planetGO, Sector sector, BaseQuantumGroupInfo quantumGroup, (GameObject go, DetailInfo detail)[] propsInGroup) + public static void Make(GameObject planetGO, Sector sector, IModBehaviour mod, BaseQuantumGroupInfo quantumGroup) { + if (quantumGroup.details == null || !quantumGroup.details.Any()) + { + NHLogger.LogError($"Found quantum group with no details - [{planetGO.name}] [{quantumGroup.name}]"); + return; + } + if (quantumGroup is SocketQuantumGroupInfo socketGroup) { - MakeSocketGroup(planetGO, sector, socketGroup, propsInGroup); + MakeSocketGroup(planetGO, sector, mod, socketGroup); } else if (quantumGroup is StateQuantumGroupInfo stateGroup) { - MakeStateGroup(planetGO, sector, stateGroup, propsInGroup.Select(x => x.go).ToArray()); + MakeStateGroup(planetGO, sector, mod, stateGroup); } else if (quantumGroup is LightningQuantumGroupInfo lightningGroup) { - MakeQuantumLightning(planetGO, sector, lightningGroup, propsInGroup); + MakeQuantumLightning(planetGO, sector, mod, lightningGroup); } } - public static void MakeQuantumLightning(GameObject planetGO, Sector sector, LightningQuantumGroupInfo quantumGroup, (GameObject go, DetailInfo detail)[] propsInGroup) + public static void MakeQuantumLightning(GameObject planetGO, Sector sector, IModBehaviour mod, LightningQuantumGroupInfo quantumGroup) { + (GameObject go, QuantumDetailInfo detail)[] propsInGroup = quantumGroup.details.Select(x => (DetailBuilder.Make(planetGO, sector, mod, x), x)).ToArray(); + // Bases its position off the first object with position set var root = propsInGroup.FirstOrDefault(x => x.detail.position != null || x.detail.rotation != null || x.detail.isRelativeToParent); @@ -107,15 +72,20 @@ namespace NewHorizons.Builder.Props lightningController._models = propsInGroup.Select(x => x.go).ToArray(); lightningController.enabled = true; - lightning.name = "Quantum Lightning - " + quantumGroup.id; + lightning.name = "Quantum Lightning - " + quantumGroup.name; lightning.SetActive(true); + + // Not sure why but it isn't enabling itself + Delay.FireOnNextUpdate(() => lightningController.enabled = true); } // Nice to have: socket groups that specify a filledSocketObject and an emptySocketObject (eg the archway in the giant's deep tower) - public static void MakeSocketGroup(GameObject planetGO, Sector sector, SocketQuantumGroupInfo quantumGroup, (GameObject go, DetailInfo detail)[] propsInGroup) + public static void MakeSocketGroup(GameObject planetGO, Sector sector, IModBehaviour mod, SocketQuantumGroupInfo quantumGroup) { + (GameObject go, QuantumDetailInfo detail)[] propsInGroup = quantumGroup.details.Select(x => (DetailBuilder.Make(planetGO, sector, mod, x), x)).ToArray(); + GameObject specialProp = null; - DetailInfo specialInfo = null; + QuantumDetailInfo specialInfo = null; if (propsInGroup.Length == quantumGroup.sockets.Length) { // Special case! @@ -126,7 +96,7 @@ namespace NewHorizons.Builder.Props propsInGroup = propsInGroupList.ToArray(); } - var groupRoot = new GameObject("Quantum Sockets - " + quantumGroup.id); + var groupRoot = new GameObject("Quantum Sockets - " + quantumGroup.name); groupRoot.transform.parent = sector?.transform ?? planetGO.transform; groupRoot.transform.localPosition = Vector3.zero; groupRoot.transform.localEulerAngles = Vector3.zero; @@ -151,9 +121,9 @@ namespace NewHorizons.Builder.Props quantumObject._socketList = sockets.ToList(); quantumObject._sockets = sockets; quantumObject._prebuilt = true; - quantumObject._alignWithSocket = !prop.detail.quantumAlignWithGravity; - quantumObject._randomYRotation = prop.detail.quantumRandomizeYRotation; - quantumObject._alignWithGravity = prop.detail.quantumAlignWithGravity; + quantumObject._alignWithSocket = !prop.detail.alignWithGravity; + quantumObject._randomYRotation = prop.detail.randomizeYRotation; + quantumObject._alignWithGravity = prop.detail.alignWithGravity; quantumObject._childSockets = new List(); if (prop.go.GetComponentInChildren() == null) { @@ -168,7 +138,7 @@ namespace NewHorizons.Builder.Props // Instead we have a duplicate of the final object for each slot, which appears when that slot is "empty" for (int i = 0; i < sockets.Length; i++) { - var emptySocketObject = DetailBuilder.Make(planetGO, sector, Main.Instance, specialProp, new DetailInfo()); + var emptySocketObject = DetailBuilder.Make(planetGO, sector, mod, specialProp, new DetailInfo()); var socket = sockets[i]; socket._emptySocketObject = emptySocketObject; emptySocketObject.SetActive(socket._quantumObject == null); @@ -188,9 +158,9 @@ namespace NewHorizons.Builder.Props tracker.AddComponent(); // Using a quantum object bc it can be locked by camera var quantumObject = socket.gameObject.AddComponent(); - quantumObject._alignWithSocket = !specialInfo.quantumAlignWithGravity; - quantumObject._randomYRotation = specialInfo.quantumRandomizeYRotation; - quantumObject._alignWithGravity = specialInfo.quantumAlignWithGravity; + quantumObject._alignWithSocket = !specialInfo.alignWithGravity; + quantumObject._randomYRotation = specialInfo.randomizeYRotation; + quantumObject._alignWithGravity = specialInfo.alignWithGravity; quantumObject.emptySocketObject = emptySocketObject; socket._visibilityObject = quantumObject; @@ -199,7 +169,7 @@ namespace NewHorizons.Builder.Props } } - public static void MakeStateGroup(GameObject go, Sector sector, StateQuantumGroupInfo quantumGroup, GameObject[] propsInGroup) + public static void MakeStateGroup(GameObject go, Sector sector, IModBehaviour mod, StateQuantumGroupInfo quantumGroup) { // NOTE: States groups need special consideration that socket groups don't // this is because the base class QuantumObject (and this is important) IGNORES PICTURES TAKEN FROM OVER 100 METERS AWAY @@ -207,7 +177,9 @@ namespace NewHorizons.Builder.Props // while states put the QuantumObject component (NHMultiStateQuantumObject) on the parent, which is located at the center of the planet // this means that the distance measured by QuantumObject is not accurate, since it's not measuring from the active prop, but from the center of the planet - var groupRoot = new GameObject("Quantum States - " + quantumGroup.id); + var propsInGroup = quantumGroup.details.Select(x => DetailBuilder.Make(go, sector, mod, x)).ToArray(); + + var groupRoot = new GameObject("Quantum States - " + quantumGroup.name); groupRoot.transform.parent = sector?.transform ?? go.transform; groupRoot.transform.localPosition = Vector3.zero; @@ -256,7 +228,7 @@ namespace NewHorizons.Builder.Props public static void MakeShuffleGroup(GameObject go, Sector sector, BaseQuantumGroupInfo quantumGroup, GameObject[] propsInGroup) { //var averagePosition = propsInGroup.Aggregate(Vector3.zero, (avg, prop) => avg + prop.transform.position) / propsInGroup.Count(); - GameObject shuffleParent = new GameObject("Quantum Shuffle - " + quantumGroup.id); + GameObject shuffleParent = new GameObject("Quantum Shuffle - " + quantumGroup.name); shuffleParent.SetActive(false); shuffleParent.transform.parent = sector?.transform ?? go.transform; shuffleParent.transform.localPosition = Vector3.zero; diff --git a/NewHorizons/External/Configs/PlanetConfig.cs b/NewHorizons/External/Configs/PlanetConfig.cs index c9364aeb..2ff2ee73 100644 --- a/NewHorizons/External/Configs/PlanetConfig.cs +++ b/NewHorizons/External/Configs/PlanetConfig.cs @@ -711,8 +711,9 @@ namespace NewHorizons.External.Configs { var socketQuantumGroups = Props.quantumGroups.Where(x => x.type == QuantumGroupType.Sockets).Select(x => new SocketQuantumGroupInfo() { - id = x.id, + name = x.id, sockets = x.sockets, + details = Props.details.Where(y => y.quantumGroupID == x.id).Select(x => new QuantumDetailInfo(x)).ToArray() }); if (socketQuantumGroups.Any()) { @@ -720,10 +721,11 @@ namespace NewHorizons.External.Configs } var stateQuantumGroups = Props.quantumGroups.Where(x => x.type == QuantumGroupType.States).Select(x => new StateQuantumGroupInfo() { - id = x.id, + name = x.id, hasEmptyState = x.hasEmptyState, loop = x.loop, - sequential = x.sequential + sequential = x.sequential, + details = Props.details.Where(y => y.quantumGroupID == x.id).Select(x => new QuantumDetailInfo(x)).ToArray() }); if (stateQuantumGroups.Any()) { diff --git a/NewHorizons/External/Modules/PropModule.cs b/NewHorizons/External/Modules/PropModule.cs index b5b3d519..e2f1200b 100644 --- a/NewHorizons/External/Modules/PropModule.cs +++ b/NewHorizons/External/Modules/PropModule.cs @@ -118,10 +118,20 @@ namespace NewHorizons.External.Modules /// public ShuttleInfo[] shuttles; + /// + /// Add a socket quantum object to a planet. Define the position of multiple "sockets" and multiple objects that jump between sockets. + /// If the number of sockets equals the number of objects, they will shuffle around. + /// public SocketQuantumGroupInfo[] socketQuantumGroups; + /// + /// Add a state quantum object to a planet. Switches between displaying different objects in a single place. + /// public StateQuantumGroupInfo[] stateQuantumGroups; + /// + /// Add quantum lightning to a planet. When lightning strikes, a different detail object is shown. The lightning will take the first defined position/rotation for all objects. + /// public LightningQuantumGroupInfo[] lightningQuantumGroups; /// diff --git a/NewHorizons/External/Modules/Props/DetailInfo.cs b/NewHorizons/External/Modules/Props/DetailInfo.cs index e273ac88..54e59b5c 100644 --- a/NewHorizons/External/Modules/Props/DetailInfo.cs +++ b/NewHorizons/External/Modules/Props/DetailInfo.cs @@ -48,19 +48,13 @@ namespace NewHorizons.External.Modules.Props /// public MVector3 stretch; - /// - /// If this value is not null, this prop will be quantum. Assign this field to the id of the quantum group it should be a part of. The group it is assigned to determines what kind of quantum object it is - /// + [Obsolete("Use QuantumDetailInfo")] public string quantumGroupID; - /// - /// If this prop is quantum, and the quantum group is socketed, this field determines whether the prop will randomly choose a Y rotation when moving to a socket. - /// + [Obsolete("Use QuantumDetailInfo")] [DefaultValue(true)] public bool quantumRandomizeYRotation = true; - /// - /// If this prop is quantum, and the quantum group is socketed, this field determines whether the prop will align with the GravityVolume (true) or align with the current socket (false). - /// + [Obsolete("Use QuantumDetailInfo")] [DefaultValue(true)] public bool quantumAlignWithGravity = true; /// diff --git a/NewHorizons/External/Modules/Props/Quantum/BaseQuantumGroupInfo.cs b/NewHorizons/External/Modules/Props/Quantum/BaseQuantumGroupInfo.cs index 4dede2f9..7eac0948 100644 --- a/NewHorizons/External/Modules/Props/Quantum/BaseQuantumGroupInfo.cs +++ b/NewHorizons/External/Modules/Props/Quantum/BaseQuantumGroupInfo.cs @@ -2,5 +2,13 @@ namespace NewHorizons.External.Modules.Props.Quantum; public class BaseQuantumGroupInfo { - public string id; + /// + /// Optional name to help identify this group + /// + public string name; + + /// + /// List of props which will be used in this quantum group + /// + public QuantumDetailInfo[] details; } diff --git a/NewHorizons/External/Modules/Props/Quantum/QuantumDetailInfo.cs b/NewHorizons/External/Modules/Props/Quantum/QuantumDetailInfo.cs new file mode 100644 index 00000000..304f1c89 --- /dev/null +++ b/NewHorizons/External/Modules/Props/Quantum/QuantumDetailInfo.cs @@ -0,0 +1,24 @@ +using Newtonsoft.Json; +using System.ComponentModel; + +namespace NewHorizons.External.Modules.Props.Quantum; + +public class QuantumDetailInfo : DetailInfo +{ + public QuantumDetailInfo() { } + + public QuantumDetailInfo(DetailInfo info) + { + JsonConvert.PopulateObject(JsonConvert.SerializeObject(info), this); + } + + /// + /// When used in a quantum socket this object will randomize its rotation around the local Y axis. + /// + [DefaultValue(true)] public bool randomizeYRotation = true; + + /// + /// When used in a quantum socket this object will align to the nearest gravity volume. Else use the rotation of the quantum socket. + /// + [DefaultValue(true)] public bool alignWithGravity = true; +}