Reorganize quantum stuff so that the details go with the sockets/states/etc

This commit is contained in:
xen-42 2025-01-06 22:59:41 -05:00
parent dea646bf8e
commit 5c9c487201
8 changed files with 92 additions and 97 deletions

View File

@ -19,7 +19,6 @@ namespace NewHorizons.Builder.Props
{ {
public static class DetailBuilder public static class DetailBuilder
{ {
private static readonly Dictionary<DetailInfo, GameObject> _detailInfoToCorrespondingSpawnedGameObject = new();
private static readonly Dictionary<(Sector, string), (GameObject prefab, bool isItem)> _fixedPrefabCache = new(); private static readonly Dictionary<(Sector, string), (GameObject prefab, bool isItem)> _fixedPrefabCache = new();
private static GameObject _emptyPrefab; private static GameObject _emptyPrefab;
@ -48,20 +47,6 @@ namespace NewHorizons.Builder.Props
UnityEngine.Object.Destroy(prefab.prefab); UnityEngine.Object.Destroy(prefab.prefab);
} }
_fixedPrefabCache.Clear(); _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];
}
} }
/// <summary> /// <summary>
@ -301,8 +286,6 @@ namespace NewHorizons.Builder.Props
ConditionalObjectActivation.SetUp(prop, detail.deactivationCondition, detail.blinkWhenActiveChanged, false); ConditionalObjectActivation.SetUp(prop, detail.deactivationCondition, detail.blinkWhenActiveChanged, false);
} }
_detailInfoToCorrespondingSpawnedGameObject[detail] = prop;
return prop; return prop;
} }

View File

@ -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; private static bool _ignoreParent;

View File

@ -10,81 +10,46 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using NewHorizons.External.Modules.Props; using NewHorizons.External.Modules.Props;
using UnityEngine; using UnityEngine;
using OWML.Common;
namespace NewHorizons.Builder.Props namespace NewHorizons.Builder.Props
{ {
public static class QuantumBuilder 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<string, List<(GameObject go, DetailInfo info)>>(); Make(planetGO, sector, mod, group);
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) public static void Make(GameObject planetGO, Sector sector, IModBehaviour mod, BaseQuantumGroupInfo quantumGroup)
{ {
foreach (var quantumGroup in group) if (quantumGroup.details == null || !quantumGroup.details.Any())
{ {
if (!quantumPropsByGroup.ContainsKey(quantumGroup.id)) continue; NHLogger.LogError($"Found quantum group with no details - [{planetGO.name}] [{quantumGroup.name}]");
var propsInGroup = quantumPropsByGroup[quantumGroup.id]; return;
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);
}
}
}
public static void Make(GameObject planetGO, Sector sector, BaseQuantumGroupInfo quantumGroup, (GameObject go, DetailInfo detail)[] propsInGroup)
{
if (quantumGroup is SocketQuantumGroupInfo socketGroup) if (quantumGroup is SocketQuantumGroupInfo socketGroup)
{ {
MakeSocketGroup(planetGO, sector, socketGroup, propsInGroup); MakeSocketGroup(planetGO, sector, mod, socketGroup);
} }
else if (quantumGroup is StateQuantumGroupInfo stateGroup) 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) 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 // 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); 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._models = propsInGroup.Select(x => x.go).ToArray();
lightningController.enabled = true; lightningController.enabled = true;
lightning.name = "Quantum Lightning - " + quantumGroup.id; lightning.name = "Quantum Lightning - " + quantumGroup.name;
lightning.SetActive(true); 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) // 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; GameObject specialProp = null;
DetailInfo specialInfo = null; QuantumDetailInfo specialInfo = null;
if (propsInGroup.Length == quantumGroup.sockets.Length) if (propsInGroup.Length == quantumGroup.sockets.Length)
{ {
// Special case! // Special case!
@ -126,7 +96,7 @@ namespace NewHorizons.Builder.Props
propsInGroup = propsInGroupList.ToArray(); 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.parent = sector?.transform ?? planetGO.transform;
groupRoot.transform.localPosition = Vector3.zero; groupRoot.transform.localPosition = Vector3.zero;
groupRoot.transform.localEulerAngles = Vector3.zero; groupRoot.transform.localEulerAngles = Vector3.zero;
@ -151,9 +121,9 @@ namespace NewHorizons.Builder.Props
quantumObject._socketList = sockets.ToList(); quantumObject._socketList = sockets.ToList();
quantumObject._sockets = sockets; quantumObject._sockets = sockets;
quantumObject._prebuilt = true; quantumObject._prebuilt = true;
quantumObject._alignWithSocket = !prop.detail.quantumAlignWithGravity; quantumObject._alignWithSocket = !prop.detail.alignWithGravity;
quantumObject._randomYRotation = prop.detail.quantumRandomizeYRotation; quantumObject._randomYRotation = prop.detail.randomizeYRotation;
quantumObject._alignWithGravity = prop.detail.quantumAlignWithGravity; quantumObject._alignWithGravity = prop.detail.alignWithGravity;
quantumObject._childSockets = new List<QuantumSocket>(); quantumObject._childSockets = new List<QuantumSocket>();
if (prop.go.GetComponentInChildren<VisibilityTracker>() == null) if (prop.go.GetComponentInChildren<VisibilityTracker>() == 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" // 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++) 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]; var socket = sockets[i];
socket._emptySocketObject = emptySocketObject; socket._emptySocketObject = emptySocketObject;
emptySocketObject.SetActive(socket._quantumObject == null); emptySocketObject.SetActive(socket._quantumObject == null);
@ -188,9 +158,9 @@ namespace NewHorizons.Builder.Props
tracker.AddComponent<ShapeVisibilityTracker>(); tracker.AddComponent<ShapeVisibilityTracker>();
// Using a quantum object bc it can be locked by camera // Using a quantum object bc it can be locked by camera
var quantumObject = socket.gameObject.AddComponent<SnapshotLockableVisibilityObject>(); var quantumObject = socket.gameObject.AddComponent<SnapshotLockableVisibilityObject>();
quantumObject._alignWithSocket = !specialInfo.quantumAlignWithGravity; quantumObject._alignWithSocket = !specialInfo.alignWithGravity;
quantumObject._randomYRotation = specialInfo.quantumRandomizeYRotation; quantumObject._randomYRotation = specialInfo.randomizeYRotation;
quantumObject._alignWithGravity = specialInfo.quantumAlignWithGravity; quantumObject._alignWithGravity = specialInfo.alignWithGravity;
quantumObject.emptySocketObject = emptySocketObject; quantumObject.emptySocketObject = emptySocketObject;
socket._visibilityObject = quantumObject; 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 // 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 // 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 // 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 // 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.parent = sector?.transform ?? go.transform;
groupRoot.transform.localPosition = Vector3.zero; 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) 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(); //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.SetActive(false);
shuffleParent.transform.parent = sector?.transform ?? go.transform; shuffleParent.transform.parent = sector?.transform ?? go.transform;
shuffleParent.transform.localPosition = Vector3.zero; shuffleParent.transform.localPosition = Vector3.zero;

View File

@ -711,8 +711,9 @@ namespace NewHorizons.External.Configs
{ {
var socketQuantumGroups = Props.quantumGroups.Where(x => x.type == QuantumGroupType.Sockets).Select(x => new SocketQuantumGroupInfo() var socketQuantumGroups = Props.quantumGroups.Where(x => x.type == QuantumGroupType.Sockets).Select(x => new SocketQuantumGroupInfo()
{ {
id = x.id, name = x.id,
sockets = x.sockets, sockets = x.sockets,
details = Props.details.Where(y => y.quantumGroupID == x.id).Select(x => new QuantumDetailInfo(x)).ToArray()
}); });
if (socketQuantumGroups.Any()) 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() var stateQuantumGroups = Props.quantumGroups.Where(x => x.type == QuantumGroupType.States).Select(x => new StateQuantumGroupInfo()
{ {
id = x.id, name = x.id,
hasEmptyState = x.hasEmptyState, hasEmptyState = x.hasEmptyState,
loop = x.loop, 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()) if (stateQuantumGroups.Any())
{ {

View File

@ -118,10 +118,20 @@ namespace NewHorizons.External.Modules
/// </summary> /// </summary>
public ShuttleInfo[] shuttles; public ShuttleInfo[] shuttles;
/// <summary>
/// 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.
/// </summary>
public SocketQuantumGroupInfo[] socketQuantumGroups; public SocketQuantumGroupInfo[] socketQuantumGroups;
/// <summary>
/// Add a state quantum object to a planet. Switches between displaying different objects in a single place.
/// </summary>
public StateQuantumGroupInfo[] stateQuantumGroups; public StateQuantumGroupInfo[] stateQuantumGroups;
/// <summary>
/// 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.
/// </summary>
public LightningQuantumGroupInfo[] lightningQuantumGroups; public LightningQuantumGroupInfo[] lightningQuantumGroups;
/// <summary> /// <summary>

View File

@ -48,19 +48,13 @@ namespace NewHorizons.External.Modules.Props
/// </summary> /// </summary>
public MVector3 stretch; public MVector3 stretch;
/// <summary> [Obsolete("Use QuantumDetailInfo")]
/// 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
/// </summary>
public string quantumGroupID; public string quantumGroupID;
/// <summary> [Obsolete("Use QuantumDetailInfo")]
/// 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.
/// </summary>
[DefaultValue(true)] public bool quantumRandomizeYRotation = true; [DefaultValue(true)] public bool quantumRandomizeYRotation = true;
/// <summary> [Obsolete("Use QuantumDetailInfo")]
/// 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).
/// </summary>
[DefaultValue(true)] public bool quantumAlignWithGravity = true; [DefaultValue(true)] public bool quantumAlignWithGravity = true;
/// <summary> /// <summary>

View File

@ -2,5 +2,13 @@ namespace NewHorizons.External.Modules.Props.Quantum;
public class BaseQuantumGroupInfo public class BaseQuantumGroupInfo
{ {
public string id; /// <summary>
/// Optional name to help identify this group
/// </summary>
public string name;
/// <summary>
/// List of props which will be used in this quantum group
/// </summary>
public QuantumDetailInfo[] details;
} }

View File

@ -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);
}
/// <summary>
/// When used in a quantum socket this object will randomize its rotation around the local Y axis.
/// </summary>
[DefaultValue(true)] public bool randomizeYRotation = true;
/// <summary>
/// When used in a quantum socket this object will align to the nearest gravity volume. Else use the rotation of the quantum socket.
/// </summary>
[DefaultValue(true)] public bool alignWithGravity = true;
}