diff --git a/NewHorizons/Assets/textures/CloudEntry_GD_Island_d.png b/NewHorizons/Assets/textures/CloudEntry_GD_Island_d.png
new file mode 100644
index 00000000..5ba6654b
Binary files /dev/null and b/NewHorizons/Assets/textures/CloudEntry_GD_Island_d.png differ
diff --git a/NewHorizons/Assets/textures/OceanEntry_PlayerShip_d.png b/NewHorizons/Assets/textures/OceanEntry_PlayerShip_d.png
index ece877d5..fc670c87 100644
Binary files a/NewHorizons/Assets/textures/OceanEntry_PlayerShip_d.png and b/NewHorizons/Assets/textures/OceanEntry_PlayerShip_d.png differ
diff --git a/NewHorizons/Assets/textures/OceanEntry_PlayerShip_d_greyscale.png b/NewHorizons/Assets/textures/OceanEntry_PlayerShip_d_greyscale.png
deleted file mode 100644
index e05049b1..00000000
Binary files a/NewHorizons/Assets/textures/OceanEntry_PlayerShip_d_greyscale.png and /dev/null differ
diff --git a/NewHorizons/Assets/textures/OceanExit_PlayerShip_d.png b/NewHorizons/Assets/textures/OceanExit_PlayerShip_d.png
index d8e33ac1..7acfe355 100644
Binary files a/NewHorizons/Assets/textures/OceanExit_PlayerShip_d.png and b/NewHorizons/Assets/textures/OceanExit_PlayerShip_d.png differ
diff --git a/NewHorizons/Assets/textures/OceanExit_PlayerShip_d_greyscale.png b/NewHorizons/Assets/textures/OceanExit_PlayerShip_d_greyscale.png
deleted file mode 100644
index bba05cc4..00000000
Binary files a/NewHorizons/Assets/textures/OceanExit_PlayerShip_d_greyscale.png and /dev/null differ
diff --git a/NewHorizons/Components/SplashColourizer.cs b/NewHorizons/Components/SplashColourizer.cs
new file mode 100644
index 00000000..6cb1716f
--- /dev/null
+++ b/NewHorizons/Components/SplashColourizer.cs
@@ -0,0 +1,272 @@
+using NewHorizons.External.Configs;
+using NewHorizons.External.SerializableData;
+using NewHorizons.Utility;
+using NewHorizons.Utility.DebugTools.Menu;
+using NewHorizons.Utility.Files;
+using NewHorizons.Utility.OuterWilds;
+using NewHorizons.Utility.OWML;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace NewHorizons.Components;
+
+///
+/// When a Fluid Detector enters this volume, it's splash effects will get colourized to match whats on this planet
+///
+public class SplashColourizer : MonoBehaviour
+{
+ public float _radius;
+
+ private SphereShape _sphereShape;
+
+ private Dictionary _cachedOriginalPrefabs = new();
+ private Dictionary _cachedModifiedPrefabs = new();
+
+ private FluidDetector _playerDetector, _shipDetector, _probeDetector;
+
+ private MColor _waterColour, _cloudColour, _plasmaColour, _sandColour;
+
+ private GameObject _prefabHolder;
+
+ private bool _probeInsideVolume;
+
+ public void Awake()
+ {
+ var volume = new GameObject("Volume");
+ volume.transform.parent = transform;
+ volume.transform.localPosition = Vector3.zero;
+ volume.layer = Layer.BasicEffectVolume;
+
+ _sphereShape = gameObject.AddComponent();
+ _sphereShape.radius = _radius;
+
+ volume.AddComponent();
+
+ _prefabHolder = new GameObject("Prefabs");
+ _prefabHolder.SetActive(false);
+ }
+
+ public void Setup(float radius, MColor waterColour, MColor cloudColour, MColor plasmaColour, MColor sandColour)
+ {
+ _radius = radius;
+ if (_sphereShape != null) _sphereShape.radius = _radius;
+
+ _waterColour = waterColour;
+ _cloudColour = cloudColour;
+ _plasmaColour = plasmaColour;
+ _sandColour = sandColour;
+ }
+
+ public static void Make(GameObject planet, PlanetConfig config, float soi)
+ {
+ var water = config.Water?.tint;
+ var cloud = config.Atmosphere?.clouds?.tint;
+ var plasma = config.Lava?.tint ?? config.Star?.tint;
+ var sand = config.Sand?.tint;
+
+ if (water != null || cloud != null || plasma != null || sand != null)
+ {
+ var size = Mathf.Max(
+ soi / 2f,
+ config.Water?.size ?? 0f,
+ config.Atmosphere?.clouds?.outerCloudRadius ?? 0f,
+ config.Lava?.size ?? 0f,
+ config.Star?.size ?? 0f,
+ config.Sand?.size ?? 0f
+ ) * 2f;
+
+ var colourizer = planet.AddComponent();
+ colourizer.Setup(size, water, cloud, plasma, sand);
+ }
+ }
+
+ public void Start()
+ {
+ // Cache all prefabs
+ CachePrefabs(_playerDetector = Locator.GetPlayerDetector().GetComponent());
+ CachePrefabs(_shipDetector = Locator.GetShipDetector().GetComponent());
+ // Probe is null??????
+ // I'm losing my mind
+ // Can't cache it here
+
+ GlobalMessenger.AddListener("RetrieveProbe", OnRetrieveProbe);
+
+ // Check if player/ship are already inside
+ if ((_playerDetector.transform.position - transform.position).magnitude < _radius)
+ {
+ SetSplashEffects(_playerDetector, true);
+ }
+ if ((_shipDetector.transform.position - transform.position).magnitude < _radius)
+ {
+ SetSplashEffects(_shipDetector, true);
+ }
+ }
+
+ public void OnDestroy()
+ {
+ GlobalMessenger.RemoveListener("RetrieveProbe", OnRetrieveProbe);
+ }
+
+ private void OnRetrieveProbe(SurveyorProbe probe)
+ {
+ if (_probeInsideVolume)
+ {
+ // Else it never leaves the volume
+ SetProbeSplashEffects(false);
+ }
+ }
+
+ public void OnTriggerEnter(Collider hitCollider) => OnEnterExit(hitCollider, true);
+
+ public void OnTriggerExit(Collider hitCollider) => OnEnterExit(hitCollider, false);
+
+ ///
+ /// The probe keeps being null idgi
+ ///
+ ///
+ private bool IsProbeLaunched()
+ {
+ return Locator.GetProbe()?.IsLaunched() ?? false;
+ }
+
+ private void OnEnterExit(Collider hitCollider, bool entering)
+ {
+ if (!enabled) return;
+
+ if (hitCollider.attachedRigidbody != null)
+ {
+ var isPlayer = hitCollider.attachedRigidbody.CompareTag("Player");
+ var isShip = hitCollider.attachedRigidbody.CompareTag("Ship");
+ var isProbe = hitCollider.attachedRigidbody.CompareTag("Probe");
+
+ if (isPlayer)
+ {
+ SetSplashEffects(_playerDetector, entering);
+ if (IsProbeLaunched())
+ {
+ SetProbeSplashEffects(entering);
+ }
+ }
+ else if (isShip)
+ {
+ SetSplashEffects(_shipDetector, entering);
+ if (PlayerState.IsInsideShip())
+ {
+ SetSplashEffects(_playerDetector, entering);
+ }
+ if (IsProbeLaunched())
+ {
+ SetProbeSplashEffects(entering);
+ }
+ }
+ else if (isProbe)
+ {
+ SetProbeSplashEffects(entering);
+ }
+
+ // If the probe isn't launched we always consider it as being inside the volume
+ if (isProbe || !IsProbeLaunched())
+ {
+ _probeInsideVolume = entering;
+ }
+ }
+ }
+
+ public void CachePrefabs(FluidDetector detector)
+ {
+ foreach (var splashEffect in detector._splashEffects)
+ {
+ if (!_cachedOriginalPrefabs.ContainsKey(splashEffect))
+ {
+ _cachedOriginalPrefabs[splashEffect] = splashEffect.splashPrefab;
+ }
+ if (!_cachedModifiedPrefabs.ContainsKey(splashEffect))
+ {
+ Color? colour = null;
+ if (splashEffect.fluidType == FluidVolume.Type.CLOUD)
+ {
+ colour = _cloudColour?.ToColor();
+ }
+ switch(splashEffect.fluidType)
+ {
+ case FluidVolume.Type.CLOUD:
+ colour = _cloudColour?.ToColor();
+ break;
+ case FluidVolume.Type.WATER:
+ colour = _waterColour?.ToColor();
+ break;
+ case FluidVolume.Type.PLASMA:
+ colour = _plasmaColour?.ToColor();
+ break;
+ case FluidVolume.Type.SAND:
+ colour = _sandColour?.ToColor();
+ break;
+ }
+
+ if (colour is Color tint)
+ {
+ var flagError = false;
+ var prefab = splashEffect.splashPrefab.InstantiateInactive();
+ var meshRenderers = prefab.GetComponentsInChildren(true);
+ foreach (var meshRenderer in meshRenderers)
+ {
+ // Can't access the textures in memory so we need to have our own copies
+ var texture = ImageUtilities.GetTexture(Main.Instance, $"Assets/textures/{meshRenderer.material.mainTexture.name}.png");
+ if (texture == null)
+ {
+ NHLogger.LogError($"Go tell an NH dev to add this image texture to the mod because I can't be bothered to until somebody complains: {meshRenderer.material.mainTexture.name}");
+ GameObject.Destroy(prefab);
+ flagError = true;
+ }
+ meshRenderer.material = new(meshRenderer.material)
+ {
+ color = Color.white,
+ mainTexture = ImageUtilities.TintImage(texture, tint)
+ };
+ }
+
+ if (flagError) continue;
+
+ // Have to be active when being used by the base game classes but can't be seen
+ // Keep them active as children of an inactive game object
+ prefab.transform.parent = _prefabHolder.transform;
+ prefab.SetActive(true);
+
+ _cachedModifiedPrefabs[splashEffect] = prefab;
+ }
+ }
+ }
+ }
+
+ public void SetSplashEffects(FluidDetector detector, bool entering)
+ {
+ foreach (var splashEffect in detector._splashEffects)
+ {
+ var prefabs = entering ? _cachedModifiedPrefabs : _cachedOriginalPrefabs;
+ if (prefabs.TryGetValue(splashEffect, out var prefab))
+ {
+ splashEffect.splashPrefab = prefab;
+ }
+ }
+ }
+
+ public void SetProbeSplashEffects(bool entering)
+ {
+ // Probe detector keeps being null, I hate my life
+ if (_probeDetector == null)
+ {
+ _probeDetector ??= Locator.GetProbe()?.GetDetectorObject()?.GetComponent();
+ if (_probeDetector != null)
+ {
+ CachePrefabs(_probeDetector);
+ }
+ }
+
+ _probeInsideVolume = entering;
+
+ if (_probeDetector != null)
+ {
+ SetSplashEffects(_probeDetector, entering);
+ }
+ }
+}
diff --git a/NewHorizons/Handlers/PlanetCreationHandler.cs b/NewHorizons/Handlers/PlanetCreationHandler.cs
index a778b8ad..c23649a8 100644
--- a/NewHorizons/Handlers/PlanetCreationHandler.cs
+++ b/NewHorizons/Handlers/PlanetCreationHandler.cs
@@ -19,6 +19,7 @@ using UnityEngine;
using NewHorizons.Streaming;
using Newtonsoft.Json;
using NewHorizons.External.Modules.VariableSize;
+using NewHorizons.Components;
namespace NewHorizons.Handlers
{
@@ -727,6 +728,8 @@ namespace NewHorizons.Handlers
SupernovaEffectBuilder.Make(go, sector, body.Config, body.Mod, procGen, ambientLight, fog, atmosphere, null, fog?._fogImpostor);
}
+ SplashColourizer.Make(go, body.Config, sphereOfInfluence);
+
// We allow removing children afterwards so you can also take bits off of the modules you used
if (body.Config.removeChildren != null) RemoveChildren(go, body);