using QSB.Player; using UnityEngine; namespace QSB.Utility { internal class CustomRelativisticParticleSystem : MonoBehaviour { private ParticleSystem _particleSystem; private Transform _simulationSpace; private Quaternion _rotation; private ParticleSystem.MainModule _mainModule; private ParticleSystem.VelocityOverLifetimeModule _velocityOverLifetimeModule; private ModuleVector _velocityOverLifetimeVector; private ParticleSystem.LimitVelocityOverLifetimeModule _limitVelocityOverLifetimeModule; private ModuleVector _limitVelocityOverLifetimeVector; private ParticleSystem.ForceOverLifetimeModule _forceOverLifetimeModule; private ModuleVector _forceOverLifetimeVector; private bool _isReady; private void Awake() { if (!_isReady) { DebugLog.ToConsole($"Warning - Awake() ran when _isReady is false!", OWML.Common.MessageType.Warning); return; } _particleSystem = GetComponent(); if (_particleSystem == null) { DebugLog.ToConsole($"Error - _particleSystem is null.", OWML.Common.MessageType.Error); _isReady = false; return; } _rotation = transform.rotation; _mainModule = _particleSystem.main; _mainModule.simulationSpace = ParticleSystemSimulationSpace.Custom; _mainModule.customSimulationSpace = _simulationSpace; _velocityOverLifetimeModule = _particleSystem.velocityOverLifetime; _velocityOverLifetimeVector = new ModuleVector(_velocityOverLifetimeModule.x, _velocityOverLifetimeModule.y, _velocityOverLifetimeModule.z); _limitVelocityOverLifetimeModule = _particleSystem.limitVelocityOverLifetime; _limitVelocityOverLifetimeVector = new ModuleVector(_limitVelocityOverLifetimeModule.limitX, _limitVelocityOverLifetimeModule.limitY, _limitVelocityOverLifetimeModule.limitZ); _forceOverLifetimeModule = _particleSystem.forceOverLifetime; _forceOverLifetimeVector = new ModuleVector(_forceOverLifetimeModule.x, _forceOverLifetimeModule.y, _forceOverLifetimeModule.z); } public void Init(PlayerInfo playerInfo) { var space = new GameObject($"{name}_ReferenceFrame"); if (playerInfo.Body == null) { DebugLog.ToConsole($"Error - Player body is null! Did you create this too early?", OWML.Common.MessageType.Error); return; } space.transform.parent = playerInfo.Body.transform; _simulationSpace = space.transform; _isReady = true; } private void FixedUpdate() { if (!QSBCore.WorldObjectsReady || !_isReady) { return; } if (_simulationSpace == null) { DebugLog.ToConsole($"Error - _simulationSpace is null.", OWML.Common.MessageType.Error); _isReady = false; } _simulationSpace.rotation = _rotation; if (!_velocityOverLifetimeModule.enabled && (!_limitVelocityOverLifetimeModule.enabled || !_limitVelocityOverLifetimeModule.separateAxes) && !_forceOverLifetimeModule.enabled) { return; } var rotation = Quaternion.Inverse(_rotation) * transform.rotation; if (_velocityOverLifetimeModule.enabled) { _velocityOverLifetimeVector.GetRotatedVector(rotation, out var x, out var y, out var z); _velocityOverLifetimeModule.x = x; _velocityOverLifetimeModule.y = y; _velocityOverLifetimeModule.z = z; } if (_limitVelocityOverLifetimeModule.enabled) { _limitVelocityOverLifetimeVector.GetRotatedVector(rotation, out var x, out var y, out var z); _limitVelocityOverLifetimeModule.limitX = x; _limitVelocityOverLifetimeModule.limitY = y; _limitVelocityOverLifetimeModule.limitZ = z; } if (_forceOverLifetimeModule.enabled) { _forceOverLifetimeVector.GetRotatedVector(rotation, out var x, out var y, out var z); _forceOverLifetimeModule.x = x; _forceOverLifetimeModule.y = y; _forceOverLifetimeModule.z = z; } } private struct ModuleVector { public ParticleSystem.MinMaxCurve OrigX; public ParticleSystem.MinMaxCurve OrigY; public ParticleSystem.MinMaxCurve OrigZ; public ModuleVector(ParticleSystem.MinMaxCurve x, ParticleSystem.MinMaxCurve y, ParticleSystem.MinMaxCurve z) { OrigX = x; OrigY = y; OrigZ = z; } public void GetRotatedVector(Quaternion rotation, out ParticleSystem.MinMaxCurve x, out ParticleSystem.MinMaxCurve y, out ParticleSystem.MinMaxCurve z) { if (OrigX.mode == ParticleSystemCurveMode.Constant) { var vector = rotation * new Vector3(OrigX.constant, OrigY.constant, OrigZ.constant); x = new ParticleSystem.MinMaxCurve(vector.x); y = new ParticleSystem.MinMaxCurve(vector.y); z = new ParticleSystem.MinMaxCurve(vector.z); } else if (OrigX.mode == ParticleSystemCurveMode.TwoConstants) { var vector2 = rotation * new Vector3(OrigX.constantMin, OrigY.constantMin, OrigZ.constantMin); var vector3 = rotation * new Vector3(OrigX.constantMax, OrigY.constantMax, OrigZ.constantMax); x = new ParticleSystem.MinMaxCurve(vector2.x, vector3.x); y = new ParticleSystem.MinMaxCurve(vector2.y, vector3.y); z = new ParticleSystem.MinMaxCurve(vector2.z, vector3.z); } else { Debug.LogWarning("Cannot properly rotate Module Curves! Use Constants mode instead, dummy."); x = OrigX; y = OrigY; z = OrigZ; } } } } }