using System.Runtime.Serialization; using NewHorizons.Components; using NewHorizons.Utility; using UnityEngine; using Logger = NewHorizons.Utility.Logger; using NewHorizons.External.Modules.VariableSize; namespace NewHorizons.Builder.Body { public static class FunnelBuilder { private static readonly int FogColor = Shader.PropertyToID("_FogColor"); private static readonly int EmissionColor = Shader.PropertyToID("_EmissionColor"); private static readonly int HeightScale = Shader.PropertyToID("_HeightScale"); public static void Make(GameObject planetGO, ConstantForceDetector detector, OWRigidbody rigidbody, FunnelModule module) { var funnelType = module.Type; var funnelGO = new GameObject($"{planetGO.name.Replace("_Body", "")}Funnel_Body"); funnelGO.SetActive(false); funnelGO.transform.parent = planetGO.transform; funnelGO.AddComponent(); var matchMotion = funnelGO.AddComponent(); matchMotion.SetBodyToMatch(rigidbody); funnelGO.AddComponent(); funnelGO.AddComponent(); var detectorGO = new GameObject("Detector_Funnel"); detectorGO.transform.parent = funnelGO.transform; var funnelDetector = detectorGO.AddComponent(); funnelDetector._inheritDetector = detector; funnelDetector._detectableFields = new ForceVolume[0]; detectorGO.AddComponent(); var scaleRoot = new GameObject("ScaleRoot"); scaleRoot.transform.parent = funnelGO.transform; scaleRoot.transform.rotation = Quaternion.identity; scaleRoot.transform.localPosition = Vector3.zero; scaleRoot.transform.localScale = new Vector3(1, 1, 1); var proxyGO = GameObject.Instantiate(GameObject.Find("SandFunnel_Body/ScaleRoot/Proxy_SandFunnel"), scaleRoot.transform); proxyGO.name = "Proxy_Funnel"; var geoGO = GameObject.Instantiate(GameObject.Find("SandFunnel_Body/ScaleRoot/Geo_SandFunnel"), scaleRoot.transform); geoGO.name = "Geo_Funnel"; var volumesGO = GameObject.Instantiate(GameObject.Find("SandFunnel_Body/ScaleRoot/Volumes_SandFunnel"), scaleRoot.transform); volumesGO.name = "Volumes_Funnel"; var sfv = volumesGO.GetComponentInChildren(); var fluidVolume = sfv.gameObject; switch (funnelType) { case FunnelType.Sand: sfv._fluidType = FluidVolume.Type.SAND; break; case FunnelType.Water: sfv._fluidType = FluidVolume.Type.WATER; GameObject.Destroy(geoGO.transform.Find("Effects_HT_SandColumn/SandColumn_Interior").gameObject); var waterMaterials = GameObject.Find("TimberHearth_Body/Sector_TH/Geometry_TH/Terrain_TH_Water_v3/Village_Upper_Water/Village_Upper_Water_Geo").GetComponent().materials; var materials = new Material[waterMaterials.Length]; for (int i = 0; i < waterMaterials.Length; i++) { materials[i] = new Material(waterMaterials[i]); if (module.Tint != null) { materials[i].SetColor(FogColor, module.Tint.ToColor()); } } // Proxy var proxyExterior = proxyGO.transform.Find("SandColumn_Exterior (1)"); proxyExterior.name = "WaterColumn_Exterior"; var proxyExteriorMR = proxyExterior.GetComponent(); proxyExteriorMR.material = materials[0]; proxyExteriorMR.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; /* var proxyInterior = GameObject.Instantiate(proxyExterior, proxyGO.transform); proxyInterior.name = "WaterColumn_Interior"; var proxyInteriorMR = proxyInterior.GetComponent(); proxyInteriorMR.material = materials[1]; proxyInteriorMR.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; */ // Geometry var geoExterior = geoGO.transform.Find("Effects_HT_SandColumn/SandColumn_Exterior"); geoExterior.name = "WaterColumn_Exterior"; var geoExteriorMR = geoExterior.GetComponent(); geoExteriorMR.material = materials[0]; geoExteriorMR.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; /* var geoInterior = GameObject.Instantiate(geoExterior, geoGO.transform); geoInterior.name = "WaterColumn_Interior"; var geoInteriorMR = geoInterior.GetComponent(); geoInteriorMR.material = materials[1]; geoInteriorMR.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; */ break; case FunnelType.Lava: case FunnelType.Star: sfv._fluidType = FluidVolume.Type.PLASMA; GameObject.Destroy(geoGO.transform.Find("Effects_HT_SandColumn/SandColumn_Interior").gameObject); var lavaMaterial = new Material(GameObject.Find("VolcanicMoon_Body/MoltenCore_VM/LavaSphere").GetComponent().material); lavaMaterial.mainTextureOffset = new Vector2(0.1f, 0.2f); lavaMaterial.mainTextureScale = new Vector2(1f, 3f); if (module.Tint != null) { lavaMaterial.SetColor(EmissionColor, module.Tint.ToColor()); } proxyGO.GetComponentInChildren().material = lavaMaterial; geoGO.GetComponentInChildren().material = lavaMaterial; if (funnelType == FunnelType.Lava) { lavaMaterial.SetFloat(HeightScale, 0); AddDestructionVolumes(fluidVolume, DeathType.Lava); } else if (funnelType == FunnelType.Star) { lavaMaterial.renderQueue = 2999; lavaMaterial.SetFloat(HeightScale, 100000); AddDestructionVolumes(fluidVolume, DeathType.Energy); } break; } var sector = planetGO.GetComponent().GetPrimaryBody().GetRootSector(); proxyGO.GetComponent().SetSector(sector); geoGO.GetComponent().SetSector(sector); volumesGO.GetComponent().SetSector(sector); funnelGO.transform.localPosition = Vector3.zero; var funnelSizeController = funnelGO.AddComponent(); if (module.Curve != null) { var curve = new AnimationCurve(); foreach (var pair in module.Curve) { curve.AddKey(new Keyframe(pair.Time, pair.Value)); } funnelSizeController.scaleCurve = curve; } funnelSizeController.anchor = planetGO.transform; // Finish up next tick Main.Instance.ModHelper.Events.Unity.FireOnNextUpdate(() => PostMake(funnelGO, funnelSizeController, module)); } private static void PostMake(GameObject funnelGO, FunnelController funnelSizeController, FunnelModule module) { var targetAO = AstroObjectLocator.GetAstroObject(module.Target); var target = targetAO?.GetAttachedOWRigidbody(); if (target == null) { if (targetAO != null) Logger.LogError($"Found funnel target ({targetAO.name}) but couldn't find rigidbody for the funnel {funnelGO.name}"); else Logger.LogError($"Couldn't find the target ({module.Target}) for the funnel {funnelGO.name}"); return; } funnelSizeController.target = target.gameObject.transform; funnelGO.SetActive(true); } private static void AddDestructionVolumes(GameObject go, DeathType deathType) { // Gotta put destruction volumes on the children reeeeeeeeee foreach (Transform child in go.transform) { var capsuleShape = child.GetComponent(); var capsuleCollider = child.gameObject.AddComponent(); capsuleCollider.radius = capsuleShape.radius; capsuleCollider.height = capsuleShape.height; capsuleCollider.isTrigger = true; child.gameObject.AddComponent(); var destructionVolume = child.gameObject.AddComponent(); destructionVolume._deathType = deathType; // Only stars should destroy planets destructionVolume._onlyAffectsPlayerAndShip = deathType != DeathType.Energy; } } } }