mirror of
https://github.com/Outer-Wilds-New-Horizons/new-horizons.git
synced 2025-12-11 20:15:44 +01:00
Merge branch 'dev' into eye-of-the-universe
This commit is contained in:
commit
d451650cb6
5
.gitignore
vendored
5
.gitignore
vendored
@ -417,4 +417,9 @@ docs/.m_cache/**
|
||||
# Unity project
|
||||
new-horizons-unity
|
||||
new-horizons-unity/**
|
||||
nh-unity
|
||||
nh-unity/**
|
||||
NewHorizons/Assets/Assets
|
||||
NewHorizons/Assets/Assets.manifest
|
||||
|
||||
docs/.env
|
||||
|
||||
Binary file not shown.
23
NewHorizons/Assets/xen.newhorizons.manifest
Normal file
23
NewHorizons/Assets/xen.newhorizons.manifest
Normal file
@ -0,0 +1,23 @@
|
||||
ManifestFileVersion: 0
|
||||
CRC: 1014555239
|
||||
Hashes:
|
||||
AssetFileHash:
|
||||
serializedVersion: 2
|
||||
Hash: 45fa3430ee7bea1e8384e57927fc0f76
|
||||
TypeTreeHash:
|
||||
serializedVersion: 2
|
||||
Hash: 55d48f4ad9c3b13330b9eb5ee5686477
|
||||
HashAppended: 0
|
||||
ClassTypes:
|
||||
- Class: 48
|
||||
Script: {instanceID: 0}
|
||||
SerializeReferenceClassIdentifiers: []
|
||||
Assets:
|
||||
- Assets/Shaders/SphereTextureWrapper.shader
|
||||
- Assets/Shaders/Ring.shader
|
||||
- Assets/Shaders/SphereTextureWrapperNormal.shader
|
||||
- Assets/Shaders/UnlitRing1Pixel.shader
|
||||
- Assets/Shaders/UnlitTransparent.shader
|
||||
- Assets/Shaders/StandardCullOFF.shader
|
||||
- Assets/Shaders/Ring1Pixel.shader
|
||||
Dependencies: []
|
||||
@ -23,6 +23,8 @@ namespace NewHorizons.Builder.Atmosphere
|
||||
sfv._allowShipAutoroll = true;
|
||||
sfv._disableOnStart = false;
|
||||
|
||||
if (config.Atmosphere.hasShockLayer)
|
||||
{
|
||||
// Try to parent it to the same as other rulesets to match vanilla but if its null put it on the root object
|
||||
var ruleSetGO = planetGO.GetComponentInChildren<PlanetoidRuleset>()?.gameObject;
|
||||
if (ruleSetGO == null) ruleSetGO = planetGO;
|
||||
@ -46,6 +48,7 @@ namespace NewHorizons.Builder.Atmosphere
|
||||
shockLayerRuleset._innerRadius = (bottom + top) / 2f;
|
||||
shockLayerRuleset._outerRadius = top;
|
||||
}
|
||||
}
|
||||
|
||||
if (config.Atmosphere.hasOxygen)
|
||||
{
|
||||
|
||||
@ -9,7 +9,6 @@ namespace NewHorizons.Builder.Atmosphere
|
||||
{
|
||||
public static class CloudsBuilder
|
||||
{
|
||||
private static Shader _sphereShader = null;
|
||||
private static Material[] _gdCloudMaterials;
|
||||
private static Material[] _qmCloudMaterials;
|
||||
private static GameObject _lightningPrefab;
|
||||
@ -91,8 +90,6 @@ namespace NewHorizons.Builder.Atmosphere
|
||||
|
||||
// Fix the rotations once the rest is done
|
||||
cloudsMainGO.transform.rotation = planetGO.transform.TransformRotation(Quaternion.Euler(0, 0, 0));
|
||||
// For the base shader it has to be rotated idk
|
||||
if (atmo.clouds.cloudsPrefab == CloudPrefabType.Basic) cloudsMainGO.transform.rotation = planetGO.transform.TransformRotation(Quaternion.Euler(90, 0, 0));
|
||||
|
||||
// Lightning
|
||||
if (atmo.clouds.hasLightning)
|
||||
@ -170,7 +167,6 @@ namespace NewHorizons.Builder.Atmosphere
|
||||
|
||||
MeshRenderer topMR = cloudsTopGO.AddComponent<MeshRenderer>();
|
||||
|
||||
if (_sphereShader == null) _sphereShader = Main.NHAssetBundle.LoadAsset<Shader>("Assets/Shaders/SphereTextureWrapper.shader");
|
||||
if (_gdCloudMaterials == null) _gdCloudMaterials = SearchUtilities.Find("CloudsTopLayer_GD").GetComponent<MeshRenderer>().sharedMaterials;
|
||||
if (_qmCloudMaterials == null) _qmCloudMaterials = SearchUtilities.Find("CloudsTopLayer_QM").GetComponent<MeshRenderer>().sharedMaterials;
|
||||
Material[] prefabMaterials = atmo.clouds.cloudsPrefab == CloudPrefabType.GiantsDeep ? _gdCloudMaterials : _qmCloudMaterials;
|
||||
@ -178,10 +174,10 @@ namespace NewHorizons.Builder.Atmosphere
|
||||
|
||||
if (atmo.clouds.cloudsPrefab == CloudPrefabType.Basic)
|
||||
{
|
||||
var material = new Material(_sphereShader);
|
||||
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
|
||||
tempArray[0] = material;
|
||||
}
|
||||
else
|
||||
@ -211,8 +207,7 @@ namespace NewHorizons.Builder.Atmosphere
|
||||
if (atmo.clouds.rotationSpeed != 0f)
|
||||
{
|
||||
var topRT = cloudsTopGO.AddComponent<RotateTransform>();
|
||||
// Idk why but the axis is weird
|
||||
topRT._localAxis = atmo.clouds.cloudsPrefab == CloudPrefabType.Basic ? Vector3.forward : Vector3.up;
|
||||
topRT._localAxis = Vector3.up;
|
||||
topRT._degreesPerSecond = atmo.clouds.rotationSpeed;
|
||||
topRT._randomizeRotationRate = false;
|
||||
}
|
||||
|
||||
@ -20,6 +20,16 @@ namespace NewHorizons.Builder.Atmosphere
|
||||
SCG._dynamicCullingBounds = false;
|
||||
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
|
||||
else minHeight = config.HeightMap.minHeight;
|
||||
}
|
||||
else if (config.Water?.size != null) minHeight = config.Water.size;
|
||||
else if (config.Lava?.size != null) minHeight = config.Lava.size;
|
||||
|
||||
if (config.Atmosphere.hasRain)
|
||||
{
|
||||
var rainGO = GameObject.Instantiate(SearchUtilities.Find("GiantsDeep_Body/Sector_GD/Sector_GDInterior/Effects_GDInterior/Effects_GD_Rain"), effectsGO.transform);
|
||||
@ -29,9 +39,9 @@ namespace NewHorizons.Builder.Atmosphere
|
||||
var pvc = rainGO.GetComponent<PlanetaryVectionController>();
|
||||
pvc._densityByHeight = new AnimationCurve(new Keyframe[]
|
||||
{
|
||||
new Keyframe(surfaceSize - 0.5f, 0),
|
||||
new Keyframe(surfaceSize, 10f),
|
||||
new Keyframe(config.Atmosphere.size, 0f)
|
||||
new Keyframe(minHeight - 0.5f, 0),
|
||||
new Keyframe(minHeight, 10f),
|
||||
new Keyframe(maxHeight, 0f)
|
||||
});
|
||||
|
||||
rainGO.GetComponent<PlanetaryVectionController>()._activeInSector = sector;
|
||||
@ -53,9 +63,9 @@ namespace NewHorizons.Builder.Atmosphere
|
||||
var pvc = snowEmitter.GetComponent<PlanetaryVectionController>();
|
||||
pvc._densityByHeight = new AnimationCurve(new Keyframe[]
|
||||
{
|
||||
new Keyframe(surfaceSize - 0.5f, 0),
|
||||
new Keyframe(surfaceSize, 10f),
|
||||
new Keyframe(config.Atmosphere.size, 0f)
|
||||
new Keyframe(minHeight - 0.5f, 0),
|
||||
new Keyframe(minHeight, 10f),
|
||||
new Keyframe(maxHeight, 0f)
|
||||
});
|
||||
|
||||
snowEmitter.GetComponent<PlanetaryVectionController>()._activeInSector = sector;
|
||||
|
||||
@ -192,8 +192,12 @@ namespace NewHorizons.Builder.Body
|
||||
cloak.GetComponent<Renderer>().enabled = true;
|
||||
|
||||
// Cull stuff
|
||||
// Do next update so other nodes can be built first
|
||||
Delay.FireOnNextUpdate(() =>
|
||||
{
|
||||
var cullController = go.AddComponent<BrambleSectorController>();
|
||||
cullController.SetSector(sector);
|
||||
});
|
||||
|
||||
// finalize
|
||||
atmo.SetActive(true);
|
||||
|
||||
@ -54,7 +54,7 @@ namespace NewHorizons.Builder.Body
|
||||
remnantGO.transform.parent = proxy.transform;
|
||||
remnantGO.transform.localPosition = Vector3.zero;
|
||||
|
||||
SharedMake(planetGO, remnantGO, proxyController, remnant);
|
||||
SharedMake(planetGO, remnantGO, null, remnant);
|
||||
|
||||
proxyController.stellarRemnantGO = remnantGO;
|
||||
}
|
||||
@ -125,9 +125,11 @@ namespace NewHorizons.Builder.Body
|
||||
if (realSize < body.Config.Ring.outerRadius) realSize = body.Config.Ring.outerRadius;
|
||||
}
|
||||
|
||||
Renderer starAtmosphere = null;
|
||||
Renderer starFog = null;
|
||||
if (body.Config.Star != null)
|
||||
{
|
||||
StarBuilder.MakeStarProxy(planetGO, proxy, body.Config.Star, body.Mod, body.Config.isStellarRemnant);
|
||||
(_, starAtmosphere, starFog) = StarBuilder.MakeStarProxy(planetGO, proxy, body.Config.Star, body.Mod, body.Config.isStellarRemnant);
|
||||
|
||||
if (realSize < body.Config.Star.size) realSize = body.Config.Star.size;
|
||||
}
|
||||
@ -217,9 +219,17 @@ namespace NewHorizons.Builder.Body
|
||||
|
||||
if (proxyController != null)
|
||||
{
|
||||
proxyController._atmosphere = atmosphere;
|
||||
proxyController._atmosphere = atmosphere ?? starAtmosphere;
|
||||
if (fog != null)
|
||||
{
|
||||
proxyController._fog = fog;
|
||||
proxyController._fogCurveMaxVal = fogCurveMaxVal;
|
||||
}
|
||||
else if (starFog != null)
|
||||
{
|
||||
proxyController._fog = starFog;
|
||||
proxyController._fogCurveMaxVal = 0.05f;
|
||||
}
|
||||
proxyController.topClouds = topClouds;
|
||||
proxyController.lightningGenerator = lightningGenerator;
|
||||
proxyController.supernovaPlanetEffectController = supernovaPlanetEffect;
|
||||
|
||||
@ -46,24 +46,30 @@ namespace NewHorizons.Builder.Body
|
||||
sunAtmosphere.transform.position = planetGO.transform.position;
|
||||
sunAtmosphere.transform.localScale = Vector3.one * OuterRadiusRatio;
|
||||
sunAtmosphere.name = "Atmosphere_Star";
|
||||
|
||||
var atmospheres = sunAtmosphere.transform.Find("AtmoSphere");
|
||||
atmospheres.transform.localScale = Vector3.one;
|
||||
var lods = atmospheres.GetComponentsInChildren<MeshRenderer>();
|
||||
foreach (var lod in lods)
|
||||
{
|
||||
lod.material.SetFloat(InnerRadius, starModule.size);
|
||||
lod.material.SetFloat(OuterRadius, starModule.size * OuterRadiusRatio);
|
||||
}
|
||||
|
||||
var fog = sunAtmosphere.transform.Find("FogSphere").GetComponent<PlanetaryFogController>();
|
||||
fog.transform.localScale = Vector3.one;
|
||||
fog.fogRadius = starModule.size * OuterRadiusRatio;
|
||||
fog.lodFadeDistance = fog.fogRadius * (StarBuilder.OuterRadiusRatio - 1f);
|
||||
|
||||
fog.fogImpostor.material.SetFloat(Radius, starModule.size * OuterRadiusRatio);
|
||||
if (starModule.tint != null)
|
||||
{
|
||||
fog.fogTint = starModule.tint.ToColor();
|
||||
fog.fogImpostor.material.SetColor(Tint, starModule.tint.ToColor());
|
||||
sunAtmosphere.transform.Find("AtmoSphere").transform.localScale = Vector3.one;
|
||||
foreach (var lod in sunAtmosphere.transform.Find("AtmoSphere").GetComponentsInChildren<MeshRenderer>())
|
||||
{
|
||||
foreach (var lod in lods)
|
||||
lod.material.SetColor(SkyColor, starModule.tint.ToColor());
|
||||
lod.material.SetFloat(InnerRadius, starModule.size);
|
||||
lod.material.SetFloat(OuterRadius, starModule.size * OuterRadiusRatio);
|
||||
}
|
||||
}
|
||||
fog.transform.localScale = Vector3.one;
|
||||
fog.fogRadius = starModule.size * OuterRadiusRatio;
|
||||
fog.lodFadeDistance = fog.fogRadius * (StarBuilder.OuterRadiusRatio - 1f);
|
||||
fog.fogImpostor.material.SetFloat(Radius, starModule.size * OuterRadiusRatio);
|
||||
}
|
||||
|
||||
var ambientLightGO = Object.Instantiate(SearchUtilities.Find("Sun_Body/AmbientLight_SUN"), starGO.transform);
|
||||
ambientLightGO.transform.localPosition = Vector3.zero;
|
||||
@ -179,10 +185,37 @@ namespace NewHorizons.Builder.Body
|
||||
return (starGO, starController, starEvolutionController);
|
||||
}
|
||||
|
||||
public static GameObject MakeStarProxy(GameObject planet, GameObject proxyGO, StarModule starModule, IModBehaviour mod, bool isStellarRemnant)
|
||||
public static (GameObject, Renderer, Renderer) MakeStarProxy(GameObject planet, GameObject proxyGO, StarModule starModule, IModBehaviour mod, bool isStellarRemnant)
|
||||
{
|
||||
var (starGO, controller, supernova) = SharedStarGeneration(proxyGO, null, mod, starModule, isStellarRemnant);
|
||||
|
||||
Renderer atmosphere = null;
|
||||
Renderer fog = null;
|
||||
if (starModule.hasAtmosphere)
|
||||
{
|
||||
GameObject sunAtmosphere = Object.Instantiate(SearchUtilities.Find("SunProxy/Sun_Proxy_Body/Atmosphere_SUN", false) ?? SearchUtilities.Find("SunProxy(Clone)/Sun_Proxy_Body/Atmosphere_SUN"), starGO.transform);
|
||||
sunAtmosphere.transform.position = proxyGO.transform.position;
|
||||
sunAtmosphere.transform.localScale = Vector3.one * OuterRadiusRatio;
|
||||
sunAtmosphere.name = "Atmosphere_Star";
|
||||
|
||||
atmosphere = sunAtmosphere.transform.Find("Atmosphere_LOD2").GetComponent<MeshRenderer>();
|
||||
atmosphere.transform.localScale = Vector3.one;
|
||||
atmosphere.material.SetFloat(InnerRadius, starModule.size);
|
||||
atmosphere.material.SetFloat(OuterRadius, starModule.size * OuterRadiusRatio);
|
||||
|
||||
fog = sunAtmosphere.transform.Find("FogSphere").GetComponent<MeshRenderer>();
|
||||
fog.transform.localScale = Vector3.one;
|
||||
fog.material.SetFloat(Radius, starModule.size * OuterRadiusRatio);
|
||||
|
||||
if (starModule.tint != null)
|
||||
{
|
||||
fog.material.SetColor(Tint, starModule.tint.ToColor());
|
||||
atmosphere.material.SetColor(SkyColor, starModule.tint.ToColor());
|
||||
}
|
||||
|
||||
controller.atmosphere = sunAtmosphere;
|
||||
}
|
||||
|
||||
controller.isProxy = true;
|
||||
|
||||
// Planet can have multiple stars on them, so find the one that is also a remnant / not a remnant
|
||||
@ -198,7 +231,7 @@ namespace NewHorizons.Builder.Body
|
||||
supernova.mainStellerDeathController = mainController.supernova;
|
||||
}
|
||||
|
||||
return starGO;
|
||||
return (starGO, atmosphere, fog);
|
||||
}
|
||||
|
||||
private static (GameObject, StarEvolutionController, StellarDeathController) SharedStarGeneration(GameObject planetGO, Sector sector, IModBehaviour mod, StarModule starModule, bool isStellarRemnant)
|
||||
|
||||
@ -88,7 +88,7 @@ namespace NewHorizons.Builder.Body
|
||||
lightRadius = 10000,
|
||||
solarLuminosity = 0.5f
|
||||
};
|
||||
if (proxy != null) return StarBuilder.MakeStarProxy(planetGO, proxy, whiteDwarfModule, mod, true);
|
||||
if (proxy != null) return StarBuilder.MakeStarProxy(planetGO, proxy, whiteDwarfModule, mod, true).Item1;
|
||||
else return StarBuilder.Make(planetGO, sector, whiteDwarfModule, mod, true).Item1;
|
||||
}
|
||||
|
||||
@ -107,7 +107,7 @@ namespace NewHorizons.Builder.Body
|
||||
|
||||
// Instead of showing the typical star surface we use a tinted singularity
|
||||
GameObject neutronStar;
|
||||
if (proxy != null) neutronStar = StarBuilder.MakeStarProxy(planetGO, proxy, neutronStarModule, mod, true);
|
||||
if (proxy != null) neutronStar = StarBuilder.MakeStarProxy(planetGO, proxy, neutronStarModule, mod, true).Item1;
|
||||
else (neutronStar, _, _) = StarBuilder.Make(planetGO, sector, neutronStarModule, mod, true);
|
||||
neutronStar.FindChild("Surface").SetActive(false);
|
||||
|
||||
|
||||
@ -221,7 +221,7 @@ namespace NewHorizons.Builder.Props
|
||||
public static GameObject MakeMindSlidesTarget(GameObject planetGO, Sector sector, PropModule.ProjectionInfo info, IModBehaviour mod)
|
||||
{
|
||||
// spawn a trigger for the vision torch
|
||||
var path = "DreamWorld_Body/Sector_DreamWorld/Sector_Underground/Sector_PrisonCell/Ghosts_PrisonCell/GhostNodeMap_PrisonCell_Lower/Prefab_IP_GhostBird_Prisoner/Ghostbird_IP_ANIM/Ghostbird_Skin_01:Ghostbird_Rig_V01:Base/Ghostbird_Skin_01:Ghostbird_Rig_V01:Root/Ghostbird_Skin_01:Ghostbird_Rig_V01:Spine01/Ghostbird_Skin_01:Ghostbird_Rig_V01:Spine02/Ghostbird_Skin_01:Ghostbird_Rig_V01:Spine03/Ghostbird_Skin_01:Ghostbird_Rig_V01:Spine04/Ghostbird_Skin_01:Ghostbird_Rig_V01:Neck01/Ghostbird_Skin_01:Ghostbird_Rig_V01:Neck02/Ghostbird_Skin_01:Ghostbird_Rig_V01:Head/PrisonerHeadDetector";
|
||||
var path = "DreamWorld_Body/Sector_DreamWorld/Sector_Underground/Sector_PrisonCell/Ghosts_PrisonCell/GhostDirector_Prisoner/Prefab_IP_GhostBird_Prisoner/Ghostbird_IP_ANIM/Ghostbird_Skin_01:Ghostbird_Rig_V01:Base/Ghostbird_Skin_01:Ghostbird_Rig_V01:Root/Ghostbird_Skin_01:Ghostbird_Rig_V01:Spine01/Ghostbird_Skin_01:Ghostbird_Rig_V01:Spine02/Ghostbird_Skin_01:Ghostbird_Rig_V01:Spine03/Ghostbird_Skin_01:Ghostbird_Rig_V01:Spine04/Ghostbird_Skin_01:Ghostbird_Rig_V01:Neck01/Ghostbird_Skin_01:Ghostbird_Rig_V01:Neck02/Ghostbird_Skin_01:Ghostbird_Rig_V01:Head/PrisonerHeadDetector";
|
||||
var prefab = SearchUtilities.Find(path);
|
||||
var detailInfo = new PropModule.DetailInfo()
|
||||
{
|
||||
|
||||
@ -109,20 +109,6 @@ namespace NewHorizons.Builder.Props
|
||||
}
|
||||
}
|
||||
}
|
||||
if (config.Props.reveal != null)
|
||||
{
|
||||
foreach (var revealInfo in config.Props.reveal)
|
||||
{
|
||||
try
|
||||
{
|
||||
RevealBuilder.Make(go, sector, revealInfo, mod);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Couldn't make reveal location [{revealInfo.reveals}] for [{go.name}]:\n{ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (config.Props.entryLocation != null)
|
||||
{
|
||||
foreach (var entryLocationInfo in config.Props.entryLocation)
|
||||
@ -207,13 +193,6 @@ namespace NewHorizons.Builder.Props
|
||||
}
|
||||
}
|
||||
}
|
||||
if (config.Props.audioVolumes != null)
|
||||
{
|
||||
foreach (var audioVolume in config.Props.audioVolumes)
|
||||
{
|
||||
AudioVolumeBuilder.Make(go, sector, audioVolume, mod);
|
||||
}
|
||||
}
|
||||
if (config.Props.signals != null)
|
||||
{
|
||||
foreach (var signal in config.Props.signals)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
using NewHorizons.External.Modules;
|
||||
using NewHorizons.External.Modules;
|
||||
using OWML.Common;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
@ -15,6 +15,7 @@ namespace NewHorizons.Builder.ShipLog
|
||||
entryLocationGameObject.transform.position = go.transform.TransformPoint(info.position ?? Vector3.zero);
|
||||
ShipLogEntryLocation newLocation = entryLocationGameObject.AddComponent<ShipLogEntryLocation>();
|
||||
newLocation._entryID = info.id;
|
||||
newLocation._outerFogWarpVolume = go.GetComponentInChildren<OuterFogWarpVolume>();
|
||||
newLocation._isWithinCloakField = info.cloaked;
|
||||
_locationsToInitialize.Add(newLocation);
|
||||
entryLocationGameObject.SetActive(true);
|
||||
|
||||
@ -7,18 +7,18 @@ namespace NewHorizons.Builder.ShipLog
|
||||
{
|
||||
public static class RevealBuilder
|
||||
{
|
||||
public static void Make(GameObject go, Sector sector, PropModule.RevealInfo info, IModBehaviour mod)
|
||||
public static void Make(GameObject go, Sector sector, VolumesModule.RevealVolumeInfo info, IModBehaviour mod)
|
||||
{
|
||||
var newRevealGO = MakeGameObject(go, sector, info, mod);
|
||||
switch (info.revealOn)
|
||||
{
|
||||
case PropModule.RevealInfo.RevealVolumeType.Enter:
|
||||
case VolumesModule.RevealVolumeInfo.RevealVolumeType.Enter:
|
||||
MakeTrigger(newRevealGO, sector, info, mod);
|
||||
break;
|
||||
case PropModule.RevealInfo.RevealVolumeType.Observe:
|
||||
case VolumesModule.RevealVolumeInfo.RevealVolumeType.Observe:
|
||||
MakeObservable(newRevealGO, sector, info, mod);
|
||||
break;
|
||||
case PropModule.RevealInfo.RevealVolumeType.Snapshot:
|
||||
case VolumesModule.RevealVolumeInfo.RevealVolumeType.Snapshot:
|
||||
MakeSnapshot(newRevealGO, sector, info, mod);
|
||||
break;
|
||||
default:
|
||||
@ -28,7 +28,7 @@ namespace NewHorizons.Builder.ShipLog
|
||||
newRevealGO.SetActive(true);
|
||||
}
|
||||
|
||||
private static SphereShape MakeShape(GameObject go, PropModule.RevealInfo info, Shape.CollisionMode collisionMode)
|
||||
private static SphereShape MakeShape(GameObject go, VolumesModule.RevealVolumeInfo info, Shape.CollisionMode collisionMode)
|
||||
{
|
||||
SphereShape newShape = go.AddComponent<SphereShape>();
|
||||
newShape.radius = info.radius;
|
||||
@ -36,7 +36,7 @@ namespace NewHorizons.Builder.ShipLog
|
||||
return newShape;
|
||||
}
|
||||
|
||||
private static GameObject MakeGameObject(GameObject planetGO, Sector sector, PropModule.RevealInfo info, IModBehaviour mod)
|
||||
private static GameObject MakeGameObject(GameObject planetGO, Sector sector, VolumesModule.RevealVolumeInfo info, IModBehaviour mod)
|
||||
{
|
||||
GameObject revealTriggerVolume = new GameObject("Reveal Volume (" + info.revealOn + ")");
|
||||
revealTriggerVolume.SetActive(false);
|
||||
@ -45,7 +45,7 @@ namespace NewHorizons.Builder.ShipLog
|
||||
return revealTriggerVolume;
|
||||
}
|
||||
|
||||
private static void MakeTrigger(GameObject go, Sector sector, PropModule.RevealInfo info, IModBehaviour mod)
|
||||
private static void MakeTrigger(GameObject go, Sector sector, VolumesModule.RevealVolumeInfo info, IModBehaviour mod)
|
||||
{
|
||||
var shape = MakeShape(go, info, Shape.CollisionMode.Volume);
|
||||
|
||||
@ -65,7 +65,7 @@ namespace NewHorizons.Builder.ShipLog
|
||||
}
|
||||
}
|
||||
|
||||
private static void MakeObservable(GameObject go, Sector sector, PropModule.RevealInfo info, IModBehaviour mod)
|
||||
private static void MakeObservable(GameObject go, Sector sector, VolumesModule.RevealVolumeInfo info, IModBehaviour mod)
|
||||
{
|
||||
go.layer = LayerMask.NameToLayer("Interactible");
|
||||
|
||||
@ -96,7 +96,7 @@ namespace NewHorizons.Builder.ShipLog
|
||||
}
|
||||
}
|
||||
|
||||
private static void MakeSnapshot(GameObject go, Sector sector, PropModule.RevealInfo info, IModBehaviour mod)
|
||||
private static void MakeSnapshot(GameObject go, Sector sector, VolumesModule.RevealVolumeInfo info, IModBehaviour mod)
|
||||
{
|
||||
var shape = MakeShape(go, info, Shape.CollisionMode.Manual);
|
||||
|
||||
|
||||
@ -9,11 +9,11 @@ using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using Logger = NewHorizons.Utility.Logger;
|
||||
|
||||
namespace NewHorizons.Builder.Props
|
||||
namespace NewHorizons.Builder.Volumes
|
||||
{
|
||||
public static class AudioVolumeBuilder
|
||||
{
|
||||
public static AudioVolume Make(GameObject planetGO, Sector sector, PropModule.AudioVolumeInfo info, IModBehaviour mod)
|
||||
public static AudioVolume Make(GameObject planetGO, Sector sector, VolumesModule.AudioVolumeInfo info, IModBehaviour mod)
|
||||
{
|
||||
var go = new GameObject("AudioVolume");
|
||||
go.SetActive(false);
|
||||
@ -26,7 +26,7 @@ namespace NewHorizons.Builder.Props
|
||||
|
||||
var owAudioSource = go.AddComponent<OWAudioSource>();
|
||||
owAudioSource._audioSource = audioSource;
|
||||
owAudioSource.loop = true;
|
||||
owAudioSource.loop = info.loop;
|
||||
owAudioSource.SetTrack((OWAudioMixer.TrackName)Enum.Parse(typeof(OWAudioMixer.TrackName), Enum.GetName(typeof(AudioMixerTrackName), info.track)));
|
||||
AudioUtilities.SetAudioClip(owAudioSource, info.audio, mod);
|
||||
|
||||
40
NewHorizons/Builder/Volumes/HazardVolumeBuilder.cs
Normal file
40
NewHorizons/Builder/Volumes/HazardVolumeBuilder.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using NewHorizons.External.Modules;
|
||||
using OWML.Common;
|
||||
using OWML.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Builder.Volumes
|
||||
{
|
||||
public static class HazardVolumeBuilder
|
||||
{
|
||||
public static HazardVolume Make(GameObject planetGO, Sector sector, OWRigidbody owrb, VolumesModule.HazardVolumeInfo info, IModBehaviour mod)
|
||||
{
|
||||
var go = new GameObject("HazardVolume");
|
||||
go.SetActive(false);
|
||||
|
||||
go.transform.parent = sector?.transform ?? planetGO.transform;
|
||||
go.transform.position = planetGO.transform.TransformPoint(info.position != null ? (Vector3)info.position : Vector3.zero);
|
||||
go.layer = LayerMask.NameToLayer("BasicEffectVolume");
|
||||
|
||||
var shape = go.AddComponent<SphereShape>();
|
||||
shape.radius = info.radius;
|
||||
|
||||
var owTriggerVolume = go.AddComponent<OWTriggerVolume>();
|
||||
owTriggerVolume._shape = shape;
|
||||
|
||||
var hazardVolume = go.AddComponent<SimpleHazardVolume>();
|
||||
hazardVolume._attachedBody = owrb;
|
||||
hazardVolume._type = EnumUtils.Parse<HazardVolume.HazardType>(info.type.ToString(), HazardVolume.HazardType.GENERAL);
|
||||
hazardVolume._damagePerSecond = info.damagePerSecond;
|
||||
hazardVolume._firstContactDamageType = EnumUtils.Parse<InstantDamageType>(info.firstContactDamageType.ToString(), InstantDamageType.Impact);
|
||||
hazardVolume._firstContactDamage = info.firstContactDamage;
|
||||
|
||||
go.SetActive(true);
|
||||
|
||||
return hazardVolume;
|
||||
}
|
||||
}
|
||||
}
|
||||
43
NewHorizons/Builder/Volumes/NotificationVolumeBuilder.cs
Normal file
43
NewHorizons/Builder/Volumes/NotificationVolumeBuilder.cs
Normal file
@ -0,0 +1,43 @@
|
||||
using NewHorizons.Components;
|
||||
using NewHorizons.External.Modules;
|
||||
using NewHorizons.Utility;
|
||||
using OWML.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using Logger = NewHorizons.Utility.Logger;
|
||||
using NHNotificationVolume = NewHorizons.Components.NotificationVolume;
|
||||
|
||||
namespace NewHorizons.Builder.Volumes
|
||||
{
|
||||
public static class NotificationVolumeBuilder
|
||||
{
|
||||
public static NHNotificationVolume Make(GameObject planetGO, Sector sector, VolumesModule.NotificationVolumeInfo info, IModBehaviour mod)
|
||||
{
|
||||
var go = new GameObject("NotificationVolume");
|
||||
go.SetActive(false);
|
||||
|
||||
go.transform.parent = sector?.transform ?? planetGO.transform;
|
||||
go.transform.position = planetGO.transform.TransformPoint(info.position != null ? (Vector3)info.position : Vector3.zero);
|
||||
go.layer = LayerMask.NameToLayer("BasicEffectVolume");
|
||||
|
||||
var shape = go.AddComponent<SphereShape>();
|
||||
shape.radius = info.radius;
|
||||
|
||||
var owTriggerVolume = go.AddComponent<OWTriggerVolume>();
|
||||
owTriggerVolume._shape = shape;
|
||||
|
||||
var notificationVolume = go.AddComponent<NHNotificationVolume>();
|
||||
notificationVolume.SetTarget(info.target);
|
||||
if (info.entryNotification != null) notificationVolume.SetEntryNotification(info.entryNotification.displayMessage, info.entryNotification.duration);
|
||||
if (info.exitNotification != null) notificationVolume.SetExitNotification(info.exitNotification.displayMessage, info.exitNotification.duration);
|
||||
|
||||
go.SetActive(true);
|
||||
|
||||
return notificationVolume;
|
||||
}
|
||||
}
|
||||
}
|
||||
54
NewHorizons/Builder/Volumes/VolumesBuildManager.cs
Normal file
54
NewHorizons/Builder/Volumes/VolumesBuildManager.cs
Normal file
@ -0,0 +1,54 @@
|
||||
using NewHorizons.Builder.Body;
|
||||
using NewHorizons.Builder.ShipLog;
|
||||
using NewHorizons.Builder.Volumes;
|
||||
using NewHorizons.External.Configs;
|
||||
using OWML.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Logger = NewHorizons.Utility.Logger;
|
||||
|
||||
namespace NewHorizons.Builder.Volumes
|
||||
{
|
||||
public static class VolumesBuildManager
|
||||
{
|
||||
public static void Make(GameObject go, Sector sector, OWRigidbody planetBody, PlanetConfig config, IModBehaviour mod)
|
||||
{
|
||||
if (config.Volumes.revealVolumes != null)
|
||||
{
|
||||
foreach (var revealInfo in config.Volumes.revealVolumes)
|
||||
{
|
||||
try
|
||||
{
|
||||
RevealBuilder.Make(go, sector, revealInfo, mod);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Couldn't make reveal location [{revealInfo.reveals}] for [{go.name}]:\n{ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (config.Volumes.audioVolumes != null)
|
||||
{
|
||||
foreach (var audioVolume in config.Volumes.audioVolumes)
|
||||
{
|
||||
AudioVolumeBuilder.Make(go, sector, audioVolume, mod);
|
||||
}
|
||||
}
|
||||
if (config.Volumes.notificationVolumes != null)
|
||||
{
|
||||
foreach (var notificationVolume in config.Volumes.notificationVolumes)
|
||||
{
|
||||
NotificationVolumeBuilder.Make(go, sector, notificationVolume, mod);
|
||||
}
|
||||
}
|
||||
if (config.Volumes.hazardVolumes != null)
|
||||
{
|
||||
foreach (var hazardVolume in config.Volumes.hazardVolumes)
|
||||
{
|
||||
HazardVolumeBuilder.Make(go, sector, planetBody, hazardVolume, mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
129
NewHorizons/Components/NotificationVolume.cs
Normal file
129
NewHorizons/Components/NotificationVolume.cs
Normal file
@ -0,0 +1,129 @@
|
||||
using NewHorizons.Handlers;
|
||||
using OWML.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Components
|
||||
{
|
||||
[RequireComponent(typeof(OWTriggerVolume))]
|
||||
public class NotificationVolume : MonoBehaviour
|
||||
{
|
||||
private NotificationTarget _target = NotificationTarget.All;
|
||||
private bool _pin = false;
|
||||
private OWTriggerVolume _triggerVolume;
|
||||
private NotificationData _entryNotification;
|
||||
private NotificationData _exitNotification;
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
_triggerVolume = this.GetRequiredComponent<OWTriggerVolume>();
|
||||
_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<NotificationTarget>(target.ToString(), NotificationTarget.All));
|
||||
|
||||
public void SetTarget(NotificationTarget target) => _target = target;
|
||||
|
||||
public void SetEntryNotification(string displayMessage, float duration = 5)
|
||||
{
|
||||
_entryNotification = new NotificationData(_target, TranslationHandler.GetTranslation(displayMessage, TranslationHandler.TextType.UI), duration);
|
||||
}
|
||||
|
||||
public void SetExitNotification(string displayMessage, float duration = 5)
|
||||
{
|
||||
_exitNotification = new NotificationData(_target, TranslationHandler.GetTranslation(displayMessage, TranslationHandler.TextType.UI), duration);
|
||||
}
|
||||
|
||||
public void OnTriggerVolumeEntry(GameObject hitObj)
|
||||
{
|
||||
if (_target == NotificationTarget.All)
|
||||
{
|
||||
if (hitObj.CompareTag("PlayerDetector") || hitObj.CompareTag("ShipDetector"))
|
||||
{
|
||||
PostEntryNotification();
|
||||
}
|
||||
}
|
||||
else if (_target == NotificationTarget.Player)
|
||||
{
|
||||
if (hitObj.CompareTag("PlayerDetector"))
|
||||
{
|
||||
PostEntryNotification();
|
||||
}
|
||||
}
|
||||
else if (_target == NotificationTarget.Ship)
|
||||
{
|
||||
if (hitObj.CompareTag("ShipDetector"))
|
||||
{
|
||||
PostEntryNotification();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnTriggerVolumeExit(GameObject hitObj)
|
||||
{
|
||||
if (_target == NotificationTarget.All)
|
||||
{
|
||||
if (hitObj.CompareTag("PlayerDetector") || hitObj.CompareTag("ShipDetector"))
|
||||
{
|
||||
PostExitNotification();
|
||||
}
|
||||
}
|
||||
else if (_target == NotificationTarget.Player)
|
||||
{
|
||||
if (hitObj.CompareTag("PlayerDetector"))
|
||||
{
|
||||
PostExitNotification();
|
||||
}
|
||||
}
|
||||
else if (_target == NotificationTarget.Ship)
|
||||
{
|
||||
if (hitObj.CompareTag("ShipDetector"))
|
||||
{
|
||||
PostExitNotification();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void PostEntryNotification()
|
||||
{
|
||||
if (_entryNotification == null) return;
|
||||
NotificationManager.SharedInstance.PostNotification(_entryNotification, _pin);
|
||||
}
|
||||
|
||||
public void PostExitNotification()
|
||||
{
|
||||
if (_exitNotification == null) return;
|
||||
NotificationManager.SharedInstance.PostNotification(_exitNotification, _pin);
|
||||
}
|
||||
|
||||
public void UnpinEntryNotification()
|
||||
{
|
||||
if (_entryNotification == null) return;
|
||||
if (NotificationManager.SharedInstance.IsPinnedNotification(_entryNotification))
|
||||
{
|
||||
NotificationManager.SharedInstance.UnpinNotification(_entryNotification);
|
||||
}
|
||||
}
|
||||
|
||||
public void UnpinExitNotification()
|
||||
{
|
||||
if (_exitNotification == null) return;
|
||||
if (NotificationManager.SharedInstance.IsPinnedNotification(_exitNotification))
|
||||
{
|
||||
NotificationManager.SharedInstance.UnpinNotification(_exitNotification);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -98,6 +98,8 @@ namespace NewHorizons.Components.SizeControllers
|
||||
{
|
||||
var sun = GameObject.FindObjectOfType<SunController>();
|
||||
|
||||
if (sun == null) return;
|
||||
|
||||
// Need to grab all this early bc the star might only Start after the solar system was made (remnants)
|
||||
_defaultCollapseStartSurfaceMaterial = new Material(sun._collapseStartSurfaceMaterial);
|
||||
_defaultCollapseEndSurfaceMaterial = new Material(sun._collapseEndSurfaceMaterial);
|
||||
@ -194,7 +196,7 @@ namespace NewHorizons.Components.SizeControllers
|
||||
|
||||
var secondsElapsed = TimeLoop.GetSecondsElapsed();
|
||||
var lifespanInSeconds = lifespan * 60;
|
||||
if (secondsElapsed >= lifespanInSeconds)
|
||||
if (willExplode && secondsElapsed >= lifespanInSeconds)
|
||||
{
|
||||
var timeAfter = secondsElapsed - lifespanInSeconds;
|
||||
if (timeAfter <= collapseTime)
|
||||
@ -370,6 +372,7 @@ namespace NewHorizons.Components.SizeControllers
|
||||
|
||||
public void StartSupernova()
|
||||
{
|
||||
if (supernova == null) return;
|
||||
if (_isSupernova) return;
|
||||
|
||||
Logger.LogVerbose($"{gameObject.transform.root.name} started supernova");
|
||||
@ -393,7 +396,7 @@ namespace NewHorizons.Components.SizeControllers
|
||||
Logger.LogVerbose($"{gameObject.transform.root.name} stopped supernova");
|
||||
|
||||
SupernovaStop.Invoke();
|
||||
supernova.Deactivate();
|
||||
if (supernova != null) supernova.Deactivate();
|
||||
_isSupernova = false;
|
||||
if (atmosphere != null) atmosphere.SetActive(true);
|
||||
if (destructionVolume != null)
|
||||
|
||||
24
NewHorizons/External/Configs/PlanetConfig.cs
vendored
24
NewHorizons/External/Configs/PlanetConfig.cs
vendored
@ -169,6 +169,16 @@ namespace NewHorizons.External.Configs
|
||||
/// </summary>
|
||||
public WaterModule Water;
|
||||
|
||||
/// <summary>
|
||||
/// Add various volumes on this body
|
||||
/// </summary>
|
||||
public VolumesModule Volumes;
|
||||
|
||||
/// <summary>
|
||||
/// Extra data that may be used by extension mods
|
||||
/// </summary>
|
||||
public object extras;
|
||||
|
||||
public PlanetConfig()
|
||||
{
|
||||
// Always have to have a base module
|
||||
@ -312,6 +322,20 @@ namespace NewHorizons.External.Configs
|
||||
if (tornado.downwards)
|
||||
tornado.type = PropModule.TornadoInfo.TornadoType.Downwards;
|
||||
|
||||
if (Props?.audioVolumes != null)
|
||||
{
|
||||
if (Volumes == null) Volumes = new VolumesModule();
|
||||
if (Volumes.audioVolumes == null) Volumes.audioVolumes = new VolumesModule.AudioVolumeInfo[0];
|
||||
Volumes.audioVolumes = Volumes.audioVolumes.Concat(Props.audioVolumes).ToArray();
|
||||
}
|
||||
|
||||
if (Props?.reveal != null)
|
||||
{
|
||||
if (Volumes == null) Volumes = new VolumesModule();
|
||||
if (Volumes.revealVolumes == null) Volumes.revealVolumes = new VolumesModule.RevealVolumeInfo[0];
|
||||
Volumes.revealVolumes = Volumes.revealVolumes.Concat(Props.reveal).ToArray();
|
||||
}
|
||||
|
||||
if (Base.sphereOfInfluence != 0f) Base.soiOverride = Base.sphereOfInfluence;
|
||||
|
||||
// Moved a bunch of stuff off of shiplog module to star system module because it didnt exist when we made this
|
||||
|
||||
@ -102,6 +102,11 @@ namespace NewHorizons.External.Configs
|
||||
/// </summary>
|
||||
public CuriosityColorInfo[] curiosities;
|
||||
|
||||
/// <summary>
|
||||
/// Extra data that may be used by extension mods
|
||||
/// </summary>
|
||||
public object extras;
|
||||
|
||||
public class NomaiCoordinates
|
||||
{
|
||||
[MinLength(2)]
|
||||
|
||||
@ -100,6 +100,11 @@ namespace NewHorizons.External.Modules
|
||||
/// </summary>
|
||||
public bool useAtmosphereShader;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this atmosphere will have flames appear when your ship goes a certain speed.
|
||||
/// </summary>
|
||||
[DefaultValue(true)] public bool hasShockLayer = true;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum speed that your ship can go in the atmosphere where flames will appear.
|
||||
/// </summary>
|
||||
|
||||
108
NewHorizons/External/Modules/PropModule.cs
vendored
108
NewHorizons/External/Modules/PropModule.cs
vendored
@ -48,11 +48,6 @@ namespace NewHorizons.External.Modules
|
||||
/// </summary>
|
||||
public RaftInfo[] rafts;
|
||||
|
||||
/// <summary>
|
||||
/// Add triggers that reveal parts of the ship log on this planet
|
||||
/// </summary>
|
||||
public RevealInfo[] reveal;
|
||||
|
||||
/// <summary>
|
||||
/// Scatter props around this planet's surface
|
||||
/// </summary>
|
||||
@ -83,11 +78,6 @@ namespace NewHorizons.External.Modules
|
||||
/// </summary>
|
||||
public SingularityModule[] singularities;
|
||||
|
||||
/// <summary>
|
||||
/// Add audio volumes to this planet
|
||||
/// </summary>
|
||||
public AudioVolumeInfo[] audioVolumes;
|
||||
|
||||
/// <summary>
|
||||
/// Add signalscope signals to this planet
|
||||
/// </summary>
|
||||
@ -98,6 +88,10 @@ namespace NewHorizons.External.Modules
|
||||
/// </summary>
|
||||
public RemoteInfo[] remotes;
|
||||
|
||||
[Obsolete("reveal is deprecated. Use Volumes->revealVolumes instead.")] public VolumesModule.RevealVolumeInfo[] reveal;
|
||||
|
||||
[Obsolete("audioVolumes is deprecated. Use Volumes->audioVolumes instead.")] public VolumesModule.AudioVolumeInfo[] audioVolumes;
|
||||
|
||||
[JsonObject]
|
||||
public class ScatterInfo
|
||||
{
|
||||
@ -433,55 +427,6 @@ namespace NewHorizons.External.Modules
|
||||
public string xmlFile;
|
||||
}
|
||||
|
||||
[JsonObject]
|
||||
public class RevealInfo
|
||||
{
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum RevealVolumeType
|
||||
{
|
||||
[EnumMember(Value = @"enter")] Enter = 0,
|
||||
|
||||
[EnumMember(Value = @"observe")] Observe = 1,
|
||||
|
||||
[EnumMember(Value = @"snapshot")] Snapshot = 2
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The max view angle (in degrees) the player can see the volume with to unlock the fact (`observe` only)
|
||||
/// </summary>
|
||||
public float maxAngle = 180f; // Observe Only
|
||||
|
||||
/// <summary>
|
||||
/// The max distance the user can be away from the volume to reveal the fact (`snapshot` and `observe` only)
|
||||
/// </summary>
|
||||
public float maxDistance = -1f; // Snapshot & Observe Only
|
||||
|
||||
/// <summary>
|
||||
/// The position to place this volume at
|
||||
/// </summary>
|
||||
public MVector3 position;
|
||||
|
||||
/// <summary>
|
||||
/// The radius of this reveal volume
|
||||
/// </summary>
|
||||
public float radius = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// What needs to be done to the volume to unlock the facts
|
||||
/// </summary>
|
||||
[DefaultValue("enter")] public RevealVolumeType revealOn = RevealVolumeType.Enter;
|
||||
|
||||
/// <summary>
|
||||
/// A list of facts to reveal
|
||||
/// </summary>
|
||||
public string[] reveals;
|
||||
|
||||
/// <summary>
|
||||
/// An achievement to unlock. Optional.
|
||||
/// </summary>
|
||||
public string achievementID;
|
||||
}
|
||||
|
||||
[JsonObject]
|
||||
public class EntryLocationInfo
|
||||
{
|
||||
@ -818,30 +763,6 @@ namespace NewHorizons.External.Modules
|
||||
[DefaultValue(1f)] public float probability = 1f;
|
||||
}
|
||||
|
||||
[JsonObject]
|
||||
public class AudioVolumeInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The location of this audio volume. Optional (will default to 0,0,0).
|
||||
/// </summary>
|
||||
public MVector3 position;
|
||||
|
||||
/// <summary>
|
||||
/// The radius of this audio volume
|
||||
/// </summary>
|
||||
public float radius;
|
||||
|
||||
/// <summary>
|
||||
/// The audio to use. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list.
|
||||
/// </summary>
|
||||
public string audio;
|
||||
|
||||
/// <summary>
|
||||
/// The audio track of this audio volume
|
||||
/// </summary>
|
||||
[DefaultValue("environment")] public AudioMixerTrackName track = AudioMixerTrackName.Environment;
|
||||
}
|
||||
|
||||
[JsonObject]
|
||||
public class RemoteInfo
|
||||
{
|
||||
@ -1002,25 +923,4 @@ namespace NewHorizons.External.Modules
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum AudioMixerTrackName
|
||||
{
|
||||
[EnumMember(Value = @"undefined")] Undefined = 0,
|
||||
[EnumMember(Value = @"menu")] Menu = 1,
|
||||
[EnumMember(Value = @"music")] Music = 2,
|
||||
[EnumMember(Value = @"environment")] Environment = 4,
|
||||
[EnumMember(Value = @"environmentUnfiltered")] Environment_Unfiltered = 5,
|
||||
[EnumMember(Value = @"endTimesSfx")] EndTimes_SFX = 8,
|
||||
[EnumMember(Value = @"signal")] Signal = 16,
|
||||
[EnumMember(Value = @"death")] Death = 32,
|
||||
[EnumMember(Value = @"player")] Player = 64,
|
||||
[EnumMember(Value = @"playerExternal")] Player_External = 65,
|
||||
[EnumMember(Value = @"ship")] Ship = 128,
|
||||
[EnumMember(Value = @"map")] Map = 256,
|
||||
[EnumMember(Value = @"endTimesMusic")] EndTimes_Music = 512,
|
||||
[EnumMember(Value = @"muffleWhileRafting")] MuffleWhileRafting = 1024,
|
||||
[EnumMember(Value = @"muffleIndoors")] MuffleIndoors = 2048,
|
||||
[EnumMember(Value = @"slideReelMusic")] SlideReelMusic = 4096,
|
||||
}
|
||||
}
|
||||
243
NewHorizons/External/Modules/VolumesModule.cs
vendored
Normal file
243
NewHorizons/External/Modules/VolumesModule.cs
vendored
Normal file
@ -0,0 +1,243 @@
|
||||
using NewHorizons.Utility;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NewHorizons.External.Modules
|
||||
{
|
||||
[JsonObject]
|
||||
public class VolumesModule
|
||||
{
|
||||
/// <summary>
|
||||
/// Add audio volumes to this planet
|
||||
/// </summary>
|
||||
public AudioVolumeInfo[] audioVolumes;
|
||||
|
||||
/// <summary>
|
||||
/// Add hazard volumes to this planet
|
||||
/// </summary>
|
||||
public HazardVolumeInfo[] hazardVolumes;
|
||||
|
||||
/// <summary>
|
||||
/// Add notification volumes to this planet
|
||||
/// </summary>
|
||||
public NotificationVolumeInfo[] notificationVolumes;
|
||||
|
||||
/// <summary>
|
||||
/// Add triggers that reveal parts of the ship log on this planet
|
||||
/// </summary>
|
||||
public RevealVolumeInfo[] revealVolumes;
|
||||
|
||||
[JsonObject]
|
||||
public class RevealVolumeInfo
|
||||
{
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum RevealVolumeType
|
||||
{
|
||||
[EnumMember(Value = @"enter")] Enter = 0,
|
||||
|
||||
[EnumMember(Value = @"observe")] Observe = 1,
|
||||
|
||||
[EnumMember(Value = @"snapshot")] Snapshot = 2
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The max view angle (in degrees) the player can see the volume with to unlock the fact (`observe` only)
|
||||
/// </summary>
|
||||
public float maxAngle = 180f; // Observe Only
|
||||
|
||||
/// <summary>
|
||||
/// The max distance the user can be away from the volume to reveal the fact (`snapshot` and `observe` only)
|
||||
/// </summary>
|
||||
public float maxDistance = -1f; // Snapshot & Observe Only
|
||||
|
||||
/// <summary>
|
||||
/// The position to place this volume at
|
||||
/// </summary>
|
||||
public MVector3 position;
|
||||
|
||||
/// <summary>
|
||||
/// The radius of this reveal volume
|
||||
/// </summary>
|
||||
public float radius = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// What needs to be done to the volume to unlock the facts
|
||||
/// </summary>
|
||||
[DefaultValue("enter")] public RevealVolumeType revealOn = RevealVolumeType.Enter;
|
||||
|
||||
/// <summary>
|
||||
/// A list of facts to reveal
|
||||
/// </summary>
|
||||
public string[] reveals;
|
||||
|
||||
/// <summary>
|
||||
/// An achievement to unlock. Optional.
|
||||
/// </summary>
|
||||
public string achievementID;
|
||||
}
|
||||
|
||||
[JsonObject]
|
||||
public class AudioVolumeInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The location of this audio volume. Optional (will default to 0,0,0).
|
||||
/// </summary>
|
||||
public MVector3 position;
|
||||
|
||||
/// <summary>
|
||||
/// The radius of this audio volume
|
||||
/// </summary>
|
||||
public float radius;
|
||||
|
||||
/// <summary>
|
||||
/// The audio to use. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list.
|
||||
/// </summary>
|
||||
public string audio;
|
||||
|
||||
/// <summary>
|
||||
/// The audio track of this audio volume
|
||||
/// </summary>
|
||||
[DefaultValue("environment")] public AudioMixerTrackName track = AudioMixerTrackName.Environment;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to loop this audio while in this audio volume or just play it once
|
||||
/// </summary>
|
||||
[DefaultValue(true)] public bool loop = true;
|
||||
}
|
||||
|
||||
[JsonObject]
|
||||
public class NotificationVolumeInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// What the notification will show for.
|
||||
/// </summary>
|
||||
[DefaultValue("all")] public NotificationTarget target = NotificationTarget.All;
|
||||
|
||||
/// <summary>
|
||||
/// The location of this notification volume. Optional (will default to 0,0,0).
|
||||
/// </summary>
|
||||
public MVector3 position;
|
||||
|
||||
/// <summary>
|
||||
/// The radius of this notification volume.
|
||||
/// </summary>
|
||||
public float radius;
|
||||
|
||||
/// <summary>
|
||||
/// The notification that will play when you enter this volume.
|
||||
/// </summary>
|
||||
public NotificationInfo entryNotification;
|
||||
|
||||
/// <summary>
|
||||
/// The notification that will play when you exit this volume.
|
||||
/// </summary>
|
||||
public NotificationInfo exitNotification;
|
||||
|
||||
|
||||
[JsonObject]
|
||||
public class NotificationInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The message that will be displayed.
|
||||
/// </summary>
|
||||
public string displayMessage;
|
||||
|
||||
/// <summary>
|
||||
/// The duration this notification will be displayed.
|
||||
/// </summary>
|
||||
[DefaultValue(5f)] public float duration = 5f;
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum NotificationTarget
|
||||
{
|
||||
[EnumMember(Value = @"all")] All = 0,
|
||||
[EnumMember(Value = @"ship")] Ship = 1,
|
||||
[EnumMember(Value = @"player")] Player = 2,
|
||||
}
|
||||
}
|
||||
|
||||
[JsonObject]
|
||||
public class HazardVolumeInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The location of this hazard volume. Optional (will default to 0,0,0).
|
||||
/// </summary>
|
||||
public MVector3 position;
|
||||
|
||||
/// <summary>
|
||||
/// The radius of this hazard volume.
|
||||
/// </summary>
|
||||
public float radius;
|
||||
|
||||
/// <summary>
|
||||
/// The type of hazard for this volume.
|
||||
/// </summary>
|
||||
[DefaultValue("general")] public HazardType type = HazardType.GENERAL;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of damage you will take per second while inside this volume.
|
||||
/// </summary>
|
||||
[DefaultValue(10f)] public float damagePerSecond = 10f;
|
||||
|
||||
/// <summary>
|
||||
/// The type of damage you will take when you first touch this volume.
|
||||
/// </summary>
|
||||
[DefaultValue("impact")] public InstantDamageType firstContactDamageType = InstantDamageType.Impact;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of damage you will take when you first touch this volume.
|
||||
/// </summary>
|
||||
public float firstContactDamage;
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum HazardType
|
||||
{
|
||||
[EnumMember(Value = @"none")] NONE = 0,
|
||||
[EnumMember(Value = @"general")] GENERAL = 1,
|
||||
[EnumMember(Value = @"darkMatter")] DARKMATTER = 2,
|
||||
[EnumMember(Value = @"heat")] HEAT = 4,
|
||||
[EnumMember(Value = @"fire")] FIRE = 8,
|
||||
[EnumMember(Value = @"sandfall")] SANDFALL = 16,
|
||||
[EnumMember(Value = @"electricity")] ELECTRICITY = 32,
|
||||
[EnumMember(Value = @"rapids")] RAPIDS = 64
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum InstantDamageType
|
||||
{
|
||||
[EnumMember(Value = @"impact")] Impact,
|
||||
[EnumMember(Value = @"puncture")] Puncture,
|
||||
[EnumMember(Value = @"electrical")] Electrical
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum AudioMixerTrackName
|
||||
{
|
||||
[EnumMember(Value = @"undefined")] Undefined = 0,
|
||||
[EnumMember(Value = @"menu")] Menu = 1,
|
||||
[EnumMember(Value = @"music")] Music = 2,
|
||||
[EnumMember(Value = @"environment")] Environment = 4,
|
||||
[EnumMember(Value = @"environmentUnfiltered")] Environment_Unfiltered = 5,
|
||||
[EnumMember(Value = @"endTimesSfx")] EndTimes_SFX = 8,
|
||||
[EnumMember(Value = @"signal")] Signal = 16,
|
||||
[EnumMember(Value = @"death")] Death = 32,
|
||||
[EnumMember(Value = @"player")] Player = 64,
|
||||
[EnumMember(Value = @"playerExternal")] Player_External = 65,
|
||||
[EnumMember(Value = @"ship")] Ship = 128,
|
||||
[EnumMember(Value = @"map")] Map = 256,
|
||||
[EnumMember(Value = @"endTimesMusic")] EndTimes_Music = 512,
|
||||
[EnumMember(Value = @"muffleWhileRafting")] MuffleWhileRafting = 1024,
|
||||
[EnumMember(Value = @"muffleIndoors")] MuffleIndoors = 2048,
|
||||
[EnumMember(Value = @"slideReelMusic")] SlideReelMusic = 4096,
|
||||
}
|
||||
}
|
||||
7
NewHorizons/External/NewHorizonsData.cs
vendored
7
NewHorizons/External/NewHorizonsData.cs
vendored
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NewHorizons.Utility;
|
||||
|
||||
@ -11,9 +11,12 @@ namespace NewHorizons.External
|
||||
private static string _activeProfileName;
|
||||
private static readonly string FileName = "save.json";
|
||||
|
||||
// This is its own method so it can be patched by NH-QSB compat
|
||||
public static string GetProfileName() => StandaloneProfileManager.SharedInstance?.currentProfile?.profileName;
|
||||
|
||||
public static void Load()
|
||||
{
|
||||
_activeProfileName = StandaloneProfileManager.SharedInstance?.currentProfile?.profileName;
|
||||
_activeProfileName = GetProfileName();
|
||||
if (_activeProfileName == null)
|
||||
{
|
||||
Logger.LogError("Couldn't find active profile, are you on Gamepass?");
|
||||
|
||||
@ -3,6 +3,7 @@ using NewHorizons.Builder.Body;
|
||||
using NewHorizons.Builder.General;
|
||||
using NewHorizons.Builder.Orbital;
|
||||
using NewHorizons.Builder.Props;
|
||||
using NewHorizons.Builder.Volumes;
|
||||
using NewHorizons.Components;
|
||||
using NewHorizons.Components.Orbital;
|
||||
using NewHorizons.OtherMods.OWRichPresence;
|
||||
@ -251,6 +252,16 @@ namespace NewHorizons.Handlers
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Main.Instance.OnPlanetLoaded?.Invoke(body.Config.name);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError($"Error in event handler for OnPlanetLoaded on body {body.Config.name}: {e}");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -613,6 +624,11 @@ namespace NewHorizons.Handlers
|
||||
PropBuildManager.Make(go, sector, rb, body.Config, body.Mod);
|
||||
}
|
||||
|
||||
if (body.Config.Volumes != null)
|
||||
{
|
||||
VolumesBuildManager.Make(go, sector, rb, body.Config, body.Mod);
|
||||
}
|
||||
|
||||
if (body.Config.Funnel != null)
|
||||
{
|
||||
FunnelBuilder.Make(go, go.GetComponentInChildren<ConstantForceDetector>(), rb, body.Config.Funnel);
|
||||
|
||||
@ -66,9 +66,10 @@ namespace NewHorizons.Handlers
|
||||
foreach (var originalKey in config.ShipLogDictionary.Keys)
|
||||
{
|
||||
var key = originalKey.Replace("<", "<").Replace(">", ">").Replace("<![CDATA[", "").Replace("]]>", "");
|
||||
var value = config.ShipLogDictionary[originalKey].Replace("<", "<").Replace(">", ">").Replace("<![CDATA[", "").Replace("]]>", "");
|
||||
|
||||
if (!_shipLogTranslationDictionary[language].ContainsKey(key)) _shipLogTranslationDictionary[language].Add(key, config.ShipLogDictionary[originalKey]);
|
||||
else _shipLogTranslationDictionary[language][key] = config.ShipLogDictionary[originalKey];
|
||||
if (!_shipLogTranslationDictionary[language].ContainsKey(key)) _shipLogTranslationDictionary[language].Add(key, value);
|
||||
else _shipLogTranslationDictionary[language][key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,9 +79,10 @@ namespace NewHorizons.Handlers
|
||||
foreach (var originalKey in config.DialogueDictionary.Keys)
|
||||
{
|
||||
var key = originalKey.Replace("<", "<").Replace(">", ">").Replace("<![CDATA[", "").Replace("]]>", "");
|
||||
var value = config.DialogueDictionary[originalKey].Replace("<", "<").Replace(">", ">").Replace("<![CDATA[", "").Replace("]]>", "");
|
||||
|
||||
if (!_dialogueTranslationDictionary[language].ContainsKey(key)) _dialogueTranslationDictionary[language].Add(key, config.DialogueDictionary[originalKey]);
|
||||
else _dialogueTranslationDictionary[language][key] = config.DialogueDictionary[originalKey];
|
||||
if (!_dialogueTranslationDictionary[language].ContainsKey(key)) _dialogueTranslationDictionary[language].Add(key, value);
|
||||
else _dialogueTranslationDictionary[language][key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,9 +92,10 @@ namespace NewHorizons.Handlers
|
||||
foreach (var originalKey in config.UIDictionary.Keys)
|
||||
{
|
||||
var key = originalKey.Replace("<", "<").Replace(">", ">").Replace("<![CDATA[", "").Replace("]]>", "");
|
||||
var value = config.UIDictionary[originalKey].Replace("<", "<").Replace(">", ">").Replace("<![CDATA[", "").Replace("]]>", "");
|
||||
|
||||
if (!_uiTranslationDictionary[language].ContainsKey(key)) _uiTranslationDictionary[language].Add(key, config.UIDictionary[originalKey]);
|
||||
else _uiTranslationDictionary[language][key] = config.UIDictionary[originalKey];
|
||||
if (!_uiTranslationDictionary[language].ContainsKey(key)) _uiTranslationDictionary[language].Add(key, value);
|
||||
else _uiTranslationDictionary[language][key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,6 +43,22 @@ namespace NewHorizons
|
||||
/// </summary>
|
||||
UnityEvent<string> GetStarSystemLoadedEvent();
|
||||
|
||||
/// <summary>
|
||||
/// An event invoked when NH has finished a planet for a star system.
|
||||
/// Gives the name of the planet that was just loaded.
|
||||
/// </summary>
|
||||
UnityEvent<string> GetBodyLoadedEvent();
|
||||
|
||||
/// <summary>
|
||||
/// Uses JSONPath to query a body
|
||||
/// </summary>
|
||||
object QueryBody(Type outType, string bodyName, string path);
|
||||
|
||||
/// <summary>
|
||||
/// Uses JSONPath to query a system
|
||||
/// </summary>
|
||||
object QuerySystem(Type outType, string path);
|
||||
|
||||
/// <summary>
|
||||
/// Allows you to overwrite the default system. This is where the player is respawned after dying.
|
||||
/// </summary>
|
||||
|
||||
@ -69,6 +69,7 @@ namespace NewHorizons
|
||||
public class StarSystemEvent : UnityEvent<string> { }
|
||||
public StarSystemEvent OnChangeStarSystem;
|
||||
public StarSystemEvent OnStarSystemLoaded;
|
||||
public StarSystemEvent OnPlanetLoaded;
|
||||
|
||||
// For warping to the eye system
|
||||
private GameObject _ship;
|
||||
@ -127,7 +128,7 @@ namespace NewHorizons
|
||||
|
||||
BodyDict["SolarSystem"] = new List<NewHorizonsBody>();
|
||||
BodyDict["EyeOfTheUniverse"] = new List<NewHorizonsBody>(); // Keep this empty tho fr
|
||||
SystemDict["SolarSystem"] = new NewHorizonsSystem("SolarSystem", new StarSystemConfig(), Instance)
|
||||
SystemDict["SolarSystem"] = new NewHorizonsSystem("SolarSystem", new StarSystemConfig(), "", Instance)
|
||||
{
|
||||
Config =
|
||||
{
|
||||
@ -143,7 +144,7 @@ namespace NewHorizons
|
||||
}
|
||||
}
|
||||
};
|
||||
SystemDict["EyeOfTheUniverse"] = new NewHorizonsSystem("EyeOfTheUniverse", new StarSystemConfig(), Instance)
|
||||
SystemDict["EyeOfTheUniverse"] = new NewHorizonsSystem("EyeOfTheUniverse", new StarSystemConfig(), "", Instance)
|
||||
{
|
||||
Config =
|
||||
{
|
||||
@ -173,6 +174,7 @@ namespace NewHorizons
|
||||
|
||||
OnChangeStarSystem = new StarSystemEvent();
|
||||
OnStarSystemLoaded = new StarSystemEvent();
|
||||
OnPlanetLoaded = new StarSystemEvent();
|
||||
|
||||
SceneManager.sceneLoaded += OnSceneLoaded;
|
||||
SceneManager.sceneUnloaded += OnSceneUnloaded;
|
||||
@ -604,7 +606,7 @@ namespace NewHorizons
|
||||
}
|
||||
else
|
||||
{
|
||||
SystemDict[name] = new NewHorizonsSystem(name, starSystemConfig, mod);
|
||||
SystemDict[name] = new NewHorizonsSystem(name, starSystemConfig, relativePath, mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -705,7 +707,7 @@ namespace NewHorizons
|
||||
starSystemConfig.Migrate();
|
||||
starSystemConfig.FixCoordinates();
|
||||
|
||||
var system = new NewHorizonsSystem(config.starSystem, starSystemConfig, mod);
|
||||
var system = new NewHorizonsSystem(config.starSystem, starSystemConfig, $"", mod);
|
||||
|
||||
SystemDict.Add(config.starSystem, system);
|
||||
|
||||
@ -736,6 +738,13 @@ namespace NewHorizons
|
||||
#region Change star system
|
||||
public void ChangeCurrentStarSystem(string newStarSystem, bool warp = false, bool vessel = false)
|
||||
{
|
||||
// If we're just on the title screen set the system for later
|
||||
if (LoadManager.GetCurrentScene() == OWScene.TitleScreen)
|
||||
{
|
||||
_currentStarSystem = newStarSystem;
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsChangingStarSystem) return;
|
||||
|
||||
IsWarpingFromShip = warp;
|
||||
@ -747,9 +756,6 @@ namespace NewHorizons
|
||||
IsChangingStarSystem = true;
|
||||
WearingSuit = PlayerState.IsWearingSuit();
|
||||
|
||||
// We kill them so they don't move as much
|
||||
Locator.GetDeathManager().KillPlayer(DeathType.Meditation);
|
||||
|
||||
OWScene sceneToLoad;
|
||||
|
||||
if (newStarSystem == "EyeOfTheUniverse")
|
||||
@ -769,12 +775,15 @@ namespace NewHorizons
|
||||
|
||||
_currentStarSystem = newStarSystem;
|
||||
|
||||
// Freeze player inputs
|
||||
OWInput.ChangeInputMode(InputMode.None);
|
||||
|
||||
LoadManager.LoadSceneAsync(sceneToLoad, !vessel, LoadManager.FadeType.ToBlack, 0.1f, true);
|
||||
}
|
||||
|
||||
void OnDeath(DeathType _)
|
||||
{
|
||||
// We reset the solar system on death (unless we just killed the player)
|
||||
// We reset the solar system on death
|
||||
if (!IsChangingStarSystem)
|
||||
{
|
||||
// If the override is a valid system then we go there
|
||||
|
||||
@ -16,8 +16,8 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="HarmonyX" Version="2.10.0" />
|
||||
<PackageReference Include="OWML" Version="2.5.2" />
|
||||
<PackageReference Include="OuterWildsGameLibs" Version="1.1.12.168" />
|
||||
<PackageReference Include="OWML" Version="2.6.0" />
|
||||
<PackageReference Include="OuterWildsGameLibs" Version="1.1.12.201" />
|
||||
<Reference Include="../Lib/System.ComponentModel.Annotations.dll" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@ -7,12 +7,15 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using Logger = NewHorizons.Utility.Logger;
|
||||
|
||||
namespace NewHorizons
|
||||
{
|
||||
|
||||
public class NewHorizonsApi : INewHorizons
|
||||
{
|
||||
[Obsolete("Create(Dictionary<string, object> config) is deprecated, please use LoadConfigs(IModBehaviour mod) instead")]
|
||||
@ -64,20 +67,10 @@ namespace NewHorizons
|
||||
return Main.BodyDict.Values.SelectMany(x => x)?.ToList()?.FirstOrDefault(x => x.Config.name == name)?.Object;
|
||||
}
|
||||
|
||||
public string GetCurrentStarSystem()
|
||||
{
|
||||
return Main.Instance.CurrentStarSystem;
|
||||
}
|
||||
|
||||
public UnityEvent<string> GetChangeStarSystemEvent()
|
||||
{
|
||||
return Main.Instance.OnChangeStarSystem;
|
||||
}
|
||||
|
||||
public UnityEvent<string> GetStarSystemLoadedEvent()
|
||||
{
|
||||
return Main.Instance.OnStarSystemLoaded;
|
||||
}
|
||||
public string GetCurrentStarSystem() => Main.Instance.CurrentStarSystem;
|
||||
public UnityEvent<string> GetChangeStarSystemEvent() => Main.Instance.OnChangeStarSystem;
|
||||
public UnityEvent<string> GetStarSystemLoadedEvent() => Main.Instance.OnStarSystemLoaded;
|
||||
public UnityEvent<string> GetBodyLoadedEvent() => Main.Instance.OnPlanetLoaded;
|
||||
|
||||
public bool SetDefaultSystem(string name)
|
||||
{
|
||||
@ -108,6 +101,42 @@ namespace NewHorizons
|
||||
}
|
||||
}
|
||||
|
||||
private static object QueryJson(Type outType, string filePath, string jsonPath)
|
||||
{
|
||||
if (filePath == "") return null;
|
||||
try
|
||||
{
|
||||
var jsonText = File.ReadAllText(filePath);
|
||||
var jsonData = JObject.Parse(jsonText);
|
||||
return jsonData.SelectToken(jsonPath)?.ToObject(outType);
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
catch (JsonException e)
|
||||
{
|
||||
Logger.LogError(e.ToString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public object QueryBody(Type outType, string bodyName, string jsonPath)
|
||||
{
|
||||
var planet = Main.BodyDict[Main.Instance.CurrentStarSystem].Find((b) => b.Config.name == bodyName);
|
||||
return planet == null
|
||||
? null
|
||||
: QueryJson(outType, planet.Mod.ModHelper.Manifest.ModFolderPath + planet.RelativePath, jsonPath);
|
||||
}
|
||||
|
||||
public object QuerySystem(Type outType, string jsonPath)
|
||||
{
|
||||
var system = Main.SystemDict[Main.Instance.CurrentStarSystem];
|
||||
return system == null
|
||||
? null
|
||||
: QueryJson(outType, system.Mod.ModHelper.Manifest.ModFolderPath + system.RelativePath, jsonPath);
|
||||
}
|
||||
|
||||
public GameObject SpawnObject(GameObject planet, Sector sector, string propToCopyPath, Vector3 position, Vector3 eulerAngles,
|
||||
float scale, bool alignWithNormal)
|
||||
{
|
||||
|
||||
@ -47,7 +47,7 @@ namespace NewHorizons.OtherMods.OWRichPresence
|
||||
var localizedName = TranslationHandler.GetTranslation(name, TranslationHandler.TextType.UI);
|
||||
var message = TranslationHandler.GetTranslation("RICH_PRESENCE_EXPLORING", TranslationHandler.TextType.UI).Replace("{0}", localizedName);
|
||||
|
||||
API.CreateTrigger(go, sector, message, name.Replace(" ", "").Replace("'", "").ToLowerInvariant());
|
||||
API.CreateTrigger(go, sector, message, name.Replace(" ", "").Replace("'", "").Replace("-", "").ToLowerInvariant());
|
||||
}
|
||||
|
||||
public static void OnStarSystemLoaded(string name)
|
||||
|
||||
29
NewHorizons/Patches/ShapePatches.cs
Normal file
29
NewHorizons/Patches/ShapePatches.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using HarmonyLib;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NewHorizons.Patches
|
||||
{
|
||||
[HarmonyPatch]
|
||||
public class ShapePatches
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(ShapeManager), nameof(ShapeManager.Initialize))]
|
||||
public static bool ShapeManager_Initialize()
|
||||
{
|
||||
ShapeManager._exists = true;
|
||||
|
||||
ShapeManager._detectors = new ShapeManager.Layer(256);
|
||||
for (int index = 0; index < 256; ++index)
|
||||
ShapeManager._detectors[index].contacts = new List<ShapeManager.ContactData>(64);
|
||||
|
||||
ShapeManager._volumes = new ShapeManager.Layer[4];
|
||||
for (int index = 0; index < 4; ++index)
|
||||
ShapeManager._volumes[index] = new ShapeManager.Layer(2048);
|
||||
|
||||
ShapeManager._locked = false;
|
||||
ShapeManager._frameFlag = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -128,6 +128,17 @@
|
||||
"description": "Add water to this planet",
|
||||
"$ref": "#/definitions/WaterModule"
|
||||
},
|
||||
"Volumes": {
|
||||
"description": "Add various volumes on this body",
|
||||
"$ref": "#/definitions/VolumesModule"
|
||||
},
|
||||
"extras": {
|
||||
"type": "object",
|
||||
"description": "Extra data that may be used by extension mods",
|
||||
"additionalProperties": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"description": "The schema to validate with"
|
||||
@ -302,6 +313,11 @@
|
||||
"type": "boolean",
|
||||
"description": "Whether we use an atmospheric shader on the planet. Doesn't affect clouds, fog, rain, snow, oxygen, etc. Purely\nvisual."
|
||||
},
|
||||
"hasShockLayer": {
|
||||
"type": "boolean",
|
||||
"description": "Whether this atmosphere will have flames appear when your ship goes a certain speed.",
|
||||
"default": true
|
||||
},
|
||||
"minShockSpeed": {
|
||||
"type": "number",
|
||||
"description": "Minimum speed that your ship can go in the atmosphere where flames will appear.",
|
||||
@ -925,13 +941,6 @@
|
||||
"$ref": "#/definitions/RaftInfo"
|
||||
}
|
||||
},
|
||||
"reveal": {
|
||||
"type": "array",
|
||||
"description": "Add triggers that reveal parts of the ship log on this planet",
|
||||
"items": {
|
||||
"$ref": "#/definitions/RevealInfo"
|
||||
}
|
||||
},
|
||||
"scatter": {
|
||||
"type": "array",
|
||||
"description": "Scatter props around this planet's surface",
|
||||
@ -974,13 +983,6 @@
|
||||
"$ref": "#/definitions/SingularityModule"
|
||||
}
|
||||
},
|
||||
"audioVolumes": {
|
||||
"type": "array",
|
||||
"description": "Add audio volumes to this planet",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AudioVolumeInfo"
|
||||
}
|
||||
},
|
||||
"signals": {
|
||||
"type": "array",
|
||||
"description": "Add signalscope signals to this planet",
|
||||
@ -1337,61 +1339,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"RevealInfo": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"maxAngle": {
|
||||
"type": "number",
|
||||
"description": "The max view angle (in degrees) the player can see the volume with to unlock the fact (`observe` only)",
|
||||
"format": "float"
|
||||
},
|
||||
"maxDistance": {
|
||||
"type": "number",
|
||||
"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",
|
||||
"$ref": "#/definitions/RevealVolumeType"
|
||||
},
|
||||
"reveals": {
|
||||
"type": "array",
|
||||
"description": "A list of facts to reveal",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"achievementID": {
|
||||
"type": "string",
|
||||
"description": "An achievement to unlock. Optional."
|
||||
}
|
||||
}
|
||||
},
|
||||
"RevealVolumeType": {
|
||||
"type": "string",
|
||||
"description": "",
|
||||
"x-enumNames": [
|
||||
"Enter",
|
||||
"Observe",
|
||||
"Snapshot"
|
||||
],
|
||||
"enum": [
|
||||
"enter",
|
||||
"observe",
|
||||
"snapshot"
|
||||
]
|
||||
},
|
||||
"ScatterInfo": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
@ -1801,70 +1748,6 @@
|
||||
"whiteHole"
|
||||
]
|
||||
},
|
||||
"AudioVolumeInfo": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"position": {
|
||||
"description": "The location of this audio volume. Optional (will default to 0,0,0).",
|
||||
"$ref": "#/definitions/MVector3"
|
||||
},
|
||||
"radius": {
|
||||
"type": "number",
|
||||
"description": "The radius of this audio volume",
|
||||
"format": "float"
|
||||
},
|
||||
"audio": {
|
||||
"type": "string",
|
||||
"description": "The audio to use. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list."
|
||||
},
|
||||
"track": {
|
||||
"description": "The audio track of this audio volume",
|
||||
"default": "environment",
|
||||
"$ref": "#/definitions/AudioMixerTrackName"
|
||||
}
|
||||
}
|
||||
},
|
||||
"AudioMixerTrackName": {
|
||||
"type": "string",
|
||||
"description": "",
|
||||
"x-enumNames": [
|
||||
"Undefined",
|
||||
"Menu",
|
||||
"Music",
|
||||
"Environment",
|
||||
"Environment_Unfiltered",
|
||||
"EndTimes_SFX",
|
||||
"Signal",
|
||||
"Death",
|
||||
"Player",
|
||||
"Player_External",
|
||||
"Ship",
|
||||
"Map",
|
||||
"EndTimes_Music",
|
||||
"MuffleWhileRafting",
|
||||
"MuffleIndoors",
|
||||
"SlideReelMusic"
|
||||
],
|
||||
"enum": [
|
||||
"undefined",
|
||||
"menu",
|
||||
"music",
|
||||
"environment",
|
||||
"environmentUnfiltered",
|
||||
"endTimesSfx",
|
||||
"signal",
|
||||
"death",
|
||||
"player",
|
||||
"playerExternal",
|
||||
"ship",
|
||||
"map",
|
||||
"endTimesMusic",
|
||||
"muffleWhileRafting",
|
||||
"muffleIndoors",
|
||||
"slideReelMusic"
|
||||
]
|
||||
},
|
||||
"SignalInfo": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
@ -2503,6 +2386,296 @@
|
||||
"$ref": "#/definitions/MColor"
|
||||
}
|
||||
}
|
||||
},
|
||||
"VolumesModule": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"audioVolumes": {
|
||||
"type": "array",
|
||||
"description": "Add audio volumes to this planet",
|
||||
"items": {
|
||||
"$ref": "#/definitions/AudioVolumeInfo"
|
||||
}
|
||||
},
|
||||
"hazardVolumes": {
|
||||
"type": "array",
|
||||
"description": "Add hazard volumes to this planet",
|
||||
"items": {
|
||||
"$ref": "#/definitions/HazardVolumeInfo"
|
||||
}
|
||||
},
|
||||
"notificationVolumes": {
|
||||
"type": "array",
|
||||
"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",
|
||||
"items": {
|
||||
"$ref": "#/definitions/RevealVolumeInfo"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"AudioVolumeInfo": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"position": {
|
||||
"description": "The location of this audio volume. Optional (will default to 0,0,0).",
|
||||
"$ref": "#/definitions/MVector3"
|
||||
},
|
||||
"radius": {
|
||||
"type": "number",
|
||||
"description": "The radius of this audio volume",
|
||||
"format": "float"
|
||||
},
|
||||
"audio": {
|
||||
"type": "string",
|
||||
"description": "The audio to use. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list."
|
||||
},
|
||||
"track": {
|
||||
"description": "The audio track of this audio volume",
|
||||
"default": "environment",
|
||||
"$ref": "#/definitions/AudioMixerTrackName"
|
||||
},
|
||||
"loop": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to loop this audio while in this audio volume or just play it once",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"AudioMixerTrackName": {
|
||||
"type": "string",
|
||||
"description": "",
|
||||
"x-enumNames": [
|
||||
"Undefined",
|
||||
"Menu",
|
||||
"Music",
|
||||
"Environment",
|
||||
"Environment_Unfiltered",
|
||||
"EndTimes_SFX",
|
||||
"Signal",
|
||||
"Death",
|
||||
"Player",
|
||||
"Player_External",
|
||||
"Ship",
|
||||
"Map",
|
||||
"EndTimes_Music",
|
||||
"MuffleWhileRafting",
|
||||
"MuffleIndoors",
|
||||
"SlideReelMusic"
|
||||
],
|
||||
"enum": [
|
||||
"undefined",
|
||||
"menu",
|
||||
"music",
|
||||
"environment",
|
||||
"environmentUnfiltered",
|
||||
"endTimesSfx",
|
||||
"signal",
|
||||
"death",
|
||||
"player",
|
||||
"playerExternal",
|
||||
"ship",
|
||||
"map",
|
||||
"endTimesMusic",
|
||||
"muffleWhileRafting",
|
||||
"muffleIndoors",
|
||||
"slideReelMusic"
|
||||
]
|
||||
},
|
||||
"HazardVolumeInfo": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"position": {
|
||||
"description": "The location of this hazard volume. Optional (will default to 0,0,0).",
|
||||
"$ref": "#/definitions/MVector3"
|
||||
},
|
||||
"radius": {
|
||||
"type": "number",
|
||||
"description": "The radius of this hazard volume.",
|
||||
"format": "float"
|
||||
},
|
||||
"type": {
|
||||
"description": "The type of hazard for this volume.",
|
||||
"default": "general",
|
||||
"$ref": "#/definitions/HazardType"
|
||||
},
|
||||
"damagePerSecond": {
|
||||
"type": "number",
|
||||
"description": "The amount of damage you will take per second while inside this volume.",
|
||||
"format": "float",
|
||||
"default": 10.0
|
||||
},
|
||||
"firstContactDamageType": {
|
||||
"description": "The type of damage you will take when you first touch this volume.",
|
||||
"default": "impact",
|
||||
"$ref": "#/definitions/InstantDamageType"
|
||||
},
|
||||
"firstContactDamage": {
|
||||
"type": "number",
|
||||
"description": "The amount of damage you will take when you first touch this volume.",
|
||||
"format": "float"
|
||||
}
|
||||
}
|
||||
},
|
||||
"HazardType": {
|
||||
"type": "string",
|
||||
"description": "",
|
||||
"x-enumNames": [
|
||||
"NONE",
|
||||
"GENERAL",
|
||||
"DARKMATTER",
|
||||
"HEAT",
|
||||
"FIRE",
|
||||
"SANDFALL",
|
||||
"ELECTRICITY",
|
||||
"RAPIDS"
|
||||
],
|
||||
"enum": [
|
||||
"none",
|
||||
"general",
|
||||
"darkMatter",
|
||||
"heat",
|
||||
"fire",
|
||||
"sandfall",
|
||||
"electricity",
|
||||
"rapids"
|
||||
]
|
||||
},
|
||||
"InstantDamageType": {
|
||||
"type": "string",
|
||||
"description": "",
|
||||
"x-enumNames": [
|
||||
"Impact",
|
||||
"Puncture",
|
||||
"Electrical"
|
||||
],
|
||||
"enum": [
|
||||
"impact",
|
||||
"puncture",
|
||||
"electrical"
|
||||
]
|
||||
},
|
||||
"NotificationVolumeInfo": {
|
||||
"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).",
|
||||
"$ref": "#/definitions/MVector3"
|
||||
},
|
||||
"radius": {
|
||||
"type": "number",
|
||||
"description": "The radius of this notification volume.",
|
||||
"format": "float"
|
||||
},
|
||||
"entryNotification": {
|
||||
"description": "The notification that will play when you enter this volume.",
|
||||
"$ref": "#/definitions/NotificationInfo"
|
||||
},
|
||||
"exitNotification": {
|
||||
"description": "The notification that will play when you exit this volume.",
|
||||
"$ref": "#/definitions/NotificationInfo"
|
||||
}
|
||||
}
|
||||
},
|
||||
"NotificationTarget": {
|
||||
"type": "string",
|
||||
"description": "",
|
||||
"x-enumNames": [
|
||||
"All",
|
||||
"Ship",
|
||||
"Player"
|
||||
],
|
||||
"enum": [
|
||||
"all",
|
||||
"ship",
|
||||
"player"
|
||||
]
|
||||
},
|
||||
"NotificationInfo": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"displayMessage": {
|
||||
"type": "string",
|
||||
"description": "The message that will be displayed."
|
||||
},
|
||||
"duration": {
|
||||
"type": "number",
|
||||
"description": "The duration this notification will be displayed.",
|
||||
"format": "float",
|
||||
"default": 5.0
|
||||
}
|
||||
}
|
||||
},
|
||||
"RevealVolumeInfo": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"maxAngle": {
|
||||
"type": "number",
|
||||
"description": "The max view angle (in degrees) the player can see the volume with to unlock the fact (`observe` only)",
|
||||
"format": "float"
|
||||
},
|
||||
"maxDistance": {
|
||||
"type": "number",
|
||||
"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",
|
||||
"$ref": "#/definitions/RevealVolumeType"
|
||||
},
|
||||
"reveals": {
|
||||
"type": "array",
|
||||
"description": "A list of facts to reveal",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"achievementID": {
|
||||
"type": "string",
|
||||
"description": "An achievement to unlock. Optional."
|
||||
}
|
||||
}
|
||||
},
|
||||
"RevealVolumeType": {
|
||||
"type": "string",
|
||||
"description": "",
|
||||
"x-enumNames": [
|
||||
"Enter",
|
||||
"Observe",
|
||||
"Snapshot"
|
||||
],
|
||||
"enum": [
|
||||
"enter",
|
||||
"observe",
|
||||
"snapshot"
|
||||
]
|
||||
}
|
||||
},
|
||||
"$docs": {
|
||||
|
||||
@ -71,6 +71,13 @@
|
||||
"$ref": "#/definitions/CuriosityColorInfo"
|
||||
}
|
||||
},
|
||||
"extras": {
|
||||
"type": "object",
|
||||
"description": "Extra data that may be used by extension mods",
|
||||
"additionalProperties": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"description": "The schema to validate with"
|
||||
|
||||
@ -69,7 +69,13 @@ namespace NewHorizons.Utility.DebugMenu
|
||||
|
||||
PauseMenuInitHook();
|
||||
|
||||
Main.Instance.OnChangeStarSystem.AddListener((string s) => SaveLoadedConfigsForRecentSystem());
|
||||
Main.Instance.OnChangeStarSystem.AddListener((string s) => {
|
||||
if (saveButtonUnlocked)
|
||||
{
|
||||
SaveLoadedConfigsForRecentSystem();
|
||||
saveButtonUnlocked = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -227,6 +233,24 @@ namespace NewHorizons.Utility.DebugMenu
|
||||
|
||||
var json = loadedConfigFiles[filePath].ToSerializedJson();
|
||||
|
||||
try
|
||||
{
|
||||
var path = loadedMod.ModHelper.Manifest.ModFolderPath + backupFolderName + relativePath;
|
||||
Logger.LogVerbose($"Backing up... {relativePath} to {path}");
|
||||
var oldPath = loadedMod.ModHelper.Manifest.ModFolderPath + relativePath;
|
||||
var directoryName = Path.GetDirectoryName(path);
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
if (File.Exists(oldPath))
|
||||
File.WriteAllBytes(path, File.ReadAllBytes(oldPath));
|
||||
else
|
||||
File.WriteAllText(path, json);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError($"Failed to save backup file {backupFolderName}{relativePath}:\n{e}");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Logger.Log($"Saving... {relativePath} to {filePath}");
|
||||
@ -240,19 +264,6 @@ namespace NewHorizons.Utility.DebugMenu
|
||||
{
|
||||
Logger.LogError($"Failed to save file {relativePath}:\n{e}");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var path = Main.Instance.ModHelper.Manifest.ModFolderPath + backupFolderName + relativePath;
|
||||
var directoryName = Path.GetDirectoryName(path);
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
File.WriteAllText(path, json);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError($"Failed to save backup file {backupFolderName}{relativePath}:\n{e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -11,15 +11,17 @@ namespace NewHorizons.Utility
|
||||
public class NewHorizonsSystem
|
||||
{
|
||||
public string UniqueID;
|
||||
public string RelativePath;
|
||||
public SpawnModule Spawn = null;
|
||||
public SpawnPoint SpawnPoint = null;
|
||||
public StarSystemConfig Config;
|
||||
public IModBehaviour Mod;
|
||||
|
||||
public NewHorizonsSystem(string uniqueID, StarSystemConfig config, IModBehaviour mod)
|
||||
public NewHorizonsSystem(string uniqueID, StarSystemConfig config, string relativePath, IModBehaviour mod)
|
||||
{
|
||||
UniqueID = uniqueID;
|
||||
Config = config;
|
||||
RelativePath = relativePath;
|
||||
Mod = mod;
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,40 +95,43 @@ namespace NewHorizons.Utility
|
||||
{
|
||||
if (CachedGameObjects.TryGetValue(path, out var go)) return go;
|
||||
|
||||
// 1: normal find
|
||||
go = GameObject.Find(path);
|
||||
if (go == null)
|
||||
if (go)
|
||||
{
|
||||
// find inactive use root + transform.find
|
||||
var names = path.Split('/');
|
||||
var rootName = names[0];
|
||||
var root = SceneManager.GetActiveScene().GetRootGameObjects().FirstOrDefault(x => x.name == rootName);
|
||||
if (root == null)
|
||||
{
|
||||
if (warn) Logger.LogWarning($"Couldn't find root object in path {path}");
|
||||
return null;
|
||||
}
|
||||
|
||||
var childPath = string.Join("/", names.Skip(1));
|
||||
go = root.FindChild(childPath);
|
||||
if (go == null)
|
||||
{
|
||||
var name = names.Last();
|
||||
if (warn) Logger.LogWarning($"Couldn't find object in path {path}, will look for potential matches for name {name}");
|
||||
// find resource to include inactive objects
|
||||
// also includes prefabs but hopefully thats okay
|
||||
go = FindResourceOfTypeAndName<GameObject>(name);
|
||||
if (go == null)
|
||||
{
|
||||
if (warn) Logger.LogWarning($"Couldn't find object with name {name}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CachedGameObjects.Add(path, go);
|
||||
return go;
|
||||
}
|
||||
|
||||
// 2: find inactive using root + transform.find
|
||||
var names = path.Split('/');
|
||||
|
||||
var rootName = names[0];
|
||||
var root = SceneManager.GetActiveScene().GetRootGameObjects().FirstOrDefault(x => x.name == rootName);
|
||||
|
||||
var childPath = string.Join("/", names.Skip(1));
|
||||
go = root ? root.FindChild(childPath) : null;
|
||||
if (go)
|
||||
{
|
||||
CachedGameObjects.Add(path, go);
|
||||
return go;
|
||||
}
|
||||
|
||||
var name = names.Last();
|
||||
if (warn) Logger.LogWarning($"Couldn't find object in path {path}, will look for potential matches for name {name}");
|
||||
// 3: find resource to include inactive objects
|
||||
// also includes prefabs but hopefully thats okay
|
||||
go = FindResourceOfTypeAndName<GameObject>(name);
|
||||
if (go)
|
||||
{
|
||||
CachedGameObjects.Add(path, go);
|
||||
return go;
|
||||
}
|
||||
|
||||
if (warn) Logger.LogWarning($"Couldn't find object with name {name}");
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<GameObject> GetAllChildren(this GameObject parent)
|
||||
{
|
||||
var children = new List<GameObject>();
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
"author": "xen, Bwc9876, clay, MegaPiggy, John, Hawkbar, Trifid, Book",
|
||||
"name": "New Horizons",
|
||||
"uniqueName": "xen.NewHorizons",
|
||||
"version": "1.5.0",
|
||||
"version": "1.5.1",
|
||||
"owmlVersion": "2.6.0",
|
||||
"dependencies": [ "JohnCorby.VanillaFix" ],
|
||||
"conflicts": [ "Raicuparta.QuantumSpaceBuddies", "PacificEngine.OW_Randomizer" ],
|
||||
|
||||
@ -78,16 +78,29 @@ public static class SchemaExporter
|
||||
{"description", _description}
|
||||
});
|
||||
|
||||
if (_title == "Celestial Body Schema")
|
||||
switch (_title)
|
||||
{
|
||||
case "Celestial Body Schema":
|
||||
schema.Definitions["OrbitModule"].Properties["semiMajorAxis"].Default = 5000f;
|
||||
}
|
||||
|
||||
if (_title == "Star System Schema")
|
||||
{
|
||||
break;
|
||||
case "Star System Schema":
|
||||
schema.Definitions["NomaiCoordinates"].Properties["x"].UniqueItems = true;
|
||||
schema.Definitions["NomaiCoordinates"].Properties["y"].UniqueItems = true;
|
||||
schema.Definitions["NomaiCoordinates"].Properties["z"].UniqueItems = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (_title is "Star System Schema" or "Celestial Body Schema")
|
||||
{
|
||||
schema.Properties["extras"] = new JsonSchemaProperty {
|
||||
Type = JsonObjectType.Object,
|
||||
Description = "Extra data that may be used by extension mods",
|
||||
AllowAdditionalProperties = true,
|
||||
AdditionalPropertiesSchema = new JsonSchema
|
||||
{
|
||||
Type = JsonObjectType.Object
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return schema;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
@ -20,7 +20,7 @@
|
||||
</None>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="NJsonSchema" Version="10.7.2" />
|
||||
<PackageReference Include="OuterWildsGameLibs" Version="1.1.12.168" IncludeAssets="compile" />
|
||||
<PackageReference Include="OuterWildsGameLibs" Version="1.1.12.201" IncludeAssets="compile" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@ -9,21 +9,99 @@ First create the following interface in your mod:
|
||||
|
||||
```cs
|
||||
public interface INewHorizons
|
||||
{
|
||||
{
|
||||
[Obsolete("Create(Dictionary<string, object> config) is deprecated, please use LoadConfigs(IModBehaviour mod) instead")]
|
||||
void Create(Dictionary<string, object> config);
|
||||
|
||||
[Obsolete("Create(Dictionary<string, object> config) is deprecated, please use LoadConfigs(IModBehaviour mod) instead")]
|
||||
void Create(Dictionary<string, object> config, IModBehaviour mod);
|
||||
|
||||
/// <summary>
|
||||
/// Will load all configs in the regular folders (planets, systems, translations, etc) for this mod.
|
||||
/// The NH addon config template is just a single call to this API method.
|
||||
/// </summary>
|
||||
void LoadConfigs(IModBehaviour mod);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve the root GameObject of a custom planet made by creating configs.
|
||||
/// Will only work if the planet has been created (see GetStarSystemLoadedEvent)
|
||||
/// </summary>
|
||||
GameObject GetPlanet(string name);
|
||||
|
||||
/// <summary>
|
||||
/// The name of the current star system loaded.
|
||||
/// </summary>
|
||||
string GetCurrentStarSystem();
|
||||
|
||||
/// <summary>
|
||||
/// An event invoked when the player begins loading the new star system, before the scene starts to load.
|
||||
/// Gives the name of the star system being switched to.
|
||||
/// </summary>
|
||||
UnityEvent<string> GetChangeStarSystemEvent();
|
||||
|
||||
/// <summary>
|
||||
/// An event invoked when NH has finished generating all planets for a new star system.
|
||||
/// Gives the name of the star system that was just loaded.
|
||||
/// </summary>
|
||||
UnityEvent<string> GetStarSystemLoadedEvent();
|
||||
|
||||
GameObject SpawnObject(GameObject planet, Sector sector, string propToCopyPath, Vector3 position, Vector3 eulerAngles, float scale, bool alignWithNormal);
|
||||
/// <summary>
|
||||
/// An event invoked when NH has finished a planet for a star system.
|
||||
/// Gives the name of the planet that was just loaded.
|
||||
/// </summary>
|
||||
UnityEvent<string> GetBodyLoadedEvent();
|
||||
|
||||
/// <summary>
|
||||
/// Uses JSONPath to query a body
|
||||
/// </summary>
|
||||
object QueryBody(Type outType, string bodyName, string path);
|
||||
|
||||
/// <summary>
|
||||
/// Uses JSONPath to query a system
|
||||
/// </summary>
|
||||
object QuerySystem(Type outType, string path);
|
||||
|
||||
/// <summary>
|
||||
/// Allows you to overwrite the default system. This is where the player is respawned after dying.
|
||||
/// </summary>
|
||||
bool SetDefaultSystem(string name);
|
||||
|
||||
/// <summary>
|
||||
/// Allows you to instantly begin a warp to a new star system.
|
||||
/// Will return false if that system does not exist (cannot be warped to).
|
||||
/// </summary>
|
||||
bool ChangeCurrentStarSystem(string name);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the uniqueIDs of each installed NH addon.
|
||||
/// </summary>
|
||||
string[] GetInstalledAddons();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows you to spawn a copy of a prop by specifying its path.
|
||||
/// This is the same as using Props->details in a config, but also returns the spawned gameObject to you.
|
||||
/// </summary>
|
||||
GameObject SpawnObject(GameObject planet, Sector sector, string propToCopyPath, Vector3 position, Vector3 eulerAngles,
|
||||
float scale, bool alignWithNormal);
|
||||
|
||||
/// <summary>
|
||||
/// Allows you to spawn an AudioSignal on a planet.
|
||||
/// This is the same as using Props->signals in a config, but also returns the spawned AudioSignal to you.
|
||||
/// This method will not set its position. You will have to do that with the returned object.
|
||||
/// </summary>
|
||||
AudioSignal SpawnSignal(IModBehaviour mod, GameObject root, string audio, string name, string frequency,
|
||||
float sourceRadius = 1f, float detectionRadius = 20f, float identificationRadius = 10f, bool insideCloak = false,
|
||||
bool onlyAudibleToScope = true, string reveals = "");
|
||||
|
||||
/// <summary>
|
||||
/// Allows you to spawn character dialogue on a planet. Also returns the RemoteDialogueTrigger if remoteTriggerRadius is specified.
|
||||
/// This is the same as using Props->dialogue in a config, but also returns the spawned game objects to you.
|
||||
/// This method will not set the position of the dialogue or remote trigger. You will have to do that with the returned objects.
|
||||
/// </summary>
|
||||
(CharacterDialogueTree, RemoteDialogueTrigger) SpawnDialogue(IModBehaviour mod, GameObject root, string xmlFile, float radius = 1f,
|
||||
float range = 1f, string blockAfterPersistentCondition = null, float lookAtRadius = 1f, string pathToAnimController = null,
|
||||
float remoteTriggerRadius = 0f);
|
||||
}
|
||||
```
|
||||
|
||||
In your main `ModBehaviour` class you can get the NewHorizons API like so:
|
||||
@ -33,7 +111,7 @@ public class MyMod : ModBehaviour
|
||||
{
|
||||
void Start()
|
||||
{
|
||||
INewHorizons NewHorizonsAPI = ModHelper.Interaction.GetModApi<INewHorizons>("xen.NewHorizons");
|
||||
INewHorizons NewHorizonsAPI = ModHelper.Interaction.TryGetModApi<INewHorizons>("xen.NewHorizons");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
74
docs/content/pages/tutorials/extending.md
Normal file
74
docs/content/pages/tutorials/extending.md
Normal file
@ -0,0 +1,74 @@
|
||||
---
|
||||
Title: Extending Configs
|
||||
Description: A guide on extending config files with the New Horizons API
|
||||
Sort_Priority: 5
|
||||
---
|
||||
|
||||
# Extending Configs
|
||||
|
||||
This guide will explain how to use the API to add new features to New Horizons.
|
||||
|
||||
## How Extending Works
|
||||
|
||||
Addon developers will add a key to the `extras` object in the root of the config
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Wetrock",
|
||||
"extras": {
|
||||
"myCoolExtensionData": {
|
||||
"myCoolExtensionProperty": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Your mod will then use the API's `QueryBody` method to obtain the `myCoolExtensionData` object.
|
||||
|
||||
**It's up to the addon dev to list your mod as a dependency!**
|
||||
|
||||
## Extending Planets
|
||||
|
||||
You can extend all planets by hooking into the `OnBodyLoaded` event of the API:
|
||||
|
||||
```csharp
|
||||
var api = ModHelper.Interactions.TryGetModApi<INewHorizons>("xen.NewHorizons");
|
||||
api.GetBodyLoadedEvent().AddListener((name) => {
|
||||
ModHelper.Console.WriteLine($"Body: {name} Loaded!");
|
||||
});
|
||||
```
|
||||
|
||||
In order to get your extra module, first define the module as a class:
|
||||
|
||||
```csharp
|
||||
public class MyCoolExtensionData {
|
||||
int myCoolExtensionProperty;
|
||||
}
|
||||
```
|
||||
|
||||
Then, use the `QueryBody` method:
|
||||
|
||||
```csharp
|
||||
var api = ModHelper.Interactions.TryGetModApi<INewHorizons>("xen.NewHorizons");
|
||||
api.GetBodyLoadedEvent().AddListener((name) => {
|
||||
ModHelper.Console.WriteLine($"Body: {name} Loaded!");
|
||||
var potentialData = api.QueryBody(typeof(MyCoolExtensionData), "$.extras.myCoolExtensionData", name);
|
||||
// Makes sure the module is valid and not null
|
||||
if (potentialData is MyCoolExtensionData data) {
|
||||
ModHelper.Console.WriteLine($"myCoolExtensionProperty for {name} is {data.myCoolExtensionProperty}!");
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Extending Systems
|
||||
|
||||
Extending systems is the exact same as extending planets, except you use the `QuerySystem` method instead.
|
||||
|
||||
## Accessing Other Values
|
||||
|
||||
You can also use the `QueryBody` method to get values of the config outside of your extension object
|
||||
|
||||
```csharp
|
||||
var primaryBody = api.QueryBody(typeof(string), "Wetrock", "$.Orbit.primaryBody");
|
||||
ModHelper.Console.WriteLine($"Primary of {bodyName} is {primaryBody ?? "NULL"}!");
|
||||
```
|
||||
Loading…
x
Reference in New Issue
Block a user