Merge branch 'master' into no-more-CR

This commit is contained in:
Nick 2022-05-02 02:59:20 -04:00
commit ba1636c214
24 changed files with 357 additions and 100 deletions

View File

@ -19,14 +19,18 @@ namespace NewHorizons.Builder.Orbital
var lineRenderer = orbitGO.AddComponent<LineRenderer>();
lineRenderer.material = config.Orbit.DottedOrbitLine ? GameObject.Find("HearthianMapSatellite_Body/OrbitLine").GetComponent<LineRenderer>().material : GameObject.Find("OrbitLine_CO").GetComponent<LineRenderer>().material;
lineRenderer.textureMode = config.Orbit.DottedOrbitLine ? LineTextureMode.RepeatPerSegment : LineTextureMode.Stretch;
OrbitLine orbitLinePrefab;
orbitLinePrefab = GameObject.Find("GiantsDeep_Body/OrbitLine_GD").GetComponent<OrbitLine>();
//orbitLinePrefab = GameObject.Find("HearthianMapSatellite_Body/OrbitLine").GetComponent<OrbitLine>();
lineRenderer.material = orbitLinePrefab.GetComponent<LineRenderer>().material;
var width = config.Orbit.DottedOrbitLine ? 100 : 50;
lineRenderer.startWidth = width;
lineRenderer.endWidth = width;
lineRenderer.useWorldSpace = false;
lineRenderer.loop = false;
var numVerts = config.Orbit.DottedOrbitLine ? 128 : 256;
lineRenderer.positionCount = numVerts;
var ecc = config.Orbit.Eccentricity;
var parentGravity = astroObject.GetPrimaryBody()?.GetGravityVolume();
@ -65,9 +69,10 @@ namespace NewHorizons.Builder.Orbital
orbitLine._astroObject = astroObject;
orbitLine._fade = fade;
orbitLine._lineWidth = 0.2f;
orbitLine._numVerts = (int)Mathf.Clamp(config.Orbit.SemiMajorAxis / 1000f, 128, 4096);
orbitLine._numVerts = (int)Mathf.Clamp(config.Orbit.SemiMajorAxis / 1000f, numVerts, 4096);
Main.Instance.ModHelper.Events.Unity.FireOnNextUpdate(orbitLine.InitializeLineRenderer);
}

View File

@ -85,9 +85,23 @@ namespace NewHorizons.Builder.Props
if (component is GhostIK) (component as GhostIK).enabled = false;
if (component is GhostEffects) (component as GhostEffects).enabled = false;
// If it's not a moving anglerfish make sure the anim controller is off
// If it's not a moving anglerfish make sure the anim controller is regular
if(component is AnglerfishAnimController && component.GetComponentInParent<AnglerfishController>() == null)
Main.Instance.ModHelper.Events.Unity.FireOnNextUpdate(() => (component as AnglerfishAnimController).enabled = false);
Main.Instance.ModHelper.Events.Unity.RunWhen(() => Main.IsSystemReady, () =>
{
Logger.Log("Enabling anglerfish animation");
var angler = (component as AnglerfishAnimController);
// Remove any reference to its angler
if(angler._anglerfishController)
{
angler._anglerfishController.OnChangeAnglerState -= angler.OnChangeAnglerState;
angler._anglerfishController.OnAnglerTurn -= angler.OnAnglerTurn;
angler._anglerfishController.OnAnglerSuspended -= angler.OnAnglerSuspended;
angler._anglerfishController.OnAnglerUnsuspended -= angler.OnAnglerUnsuspended;
}
angler.enabled = true;
angler.OnChangeAnglerState(AnglerfishController.AnglerState.Lurking);
});
if (component is Animator) Main.Instance.ModHelper.Events.Unity.RunWhen(() => Main.IsSystemReady, () => (component as Animator).enabled = true);
if (component is Collider) Main.Instance.ModHelper.Events.Unity.FireOnNextUpdate(() => (component as Collider).enabled = true);
@ -134,6 +148,8 @@ namespace NewHorizons.Builder.Props
if (front.sqrMagnitude == 0f) front = Vector3.Cross(up, Vector3.forward);
if (front.sqrMagnitude == 0f) front = Vector3.Cross(up, Vector3.up);
front = rot * front;
prop.transform.LookAt(prop.transform.position + front, up);
}

View File

@ -16,10 +16,16 @@ namespace NewHorizons.Builder.Props
{
public static void Make(GameObject go, Sector sector, PropModule.DialogueInfo info, IModBehaviour mod)
{
// In stock I think they disable dialogue stuff with conditions
// Here we just don't make it at all
if (info.blockAfterPersistentCondition != null && PlayerData._currentGameSave.GetPersistentCondition(info.blockAfterPersistentCondition)) return;
var dialogue = MakeConversationZone(go, sector, info, mod.ModHelper);
if (info.remoteTriggerPosition != null) MakeRemoteDialogueTrigger(go, sector, info, dialogue);
// Make the character look at the player
// Useful for dialogue replacement
if (!string.IsNullOrEmpty(info.pathToAnimController)) MakePlayerTrackingZone(go, dialogue, info);
}
public static void MakeRemoteDialogueTrigger(GameObject go, Sector sector, PropModule.DialogueInfo info, CharacterDialogueTree dialogue)
@ -81,6 +87,83 @@ namespace NewHorizons.Builder.Props
return dialogueTree;
}
public static void MakePlayerTrackingZone(GameObject go, CharacterDialogueTree dialogue, PropModule.DialogueInfo info)
{
var character = go.transform.Find(info.pathToAnimController);
// At most one of these should ever not be null
var nomaiController = character.GetComponent<SolanumAnimController>();
var controller = character.GetComponent<CharacterAnimController>();
var lookOnlyWhenTalking = info.lookAtRadius <= 0;
// To have them look when you start talking
if (controller != null)
{
controller._dialogueTree = dialogue;
controller.lookOnlyWhenTalking = lookOnlyWhenTalking;
}
else if(nomaiController != null)
{
if (lookOnlyWhenTalking)
{
dialogue.OnStartConversation += nomaiController.StartWatchingPlayer;
dialogue.OnEndConversation += nomaiController.StopWatchingPlayer;
}
}
else
{
// TODO: make a custom controller for basic characters to just turn them to face you
}
if (info.lookAtRadius > 0)
{
GameObject playerTrackingZone = new GameObject("PlayerTrackingZone");
playerTrackingZone.SetActive(false);
playerTrackingZone.layer = LayerMask.NameToLayer("BasicEffectVolume");
playerTrackingZone.SetActive(false);
var sphereCollider = playerTrackingZone.AddComponent<SphereCollider>();
sphereCollider.radius = info.lookAtRadius;
sphereCollider.isTrigger = true;
playerTrackingZone.AddComponent<OWCollider>();
var triggerVolume = playerTrackingZone.AddComponent<OWTriggerVolume>();
if (controller)
{
// Since the Awake method is CharacterAnimController was already called
if (controller.playerTrackingZone)
{
controller.playerTrackingZone.OnEntry -= controller.OnZoneEntry;
controller.playerTrackingZone.OnExit -= controller.OnZoneExit;
}
// Set it to use the new zone
controller.playerTrackingZone = triggerVolume;
triggerVolume.OnEntry += controller.OnZoneEntry;
triggerVolume.OnExit += controller.OnZoneExit;
}
// Simpler for the Nomai
else if(nomaiController)
{
triggerVolume.OnEntry += (_) => nomaiController.StartWatchingPlayer();
triggerVolume.OnExit += (_) => nomaiController.StopWatchingPlayer();
}
// No controller
else
{
// TODO
}
playerTrackingZone.transform.parent = dialogue.gameObject.transform;
playerTrackingZone.transform.localPosition = Vector3.zero;
playerTrackingZone.SetActive(true);
}
}
private static void AddTranslation(string xml)
{
XmlDocument xmlDocument = new XmlDocument();

View File

@ -46,16 +46,17 @@ namespace NewHorizons.Builder.Props
{
foreach(var tornadoInfo in config.Props.Tornados)
{
TornadoBuilder.Make(go, sector, tornadoInfo, config.Atmosphere?.Cloud != null);
//TornadoBuilder.Make(go, sector, tornadoInfo, config.Atmosphere?.Cloud != null);
}
}
if (config.Props.Volcanos != null)
if (config.Props.Volcanoes != null)
{
foreach (var volcanoInfo in config.Props.Volcanos)
foreach (var volcanoInfo in config.Props.Volcanoes)
{
VolcanoBuilder.Make(go, sector, volcanoInfo);
}
}
// Reminder that dialogue has to be built after props if they're going to be using CharacterAnimController stuff
if (config.Props.Dialogue != null)
{
foreach(var dialogueInfo in config.Props.Dialogue)

View File

@ -161,7 +161,7 @@ namespace NewHorizons.Builder.Props
{
try
{
clip = AudioUtility.LoadAudio(mod.ModHelper.Manifest.ModFolderPath + "/" + info.AudioFilePath);
clip = AudioUtilities.LoadAudio(mod.ModHelper.Manifest.ModFolderPath + "/" + info.AudioFilePath);
}
catch(Exception e)
{
@ -198,7 +198,8 @@ namespace NewHorizons.Builder.Props
_customCurve = GameObject.Find("Moon_Body/Sector_THM/Characters_THM/Villager_HEA_Esker/Signal_Whistling").GetComponent<AudioSource>().GetCustomCurve(AudioSourceCurveType.CustomRolloff);
source.SetCustomCurve(AudioSourceCurveType.CustomRolloff, _customCurve);
source.playOnAwake = false;
// If it can be heard regularly then we play it immediately
source.playOnAwake = !info.OnlyAudibleToScope;
source.spatialBlend = 1f;
source.volume = 0.5f;
source.dopplerLevel = 0;

View File

@ -17,6 +17,7 @@ namespace NewHorizons.Builder.Props
var launcherGO = prefab.InstantiateInactive();
launcherGO.transform.parent = sector.transform;
launcherGO.transform.localPosition = info.position == null ? Vector3.zero : (Vector3) info.position;
launcherGO.transform.rotation = Quaternion.FromToRotation(launcherGO.transform.TransformDirection(Vector3.up), ((Vector3)info.position).normalized).normalized;
launcherGO.name = "MeteorLauncher";
var meteorLauncher = launcherGO.GetComponent<MeteorLauncher>();
@ -24,30 +25,49 @@ namespace NewHorizons.Builder.Props
meteorLauncher._detectableFluid = null;
meteorLauncher._detectableField = null;
meteorLauncher._launchDirection = info.position == null ? Vector3.up : ((Vector3)info.position).normalized;
meteorLauncher._launchDirection = Vector3.up;
var meteorPrefab = GameObject.Instantiate(meteorLauncher._meteorPrefab);
FixMeteor(meteorPrefab, info);
meteorLauncher._dynamicProbability = 0f;
meteorLauncher._meteorPrefab = meteorPrefab;
meteorLauncher._minLaunchSpeed = info.minLaunchSpeed;
meteorLauncher._maxLaunchSpeed = info.maxLaunchSpeed;
meteorLauncher._minInterval = info.minInterval;
meteorLauncher._maxInterval = info.maxInterval;
launcherGO.SetActive(true);
// Kill the prefab when its done with it
Main.Instance.ModHelper.Events.Unity.FireOnNextUpdate(() => meteorPrefab.SetActive(false));
// Have to null check else it breaks on reload configs
Main.Instance.ModHelper.Events.Unity.RunWhen(() => Main.IsSystemReady && meteorLauncher._meteorPool != null, () => {
foreach (var meteor in meteorLauncher._meteorPool)
{
FixMeteor(meteor, info);
}
private static void FixMeteor(GameObject meteor, PropModule.VolcanoInfo info)
});
}
private static void FixMeteor(MeteorController meteor, PropModule.VolcanoInfo info)
{
var mat = meteor.GetComponentInChildren<MeshRenderer>().material;
mat.SetColor("_Color", info.stoneTint == null ? defaultStoneTint : info.stoneTint.ToColor());
mat.SetColor("_EmissionColor", info.lavaTint == null ? defaultLavaTint : info.lavaTint.ToColor());
var detectors = meteor.transform.Find("ConstantDetectors");
var detectors = meteor.transform.Find("ConstantDetectors").gameObject;
GameObject.Destroy(detectors.GetComponent<ConstantForceDetector>());
GameObject.Destroy(detectors.GetComponent<ConstantFluidDetector>());
detectors.gameObject.AddComponent<DynamicForceDetector>();
var forceDetector = detectors.gameObject.AddComponent<DynamicForceDetector>();
detectors.gameObject.AddComponent<DynamicFluidDetector>();
detectors.layer = LayerMask.NameToLayer("BasicDetector");
var sphere = detectors.AddComponent<SphereCollider>();
sphere.radius = 1;
var sphere2 = detectors.AddComponent<SphereShape>();
sphere2._collisionMode = Shape.CollisionMode.Detector;
sphere2.radius = 1;
forceDetector._collider = sphere;
forceDetector._shape = sphere2;
}
}
}

View File

@ -23,6 +23,7 @@ namespace NewHorizons.External
public bool IsTidallyLocked { get; set; }
public MVector3 AlignmentAxis { get; set; }
public bool ShowOrbitLine { get; set; } = true;
public bool DottedOrbitLine { get; set; } = false;
public bool IsStatic { get; set; }
public MColor Tint { get; set; }
public bool TrackingOrbitLine { get; set; } = false;

View File

@ -14,7 +14,7 @@ namespace NewHorizons.External
public RaftInfo[] Rafts;
public GeyserInfo[] Geysers;
public TornadoInfo[] Tornados;
public VolcanoInfo[] Volcanos;
public VolcanoInfo[] Volcanoes;
public DialogueInfo[] Dialogue;
public RevealInfo[] Reveal;
public EntryLocationInfo[] EntryLocation;
@ -33,8 +33,8 @@ namespace NewHorizons.External
public class DetailInfo
{
public string path;
public string objFilePath;
public string mtlFilePath;
public string objFilePath; //obsolete DO NOT DOCUMENT
public string mtlFilePath; //obsolete
public string assetBundle;
public MVector3 position;
public MVector3 rotation;
@ -67,6 +67,10 @@ namespace NewHorizons.External
public MVector3 position = null;
public MColor stoneTint = null;
public MColor lavaTint = null;
public float minLaunchSpeed = 50f;
public float maxLaunchSpeed = 150f;
public float minInterval = 5f;
public float maxInterval = 20f;
}
public class DialogueInfo
@ -76,6 +80,8 @@ namespace NewHorizons.External
public string xmlFile;
public MVector3 remoteTriggerPosition;
public string blockAfterPersistentCondition;
public string pathToAnimController;
public float lookAtRadius;
}
public class RevealInfo

View File

@ -81,6 +81,8 @@ namespace NewHorizons
OnStarSystemLoaded = new StarSystemEvent();
SceneManager.sceneLoaded += OnSceneLoaded;
SceneManager.sceneUnloaded += OnSceneUnloaded;
Instance = this;
GlobalMessenger<DeathType>.AddListener("PlayerDeath", OnDeath);
GlobalMessenger.AddListener("WakeUp", new Callback(OnWakeUp));
@ -111,8 +113,6 @@ namespace NewHorizons
Instance.ModHelper.Menus.PauseMenu.OnInit += DebugReload.InitializePauseMenu;
}
public void OnDestroy()
{
Logger.Log($"Destroying NewHorizons");
@ -127,12 +127,17 @@ namespace NewHorizons
Instance.OnStarSystemLoaded?.Invoke(Instance.CurrentStarSystem);
}
void OnSceneLoaded(Scene scene, LoadSceneMode mode)
private void OnSceneUnloaded(Scene scene)
{
Logger.Log($"Scene Loaded: {scene.name} {mode}");
SearchUtilities.ClearCache();
ImageUtilities.ClearCache();
AudioUtilities.ClearCache();
IsSystemReady = false;
}
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
Logger.Log($"Scene Loaded: {scene.name} {mode}");
_isChangingStarSystem = false;

View File

@ -9,18 +9,38 @@ using UnityEngine.Networking;
namespace NewHorizons.Utility
{
public static class AudioUtility
public static class AudioUtilities
{
public static AudioClip LoadAudio(string filePath)
private static Dictionary<string, AudioClip> _loadedAudioClips = new Dictionary<string, AudioClip>();
public static AudioClip LoadAudio(string path)
{
var task = Task.Run(async () => await GetAudioClip(filePath));
if (_loadedAudioClips.ContainsKey(path))
{
Logger.Log($"Already loaded audio at path: {path}");
return _loadedAudioClips[path];
}
Logger.Log($"Loading audio at path: {path}");
var task = Task.Run(async () => await GetAudioClip(path));
task.Wait();
_loadedAudioClips.Add(path, task.Result);
return task.Result;
}
public static void ClearCache()
{
Logger.Log("Clearing audio cache");
foreach (var audioClip in _loadedAudioClips.Values)
{
if (audioClip == null) continue;
UnityEngine.Object.Destroy(audioClip);
}
_loadedAudioClips.Clear();
}
private static async Task<AudioClip> GetAudioClip(string filePath)
{
var extension = filePath.Split(new char[] { '.' }).Last();
UnityEngine.AudioType audioType;

View File

@ -8,6 +8,46 @@ namespace NewHorizons.Utility
{
static class ImageUtilities
{
private static Dictionary<string, Texture2D> _loadedTextures = new Dictionary<string, Texture2D>();
private static List<Texture2D> _generatedTextures = new List<Texture2D>();
public static Texture2D GetTexture(IModBehaviour mod, string filename)
{
// Copied from OWML but without the print statement lol
var path = mod.ModHelper.Manifest.ModFolderPath + filename;
if (_loadedTextures.ContainsKey(path))
{
Logger.Log($"Already loaded image at path: {path}");
return _loadedTextures[path];
}
Logger.Log($"Loading image at path: {path}");
var data = File.ReadAllBytes(path);
var texture = new Texture2D(2, 2);
texture.name = Path.GetFileNameWithoutExtension(path);
texture.LoadImage(data);
_loadedTextures.Add(path, texture);
return texture;
}
public static void ClearCache()
{
Logger.Log("Cleaing image cache");
foreach (var texture in _loadedTextures.Values)
{
if (texture == null) continue;
UnityEngine.Object.Destroy(texture);
}
_loadedTextures.Clear();
foreach(var texture in _generatedTextures)
{
if (texture == null) continue;
UnityEngine.Object.Destroy(texture);
}
_generatedTextures.Clear();
}
public static Texture2D MakeOutline(Texture2D texture, Color color, int thickness)
{
var outline = new Texture2D(texture.width, texture.height, TextureFormat.ARGB32, false);
@ -32,6 +72,8 @@ namespace NewHorizons.Utility
outline.SetPixels(outlinePixels);
outline.Apply();
_generatedTextures.Add(outline);
return outline;
}
@ -67,6 +109,9 @@ namespace NewHorizons.Utility
newImage.name = image.name + "Tinted";
newImage.SetPixels(pixels);
newImage.Apply();
_generatedTextures.Add(newImage);
return newImage;
}
@ -84,15 +129,10 @@ namespace NewHorizons.Utility
newImage.name = image.name + "LerpedGrayscale";
newImage.SetPixels(pixels);
newImage.Apply();
return newImage;
}
public static Texture2D LoadImage(string filepath)
{
Texture2D tex = new Texture2D(2, 2);
tex.name = Path.GetFileNameWithoutExtension(filepath);
tex.LoadRawTextureData(File.ReadAllBytes(filepath));
return tex;
_generatedTextures.Add(newImage);
return newImage;
}
public static Texture2D ClearTexture(int width, int height)
@ -107,6 +147,9 @@ namespace NewHorizons.Utility
}
tex.SetPixels(fillPixels);
tex.Apply();
_generatedTextures.Add(tex);
return tex;
}
@ -124,39 +167,12 @@ namespace NewHorizons.Utility
}
tex.SetPixels(fillPixels);
tex.Apply();
_generatedTextures.Add(tex);
return tex;
}
private static Dictionary<string, Texture2D> _loadedTextures = new Dictionary<string, Texture2D>();
public static Texture2D GetTexture(IModBehaviour mod, string filename)
{
// Copied from OWML but without the print statement lol
var path = mod.ModHelper.Manifest.ModFolderPath + filename;
if (_loadedTextures.ContainsKey(path))
{
Logger.Log($"Already loaded image at path: {path}");
return _loadedTextures[path];
}
Logger.Log($"Loading image at path: {path}");
var data = File.ReadAllBytes(path);
var texture = new Texture2D(2, 2);
texture.name = Path.GetFileNameWithoutExtension(path);
texture.LoadImage(data);
_loadedTextures.Add(path, texture);
return texture;
}
public static void ClearCache()
{
foreach(var texture in _loadedTextures.Values)
{
if (texture == null) continue;
UnityEngine.Object.Destroy(texture);
}
_loadedTextures.Clear();
}
public static Color GetAverageColor(Texture2D src)
{
var pixels = src.GetPixels32();

View File

@ -14,6 +14,7 @@ namespace NewHorizons.Utility
public static void ClearCache()
{
Logger.Log("Clearing search cache");
CachedGameObjects.Clear();
}

View File

@ -3,7 +3,7 @@
"author": "xen, Bwc9876, & Book",
"name": "New Horizons",
"uniqueName": "xen.NewHorizons",
"version": "0.10.3",
"version": "0.11.0",
"owmlVersion": "2.1.0",
"conflicts": [ "Raicuparta.QuantumSpaceBuddies", "Vesper.OuterWildsMMO", "Vesper.AutoResume" ],
"pathsToPreserve": [ "planets", "systems", "translations" ]

View File

@ -363,6 +363,10 @@
"default": false,
"description": "Referring to the orbit line in the map screen."
},
"dottedOrbitLine": {
"type": "boolean",
"default": false
},
"isStatic": {
"type": "boolean",
"default": false,
@ -603,14 +607,6 @@
"type": "string",
"description": "Relative filepath to an asset-bundle"
},
"objFilePath": {
"type": "string",
"description": "Relative filepath to the obj file"
},
"mtlFilePath": {
"type": "string",
"description": "Relative filepath to the mtl file of the obj file"
},
"position": {
"$ref": "#/$defs/vector3"
},
@ -663,6 +659,15 @@
"blockAfterPersistentCondition": {
"type": "string",
"description": "Prevents the dialogue from being created after a specific persistent condition is set. Useful for remote dialogue triggers that you want to have happen only once."
},
"pathToAnimController": {
"type": "string",
"description": "If this dialogue is meant for a character, this is the relative path from the planet to that character's CharacterAnimController or SolanumAnimController."
},
"lookAtRadius": {
"type": "number",
"default": 0,
"description": "If a pathToAnimController is supplied, if you are within this distance the character will look at you. If it is set to 0, they will only look at you when spoken to."
}
}
}
@ -747,6 +752,48 @@
}
}
}
},
"volcanoes": {
"type": "array",
"description": "A set of meteor-spewing volcanoes",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"position": {
"$ref": "#/$defs/vector3",
"description": "The position of this volcano."
},
"stoneTint": {
"$ref": "#/$defs/color",
"description": "The colour of the meteor's stone."
},
"lavaTint": {
"$ref": "#/$defs/color",
"description": "The colour of the meteor's lava."
},
"minLaunchSpeed": {
"type": "number",
"description": "Minimum random speed at which meteors are launched.",
"default": 50
},
"maxLaunchSpeed": {
"type": "number",
"description": "Maximum random speed at which meteors are launched.",
"default": 150
},
"minInterval": {
"type": "number",
"description": "Minimum time between meteor launches.",
"default": 5
},
"maxInterval": {
"type": "number",
"description": "Maximum time between meteor launches.",
"default": 20
}
}
}
}
}
},

View File

@ -35,7 +35,7 @@ Check the ship's log for how to use your warp drive to travel between star syste
- Separate solar system scenes accessible via wormhole OR via the ship's new warp drive feature accessible via the ship's log
- Remove existing planets
- Create planets from heightmaps/texturemaps
- Create stars, comets, asteroid belts, satellites, geysers, cloak fields
- Create stars, comets, asteroid belts, satellites, geysers, cloak fields, meteor-launching volcanoes
- Binary orbits
- Signalscope signals and custom frequencies
- Surface scatter: rocks, trees, etc, using in-game models, or custom ones
@ -51,7 +51,6 @@ Check the ship's log for how to use your warp drive to travel between star syste
- Implement all planet features:
- Tornados + floating islands
- Let any star go supernova
- Meteors
- Pocket dimensions
- Timed position/velocity changes
- Implement custom Nomai scrolls

32
docs/Pipfile.lock generated
View File

@ -50,11 +50,11 @@
},
"click": {
"hashes": [
"sha256:24e1a4a9ec5bf6299411369b208c1df2188d9eb8d916302fe6bf03faed227f1e",
"sha256:479707fe14d9ec9a0757618b7a100a0ae4c4e236fac5b7f80ca68028141a1a72"
"sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e",
"sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"
],
"markers": "python_version >= '3.7'",
"version": "==8.1.2"
"version": "==8.1.3"
},
"colorama": {
"hashes": [
@ -74,11 +74,11 @@
},
"elementpath": {
"hashes": [
"sha256:2a432775e37a19e4362443078130a7dbfc457d7d093cd421c03958d9034cc08b",
"sha256:3a27aaf3399929fccda013899cb76d3ff111734abf4281e5f9d3721ba0b9ffa3"
"sha256:1f41f1160aaae66bc25a8cb9451e5b31ca4553b6dccd9b57045205b005e5406e",
"sha256:c556a9b9dde47fdf05bb3ad525dfb43fc6becb95532a053f6a73024e586ead37"
],
"markers": "python_version >= '3.7'",
"version": "==2.5.0"
"version": "==2.5.1"
},
"htmlmin": {
"hashes": [
@ -96,11 +96,11 @@
},
"jinja2": {
"hashes": [
"sha256:539835f51a74a69f41b848a9645dbdc35b4f20a3b601e2d9a7e22947b15ff119",
"sha256:640bed4bb501cbd17194b3cace1dc2126f5b619cf068a726b98192a0fde74ae9"
"sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852",
"sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"
],
"markers": "python_version >= '3.7'",
"version": "==3.1.1"
"version": "==3.1.2"
},
"json-minify": {
"hashes": [
@ -114,7 +114,7 @@
"sha256:66784a3d37c8f730588524cc8f103448847533f067ba8b5d76e7667675ee31f1",
"sha256:ed900db6b19b41bf681513c48ae5e403632878745775ddfc8d5b73438d2930fe"
],
"markers": "python_version >= '3.7' and python_version < '4'",
"markers": "python_version >= '3.7' and python_version < '4.0'",
"version": "==0.40.2"
},
"jsonschema": {
@ -135,11 +135,11 @@
},
"markdown2": {
"hashes": [
"sha256:8f4ac8d9a124ab408c67361090ed512deda746c04362c36c2ec16190c720c2b0",
"sha256:91113caf23aa662570fe21984f08fe74f814695c0a0ea8e863a8b4c4f63f9f6e"
"sha256:412520c7b6bba540c2c2067d6be3a523ab885703bf6a81d93963f848b55dfb9a",
"sha256:f344d4adfba5d1de821f7850b36e3507f583468a7eb47e6fa191765ed0b9c66b"
],
"markers": "python_version >= '3.5' and python_version < '4'",
"version": "==2.4.2"
"markers": "python_version >= '3.5' and python_version < '4.0'",
"version": "==2.4.3"
},
"markupsafe": {
"hashes": [
@ -222,7 +222,7 @@
"sha256:5053fc5ca7b8a281081274702ebf1584e341f40a68e6ab8f6b4b79f4b3fdf18e",
"sha256:8e8226f15c0b25565aa391797963b78c95930e12efc40e905153130783e766be"
],
"markers": "python_version >= '3.8' and python_version < '4'",
"markers": "python_version >= '3.8' and python_version < '4.0'",
"version": "==0.1.0"
},
"packaging": {
@ -453,7 +453,7 @@
"sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14",
"sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4.0'",
"version": "==1.26.9"
},
"xmlschema": {

View File

@ -2,6 +2,7 @@ Title: API
Sort_Priority: 40
## How to use the API
___
First create the following interface in your mod:

View File

@ -2,6 +2,7 @@ Title: Detailing
Sort_Priority: 90
## Details/Scatterer
___
For physical objects there are currently two ways of setting them up: specify an asset bundle and path to load a custom asset you created, or specify the path to the item you want to copy from the game in the scene hierarchy. Use the [Unity Explorer](https://outerwildsmods.com/mods/unityexplorer){ target="_blank" } mod to find an object you want to copy onto your new body. Some objects work better than others for this. Good luck. Some pointers:
- Use "Object Explorer" to search
@ -9,6 +10,7 @@ For physical objects there are currently two ways of setting them up: specify an
- Generally you can find planets by writing their name with no spaces/punctuation followed by "_Body".
## Asset Bundles
___
Here is a template project: [Outer Wilds Unity Template](https://github.com/xen-42/outer-wilds-unity-template){ target="_blank" }
@ -47,10 +49,12 @@ public class CreateAssetBundles
6. Copy the asset bundle and asset bundle .manifest files from StreamingAssets into your mod's "planets" folder. If you did everything properly they should work in game. To double-check everything is included, open the .manifest file in a text editor to see the files included and their paths.
## Importing a planet's surface from Unity
___
Making a planet's entire surface from a Unity prefab is the exact same thing as adding one single big detail at position (0, 0, 0).
## Examples
___
To add a Mars rover to the red planet in [RSS](https://github.com/xen-42/outer-wilds-real-solar-system), its model was put in an asset bundle as explained above, and then the following was put into the `Props` module:

View File

@ -2,6 +2,7 @@ Title: Dialogue
Sort_Priority: 50
## Dialogue
___
Here's an example dialogue XML:

View File

@ -5,10 +5,12 @@ Sort_Priority: 100
![New Horizons Logo]({{ 'images/home/home_logo.webp'|static }})
# Outer Wilds New Horizons
___
This is the official documentation for [New Horizons](https://github.com/xen-42/outer-wilds-new-horizons){ target="_blank" }
## Getting Started
___
Before starting, go into your in-game mod settings for New Horizons and switch Debug mode on. This allows you to:
@ -114,11 +116,13 @@ To see all the different things you can put into a config file check out the [Ce
Check out the rest of the site for how to format [star system]({{ 'Star System Schema'|route}}), [dialogue]({{ 'Dialogue Schema'|route}}), [ship log]({{ 'Shiplog Schema'|route}}), and [translation]({{ 'Translation Schema'|route}}) files!
## Publishing Your Mod
___
Once your mod is complete, you can use the [addon creation tool](https://outerwildsmods.com/custom-worlds/create/){ target="_blank" } to upload your mod to the database.
Alternatively, you can use the [planet creation template](https://github.com/xen-42/ow-new-horizons-config-template#readme){ target="_blank" } GitHub template if you're familiar with Git and GitHub
## Helpful Resources
___
The texturemap/heightmap feature was inspired by the Kerbal Space Program mod Kopernicus. A lot of the same techniques that apply to
planet creation there apply to New Horizons. If you need help with planetary texturing, check out [The KSP texturing guide](https://forum.kerbalspaceprogram.com/index.php?/topic/165285-planetary-texturing-guide-repository/){ target="_blank" }.

View File

@ -3,6 +3,7 @@ Description: Hehe funny secret
Hide_In_Nav: True
# Hello!!
___
Uh idk what to put here thought it would be funny haha

View File

@ -3,9 +3,11 @@ Description: A guide to editing the ship log in New Horizons
Sort_Priority: 70
# Intro
___
Welcome! this page outlines how to create a custom ship log.
## Helpful Mods
___
These mods are useful when developing your ship log
@ -14,6 +16,7 @@ These mods are useful when developing your ship log
- [Save Editor](https://outerwildsmods.com/mods/saveeditor){ target="_blank" }
## Helpful Tools
___
These tools/references are highly recommended
@ -24,10 +27,12 @@ These tools/references are highly recommended
- [The Examples Mod](https://github.com/xen-42/ow-new-horizons-examples){ target="_blank" }
# Understanding Ship Logs
___
First thing's first, I'll define some terminology regarding ship logs in the game, and how ship logs are structured.
## Entries
___
An entry is a card you see in rumor mode, it represents a specific area or concept in the game, such as Timber Hearth's
village or the southern observatory on Brittle Hollow.
@ -53,6 +58,7 @@ Entries can be children of other entries, meaning they'll be smaller.
*The murals at the old settlement on Brittle Hollow are examples of child entries*
## Rumor Facts
___
A rumor fact represents the information you might hear about a specific area or concept, usually, you get these through
dialogue or maybe by observing a faraway planet.
@ -60,18 +66,21 @@ dialogue or maybe by observing a faraway planet.
![rumorFactExample]({{ "images/ship_log/rumor_example.webp"|static }})
## Explore Facts
___
Explore facts represent the information you learn about a specific area or concept.
![exploreFactExample]({{ "images/ship_log/explore_example.webp"|static }})
# The XML
___
Now that we know some terminology, let's get into how the XML works.
Every planet in the ship log is represented by a single XML file, you can see this if you use the unity explorer mod and
navigate to ShipLogManager.
## Example File
___
```xml
<!-- Example File -->
@ -150,6 +159,7 @@ navigate to ShipLogManager.
```
## Using The Schema
___
In the example XML, you may notice something like `xsi:noNamespaceSchemaLocation` at the top, this tells whatever editor
you're using that the file at that link is the schema. The game simply ignores this though, so it won't be able to catch
@ -158,6 +168,7 @@ Some editors may require you to [Trust](https://code.visualstudio.com/docs/edito
the schema file. Doing this varies per-editor, and you may also have to right-click the link and click download.
## Loading The File
___
You can load your XML file to your planet by doing adding the following to your planet's config
@ -172,6 +183,7 @@ You can load your XML file to your planet by doing adding the following to your
# Rumor Mode Options
## Entry Layout
___
By default, entries in rumor mode are laid out by rows, where each row is one planet. This will not make for a perfect
layout, so you can use the `entryPositions` property to change them
@ -205,6 +217,7 @@ For example, if I want to change an entry with the ID of `EXAMPLE_ENTRY` and ano
*A set of entries laid out with auto mode*
## Images
___
Custom entry images are a bit different from other custom images, instead of pointing to each file for each entry, you
point to a folder:
@ -224,6 +237,7 @@ you set alternate sprites by making a file with the entry's ID and `_ALT` at the
would be `EXAMPLE_ENTRY_ALT.png`.
## Curiosity Colors
___
Colors for each curiosity is given in a list, so if I wanted the curiosity `EXAMPLE_ENTRY` to have a color of blue:
@ -256,8 +270,10 @@ Colors for each curiosity is given in a list, so if I wanted the curiosity `EXAM
*The curiosity's color is changed to blue*
# Map Mode Options
___
## Layout
___
Layout in map mode can be handled in two different ways, either manual or automatic, if you try to mix them you'll get
an error.
@ -424,10 +440,12 @@ between Ash Twin and Ember Twin)
As you can see, they have similar properties to planets, with the addition of rotation
# Revealing Facts
___
Of course, having a custom ship log is neat and all, but what use is it if the player can't unlock it?
## Initial Reveal
___
You can set facts to reveal as soon as the player enters the system by adding the `initialReveal` property
@ -443,6 +461,7 @@ You can set facts to reveal as soon as the player enters the system by adding th
```
## Signal Discovery
___
You can set a fact to reveal as soon as a signal is identified by editing the signal's `Reveals` attribute
@ -463,6 +482,7 @@ You can set a fact to reveal as soon as a signal is identified by editing the si
```
## Dialogue
___
You can set a fact to reveal in dialogue with the `<RevealFacts>` tag
@ -484,6 +504,7 @@ You can set a fact to reveal in dialogue with the `<RevealFacts>` tag
```
## Reveal Volumes
___
Reveal volumes are triggers/colliders in the world that can unlock facts from a variety of actions.
Reveal volumes are specified in the `Props` module, its key is `reveal`.
@ -551,6 +572,7 @@ trigger the reveal
```
# Setting Entry Locations
___
Entry locations are the "Mark On HUD" option you see when in map mode, this allows the player to go back to where they
were in the event of the big funny.

View File

@ -2,6 +2,7 @@ Title: Translations
Sort_Priority: 60
## Translations
___
There are 12 supported languages in Outer Wilds: english, spanish_la, german, french, italian, polish, portuguese_br, japanese, russian, chinese_simple, korean, and turkish.

View File

@ -2,6 +2,7 @@ Title: Update Planets
Sort_Priority: 80
## Update Existing Planets
___
Similar to above, make a config where "Name" is the name of the planet. The name should be able to just match their in-game english names, however if you encounter any issues with that here are the in-code names for planets that are guaranteed to work: `SUN`, `CAVE_TWIN` (Ember Twin), `TOWER_TWIN` (Ash Twin), `TIMBER_HEARTH`, `BRITTLE_HOLLOW`, `GIANTS_DEEP`, `DARK_BRAMBLE`, `COMET` (Interloper), `WHITE_HOLE`, `WHITE_HOLE_TARGET` (Whitehole station I believe), `QUANTUM_MOON`, `ORBITAL_PROBE_CANNON`, `TIMBER_MOON` (Attlerock), `VOLCANIC_MOON` (Hollow's Lantern), `DREAMWORLD`, `MapSatellite`, `RINGWORLD` (the Stranger).
@ -20,6 +21,7 @@ You can also delete parts of an existing planet. Here's part of an example confi
In `childrenToDestroy` you list the relative paths for the children of the planet's gameObject that you want to delete.
## Destroy Existing Planets
___
You do this (but with the appropriate name) as its own config.
```json