mirror of
https://github.com/Outer-Wilds-New-Horizons/new-horizons.git
synced 2025-12-11 20:15:44 +01:00
362 lines
20 KiB
C#
362 lines
20 KiB
C#
using NewHorizons.Builder.Props;
|
|
using NewHorizons.Builder.Volumes;
|
|
using NewHorizons.Components.SizeControllers;
|
|
using NewHorizons.Components.Volumes;
|
|
using NewHorizons.External.Configs;
|
|
using NewHorizons.External.Modules.VariableSize;
|
|
using NewHorizons.External.SerializableData;
|
|
using NewHorizons.Utility;
|
|
using NewHorizons.Utility.OuterWilds;
|
|
using NewHorizons.Utility.OWML;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using Color = UnityEngine.Color;
|
|
|
|
namespace NewHorizons.Builder.Body
|
|
{
|
|
public static class SingularityBuilder
|
|
{
|
|
private static readonly string _blackHoleProxyPath = "TowerTwin_Body/Sector_TowerTwin/Sector_Tower_HGT/Interactables_Tower_HGT/Interactables_Tower_TT/Prefab_NOM_WarpTransmitter (1)/BlackHole/BlackHoleSingularity";
|
|
private static readonly string _whiteHoleProxyPath = "TowerTwin_Body/Sector_TowerTwin/Sector_Tower_HGT/Interactables_Tower_HGT/Interactables_Tower_CT/Prefab_NOM_WarpTransmitter/WhiteHole/WhiteHoleSingularity";
|
|
private static GameObject _blackHoleProxyPrefab, _whiteHoleProxyPrefab;
|
|
|
|
private static Shader blackHoleShader = null;
|
|
private static Shader whiteHoleShader = null;
|
|
|
|
public static readonly int Radius = Shader.PropertyToID("_Radius");
|
|
public static readonly int MaxDistortRadius = Shader.PropertyToID("_MaxDistortRadius");
|
|
public static readonly int MassScale = Shader.PropertyToID("_MassScale");
|
|
public static readonly int DistortFadeDist = Shader.PropertyToID("_DistortFadeDist");
|
|
public static readonly int Color1 = Shader.PropertyToID("_Color");
|
|
|
|
private static Dictionary<string, GameObject> _singularitiesByID;
|
|
private static List<(string, string)> _pairsToLink;
|
|
|
|
private static Mesh _blackHoleMesh;
|
|
private static GameObject _blackHoleAmbience;
|
|
private static GameObject _blackHoleEmissionOneShot;
|
|
private static GameObject _blackHoleVolume;
|
|
|
|
private static Mesh _whiteHoleMesh;
|
|
private static GameObject _whiteHoleAmbientLight;
|
|
private static GameObject _whiteHoleAmbience;
|
|
private static GameObject _whiteHoleZeroGVolume;
|
|
private static GameObject _whiteHoleRulesetVolume;
|
|
private static GameObject _whiteHoleVolume;
|
|
|
|
internal static void InitPrefabs()
|
|
{
|
|
if (_blackHoleProxyPrefab == null) _blackHoleProxyPrefab = SearchUtilities.Find(_blackHoleProxyPath).InstantiateInactive().Rename("BlackHoleSingularity").DontDestroyOnLoad();
|
|
if (_whiteHoleProxyPrefab == null) _whiteHoleProxyPrefab = SearchUtilities.Find(_whiteHoleProxyPath).InstantiateInactive().Rename("WhiteHoleSingularity").DontDestroyOnLoad();
|
|
|
|
if (blackHoleShader == null) blackHoleShader = SearchUtilities.Find("BrittleHollow_Body/BlackHole_BH/BlackHoleRenderer").GetComponent<MeshRenderer>().sharedMaterial.shader;
|
|
if (whiteHoleShader == null) whiteHoleShader = SearchUtilities.Find("WhiteHole_Body/WhiteHoleVisuals/Singularity").GetComponent<MeshRenderer>().sharedMaterial.shader;
|
|
|
|
if (_blackHoleMesh == null) _blackHoleMesh = SearchUtilities.Find("BrittleHollow_Body/BlackHole_BH/BlackHoleRenderer").GetComponent<MeshFilter>().mesh.DontDestroyOnLoad();
|
|
if (_blackHoleAmbience == null) _blackHoleAmbience = SearchUtilities.Find("BrittleHollow_Body/BlackHole_BH/BlackHoleAmbience").InstantiateInactive().Rename("BlackHoleAmbience").DontDestroyOnLoad();
|
|
if (_blackHoleEmissionOneShot == null) _blackHoleEmissionOneShot = SearchUtilities.Find("BrittleHollow_Body/BlackHole_BH/BlackHoleEmissionOneShot").InstantiateInactive().Rename("BlackHoleEmissionOneShot").DontDestroyOnLoad();
|
|
if (_blackHoleVolume == null) _blackHoleVolume = SearchUtilities.Find("BrittleHollow_Body/BlackHole_BH/BlackHoleVolume").InstantiateInactive().Rename("BlackHoleVolume").DontDestroyOnLoad();
|
|
|
|
if (_whiteHoleMesh == null) _whiteHoleMesh = SearchUtilities.Find("WhiteHole_Body/WhiteHoleVisuals/Singularity").GetComponent<MeshFilter>().mesh.DontDestroyOnLoad();
|
|
if (_whiteHoleAmbientLight == null) _whiteHoleAmbientLight = SearchUtilities.Find("WhiteHole_Body/WhiteHoleVisuals/AmbientLight_WH").InstantiateInactive().Rename("WhiteHoleAmbientLight").DontDestroyOnLoad();
|
|
if (_whiteHoleAmbience == null) _whiteHoleAmbience = SearchUtilities.Find("WhiteHole_Body/Ambience_WhiteHole").InstantiateInactive().Rename("WhiteHoleAmbience").DontDestroyOnLoad();
|
|
if (_whiteHoleZeroGVolume == null) _whiteHoleZeroGVolume = SearchUtilities.Find("WhiteHole_Body/ZeroGVolume").InstantiateInactive().Rename("WhiteHoleZeroGVolume").DontDestroyOnLoad();
|
|
if (_whiteHoleRulesetVolume == null) _whiteHoleRulesetVolume = SearchUtilities.Find("WhiteHole_Body/Sector_WhiteHole/RulesetVolumes_WhiteHole").InstantiateInactive().Rename("WhiteHoleRulesetVolume").DontDestroyOnLoad();
|
|
if (_whiteHoleVolume == null) _whiteHoleVolume = SearchUtilities.Find("WhiteHole_Body/WhiteHoleVolume").InstantiateInactive().Rename("WhiteHoleVolume").DontDestroyOnLoad();
|
|
}
|
|
|
|
public static void Init()
|
|
{
|
|
_singularitiesByID = new Dictionary<string, GameObject>();
|
|
_pairsToLink = new List<(string, string)>();
|
|
}
|
|
|
|
public static void Make(GameObject go, Sector sector, OWRigidbody OWRB, PlanetConfig config, SingularityModule singularity)
|
|
{
|
|
var horizonRadius = singularity.horizonRadius;
|
|
var distortRadius = singularity.distortRadius != 0f ? singularity.distortRadius : horizonRadius * 2.5f;
|
|
var pairedSingularity = singularity.pairedSingularity;
|
|
|
|
bool polarity = singularity.type == SingularityModule.SingularityType.BlackHole;
|
|
|
|
bool isWormHole = singularity?.targetStarSystem != null;
|
|
bool hasHazardVolume = !isWormHole && (pairedSingularity == null);
|
|
|
|
Vector3 localPosition = singularity?.position == null ? Vector3.zero : singularity.position;
|
|
Vector3 localRotation = singularity?.rotation == null ? Vector3.zero : singularity.rotation;
|
|
|
|
GameObject newSingularity = MakeSingularity(go, sector, localPosition, localRotation, polarity, horizonRadius, distortRadius,
|
|
hasHazardVolume, singularity.targetStarSystem, singularity.spawnPointID, singularity.curve, singularity.hasWarpEffects, singularity.renderQueueOverride, singularity.rename, singularity.parentPath, singularity.isRelativeToParent);
|
|
|
|
var uniqueID = string.IsNullOrEmpty(singularity.uniqueID) ? config.name : singularity.uniqueID;
|
|
|
|
_singularitiesByID.Add(uniqueID, newSingularity);
|
|
if (!string.IsNullOrEmpty(pairedSingularity))
|
|
{
|
|
if (polarity)
|
|
{
|
|
_pairsToLink.Add((uniqueID, pairedSingularity));
|
|
}
|
|
else
|
|
{
|
|
_pairsToLink.Add((pairedSingularity, uniqueID));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
public static void PairAllSingularities()
|
|
{
|
|
foreach (var pair in _pairsToLink)
|
|
{
|
|
try
|
|
{
|
|
var (blackHoleID, whiteHoleID) = pair;
|
|
if (!_singularitiesByID.TryGetValue(blackHoleID, out GameObject blackHole))
|
|
{
|
|
NHLogger.LogWarning($"Black hole [{blackHoleID}] is missing.");
|
|
continue;
|
|
}
|
|
if (!_singularitiesByID.TryGetValue(whiteHoleID, out GameObject whiteHole))
|
|
{
|
|
NHLogger.LogWarning($"White hole [{whiteHoleID}] is missing.");
|
|
continue;
|
|
}
|
|
var whiteHoleVolume = whiteHole.GetComponentInChildren<WhiteHoleVolume>();
|
|
var blackHoleVolume = blackHole.GetComponentInChildren<BlackHoleVolume>();
|
|
if (whiteHoleVolume == null || blackHoleVolume == null)
|
|
{
|
|
NHLogger.LogWarning($"Singularities [{blackHoleID}] and [{whiteHoleID}] do not have compatible polarities.");
|
|
continue;
|
|
}
|
|
if (blackHoleVolume._whiteHole != null && blackHoleVolume._whiteHole != whiteHoleVolume)
|
|
{
|
|
NHLogger.LogWarning($"Black hole [{blackHoleID}] has already been linked!");
|
|
continue;
|
|
}
|
|
NHLogger.LogVerbose($"Pairing singularities [{blackHoleID}], [{whiteHoleID}]");
|
|
blackHoleVolume._whiteHole = whiteHoleVolume;
|
|
|
|
// If warping to a vanilla planet, we add a streaming volume to pre-load it
|
|
var streamingGroup = whiteHoleVolume.GetAttachedOWRigidbody().GetComponentInChildren<StreamingGroup>();
|
|
if (streamingGroup != null)
|
|
{
|
|
var sphereCollider = blackHoleVolume.GetComponent<SphereCollider>();
|
|
// Shouldn't ever be null but doesn't hurt ig
|
|
var loadRadius = sphereCollider == null ? 100f : sphereCollider.radius + 50f;
|
|
var streamingVolume = VolumeBuilder.Make<StreamingWarpVolume>(blackHoleVolume.GetAttachedOWRigidbody().gameObject, blackHoleVolume.GetComponentInParent<Sector>(),
|
|
new External.Modules.Volumes.VolumeInfos.VolumeInfo() { radius = loadRadius });
|
|
streamingVolume.streamingGroup = streamingGroup;
|
|
streamingVolume.transform.parent = blackHoleVolume.transform;
|
|
streamingVolume.transform.localPosition = Vector3.zero;
|
|
streamingVolume.gameObject.SetActive(true);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
NHLogger.LogError($"Failed to pair singularities {e}");
|
|
}
|
|
}
|
|
}
|
|
|
|
public static GameObject MakeSingularity(GameObject planetGO, Sector sector, Vector3 position, Vector3 rotation, bool polarity, float horizon, float distort,
|
|
bool hasDestructionVolume, string targetStarSystem = null, string targetSpawnID = null, TimeValuePair[] curve = null, bool warpEffects = true, int renderQueue = 2985, string rename = null, string parentPath = null, bool isRelativeToParent = false)
|
|
{
|
|
// polarity true = black, false = white
|
|
|
|
var info = new SingularityModule
|
|
{
|
|
position = position,
|
|
rotation = rotation,
|
|
isRelativeToParent = isRelativeToParent,
|
|
parentPath = parentPath,
|
|
rename = rename,
|
|
};
|
|
|
|
var singularity = GeneralPropBuilder.MakeNew(polarity ? "BlackHole" : "WhiteHole", planetGO, sector, info);
|
|
|
|
var singularityRenderer = MakeSingularityGraphics(singularity, polarity, horizon, distort, renderQueue);
|
|
|
|
SingularitySizeController sizeController = null;
|
|
if (curve != null)
|
|
{
|
|
sizeController = singularityRenderer.gameObject.AddComponent<SingularitySizeController>();
|
|
sizeController.SetScaleCurve(curve);
|
|
sizeController.size = distort;
|
|
sizeController.innerScale = horizon;
|
|
sizeController.material = singularityRenderer.material;
|
|
}
|
|
|
|
OWAudioSource oneShotOWAudioSource = null;
|
|
|
|
var singularityAmbience = GameObject.Instantiate(_blackHoleAmbience, singularity.transform);
|
|
singularityAmbience.name = polarity ? "BlackHoleAmbience" : "WhiteHoleAmbience";
|
|
singularityAmbience.SetActive(true);
|
|
singularityAmbience.GetComponent<SectorAudioGroup>().SetSector(sector);
|
|
|
|
var singularityAudioSource = singularityAmbience.GetComponent<AudioSource>();
|
|
singularityAudioSource.maxDistance = distort * 2.5f;
|
|
singularityAudioSource.minDistance = horizon;
|
|
|
|
// Sounds really weird on large black holes but isn't noticeable on small ones. #226
|
|
singularityAudioSource.dopplerLevel = 0;
|
|
|
|
singularityAmbience.transform.localPosition = Vector3.zero;
|
|
if (sizeController != null) sizeController.audioSource = singularityAudioSource;
|
|
|
|
if (polarity)
|
|
{
|
|
if (hasDestructionVolume || targetStarSystem != null)
|
|
{
|
|
var destructionVolumeGO = new GameObject("DestructionVolume");
|
|
destructionVolumeGO.layer = Layer.BasicEffectVolume;
|
|
destructionVolumeGO.transform.parent = singularity.transform;
|
|
destructionVolumeGO.transform.localScale = Vector3.one;
|
|
destructionVolumeGO.transform.localPosition = Vector3.zero;
|
|
|
|
var sphereCollider = destructionVolumeGO.AddComponent<SphereCollider>();
|
|
sphereCollider.radius = horizon;
|
|
sphereCollider.isTrigger = true;
|
|
if (sizeController != null) sizeController.sphereCollider = sphereCollider;
|
|
|
|
var audio = destructionVolumeGO.AddComponent<AudioSource>();
|
|
audio.spatialBlend = 1f;
|
|
audio.maxDistance = distort * 2.5f;
|
|
destructionVolumeGO.AddComponent<OWAudioSource>();
|
|
|
|
if (hasDestructionVolume)
|
|
{
|
|
destructionVolumeGO.AddComponent<BlackHoleDestructionVolume>();
|
|
}
|
|
else if (targetStarSystem != null)
|
|
{
|
|
var wormholeVolume = destructionVolumeGO.AddComponent<BlackHoleWarpVolume>();
|
|
wormholeVolume.TargetSolarSystem = targetStarSystem;
|
|
wormholeVolume.TargetSpawnID = targetSpawnID;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var blackHoleOneShot = GameObject.Instantiate(_blackHoleEmissionOneShot, singularity.transform);
|
|
blackHoleOneShot.name = "BlackHoleEmissionOneShot";
|
|
blackHoleOneShot.SetActive(true);
|
|
oneShotOWAudioSource = blackHoleOneShot.GetComponent<OWAudioSource>();
|
|
var oneShotAudioSource = blackHoleOneShot.GetComponent<AudioSource>();
|
|
oneShotAudioSource.maxDistance = distort * 3f;
|
|
oneShotAudioSource.minDistance = horizon;
|
|
if (sizeController != null) sizeController.oneShotAudioSource = oneShotAudioSource;
|
|
|
|
var blackHoleVolume = GameObject.Instantiate(_blackHoleVolume, singularity.transform);
|
|
blackHoleVolume.name = "BlackHoleVolume";
|
|
|
|
// Scale vanish effect to black hole size
|
|
var bhVolume = blackHoleVolume.GetComponent<BlackHoleVolume>();
|
|
foreach (var ps in bhVolume._vanishEffectPrefab.GetComponentsInChildren<ParticleSystem>())
|
|
{
|
|
#pragma warning disable CS0618 // Type or member is obsolete - It tells you to use some readonly shit instead
|
|
ps.scalingMode = ParticleSystemScalingMode.Hierarchy;
|
|
#pragma warning restore CS0618 // Type or member is obsolete
|
|
}
|
|
bhVolume._vanishEffectPrefab.transform.localScale = Vector3.one * horizon / 100f;
|
|
|
|
blackHoleVolume.SetActive(true);
|
|
|
|
bhVolume._audioSector = sector;
|
|
bhVolume._emissionSource = oneShotOWAudioSource;
|
|
var blackHoleSphereCollider = blackHoleVolume.GetComponent<SphereCollider>();
|
|
blackHoleSphereCollider.radius = horizon;
|
|
if (sizeController != null) sizeController.sphereCollider = blackHoleSphereCollider;
|
|
if (!warpEffects)
|
|
{
|
|
Delay.FireOnNextUpdate(() =>
|
|
{
|
|
foreach (var renderer in blackHoleVolume.GetComponentsInChildren<ParticleSystemRenderer>(true))
|
|
{
|
|
GameObject.Destroy(renderer);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GameObject whiteHoleVolumeGO = GameObject.Instantiate(_whiteHoleVolume);
|
|
whiteHoleVolumeGO.transform.parent = singularity.transform;
|
|
whiteHoleVolumeGO.transform.localPosition = Vector3.zero;
|
|
whiteHoleVolumeGO.transform.localScale = Vector3.one;
|
|
whiteHoleVolumeGO.GetComponent<SphereCollider>().radius = horizon;
|
|
whiteHoleVolumeGO.name = "WhiteHoleVolume";
|
|
whiteHoleVolumeGO.SetActive(true);
|
|
if (sizeController != null) sizeController.sphereCollider = whiteHoleVolumeGO.GetComponent<SphereCollider>();
|
|
|
|
var OWRB = planetGO.GetComponent<OWRigidbody>();
|
|
|
|
var whiteHoleFluidVolume = whiteHoleVolumeGO.GetComponent<WhiteHoleFluidVolume>();
|
|
whiteHoleFluidVolume._innerRadius = horizon;
|
|
whiteHoleFluidVolume._outerRadius = distort;
|
|
whiteHoleFluidVolume._attachedBody = OWRB;
|
|
if (sizeController != null) sizeController.fluidVolume = whiteHoleFluidVolume;
|
|
|
|
var whiteHoleVolume = whiteHoleVolumeGO.GetComponent<WhiteHoleVolume>();
|
|
whiteHoleVolume._debrisDistMax = distort * 6.5f;
|
|
whiteHoleVolume._debrisDistMin = distort * 2f;
|
|
whiteHoleVolume._whiteHoleSector = sector;
|
|
whiteHoleVolume._fluidVolume = whiteHoleFluidVolume;
|
|
whiteHoleVolume._whiteHoleBody = OWRB;
|
|
whiteHoleVolume._whiteHoleProxyShadowSuperGroup = planetGO.GetComponent<ProxyShadowCasterSuperGroup>();
|
|
whiteHoleVolume._radius = horizon;
|
|
whiteHoleVolume._airlocksToOpen = new NomaiAirlock[0];
|
|
if (sizeController != null) sizeController.volume = whiteHoleVolume;
|
|
|
|
whiteHoleVolume.enabled = true;
|
|
whiteHoleFluidVolume.enabled = true;
|
|
}
|
|
|
|
singularity.SetActive(true);
|
|
return singularity;
|
|
}
|
|
|
|
public static MeshRenderer MakeSingularityGraphics(GameObject singularity, bool polarity, float horizon, float distort, int queue = 2985)
|
|
{
|
|
var singularityRenderer = new GameObject(polarity ? "BlackHoleRenderer" : "WhiteHoleRenderer");
|
|
singularityRenderer.transform.parent = singularity.transform;
|
|
singularityRenderer.transform.localPosition = Vector3.zero;
|
|
singularityRenderer.transform.localScale = Vector3.one * distort;
|
|
|
|
var meshFilter = singularityRenderer.AddComponent<MeshFilter>();
|
|
meshFilter.mesh = polarity ? _blackHoleMesh : _whiteHoleMesh;
|
|
|
|
var meshRenderer = singularityRenderer.AddComponent<MeshRenderer>();
|
|
meshRenderer.material = new Material(polarity ? blackHoleShader : whiteHoleShader);
|
|
meshRenderer.material.SetFloat(Radius, horizon);
|
|
meshRenderer.material.SetFloat(MaxDistortRadius, distort);
|
|
meshRenderer.material.SetFloat(MassScale, polarity ? 1f : -1f);
|
|
meshRenderer.material.SetFloat(DistortFadeDist, distort - horizon);
|
|
if (!polarity) meshRenderer.material.SetColor(Color1, new Color(1.88f, 1.88f, 1.88f, 1f));
|
|
meshRenderer.material.renderQueue = queue;
|
|
|
|
return meshRenderer;
|
|
}
|
|
|
|
public static GameObject MakeSingularityProxy(GameObject rootObject, MVector3 position, bool polarity, float horizon, float distort, TimeValuePair[] curve = null, int queue = 2985)
|
|
{
|
|
var singularityRenderer = MakeSingularityGraphics(rootObject, polarity, horizon, distort, queue);
|
|
if (position != null) singularityRenderer.transform.localPosition = position;
|
|
|
|
SingularitySizeController sizeController = null;
|
|
if (curve != null)
|
|
{
|
|
sizeController = singularityRenderer.gameObject.AddComponent<SingularitySizeController>();
|
|
sizeController.SetScaleCurve(curve);
|
|
sizeController.size = distort;
|
|
sizeController.innerScale = horizon / distort;
|
|
sizeController.material = singularityRenderer.material;
|
|
}
|
|
|
|
singularityRenderer.gameObject.SetActive(true);
|
|
return singularityRenderer.gameObject;
|
|
}
|
|
}
|
|
}
|