mirror of
https://github.com/Outer-Wilds-New-Horizons/new-horizons.git
synced 2025-12-11 12:05:23 +01:00
1.28.0 (#1071)
## Major features - Added a new `forces` sub-module to the `Volumes` planet module with new volume types for the various force volumes available in the game, including `directionalVolumes` (used by Nomai gravity crystals and anti-gravity surfaces) and `cylindricalVolumes` (used by the rings on the Construction Yard island), as well as `gravityVolumes`, `polarVolumes`, and `radialVolumes`. Resolves #1041 ## Minor features - Added additional `gameOver` settings for custom music and credits length: `audio`, `audioVolume`, `audioLooping` and `length` (thanks Smuffy!) - Added `raftDocks` to `Props` module. An easier way to spawn in docks for rafts. - Added `dockPath` to `RaftInfo`. This is a path to the dock the raft will start attached to. - Added `pristine` boolean to `RaftInfo`. Makes the raft use the Dreamworld model. - Added `speedLimiterVolumes` to `Volumes` module. This is the same thing the Stranger uses to slow you down. - All volumes now support shapes other than the default sphere via the new `shape` property, such as boxes, capsules, cylinders, hemispheres, and more. - All volumes now have `rotation` and `alignRadial` fields, allowing for non-spherical shapes to be rotated, and for `revealVolumes` to be pointed in other directions for the `maxAngle` check. ## Improvements - Standing vision torch now shows up in tronworld - Standing vision torch can use the dreamworld model with `slideCondition` property set to `pristine`. (resolves #1080) ## Bug fixes - Fixed a bug that didn't allow comet tails to be made on unmoved vanilla planets/moons. - Fixed a bug where atmospheres on the Dreamworld were visible at all times (affected Hazy Dreams) - Specific objects copied from the Stranger no longer vanish when the dam breaks - NH-made rafts no longer skip a node when riding on the Dam's raft carrier.
This commit is contained in:
commit
b3c4162931
7
.github/workflows/build.yaml
vendored
7
.github/workflows/build.yaml
vendored
@ -62,7 +62,8 @@ jobs:
|
||||
name: NewHorizons-Schemas-${{ inputs.build_type }}
|
||||
path: .\NewHorizons\Schemas
|
||||
|
||||
- name: Check Changed Schemas
|
||||
- name: Verify Changed Schemas
|
||||
uses: tj-actions/verify-changed-files@v20
|
||||
id: changed_files
|
||||
run: |
|
||||
echo "files_changed=$(git diff --exit-code NewHorizons/Schemas 2>&1>$null && echo false || echo true)" >> $Env:GITHUB_OUTPUT
|
||||
with:
|
||||
files: NewHorizons/Schemas/**
|
||||
@ -100,6 +100,15 @@ namespace NewHorizons.Builder.Atmosphere
|
||||
atmoGO.transform.position = planetGO.transform.TransformPoint(Vector3.zero);
|
||||
atmoGO.SetActive(true);
|
||||
|
||||
// CullGroups have already set up their renderers when this is done so we need to add ourself to it
|
||||
// TODO: There are probably other builders where this is relevant
|
||||
// This in particular was a bug affecting hazy dreams
|
||||
if (sector != null && sector.gameObject.GetComponent<CullGroup>() is CullGroup cullGroup)
|
||||
{
|
||||
cullGroup.RecursivelyAddRenderers(atmoGO.transform, true);
|
||||
cullGroup.SetVisible(cullGroup.IsVisible());
|
||||
}
|
||||
|
||||
return atmoGO;
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,14 +60,22 @@ namespace NewHorizons.Builder.Body
|
||||
}
|
||||
}
|
||||
|
||||
public static void Make(GameObject planetGO, Sector sector, CometTailModule cometTailModule, PlanetConfig config)
|
||||
public static void Make(GameObject planetGO, Sector sector, CometTailModule cometTailModule, PlanetConfig config, AstroObject ao)
|
||||
{
|
||||
if (config.Orbit.primaryBody == null)
|
||||
var primaryBody = ao.GetPrimaryBody();
|
||||
|
||||
if (!string.IsNullOrEmpty(config.Orbit.primaryBody)) primaryBody = AstroObjectLocator.GetAstroObject(config.Orbit.primaryBody);
|
||||
|
||||
if (primaryBody == null)
|
||||
{
|
||||
NHLogger.LogError($"Comet {planetGO.name} does not orbit anything. That makes no sense");
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(cometTailModule.primaryBody))
|
||||
cometTailModule.primaryBody = !string.IsNullOrEmpty(config.Orbit.primaryBody) ? config.Orbit.primaryBody
|
||||
: primaryBody.GetKey();
|
||||
|
||||
var rootObj = new GameObject("CometRoot");
|
||||
rootObj.SetActive(false);
|
||||
rootObj.transform.parent = sector?.transform ?? planetGO.transform;
|
||||
@ -79,13 +87,11 @@ namespace NewHorizons.Builder.Body
|
||||
|
||||
if (cometTailModule.rotationOverride != null) controller.SetRotationOverride(cometTailModule.rotationOverride);
|
||||
|
||||
if (string.IsNullOrEmpty(cometTailModule.primaryBody)) cometTailModule.primaryBody = config.Orbit.primaryBody;
|
||||
|
||||
Delay.FireOnNextUpdate(() =>
|
||||
{
|
||||
controller.SetPrimaryBody(
|
||||
AstroObjectLocator.GetAstroObject(cometTailModule.primaryBody).transform,
|
||||
AstroObjectLocator.GetAstroObject(config.Orbit.primaryBody).GetAttachedOWRigidbody()
|
||||
AstroObjectLocator.GetAstroObject(cometTailModule.primaryBody).transform,
|
||||
primaryBody.GetAttachedOWRigidbody()
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@ -203,7 +203,7 @@ namespace NewHorizons.Builder.Body
|
||||
|
||||
if (body.Config.CometTail != null)
|
||||
{
|
||||
CometTailBuilder.Make(proxy, null, body.Config.CometTail, body.Config);
|
||||
CometTailBuilder.Make(proxy, null, body.Config.CometTail, body.Config, planetGO.GetComponent<AstroObject>());
|
||||
}
|
||||
|
||||
if (body.Config.Props?.proxyDetails != null)
|
||||
|
||||
@ -150,6 +150,7 @@ namespace NewHorizons.Builder.Body
|
||||
streamingVolume.streamingGroup = streamingGroup;
|
||||
streamingVolume.transform.parent = blackHoleVolume.transform;
|
||||
streamingVolume.transform.localPosition = Vector3.zero;
|
||||
streamingVolume.gameObject.SetActive(true);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
@ -473,10 +473,14 @@ namespace NewHorizons.Builder.Props
|
||||
{
|
||||
// These flood toggles are to disable flooded docks on the Stranger
|
||||
// Presumably the user isn't making one of those
|
||||
foreach (var toggle in dock.GetComponents<FloodToggle>())
|
||||
foreach (var toggle in dock.GetComponentsInChildren<FloodToggle>())
|
||||
{
|
||||
Component.DestroyImmediate(toggle);
|
||||
}
|
||||
foreach (var floodSensor in dock.GetComponentsInChildren<RingRiverFloodSensor>())
|
||||
{
|
||||
Component.DestroyImmediate(floodSensor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
180
NewHorizons/Builder/Props/EchoesOfTheEye/RaftBuilder.cs
Normal file
180
NewHorizons/Builder/Props/EchoesOfTheEye/RaftBuilder.cs
Normal file
@ -0,0 +1,180 @@
|
||||
using NewHorizons.Components.Props;
|
||||
using NewHorizons.External.Modules.Props.EchoesOfTheEye;
|
||||
using NewHorizons.Handlers;
|
||||
using NewHorizons.Utility;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Builder.Props.EchoesOfTheEye
|
||||
{
|
||||
public static class RaftBuilder
|
||||
{
|
||||
private static GameObject _prefab;
|
||||
private static GameObject _cleanPrefab;
|
||||
|
||||
internal static void InitPrefab()
|
||||
{
|
||||
if (_prefab == null)
|
||||
{
|
||||
_prefab = Object.FindObjectOfType<RaftController>()?.gameObject?.InstantiateInactive()?.Rename("Raft_Body_Prefab")?.DontDestroyOnLoad();
|
||||
if (_prefab == null)
|
||||
{
|
||||
NHLogger.LogWarning($"Tried to make a raft but couldn't. Do you have the DLC installed?");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
_prefab.AddComponent<DestroyOnDLC>()._destroyOnDLCNotOwned = true;
|
||||
var raftController = _prefab.GetComponent<RaftController>();
|
||||
if (raftController._sector != null)
|
||||
{
|
||||
// Since awake already ran we have to unhook these events
|
||||
raftController._sector.OnOccupantEnterSector -= raftController.OnOccupantEnterSector;
|
||||
raftController._sector.OnOccupantExitSector -= raftController.OnOccupantExitSector;
|
||||
raftController._sector = null;
|
||||
}
|
||||
raftController._riverFluid = null;
|
||||
foreach (var lightSensor in _prefab.GetComponentsInChildren<SingleLightSensor>())
|
||||
{
|
||||
if (lightSensor._sector != null)
|
||||
{
|
||||
lightSensor._sector.OnSectorOccupantsUpdated -= lightSensor.OnSectorOccupantsUpdated;
|
||||
lightSensor._sector = null;
|
||||
}
|
||||
lightSensor._detectDreamLanterns = true;
|
||||
lightSensor._lanternFocusThreshold = 0.9f;
|
||||
lightSensor._illuminatingDreamLanternList = new List<DreamLanternController>();
|
||||
lightSensor._lightSourceMask |= LightSourceType.DREAM_LANTERN;
|
||||
}
|
||||
|
||||
// TODO: Change to one mesh
|
||||
var twRaftRoot = new GameObject("Effects_IP_SIM_Raft");
|
||||
twRaftRoot.transform.SetParent(_prefab.transform, false);
|
||||
var twRaft = SearchUtilities.Find("DreamWorld_Body/Sector_DreamWorld/Interactibles_Dreamworld/DreamRaft_Body/Effects_IP_SIM_Raft_1Way")
|
||||
.Instantiate(Vector3.zero, Quaternion.identity, twRaftRoot.transform).Rename("Effects_IP_SIM_Raft_1Way");
|
||||
twRaft.Instantiate(Vector3.zero, Quaternion.Euler(0, 180, 0), twRaftRoot.transform).Rename(twRaft.name);
|
||||
twRaft.Instantiate(Vector3.zero, Quaternion.Euler(0, 90, 0), twRaftRoot.transform).Rename(twRaft.name);
|
||||
twRaft.Instantiate(Vector3.zero, Quaternion.Euler(0, -90, 0), twRaftRoot.transform).Rename(twRaft.name);
|
||||
}
|
||||
}
|
||||
if (_cleanPrefab == null && _prefab != null)
|
||||
{
|
||||
_cleanPrefab = _prefab?.InstantiateInactive()?.Rename("Raft_Body_Prefab_Clean")?.DontDestroyOnLoad();
|
||||
if (_cleanPrefab == null)
|
||||
{
|
||||
NHLogger.LogWarning($"Tried to make a raft but couldn't. Do you have the DLC installed?");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
var raftController = _cleanPrefab.GetComponent<RaftController>();
|
||||
var rwRaft = _cleanPrefab.FindChild("Structure_IP_Raft");
|
||||
var rwRaftAnimator = rwRaft.GetComponent<Animator>();
|
||||
var rac = rwRaftAnimator.runtimeAnimatorController;
|
||||
Object.DestroyImmediate(rwRaft);
|
||||
|
||||
var dwRaft = SearchUtilities.Find("DreamWorld_Body/Sector_DreamWorld/Interactibles_Dreamworld/DreamRaft_Body/Structure_IP_Raft")
|
||||
.Instantiate(Vector3.zero, Quaternion.identity, _cleanPrefab.transform).Rename("Structure_IP_DreamRaft");
|
||||
dwRaft.transform.SetSiblingIndex(3);
|
||||
foreach (var child in dwRaft.GetAllChildren())
|
||||
{
|
||||
child.SetActive(true);
|
||||
}
|
||||
|
||||
var dwRaftAnimator = dwRaft.AddComponent<Animator>();
|
||||
dwRaftAnimator.runtimeAnimatorController = rac;
|
||||
raftController._railingAnimator = dwRaftAnimator;
|
||||
|
||||
var dwLightSensorForward = SearchUtilities.Find("DreamWorld_Body/Sector_DreamWorld/Interactibles_Dreamworld/DreamRaft_Body/LightSensor_Forward");
|
||||
var dwLightSensorOrigMaterial = dwLightSensorForward.GetComponent<LightSensorEffects>()._origMaterial;
|
||||
var dwLightSensor = dwLightSensorForward.FindChild("Structure_IP_Raft_Sensor");
|
||||
ChangeSensor(_cleanPrefab.FindChild("LightSensorRoot/LightSensor_Forward"), dwLightSensorOrigMaterial, dwLightSensor);
|
||||
ChangeSensor(_cleanPrefab.FindChild("LightSensorRoot/LightSensor_Right"), dwLightSensorOrigMaterial, dwLightSensor);
|
||||
ChangeSensor(_cleanPrefab.FindChild("LightSensorRoot/LightSensor_Rear"), dwLightSensorOrigMaterial, dwLightSensor, true);
|
||||
ChangeSensor(_cleanPrefab.FindChild("LightSensorRoot/LightSensor_Left"), dwLightSensorOrigMaterial, dwLightSensor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ChangeSensor(GameObject lightSensor, Material origMaterial, GameObject newLightSensor, bool reverse = false)
|
||||
{
|
||||
var singleLightSensor = lightSensor.GetComponent<SingleLightSensor>();
|
||||
var lightSensorEffects = lightSensor.GetComponent<LightSensorEffects>();
|
||||
lightSensorEffects._lightSensor = singleLightSensor;
|
||||
Object.DestroyImmediate(lightSensor.FindChild("Structure_IP_Raft_Sensor"));
|
||||
lightSensorEffects._origMaterial = origMaterial;
|
||||
var copiedLightSensor = newLightSensor
|
||||
.Instantiate(reverse ? new Vector3(0, -1.5f, -0.1303f) : new Vector3(0, -1.5f, -0.0297f),
|
||||
reverse ? Quaternion.identity : Quaternion.Euler(0, 180, 0),
|
||||
lightSensor.transform).Rename("Structure_IP_DreamRaft_Sensor");
|
||||
var bulb = copiedLightSensor.FindChild("Props_IP_Raft_Lamp_geoBulb");
|
||||
lightSensorEffects._renderer = bulb.GetComponent<MeshRenderer>();
|
||||
lightSensorEffects._lightRenderer = bulb.GetComponent<OWRenderer>();
|
||||
}
|
||||
|
||||
public static GameObject Make(GameObject planetGO, Sector sector, RaftInfo info, OWRigidbody planetBody)
|
||||
{
|
||||
InitPrefab();
|
||||
|
||||
if (_prefab == null || _cleanPrefab == null || sector == null) return null;
|
||||
|
||||
GameObject raftObject = GeneralPropBuilder.MakeFromPrefab(info.pristine ? _cleanPrefab : _prefab, "Raft_Body", planetGO, sector, info);
|
||||
|
||||
StreamingHandler.SetUpStreaming(raftObject, sector);
|
||||
|
||||
var raftController = raftObject.GetComponent<RaftController>();
|
||||
raftController._sector = sector;
|
||||
raftController._acceleration = info.acceleration;
|
||||
sector.OnOccupantEnterSector += raftController.OnOccupantEnterSector;
|
||||
sector.OnOccupantExitSector += raftController.OnOccupantExitSector;
|
||||
|
||||
// Detectors
|
||||
var fluidDetector = raftObject.transform.Find("Detector_Raft").GetComponent<RaftFluidDetector>();
|
||||
var waterVolume = planetGO.GetComponentInChildren<RadialFluidVolume>();
|
||||
fluidDetector._alignmentFluid = waterVolume;
|
||||
fluidDetector._buoyancy.checkAgainstWaves = true;
|
||||
// Rafts were unable to trigger docks because these were disabled for some reason
|
||||
fluidDetector.GetComponent<BoxShape>().enabled = true;
|
||||
fluidDetector.GetComponent<OWCollider>().enabled = true;
|
||||
|
||||
// Light sensors
|
||||
foreach (var lightSensor in raftObject.GetComponentsInChildren<SingleLightSensor>())
|
||||
{
|
||||
lightSensor._sector = sector;
|
||||
sector.OnSectorOccupantsUpdated += lightSensor.OnSectorOccupantsUpdated;
|
||||
}
|
||||
|
||||
var nhRaftController = raftObject.AddComponent<NHRaftController>();
|
||||
|
||||
var achievementObject = new GameObject("AchievementVolume");
|
||||
achievementObject.transform.SetParent(raftObject.transform, false);
|
||||
|
||||
var shape = achievementObject.AddComponent<SphereShape>();
|
||||
shape.radius = 3;
|
||||
shape.SetCollisionMode(Shape.CollisionMode.Volume);
|
||||
|
||||
achievementObject.AddComponent<OWTriggerVolume>()._shape = shape;
|
||||
achievementObject.AddComponent<OtherMods.AchievementsPlus.NH.RaftingAchievement>();
|
||||
|
||||
raftObject.SetActive(true);
|
||||
|
||||
if (planetGO != null && !string.IsNullOrEmpty(info.dockPath))
|
||||
{
|
||||
var dockTransform = planetGO.transform.Find(info.dockPath);
|
||||
if (dockTransform != null && dockTransform.TryGetComponent(out RaftDock raftDock))
|
||||
{
|
||||
raftController.SkipSuspendOnStart();
|
||||
raftDock._startRaft = raftController;
|
||||
raftDock._raft = raftController;
|
||||
}
|
||||
else
|
||||
{
|
||||
NHLogger.LogError($"Cannot find raft dock object at path: {planetGO.name}/{info.dockPath}");
|
||||
}
|
||||
}
|
||||
|
||||
return raftObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
55
NewHorizons/Builder/Props/EchoesOfTheEye/RaftDockBuilder.cs
Normal file
55
NewHorizons/Builder/Props/EchoesOfTheEye/RaftDockBuilder.cs
Normal file
@ -0,0 +1,55 @@
|
||||
using NewHorizons.Components.Props;
|
||||
using NewHorizons.External.Modules.Props;
|
||||
using NewHorizons.External.Modules.Props.EchoesOfTheEye;
|
||||
using NewHorizons.Handlers;
|
||||
using NewHorizons.Utility;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using OWML.Common;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Builder.Props.EchoesOfTheEye
|
||||
{
|
||||
public static class RaftDockBuilder
|
||||
{
|
||||
private static GameObject _prefab;
|
||||
|
||||
internal static void InitPrefab()
|
||||
{
|
||||
if (_prefab == null)
|
||||
{
|
||||
_prefab = SearchUtilities.Find("RingWorld_Body/Sector_RingInterior/Sector_Zone1/Structures_Zone1/RaftHouse_ArrivalPatio_Zone1/Interactables_RaftHouse_ArrivalPatio_Zone1/Prefab_IP_RaftDock").InstantiateInactive().Rename("Prefab_RaftDock").DontDestroyOnLoad();
|
||||
if (_prefab == null)
|
||||
{
|
||||
NHLogger.LogWarning($"Tried to make a raft dock but couldn't. Do you have the DLC installed?");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
_prefab.AddComponent<DestroyOnDLC>()._destroyOnDLCNotOwned = true;
|
||||
var raftDock = _prefab.GetComponent<RaftDock>();
|
||||
raftDock._startRaft = null;
|
||||
raftDock._floodSensor = null;
|
||||
foreach (var floodToggle in _prefab.GetComponents<FloodToggle>())
|
||||
{
|
||||
Component.DestroyImmediate(floodToggle);
|
||||
}
|
||||
Object.DestroyImmediate(_prefab.FindChild("FloodSensor"));
|
||||
Object.DestroyImmediate(_prefab.FindChild("FloodSensor_RaftHouseArrivalPatio_NoDelay"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static GameObject Make(GameObject planetGO, Sector sector, RaftDockInfo info, IModBehaviour mod)
|
||||
{
|
||||
InitPrefab();
|
||||
|
||||
if (_prefab == null || sector == null) return null;
|
||||
|
||||
var dockObject = DetailBuilder.Make(planetGO, sector, mod, _prefab, new DetailInfo(info));
|
||||
|
||||
//var raftDock = dockObject.GetComponent<RaftDock>();
|
||||
|
||||
return dockObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -43,6 +43,7 @@ namespace NewHorizons.Builder.Props
|
||||
private static GameObject _autoPrefab;
|
||||
private static GameObject _visionTorchDetectorPrefab;
|
||||
private static GameObject _standingVisionTorchPrefab;
|
||||
private static GameObject _standingVisionTorchCleanPrefab;
|
||||
private static readonly int EmissionMap = Shader.PropertyToID("_EmissionMap");
|
||||
|
||||
private static bool _isInit;
|
||||
@ -90,13 +91,28 @@ namespace NewHorizons.Builder.Props
|
||||
_visionTorchDetectorPrefab.AddComponent<DestroyOnDLC>()._destroyOnDLCNotOwned = true;
|
||||
}
|
||||
|
||||
if (_standingVisionTorchCleanPrefab == null)
|
||||
{
|
||||
_standingVisionTorchCleanPrefab = SearchUtilities.Find("DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_4/Interactibles_DreamZone_4_Upper/Prefab_IP_VisionTorchProjector")?.gameObject?.InstantiateInactive()?.Rename("Prefab_DW_VisionTorchProjector")?.DontDestroyOnLoad();
|
||||
if (_standingVisionTorchCleanPrefab == null)
|
||||
NHLogger.LogWarning($"Tried to make standing vision torch prefab but couldn't. Do you have the DLC installed?");
|
||||
else
|
||||
{
|
||||
_standingVisionTorchCleanPrefab.AddComponent<DestroyOnDLC>()._destroyOnDLCNotOwned = true;
|
||||
GameObject.DestroyImmediate(_standingVisionTorchCleanPrefab.FindChild("Prefab_IP_Reel_PrisonPeephole_Vision"));
|
||||
}
|
||||
}
|
||||
|
||||
if (_standingVisionTorchPrefab == null)
|
||||
{
|
||||
_standingVisionTorchPrefab = SearchUtilities.Find("RingWorld_Body/Sector_RingWorld/Sector_SecretEntrance/Interactibles_SecretEntrance/Experiment_1/VisionTorchApparatus/VisionTorchRoot/Prefab_IP_VisionTorchProjector")?.gameObject?.InstantiateInactive()?.Rename("Prefab_IP_VisionTorchProjector")?.DontDestroyOnLoad();
|
||||
if (_standingVisionTorchPrefab == null)
|
||||
NHLogger.LogWarning($"Tried to make standing vision torch prefab but couldn't. Do you have the DLC installed?");
|
||||
else
|
||||
{
|
||||
_standingVisionTorchPrefab.AddComponent<DestroyOnDLC>()._destroyOnDLCNotOwned = true;
|
||||
GameObject.Instantiate(_standingVisionTorchCleanPrefab.FindChild("Effects_IP_SIM_VisionTorch"), _standingVisionTorchPrefab.transform, false).Rename("Effects_IP_SIM_VisionTorch");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -446,10 +462,11 @@ namespace NewHorizons.Builder.Props
|
||||
{
|
||||
InitPrefabs();
|
||||
|
||||
if (_standingVisionTorchPrefab == null) return null;
|
||||
if (_standingVisionTorchPrefab == null || _standingVisionTorchCleanPrefab == null) return null;
|
||||
|
||||
// Spawn the torch itself
|
||||
var standingTorch = DetailBuilder.Make(planetGO, sector, mod, _standingVisionTorchPrefab, new DetailInfo(info));
|
||||
var prefab = info.reelCondition == ProjectionInfo.SlideReelCondition.Pristine ? _standingVisionTorchCleanPrefab : _standingVisionTorchPrefab;
|
||||
var standingTorch = DetailBuilder.Make(planetGO, sector, mod, prefab, new DetailInfo(info));
|
||||
|
||||
if (standingTorch == null)
|
||||
{
|
||||
|
||||
@ -114,6 +114,7 @@ namespace NewHorizons.Builder.Props
|
||||
MakeGeneralProps(go, config.Props.grappleTotems, (totem) => GrappleTotemBuilder.Make(go, sector, totem, mod));
|
||||
MakeGeneralProps(go, config.Props.dreamCampfires, (campfire) => DreamCampfireBuilder.Make(go, sector, campfire, mod), (campfire) => campfire.id);
|
||||
MakeGeneralProps(go, config.Props.dreamArrivalPoints, (point) => DreamArrivalPointBuilder.Make(go, sector, point, mod), (point) => point.id);
|
||||
MakeGeneralProps(go, config.Props.raftDocks, (dock) => RaftDockBuilder.Make(go, sector, dock, mod));
|
||||
MakeGeneralProps(go, config.Props.rafts, (raft) => RaftBuilder.Make(go, sector, raft, planetBody));
|
||||
}
|
||||
MakeGeneralProps(go, config.Props.tornados, (tornado) => TornadoBuilder.Make(go, sector, tornado, config.Atmosphere?.clouds != null));
|
||||
|
||||
@ -1,97 +0,0 @@
|
||||
using NewHorizons.Components.Props;
|
||||
using NewHorizons.External.Modules.Props.EchoesOfTheEye;
|
||||
using NewHorizons.Handlers;
|
||||
using NewHorizons.Utility;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Builder.Props
|
||||
{
|
||||
public static class RaftBuilder
|
||||
{
|
||||
private static GameObject _prefab;
|
||||
|
||||
internal static void InitPrefab()
|
||||
{
|
||||
if (_prefab == null)
|
||||
{
|
||||
_prefab = Object.FindObjectOfType<RaftController>()?.gameObject?.InstantiateInactive()?.Rename("Raft_Body_Prefab")?.DontDestroyOnLoad();
|
||||
if (_prefab == null)
|
||||
{
|
||||
NHLogger.LogWarning($"Tried to make a raft but couldn't. Do you have the DLC installed?");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
_prefab.AddComponent<DestroyOnDLC>()._destroyOnDLCNotOwned = true;
|
||||
var raftController = _prefab.GetComponent<RaftController>();
|
||||
if (raftController._sector != null)
|
||||
{
|
||||
// Since awake already ran we have to unhook these events
|
||||
raftController._sector.OnOccupantEnterSector -= raftController.OnOccupantEnterSector;
|
||||
raftController._sector.OnOccupantExitSector -= raftController.OnOccupantExitSector;
|
||||
raftController._sector = null;
|
||||
}
|
||||
raftController._riverFluid = null;
|
||||
foreach (var lightSensor in _prefab.GetComponentsInChildren<SingleLightSensor>())
|
||||
{
|
||||
if (lightSensor._sector != null)
|
||||
{
|
||||
lightSensor._sector.OnSectorOccupantsUpdated -= lightSensor.OnSectorOccupantsUpdated;
|
||||
lightSensor._sector = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static GameObject Make(GameObject planetGO, Sector sector, RaftInfo info, OWRigidbody planetBody)
|
||||
{
|
||||
InitPrefab();
|
||||
|
||||
if (_prefab == null || sector == null) return null;
|
||||
|
||||
GameObject raftObject = GeneralPropBuilder.MakeFromPrefab(_prefab, "Raft_Body", planetGO, sector, info);
|
||||
|
||||
StreamingHandler.SetUpStreaming(raftObject, sector);
|
||||
|
||||
var raftController = raftObject.GetComponent<RaftController>();
|
||||
raftController._sector = sector;
|
||||
raftController._acceleration = info.acceleration;
|
||||
sector.OnOccupantEnterSector += raftController.OnOccupantEnterSector;
|
||||
sector.OnOccupantExitSector += raftController.OnOccupantExitSector;
|
||||
|
||||
// Detectors
|
||||
var fluidDetector = raftObject.transform.Find("Detector_Raft").GetComponent<RaftFluidDetector>();
|
||||
var waterVolume = planetGO.GetComponentInChildren<RadialFluidVolume>();
|
||||
fluidDetector._alignmentFluid = waterVolume;
|
||||
fluidDetector._buoyancy.checkAgainstWaves = true;
|
||||
// Rafts were unable to trigger docks because these were disabled for some reason
|
||||
fluidDetector.GetComponent<BoxShape>().enabled = true;
|
||||
fluidDetector.GetComponent<OWCollider>().enabled = true;
|
||||
|
||||
// Light sensors
|
||||
foreach (var lightSensor in raftObject.GetComponentsInChildren<SingleLightSensor>())
|
||||
{
|
||||
lightSensor._sector = sector;
|
||||
sector.OnSectorOccupantsUpdated += lightSensor.OnSectorOccupantsUpdated;
|
||||
}
|
||||
|
||||
var nhRaftController = raftObject.AddComponent<NHRaftController>();
|
||||
|
||||
var achievementObject = new GameObject("AchievementVolume");
|
||||
achievementObject.transform.SetParent(raftObject.transform, false);
|
||||
|
||||
var shape = achievementObject.AddComponent<SphereShape>();
|
||||
shape.radius = 3;
|
||||
shape.SetCollisionMode(Shape.CollisionMode.Volume);
|
||||
|
||||
achievementObject.AddComponent<OWTriggerVolume>()._shape = shape;
|
||||
achievementObject.AddComponent<OtherMods.AchievementsPlus.NH.RaftingAchievement>();
|
||||
|
||||
raftObject.SetActive(true);
|
||||
|
||||
return raftObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
170
NewHorizons/Builder/Props/ShapeBuilder.cs
Normal file
170
NewHorizons/Builder/Props/ShapeBuilder.cs
Normal file
@ -0,0 +1,170 @@
|
||||
using NewHorizons.Components;
|
||||
using NewHorizons.External.Modules.Props;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Builder.Props
|
||||
{
|
||||
public static class ShapeBuilder
|
||||
{
|
||||
public static OWTriggerVolume AddTriggerVolume(GameObject go, ShapeInfo info, float defaultRadius)
|
||||
{
|
||||
var owTriggerVolume = go.AddComponent<OWTriggerVolume>();
|
||||
|
||||
if (info != null)
|
||||
{
|
||||
var shapeOrCol = AddShapeOrCollider(go, info);
|
||||
if (shapeOrCol is Shape shape)
|
||||
owTriggerVolume._shape = shape;
|
||||
else if (shapeOrCol is Collider col)
|
||||
owTriggerVolume._owCollider = col.GetComponent<OWCollider>();
|
||||
}
|
||||
else
|
||||
{
|
||||
var col = go.AddComponent<SphereCollider>();
|
||||
col.radius = defaultRadius;
|
||||
col.isTrigger = true;
|
||||
var owCollider = go.GetAddComponent<OWCollider>();
|
||||
|
||||
owTriggerVolume._owCollider = owCollider;
|
||||
}
|
||||
|
||||
return owTriggerVolume;
|
||||
}
|
||||
|
||||
public static Component AddShapeOrCollider(GameObject go, ShapeInfo info)
|
||||
{
|
||||
if (info.useShape.HasValue)
|
||||
{
|
||||
// Explicitly add either a shape or collider if specified
|
||||
if (info.useShape.Value)
|
||||
return AddShape(go, info);
|
||||
else
|
||||
return AddCollider(go, info);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Prefer colliders over shapes if no preference is specified
|
||||
if (info.type is ShapeType.Sphere or ShapeType.Box or ShapeType.Capsule)
|
||||
return AddCollider(go, info);
|
||||
else
|
||||
return AddShape(go, info);
|
||||
}
|
||||
}
|
||||
|
||||
public static Shape AddShape(GameObject go, ShapeInfo info)
|
||||
{
|
||||
if (info.hasCollision)
|
||||
{
|
||||
throw new NotSupportedException($"Shapes do not support collision; set {nameof(info.hasCollision)} to false or use a supported collider type (sphere, box, or capsule).");
|
||||
}
|
||||
if (info.useShape.HasValue && !info.useShape.Value)
|
||||
{
|
||||
throw new NotSupportedException($"{info.useShape} was explicitly set to false but a shape is required here.");
|
||||
}
|
||||
switch (info.type)
|
||||
{
|
||||
case ShapeType.Sphere:
|
||||
var sphereShape = go.AddComponent<SphereShape>();
|
||||
sphereShape._radius = info.radius;
|
||||
sphereShape._center = info.offset ?? Vector3.zero;
|
||||
return sphereShape;
|
||||
case ShapeType.Box:
|
||||
var boxShape = go.AddComponent<BoxShape>();
|
||||
boxShape._size = info.size ?? Vector3.one;
|
||||
boxShape._center = info.offset ?? Vector3.zero;
|
||||
return boxShape;
|
||||
case ShapeType.Capsule:
|
||||
var capsuleShape = go.AddComponent<CapsuleShape>();
|
||||
capsuleShape._radius = info.radius;
|
||||
capsuleShape._direction = (int)info.direction;
|
||||
capsuleShape._height = info.height;
|
||||
capsuleShape._center = info.offset ?? Vector3.zero;
|
||||
return capsuleShape;
|
||||
case ShapeType.Cylinder:
|
||||
var cylinderShape = go.AddComponent<CylinderShape>();
|
||||
cylinderShape._radius = info.radius;
|
||||
cylinderShape._height = info.height;
|
||||
cylinderShape._center = info.offset ?? Vector3.zero;
|
||||
cylinderShape._pointChecksOnly = true;
|
||||
return cylinderShape;
|
||||
case ShapeType.Cone:
|
||||
var coneShape = go.AddComponent<ConeShape>();
|
||||
coneShape._topRadius = info.innerRadius;
|
||||
coneShape._bottomRadius = info.outerRadius;
|
||||
coneShape._direction = (int)info.direction;
|
||||
coneShape._height = info.height;
|
||||
coneShape._center = info.offset ?? Vector3.zero;
|
||||
coneShape._pointChecksOnly = true;
|
||||
return coneShape;
|
||||
case ShapeType.Hemisphere:
|
||||
var hemisphereShape = go.AddComponent<HemisphereShape>();
|
||||
hemisphereShape._radius = info.radius;
|
||||
hemisphereShape._direction = (int)info.direction;
|
||||
hemisphereShape._cap = info.cap;
|
||||
hemisphereShape._center = info.offset ?? Vector3.zero;
|
||||
hemisphereShape._pointChecksOnly = true;
|
||||
return hemisphereShape;
|
||||
case ShapeType.Hemicapsule:
|
||||
var hemicapsuleShape = go.AddComponent<HemicapsuleShape>();
|
||||
hemicapsuleShape._radius = info.radius;
|
||||
hemicapsuleShape._direction = (int)info.direction;
|
||||
hemicapsuleShape._height = info.height;
|
||||
hemicapsuleShape._cap = info.cap;
|
||||
hemicapsuleShape._center = info.offset ?? Vector3.zero;
|
||||
hemicapsuleShape._pointChecksOnly = true;
|
||||
return hemicapsuleShape;
|
||||
case ShapeType.Ring:
|
||||
var ringShape = go.AddComponent<RingShape>();
|
||||
ringShape.innerRadius = info.innerRadius;
|
||||
ringShape.outerRadius = info.outerRadius;
|
||||
ringShape.height = info.height;
|
||||
ringShape.center = info.offset ?? Vector3.zero;
|
||||
ringShape._pointChecksOnly = true;
|
||||
return ringShape;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(info.type), info.type, $"Unsupported shape type");
|
||||
}
|
||||
}
|
||||
|
||||
public static Collider AddCollider(GameObject go, ShapeInfo info)
|
||||
{
|
||||
if (info.useShape.HasValue && info.useShape.Value)
|
||||
{
|
||||
throw new NotSupportedException($"{info.useShape} was explicitly set to true but a non-shape collider is required here.");
|
||||
}
|
||||
switch (info.type)
|
||||
{
|
||||
case ShapeType.Sphere:
|
||||
var sphereCollider = go.AddComponent<SphereCollider>();
|
||||
sphereCollider.radius = info.radius;
|
||||
sphereCollider.center = info.offset ?? Vector3.zero;
|
||||
sphereCollider.isTrigger = !info.hasCollision;
|
||||
go.GetAddComponent<OWCollider>();
|
||||
return sphereCollider;
|
||||
case ShapeType.Box:
|
||||
var boxCollider = go.AddComponent<BoxCollider>();
|
||||
boxCollider.size = info.size ?? Vector3.one;
|
||||
boxCollider.center = info.offset ?? Vector3.zero;
|
||||
boxCollider.isTrigger = !info.hasCollision;
|
||||
go.GetAddComponent<OWCollider>();
|
||||
return boxCollider;
|
||||
case ShapeType.Capsule:
|
||||
var capsuleCollider = go.AddComponent<CapsuleCollider>();
|
||||
capsuleCollider.radius = info.radius;
|
||||
capsuleCollider.direction = (int)info.direction;
|
||||
capsuleCollider.height = info.height;
|
||||
capsuleCollider.center = info.offset ?? Vector3.zero;
|
||||
capsuleCollider.isTrigger = !info.hasCollision;
|
||||
go.GetAddComponent<OWCollider>();
|
||||
return capsuleCollider;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(info.type), info.type, $"Unsupported collider type");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -471,7 +471,7 @@ namespace NewHorizons.Builder.Props.TranslatorText
|
||||
|
||||
if (info.arcInfo != null && info.arcInfo.Count() != dict.Values.Count())
|
||||
{
|
||||
NHLogger.LogError($"Can't make NomaiWallText, arcInfo length [{info.arcInfo.Count()}] doesn't equal number of TextBlocks [{dict.Values.Count()}] in the xml");
|
||||
NHLogger.LogError($"Can't make NomaiWallText [{info.xmlFile}], arcInfo length [{info.arcInfo.Count()}] doesn't equal number of TextBlocks [{dict.Values.Count()}] in the xml");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -25,7 +25,8 @@ namespace NewHorizons.Builder.Volumes
|
||||
owAudioSource.SetTrack(info.track.ConvertToOW());
|
||||
AudioUtilities.SetAudioClip(owAudioSource, info.audio, mod);
|
||||
|
||||
var audioVolume = go.AddComponent<AudioVolume>();
|
||||
var audioVolume = PriorityVolumeBuilder.MakeExisting<AudioVolume>(go, planetGO, sector, info);
|
||||
|
||||
audioVolume._layer = info.layer;
|
||||
audioVolume.SetPriority(info.priority);
|
||||
audioVolume._fadeSeconds = info.fadeSeconds;
|
||||
@ -33,11 +34,7 @@ namespace NewHorizons.Builder.Volumes
|
||||
audioVolume._randomizePlayhead = info.randomizePlayhead;
|
||||
audioVolume._pauseOnFadeOut = info.pauseOnFadeOut;
|
||||
|
||||
var shape = go.AddComponent<SphereShape>();
|
||||
shape.radius = info.radius;
|
||||
|
||||
var owTriggerVolume = go.AddComponent<OWTriggerVolume>();
|
||||
owTriggerVolume._shape = shape;
|
||||
var owTriggerVolume = go.GetComponent<OWTriggerVolume>();
|
||||
audioVolume._triggerVolumeOverride = owTriggerVolume;
|
||||
|
||||
go.SetActive(true);
|
||||
|
||||
@ -13,6 +13,8 @@ namespace NewHorizons.Builder.Volumes
|
||||
volume.TargetSolarSystem = info.targetStarSystem;
|
||||
volume.TargetSpawnID = info.spawnPointID;
|
||||
|
||||
volume.gameObject.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using NewHorizons.Components.Volumes;
|
||||
using NewHorizons.External.Modules.Volumes.VolumeInfos;
|
||||
using OWML.Common;
|
||||
using OWML.Utils;
|
||||
using UnityEngine;
|
||||
|
||||
@ -7,12 +8,15 @@ namespace NewHorizons.Builder.Volumes
|
||||
{
|
||||
internal static class CreditsVolumeBuilder
|
||||
{
|
||||
public static LoadCreditsVolume Make(GameObject planetGO, Sector sector, LoadCreditsVolumeInfo info)
|
||||
public static LoadCreditsVolume Make(GameObject planetGO, Sector sector, LoadCreditsVolumeInfo info, IModBehaviour mod)
|
||||
{
|
||||
var volume = VolumeBuilder.Make<LoadCreditsVolume>(planetGO, sector, info);
|
||||
|
||||
volume.gameOver = info.gameOver;
|
||||
volume.deathType = info.deathType == null ? null : EnumUtils.Parse(info.deathType.ToString(), DeathType.Default);
|
||||
volume.mod = mod;
|
||||
|
||||
volume.gameObject.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
|
||||
@ -15,7 +15,8 @@ namespace NewHorizons.Builder.Volumes
|
||||
var go = GeneralPropBuilder.MakeNew("DayNightAudioVolume", planetGO, sector, info);
|
||||
go.layer = Layer.AdvancedEffectVolume;
|
||||
|
||||
var audioVolume = go.AddComponent<NHDayNightAudioVolume>();
|
||||
var audioVolume = PriorityVolumeBuilder.MakeExisting<NHDayNightAudioVolume>(go, planetGO, sector, info);
|
||||
|
||||
audioVolume.sunName = info.sun;
|
||||
audioVolume.dayWindow = info.dayWindow;
|
||||
audioVolume.dayAudio = info.dayAudio;
|
||||
@ -24,13 +25,7 @@ namespace NewHorizons.Builder.Volumes
|
||||
audioVolume.volume = info.volume;
|
||||
audioVolume.SetTrack(info.track.ConvertToOW());
|
||||
|
||||
var shape = go.AddComponent<SphereShape>();
|
||||
shape.radius = info.radius;
|
||||
|
||||
var owTriggerVolume = go.AddComponent<OWTriggerVolume>();
|
||||
owTriggerVolume._shape = shape;
|
||||
|
||||
go.SetActive(true);
|
||||
audioVolume.gameObject.SetActive(true);
|
||||
|
||||
return audioVolume;
|
||||
}
|
||||
|
||||
@ -12,6 +12,8 @@ namespace NewHorizons.Builder.Volumes
|
||||
|
||||
volume._deathType = EnumUtils.Parse<DeathType>(info.deathType.ToString(), DeathType.Default);
|
||||
|
||||
volume.gameObject.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,6 +35,8 @@ namespace NewHorizons.Builder.Volumes
|
||||
volume._allowShipAutoroll = info.allowShipAutoroll;
|
||||
volume._disableOnStart = info.disableOnStart;
|
||||
|
||||
volume.gameObject.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
|
||||
111
NewHorizons/Builder/Volumes/ForceVolumeBuilder.cs
Normal file
111
NewHorizons/Builder/Volumes/ForceVolumeBuilder.cs
Normal file
@ -0,0 +1,111 @@
|
||||
using NewHorizons.Builder.Props;
|
||||
using NewHorizons.External;
|
||||
using NewHorizons.External.Modules;
|
||||
using NewHorizons.External.Modules.Volumes.VolumeInfos;
|
||||
using NewHorizons.Utility.OuterWilds;
|
||||
using OWML.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Builder.Volumes
|
||||
{
|
||||
public static class ForceVolumeBuilder
|
||||
{
|
||||
public static CylindricalForceVolume Make(GameObject planetGO, Sector sector, CylindricalForceVolumeInfo info)
|
||||
{
|
||||
var forceVolume = Make<CylindricalForceVolume>(planetGO, sector, info);
|
||||
|
||||
forceVolume._acceleration = info.force;
|
||||
forceVolume._localAxis = info.normal ?? Vector3.up;
|
||||
forceVolume._playGravityCrystalAudio = info.playGravityCrystalAudio;
|
||||
|
||||
forceVolume.gameObject.SetActive(true);
|
||||
|
||||
return forceVolume;
|
||||
}
|
||||
|
||||
public static DirectionalForceVolume Make(GameObject planetGO, Sector sector, DirectionalForceVolumeInfo info)
|
||||
{
|
||||
var forceVolume = Make<DirectionalForceVolume>(planetGO, sector, info);
|
||||
|
||||
forceVolume._fieldDirection = info.normal ?? Vector3.up;
|
||||
forceVolume._fieldMagnitude = info.force;
|
||||
forceVolume._affectsAlignment = info.affectsAlignment;
|
||||
forceVolume._offsetCentripetalForce = info.offsetCentripetalForce;
|
||||
forceVolume._playGravityCrystalAudio = info.playGravityCrystalAudio;
|
||||
|
||||
forceVolume.gameObject.SetActive(true);
|
||||
|
||||
return forceVolume;
|
||||
}
|
||||
|
||||
public static GravityVolume Make(GameObject planetGO, Sector sector, GravityVolumeInfo info)
|
||||
{
|
||||
var forceVolume = Make<GravityVolume>(planetGO, sector, info);
|
||||
|
||||
forceVolume._isPlanetGravityVolume = false;
|
||||
forceVolume._setMass = false;
|
||||
forceVolume._surfaceAcceleration = info.force;
|
||||
forceVolume._upperSurfaceRadius = info.upperRadius;
|
||||
forceVolume._lowerSurfaceRadius = info.lowerRadius;
|
||||
forceVolume._cutoffAcceleration = info.minForce;
|
||||
forceVolume._cutoffRadius = info.minRadius;
|
||||
forceVolume._alignmentRadius = info.alignmentRadius ?? info.upperRadius * 1.5f;
|
||||
forceVolume._falloffType = info.fallOff switch
|
||||
{
|
||||
GravityFallOff.Linear => GravityVolume.FalloffType.linear,
|
||||
GravityFallOff.InverseSquared => GravityVolume.FalloffType.inverseSquared,
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
forceVolume.gameObject.SetActive(true);
|
||||
|
||||
return forceVolume;
|
||||
}
|
||||
|
||||
public static PolarForceVolume Make(GameObject planetGO, Sector sector, PolarForceVolumeInfo info)
|
||||
{
|
||||
var forceVolume = Make<PolarForceVolume>(planetGO, sector, info);
|
||||
|
||||
forceVolume._acceleration = info.force;
|
||||
forceVolume._localAxis = info.normal ?? Vector3.up;
|
||||
forceVolume._fieldMode = info.tangential ? PolarForceVolume.ForceMode.Tangential : PolarForceVolume.ForceMode.Polar;
|
||||
|
||||
forceVolume.gameObject.SetActive(true);
|
||||
|
||||
return forceVolume;
|
||||
}
|
||||
|
||||
public static RadialForceVolume Make(GameObject planetGO, Sector sector, RadialForceVolumeInfo info)
|
||||
{
|
||||
var forceVolume = Make<RadialForceVolume>(planetGO, sector, info);
|
||||
|
||||
forceVolume._acceleration = info.force;
|
||||
forceVolume._falloff = info.fallOff switch
|
||||
{
|
||||
RadialForceVolumeInfo.FallOff.Constant => RadialForceVolume.Falloff.Constant,
|
||||
RadialForceVolumeInfo.FallOff.Linear => RadialForceVolume.Falloff.Linear,
|
||||
RadialForceVolumeInfo.FallOff.InverseSquared => RadialForceVolume.Falloff.InvSqr,
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
forceVolume.gameObject.SetActive(true);
|
||||
|
||||
return forceVolume;
|
||||
}
|
||||
|
||||
public static TVolume Make<TVolume>(GameObject planetGO, Sector sector, ForceVolumeInfo info) where TVolume : ForceVolume
|
||||
{
|
||||
var forceVolume = PriorityVolumeBuilder.Make<TVolume>(planetGO, sector, info);
|
||||
|
||||
forceVolume._alignmentPriority = info.alignmentPriority;
|
||||
forceVolume._inheritable = info.inheritable;
|
||||
|
||||
return forceVolume;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,6 +3,7 @@ using NewHorizons.External.Modules.Volumes.VolumeInfos;
|
||||
using NewHorizons.Utility.OuterWilds;
|
||||
using OWML.Common;
|
||||
using OWML.Utils;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
@ -13,35 +14,28 @@ namespace NewHorizons.Builder.Volumes
|
||||
public static HazardVolume Make(GameObject planetGO, Sector sector, OWRigidbody owrb, HazardVolumeInfo info, IModBehaviour mod)
|
||||
{
|
||||
var go = GeneralPropBuilder.MakeNew("HazardVolume", planetGO, sector, info);
|
||||
go.layer = Layer.BasicEffectVolume;
|
||||
|
||||
var shape = go.AddComponent<SphereShape>();
|
||||
shape.radius = info.radius;
|
||||
|
||||
var owTriggerVolume = go.AddComponent<OWTriggerVolume>();
|
||||
owTriggerVolume._shape = shape;
|
||||
|
||||
var volume = AddHazardVolume(go, sector, owrb, info.type, info.firstContactDamageType, info.firstContactDamage, info.damagePerSecond);
|
||||
|
||||
var volume = MakeExisting(go, planetGO, sector, owrb, info);
|
||||
|
||||
go.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
|
||||
public static HazardVolume AddHazardVolume(GameObject go, Sector sector, OWRigidbody owrb, HazardVolumeInfo.HazardType? type, HazardVolumeInfo.InstantDamageType? firstContactDamageType, float firstContactDamage, float damagePerSecond)
|
||||
public static HazardVolume MakeExisting(GameObject go, GameObject planetGO, Sector sector, OWRigidbody owrb, HazardVolumeInfo info)
|
||||
{
|
||||
HazardVolume hazardVolume = null;
|
||||
if (type == HazardVolumeInfo.HazardType.RIVERHEAT)
|
||||
if (info.type == HazardVolumeInfo.HazardType.RIVERHEAT)
|
||||
{
|
||||
hazardVolume = go.AddComponent<RiverHeatHazardVolume>();
|
||||
hazardVolume = VolumeBuilder.MakeExisting<RiverHeatHazardVolume>(go, planetGO, sector, info);
|
||||
}
|
||||
else if (type == HazardVolumeInfo.HazardType.HEAT)
|
||||
else if (info.type == HazardVolumeInfo.HazardType.HEAT)
|
||||
{
|
||||
hazardVolume = go.AddComponent<HeatHazardVolume>();
|
||||
hazardVolume = VolumeBuilder.MakeExisting<HeatHazardVolume>(go, planetGO, sector, info);
|
||||
}
|
||||
else if (type == HazardVolumeInfo.HazardType.DARKMATTER)
|
||||
else if (info.type == HazardVolumeInfo.HazardType.DARKMATTER)
|
||||
{
|
||||
hazardVolume = go.AddComponent<DarkMatterVolume>();
|
||||
hazardVolume = VolumeBuilder.MakeExisting<DarkMatterVolume>(go, planetGO, sector, info);
|
||||
var visorFrostEffectVolume = go.AddComponent<VisorFrostEffectVolume>();
|
||||
visorFrostEffectVolume._frostRate = 0.5f;
|
||||
visorFrostEffectVolume._maxFrost = 0.91f;
|
||||
@ -67,28 +61,38 @@ namespace NewHorizons.Builder.Volumes
|
||||
submerge._fluidDetector = detector;
|
||||
}
|
||||
}
|
||||
else if (type == HazardVolumeInfo.HazardType.ELECTRICITY)
|
||||
else if (info.type == HazardVolumeInfo.HazardType.ELECTRICITY)
|
||||
{
|
||||
var electricityVolume = go.AddComponent<ElectricityVolume>();
|
||||
var electricityVolume = VolumeBuilder.MakeExisting<ElectricityVolume>(go, planetGO, sector, info);
|
||||
electricityVolume._shockAudioPool = new OWAudioSource[0];
|
||||
hazardVolume = electricityVolume;
|
||||
}
|
||||
else
|
||||
{
|
||||
var simpleHazardVolume = go.AddComponent<SimpleHazardVolume>();
|
||||
simpleHazardVolume._type = EnumUtils.Parse(type.ToString(), HazardVolume.HazardType.GENERAL);
|
||||
simpleHazardVolume._type = EnumUtils.Parse(info.type.ToString(), HazardVolume.HazardType.GENERAL);
|
||||
hazardVolume = simpleHazardVolume;
|
||||
}
|
||||
hazardVolume._attachedBody = owrb;
|
||||
hazardVolume._damagePerSecond = type == null ? 0f : damagePerSecond;
|
||||
hazardVolume._damagePerSecond = info.type == HazardVolumeInfo.HazardType.NONE ? 0f : info.damagePerSecond;
|
||||
|
||||
if (firstContactDamageType != null)
|
||||
{
|
||||
hazardVolume._firstContactDamageType = EnumUtils.Parse(firstContactDamageType.ToString(), InstantDamageType.Impact);
|
||||
hazardVolume._firstContactDamage = firstContactDamage;
|
||||
}
|
||||
hazardVolume._firstContactDamageType = EnumUtils.Parse(info.firstContactDamageType.ToString(), InstantDamageType.Impact);
|
||||
hazardVolume._firstContactDamage = info.firstContactDamage;
|
||||
|
||||
return hazardVolume;
|
||||
}
|
||||
|
||||
public static HazardVolume AddHazardVolume(GameObject go, Sector sector, OWRigidbody owrb, HazardVolumeInfo.HazardType? type, HazardVolumeInfo.InstantDamageType? firstContactDamageType, float firstContactDamage, float damagePerSecond)
|
||||
{
|
||||
var planetGO = sector.transform.root.gameObject;
|
||||
return MakeExisting(go, planetGO, sector, owrb, new HazardVolumeInfo
|
||||
{
|
||||
radius = 0f, // Volume builder should skip creating an extra trigger volume and collider if radius is 0
|
||||
type = type ?? HazardVolumeInfo.HazardType.NONE,
|
||||
firstContactDamageType = firstContactDamageType ?? HazardVolumeInfo.InstantDamageType.Impact,
|
||||
firstContactDamage = firstContactDamage,
|
||||
damagePerSecond = damagePerSecond
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,21 +11,16 @@ namespace NewHorizons.Builder.Volumes
|
||||
{
|
||||
public static NHNotificationVolume Make(GameObject planetGO, Sector sector, NotificationVolumeInfo info, IModBehaviour mod)
|
||||
{
|
||||
var go = GeneralPropBuilder.MakeNew("NotificationVolume", planetGO, sector, info);
|
||||
go.layer = Layer.BasicEffectVolume;
|
||||
var notificationVolume = VolumeBuilder.Make<NHNotificationVolume>(planetGO, sector, info);
|
||||
|
||||
var shape = go.AddComponent<SphereShape>();
|
||||
shape.radius = info.radius;
|
||||
// Preserving name for backwards compatibility
|
||||
notificationVolume.gameObject.name = string.IsNullOrEmpty(info.rename) ? "NotificationVolume" : info.rename;
|
||||
|
||||
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);
|
||||
notificationVolume.gameObject.SetActive(true);
|
||||
|
||||
return notificationVolume;
|
||||
}
|
||||
|
||||
@ -12,6 +12,8 @@ namespace NewHorizons.Builder.Volumes
|
||||
volume._treeVolume = info.treeVolume;
|
||||
volume._playRefillAudio = info.playRefillAudio;
|
||||
|
||||
volume.gameObject.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,17 @@ namespace NewHorizons.Builder.Volumes
|
||||
{
|
||||
public static class PriorityVolumeBuilder
|
||||
{
|
||||
public static TVolume MakeExisting<TVolume>(GameObject go, GameObject planetGO, Sector sector, PriorityVolumeInfo info) where TVolume : PriorityVolume
|
||||
{
|
||||
var volume = VolumeBuilder.MakeExisting<TVolume>(go, planetGO, sector, info);
|
||||
|
||||
volume._layer = info.layer;
|
||||
volume.SetPriority(info.priority);
|
||||
|
||||
return volume;
|
||||
}
|
||||
|
||||
|
||||
public static TVolume Make<TVolume>(GameObject planetGO, Sector sector, PriorityVolumeInfo info) where TVolume : PriorityVolume
|
||||
{
|
||||
var volume = VolumeBuilder.Make<TVolume>(planetGO, sector, info);
|
||||
|
||||
@ -12,6 +12,8 @@ namespace NewHorizons.Builder.Volumes.Rulesets
|
||||
volume.minImpactSpeed = info.minImpactSpeed;
|
||||
volume.maxImpactSpeed = info.maxImpactSpeed;
|
||||
|
||||
volume.gameObject.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,6 +15,8 @@ namespace NewHorizons.Builder.Volumes.Rulesets
|
||||
volume._lanternRange = info.lanternRange;
|
||||
volume._ignoreAnchor = info.ignoreAnchor;
|
||||
|
||||
volume.gameObject.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,6 +13,8 @@ namespace NewHorizons.Builder.Volumes.Rulesets
|
||||
volume._nerfJetpackBooster = info.nerfJetpackBooster;
|
||||
volume._nerfDuration = info.nerfDuration;
|
||||
|
||||
volume.gameObject.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
|
||||
22
NewHorizons/Builder/Volumes/SpeedLimiterVolumeBuilder.cs
Normal file
22
NewHorizons/Builder/Volumes/SpeedLimiterVolumeBuilder.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using NewHorizons.Components.Volumes;
|
||||
using NewHorizons.External.Modules.Volumes.VolumeInfos;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Builder.Volumes
|
||||
{
|
||||
public static class SpeedLimiterVolumeBuilder
|
||||
{
|
||||
public static SpeedLimiterVolume Make(GameObject planetGO, Sector sector, SpeedLimiterVolumeInfo info)
|
||||
{
|
||||
var volume = VolumeBuilder.Make<SpeedLimiterVolume>(planetGO, sector, info);
|
||||
|
||||
volume.maxSpeed = info.maxSpeed;
|
||||
volume.stoppingDistance = info.stoppingDistance;
|
||||
volume.maxEntryAngle = info.maxEntryAngle;
|
||||
|
||||
volume.gameObject.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -12,6 +12,8 @@ namespace NewHorizons.Builder.Volumes
|
||||
volume._speedLimit = info.speedLimit;
|
||||
volume._acceleration = info.acceleration;
|
||||
|
||||
volume.gameObject.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
using NewHorizons.Builder.Props;
|
||||
using NewHorizons.External.Modules.Volumes.VolumeInfos;
|
||||
using NewHorizons.Utility.OuterWilds;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Builder.Volumes
|
||||
@ -9,27 +7,13 @@ namespace NewHorizons.Builder.Volumes
|
||||
{
|
||||
public static TVolume Make<TVolume>(GameObject planetGO, Sector sector, VanishVolumeInfo info) where TVolume : VanishVolume
|
||||
{
|
||||
var go = GeneralPropBuilder.MakeNew(typeof(TVolume).Name, planetGO, sector, info);
|
||||
go.layer = Layer.BasicEffectVolume;
|
||||
|
||||
var collider = go.AddComponent<SphereCollider>();
|
||||
collider.isTrigger = true;
|
||||
collider.radius = info.radius;
|
||||
|
||||
var owCollider = go.AddComponent<OWCollider>();
|
||||
owCollider._collider = collider;
|
||||
|
||||
var owTriggerVolume = go.AddComponent<OWTriggerVolume>();
|
||||
owTriggerVolume._owCollider = owCollider;
|
||||
|
||||
var volume = go.AddComponent<TVolume>();
|
||||
var volume = VolumeBuilder.Make<TVolume>(planetGO, sector, info);
|
||||
|
||||
var collider = volume.gameObject.GetComponent<SphereCollider>();
|
||||
volume._collider = collider;
|
||||
volume._shrinkBodies = info.shrinkBodies;
|
||||
volume._onlyAffectsPlayerAndShip = info.onlyAffectsPlayerRelatedBodies;
|
||||
|
||||
go.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,6 +12,8 @@ namespace NewHorizons.Builder.Volumes.VisorEffects
|
||||
volume._frostRate = info.frostRate;
|
||||
volume._maxFrost = info.maxFrost;
|
||||
|
||||
volume.gameObject.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,6 +13,8 @@ namespace NewHorizons.Builder.Volumes.VisorEffects
|
||||
volume._dropletRate = info.dropletRate;
|
||||
volume._streakRate = info.streakRate;
|
||||
|
||||
volume.gameObject.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,21 +7,36 @@ namespace NewHorizons.Builder.Volumes
|
||||
{
|
||||
public static class VolumeBuilder
|
||||
{
|
||||
public static TVolume MakeExisting<TVolume>(GameObject go, GameObject planetGO, Sector sector, VolumeInfo info) where TVolume : MonoBehaviour
|
||||
{
|
||||
// Respect existing layer if set to a valid volume layer
|
||||
if (go.layer != Layer.AdvancedEffectVolume)
|
||||
{
|
||||
go.layer = Layer.BasicEffectVolume;
|
||||
}
|
||||
|
||||
// Skip creating a trigger volume if one already exists and has a shape set and we aren't overriding it
|
||||
var trigger = go.GetComponent<OWTriggerVolume>();
|
||||
if (trigger == null || (trigger._shape == null && trigger._owCollider == null) || info.shape != null || info.radius > 0f)
|
||||
{
|
||||
ShapeBuilder.AddTriggerVolume(go, info.shape, info.radius);
|
||||
}
|
||||
|
||||
var volume = go.AddComponent<TVolume>();
|
||||
|
||||
return volume;
|
||||
}
|
||||
|
||||
public static TVolume Make<TVolume>(GameObject planetGO, Sector sector, VolumeInfo info) where TVolume : MonoBehaviour //Could be BaseVolume but I need to create vanilla volumes too.
|
||||
{
|
||||
var go = GeneralPropBuilder.MakeNew(typeof(TVolume).Name, planetGO, sector, info);
|
||||
go.layer = Layer.BasicEffectVolume;
|
||||
|
||||
var shape = go.AddComponent<SphereShape>();
|
||||
shape.radius = info.radius;
|
||||
|
||||
var owTriggerVolume = go.AddComponent<OWTriggerVolume>();
|
||||
owTriggerVolume._shape = shape;
|
||||
|
||||
var volume = go.AddComponent<TVolume>();
|
||||
|
||||
go.SetActive(true);
|
||||
return MakeExisting<TVolume>(go, planetGO, sector, info);
|
||||
}
|
||||
|
||||
public static TVolume MakeAndEnable<TVolume>(GameObject planetGO, Sector sector, VolumeInfo info) where TVolume : MonoBehaviour
|
||||
{
|
||||
var volume = Make<TVolume>(planetGO, sector, info);
|
||||
volume.gameObject.SetActive(true);
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,28 +60,28 @@ namespace NewHorizons.Builder.Volumes
|
||||
{
|
||||
foreach (var mapRestrictionVolume in config.Volumes.mapRestrictionVolumes)
|
||||
{
|
||||
VolumeBuilder.Make<MapRestrictionVolume>(go, sector, mapRestrictionVolume);
|
||||
VolumeBuilder.MakeAndEnable<MapRestrictionVolume>(go, sector, mapRestrictionVolume);
|
||||
}
|
||||
}
|
||||
if (config.Volumes.interferenceVolumes != null)
|
||||
{
|
||||
foreach (var interferenceVolume in config.Volumes.interferenceVolumes)
|
||||
{
|
||||
VolumeBuilder.Make<Components.Volumes.InterferenceVolume>(go, sector, interferenceVolume);
|
||||
VolumeBuilder.MakeAndEnable<Components.Volumes.InterferenceVolume>(go, sector, interferenceVolume);
|
||||
}
|
||||
}
|
||||
if (config.Volumes.reverbVolumes != null)
|
||||
{
|
||||
foreach (var reverbVolume in config.Volumes.reverbVolumes)
|
||||
{
|
||||
VolumeBuilder.Make<ReverbTriggerVolume>(go, sector, reverbVolume);
|
||||
VolumeBuilder.MakeAndEnable<ReverbTriggerVolume>(go, sector, reverbVolume);
|
||||
}
|
||||
}
|
||||
if (config.Volumes.insulatingVolumes != null)
|
||||
{
|
||||
foreach (var insulatingVolume in config.Volumes.insulatingVolumes)
|
||||
{
|
||||
VolumeBuilder.Make<InsulatingVolume>(go, sector, insulatingVolume);
|
||||
VolumeBuilder.MakeAndEnable<InsulatingVolume>(go, sector, insulatingVolume);
|
||||
}
|
||||
}
|
||||
if (config.Volumes.zeroGravityVolumes != null)
|
||||
@ -112,20 +112,58 @@ namespace NewHorizons.Builder.Volumes
|
||||
FluidVolumeBuilder.Make(go, sector, fluidVolume);
|
||||
}
|
||||
}
|
||||
if (config.Volumes.forces != null)
|
||||
{
|
||||
if (config.Volumes.forces.cylindricalVolumes != null)
|
||||
{
|
||||
foreach (var cylindricalVolume in config.Volumes.forces.cylindricalVolumes)
|
||||
{
|
||||
ForceVolumeBuilder.Make(go, sector, cylindricalVolume);
|
||||
}
|
||||
}
|
||||
if (config.Volumes.forces.directionalVolumes != null)
|
||||
{
|
||||
foreach (var directionalVolume in config.Volumes.forces.directionalVolumes)
|
||||
{
|
||||
ForceVolumeBuilder.Make(go, sector, directionalVolume);
|
||||
}
|
||||
}
|
||||
if (config.Volumes.forces.gravityVolumes != null)
|
||||
{
|
||||
foreach (var gravityVolume in config.Volumes.forces.gravityVolumes)
|
||||
{
|
||||
ForceVolumeBuilder.Make(go, sector, gravityVolume);
|
||||
}
|
||||
}
|
||||
if (config.Volumes.forces.polarVolumes != null)
|
||||
{
|
||||
foreach (var polarVolume in config.Volumes.forces.polarVolumes)
|
||||
{
|
||||
ForceVolumeBuilder.Make(go, sector, polarVolume);
|
||||
}
|
||||
}
|
||||
if (config.Volumes.forces.radialVolumes != null)
|
||||
{
|
||||
foreach (var radialVolume in config.Volumes.forces.radialVolumes)
|
||||
{
|
||||
ForceVolumeBuilder.Make(go, sector, radialVolume);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (config.Volumes.probe != null)
|
||||
{
|
||||
if (config.Volumes.probe.destructionVolumes != null)
|
||||
{
|
||||
foreach (var destructionVolume in config.Volumes.probe.destructionVolumes)
|
||||
{
|
||||
VolumeBuilder.Make<ProbeDestructionVolume>(go, sector, destructionVolume);
|
||||
VolumeBuilder.MakeAndEnable<ProbeDestructionVolume>(go, sector, destructionVolume);
|
||||
}
|
||||
}
|
||||
if (config.Volumes.probe.safetyVolumes != null)
|
||||
{
|
||||
foreach (var safetyVolume in config.Volumes.probe.safetyVolumes)
|
||||
{
|
||||
VolumeBuilder.Make<ProbeSafetyVolume>(go, sector, safetyVolume);
|
||||
VolumeBuilder.MakeAndEnable<ProbeSafetyVolume>(go, sector, safetyVolume);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -152,7 +190,7 @@ namespace NewHorizons.Builder.Volumes
|
||||
{
|
||||
foreach (var antiTravelMusicRuleset in config.Volumes.rulesets.antiTravelMusicRulesets)
|
||||
{
|
||||
VolumeBuilder.Make<AntiTravelMusicRuleset>(go, sector, antiTravelMusicRuleset);
|
||||
VolumeBuilder.MakeAndEnable<AntiTravelMusicRuleset>(go, sector, antiTravelMusicRuleset);
|
||||
}
|
||||
}
|
||||
if (config.Volumes.rulesets.playerImpactRulesets != null)
|
||||
@ -181,7 +219,7 @@ namespace NewHorizons.Builder.Volumes
|
||||
{
|
||||
foreach (var referenceFrameBlockerVolume in config.Volumes.referenceFrameBlockerVolumes)
|
||||
{
|
||||
VolumeBuilder.Make<ReferenceFrameBlockerVolume>(go, sector, referenceFrameBlockerVolume);
|
||||
VolumeBuilder.MakeAndEnable<ReferenceFrameBlockerVolume>(go, sector, referenceFrameBlockerVolume);
|
||||
}
|
||||
}
|
||||
if (config.Volumes.speedTrapVolumes != null)
|
||||
@ -191,11 +229,18 @@ namespace NewHorizons.Builder.Volumes
|
||||
SpeedTrapVolumeBuilder.Make(go, sector, speedTrapVolume);
|
||||
}
|
||||
}
|
||||
if (config.Volumes.speedLimiterVolumes != null)
|
||||
{
|
||||
foreach (var speedLimiterVolume in config.Volumes.speedLimiterVolumes)
|
||||
{
|
||||
SpeedLimiterVolumeBuilder.Make(go, sector, speedLimiterVolume);
|
||||
}
|
||||
}
|
||||
if (config.Volumes.lightSourceVolumes != null)
|
||||
{
|
||||
foreach (var lightSourceVolume in config.Volumes.lightSourceVolumes)
|
||||
{
|
||||
VolumeBuilder.Make<LightlessLightSourceVolume>(go, sector, lightSourceVolume);
|
||||
VolumeBuilder.MakeAndEnable<LightlessLightSourceVolume>(go, sector, lightSourceVolume);
|
||||
}
|
||||
}
|
||||
if (config.Volumes.solarSystemVolume != null)
|
||||
@ -209,7 +254,7 @@ namespace NewHorizons.Builder.Volumes
|
||||
{
|
||||
foreach (var creditsVolume in config.Volumes.creditsVolume)
|
||||
{
|
||||
CreditsVolumeBuilder.Make(go, sector, creditsVolume);
|
||||
CreditsVolumeBuilder.Make(go, sector, creditsVolume, mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,6 +11,8 @@ namespace NewHorizons.Builder.Volumes
|
||||
|
||||
volume._inheritable = true;
|
||||
|
||||
volume.gameObject.SetActive(true);
|
||||
|
||||
return volume;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
using NewHorizons.External.Modules;
|
||||
using NewHorizons.External.SerializableEnums;
|
||||
using NewHorizons.Handlers;
|
||||
using NewHorizons.Patches.CreditsScenePatches;
|
||||
using NewHorizons.Utility.Files;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using OWML.Common;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@ -15,14 +19,14 @@ namespace NewHorizons.Components
|
||||
/// Mod unique id to game over module list
|
||||
/// Done as a dictionary so that Reload Configs can overwrite entries per mod
|
||||
/// </summary>
|
||||
public static Dictionary<string, GameOverModule[]> gameOvers = new();
|
||||
public static Dictionary<IModBehaviour, GameOverModule[]> gameOvers = new();
|
||||
|
||||
public static NHGameOverManager Instance { get; private set; }
|
||||
|
||||
private GameOverController _gameOverController;
|
||||
private PlayerCameraEffectController _playerCameraEffectController;
|
||||
|
||||
private GameOverModule[] _gameOvers;
|
||||
private (IModBehaviour mod, GameOverModule gameOver)[] _gameOvers;
|
||||
|
||||
private bool _gameOverSequenceStarted;
|
||||
|
||||
@ -36,25 +40,35 @@ namespace NewHorizons.Components
|
||||
_gameOverController = FindObjectOfType<GameOverController>();
|
||||
_playerCameraEffectController = FindObjectOfType<PlayerCameraEffectController>();
|
||||
|
||||
_gameOvers = gameOvers.SelectMany(x => x.Value).ToArray();
|
||||
var gameOverList = new List<(IModBehaviour, GameOverModule)>();
|
||||
foreach (var gameOverPair in gameOvers)
|
||||
{
|
||||
var mod = gameOverPair.Key;
|
||||
foreach (var gameOver in gameOverPair.Value)
|
||||
{
|
||||
gameOverList.Add((mod, gameOver));
|
||||
}
|
||||
}
|
||||
_gameOvers = gameOverList.ToArray();
|
||||
}
|
||||
|
||||
public void TryHijackDeathSequence()
|
||||
{
|
||||
var gameOver = _gameOvers.FirstOrDefault(x => !string.IsNullOrEmpty(x.condition) && DialogueConditionManager.SharedInstance.GetConditionState(x.condition));
|
||||
if (!_gameOverSequenceStarted && gameOver != null && !Locator.GetDeathManager()._finishedDLC)
|
||||
var gameOver = _gameOvers.FirstOrDefault(x => !string.IsNullOrEmpty(x.gameOver.condition)
|
||||
&& DialogueConditionManager.SharedInstance.GetConditionState(x.gameOver.condition));
|
||||
if (!_gameOverSequenceStarted && gameOver != default && !Locator.GetDeathManager()._finishedDLC)
|
||||
{
|
||||
StartGameOverSequence(gameOver, null);
|
||||
StartGameOverSequence(gameOver.gameOver, null, gameOver.mod);
|
||||
}
|
||||
}
|
||||
|
||||
public void StartGameOverSequence(GameOverModule gameOver, DeathType? deathType)
|
||||
public void StartGameOverSequence(GameOverModule gameOver, DeathType? deathType, IModBehaviour mod)
|
||||
{
|
||||
_gameOverSequenceStarted = true;
|
||||
Delay.StartCoroutine(GameOver(gameOver, deathType));
|
||||
Delay.StartCoroutine(GameOver(gameOver, deathType, mod));
|
||||
}
|
||||
|
||||
private IEnumerator GameOver(GameOverModule gameOver, DeathType? deathType)
|
||||
private IEnumerator GameOver(GameOverModule gameOver, DeathType? deathType, IModBehaviour mod)
|
||||
{
|
||||
OWInput.ChangeInputMode(InputMode.None);
|
||||
ReticleController.Hide();
|
||||
@ -104,12 +118,12 @@ namespace NewHorizons.Components
|
||||
yield return new WaitUntil(ReadytoLoadCreditsScene);
|
||||
}
|
||||
|
||||
LoadCreditsScene(gameOver);
|
||||
LoadCreditsScene(gameOver, mod);
|
||||
}
|
||||
|
||||
private bool ReadytoLoadCreditsScene() => _gameOverController._fadedOutText && _gameOverController._textAnimator.IsComplete();
|
||||
|
||||
private void LoadCreditsScene(GameOverModule gameOver)
|
||||
private void LoadCreditsScene(GameOverModule gameOver, IModBehaviour mod)
|
||||
{
|
||||
NHLogger.LogVerbose($"Load credits {gameOver.creditsType}");
|
||||
|
||||
@ -125,6 +139,9 @@ namespace NewHorizons.Components
|
||||
TimelineObliterationController.s_hasRealityEnded = true;
|
||||
LoadManager.LoadScene(OWScene.Credits_Fast, LoadManager.FadeType.ToBlack);
|
||||
break;
|
||||
case NHCreditsType.Custom:
|
||||
LoadCustomCreditsScene(gameOver, mod);
|
||||
break;
|
||||
default:
|
||||
// GameOverController disables post processing
|
||||
_gameOverController._flashbackCamera.postProcessing.enabled = true;
|
||||
@ -134,5 +151,42 @@ namespace NewHorizons.Components
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadCustomCreditsScene(GameOverModule gameOver, IModBehaviour mod)
|
||||
{
|
||||
LoadManager.LoadScene(OWScene.Credits_Fast, LoadManager.FadeType.ToBlack);
|
||||
|
||||
// Unfortunately we can't make this a private method, as EventArgs/EventHandler enforces the (sender, e) parameters, which prevents us from passing in gameOver and mod, which we need.
|
||||
EventHandler onCreditsBuilt = null; // needs to be done so we can unsubscribe from within the lambda.
|
||||
onCreditsBuilt = (sender, e) =>
|
||||
{
|
||||
// Unsubscribe first, playing it safe in case it NREs
|
||||
CreditsPatches.CreditsBuilt -= onCreditsBuilt;
|
||||
|
||||
// Patch new music clip
|
||||
var musicSource = Locator.FindObjectsOfType<OWAudioSource>().Where(x => x.name == "AudioSource").Single(); // AudioSource that plays the credits music is literally called "AudioSource", luckily it's the only one called that. Lazy OW devs do be lazy.
|
||||
if (!string.IsNullOrEmpty(gameOver.audio)) // string.Empty is default value for "audio" in GameOverModule, means no audio is specified.
|
||||
{
|
||||
AudioUtilities.SetAudioClip(musicSource, gameOver.audio, mod); // Load audio if specified
|
||||
}
|
||||
else
|
||||
{
|
||||
musicSource.AssignAudioLibraryClip(AudioType.PLACEHOLDER); // Otherwise default custom credits are silent - AudioType.PLACEHOLDER is silence (apparently)
|
||||
}
|
||||
|
||||
musicSource.loop = gameOver.audioLooping;
|
||||
musicSource._maxSourceVolume = gameOver.audioVolume;
|
||||
|
||||
// Override fade in
|
||||
musicSource.Stop();
|
||||
musicSource.Play();
|
||||
|
||||
// Patch scroll duration
|
||||
var creditsScroll = Locator.FindObjectOfType<CreditsScrollSection>();
|
||||
creditsScroll._scrollDuration = gameOver.length;
|
||||
};
|
||||
|
||||
CreditsPatches.CreditsBuilt += onCreditsBuilt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using NewHorizons.External.Modules;
|
||||
using OWML.Common;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
@ -8,12 +9,13 @@ namespace NewHorizons.Components.Volumes
|
||||
{
|
||||
public GameOverModule gameOver;
|
||||
public DeathType? deathType;
|
||||
public IModBehaviour mod;
|
||||
|
||||
public override void OnTriggerVolumeEntry(GameObject hitObj)
|
||||
{
|
||||
if (hitObj.CompareTag("PlayerDetector") && enabled && (string.IsNullOrEmpty(gameOver.condition) || DialogueConditionManager.SharedInstance.GetConditionState(gameOver.condition)))
|
||||
{
|
||||
NHGameOverManager.Instance.StartGameOverSequence(gameOver, deathType);
|
||||
NHGameOverManager.Instance.StartGameOverSequence(gameOver, deathType, mod);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
164
NewHorizons/Components/Volumes/SpeedLimiterVolume.cs
Normal file
164
NewHorizons/Components/Volumes/SpeedLimiterVolume.cs
Normal file
@ -0,0 +1,164 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Components.Volumes
|
||||
{
|
||||
public class SpeedLimiterVolume : BaseVolume
|
||||
{
|
||||
public float maxSpeed = 10f;
|
||||
public float stoppingDistance = 100f;
|
||||
public float maxEntryAngle = 60f;
|
||||
|
||||
private OWRigidbody _parentBody;
|
||||
private List<TrackedBody> _trackedBodies = new List<TrackedBody>();
|
||||
private bool _playerJustExitedDream;
|
||||
|
||||
public override void Awake()
|
||||
{
|
||||
_parentBody = GetComponentInParent<OWRigidbody>();
|
||||
base.Awake();
|
||||
GlobalMessenger.AddListener("ExitDreamWorld", OnExitDreamWorld);
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
public override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
GlobalMessenger.RemoveListener("ExitDreamWorld", OnExitDreamWorld);
|
||||
}
|
||||
|
||||
public void FixedUpdate()
|
||||
{
|
||||
foreach (var trackedBody in _trackedBodies)
|
||||
{
|
||||
bool slowed = false;
|
||||
Vector3 velocity = trackedBody.body.GetVelocity() - _parentBody.GetVelocity();
|
||||
float magnitude = velocity.magnitude;
|
||||
if (magnitude <= maxSpeed)
|
||||
{
|
||||
slowed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool needsSlowing = true;
|
||||
float velocityReduction = trackedBody.deceleration * Time.deltaTime;
|
||||
float requiredReduction = maxSpeed - magnitude;
|
||||
if (requiredReduction > velocityReduction)
|
||||
{
|
||||
velocityReduction = requiredReduction;
|
||||
slowed = true;
|
||||
}
|
||||
if (trackedBody.name == Detector.Name.Ship)
|
||||
{
|
||||
Autopilot component = Locator.GetShipTransform().GetComponent<Autopilot>();
|
||||
if (component != null && component.IsFlyingToDestination())
|
||||
{
|
||||
needsSlowing = false;
|
||||
}
|
||||
}
|
||||
if (needsSlowing)
|
||||
{
|
||||
Vector3 velocityChange = velocityReduction * velocity.normalized;
|
||||
trackedBody.body.AddVelocityChange(velocityChange);
|
||||
if (trackedBody.name == Detector.Name.Ship && PlayerState.IsInsideShip())
|
||||
{
|
||||
Locator.GetPlayerBody().AddVelocityChange(velocityChange);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (slowed)
|
||||
{
|
||||
if (trackedBody.name == Detector.Name.Ship)
|
||||
GlobalMessenger.FireEvent("ShipExitSpeedLimiter");
|
||||
_trackedBodies.Remove(trackedBody);
|
||||
if (_trackedBodies.Count == 0)
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnExitDreamWorld()
|
||||
{
|
||||
_playerJustExitedDream = true;
|
||||
}
|
||||
|
||||
public override void OnTriggerVolumeEntry(GameObject hitObj)
|
||||
{
|
||||
DynamicForceDetector component = hitObj.GetComponent<DynamicForceDetector>();
|
||||
if (component == null || !component.CompareNameMask(Detector.Name.Player | Detector.Name.Probe | Detector.Name.Ship)) return;
|
||||
|
||||
if (component.GetName() == Detector.Name.Player && (PlayerState.IsInsideShip() || _playerJustExitedDream))
|
||||
{
|
||||
_playerJustExitedDream = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
OWRigidbody attachedOWRigidbody = component.GetAttachedOWRigidbody();
|
||||
Vector3 from = transform.position - attachedOWRigidbody.GetPosition();
|
||||
Vector3 to = attachedOWRigidbody.GetVelocity() - _parentBody.GetVelocity();
|
||||
float magnitude = to.magnitude;
|
||||
if (magnitude > maxSpeed && Vector3.Angle(from, to) < maxEntryAngle)
|
||||
{
|
||||
float deceleration = (maxSpeed * maxSpeed - magnitude * magnitude) / (2f * stoppingDistance);
|
||||
TrackedBody trackedBody = new TrackedBody(attachedOWRigidbody, component.GetName(), deceleration);
|
||||
_trackedBodies.Add(trackedBody);
|
||||
if (component.GetName() == Detector.Name.Ship)
|
||||
GlobalMessenger.FireEvent("ShipEnterSpeedLimiter");
|
||||
enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnTriggerVolumeExit(GameObject hitObj)
|
||||
{
|
||||
DynamicForceDetector component = hitObj.GetComponent<DynamicForceDetector>();
|
||||
if (component == null) return;
|
||||
|
||||
OWRigidbody body = component.GetAttachedOWRigidbody();
|
||||
TrackedBody trackedBody = _trackedBodies.Find((TrackedBody i) => i.body == body);
|
||||
if (trackedBody != null)
|
||||
{
|
||||
if (trackedBody.name == Detector.Name.Ship)
|
||||
GlobalMessenger.FireEvent("ShipExitSpeedLimiter");
|
||||
|
||||
_trackedBodies.Remove(trackedBody);
|
||||
|
||||
if (_trackedBodies.Count == 0)
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
protected class TrackedBody
|
||||
{
|
||||
public OWRigidbody body;
|
||||
|
||||
public Detector.Name name;
|
||||
|
||||
public float deceleration;
|
||||
|
||||
public TrackedBody(OWRigidbody body, Detector.Name name, float deceleration)
|
||||
{
|
||||
this.body = body;
|
||||
this.name = name;
|
||||
this.deceleration = deceleration;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is TrackedBody trackedBody)
|
||||
{
|
||||
return trackedBody.body == body && trackedBody.name == name;
|
||||
}
|
||||
return base.Equals(obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode() => body.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
25
NewHorizons/External/Modules/GameOverModule.cs
vendored
25
NewHorizons/External/Modules/GameOverModule.cs
vendored
@ -24,6 +24,31 @@ namespace NewHorizons.External.Modules
|
||||
/// </summary>
|
||||
public string condition;
|
||||
|
||||
/// <summary>
|
||||
/// The audio to use for the credits music. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list.
|
||||
/// Credits will be silent unless this attribute is specified.
|
||||
/// Note: only applies when creditsType is set to "custom".
|
||||
/// </summary>
|
||||
public string audio;
|
||||
|
||||
/// <summary>
|
||||
/// The length of the fade in and out for the credits music.
|
||||
/// Note: only applies when creditsType is set to "custom".
|
||||
/// </summary>
|
||||
[DefaultValue(1f)] public float audioVolume = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the credits music should loop.
|
||||
/// Note: only applies when creditsType is set to "custom".
|
||||
/// </summary>
|
||||
[DefaultValue(false)] public bool audioLooping = false;
|
||||
|
||||
/// <summary>
|
||||
/// Duration of the credits scroll in seconds.
|
||||
/// Note: only applies when creditsType is set to "custom".
|
||||
/// </summary>
|
||||
[DefaultValue(120f)] public float length = 120f;
|
||||
|
||||
/// <summary>
|
||||
/// The type of credits that will run after the game over message is shown
|
||||
/// </summary>
|
||||
|
||||
5
NewHorizons/External/Modules/PropModule.cs
vendored
5
NewHorizons/External/Modules/PropModule.cs
vendored
@ -58,6 +58,11 @@ namespace NewHorizons.External.Modules
|
||||
/// </summary>
|
||||
public RaftInfo[] rafts;
|
||||
|
||||
/// <summary>
|
||||
/// Add raft docks to this planet (requires Echoes of the Eye DLC)
|
||||
/// </summary>
|
||||
public RaftDockInfo[] raftDocks;
|
||||
|
||||
/// <summary>
|
||||
/// Scatter props around this planet's surface
|
||||
/// </summary>
|
||||
|
||||
@ -82,7 +82,7 @@ namespace NewHorizons.External.Modules.Props.EchoesOfTheEye
|
||||
[DefaultValue("sevenSlides")] public SlideReelType reelModel = SlideReelType.SevenSlides;
|
||||
|
||||
/// <summary>
|
||||
/// Exclusive to the slide reel type. Condition/material of the reel. Antique is the Stranger, Pristine is the Dreamworld, Rusted is a burned reel.
|
||||
/// Exclusive to the slide reel and standing vision torch type. Condition/material of the reel. Antique is the Stranger, Pristine is the Dreamworld, Rusted (exclusive to slide reels) is a burned reel.
|
||||
/// </summary>
|
||||
[DefaultValue("antique")] public SlideReelCondition reelCondition = SlideReelCondition.Antique;
|
||||
|
||||
|
||||
10
NewHorizons/External/Modules/Props/EchoesOfTheEye/RaftDockInfo.cs
vendored
Normal file
10
NewHorizons/External/Modules/Props/EchoesOfTheEye/RaftDockInfo.cs
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace NewHorizons.External.Modules.Props.EchoesOfTheEye
|
||||
{
|
||||
[JsonObject]
|
||||
public class RaftDockInfo : GeneralPropInfo
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -10,6 +10,16 @@ namespace NewHorizons.External.Modules.Props.EchoesOfTheEye
|
||||
/// Acceleration of the raft. Default acceleration is 5.
|
||||
/// </summary>
|
||||
[DefaultValue(5f)] public float acceleration = 5f;
|
||||
|
||||
/// <summary>
|
||||
/// Path to the dock this raft will start attached to.
|
||||
/// </summary>
|
||||
public string dockPath;
|
||||
|
||||
/// <summary>
|
||||
/// Uses the raft model from the dreamworld
|
||||
/// </summary>
|
||||
public bool pristine;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
92
NewHorizons/External/Modules/Props/ShapeInfo.cs
vendored
Normal file
92
NewHorizons/External/Modules/Props/ShapeInfo.cs
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
using NewHorizons.External.SerializableData;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NewHorizons.External.Modules.Props
|
||||
{
|
||||
[JsonObject]
|
||||
public class ShapeInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of shape or collider to add. Sphere, box, and capsule colliders are more performant and support collision. Defaults to sphere.
|
||||
/// </summary>
|
||||
public ShapeType type = ShapeType.Sphere;
|
||||
|
||||
/// <summary>
|
||||
/// The radius of the shape or collider. Defaults to 0.5 meters. Only used by spheres, capsules, cylinders, hemispheres, hemicapsules, and rings.
|
||||
/// </summary>
|
||||
public float radius = 0.5f;
|
||||
|
||||
/// <summary>
|
||||
/// The height of the shape or collider. Defaults to 1 meter. Only used by capsules, cylinders, cones, hemicapsules, and rings.
|
||||
/// </summary>
|
||||
public float height = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// The axis that the shape or collider is aligned with. Defaults to the Y axis (up). The flat bottom of the shape will be pointing towards the negative axis. Only used by capsules, cones, hemispheres, and hemicapsules.
|
||||
/// </summary>
|
||||
public ColliderAxis direction = ColliderAxis.Y;
|
||||
|
||||
/// <summary>
|
||||
/// The inner radius of the shape. Defaults to 0 meters. Only used by cones and rings.
|
||||
/// </summary>
|
||||
public float innerRadius = 0f;
|
||||
|
||||
/// <summary>
|
||||
/// The outer radius of the shape. Defaults to 0.5 meters. Only used by cones and rings.
|
||||
/// </summary>
|
||||
public float outerRadius = 0.5f;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the shape has an end cap. Defaults to true. Only used by hemispheres and hemicapsules.
|
||||
/// </summary>
|
||||
public bool cap = true;
|
||||
|
||||
/// <summary>
|
||||
/// The size of the shape or collider. Defaults to (1,1,1). Only used by boxes.
|
||||
/// </summary>
|
||||
public MVector3 size;
|
||||
|
||||
/// <summary>
|
||||
/// The offset of the shape or collider from the object's origin. Defaults to (0,0,0). Supported by all collider and shape types.
|
||||
/// </summary>
|
||||
public MVector3 offset;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the collider should have collision enabled. If false, the collider will be a trigger. Defaults to false. Only supported for spheres, boxes, and capsules.
|
||||
/// </summary>
|
||||
public bool hasCollision = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to explicitly use a shape instead of a collider. Shapes do not support collision and are less performant, but support a wider set of shapes and are required by some components. Omit this unless you explicitly want to use a sphere, box, or capsule shape instead of a collider.
|
||||
/// </summary>
|
||||
public bool? useShape;
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum ShapeType
|
||||
{
|
||||
[EnumMember(Value = @"sphere")] Sphere,
|
||||
[EnumMember(Value = @"box")] Box,
|
||||
[EnumMember(Value = @"capsule")] Capsule,
|
||||
[EnumMember(Value = @"cylinder")] Cylinder,
|
||||
[EnumMember(Value = @"cone")] Cone,
|
||||
[EnumMember(Value = @"hemisphere")] Hemisphere,
|
||||
[EnumMember(Value = @"hemicapsule")] Hemicapsule,
|
||||
[EnumMember(Value = @"ring")] Ring,
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum ColliderAxis
|
||||
{
|
||||
[EnumMember(Value = @"x")] X = 0,
|
||||
[EnumMember(Value = @"y")] Y = 1,
|
||||
[EnumMember(Value = @"z")] Z = 2,
|
||||
}
|
||||
}
|
||||
40
NewHorizons/External/Modules/Volumes/ForceModule.cs
vendored
Normal file
40
NewHorizons/External/Modules/Volumes/ForceModule.cs
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
using NewHorizons.External.Modules.Volumes.VolumeInfos;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NewHorizons.External.Modules.Volumes
|
||||
{
|
||||
[JsonObject]
|
||||
public class ForceModule
|
||||
{
|
||||
/// <summary>
|
||||
/// Applies a constant force along the volume's XZ plane towards the volume's center. Affects alignment.
|
||||
/// </summary>
|
||||
public CylindricalForceVolumeInfo[] cylindricalVolumes;
|
||||
|
||||
/// <summary>
|
||||
/// Applies a constant force in the direction of the volume's Y axis. May affect alignment.
|
||||
/// </summary>
|
||||
public DirectionalForceVolumeInfo[] directionalVolumes;
|
||||
|
||||
/// <summary>
|
||||
/// Applies planet-like gravity towards the volume's center with falloff by distance. May affect alignment.
|
||||
/// For actual planetary body gravity, use the properties in the Base module.
|
||||
/// </summary>
|
||||
public GravityVolumeInfo[] gravityVolumes;
|
||||
|
||||
/// <summary>
|
||||
/// Applies a constant force towards the volume's center. Affects alignment.
|
||||
/// </summary>
|
||||
public PolarForceVolumeInfo[] polarVolumes;
|
||||
|
||||
/// <summary>
|
||||
/// Applies a force towards the volume's center with falloff by distance. Affects alignment.
|
||||
/// </summary>
|
||||
public RadialForceVolumeInfo[] radialVolumes;
|
||||
}
|
||||
}
|
||||
24
NewHorizons/External/Modules/Volumes/VolumeInfos/CylindricalForceVolumeInfo.cs
vendored
Normal file
24
NewHorizons/External/Modules/Volumes/VolumeInfos/CylindricalForceVolumeInfo.cs
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
using NewHorizons.External.SerializableData;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NewHorizons.External.Modules.Volumes.VolumeInfos
|
||||
{
|
||||
[JsonObject]
|
||||
public class CylindricalForceVolumeInfo : ForceVolumeInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The direction that the force applied by this volume will be perpendicular to. Defaults to up (0, 1, 0).
|
||||
/// </summary>
|
||||
public MVector3 normal;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to play the gravity crystal audio when the player is in this volume.
|
||||
/// </summary>
|
||||
public bool playGravityCrystalAudio;
|
||||
}
|
||||
}
|
||||
35
NewHorizons/External/Modules/Volumes/VolumeInfos/DirectionalForceVolumeInfo.cs
vendored
Normal file
35
NewHorizons/External/Modules/Volumes/VolumeInfos/DirectionalForceVolumeInfo.cs
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
using NewHorizons.External.SerializableData;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NewHorizons.External.Modules.Volumes.VolumeInfos
|
||||
{
|
||||
[JsonObject]
|
||||
public class DirectionalForceVolumeInfo : ForceVolumeInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The direction of the force applied by this volume. Defaults to up (0, 1, 0).
|
||||
/// </summary>
|
||||
public MVector3 normal;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this force volume affects alignment. Defaults to true.
|
||||
/// </summary>
|
||||
[DefaultValue(true)] public bool affectsAlignment = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the force applied by this volume takes the centripetal force of the volume's parent body into account. Defaults to false.
|
||||
/// </summary>
|
||||
public bool offsetCentripetalForce;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to play the gravity crystal audio when the player is in this volume.
|
||||
/// </summary>
|
||||
public bool playGravityCrystalAudio;
|
||||
}
|
||||
}
|
||||
35
NewHorizons/External/Modules/Volumes/VolumeInfos/ForceVolumeInfo.cs
vendored
Normal file
35
NewHorizons/External/Modules/Volumes/VolumeInfos/ForceVolumeInfo.cs
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
using NewHorizons.External.Modules.Props;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NewHorizons.External.Modules.Volumes.VolumeInfos
|
||||
{
|
||||
[JsonObject]
|
||||
public class ForceVolumeInfo : PriorityVolumeInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The force applied by this volume. Can be negative to reverse the direction.
|
||||
/// </summary>
|
||||
public float force;
|
||||
|
||||
/// <summary>
|
||||
/// The priority of this force volume for the purposes of alignment.
|
||||
///
|
||||
/// Volumes of higher priority will override volumes of lower priority. Volumes of the same priority will stack like normal.
|
||||
/// Ex: A player in a gravity volume with priority 0, and zero-gravity volume with priority 1, will feel zero gravity.
|
||||
///
|
||||
/// Default value here is 1 instead of 0 so it automatically overrides planet gravity, which is 0 by default.
|
||||
/// </summary>
|
||||
[DefaultValue(1)] public int alignmentPriority = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this force volume is inheritable. The most recently activated inheritable force volume will stack with other force volumes even if their priorities differ.
|
||||
/// </summary>
|
||||
public bool inheritable;
|
||||
}
|
||||
}
|
||||
44
NewHorizons/External/Modules/Volumes/VolumeInfos/GravityVolumeInfo.cs
vendored
Normal file
44
NewHorizons/External/Modules/Volumes/VolumeInfos/GravityVolumeInfo.cs
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NewHorizons.External.Modules.Volumes.VolumeInfos
|
||||
{
|
||||
[JsonObject]
|
||||
public class GravityVolumeInfo : ForceVolumeInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The upper bounds of the volume's "surface". Above this radius, the force applied by this volume will have falloff applied.
|
||||
/// </summary>
|
||||
public float upperRadius;
|
||||
|
||||
/// <summary>
|
||||
/// The lower bounds of the volume's "surface". Above this radius and below the `upperRadius`, the force applied by this volume will be constant. Defaults to 0.
|
||||
/// </summary>
|
||||
[DefaultValue(0f)] public float lowerRadius;
|
||||
|
||||
/// <summary>
|
||||
/// The volume's force will decrease linearly from `force` to `minForce` as distance decreases from `lowerRadius` to `minRadius`. Defaults to 0.
|
||||
/// </summary>
|
||||
[DefaultValue(0f)] public float minRadius;
|
||||
|
||||
/// <summary>
|
||||
/// The minimum force applied by this volume between `lowerRadius` and `minRadius`. Defaults to 0.
|
||||
/// </summary>
|
||||
[DefaultValue(0f)] public float minForce;
|
||||
|
||||
/// <summary>
|
||||
/// How the force falls off with distance. Most planets use linear but the sun and some moons use inverseSquared.
|
||||
/// </summary>
|
||||
[DefaultValue("linear")] public GravityFallOff fallOff = GravityFallOff.Linear;
|
||||
|
||||
/// <summary>
|
||||
/// The radius where objects will be aligned to the volume's force. Defaults to 1.5x the `upperRadius`. Set to 0 to disable alignment.
|
||||
/// </summary>
|
||||
public float? alignmentRadius;
|
||||
}
|
||||
}
|
||||
23
NewHorizons/External/Modules/Volumes/VolumeInfos/PolarForceVolumeInfo.cs
vendored
Normal file
23
NewHorizons/External/Modules/Volumes/VolumeInfos/PolarForceVolumeInfo.cs
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
using NewHorizons.External.SerializableData;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NewHorizons.External.Modules.Volumes.VolumeInfos
|
||||
{
|
||||
[JsonObject]
|
||||
public class PolarForceVolumeInfo : ForceVolumeInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Tangential mode only. The force applied by this volume will be perpendicular to this direction and the direction to the other body. Defaults to up (0, 1, 0).
|
||||
/// </summary>
|
||||
public MVector3 normal;
|
||||
/// <summary>
|
||||
/// Enables tangential mode. The force applied by this volume will be perpendicular to the normal and the direction to the other body. Defaults to false.
|
||||
/// </summary>
|
||||
public bool tangential;
|
||||
}
|
||||
}
|
||||
32
NewHorizons/External/Modules/Volumes/VolumeInfos/RadialForceVolumeInfo.cs
vendored
Normal file
32
NewHorizons/External/Modules/Volumes/VolumeInfos/RadialForceVolumeInfo.cs
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
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.Volumes.VolumeInfos
|
||||
{
|
||||
[JsonObject]
|
||||
public class RadialForceVolumeInfo : ForceVolumeInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// How the force falls off with distance. Defaults to linear.
|
||||
/// </summary>
|
||||
[DefaultValue("linear")] public FallOff fallOff = FallOff.Linear;
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum FallOff
|
||||
{
|
||||
[EnumMember(Value = @"constant")] Constant = 0,
|
||||
|
||||
[EnumMember(Value = @"linear")] Linear = 1,
|
||||
|
||||
[EnumMember(Value = @"inverseSquared")]
|
||||
InverseSquared = 2
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -29,7 +29,7 @@ namespace NewHorizons.External.Modules.Volumes.VolumeInfos
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The max view angle (in degrees) the player can see the volume with to unlock the fact (`observe` only)
|
||||
/// The max view angle (in degrees) the player can see the volume with to unlock the fact (`observe` only). This will effectively be a cone extending from the volume's center forwards (along the Z axis) based on the volume's rotation.
|
||||
/// </summary>
|
||||
[DefaultValue(180f)]
|
||||
public float maxAngle = 180f; // Observe Only
|
||||
|
||||
28
NewHorizons/External/Modules/Volumes/VolumeInfos/SpeedLimiterVolumeInfo.cs
vendored
Normal file
28
NewHorizons/External/Modules/Volumes/VolumeInfos/SpeedLimiterVolumeInfo.cs
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.ComponentModel;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.External.Modules.Volumes.VolumeInfos
|
||||
{
|
||||
[JsonObject]
|
||||
public class SpeedLimiterVolumeInfo : VolumeInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The speed the volume will slow you down to when you enter it.
|
||||
/// </summary>
|
||||
[DefaultValue(10f)]
|
||||
public float maxSpeed = 10f;
|
||||
|
||||
/// <summary>
|
||||
/// The distance from the outside of the volume that the limiter slows you down to max speed at.
|
||||
/// </summary>
|
||||
[DefaultValue(100f)]
|
||||
public float stoppingDistance = 100f;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum angle (in degrees) between the direction the incoming object is moving relative to the volume's center and the line from the object toward the center of the volume, within which the speed limiter will activate.
|
||||
/// </summary>
|
||||
[DefaultValue(60f)]
|
||||
public float maxEntryAngle = 60f;
|
||||
}
|
||||
}
|
||||
@ -1,14 +1,20 @@
|
||||
using NewHorizons.External.Modules.Props;
|
||||
using Newtonsoft.Json;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace NewHorizons.External.Modules.Volumes.VolumeInfos
|
||||
{
|
||||
[JsonObject]
|
||||
public class VolumeInfo : GeneralPointPropInfo
|
||||
public class VolumeInfo : GeneralPropInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The radius of this volume.
|
||||
/// The radius of this volume, if a shape is not specified.
|
||||
/// </summary>
|
||||
[DefaultValue(1f)] public float radius = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// The shape of this volume. Defaults to a sphere with a radius of `radius` if not specified.
|
||||
/// </summary>
|
||||
public ShapeInfo shape;
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,6 +27,11 @@ namespace NewHorizons.External.Modules.Volumes
|
||||
/// </summary>
|
||||
public FluidVolumeInfo[] fluidVolumes;
|
||||
|
||||
/// <summary>
|
||||
/// Add force volumes to this planet.
|
||||
/// </summary>
|
||||
public ForceModule forces;
|
||||
|
||||
/// <summary>
|
||||
/// Add hazard volumes to this planet.
|
||||
/// Causes damage to player when inside this volume.
|
||||
@ -101,6 +106,13 @@ namespace NewHorizons.External.Modules.Volumes
|
||||
/// </summary>
|
||||
public SpeedTrapVolumeInfo[] speedTrapVolumes;
|
||||
|
||||
/// <summary>
|
||||
/// Add speed limiter volumes to this planet.
|
||||
/// Slows down the player, ship, and probe when they enter this volume.
|
||||
/// Used on the Stranger in DLC.
|
||||
/// </summary>
|
||||
public SpeedLimiterVolumeInfo[] speedLimiterVolumes;
|
||||
|
||||
/// <summary>
|
||||
/// Add visor effect volumes to this planet.
|
||||
/// </summary>
|
||||
|
||||
@ -13,6 +13,8 @@ namespace NewHorizons.External.SerializableEnums
|
||||
|
||||
[EnumMember(Value = @"kazoo")] Kazoo = 2,
|
||||
|
||||
[EnumMember(Value = @"none")] None = 3
|
||||
[EnumMember(Value = @"custom")] Custom = 3,
|
||||
|
||||
[EnumMember(Value = @"none")] None = 4
|
||||
}
|
||||
}
|
||||
|
||||
@ -657,7 +657,7 @@ namespace NewHorizons.Handlers
|
||||
|
||||
if (body.Config.CometTail != null)
|
||||
{
|
||||
CometTailBuilder.Make(go, sector, body.Config.CometTail, body.Config);
|
||||
CometTailBuilder.Make(go, sector, body.Config.CometTail, body.Config, go.GetComponent<AstroObject>());
|
||||
}
|
||||
|
||||
if (body.Config.Lava != null)
|
||||
|
||||
@ -382,6 +382,7 @@ namespace NewHorizons
|
||||
ProjectionBuilder.InitPrefabs();
|
||||
CloakBuilder.InitPrefab();
|
||||
RaftBuilder.InitPrefab();
|
||||
RaftDockBuilder.InitPrefab();
|
||||
DreamCampfireBuilder.InitPrefab();
|
||||
DreamArrivalPointBuilder.InitPrefab();
|
||||
}
|
||||
@ -849,7 +850,7 @@ namespace NewHorizons
|
||||
}
|
||||
if (addonConfig.gameOver != null)
|
||||
{
|
||||
NHGameOverManager.gameOvers[mod.ModHelper.Manifest.UniqueName] = addonConfig.gameOver;
|
||||
NHGameOverManager.gameOvers[mod] = addonConfig.gameOver;
|
||||
}
|
||||
|
||||
AddonConfigs[mod] = addonConfig;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<Project>
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<OutputPath>$(AppData)\OuterWildsModManager\OWML\Mods\xen.NewHorizons</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
@ -1,17 +1,30 @@
|
||||
using HarmonyLib;
|
||||
using NewHorizons.Handlers;
|
||||
using System;
|
||||
|
||||
namespace NewHorizons.Patches.CreditsScenePatches
|
||||
{
|
||||
[HarmonyPatch(typeof(Credits))]
|
||||
public static class CreditsPatches
|
||||
{
|
||||
public static event EventHandler CreditsBuilt; // Used in NHGameOverManager to patch credits music and scroll speed
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(Credits.Start))]
|
||||
public static void Credits_Start(Credits __instance)
|
||||
{
|
||||
CreditsHandler.AddCredits(__instance);
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(Credits.BuildCredits))]
|
||||
public static void Credits_BuildCredits_Post(Credits __instance)
|
||||
{
|
||||
// Do things BuildCredits() normally does
|
||||
|
||||
// Fire event once finished
|
||||
CreditsBuilt?.Invoke(__instance, new EventArgs());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -75,30 +75,41 @@ namespace NewHorizons.Patches.EchoesOfTheEyePatches
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(RaftController.UpdateMoveToTarget))]
|
||||
public static void UpdateMoveToTarget(RaftController __instance)
|
||||
public static bool UpdateMoveToTarget(RaftController __instance)
|
||||
{
|
||||
OWRigidbody raftBody = __instance._raftBody;
|
||||
OWRigidbody origParentBody = raftBody.GetOrigParentBody();
|
||||
Transform transform = origParentBody.transform;
|
||||
Vector3 position = transform.TransformPoint(__instance._targetLocalPosition);
|
||||
Vector3 distance = position - raftBody.GetPosition();
|
||||
float speed = Mathf.Min(__instance._targetSpeed, distance.magnitude / Time.deltaTime);
|
||||
Vector3 pointVelocity = raftBody.GetOrigParentBody().GetPointVelocity(raftBody.GetPosition());
|
||||
raftBody.SetVelocity(pointVelocity + (distance.normalized * speed));
|
||||
float t = (__instance.currentDistanceLerp = Mathf.InverseLerp(__instance._startDistance, 0.001f, distance.magnitude));
|
||||
var st = Mathf.SmoothStep(0f, 1f, t);
|
||||
// If it has a riverFluid volume then its a regular stranger one
|
||||
if (__instance._movingToTarget && __instance._riverFluid == null)
|
||||
var isStranger = __instance._riverFluid != null;
|
||||
// Base game threshold has this at 1f (after doing smoothstep on it)
|
||||
// For whatever reason it never hits that for NH planets (probably since they're moving so much compared to the steady velocity of the Stranger)
|
||||
// Might break for somebody with a wacky spinning planet in which case we can adjust this or add some kind of fallback (i.e., wait x seconds and then just say its there)
|
||||
// Fixes #1005
|
||||
if (isStranger ? (st >= 1f) : (t > 0.999f))
|
||||
{
|
||||
OWRigidbody raftBody = __instance._raftBody;
|
||||
OWRigidbody origParentBody = __instance._raftBody.GetOrigParentBody();
|
||||
Transform transform = origParentBody.transform;
|
||||
Vector3 vector = transform.TransformPoint(__instance._targetLocalPosition);
|
||||
|
||||
// Base game threshold has this at 1f (after doing smoothstep on it)
|
||||
// For whatever reason it never hits that for NH planets (probably since they're moving so much compared to the steady velocity of the Stranger)
|
||||
// Might break for somebody with a wacky spinning planet in which case we can adjust this or add some kind of fallback (i.e., wait x seconds and then just say its there)
|
||||
// Fixes #1005
|
||||
if (__instance.currentDistanceLerp > 0.999f)
|
||||
{
|
||||
raftBody.SetPosition(vector);
|
||||
raftBody.SetRotation(transform.rotation * __instance._targetLocalRotation);
|
||||
__instance.StopMovingToTarget();
|
||||
__instance.OnArriveAtTarget.Invoke();
|
||||
}
|
||||
raftBody.SetPosition(position);
|
||||
raftBody.SetRotation(transform.rotation * __instance._targetLocalRotation);
|
||||
__instance.StopMovingToTarget();
|
||||
__instance.OnArriveAtTarget.Invoke();
|
||||
}
|
||||
else
|
||||
{
|
||||
Quaternion quaternion = Quaternion.Slerp(__instance._startLocalRotation, __instance._targetLocalRotation, st);
|
||||
Quaternion toRotation = transform.rotation * quaternion;
|
||||
Vector3 vector4 = OWPhysics.FromToAngularVelocity(raftBody.GetRotation(), toRotation);
|
||||
raftBody.SetAngularVelocity(origParentBody.GetAngularVelocity() + vector4);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,6 +103,27 @@
|
||||
"type": "string",
|
||||
"description": "Condition that must be true for this game over to trigger. If this is on a LoadCreditsVolume, leave empty to always trigger this game over.\nNote this is a regular dialogue condition, not a persistent condition."
|
||||
},
|
||||
"audio": {
|
||||
"type": "string",
|
||||
"description": "The audio to use for the credits music. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list.\nCredits will be silent unless this attribute is specified.\nNote: only applies when creditsType is set to \"custom\"."
|
||||
},
|
||||
"audioVolume": {
|
||||
"type": "number",
|
||||
"description": "The length of the fade in and out for the credits music.\nNote: only applies when creditsType is set to \"custom\".",
|
||||
"format": "float",
|
||||
"default": 1.0
|
||||
},
|
||||
"audioLooping": {
|
||||
"type": "boolean",
|
||||
"description": "Determines if the credits music should loop.\nNote: only applies when creditsType is set to \"custom\".",
|
||||
"default": false
|
||||
},
|
||||
"length": {
|
||||
"type": "number",
|
||||
"description": "Duration of the credits scroll in seconds.\nNote: only applies when creditsType is set to \"custom\".",
|
||||
"format": "float",
|
||||
"default": 120.0
|
||||
},
|
||||
"creditsType": {
|
||||
"description": "The type of credits that will run after the game over message is shown",
|
||||
"default": "fast",
|
||||
@ -152,12 +173,14 @@
|
||||
"Fast",
|
||||
"Final",
|
||||
"Kazoo",
|
||||
"Custom",
|
||||
"None"
|
||||
],
|
||||
"enum": [
|
||||
"fast",
|
||||
"final",
|
||||
"kazoo",
|
||||
"custom",
|
||||
"none"
|
||||
]
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -27,23 +27,34 @@ namespace NewHorizons.Utility.Files
|
||||
source._clipArrayLength = 0;
|
||||
source._clipSelectionOnPlay = OWAudioSource.ClipSelectionOnPlay.MANUAL;
|
||||
source.clip = clip;
|
||||
NHLogger.LogVerbose($"[{nameof(AudioUtilities)}] : Audio {audio} was loaded from a file");
|
||||
return;
|
||||
}
|
||||
catch
|
||||
{
|
||||
NHLogger.LogError($"Could not load file {audio}");
|
||||
NHLogger.LogError($"[{nameof(AudioUtilities)}] : Could not load file {audio}");
|
||||
}
|
||||
}
|
||||
|
||||
if (EnumUtils.TryParse(audio, out AudioType type))
|
||||
{
|
||||
source._audioLibraryClip = type;
|
||||
NHLogger.LogVerbose($"[{nameof(AudioUtilities)}] : Audio {audio} was an AudioType enum");
|
||||
}
|
||||
else
|
||||
{
|
||||
var audioClip = SearchUtilities.FindResourceOfTypeAndName<AudioClip>(audio);
|
||||
if (audioClip == null) NHLogger.Log($"Couldn't find audio clip {audio}");
|
||||
else source.clip = audioClip;
|
||||
if (audioClip == null)
|
||||
{
|
||||
NHLogger.LogError($"[{nameof(AudioUtilities)}] : Couldn't find audio clip {audio}");
|
||||
}
|
||||
else
|
||||
{
|
||||
NHLogger.LogVerbose($"[{nameof(AudioUtilities)}] : Audio {audio} was an AudioClip resource");
|
||||
// Else if this is set it will try to change the clip back when it starts playing
|
||||
source._audioLibraryClip = AudioType.None;
|
||||
source.clip = audioClip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -274,6 +274,14 @@ namespace NewHorizons.Utility
|
||||
return UnityEngine.Object.Instantiate(original);
|
||||
}
|
||||
|
||||
public static GameObject Instantiate(this GameObject original, Vector3 localPosition, Quaternion localRotation, Transform parent)
|
||||
{
|
||||
var copy = UnityEngine.Object.Instantiate(original, parent, false);
|
||||
copy.transform.localPosition = localPosition;
|
||||
copy.transform.localRotation = localRotation;
|
||||
return copy;
|
||||
}
|
||||
|
||||
public static T DontDestroyOnLoad<T>(this T target) where T : UnityEngine.Object
|
||||
{
|
||||
UnityEngine.Object.DontDestroyOnLoad(target);
|
||||
@ -445,6 +453,11 @@ namespace NewHorizons.Utility
|
||||
return globalMusicController._endTimesSource.clip.length;
|
||||
}
|
||||
|
||||
public static string GetKey(this AstroObject ao)
|
||||
{
|
||||
return ao._name == AstroObject.Name.CustomString ? ao.GetCustomName() : ao._name.ToString();
|
||||
}
|
||||
|
||||
public static CodeMatcher LogInstructions(this CodeMatcher matcher, string prefix)
|
||||
{
|
||||
matcher.InstructionEnumeration().LogInstructions(prefix);
|
||||
|
||||
@ -65,7 +65,7 @@ namespace NewHorizons.Utility.OuterWilds
|
||||
|
||||
public static void RegisterCustomAstroObject(AstroObject ao)
|
||||
{
|
||||
var key = ao._name == AstroObject.Name.CustomString ? ao.GetCustomName() : ao._name.ToString();
|
||||
var key = ao.GetKey();
|
||||
|
||||
if (_customAstroObjectDictionary.ContainsKey(key))
|
||||
{
|
||||
@ -81,7 +81,7 @@ namespace NewHorizons.Utility.OuterWilds
|
||||
|
||||
public static void DeregisterCustomAstroObject(AstroObject ao)
|
||||
{
|
||||
var key = ao._name == AstroObject.Name.CustomString ? ao.GetCustomName() : ao._name.ToString();
|
||||
var key = ao.GetKey();
|
||||
_customAstroObjectDictionary.Remove(key);
|
||||
}
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
"author": "xen, Bwc9876, JohnCorby, MegaPiggy, and friends",
|
||||
"name": "New Horizons",
|
||||
"uniqueName": "xen.NewHorizons",
|
||||
"version": "1.27.3",
|
||||
"version": "1.28.0",
|
||||
"owmlVersion": "2.12.1",
|
||||
"dependencies": [ "JohnCorby.VanillaFix", "xen.CommonCameraUtility", "dgarro.CustomShipLogModes" ],
|
||||
"conflicts": [ "PacificEngine.OW_CommonResources" ],
|
||||
|
||||
@ -20,12 +20,12 @@ You can use [Unity Explorer](https://outerwildsmods.com/mods/unityexplorer) to t
|
||||
|
||||
## Asset Bundles
|
||||
|
||||
There is an [old unity template](https://github.com/xen-42/outer-wilds-unity-template) and a [new one](https://github.com/ow-mods/outer-wilds-unity-wiki/wiki#outer-wilds-unity-assets)
|
||||
There is an [old unity template](https://github.com/xen-42/outer-wilds-unity-template) and a [new one](https://github.com/ow-mods/outer-wilds-unity-wiki/wiki/Tools-%E2%80%90-Outer-Wilds-Unity-Assets-repository)
|
||||
|
||||
The project contains ripped versions of all the game scripts, meaning you can put things like DirectionalForceVolumes in your Unity project to have artificial gravity volumes loaded right into the game.\
|
||||
Either one works, but the new one has more tools and better versions of the scripts (in exchange for being invite-only).
|
||||
|
||||
Read [this guide](https://github.com/ow-mods/outer-wilds-unity-wiki/wiki/Tutorials-%E2%80%90-Using-asset-bundles) on how to work with asset bundles in editor.
|
||||
Read [this guide](https://github.com/ow-mods/outer-wilds-unity-wiki/wiki/Tutorials-%E2%80%90-Using-AssetBundles) on how to work with asset bundles in editor.
|
||||
|
||||
## Importing a planet's surface from Unity
|
||||
|
||||
|
||||
@ -19,5 +19,5 @@ which interact poorly with the fluid detector and can mess up the movement of th
|
||||
Either clear the .nhcache files or enable Debug mode to always regenerate the text cache.
|
||||
|
||||
## Prop placer is gone!
|
||||
This is not a bug, actually. We removed prop placer because it was inconsistent and buggy, and no one in years cared enough to fix it.
|
||||
Use the debug raycast button and Unity Explorer to place your props, or otherwise work in unity editor.
|
||||
It has been moved to a [separate mod](https://outerwildsmods.com/mods/propplacer/).
|
||||
Use it in addition to the debug raycast button and Unity Explorer to place your props, or otherwise work in unity editor.
|
||||
|
||||
72
docs/src/content/docs/guides/volumes.md
Normal file
72
docs/src/content/docs/guides/volumes.md
Normal file
@ -0,0 +1,72 @@
|
||||
---
|
||||
title: Volumes
|
||||
description: Guide to making volumes in New Horizons
|
||||
---
|
||||
|
||||
Volumes are invisible 3D "zones" or "triggers" that cause various effects when objects enter or leave them. For example, `oxygenVolumes` refill the player's oxygen when they enter (used for the various oxygen-generating trees in the game), `forces.directionalVolumes` push players and other physics objects in a specific direction (used by both Nomai artificial gravity surfaces and tractor beams), `revealVolumes` unlock ship log facts when the player enters or observes them (used everywhere in the game), and more.
|
||||
|
||||
New Horizons makes adding volumes to your planets easy; just specify them like you would [for a prop](/guides/details/) but under `Volumes` instead of `Props`. For example, to add an oxygen volume at certain location:
|
||||
|
||||
```json title="planets/My Cool Planet.json"
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/Outer-Wilds-New-Horizons/new-horizons/main/NewHorizons/Schemas/body_schema.json",
|
||||
"name" : "My Cool Planet",
|
||||
"Volumes": {
|
||||
"oxygenVolumes": [
|
||||
{
|
||||
"position": {"x": 399.4909, "y": -1.562098, "z": 20.11444},
|
||||
"radius": 30,
|
||||
"treeVolume": true,
|
||||
"playRefillAudio": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Listing out every type of volume is outside the scope of this guide, but you can see every supported type of volume and the properties they need in [the VolumesModule schema](/schemas/body-schema/defs/volumesmodule/).
|
||||
|
||||
## Volume Shapes
|
||||
|
||||
By default, volumes are spherical, and you can specify the radius of that sphere with the `radius` property. If you want to use a different shape for your volume, such as a box or capsule, you can specify your volume's `shape` like so:
|
||||
|
||||
```json title="planets/My Cool Planet.json"
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/Outer-Wilds-New-Horizons/new-horizons/main/NewHorizons/Schemas/body_schema.json",
|
||||
"name" : "My Cool Planet",
|
||||
"Volumes": {
|
||||
"forces": {
|
||||
"directionalVolumes": [
|
||||
{
|
||||
"rename": "ArtificialGravitySurface",
|
||||
"force": 8,
|
||||
"playGravityCrystalAudio": true,
|
||||
"shape": {
|
||||
"type": "box",
|
||||
"size": {
|
||||
"x": 15.0,
|
||||
"y": 10.0,
|
||||
"z": 5.0
|
||||
},
|
||||
"offset": {
|
||||
"x": 0,
|
||||
"y": 5.0,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
"position": { "x": 0, "y": -110, "z": 0 },
|
||||
"rotation": { "x": 180, "y": 0, "z": 0 }
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The supported shape types are: `sphere`, `box`, `capsule`, `cylinder`, `cone`, `hemisphere`, `hemicapsule`, and `ring`. See [the ShapeInfo schema](/schemas/body-schema/defs/shapeinfo/) for the full list of properties available to define each shape.
|
||||
|
||||
Note that `sphere`, `box`, and `capsule` shapes are more reliable and efficient than other shapes, so prefer using them whenever possible.
|
||||
|
||||
### Debugging
|
||||
|
||||
To visualize the shapes of your volumes in-game, use the [Collider Visualizer mod](https://outerwildsmods.com/mods/collidervisualizer/). It will display a wireframe of the shapes around you so you can see precisely where they are and reposition or resize them as needed.
|
||||
Loading…
x
Reference in New Issue
Block a user