new-horizons/NewHorizons/Components/SizeControllers/StarEvolutionController.cs
2022-08-22 00:11:29 -04:00

445 lines
18 KiB
C#

using NewHorizons.Builder.Body;
using NewHorizons.External.Modules.VariableSize;
using NewHorizons.Handlers;
using NewHorizons.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Events;
using Logger = NewHorizons.Utility.Logger;
namespace NewHorizons.Components.SizeControllers
{
public class StarEvolutionController : SizeController
{
public bool _isProxy;
public GameObject atmosphere;
public StarController controller;
public StellarDeathController supernova;
public bool WillExplode { get; set; }
public MColor StartColour { get; set; }
public MColor EndColour { get; set; }
public MColor SupernovaColour { get; set; }
public Texture normalRamp;
public Texture collapseRamp;
private Color _startColour;
private Color _endColour;
private StellarRemnantController _stellarRemnantController;
private PlanetaryFogController _fog;
private MeshRenderer[] _atmosphereRenderers;
public HeatHazardVolume _heatVolume;
public DestructionVolume _destructionVolume;
public StarDestructionVolume _planetDestructionVolume;
public StarFluidVolume _starFluidVolume;
private SolarFlareEmitter _flareEmitter;
private MapMarker _mapMarker;
private OWRigidbody _rigidbody;
public OWAudioSource _oneShotSource;
private bool _isCollapsing;
private float _collapseStartSize;
private float _collapseTimer;
public float collapseTime = 10f; // seconds
public float supernovaScaleStart = 45f; // seconds
public float supernovaScaleEnd = 50f; // seconds
public float supernovaTime = 50f; // seconds
public float lifespan = 22f; // minutes
public float supernovaSize = 50000f;
public StellarDeathType deathType;
private bool _isSupernova;
private float _supernovaStartTime;
private Material _collapseStartSurfaceMaterial;
private Material _collapseEndSurfaceMaterial;
private Material _startSurfaceMaterial;
private Material _endSurfaceMaterial;
private Material _surfaceMaterial;
private Texture _normalRamp;
private Texture _collapseRamp;
private StarEvolutionController _proxy;
public UnityEvent CollapseStart = new UnityEvent();
public UnityEvent CollapseStop = new UnityEvent();
public UnityEvent SupernovaStart = new UnityEvent();
public UnityEvent SupernovaStop = new UnityEvent();
private float maxScale;
private float minScale;
private static readonly int ColorRamp = Shader.PropertyToID("_ColorRamp");
private static readonly int ColorTime = Shader.PropertyToID("_ColorTime");
private static readonly int InnerRadius = Shader.PropertyToID("_InnerRadius");
private static readonly int OuterRadius = Shader.PropertyToID("_OuterRadius");
private static readonly int SkyColor = Shader.PropertyToID("_SkyColor");
private Color _currentColour;
private void Start()
{
_rigidbody = this.GetAttachedOWRigidbody();
if (_rigidbody != null) _mapMarker = _rigidbody.GetComponent<MapMarker>();
var sun = GameObject.FindObjectOfType<SunController>();
_collapseStartSurfaceMaterial = new Material(sun._collapseStartSurfaceMaterial);
_collapseEndSurfaceMaterial = new Material(sun._collapseEndSurfaceMaterial);
_startSurfaceMaterial = new Material(sun._startSurfaceMaterial);
_endSurfaceMaterial = new Material(sun._endSurfaceMaterial);
if (normalRamp == null)
{
_normalRamp = sun._startSurfaceMaterial.GetTexture(ColorRamp);
} else
{
_normalRamp = normalRamp;
}
if (collapseRamp == null)
{
_collapseRamp = sun._collapseStartSurfaceMaterial.GetTexture(ColorRamp);
} else
{
_collapseRamp = collapseRamp;
}
// Copy over the material that was set in star builder
_collapseStartSurfaceMaterial.SetTexture(ColorRamp, _collapseRamp);
_collapseEndSurfaceMaterial.SetTexture(ColorRamp, _collapseRamp);
_startSurfaceMaterial.SetTexture(ColorRamp, _normalRamp);
_endSurfaceMaterial.SetTexture(ColorRamp, _normalRamp);
if (StartColour == null)
{
_startColour = _startSurfaceMaterial.color;
}
else
{
_startColour = StartColour.ToColor();
_startSurfaceMaterial.color = _startColour;
}
if (EndColour == null)
{
_endColour = _startColour;
_endSurfaceMaterial.color = _startColour;
}
else
{
_endColour = EndColour.ToColor();
_endSurfaceMaterial.color = _endColour * 4.5948f;
}
if (_heatVolume == null) _heatVolume = GetComponentInChildren<HeatHazardVolume>();
if (_destructionVolume == null) _destructionVolume = GetComponentInChildren<DestructionVolume>();
if (_planetDestructionVolume == null) _planetDestructionVolume = GetComponentInChildren<StarDestructionVolume>();
if (_starFluidVolume == null) _starFluidVolume = GetComponentInChildren<StarFluidVolume>();
if (atmosphere != null)
{
_fog = atmosphere?.GetComponentInChildren<PlanetaryFogController>();
_atmosphereRenderers = atmosphere?.transform?.Find("AtmoSphere")?.GetComponentsInChildren<MeshRenderer>();
}
if (scaleCurve != null)
{
maxScale = scaleCurve.keys.Select(x => x.value).Max() * size;
minScale = scaleCurve.keys.Select(x => x.value).Min() * size;
}
else
{
maxScale = 0;
minScale = 0;
scaleCurve = new AnimationCurve();
scaleCurve.AddKey(0, 1);
}
_flareEmitter = GetComponentInChildren<SolarFlareEmitter>();
_surfaceMaterial = supernova._surface._materials[0];
if (!_isProxy) SupernovaEffectHandler.RegisterStar(this);
var secondsElapsed = TimeLoop.GetSecondsElapsed();
var lifespanInSeconds = lifespan * 60;
if (secondsElapsed >= lifespanInSeconds)
{
var timeAfter = secondsElapsed - lifespanInSeconds;
if (timeAfter <= collapseTime)
Delay.RunWhen(() => Main.IsSystemReady, StartCollapse);
else if (timeAfter <= collapseTime + supernovaTime)
Delay.RunWhen(() => Main.IsSystemReady, StartSupernova);
else
Delay.RunWhen(() => Main.IsSystemReady, () => Delay.FireOnNextUpdate(() => DisableStar(true)));
}
}
public void OnDestroy()
{
SupernovaEffectHandler.UnregisterStar(this);
}
public void SetProxy(StarEvolutionController proxy)
{
_proxy = proxy;
_proxy.supernova.SetIsProxy(true);
}
private void UpdateMainSequence()
{
// Only do colour transition stuff if they set an end colour
if (EndColour != null)
{
// Use minutes elapsed if theres no resizing happening, else make it get redder the larger it is or wtv
var t = TimeLoop.GetMinutesElapsed() / lifespan;
if (maxScale != minScale) t = Mathf.InverseLerp(minScale, maxScale, CurrentScale);
if (t < 1f)
{
_currentColour = Color.Lerp(_startColour, _endColour, t);
supernova._surface._materials[0].Lerp(_startSurfaceMaterial, _endSurfaceMaterial, t);
supernova._surface._materials[0].SetFloat(ColorTime, t);
}
else
{
_currentColour = _endColour;
supernova._surface._materials[0].Lerp(_startSurfaceMaterial, _endSurfaceMaterial, 1);
supernova._surface._materials[0].SetFloat(ColorTime, 1);
}
}
else
{
_currentColour = _startColour;
supernova._surface._materials[0].Lerp(_startSurfaceMaterial, _endSurfaceMaterial, 0);
supernova._surface._materials[0].SetFloat(ColorTime, 0);
}
if (_flareEmitter != null) _flareEmitter._tint = _currentColour;
}
private void UpdateCollapse()
{
// When its collapsing we directly take over the scale
var t = _collapseTimer / collapseTime;
CurrentScale = Mathf.Lerp(_collapseStartSize, 0, t);
transform.localScale = Vector3.one * CurrentScale;
_collapseTimer += Time.deltaTime;
_currentColour = Color.Lerp(_endColour, Color.white, t);
supernova._surface._materials[0].Lerp(_collapseStartSurfaceMaterial, _collapseEndSurfaceMaterial, t);
// After the collapse is done we go supernova
if (_collapseTimer > collapseTime) StartSupernova();
}
private void UpdateSupernova()
{
// Reset the scale back to normal bc now its just the supernova scaling itself + destruction and heat volumes
transform.localScale = Vector3.one;
var t = Mathf.Clamp01((Time.time - (_supernovaStartTime + supernovaScaleStart)) / (supernovaScaleEnd - supernovaScaleStart));
// Make the destruction volume scale slightly smaller so you really have to be in the supernova to die
if (_destructionVolume != null) _destructionVolume.transform.localScale = Vector3.one * supernova.GetSupernovaRadius() * Mathf.Lerp(0.9f, 1, t);
if (_heatVolume != null) _heatVolume.transform.localScale = Vector3.one * supernova.GetSupernovaRadius();
if (_planetDestructionVolume != null)
{
_planetDestructionVolume.transform.localScale = Vector3.one * supernova.GetSupernovaRadius() * Mathf.Lerp(0.9f, 1, t);
_planetDestructionVolume.GetComponent<SphereCollider>().radius = Mathf.Lerp(0.8f, 1, t);
}
if (_stellarRemnantController != null && Time.time > _supernovaStartTime + 15) _stellarRemnantController.PartiallyActivate();
if (Time.time > _supernovaStartTime + supernovaTime)
{
if (_destructionVolume != null && _destructionVolume._shrinkingBodies.Count > 0) return;
if (_planetDestructionVolume != null && _planetDestructionVolume._shrinkingBodies.Count > 0) return;
DisableStar();
}
}
private void DisableStar(bool start = false)
{
if (controller != null) StarLightController.RemoveStar(controller);
// Just turn off the star entirely
if (_isProxy)
gameObject.SetActive(false);
else
transform.parent.gameObject.SetActive(false); // Turn off sector
if (_stellarRemnantController != null) _stellarRemnantController.FullyActivate();
if (start && _planetDestructionVolume != null)
{
foreach (var collider in Physics.OverlapSphere(_planetDestructionVolume.transform.position, _planetDestructionVolume.GetComponent<SphereCollider>().radius * supernovaSize * 0.9f))
{
if (collider.attachedRigidbody != null)
{
var body = collider.attachedRigidbody.GetComponent<OWRigidbody>();
if (body != null && body != _rigidbody)
{
// Vanish anything that is not a player-related object
if (!(collider.attachedRigidbody.CompareTag("Player") || collider.attachedRigidbody.CompareTag("Ship") || collider.attachedRigidbody.CompareTag("ShipCockpit") || collider.attachedRigidbody.CompareTag("Probe")))
{
_planetDestructionVolume.Vanish(body, new RelativeLocationData(body, _rigidbody, _planetDestructionVolume.transform));
}
}
}
}
}
if (_proxy != null) _proxy.DisableStar(start);
}
public void StartCollapse()
{
if (_isCollapsing) return;
Logger.LogVerbose($"{gameObject.transform.root.name} started collapse");
CollapseStart.Invoke();
_isCollapsing = true;
_collapseStartSize = CurrentScale;
_collapseTimer = 0f;
supernova._surface._materials[0].CopyPropertiesFromMaterial(_collapseStartSurfaceMaterial);
if (_oneShotSource != null && !PlayerState.IsSleepingAtCampfire() && !PlayerState.InDreamWorld()) _oneShotSource.PlayOneShot(AudioType.Sun_Collapse);
if (_proxy != null) _proxy.StartCollapse();
}
public void StopCollapse()
{
if (!_isCollapsing) return;
Logger.LogVerbose($"{gameObject.transform.root.name} stopped collapse");
CollapseStop.Invoke();
_isCollapsing = false;
supernova._surface._materials[0].CopyPropertiesFromMaterial(_endSurfaceMaterial);
if (_proxy != null) _proxy.StopCollapse();
}
public void StartSupernova()
{
if (_isSupernova) return;
Logger.LogVerbose($"{gameObject.transform.root.name} started supernova");
SupernovaStart.Invoke();
supernova.Activate();
_isSupernova = true;
_supernovaStartTime = Time.time;
if (atmosphere != null) atmosphere.SetActive(false);
if (_destructionVolume != null) _destructionVolume._deathType = DeathType.Supernova;
if (_planetDestructionVolume != null) _planetDestructionVolume._deathType = DeathType.Supernova;
if (_oneShotSource != null && !PlayerState.IsSleepingAtCampfire() && !PlayerState.InDreamWorld()) _oneShotSource.PlayOneShot(AudioType.Sun_Explosion);
if (_proxy != null) _proxy.StartSupernova();
}
public void StopSupernova()
{
if (!_isSupernova) return;
Logger.LogVerbose($"{gameObject.transform.root.name} stopped supernova");
SupernovaStop.Invoke();
supernova.Deactivate();
_isSupernova = false;
if (atmosphere != null) atmosphere.SetActive(true);
if (_destructionVolume != null)
{
_destructionVolume._deathType = DeathType.Energy;
_destructionVolume.transform.localScale = Vector3.one;
}
if (_planetDestructionVolume != null)
{
_planetDestructionVolume._deathType = DeathType.Energy;
_planetDestructionVolume.transform.localScale = Vector3.one;
}
if (_heatVolume != null) _heatVolume.transform.localScale = Vector3.one;
gameObject.SetActive(true);
transform.localScale = Vector3.one;
supernova._surface._materials[0] = _surfaceMaterial;
supernova._surface.transform.localScale = Vector3.one;
if (_proxy != null) _proxy.StopSupernova();
}
public bool IsCollapsing() => _isCollapsing;
public float GetCollapseProgress() => _collapseTimer / collapseTime;
public bool HasSupernovaStarted() => _isSupernova;
public bool IsPointInsideSupernova(Vector3 worldPosition) => _isSupernova && (worldPosition - transform.position).sqrMagnitude < (supernova.GetSupernovaRadius() * supernova.GetSupernovaRadius());
public bool IsPointInsideMaxSupernova(Vector3 worldPosition) => (worldPosition - transform.position).sqrMagnitude < (supernovaSize * supernovaSize);
public float GetDistanceToSupernova(Vector3 worldPosition) => Vector3.Distance(worldPosition, transform.position) - supernova.GetSupernovaRadius();
public float GetDistanceToMaxSupernova(Vector3 worldPosition) => Vector3.Distance(worldPosition, transform.position) - supernovaSize;
public float GetSupernovaRadius() => supernova.GetSupernovaRadius();
public float GetMaxSupernovaRadius() => supernovaSize;
protected new void FixedUpdate()
{
// If we've gone supernova and its been 45 seconds that means it has faded out and is gone
// The 45 is from the animation curve used for the supernova alpha
if (_isSupernova)
{
UpdateSupernova();
return;
}
if (!_isCollapsing)
{
base.FixedUpdate();
UpdateMainSequence();
if (WillExplode && (TimeLoop.GetMinutesElapsed() / lifespan) >= 1) StartCollapse();
}
else
{
UpdateCollapse();
if (_isSupernova) return;
}
// This is just all the scales stuff for the atmosphere effects
if (_fog != null)
{
_fog.fogRadius = CurrentScale * StarBuilder.OuterRadiusRatio;
_fog.lodFadeDistance = CurrentScale * StarBuilder.OuterRadiusRatio / 3f;
// The colour thing goes over one
var max = Math.Max(_currentColour.g, Math.Max(_currentColour.b, _currentColour.r));
var fogColour = _currentColour / max / 1.5f;
fogColour.a = 1f;
_fog.fogTint = fogColour;
_fog._fogTint = fogColour;
}
if (_atmosphereRenderers != null)
{
foreach (var lod in _atmosphereRenderers)
{
lod.material.SetFloat(InnerRadius, CurrentScale);
lod.material.SetFloat(OuterRadius, CurrentScale * StarBuilder.OuterRadiusRatio);
lod.material.SetColor(SkyColor, _currentColour);
}
}
}
public void SetStellarRemnantController(StellarRemnantController controller) => _stellarRemnantController = controller;
}
}