diff --git a/NewHorizons/Assets/translations/english.json b/NewHorizons/Assets/translations/english.json index 82872554..d0ec3fa3 100644 --- a/NewHorizons/Assets/translations/english.json +++ b/NewHorizons/Assets/translations/english.json @@ -1,5 +1,5 @@ { - "$schema": "https://raw.githubusercontent.com/xen-42/outer-wilds-new-horizons/main/NewHorizons/Schemas/translation_schema.json", + "$schema": "https://raw.githubusercontent.com/outer-wilds-new-horizons/new-horizons/main/NewHorizons/Schemas/translation_schema.json", "DialogueDictionary": { "NEW_HORIZONS_WARP_DRIVE_DIALOGUE_1": "Your ship is now equiped with a warp drive!", "NEW_HORIZONS_WARP_DRIVE_DIALOGUE_2": "You can use the new \"Interstellar Mode\" page in the ship log to lock-on your autopilot to another star system.", @@ -15,6 +15,8 @@ "LOCK_AUTOPILOT_WARP": "Lock Autopilot to Star System", "RICH_PRESENCE_EXPLORING": "Exploring {0}.", "RICH_PRESENCE_WARPING": "Warping to {0}.", + "OUTDATED_VERSION_WARNING": "WARNING\n\nNew Horizons only works on version {0} or higher. You're on version {1}.\n\nPlease update your game or uninstall NH.", + "JSON_FAILED_TO_LOAD": "Invalid file(s): {0}", "Vessel": "Vessel", "VESSEL": "VESSEL" }, diff --git a/NewHorizons/Assets/translations/french.json b/NewHorizons/Assets/translations/french.json index 42c365a1..e86e82ea 100644 --- a/NewHorizons/Assets/translations/french.json +++ b/NewHorizons/Assets/translations/french.json @@ -1,5 +1,5 @@ { - "$schema": "https://raw.githubusercontent.com/xen-42/outer-wilds-new-horizons/main/NewHorizons/Schemas/translation_schema.json", + "$schema": "https://raw.githubusercontent.com/outer-wilds-new-horizons/new-horizons/main/NewHorizons/Schemas/translation_schema.json", "DialogueDictionary": { "NEW_HORIZONS_WARP_DRIVE_DIALOGUE_1": "Votre fusée est maintenant équipé d'un moteur de distorsion!", "NEW_HORIZONS_WARP_DRIVE_DIALOGUE_2": "Vous pouvez utiliser la nouvelle page \"Mode Interstellaire\" dans le journal de bord pour diriger votre pilote automatique vers un autre système solaire", @@ -15,6 +15,8 @@ "LOCK_AUTOPILOT_WARP": "Visez le pilote automatique", "RICH_PRESENCE_EXPLORING": "En explorant {0}.", "RICH_PRESENCE_WARPING": "En route vers {0}.", + "OUTDATED_VERSION_WARNING": "AVERTISSEMENT\n\nNew Horizons fonctionne seulement sur la version {0} ou plus récente. Vous êtes sur la version {1}.\n\nVeuillez mettre à jour votre jeu ou désinstaller NH.", + "JSON_FAILED_TO_LOAD": "Fichier(s) invalide(s): {0}", "Vessel": "Vaisseau", "VESSEL": "VAISSEAU" }, diff --git a/NewHorizons/Assets/translations/german.json b/NewHorizons/Assets/translations/german.json index b4c51471..54428e6f 100644 --- a/NewHorizons/Assets/translations/german.json +++ b/NewHorizons/Assets/translations/german.json @@ -1,5 +1,5 @@ { - "$schema": "https://raw.githubusercontent.com/xen-42/outer-wilds-new-horizons/main/NewHorizons/Schemas/translation_schema.json", + "$schema": "https://raw.githubusercontent.com/outer-wilds-new-horizons/new-horizons/main/NewHorizons/Schemas/translation_schema.json", "DialogueDictionary": { "NEW_HORIZONS_WARP_DRIVE_DIALOGUE_1": "Dein Schiff ist nun mit einem Warpantrieb ausgestattet!", "NEW_HORIZONS_WARP_DRIVE_DIALOGUE_2": "Du kannst den neuen \"Interstellar Modus\" aus dem Schiffslogbuch auswählen um mit dem Autopilot in andere Sternensysteme zu reisen.", diff --git a/NewHorizons/Assets/translations/russian.json b/NewHorizons/Assets/translations/russian.json index 7b41b3d4..ae238900 100644 --- a/NewHorizons/Assets/translations/russian.json +++ b/NewHorizons/Assets/translations/russian.json @@ -1,5 +1,5 @@ { - "$schema": "https://raw.githubusercontent.com/xen-42/outer-wilds-new-horizons/main/NewHorizons/Schemas/translation_schema.json", + "$schema": "https://raw.githubusercontent.com/outer-wilds-new-horizons/new-horizons/main/NewHorizons/Schemas/translation_schema.json", "DialogueDictionary": { "NEW_HORIZONS_WARP_DRIVE_DIALOGUE_1": "Твой корабль теперь экипирован варп-двигателем!", "NEW_HORIZONS_WARP_DRIVE_DIALOGUE_2": "Ты можешь использовать новый \"Режим Interstellar\" на своем корабле, что-бы закрепить автопилот на выбранную звёздную систему.", diff --git a/NewHorizons/Assets/translations/spanish_la.json b/NewHorizons/Assets/translations/spanish_la.json index 45e15e40..053b3659 100644 --- a/NewHorizons/Assets/translations/spanish_la.json +++ b/NewHorizons/Assets/translations/spanish_la.json @@ -1,5 +1,5 @@ { - "$schema": "https://raw.githubusercontent.com/xen-42/outer-wilds-new-horizons/main/NewHorizons/Schemas/translation_schema.json", + "$schema": "https://raw.githubusercontent.com/outer-wilds-new-horizons/new-horizons/main/NewHorizons/Schemas/translation_schema.json", "DialogueDictionary": { "NEW_HORIZONS_WARP_DRIVE_DIALOGUE_1": "¡Tu nave ahora tiene un motor de curvatura!", "NEW_HORIZONS_WARP_DRIVE_DIALOGUE_2": "Puedes usar la nueva página de \"modo interestelar\" en el registro de la nave para fijar el piloto automático a otro sistema solar.", diff --git a/NewHorizons/Assets/xen.newhorizons b/NewHorizons/Assets/xen.newhorizons index 1a754842..de0815e9 100644 Binary files a/NewHorizons/Assets/xen.newhorizons and b/NewHorizons/Assets/xen.newhorizons differ diff --git a/NewHorizons/Assets/xen.newhorizons.manifest b/NewHorizons/Assets/xen.newhorizons.manifest index d45ddec5..8902bd7f 100644 --- a/NewHorizons/Assets/xen.newhorizons.manifest +++ b/NewHorizons/Assets/xen.newhorizons.manifest @@ -1,14 +1,16 @@ ManifestFileVersion: 0 -CRC: 1014555239 +CRC: 2022446871 Hashes: AssetFileHash: serializedVersion: 2 - Hash: 45fa3430ee7bea1e8384e57927fc0f76 + Hash: 083882699617744b8fc49234bb8cb795 TypeTreeHash: serializedVersion: 2 - Hash: 55d48f4ad9c3b13330b9eb5ee5686477 + Hash: 10a6a558690295dadb3dd990eda0821a HashAppended: 0 ClassTypes: +- Class: 21 + Script: {instanceID: 0} - Class: 48 Script: {instanceID: 0} SerializeReferenceClassIdentifiers: [] @@ -18,6 +20,7 @@ Assets: - Assets/Shaders/SphereTextureWrapperNormal.shader - Assets/Shaders/UnlitRing1Pixel.shader - Assets/Shaders/UnlitTransparent.shader +- Assets/Resources/TransparentCloud.mat - Assets/Shaders/StandardCullOFF.shader - Assets/Shaders/Ring1Pixel.shader Dependencies: [] diff --git a/NewHorizons/Builder/Atmosphere/AtmosphereBuilder.cs b/NewHorizons/Builder/Atmosphere/AtmosphereBuilder.cs index 7646a205..9dd1e8a8 100644 --- a/NewHorizons/Builder/Atmosphere/AtmosphereBuilder.cs +++ b/NewHorizons/Builder/Atmosphere/AtmosphereBuilder.cs @@ -59,7 +59,7 @@ namespace NewHorizons.Builder.Atmosphere } } - material.SetFloat(InnerRadius, atmosphereModule.clouds != null ? atmosphereModule.size : surfaceSize); + material.SetFloat(InnerRadius, (atmosphereModule.clouds != null && atmosphereModule.clouds.cloudsPrefab != CloudPrefabType.Transparent) ? atmosphereModule.size : surfaceSize); material.SetFloat(OuterRadius, atmosphereModule.size * 1.2f); if (atmosphereModule.atmosphereTint != null) material.SetColor(SkyColor, atmosphereModule.atmosphereTint.ToColor()); diff --git a/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs b/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs index 6732b18f..d5b15f89 100644 --- a/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs +++ b/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs @@ -11,6 +11,7 @@ namespace NewHorizons.Builder.Atmosphere { private static Material[] _gdCloudMaterials; private static Material[] _qmCloudMaterials; + private static Material _transparentCloud; private static GameObject _lightningPrefab; private static Texture2D _colorRamp; private static readonly int Color = Shader.PropertyToID("_Color"); @@ -18,6 +19,7 @@ namespace NewHorizons.Builder.Atmosphere private static readonly int MainTex = Shader.PropertyToID("_MainTex"); private static readonly int RampTex = Shader.PropertyToID("_RampTex"); private static readonly int CapTex = Shader.PropertyToID("_CapTex"); + private static readonly int Smoothness = Shader.PropertyToID("_Glossiness"); public static void Make(GameObject planetGO, Sector sector, AtmosphereModule atmo, bool cloaked, IModBehaviour mod) { @@ -28,7 +30,15 @@ namespace NewHorizons.Builder.Atmosphere cloudsMainGO.SetActive(false); cloudsMainGO.transform.parent = sector?.transform ?? planetGO.transform; - MakeTopClouds(cloudsMainGO, atmo, mod); + if (atmo.clouds.cloudsPrefab != CloudPrefabType.Transparent) MakeTopClouds(cloudsMainGO, atmo, mod); + else + { + MakeTransparentClouds(cloudsMainGO, atmo, mod); + if (atmo.clouds.hasLightning) MakeLightning(cloudsMainGO, sector, atmo); + cloudsMainGO.transform.position = planetGO.transform.TransformPoint(Vector3.zero); + cloudsMainGO.SetActive(true); + return; + } GameObject cloudsBottomGO = new GameObject("BottomClouds"); cloudsBottomGO.SetActive(false); @@ -114,7 +124,7 @@ namespace NewHorizons.Builder.Atmosphere lightning.transform.localPosition = Vector3.zero; var lightningGenerator = lightning.GetComponent(); - lightningGenerator._altitude = (atmo.clouds.outerCloudRadius + atmo.clouds.innerCloudRadius) / 2f; + lightningGenerator._altitude = atmo.clouds.cloudsPrefab != CloudPrefabType.Transparent ? (atmo.clouds.outerCloudRadius + atmo.clouds.innerCloudRadius) / 2f : atmo.clouds.outerCloudRadius; if (noAudio) { lightningGenerator._audioPrefab = null; @@ -177,7 +187,7 @@ namespace NewHorizons.Builder.Atmosphere var material = new Material(Shader.Find("Standard")); if (atmo.clouds.unlit) material.renderQueue = 3000; material.name = atmo.clouds.unlit ? "BasicCloud" : "BasicShadowCloud"; - material.SetFloat(279, 0f); // smoothness + material.SetFloat(Smoothness, 0f); tempArray[0] = material; } else @@ -218,5 +228,65 @@ namespace NewHorizons.Builder.Atmosphere return cloudsTopGO; } + + public static GameObject MakeTransparentClouds(GameObject rootObject, AtmosphereModule atmo, IModBehaviour mod, bool isProxy = false) + { + Texture2D image; + + try + { + image = ImageUtilities.GetTexture(mod, atmo.clouds.texturePath); + } + catch (Exception e) + { + Logger.LogError($"Couldn't load Cloud texture for [{atmo.clouds.texturePath}]:\n{e}"); + return null; + } + + GameObject cloudsTransparentGO = new GameObject("TransparentClouds"); + cloudsTransparentGO.SetActive(false); + cloudsTransparentGO.transform.parent = rootObject.transform; + cloudsTransparentGO.transform.localScale = Vector3.one * atmo.clouds.outerCloudRadius; + + MeshFilter filter = cloudsTransparentGO.AddComponent(); + filter.mesh = SearchUtilities.Find("CloudsTopLayer_GD").GetComponent().mesh; + + MeshRenderer renderer = cloudsTransparentGO.AddComponent(); + if (_transparentCloud == null) _transparentCloud = Main.NHAssetBundle.LoadAsset("Assets/Resources/TransparentCloud.mat"); + var material = new Material(_transparentCloud); + material.name = "TransparentClouds_" + image.name; + material.SetTexture(MainTex, image); + renderer.sharedMaterial = material; + + if (!isProxy) + { + GameObject tcrqcGO = new GameObject("TransparentCloudRenderQueueController"); + tcrqcGO.transform.SetParent(cloudsTransparentGO.transform, false); + tcrqcGO.layer = LayerMask.NameToLayer("BasicEffectVolume"); + + var shape = tcrqcGO.AddComponent(); + shape.radius = 1; + + var owTriggerVolume = tcrqcGO.AddComponent(); + owTriggerVolume._shape = shape; + + TransparentCloudRenderQueueController tcrqc = tcrqcGO.AddComponent(); + tcrqc.renderer = renderer; + } + + if (atmo.clouds.rotationSpeed != 0f) + { + var rt = cloudsTransparentGO.AddComponent(); + rt._localAxis = Vector3.up; + rt._degreesPerSecond = atmo.clouds.rotationSpeed; + rt._randomizeRotationRate = false; + } + + cloudsTransparentGO.transform.localPosition = Vector3.zero; + + cloudsTransparentGO.SetActive(true); + + return cloudsTransparentGO; + } } } diff --git a/NewHorizons/Builder/Atmosphere/EffectsBuilder.cs b/NewHorizons/Builder/Atmosphere/EffectsBuilder.cs index c177a9ed..6394e02f 100644 --- a/NewHorizons/Builder/Atmosphere/EffectsBuilder.cs +++ b/NewHorizons/Builder/Atmosphere/EffectsBuilder.cs @@ -21,7 +21,6 @@ namespace NewHorizons.Builder.Atmosphere SCG._waitForStreaming = false; var minHeight = surfaceSize; - var maxHeight = config.Atmosphere.size; if (config.HeightMap?.minHeight != null) { if (config.Water?.size >= config.HeightMap.minHeight) minHeight = config.Water.size; // use sea level if its higher @@ -30,6 +29,9 @@ namespace NewHorizons.Builder.Atmosphere else if (config.Water?.size != null) minHeight = config.Water.size; else if (config.Lava?.size != null) minHeight = config.Lava.size; + var maxHeight = config.Atmosphere.size; + if (config.Atmosphere.clouds?.outerCloudRadius != null) maxHeight = config.Atmosphere.clouds.outerCloudRadius; + if (config.Atmosphere.hasRain) { var rainGO = GameObject.Instantiate(SearchUtilities.Find("GiantsDeep_Body/Sector_GD/Sector_GDInterior/Effects_GDInterior/Effects_GD_Rain"), effectsGO.transform); diff --git a/NewHorizons/Builder/Body/ProxyBuilder.cs b/NewHorizons/Builder/Body/ProxyBuilder.cs index 53681a33..164fb463 100644 --- a/NewHorizons/Builder/Body/ProxyBuilder.cs +++ b/NewHorizons/Builder/Body/ProxyBuilder.cs @@ -111,7 +111,8 @@ namespace NewHorizons.Builder.Body if (body.Config.Atmosphere.clouds != null) { - topClouds = CloudsBuilder.MakeTopClouds(proxy, body.Config.Atmosphere, body.Mod).GetComponent(); + if (body.Config.Atmosphere.clouds.cloudsPrefab != External.Modules.CloudPrefabType.Transparent) topClouds = CloudsBuilder.MakeTopClouds(proxy, body.Config.Atmosphere, body.Mod).GetComponent(); + else topClouds = CloudsBuilder.MakeTransparentClouds(proxy, body.Config.Atmosphere, body.Mod, true).GetAddComponent(); if (body.Config.Atmosphere.clouds.hasLightning) lightningGenerator = CloudsBuilder.MakeLightning(proxy, null, body.Config.Atmosphere, true); diff --git a/NewHorizons/Builder/General/SpawnPointBuilder.cs b/NewHorizons/Builder/General/SpawnPointBuilder.cs index f094a57a..a224333d 100644 --- a/NewHorizons/Builder/General/SpawnPointBuilder.cs +++ b/NewHorizons/Builder/General/SpawnPointBuilder.cs @@ -1,5 +1,7 @@ using NewHorizons.External.Modules; using NewHorizons.Utility; +using System; +using System.Reflection; using UnityEngine; using Logger = NewHorizons.Utility.Logger; namespace NewHorizons.Builder.General @@ -89,38 +91,28 @@ namespace NewHorizons.Builder.General public static void SuitUp() { suitUpQueued = false; - if (Locator.GetPlayerController()._isWearingSuit) return; - - Locator.GetPlayerTransform().GetComponent().SuitUp(false, true, true); - - // Make the ship act as if the player took the suit - var spv = SearchUtilities.Find("Ship_Body/Module_Supplies/Systems_Supplies/ExpeditionGear")?.GetComponent(); - - if (spv == null) return; - - spv._containsSuit = false; - - if (spv._allowSuitReturn) + if (!Locator.GetPlayerController()._isWearingSuit) { - spv._interactVolume.ChangePrompt(UITextType.ReturnSuitPrompt, spv._pickupSuitCommandIndex); + var spv = SearchUtilities.Find("Ship_Body/Module_Supplies/Systems_Supplies/ExpeditionGear")?.GetComponent(); + if (spv != null) + { + var command = spv._interactVolume.GetInteractionAt(spv._pickupSuitCommandIndex).inputCommand; + + // Make the ship act as if the player took the suit + var eventDelegate = (MulticastDelegate)typeof(MultipleInteractionVolume).GetField( + nameof(MultipleInteractionVolume.OnPressInteract), + BindingFlags.Instance | BindingFlags.NonPublic) + .GetValue(spv._interactVolume); + foreach (var handler in eventDelegate.GetInvocationList()) + { + handler.Method.Invoke(handler.Target, new object[] { command }); + } + } + else + { + Locator.GetPlayerTransform().GetComponent().SuitUp(false, true, true); + } } - else - { - spv._interactVolume.EnableSingleInteraction(false, spv._pickupSuitCommandIndex); - } - - spv._timer = 0f; - spv._index = 0; - - spv.OnSuitUp(); - - GameObject suitGeometry = spv._suitGeometry; - if (suitGeometry != null) suitGeometry.SetActive(false); - - OWCollider suitOWCollider = spv._suitOWCollider; - if (suitOWCollider != null) suitOWCollider.SetActivation(false); - - spv.enabled = true; } } } diff --git a/NewHorizons/Builder/ShipLog/RevealBuilder.cs b/NewHorizons/Builder/ShipLog/RevealBuilder.cs index 95f118b6..3bfcdd69 100644 --- a/NewHorizons/Builder/ShipLog/RevealBuilder.cs +++ b/NewHorizons/Builder/ShipLog/RevealBuilder.cs @@ -41,7 +41,27 @@ namespace NewHorizons.Builder.ShipLog GameObject revealTriggerVolume = new GameObject("Reveal Volume (" + info.revealOn + ")"); revealTriggerVolume.SetActive(false); revealTriggerVolume.transform.parent = sector?.transform ?? planetGO.transform; + + if (!string.IsNullOrEmpty(info.rename)) + { + revealTriggerVolume.name = info.rename; + } + + if (!string.IsNullOrEmpty(info.parentPath)) + { + var newParent = planetGO.transform.Find(info.parentPath); + if (newParent != null) + { + revealTriggerVolume.transform.parent = newParent; + } + else + { + Logger.LogWarning($"Cannot find parent object at path: {planetGO.name}/{info.parentPath}"); + } + } + revealTriggerVolume.transform.position = planetGO.transform.TransformPoint(info.position ?? Vector3.zero); + return revealTriggerVolume; } diff --git a/NewHorizons/Builder/Volumes/AudioVolumeBuilder.cs b/NewHorizons/Builder/Volumes/AudioVolumeBuilder.cs index b487670a..bbb201d8 100644 --- a/NewHorizons/Builder/Volumes/AudioVolumeBuilder.cs +++ b/NewHorizons/Builder/Volumes/AudioVolumeBuilder.cs @@ -19,6 +19,25 @@ namespace NewHorizons.Builder.Volumes go.SetActive(false); go.transform.parent = sector?.transform ?? planetGO.transform; + + if (!string.IsNullOrEmpty(info.rename)) + { + go.name = info.rename; + } + + if (!string.IsNullOrEmpty(info.parentPath)) + { + var newParent = planetGO.transform.Find(info.parentPath); + if (newParent != null) + { + go.transform.parent = newParent; + } + else + { + Logger.LogWarning($"Cannot find parent object at path: {planetGO.name}/{info.parentPath}"); + } + } + go.transform.position = planetGO.transform.TransformPoint(info.position != null ? (Vector3)info.position : Vector3.zero); go.layer = LayerMask.NameToLayer("AdvancedEffectVolume"); diff --git a/NewHorizons/Builder/Volumes/HazardVolumeBuilder.cs b/NewHorizons/Builder/Volumes/HazardVolumeBuilder.cs index 67c6f22c..705570d5 100644 --- a/NewHorizons/Builder/Volumes/HazardVolumeBuilder.cs +++ b/NewHorizons/Builder/Volumes/HazardVolumeBuilder.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using UnityEngine; +using Logger = NewHorizons.Utility.Logger; namespace NewHorizons.Builder.Volumes { @@ -16,6 +17,25 @@ namespace NewHorizons.Builder.Volumes go.SetActive(false); go.transform.parent = sector?.transform ?? planetGO.transform; + + if (!string.IsNullOrEmpty(info.rename)) + { + go.name = info.rename; + } + + if (!string.IsNullOrEmpty(info.parentPath)) + { + var newParent = planetGO.transform.Find(info.parentPath); + if (newParent != null) + { + go.transform.parent = newParent; + } + else + { + Logger.LogWarning($"Cannot find parent object at path: {planetGO.name}/{info.parentPath}"); + } + } + go.transform.position = planetGO.transform.TransformPoint(info.position != null ? (Vector3)info.position : Vector3.zero); go.layer = LayerMask.NameToLayer("BasicEffectVolume"); diff --git a/NewHorizons/Builder/Volumes/NotificationVolumeBuilder.cs b/NewHorizons/Builder/Volumes/NotificationVolumeBuilder.cs index c91e4161..13386e79 100644 --- a/NewHorizons/Builder/Volumes/NotificationVolumeBuilder.cs +++ b/NewHorizons/Builder/Volumes/NotificationVolumeBuilder.cs @@ -21,6 +21,25 @@ namespace NewHorizons.Builder.Volumes go.SetActive(false); go.transform.parent = sector?.transform ?? planetGO.transform; + + if (!string.IsNullOrEmpty(info.rename)) + { + go.name = info.rename; + } + + if (!string.IsNullOrEmpty(info.parentPath)) + { + var newParent = planetGO.transform.Find(info.parentPath); + if (newParent != null) + { + go.transform.parent = newParent; + } + else + { + Logger.LogWarning($"Cannot find parent object at path: {planetGO.name}/{info.parentPath}"); + } + } + go.transform.position = planetGO.transform.TransformPoint(info.position != null ? (Vector3)info.position : Vector3.zero); go.layer = LayerMask.NameToLayer("BasicEffectVolume"); diff --git a/NewHorizons/Builder/Volumes/VolumeBuilder.cs b/NewHorizons/Builder/Volumes/VolumeBuilder.cs new file mode 100644 index 00000000..966c3c25 --- /dev/null +++ b/NewHorizons/Builder/Volumes/VolumeBuilder.cs @@ -0,0 +1,51 @@ +using NewHorizons.Components; +using NewHorizons.External.Modules; +using UnityEngine; +using Logger = NewHorizons.Utility.Logger; + +namespace NewHorizons.Builder.Volumes +{ + public static class VolumeBuilder + { + public static TVolume Make(GameObject planetGO, Sector sector, VolumesModule.VolumeInfo info) where TVolume : MonoBehaviour //Could be BaseVolume but I need to create vanilla volumes too. + { + var go = new GameObject(typeof(TVolume).Name); + go.SetActive(false); + + go.transform.parent = sector?.transform ?? planetGO.transform; + + if (!string.IsNullOrEmpty(info.rename)) + { + go.name = info.rename; + } + + if (!string.IsNullOrEmpty(info.parentPath)) + { + var newParent = planetGO.transform.Find(info.parentPath); + if (newParent != null) + { + go.transform.parent = newParent; + } + else + { + Logger.LogWarning($"Cannot find parent object at path: {planetGO.name}/{info.parentPath}"); + } + } + + go.transform.position = planetGO.transform.TransformPoint(info.position != null ? (Vector3)info.position : Vector3.zero); + go.layer = LayerMask.NameToLayer("BasicEffectVolume"); + + var shape = go.AddComponent(); + shape.radius = info.radius; + + var owTriggerVolume = go.AddComponent(); + owTriggerVolume._shape = shape; + + var volume = go.AddComponent(); + + go.SetActive(true); + + return volume; + } + } +} diff --git a/NewHorizons/Builder/Volumes/VolumesBuildManager.cs b/NewHorizons/Builder/Volumes/VolumesBuildManager.cs index f7a664b9..9e9a9c36 100644 --- a/NewHorizons/Builder/Volumes/VolumesBuildManager.cs +++ b/NewHorizons/Builder/Volumes/VolumesBuildManager.cs @@ -1,6 +1,7 @@ using NewHorizons.Builder.Body; using NewHorizons.Builder.ShipLog; using NewHorizons.Builder.Volumes; +using NewHorizons.Components; using NewHorizons.External.Configs; using OWML.Common; using System; @@ -49,6 +50,34 @@ namespace NewHorizons.Builder.Volumes HazardVolumeBuilder.Make(go, sector, planetBody, hazardVolume, mod); } } + if (config.Volumes.mapRestrictionVolumes != null) + { + foreach (var mapRestrictionVolume in config.Volumes.mapRestrictionVolumes) + { + VolumeBuilder.Make(go, sector, mapRestrictionVolume); + } + } + if (config.Volumes.interferenceVolumes != null) + { + foreach (var interferenceVolume in config.Volumes.interferenceVolumes) + { + VolumeBuilder.Make(go, sector, interferenceVolume); + } + } + if (config.Volumes.reverbVolumes != null) + { + foreach (var reverbVolume in config.Volumes.reverbVolumes) + { + VolumeBuilder.Make(go, sector, reverbVolume); + } + } + if (config.Volumes.insulatingVolumes != null) + { + foreach (var insulatingVolume in config.Volumes.insulatingVolumes) + { + VolumeBuilder.Make(go, sector, insulatingVolume); + } + } } } } diff --git a/NewHorizons/Components/BaseVolume.cs b/NewHorizons/Components/BaseVolume.cs new file mode 100644 index 00000000..6ddccada --- /dev/null +++ b/NewHorizons/Components/BaseVolume.cs @@ -0,0 +1,28 @@ +using UnityEngine; + +namespace NewHorizons.Components +{ + [RequireComponent(typeof(OWTriggerVolume))] + public abstract class BaseVolume : MonoBehaviour + { + private OWTriggerVolume _triggerVolume; + + public virtual void Awake() + { + _triggerVolume = this.GetRequiredComponent(); + _triggerVolume.OnEntry += OnTriggerVolumeEntry; + _triggerVolume.OnExit += OnTriggerVolumeExit; + } + + public virtual void OnDestroy() + { + if (_triggerVolume == null) return; + _triggerVolume.OnEntry -= OnTriggerVolumeEntry; + _triggerVolume.OnExit -= OnTriggerVolumeExit; + } + + public abstract void OnTriggerVolumeEntry(GameObject hitObj); + + public abstract void OnTriggerVolumeExit(GameObject hitObj); + } +} diff --git a/NewHorizons/Components/InterferenceVolume.cs b/NewHorizons/Components/InterferenceVolume.cs new file mode 100644 index 00000000..43cf34ac --- /dev/null +++ b/NewHorizons/Components/InterferenceVolume.cs @@ -0,0 +1,54 @@ +using NewHorizons.Handlers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace NewHorizons.Components +{ + public class InterferenceVolume : BaseVolume + { + public override void OnTriggerVolumeEntry(GameObject hitObj) + { + if (hitObj.CompareTag("PlayerDetector")) + { + OnPlayerEnter(); + } + else if (hitObj.CompareTag("ProbeDetector")) + { + OnProbeEnter(); + } + else if (hitObj.CompareTag("ShipDetector")) + { + OnShipEnter(); + } + } + + public override void OnTriggerVolumeExit(GameObject hitObj) + { + if (hitObj.CompareTag("PlayerDetector")) + { + OnPlayerExit(); + } + else if (hitObj.CompareTag("ProbeDetector")) + { + OnProbeExit(); + } + else if (hitObj.CompareTag("ShipDetector")) + { + OnShipExit(); + } + } + + public void OnPlayerEnter() => InterferenceHandler.OnPlayerEnterInterferenceVolume(this); + public void OnPlayerExit() => InterferenceHandler.OnPlayerExitInterferenceVolume(this); + + public void OnProbeEnter() => InterferenceHandler.OnProbeEnterInterferenceVolume(this); + public void OnProbeExit() => InterferenceHandler.OnProbeExitInterferenceVolume(this); + + public void OnShipEnter() => InterferenceHandler.OnShipEnterInterferenceVolume(this); + public void OnShipExit() => InterferenceHandler.OnShipExitInterferenceVolume(this); + } +} diff --git a/NewHorizons/Components/MapRestrictionVolume.cs b/NewHorizons/Components/MapRestrictionVolume.cs new file mode 100644 index 00000000..04442840 --- /dev/null +++ b/NewHorizons/Components/MapRestrictionVolume.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace NewHorizons.Components +{ + public class MapRestrictionVolume : BaseVolume + { + public override void OnTriggerVolumeEntry(GameObject hitObj) + { + if (hitObj.CompareTag("PlayerDetector")) + { + Locator.GetMapController()?.OnPlayerEnterMapRestriction(); + } + } + + public override void OnTriggerVolumeExit(GameObject hitObj) + { + if (hitObj.CompareTag("PlayerDetector")) + { + Locator.GetMapController()?.OnPlayerExitMapRestriction(); + } + } + } +} diff --git a/NewHorizons/Components/NotificationVolume.cs b/NewHorizons/Components/NotificationVolume.cs index 2dc81ed1..6fe7b746 100644 --- a/NewHorizons/Components/NotificationVolume.cs +++ b/NewHorizons/Components/NotificationVolume.cs @@ -7,29 +7,13 @@ using UnityEngine; namespace NewHorizons.Components { - [RequireComponent(typeof(OWTriggerVolume))] - public class NotificationVolume : MonoBehaviour + public class NotificationVolume : BaseVolume { private NotificationTarget _target = NotificationTarget.All; private bool _pin = false; - private OWTriggerVolume _triggerVolume; private NotificationData _entryNotification; private NotificationData _exitNotification; - public void Awake() - { - _triggerVolume = this.GetRequiredComponent(); - _triggerVolume.OnEntry += OnTriggerVolumeEntry; - _triggerVolume.OnExit += OnTriggerVolumeExit; - } - - public void OnDestroy() - { - if (_triggerVolume == null) return; - _triggerVolume.OnEntry -= OnTriggerVolumeEntry; - _triggerVolume.OnExit -= OnTriggerVolumeExit; - } - public void SetPinned(bool pin) => _pin = pin; public void SetTarget(External.Modules.VolumesModule.NotificationVolumeInfo.NotificationTarget target) => SetTarget(EnumUtils.Parse(target.ToString(), NotificationTarget.All)); @@ -46,7 +30,7 @@ namespace NewHorizons.Components _exitNotification = new NotificationData(_target, TranslationHandler.GetTranslation(displayMessage, TranslationHandler.TextType.UI), duration); } - public void OnTriggerVolumeEntry(GameObject hitObj) + public override void OnTriggerVolumeEntry(GameObject hitObj) { if (_target == NotificationTarget.All) { @@ -71,7 +55,7 @@ namespace NewHorizons.Components } } - public void OnTriggerVolumeExit(GameObject hitObj) + public override void OnTriggerVolumeExit(GameObject hitObj) { if (_target == NotificationTarget.All) { diff --git a/NewHorizons/Components/TransparentCloudRenderQueueController.cs b/NewHorizons/Components/TransparentCloudRenderQueueController.cs new file mode 100644 index 00000000..d78259b1 --- /dev/null +++ b/NewHorizons/Components/TransparentCloudRenderQueueController.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace NewHorizons.Components +{ + [RequireComponent(typeof(OWTriggerVolume))] + public class TransparentCloudRenderQueueController : MonoBehaviour + { + public int insideQueue = 3001; + public int outsideQueue = 2999; + + private OWTriggerVolume _triggerVolume; + public Renderer renderer; + + public void Awake() + { + _triggerVolume = this.GetRequiredComponent(); + if (_triggerVolume == null) return; + _triggerVolume.OnEntry += OnTriggerVolumeEntry; + _triggerVolume.OnExit += OnTriggerVolumeExit; + } + + public void OnDestroy() + { + if (_triggerVolume == null) return; + _triggerVolume.OnEntry -= OnTriggerVolumeEntry; + _triggerVolume.OnExit -= OnTriggerVolumeExit; + } + + public void OnTriggerVolumeEntry(GameObject hitObj) + { + if (hitObj.CompareTag("PlayerDetector")) SetQueueToInside(); + } + + public void OnTriggerVolumeExit(GameObject hitObj) + { + if (hitObj.CompareTag("PlayerDetector")) SetQueueToOutside(); + } + + public void SetQueueToInside() + { + if (renderer == null) return; + renderer.sharedMaterial.renderQueue = insideQueue; + } + + public void SetQueueToOutside() + { + if (renderer == null) return; + renderer.sharedMaterial.renderQueue = outsideQueue; + } + } +} diff --git a/NewHorizons/External/Configs/AddonConfig.cs b/NewHorizons/External/Configs/AddonConfig.cs index 08069e00..00257080 100644 --- a/NewHorizons/External/Configs/AddonConfig.cs +++ b/NewHorizons/External/Configs/AddonConfig.cs @@ -28,5 +28,10 @@ namespace NewHorizons.External.Configs /// Credits info for this mod. A list of contributors and their roles separated by #. For example: xen#New Horizons dev. /// public string[] credits; + + /// + /// A pop-up message for the first time a user runs the add-on + /// + public string popupMessage; } } diff --git a/NewHorizons/External/Modules/AtmosphereModule.cs b/NewHorizons/External/Modules/AtmosphereModule.cs index d2b835ee..33c0ccef 100644 --- a/NewHorizons/External/Modules/AtmosphereModule.cs +++ b/NewHorizons/External/Modules/AtmosphereModule.cs @@ -30,6 +30,8 @@ namespace NewHorizons.External.Modules [EnumMember(Value = @"quantumMoon")] QuantumMoon = 1, [EnumMember(Value = @"basic")] Basic = 2, + + [EnumMember(Value = @"transparent")] Transparent = 3, } [JsonObject] diff --git a/NewHorizons/External/Modules/VolumesModule.cs b/NewHorizons/External/Modules/VolumesModule.cs index 43059fbf..49bbc542 100644 --- a/NewHorizons/External/Modules/VolumesModule.cs +++ b/NewHorizons/External/Modules/VolumesModule.cs @@ -15,27 +15,71 @@ namespace NewHorizons.External.Modules public class VolumesModule { /// - /// Add audio volumes to this planet + /// Add audio volumes to this planet. /// public AudioVolumeInfo[] audioVolumes; /// - /// Add hazard volumes to this planet + /// Add hazard volumes to this planet. /// public HazardVolumeInfo[] hazardVolumes; /// - /// Add notification volumes to this planet + /// Add interference volumes to this planet. + /// + public VolumeInfo[] interferenceVolumes; + + /// + /// Add insulating volumes to this planet. These will stop electricty hazard volumes from affecting you (just like the jellyfish). + /// + public VolumeInfo[] insulatingVolumes; + + /// + /// Add map restriction volumes to this planet. + /// + public VolumeInfo[] mapRestrictionVolumes; + + /// + /// Add notification volumes to this planet. /// public NotificationVolumeInfo[] notificationVolumes; /// - /// Add triggers that reveal parts of the ship log on this planet + /// Add triggers that reveal parts of the ship log on this planet. /// public RevealVolumeInfo[] revealVolumes; + /// + /// Add reverb volumes to this planet. Great for echoes in caves. + /// + public VolumeInfo[] reverbVolumes; + [JsonObject] - public class RevealVolumeInfo + public class VolumeInfo + { + /// + /// The location of this volume. Optional (will default to 0,0,0). + /// + public MVector3 position; + + /// + /// The radius of this volume. + /// + public float radius = 1f; + + /// + /// The relative path from the planet to the parent of this object. Optional (will default to the root sector). + /// + public string parentPath; + + /// + /// An optional rename of this volume. + /// + public string rename; + } + + [JsonObject] + public class RevealVolumeInfo : VolumeInfo { [JsonConverter(typeof(StringEnumConverter))] public enum RevealVolumeType @@ -57,16 +101,6 @@ namespace NewHorizons.External.Modules /// public float maxDistance = -1f; // Snapshot & Observe Only - /// - /// The position to place this volume at - /// - public MVector3 position; - - /// - /// The radius of this reveal volume - /// - public float radius = 1f; - /// /// What needs to be done to the volume to unlock the facts /// @@ -84,18 +118,8 @@ namespace NewHorizons.External.Modules } [JsonObject] - public class AudioVolumeInfo + public class AudioVolumeInfo : VolumeInfo { - /// - /// The location of this audio volume. Optional (will default to 0,0,0). - /// - public MVector3 position; - - /// - /// The radius of this audio volume - /// - public float radius; - /// /// The audio to use. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list. /// @@ -113,23 +137,13 @@ namespace NewHorizons.External.Modules } [JsonObject] - public class NotificationVolumeInfo + public class NotificationVolumeInfo : VolumeInfo { /// /// What the notification will show for. /// [DefaultValue("all")] public NotificationTarget target = NotificationTarget.All; - /// - /// The location of this notification volume. Optional (will default to 0,0,0). - /// - public MVector3 position; - - /// - /// The radius of this notification volume. - /// - public float radius; - /// /// The notification that will play when you enter this volume. /// @@ -165,18 +179,8 @@ namespace NewHorizons.External.Modules } [JsonObject] - public class HazardVolumeInfo + public class HazardVolumeInfo : VolumeInfo { - /// - /// The location of this hazard volume. Optional (will default to 0,0,0). - /// - public MVector3 position; - - /// - /// The radius of this hazard volume. - /// - public float radius; - /// /// The type of hazard for this volume. /// @@ -202,7 +206,7 @@ namespace NewHorizons.External.Modules { [EnumMember(Value = @"none")] NONE = 0, [EnumMember(Value = @"general")] GENERAL = 1, - [EnumMember(Value = @"darkMatter")] DARKMATTER = 2, + [EnumMember(Value = @"ghostMatter")] DARKMATTER = 2, [EnumMember(Value = @"heat")] HEAT = 4, [EnumMember(Value = @"fire")] FIRE = 8, [EnumMember(Value = @"sandfall")] SANDFALL = 16, diff --git a/NewHorizons/External/NewHorizonsData.cs b/NewHorizons/External/NewHorizonsData.cs index f90cb479..52a8cbf1 100644 --- a/NewHorizons/External/NewHorizonsData.cs +++ b/NewHorizons/External/NewHorizonsData.cs @@ -19,7 +19,7 @@ namespace NewHorizons.External _activeProfileName = GetProfileName(); if (_activeProfileName == null) { - Logger.LogError("Couldn't find active profile, are you on Gamepass?"); + Logger.LogWarning("Couldn't find active profile, are you on Gamepass?"); _activeProfileName = "XboxGamepassDefaultProfile"; } @@ -82,12 +82,13 @@ namespace NewHorizons.External KnownFrequencies = new List(); KnownSignals = new List(); NewlyRevealedFactIDs = new List(); + PopupsRead = new List(); } public List KnownFrequencies { get; } public List KnownSignals { get; } - public List NewlyRevealedFactIDs { get; } + public List PopupsRead { get; } } #region Frequencies @@ -155,5 +156,21 @@ namespace NewHorizons.External } #endregion + + #region Read popups + + public static void ReadOneTimePopup(string id) + { + _activeProfile?.PopupsRead.Add(id); + Save(); + } + + public static bool HasReadOneTimePopup(string id) + { + // To avoid spam, we'll just say the popup has been read if we can't load the profile + return _activeProfile?.PopupsRead.Contains(id) ?? true; + } + + #endregion } } \ No newline at end of file diff --git a/NewHorizons/Handlers/InterferenceHandler.cs b/NewHorizons/Handlers/InterferenceHandler.cs new file mode 100644 index 00000000..97ff0046 --- /dev/null +++ b/NewHorizons/Handlers/InterferenceHandler.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NewHorizons.Handlers +{ + using InterferenceVolume = NewHorizons.Components.InterferenceVolume; + + public static class InterferenceHandler + { + private static List _playerInterference; + private static List _probeInterference; + private static List _shipInterference; + + public static void Init() + { + _playerInterference = new List(); + _probeInterference = new List(); + _shipInterference = new List(); + } + + public static bool PlayerHasInterference() => _playerInterference.Any(volume => volume != null); + public static bool ProbeHasInterference() => _probeInterference.Any(volume => volume != null); + public static bool ShipHasInterference() => _shipInterference.Any(volume => volume != null); + + public static bool IsPlayerSameAsProbe() + { + _playerInterference.RemoveAll(volume => volume == null); + return _playerInterference.All(_probeInterference.Contains) && _playerInterference.Count == _probeInterference.Count; + } + + public static bool IsPlayerSameAsShip() + { + _playerInterference.RemoveAll(volume => volume == null); + return _playerInterference.All(_shipInterference.Contains) && _playerInterference.Count == _shipInterference.Count; + } + + public static void OnPlayerEnterInterferenceVolume(InterferenceVolume interferenceVolume) + { + _playerInterference.SafeAdd(interferenceVolume); + GlobalMessenger.FireEvent("RefreshHUDVisibility"); + } + + public static void OnPlayerExitInterferenceVolume(InterferenceVolume interferenceVolume) + { + _playerInterference.Remove(interferenceVolume); + GlobalMessenger.FireEvent("RefreshHUDVisibility"); + } + + public static void OnProbeEnterInterferenceVolume(InterferenceVolume interferenceVolume) + { + _probeInterference.SafeAdd(interferenceVolume); + GlobalMessenger.FireEvent("RefreshHUDVisibility"); + } + + public static void OnProbeExitInterferenceVolume(InterferenceVolume interferenceVolume) + { + _probeInterference.Remove(interferenceVolume); + GlobalMessenger.FireEvent("RefreshHUDVisibility"); + } + + public static void OnShipEnterInterferenceVolume(InterferenceVolume interferenceVolume) + { + _shipInterference.SafeAdd(interferenceVolume); + GlobalMessenger.FireEvent("RefreshHUDVisibility"); + } + public static void OnShipExitInterferenceVolume(InterferenceVolume interferenceVolume) + { + _shipInterference.Remove(interferenceVolume); + GlobalMessenger.FireEvent("RefreshHUDVisibility"); + } + } +} diff --git a/NewHorizons/Handlers/PlanetCreationHandler.cs b/NewHorizons/Handlers/PlanetCreationHandler.cs index 4790826a..6343eb5c 100644 --- a/NewHorizons/Handlers/PlanetCreationHandler.cs +++ b/NewHorizons/Handlers/PlanetCreationHandler.cs @@ -606,7 +606,7 @@ namespace NewHorizons.Handlers if (!string.IsNullOrEmpty(body.Config.Atmosphere?.clouds?.texturePath)) { CloudsBuilder.Make(go, sector, body.Config.Atmosphere, willHaveCloak, body.Mod); - SunOverrideBuilder.Make(go, sector, body.Config.Atmosphere, body.Config.Water, surfaceSize); + if (body.Config.Atmosphere.clouds.cloudsPrefab != External.Modules.CloudPrefabType.Transparent) SunOverrideBuilder.Make(go, sector, body.Config.Atmosphere, body.Config.Water, surfaceSize); } if (body.Config.Atmosphere.hasRain || body.Config.Atmosphere.hasSnow) diff --git a/NewHorizons/Handlers/TitleSceneHandler.cs b/NewHorizons/Handlers/TitleSceneHandler.cs index b4fb22dc..ea435305 100644 --- a/NewHorizons/Handlers/TitleSceneHandler.cs +++ b/NewHorizons/Handlers/TitleSceneHandler.cs @@ -89,7 +89,7 @@ namespace NewHorizons.Handlers heightMap.minHeight = body.Config.HeightMap.minHeight * size / body.Config.HeightMap.maxHeight; heightMap.stretch = body.Config.HeightMap.stretch; } - if (body.Config.Atmosphere?.clouds?.texturePath != null) + if (body.Config.Atmosphere?.clouds?.texturePath != null && body.Config.Atmosphere?.clouds?.cloudsPrefab != CloudPrefabType.Transparent) { // Hacky but whatever I just want a sphere size = Mathf.Clamp(body.Config.Atmosphere.size / 10, minSize, maxSize); diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index b0c93c00..1c26dcaa 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -1,17 +1,20 @@ using HarmonyLib; -using NewHorizons.OtherMods.AchievementsPlus; using NewHorizons.Builder.Atmosphere; using NewHorizons.Builder.Body; using NewHorizons.Builder.Props; using NewHorizons.Components; using NewHorizons.Components.Orbital; +using NewHorizons.Components.SizeControllers; using NewHorizons.External; using NewHorizons.External.Configs; using NewHorizons.Handlers; +using NewHorizons.OtherMods.AchievementsPlus; +using NewHorizons.OtherMods.MenuFramework; +using NewHorizons.OtherMods.OWRichPresence; +using NewHorizons.OtherMods.VoiceActing; using NewHorizons.Utility; using NewHorizons.Utility.DebugMenu; using NewHorizons.Utility.DebugUtilities; -using NewHorizons.OtherMods.VoiceActing; using OWML.Common; using OWML.ModHelper; using System; @@ -23,8 +26,6 @@ using UnityEngine; using UnityEngine.Events; using UnityEngine.SceneManagement; using Logger = NewHorizons.Utility.Logger; -using NewHorizons.OtherMods.OWRichPresence; -using NewHorizons.Components.SizeControllers; namespace NewHorizons { @@ -96,9 +97,9 @@ namespace NewHorizons DebugMenu.UpdatePauseMenuButton(); } - if (VerboseLogs) Logger.UpdateLogLevel(Logger.LogType.Verbose); - else if (Debug) Logger.UpdateLogLevel(Logger.LogType.Log); - else Logger.UpdateLogLevel(Logger.LogType.Error); + if (VerboseLogs) Logger.UpdateLogLevel(Logger.LogType.Verbose); + else if (Debug) Logger.UpdateLogLevel(Logger.LogType.Log); + else Logger.UpdateLogLevel(Logger.LogType.Error); _defaultSystemOverride = config.GetSettingsValue("Default System Override"); @@ -167,6 +168,11 @@ namespace NewHorizons TextTranslation.Get().SetLanguage(TextTranslation.Get().GetLanguage()); } + public void Awake() + { + Instance = this; + } + public void Start() { // Patches @@ -179,7 +185,6 @@ namespace NewHorizons SceneManager.sceneLoaded += OnSceneLoaded; SceneManager.sceneUnloaded += OnSceneUnloaded; - Instance = this; GlobalMessenger.AddListener("PlayerDeath", OnDeath); GlobalMessenger.AddListener("WakeUp", OnWakeUp); @@ -203,12 +208,14 @@ namespace NewHorizons Instance.ModHelper.Events.Unity.FireOnNextUpdate(() => _firstLoad = false); Instance.ModHelper.Menus.PauseMenu.OnInit += DebugReload.InitializePauseMenu; + MenuHandler.Init(); AchievementHandler.Init(); VoiceHandler.Init(); RichPresenceHandler.Init(); OnStarSystemLoaded.AddListener(RichPresenceHandler.OnStarSystemLoaded); OnChangeStarSystem.AddListener(RichPresenceHandler.OnChangeStarSystem); + LoadTranslations(ModHelper.Manifest.ModFolderPath + "Assets/", this); LoadAddonManifest("Assets/addon-manifest.json", this); } @@ -410,14 +417,12 @@ namespace NewHorizons AstroObjectLocator.Init(); StreamingHandler.Init(); AudioTypeHandler.Init(); + InterferenceHandler.Init(); RemoteHandler.Init(); AtmosphereBuilder.Init(); BrambleNodeBuilder.Init(BodyDict[CurrentStarSystem].Select(x => x.Config).Where(x => x.Bramble?.dimension != null).ToArray()); StarEvolutionController.Init(); - // Has to go before loading planets else the Discord Rich Presence mod won't show the right text - LoadTranslations(ModHelper.Manifest.ModFolderPath + "Assets/", this); - if (isSolarSystem) { foreach (var supernovaPlanetEffectController in GameObject.FindObjectsOfType()) @@ -529,7 +534,7 @@ namespace NewHorizons var ssrLight = solarSystemRoot.AddComponent(); ssrLight.innerSpotAngle = 0; ssrLight.spotAngle = 179; - ssrLight.range = Main.FurthestOrbit * (4f/3f); + ssrLight.range = Main.FurthestOrbit * (4f / 3f); ssrLight.intensity = 0.001f; var fluid = playerBody.FindChild("PlayerDetector").GetComponent(); @@ -620,7 +625,7 @@ namespace NewHorizons if (starSystemConfig.startHere) { // We always want to allow mods to overwrite setting the main SolarSystem as default but not the other way around - if (name != "SolarSystem") + if (name != "SolarSystem") { SetDefaultSystem(name); _currentStarSystem = name; @@ -682,6 +687,7 @@ namespace NewHorizons if (addonConfig.achievements != null) AchievementHandler.RegisterAddon(addonConfig, mod as ModBehaviour); if (addonConfig.credits != null) CreditsHandler.RegisterCredits(mod.ModHelper.Manifest.Name, addonConfig.credits); + if (!string.IsNullOrEmpty(addonConfig.popupMessage)) MenuHandler.RegisterOneTimePopup(mod, addonConfig.popupMessage); } private void LoadTranslations(string folder, IModBehaviour mod) @@ -721,6 +727,7 @@ namespace NewHorizons if (config == null) { Logger.LogError($"Couldn't load {relativePath}. Is your Json formatted correctly?"); + MenuHandler.RegisterFailedConfig(Path.GetFileName(relativePath)); return null; } @@ -752,6 +759,7 @@ namespace NewHorizons catch (Exception e) { Logger.LogError($"Error encounter when loading {relativePath}:\n{e}"); + MenuHandler.RegisterFailedConfig(Path.GetFileName(relativePath)); } return body; diff --git a/NewHorizons/OtherMods/MenuFramework/IMenuAPI.cs b/NewHorizons/OtherMods/MenuFramework/IMenuAPI.cs new file mode 100644 index 00000000..f44aecdf --- /dev/null +++ b/NewHorizons/OtherMods/MenuFramework/IMenuAPI.cs @@ -0,0 +1,20 @@ +using UnityEngine; +using UnityEngine.UI; + +namespace NewHorizons.OtherMods.MenuFramework +{ + public interface IMenuAPI + { + GameObject TitleScreen_MakeMenuOpenButton(string name, int index, Menu menuToOpen); + GameObject TitleScreen_MakeSceneLoadButton(string name, int index, SubmitActionLoadScene.LoadableScenes sceneToLoad, PopupMenu confirmPopup = null); + Button TitleScreen_MakeSimpleButton(string name, int index); + GameObject PauseMenu_MakeMenuOpenButton(string name, Menu menuToOpen, Menu customMenu = null); + GameObject PauseMenu_MakeSceneLoadButton(string name, SubmitActionLoadScene.LoadableScenes sceneToLoad, PopupMenu confirmPopup = null, Menu customMenu = null); + Button PauseMenu_MakeSimpleButton(string name, Menu customMenu = null); + Menu PauseMenu_MakePauseListMenu(string title); + PopupMenu MakeTwoChoicePopup(string message, string confirmText, string cancelText); + PopupInputMenu MakeInputFieldPopup(string message, string placeholderMessage, string confirmText, string cancelText); + PopupMenu MakeInfoPopup(string message, string continueButtonText); + void RegisterStartupPopup(string message); + } +} diff --git a/NewHorizons/OtherMods/MenuFramework/MenuHandler.cs b/NewHorizons/OtherMods/MenuFramework/MenuHandler.cs new file mode 100644 index 00000000..45682279 --- /dev/null +++ b/NewHorizons/OtherMods/MenuFramework/MenuHandler.cs @@ -0,0 +1,71 @@ +using NewHorizons.External; +using NewHorizons.Handlers; +using NewHorizons.Utility; +using OWML.Common; +using System.Collections.Generic; +using System.Linq; +using System.Net.Mail; +using UnityEngine; +using static UnityEngine.InputSystem.InputRemoting; +using Logger = NewHorizons.Utility.Logger; + +namespace NewHorizons.OtherMods.MenuFramework +{ + public static class MenuHandler + { + private static IMenuAPI _menuApi; + + private static List<(IModBehaviour mod, string message)> _registeredPopups = new(); + private static List _failedFiles = new(); + + public static void Init() + { + _menuApi = Main.Instance.ModHelper.Interaction.TryGetModApi("_nebula.MenuFramework"); + + TextTranslation.Get().OnLanguageChanged += OnLanguageChanged; + } + + public static void OnLanguageChanged() + { + // Have to load save data before doing popups + NewHorizonsData.Load(); + + if (!VersionUtility.CheckUpToDate()) + { + var warning = string.Format(TranslationHandler.GetTranslation("OUTDATED_VERSION_WARNING", TranslationHandler.TextType.UI), + VersionUtility.RequiredVersionString, + Application.version); + + Logger.LogError(warning); + _menuApi.RegisterStartupPopup(warning); + } + + foreach(var (mod, message) in _registeredPopups) + { + if (!NewHorizonsData.HasReadOneTimePopup(mod.ModHelper.Manifest.UniqueName)) + { + _menuApi.RegisterStartupPopup(TranslationHandler.GetTranslation(message, TranslationHandler.TextType.UI)); + NewHorizonsData.ReadOneTimePopup(mod.ModHelper.Manifest.UniqueName); + } + } + + if (_failedFiles.Count > 0) + { + var message = TranslationHandler.GetTranslation("JSON_FAILED_TO_LOAD", TranslationHandler.TextType.UI); + var mods = string.Join(",", _failedFiles.Take(10)); + if (_failedFiles.Count > 10) mods += "..."; + _menuApi.RegisterStartupPopup(string.Format(message, mods)); + } + + _registeredPopups.Clear(); + _failedFiles.Clear(); + + // Just wanted to do this when the language is loaded in initially + TextTranslation.Get().OnLanguageChanged -= OnLanguageChanged; + } + + public static void RegisterFailedConfig(string filename) => _failedFiles.Add(filename); + + public static void RegisterOneTimePopup(IModBehaviour mod, string message) => _registeredPopups.Add((mod, message)); + } +} diff --git a/NewHorizons/Patches/HUDPatches.cs b/NewHorizons/Patches/HUDPatches.cs index bbb0061a..fc37e4e0 100644 --- a/NewHorizons/Patches/HUDPatches.cs +++ b/NewHorizons/Patches/HUDPatches.cs @@ -1,4 +1,5 @@ using HarmonyLib; +using NewHorizons.Handlers; namespace NewHorizons.Patches { @@ -9,6 +10,8 @@ namespace NewHorizons.Patches [HarmonyPatch(typeof(HUDMarker), nameof(HUDMarker.Awake))] public static void HUDMarker_Awake(HUDMarker __instance) { + GlobalMessenger.AddListener("RefreshHUDVisibility", __instance.RefreshOwnVisibility); + GlobalMessenger.AddListener("RefreshHUDVisibility", __instance.RefreshOwnVisibility); GlobalMessenger.AddListener("PlayerEnterCloakField", __instance.OnPlayerEnterCloakField); GlobalMessenger.AddListener("PlayerExitCloakField", __instance.OnPlayerExitCloakField); } @@ -17,6 +20,8 @@ namespace NewHorizons.Patches [HarmonyPatch(typeof(HUDMarker), nameof(HUDMarker.OnDestroy))] public static void HUDMarker_OnDestroy(HUDMarker __instance) { + GlobalMessenger.RemoveListener("RefreshHUDVisibility", __instance.RefreshOwnVisibility); + GlobalMessenger.RemoveListener("RefreshHUDVisibility", __instance.RefreshOwnVisibility); GlobalMessenger.RemoveListener("PlayerEnterCloakField", __instance.OnPlayerEnterCloakField); GlobalMessenger.RemoveListener("PlayerExitCloakField", __instance.OnPlayerExitCloakField); } @@ -53,11 +58,67 @@ namespace NewHorizons.Patches GlobalMessenger.RemoveListener("ShipExitCloakField", __instance.RefreshOwnVisibility); } + [HarmonyPrefix] + [HarmonyPatch(typeof(ProbeHUDMarker), nameof(ProbeHUDMarker.RefreshOwnVisibility))] + public static bool ProbeHUDMarker_RefreshOwnVisibility(ProbeHUDMarker __instance) + { + bool insideEYE = Locator.GetEyeStateManager() != null && Locator.GetEyeStateManager().IsInsideTheEye(); + bool insideQM = __instance._quantumMoon != null && (__instance._quantumMoon.IsPlayerInside() || __instance._quantumMoon.IsProbeInside()); + bool insideRW = Locator.GetRingWorldController() != null && Locator.GetRingWorldController().isPlayerInside == Locator.GetRingWorldController().isProbeInside; + bool insideIP = Locator.GetCloakFieldController() != null && Locator.GetCloakFieldController().isPlayerInsideCloak == Locator.GetCloakFieldController().isProbeInsideCloak; + bool insideCloak = Components.CloakSectorController.isPlayerInside == Components.CloakSectorController.isProbeInside; + bool sameInterference = InterferenceHandler.IsPlayerSameAsProbe(); + bool isActive = __instance.gameObject.activeInHierarchy || __instance._isTLCDuplicate; + + __instance._isVisible = isActive && !insideEYE && !insideQM && !__instance._translatorEquipped && !__instance._inConversation && __instance._launched && (__instance._isWearingHelmet || __instance._atFlightConsole) && insideRW && insideIP && insideCloak && sameInterference; + + if (__instance._canvasMarker != null) __instance._canvasMarker.SetVisibility(__instance._isVisible); + + return false; + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(ShipHUDMarker), nameof(ShipHUDMarker.RefreshOwnVisibility))] + public static bool ShipHUDMarker_RefreshOwnVisibility(ShipHUDMarker __instance) + { + bool insideEYE = Locator.GetEyeStateManager() != null && Locator.GetEyeStateManager().IsInsideTheEye(); + bool insideQM = __instance._quantumMoon != null && (__instance._quantumMoon.IsPlayerInside() || __instance._quantumMoon.IsShipInside()); + bool insideRW = Locator.GetRingWorldController() != null && Locator.GetRingWorldController().isPlayerInside; + bool insideIP = Locator.GetCloakFieldController() != null ? true : Locator.GetCloakFieldController().isPlayerInsideCloak == Locator.GetCloakFieldController().isShipInsideCloak; + bool insideCloak = Components.CloakSectorController.isPlayerInside == Components.CloakSectorController.isShipInside; + bool sameInterference = InterferenceHandler.IsPlayerSameAsShip(); + + __instance._isVisible = !insideEYE && !insideQM && !insideRW && !__instance._translatorEquipped && !__instance._inConversation && !__instance._shipDestroyed && !__instance._playerInShip && PlayerState.HasPlayerEnteredShip() && __instance._isWearingHelmet && insideIP && insideCloak && sameInterference; + + if (__instance._canvasMarker != null) __instance._canvasMarker.SetVisibility(__instance._isVisible); + + return false; + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(ShipLogEntryHUDMarker), nameof(ShipLogEntryHUDMarker.RefreshOwnVisibility))] + public static bool ShipLogEntryHUDMarker_RefreshOwnVisibility(ShipLogEntryHUDMarker __instance) + { + bool hasEntryLocation = ShipLogEntryHUDMarker.s_entryLocation != null; + bool insideEYE = Locator.GetEyeStateManager() != null && Locator.GetEyeStateManager().IsInsideTheEye(); + bool insideQM = __instance._quantumMoon != null && __instance._quantumMoon.IsPlayerInside(); + bool insideRW = Locator.GetRingWorldController() != null && Locator.GetRingWorldController().isPlayerInside && ShipLogEntryHUDMarker.s_entryLocationID == "IP_RING_WORLD"; + bool insideIP = (hasEntryLocation && ShipLogEntryHUDMarker.s_entryLocation.IsWithinCloakField()) || !(Locator.GetCloakFieldController() != null && Locator.GetCloakFieldController().isPlayerInsideCloak); + bool insideCloak = (hasEntryLocation && ShipLogEntryHUDMarker.s_entryLocation.IsWithinCloakField()) || !Components.CloakSectorController.isPlayerInside; + + __instance._isVisible = (!insideEYE && !insideQM && !insideRW && !__instance._translatorEquipped && !__instance._inConversation && hasEntryLocation && (__instance._isWearingHelmet || __instance._atFlightConsole) && insideIP && insideCloak); + + if (__instance._canvasMarker != null) __instance._canvasMarker.SetVisibility(__instance._isVisible); + + return false; + } + + [HarmonyPostfix] [HarmonyPatch(typeof(ProbeCamera), nameof(ProbeCamera.HasInterference))] public static void ProbeCamera_HasInterference(ProbeCamera __instance, ref bool __result) { - __result = __result || Components.CloakSectorController.isPlayerInside != Components.CloakSectorController.isProbeInside; + __result = __result || (__instance._id != ProbeCamera.ID.PreLaunch && (Components.CloakSectorController.isPlayerInside != Components.CloakSectorController.isProbeInside || !InterferenceHandler.IsPlayerSameAsProbe())); } } } diff --git a/NewHorizons/Schemas/addon_manifest_schema.json b/NewHorizons/Schemas/addon_manifest_schema.json index bfe88b86..8d3c0da7 100644 --- a/NewHorizons/Schemas/addon_manifest_schema.json +++ b/NewHorizons/Schemas/addon_manifest_schema.json @@ -19,6 +19,10 @@ "type": "string" } }, + "popupMessage": { + "type": "string", + "description": "A pop-up message for the first time a user runs the add-on" + }, "$schema": { "type": "string", "description": "The schema to validate with" diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index 7ea40e04..18185e87 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -400,12 +400,14 @@ "x-enumNames": [ "GiantsDeep", "QuantumMoon", - "Basic" + "Basic", + "Transparent" ], "enum": [ "giantsDeep", "quantumMoon", - "basic" + "basic", + "transparent" ] }, "FluidType": { @@ -2393,31 +2395,59 @@ "properties": { "audioVolumes": { "type": "array", - "description": "Add audio volumes to this planet", + "description": "Add audio volumes to this planet.", "items": { "$ref": "#/definitions/AudioVolumeInfo" } }, "hazardVolumes": { "type": "array", - "description": "Add hazard volumes to this planet", + "description": "Add hazard volumes to this planet.", "items": { "$ref": "#/definitions/HazardVolumeInfo" } }, + "interferenceVolumes": { + "type": "array", + "description": "Add interference volumes to this planet.", + "items": { + "$ref": "#/definitions/VolumeInfo" + } + }, + "insulatingVolumes": { + "type": "array", + "description": "Add insulating volumes to this planet. These will stop electricty hazard volumes from affecting you (just like the jellyfish).", + "items": { + "$ref": "#/definitions/VolumeInfo" + } + }, + "mapRestrictionVolumes": { + "type": "array", + "description": "Add map restriction volumes to this planet.", + "items": { + "$ref": "#/definitions/VolumeInfo" + } + }, "notificationVolumes": { "type": "array", - "description": "Add notification volumes to this planet", + "description": "Add notification volumes to this planet.", "items": { "$ref": "#/definitions/NotificationVolumeInfo" } }, "revealVolumes": { "type": "array", - "description": "Add triggers that reveal parts of the ship log on this planet", + "description": "Add triggers that reveal parts of the ship log on this planet.", "items": { "$ref": "#/definitions/RevealVolumeInfo" } + }, + "reverbVolumes": { + "type": "array", + "description": "Add reverb volumes to this planet. Great for echoes in caves.", + "items": { + "$ref": "#/definitions/VolumeInfo" + } } } }, @@ -2426,14 +2456,22 @@ "additionalProperties": false, "properties": { "position": { - "description": "The location of this audio volume. Optional (will default to 0,0,0).", + "description": "The location of this volume. Optional (will default to 0,0,0).", "$ref": "#/definitions/MVector3" }, "radius": { "type": "number", - "description": "The radius of this audio volume", + "description": "The radius of this volume.", "format": "float" }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, + "rename": { + "type": "string", + "description": "An optional rename of this volume." + }, "audio": { "type": "string", "description": "The audio to use. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." @@ -2495,14 +2533,22 @@ "additionalProperties": false, "properties": { "position": { - "description": "The location of this hazard volume. Optional (will default to 0,0,0).", + "description": "The location of this volume. Optional (will default to 0,0,0).", "$ref": "#/definitions/MVector3" }, "radius": { "type": "number", - "description": "The radius of this hazard volume.", + "description": "The radius of this volume.", "format": "float" }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, + "rename": { + "type": "string", + "description": "An optional rename of this volume." + }, "type": { "description": "The type of hazard for this volume.", "default": "general", @@ -2542,7 +2588,7 @@ "enum": [ "none", "general", - "darkMatter", + "ghostMatter", "heat", "fire", "sandfall", @@ -2564,24 +2610,55 @@ "electrical" ] }, - "NotificationVolumeInfo": { + "VolumeInfo": { "type": "object", "additionalProperties": false, "properties": { - "target": { - "description": "What the notification will show for.", - "default": "all", - "$ref": "#/definitions/NotificationTarget" - }, "position": { - "description": "The location of this notification volume. Optional (will default to 0,0,0).", + "description": "The location of this volume. Optional (will default to 0,0,0).", "$ref": "#/definitions/MVector3" }, "radius": { "type": "number", - "description": "The radius of this notification volume.", + "description": "The radius of this volume.", "format": "float" }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, + "rename": { + "type": "string", + "description": "An optional rename of this volume." + } + } + }, + "NotificationVolumeInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "position": { + "description": "The location of this volume. Optional (will default to 0,0,0).", + "$ref": "#/definitions/MVector3" + }, + "radius": { + "type": "number", + "description": "The radius of this volume.", + "format": "float" + }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, + "rename": { + "type": "string", + "description": "An optional rename of this volume." + }, + "target": { + "description": "What the notification will show for.", + "default": "all", + "$ref": "#/definitions/NotificationTarget" + }, "entryNotification": { "description": "The notification that will play when you enter this volume.", "$ref": "#/definitions/NotificationInfo" @@ -2626,6 +2703,23 @@ "type": "object", "additionalProperties": false, "properties": { + "position": { + "description": "The location of this volume. Optional (will default to 0,0,0).", + "$ref": "#/definitions/MVector3" + }, + "radius": { + "type": "number", + "description": "The radius of this volume.", + "format": "float" + }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, + "rename": { + "type": "string", + "description": "An optional rename of this volume." + }, "maxAngle": { "type": "number", "description": "The max view angle (in degrees) the player can see the volume with to unlock the fact (`observe` only)", @@ -2636,15 +2730,6 @@ "description": "The max distance the user can be away from the volume to reveal the fact (`snapshot` and `observe` only)", "format": "float" }, - "position": { - "description": "The position to place this volume at", - "$ref": "#/definitions/MVector3" - }, - "radius": { - "type": "number", - "description": "The radius of this reveal volume", - "format": "float" - }, "revealOn": { "description": "What needs to be done to the volume to unlock the facts", "default": "enter", diff --git a/NewHorizons/Utility/VersionUtility.cs b/NewHorizons/Utility/VersionUtility.cs new file mode 100644 index 00000000..e7c0ea23 --- /dev/null +++ b/NewHorizons/Utility/VersionUtility.cs @@ -0,0 +1,25 @@ +using System.Linq; +using UnityEngine; + +namespace NewHorizons.Utility +{ + internal static class VersionUtility + { + public static int[] RequiredVersion => new int[] {1, 1, 12}; + public static string RequiredVersionString => string.Join(".", RequiredVersion); + + public static bool CheckUpToDate() + { + // If they're using an outdated game version we create an error popup here + var version = Application.version.Split('.').Select(x => int.Parse(x)).ToArray(); + var major = version[0]; + var minor = version[1]; + var patch = version[2]; + + // Must be at least 1.1.12 + return major > RequiredVersion[0] || + (major == RequiredVersion[0] && minor > RequiredVersion[1]) || + (major == RequiredVersion[0] && minor == RequiredVersion[1] && patch >= RequiredVersion[2]); + } + } +} diff --git a/NewHorizons/manifest.json b/NewHorizons/manifest.json index 5696ae38..a4be9c22 100644 --- a/NewHorizons/manifest.json +++ b/NewHorizons/manifest.json @@ -6,7 +6,7 @@ "uniqueName": "xen.NewHorizons", "version": "1.5.1", "owmlVersion": "2.6.0", - "dependencies": [ "JohnCorby.VanillaFix" ], + "dependencies": [ "JohnCorby.VanillaFix", "_nebula.MenuFramework" ], "conflicts": [ "Raicuparta.QuantumSpaceBuddies", "PacificEngine.OW_Randomizer" ], "pathsToPreserve": [ "planets", "systems", "translations" ] }