Merge branch 'dev' into hawkbar-colliders

This commit is contained in:
xen-42 2025-03-15 15:06:09 -04:00
commit 9a458ed7a1
20 changed files with 159 additions and 45 deletions

View File

@ -62,8 +62,7 @@ jobs:
name: NewHorizons-Schemas-${{ inputs.build_type }} name: NewHorizons-Schemas-${{ inputs.build_type }}
path: .\NewHorizons\Schemas path: .\NewHorizons\Schemas
- name: Verify Changed Schemas - name: Check Changed Schemas
uses: tj-actions/verify-changed-files@v20
id: changed_files id: changed_files
with: run: |
files: NewHorizons/Schemas/** echo "files_changed=$(git diff --exit-code NewHorizons/Schemas 2>&1>$null && echo false || echo true)" >> $Env:GITHUB_OUTPUT

View File

@ -5,6 +5,7 @@ using NewHorizons.External;
using NewHorizons.External.Modules; using NewHorizons.External.Modules;
using NewHorizons.External.Modules.Props; using NewHorizons.External.Modules.Props;
using NewHorizons.Utility; using NewHorizons.Utility;
using NewHorizons.Utility.DebugTools;
using NewHorizons.Utility.Files; using NewHorizons.Utility.Files;
using NewHorizons.Utility.OWML; using NewHorizons.Utility.OWML;
using OWML.Common; using OWML.Common;
@ -189,6 +190,7 @@ namespace NewHorizons.Builder.Body
outerFogWarpVolume._linkedInnerWarpVolume = null; outerFogWarpVolume._linkedInnerWarpVolume = null;
outerFogWarpVolume._name = OuterFogWarpVolume.Name.None; outerFogWarpVolume._name = OuterFogWarpVolume.Name.None;
outerFogWarpVolume._sector = sector; outerFogWarpVolume._sector = sector;
exitWarps.GetAddComponent<DebugFogWarp>().fogWarpVolume = outerFogWarpVolume;
PairExit(config.linksTo, outerFogWarpVolume); PairExit(config.linksTo, outerFogWarpVolume);

View File

@ -25,5 +25,8 @@ public static class GroupsBuilder
go.GetAddComponent<SectorCullGroup>()._sector = sector; go.GetAddComponent<SectorCullGroup>()._sector = sector;
go.GetAddComponent<SectorCollisionGroup>()._sector = sector; go.GetAddComponent<SectorCollisionGroup>()._sector = sector;
go.GetAddComponent<SectorLightsCullGroup>()._sector = sector; go.GetAddComponent<SectorLightsCullGroup>()._sector = sector;
// SectorCollisionGroup is unique among the sector groups because it only attaches its event listener on Start() instead of Awake(), so if the detail gets immediately deactivated then it never gets attached. To avoid this, we'll attach the event listener manually (even if it means getting attached twice).
sector.OnSectorOccupantsUpdated += go.GetComponent<SectorCollisionGroup>().OnSectorOccupantsUpdated;
} }
} }

View File

@ -15,7 +15,7 @@ namespace NewHorizons.Builder.General
{ {
var module = config.MapMarker; var module = config.MapMarker;
NHMapMarker mapMarker = body.AddComponent<NHMapMarker>(); NHMapMarker mapMarker = body.AddComponent<NHMapMarker>();
mapMarker._labelID = (UITextType)TranslationHandler.AddUI(config.name); mapMarker._labelID = (UITextType)TranslationHandler.AddUI(config.name, true);
var markerType = MapMarker.MarkerType.Planet; var markerType = MapMarker.MarkerType.Planet;

View File

@ -69,6 +69,8 @@ namespace NewHorizons.Builder.General
PlayerSpawn = playerSpawn; PlayerSpawn = playerSpawn;
PlayerSpawnInfo = point; PlayerSpawnInfo = point;
} }
spawnGO.SetActive(true);
} }
} }
@ -77,7 +79,6 @@ namespace NewHorizons.Builder.General
foreach (var point in module.shipSpawnPoints) foreach (var point in module.shipSpawnPoints)
{ {
var spawnGO = GeneralPropBuilder.MakeNew("ShipSpawnPoint", planetGO, null, point); var spawnGO = GeneralPropBuilder.MakeNew("ShipSpawnPoint", planetGO, null, point);
spawnGO.SetActive(false);
spawnGO.layer = Layer.PlayerSafetyCollider; spawnGO.layer = Layer.PlayerSafetyCollider;
var shipSpawn = spawnGO.AddComponent<SpawnPoint>(); var shipSpawn = spawnGO.AddComponent<SpawnPoint>();

View File

@ -4,6 +4,7 @@ using NewHorizons.External.Configs;
using NewHorizons.External.Modules.Props.Audio; using NewHorizons.External.Modules.Props.Audio;
using NewHorizons.Handlers; using NewHorizons.Handlers;
using NewHorizons.Utility; using NewHorizons.Utility;
using NewHorizons.Utility.DebugTools;
using NewHorizons.Utility.OuterWilds; using NewHorizons.Utility.OuterWilds;
using NewHorizons.Utility.OWML; using NewHorizons.Utility.OWML;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -204,6 +205,10 @@ namespace NewHorizons.Builder.Props
foreach (var fogWarpVolume in brambleNode.GetComponentsInChildren<FogWarpVolume>(true).Append(brambleNode.GetComponent<FogWarpVolume>())) foreach (var fogWarpVolume in brambleNode.GetComponentsInChildren<FogWarpVolume>(true).Append(brambleNode.GetComponent<FogWarpVolume>()))
{ {
_nhFogWarpVolumes.Add(fogWarpVolume); _nhFogWarpVolumes.Add(fogWarpVolume);
if (fogWarpVolume is SphericalFogWarpVolume sphericalFogWarpVolume)
{
fogWarpVolume.gameObject.GetAddComponent<DebugFogWarp>().fogWarpVolume = sphericalFogWarpVolume;
}
} }
var innerFogWarpVolume = brambleNode.GetComponent<InnerFogWarpVolume>(); var innerFogWarpVolume = brambleNode.GetComponent<InnerFogWarpVolume>();

View File

@ -356,7 +356,10 @@ namespace NewHorizons.Builder.Props
singleLightSensor._sector.OnSectorOccupantsUpdated -= singleLightSensor.OnSectorOccupantsUpdated; singleLightSensor._sector.OnSectorOccupantsUpdated -= singleLightSensor.OnSectorOccupantsUpdated;
} }
singleLightSensor._sector = sector; singleLightSensor._sector = sector;
singleLightSensor._sector.OnSectorOccupantsUpdated += singleLightSensor.OnSectorOccupantsUpdated; if (singleLightSensor._sector != null)
{
singleLightSensor._sector.OnSectorOccupantsUpdated += singleLightSensor.OnSectorOccupantsUpdated;
}
} }
} }

View File

@ -69,13 +69,17 @@ namespace NewHorizons.Builder.Props
{ {
(GameObject go, QuantumDetailInfo detail)[] propsInGroup = quantumGroup.details.Select(x => (DetailBuilder.GetGameObjectFromDetailInfo(x), x)).ToArray(); (GameObject go, QuantumDetailInfo detail)[] propsInGroup = quantumGroup.details.Select(x => (DetailBuilder.GetGameObjectFromDetailInfo(x), x)).ToArray();
GameObject specialProp = null;
QuantumDetailInfo specialInfo = null; QuantumDetailInfo specialInfo = null;
if (propsInGroup.Length == quantumGroup.sockets.Length) if (propsInGroup.Length == quantumGroup.sockets.Length)
{ {
// Special case! // Special case!
specialProp = propsInGroup.Last().go; propsInGroup.Last().go.SetActive(false);
// Will be manually positioned on the sockets anyway
specialInfo = propsInGroup.Last().detail; specialInfo = propsInGroup.Last().detail;
specialInfo.parentPath = string.Empty;
specialInfo.isRelativeToParent = false;
var propsInGroupList = propsInGroup.ToList(); var propsInGroupList = propsInGroup.ToList();
propsInGroupList.RemoveAt(propsInGroup.Length - 1); propsInGroupList.RemoveAt(propsInGroup.Length - 1);
propsInGroup = propsInGroupList.ToArray(); propsInGroup = propsInGroupList.ToArray();
@ -117,13 +121,13 @@ namespace NewHorizons.Builder.Props
prop.go.SetActive(true); prop.go.SetActive(true);
} }
if (specialProp != null) if (specialInfo != null)
{ {
// Can't have 4 objects in 4 slots // Can't have 4 objects in 4 slots
// Instead we have a duplicate of the final object for each slot, which appears when that slot is "empty" // Instead we have a duplicate of the final object for each slot, which appears when that slot is "empty"
for (int i = 0; i < sockets.Length; i++) for (int i = 0; i < sockets.Length; i++)
{ {
var emptySocketObject = DetailBuilder.Make(planetGO, sector, mod, specialProp, new DetailInfo()); var emptySocketObject = DetailBuilder.Make(planetGO, sector, mod, new DetailInfo(specialInfo));
var socket = sockets[i]; var socket = sockets[i];
socket._emptySocketObject = emptySocketObject; socket._emptySocketObject = emptySocketObject;
emptySocketObject.SetActive(socket._quantumObject == null); emptySocketObject.SetActive(socket._quantumObject == null);

View File

@ -669,14 +669,12 @@ namespace NewHorizons.External.Configs
{ {
if (!string.IsNullOrEmpty(volume.gameOverText)) if (!string.IsNullOrEmpty(volume.gameOverText))
{ {
if (volume.gameOver == null) volume.gameOver ??= new();
{
volume.gameOver = new();
}
volume.gameOver.text = volume.gameOverText; volume.gameOver.text = volume.gameOverText;
} }
if (volume.creditsType != null) if (volume.creditsType != null)
{ {
volume.gameOver ??= new();
volume.gameOver.creditsType = (SerializableEnums.NHCreditsType)volume.creditsType; volume.gameOver.creditsType = (SerializableEnums.NHCreditsType)volume.creditsType;
} }
} }

View File

@ -6,15 +6,24 @@ using Newtonsoft.Json;
namespace NewHorizons.External.Configs namespace NewHorizons.External.Configs
{ {
/// <summary>
/// Allows you to configure the title screen with custom music, skyboxes, and loading props from asset bundles.
/// You can define a list of title screen configurations, with different persistent condition/ship log facts required to display them.
/// </summary>
[JsonObject] [JsonObject]
public class TitleScreenConfig public class TitleScreenConfig
{ {
/// <summary> /// <summary>
/// Create title screens /// Create title screens.
/// The last title screen in the list with its display conditions (persistent condition and/or ship log) met will be displayed if this mod
/// is chosen to be shown on the main menu.
/// </summary> /// </summary>
public TitleScreenInfo[] titleScreens = new TitleScreenInfo[0]; public TitleScreenInfo[] titleScreens = new TitleScreenInfo[0];
} }
/// <summary>
/// A single title screen configuration
/// </summary>
[JsonObject] [JsonObject]
public class TitleScreenInfo public class TitleScreenInfo
{ {

View File

@ -96,7 +96,7 @@ namespace NewHorizons.Handlers
vesselAO._type = AstroObject.Type.SpaceStation; vesselAO._type = AstroObject.Type.SpaceStation;
vesselAO.Register(); vesselAO.Register();
vesselMapMarker._markerType = MapMarker.MarkerType.Moon; vesselMapMarker._markerType = MapMarker.MarkerType.Moon;
vesselMapMarker._labelID = (UITextType)TranslationHandler.AddUI("Vessel"); vesselMapMarker._labelID = (UITextType)TranslationHandler.AddUI("Vessel", true);
RFVolumeBuilder.Make(vessel, vesselBody, 600, new External.Modules.ReferenceFrameModule { localPosition = new MVector3(0, 0, -207.375f) }); RFVolumeBuilder.Make(vessel, vesselBody, 600, new External.Modules.ReferenceFrameModule { localPosition = new MVector3(0, 0, -207.375f) });
// Resize vessel sector so that the vessel is fully collidable. // Resize vessel sector so that the vessel is fully collidable.

View File

@ -205,11 +205,14 @@ namespace NewHorizons.Handlers
TextTranslation.Get().m_table.theShipLogTable[key] = value; TextTranslation.Get().m_table.theShipLogTable[key] = value;
} }
public static int AddUI(string rawText) public static int AddUI(string rawText) => AddUI(rawText, false);
public static int AddUI(string rawText, bool upper)
{ {
var uiTable = TextTranslation.Get().m_table.theUITable; var uiTable = TextTranslation.Get().m_table.theUITable;
var text = GetTranslation(rawText, TextType.UI).ToUpperFixed(); var text = GetTranslation(rawText, TextType.UI);
if (upper) text = text.ToUpperFixed();
var key = uiTable.Keys.Max() + 1; var key = uiTable.Keys.Max() + 1;
try try

View File

@ -210,7 +210,7 @@ namespace NewHorizons.Handlers
vesselWarpController._whiteHoleOneShot = vesselWarpController._whiteHole.transform.parent.Find("WhiteHoleAudio_OneShot").GetComponent<OWAudioSource>(); vesselWarpController._whiteHoleOneShot = vesselWarpController._whiteHole.transform.parent.Find("WhiteHoleAudio_OneShot").GetComponent<OWAudioSource>();
vesselWarpController._whiteHole._startActive = true; vesselWarpController._whiteHole._startActive = true;
vesselObject.GetComponent<MapMarker>()._labelID = (UITextType)TranslationHandler.AddUI("Vessel"); vesselObject.GetComponent<MapMarker>()._labelID = (UITextType)TranslationHandler.AddUI("Vessel", true);
var hasParentBody = !string.IsNullOrEmpty(system.Config.Vessel?.vesselSpawn?.parentBody); var hasParentBody = !string.IsNullOrEmpty(system.Config.Vessel?.vesselSpawn?.parentBody);
var hasPhysics = system.Config.Vessel?.hasPhysics ?? !hasParentBody; var hasPhysics = system.Config.Vessel?.hasPhysics ?? !hasParentBody;

View File

@ -46,6 +46,7 @@ namespace NewHorizons
// Settings // Settings
public static bool Debug { get; private set; } public static bool Debug { get; private set; }
public static bool VisualizeQuantumObjects { get; private set; } public static bool VisualizeQuantumObjects { get; private set; }
public static bool VisualizeBrambleVolumeNames { get; private set; }
public static bool VerboseLogs { get; private set; } public static bool VerboseLogs { get; private set; }
public static bool SequentialPreCaching { get; private set; } public static bool SequentialPreCaching { get; private set; }
public static bool CustomTitleScreen { get; private set; } public static bool CustomTitleScreen { get; private set; }
@ -138,6 +139,7 @@ namespace NewHorizons
Debug = config.GetSettingsValue<bool>(nameof(Debug)); Debug = config.GetSettingsValue<bool>(nameof(Debug));
VisualizeQuantumObjects = config.GetSettingsValue<bool>(nameof(VisualizeQuantumObjects)); VisualizeQuantumObjects = config.GetSettingsValue<bool>(nameof(VisualizeQuantumObjects));
VisualizeBrambleVolumeNames = config.GetSettingsValue<bool>(nameof(VisualizeBrambleVolumeNames));
VerboseLogs = config.GetSettingsValue<bool>(nameof(VerboseLogs)); VerboseLogs = config.GetSettingsValue<bool>(nameof(VerboseLogs));
SequentialPreCaching = config.GetSettingsValue<bool>(nameof(SequentialPreCaching)); SequentialPreCaching = config.GetSettingsValue<bool>(nameof(SequentialPreCaching));

View File

@ -0,0 +1,24 @@
using HarmonyLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NewHorizons.Patches.EchoesOfTheEyePatches
{
[HarmonyPatch(typeof(SingleLightSensor))]
public static class SingleLightSensorPatches
{
[HarmonyPostfix]
[HarmonyPatch(nameof(SingleLightSensor.Start))]
public static void Start(SingleLightSensor __instance)
{
// SingleLightSensor assumes that the sector will be empty when it starts and disables itself, but this may not be true if it starts disabled and is activated later, or spawned via the API
if (__instance._sector && __instance._sector.ContainsAnyOccupants(DynamicOccupant.Player | DynamicOccupant.Probe))
{
__instance.OnSectorOccupantsUpdated();
}
}
}
}

View File

@ -2,11 +2,12 @@
"$schema": "http://json-schema.org/draft-04/schema#", "$schema": "http://json-schema.org/draft-04/schema#",
"title": "Title Screen Schema", "title": "Title Screen Schema",
"type": "object", "type": "object",
"description": "Allows you to configure the title screen with custom music, skyboxes, and loading props from asset bundles.\nYou can define a list of title screen configurations, with different persistent condition/ship log facts required to display them.",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"titleScreens": { "titleScreens": {
"type": "array", "type": "array",
"description": "Create title screens", "description": "Create title screens.\nThe last title screen in the list with its display conditions (persistent condition and/or ship log) met will be displayed if this mod\nis chosen to be shown on the main menu.",
"items": { "items": {
"$ref": "#/definitions/TitleScreenInfo" "$ref": "#/definitions/TitleScreenInfo"
} }
@ -19,6 +20,7 @@
"definitions": { "definitions": {
"TitleScreenInfo": { "TitleScreenInfo": {
"type": "object", "type": "object",
"description": "A single title screen configuration",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"menuTextTint": { "menuTextTint": {

View File

@ -0,0 +1,52 @@
using UnityEngine;
namespace NewHorizons.Utility.DebugTools
{
/// <summary>
/// Adapted from Survivors https://github.com/Hawkbat/ow-mod-jam-2/blob/main/EscapePodFour.cs#L197
/// </summary>
[RequireComponent(typeof(SphericalFogWarpVolume))]
public class DebugFogWarp : MonoBehaviour
{
public SphericalFogWarpVolume fogWarpVolume;
public void OnGUI()
{
if (Main.Debug && Main.VisualizeBrambleVolumeNames && fogWarpVolume != null)
{
DrawWorldLabel(fogWarpVolume, fogWarpVolume.name);
if (fogWarpVolume._exits != null)
{
foreach (var e in fogWarpVolume._exits)
{
if (e != null)
{
DrawWorldLabel(fogWarpVolume.GetExitPosition(e), e.name);
}
}
}
}
}
public void DrawWorldLabel(Component component, string text)
{
DrawWorldLabel(component.transform.position, text);
}
public void DrawWorldLabel(Vector3 worldPos, string text)
{
var c = Locator.GetPlayerCamera();
var d = Vector3.Distance(c.transform.position, worldPos);
if (d > 1000f) return;
GUI.Label(new Rect(WorldToGui(worldPos), new Vector2(500f, 20f)), text);
}
public Vector2 WorldToGui(Vector3 wp)
{
var c = Locator.GetPlayerCamera();
var sp = c.WorldToScreenPoint(wp);
if (sp.z < 0) return new Vector2(Screen.width, Screen.height);
var gp = new Vector2(sp.x, Screen.height - sp.y);
return gp;
}
}
}

View File

@ -22,6 +22,12 @@
"value": false, "value": false,
"tooltip": "Draws boundaries around quantum objects when Debug mode is on." "tooltip": "Draws boundaries around quantum objects when Debug mode is on."
}, },
"VisualizeBrambleVolumeNames": {
"title": "Visualize Bramble Volume Names",
"type": "toggle",
"value": false,
"tooltip": "Adds a label to all custom spherical fog warp volumes and entrances/exits when Debug mode is on."
},
"VerboseLogs": { "VerboseLogs": {
"title": "Verbose Logs", "title": "Verbose Logs",
"type": "toggle", "type": "toggle",

View File

@ -1,10 +1,10 @@
{ {
"$schema": "https://raw.githubusercontent.com/amazingalek/owml/master/schemas/manifest_schema.json", "$schema": "https://raw.githubusercontent.com/amazingalek/owml/master/schemas/manifest_schema.json",
"filename": "NewHorizons.dll", "filename": "NewHorizons.dll",
"author": "xen, Bwc9876, JohnCorby, MegaPiggy, Trifid, and friends", "author": "xen, Bwc9876, JohnCorby, MegaPiggy, and friends",
"name": "New Horizons", "name": "New Horizons",
"uniqueName": "xen.NewHorizons", "uniqueName": "xen.NewHorizons",
"version": "1.27.0", "version": "1.27.3",
"owmlVersion": "2.12.1", "owmlVersion": "2.12.1",
"dependencies": [ "JohnCorby.VanillaFix", "xen.CommonCameraUtility", "dgarro.CustomShipLogModes" ], "dependencies": [ "JohnCorby.VanillaFix", "xen.CommonCameraUtility", "dgarro.CustomShipLogModes" ],
"conflicts": [ "PacificEngine.OW_CommonResources" ], "conflicts": [ "PacificEngine.OW_CommonResources" ],

View File

@ -8,7 +8,7 @@
![Latest release date](https://img.shields.io/github/release-date/xen-42/outer-wilds-new-horizons) ![Latest release date](https://img.shields.io/github/release-date/xen-42/outer-wilds-new-horizons)
[![Build](https://github.com/xen-42/outer-wilds-new-horizons/actions/workflows/build.yaml/badge.svg)](https://github.com/xen-42/outer-wilds-new-horizons/actions/workflows/build.yaml) [![Build](https://github.com/xen-42/outer-wilds-new-horizons/actions/workflows/build.yaml/badge.svg)](https://github.com/xen-42/outer-wilds-new-horizons/actions/workflows/build.yaml)
_Do you want to create planets using New Horizons?_ Then check out our [website](https://nh.outerwildsmods.com/) for all our documentation! _Do you want to create your own story mod using New Horizons?_ Then check out our [website](https://nh.outerwildsmods.com/) for all our documentation!
If you want to see examples of what NH can do check out the [examples add-on](https://github.com/xen-42/ow-new-horizons-examples) or [real solar system add-on](https://github.com/xen-42/outer-wilds-real-solar-system). If you want to see examples of what NH can do check out the [examples add-on](https://github.com/xen-42/ow-new-horizons-examples) or [real solar system add-on](https://github.com/xen-42/outer-wilds-real-solar-system).
@ -26,7 +26,7 @@ Check the ship's log for how to use your warp drive to travel between star syste
## Incompatible mods ## Incompatible mods
New Horizons conflicts with the mod Common Resources. This mod is a requirement for other mods such as Cheats Mod (we recommend you use the [Cheat and Debug Menu](https://outerwildsmods.com/mods/cheatanddebugmenu/) mod instead) and OW Randomizer. New Horizons conflicts with the mod Common Resources. This mod is a requirement for other mods such as OW Randomizer (not to be confused with Archipelago Randomizer) and Difficulty Mod.
Why do these two mods conflict? Common Resources is a mod which reimplements many of the game's features underneath the hood, for one reason or another. For instance, it completely overhauls how the orbits of planets work, as this is a requirement for it to support OW Randomizer. It does this even when you are only using Cheats Mod. In particular, having CR installed seems to, for whatever reason, break character dialogue introduced by New Horizons. As CR is no longer actively maintained, it is unlikely this issue will be resolved any time soon. Why do these two mods conflict? Common Resources is a mod which reimplements many of the game's features underneath the hood, for one reason or another. For instance, it completely overhauls how the orbits of planets work, as this is a requirement for it to support OW Randomizer. It does this even when you are only using Cheats Mod. In particular, having CR installed seems to, for whatever reason, break character dialogue introduced by New Horizons. As CR is no longer actively maintained, it is unlikely this issue will be resolved any time soon.
@ -40,25 +40,27 @@ New Horizons has optional support for a few other mods:
## Features ## Features
- Load planet meshes or details from asset bundles NH allows you to create planets:
- Use our [template Unity project](https://github.com/ow-mods/outer-wilds-unity-template) to create assets for use in NH, including all game scripts recovered using UtinyRipper - Load planet meshes or details from asset bundles made in Unity
- Separate solar system scenes accessible via wormhole OR via the ship's new warp drive feature found in the ship's log - Create custom planets from heightmaps/texturemaps with support for triplanar mapping
- Remove or edit existing planets, including their orbits - Add stock planet features: dialogue, Nomai text, geysers, cloaking fields, meteor-launching volcanoes, rafts, tornados, Dark Bramble seeds/nodes, black/white holes, funnels, lava/oceans/sand, signalscope signals/frequencies and others.
- Create custom planets from heightmaps/texturemaps with support for triplanar mapping - Edit existing planets: Remove them, alter their orbits, add/delete objects from them.
- Create stars (and supernovae), comets, asteroid belts, satellites, quantum planets/moons, and custom Dark Bramble dimensions. - All these features work in both the main solar system and the Eye of the Universe scene
- Add stock planet features to custom ones, such as geysers, cloaking fields, meteor-launching volcanoes, rafts, tornados, and Dark Bramble seeds/nodes.
- Binary orbits Even if the majority of your story mod is going to be done in Unity, NH still offers useful features which will improve mod-compatibility and stop you reinventing the wheel:
- Signalscope signals and custom frequencies - NH allows you to separate your story mod into its own solar system scene accessible via wormhole OR via the ship's new warp drive feature found in the ship's log.
- Surface scatter: rocks, trees, etc, using in-game models, or custom ones - NH allows you to create custom ship log entries.
- Black hole / white hole pairs - NH allows you to create Inhabitant slide reels with asynchronous loading optimization.
- Custom dialogue, slide-reel projections, translatable text, and custom ship log entries for rumour mode and map mode - NH has a localization system to translate ship logs, dialogue, Nomai text, and custom UI elements.
- Funnels and variable surface height (can be made of sand/water/lava/star) - NH allows you to add new characters to the cosmic-jam-session at the Eye of the Universe while ensuring mod compatibility
Use our [template Unity project](https://github.com/ow-mods/outer-wilds-unity-template) to create assets for use in NH, including all game scripts recovered using UtinyRipper
## Development ## Development
If you want to help (please dear god help us) then check out the [contact](#contact) info below or the [contributing](https://github.com/xen-42/outer-wilds-new-horizons/blob/master/CONTRIBUTING.md) page. If you want to help (please dear god help us) then check out the [contact](#contact) info below or the [contributing](https://github.com/Outer-Wilds-New-Horizons/new-horizons/blob/master/CONTRIBUTING.md) page.
The Unity project we use to make asset bundles for this mod is [here](https://github.com/xen-42/new-horizons-unity). The Unity project we use to make asset bundles for this mod is [here](https://github.com/Outer-Wilds-New-Horizons/nh-unity).
## Contact ## Contact
@ -70,11 +72,10 @@ Main authors:
- [xen](https://github.com/xen-42) - [xen](https://github.com/xen-42)
- [Bwc9876](https://github.com/Bwc9876) (New Horizons v0.9.0 onwards) - [Bwc9876](https://github.com/Bwc9876) (New Horizons v0.9.0 onwards)
New Horizons was made with help from:
- [JohnCorby](https://github.com/JohnCorby) - [JohnCorby](https://github.com/JohnCorby)
- [MegaPiggy](https://github.com/MegaPiggy) - [MegaPiggy](https://github.com/MegaPiggy)
New Horizons was made with help from:
- [FreezeDriedMangos](https://github.com/FreezeDriedMangos) - [FreezeDriedMangos](https://github.com/FreezeDriedMangos)
- [Trifid](https://github.com/TerrificTrifid) - [Trifid](https://github.com/TerrificTrifid)
- [Hawkbar](https://github.com/Hawkbat) - [Hawkbar](https://github.com/Hawkbat)
@ -89,7 +90,7 @@ Translation credits:
- Japanese: TRSasasusu - Japanese: TRSasasusu
- Portuguese: avengerx, loco-choco - Portuguese: avengerx, loco-choco
New Horizons was based off [Marshmallow](https://github.com/misternebula/Marshmallow) was made by: New Horizons was based off [Marshmallow](https://github.com/misternebula/Marshmallow) made by:
- [\_nebula](https://github.com/misternebula) - [\_nebula](https://github.com/misternebula)