From 0f40e37d1c11d3be2d93775a2f445d9caedb7aea Mon Sep 17 00:00:00 2001 From: Ben C Date: Sat, 23 Jul 2022 17:01:30 -0400 Subject: [PATCH 001/176] Start on docs redo --- docs/content/pages/home.md | 111 +------- docs/content/pages/tutorials/api.md | 47 ---- docs/content/pages/tutorials/details.md | 167 ------------ docs/content/pages/tutorials/dialogue.md | 164 ----------- .../pages/tutorials/getting_started.md | 255 ++++++++++++++++++ docs/content/pages/tutorials/planet_gen.md | 148 ++++++++++ .../pages/tutorials/reading_schemas.md | 78 ++++++ docs/content/pages/tutorials/star_system.md | 46 ---- docs/content/pages/tutorials/translation.md | 34 --- .../pages/tutorials/update_existing.md | 34 --- .../mod_manager_show_in_explorer.webp | Bin 0 -> 14312 bytes .../images/reading_schemas/body_schema_1.webp | Bin 0 -> 16008 bytes .../images/reading_schemas/body_schema_2.webp | Bin 0 -> 5756 bytes .../images/reading_schemas/body_schema_3.webp | Bin 0 -> 16610 bytes .../images/reading_schemas/body_schema_4.webp | Bin 0 -> 12050 bytes 15 files changed, 483 insertions(+), 601 deletions(-) delete mode 100644 docs/content/pages/tutorials/api.md delete mode 100644 docs/content/pages/tutorials/details.md delete mode 100644 docs/content/pages/tutorials/dialogue.md create mode 100644 docs/content/pages/tutorials/getting_started.md create mode 100644 docs/content/pages/tutorials/planet_gen.md create mode 100644 docs/content/pages/tutorials/reading_schemas.md delete mode 100644 docs/content/pages/tutorials/star_system.md delete mode 100644 docs/content/pages/tutorials/translation.md delete mode 100644 docs/content/pages/tutorials/update_existing.md create mode 100644 docs/content/static/images/getting_started/mod_manager_show_in_explorer.webp create mode 100644 docs/content/static/images/reading_schemas/body_schema_1.webp create mode 100644 docs/content/static/images/reading_schemas/body_schema_2.webp create mode 100644 docs/content/static/images/reading_schemas/body_schema_3.webp create mode 100644 docs/content/static/images/reading_schemas/body_schema_4.webp diff --git a/docs/content/pages/home.md b/docs/content/pages/home.md index 534cda0c..fa228b44 100644 --- a/docs/content/pages/home.md +++ b/docs/content/pages/home.md @@ -8,118 +8,11 @@ Sort_Priority: 100 # Outer Wilds New Horizons -This is the official documentation for [New Horizons](https://github.com/xen-42/outer-wilds-new-horizons), a framework for creating custom planets in the game [Outer Wilds](https://www.mobiusdigitalgames.com/outer-wilds.html) by Mobius Digital. Planets are created using simple JSON and XML files and are loaded at runtime. An [API]({{ "API"|route }}) is also provided for more advanced use-cases. +This is the official documentation for [New Horizons](https://github.com/xen-42/outer-wilds-new-horizons), a framework for creating custom planets in the game [Outer Wilds](https://www.mobiusdigitalgames.com/outer-wilds.html) by Mobius Digital. Planets are created using simple JSON and XML files and are loaded at runtime. An [API]({{ "API"|route }}) is also provided for more advanced use-cases. ## Getting Started -Before starting, go into your in-game mod settings for New Horizons and switch Debug mode on. This allows you to: - -- Use the [Prop Placer tool]({{ "detailing"|route }}#using-the-prop-placer). This convienence tool allows you to place details in game and save your work to your config files. -- Print the position of what you are looking at to the logs by pressing "P". This is useful for determining locations to place props the Prop Placer is unable to, such as signal scope points or dialogue triggers. -- Use the "Reload Configs" button in the pause menu. This will restart the current solar system and update all the planets. Much faster than quitting and relaunching the game. - -!!! alert-danger "Get VSCode" - Please get [VSCode](https://code.visualstudio.com/){ target="_blank" } or some other advanced text editor, as it will help highlight common errors. - -Planets are created using a JSON file format structure, and placed in a folder called planets (or in any subdirectory of it) in the location where New Horizons is installed (by default this folder doesn't exist, you have to create it within the xen.NewHorizons directory). You can learn how the configs work by picking apart the [Real Solar System](https://github.com/xen-42/outer-wilds-real-solar-system){ target="_blank" } mod or the [New Horizons Examples](https://github.com/xen-42/ow-new-horizons-examples){ target="_blank" } mod. - -To locate this directory, click the "⋮" symbol next to "New Horizons" in the Outer Wilds Mod Manager and then click " -show in explorer" in the pop-up. - -![Click the three dots in the mod manager]({{ "images/home/mod_manager_dots.webp"|static }}) - -![Create a new folder named "planets"]({{ "images/home/create_planets.webp"|static }}) - -Planets can also be placed in a folder called planets within a separate mod, if you plan on releasing your planets on the mod database. The [Config Template](https://github.com/xen-42/ow-new-horizons-config-template){ target="_blank" } is available if you want to release your own planet mod using configs. - -Now that you have created your planets folder, this is where you will put your planet config files. A config file will -look something like this: - -```json -{ - "name": "Wetrock", - "$schema": "https://raw.githubusercontent.com/xen-42/outer-wilds-new-horizons/main/NewHorizons/Schemas/body_schema.json", - "starSystem": "SolarSystem", - "Base": { - "groundSize": 100, - "surfaceSize": 101, - "surfaceGravity": 12, - "hasMapMarker": true - }, - "Orbit": { - "semiMajorAxis": 1300, - "inclination": 0, - "primaryBody": "TIMBER_HEARTH", - "isMoon": true, - "isTidallyLocked": true, - "longitudeOfAscendingNode": 0, - "eccentricity": 0, - "argumentOfPeriapsis": 0 - }, - "Atmosphere": { - "size": 150, - "fogTint": { - "r": 200, - "g": 255, - "b": 255, - "a": 255 - }, - "fogSize": 150, - "fogDensity": 0.2, - "hasRain": true - }, - "Props": { - "scatter": [ - { - "path": "DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_1/Props_DreamZone_1/OtherComponentsGroup/Trees_Z1/DreamHouseIsland/Tree_DW_M_Var", - "count": 12 - } - ] - } -} -``` - -The first field you should have in any config file is the `name`. This should be unique in the solar system. If it -isn't, the mod will instead try to modify the planet that already has that name. - -After `name` is `starSystem`. You can use this to place the planet in a different system accessible using a black-hole or via the ship's warp drive (accessible from the ship log computer). To ensure compatibility with other mods this name should be unique. After setting a value for this, the changes in the config will only affect that body in that star system. By default, it is "SolarSystem", which is the scene from the stock game. - -Including the "$schema" line is optional, but will allow your text editor to highlight errors and auto-suggest words in your config. I recommend using VSCode as a text editor, but anything that supports Json files will work. Something as basic as notepad will work but will not highlight any of your errors. - -The config file is then split into modules, each one with its own fields that define how that part of the planet will be generated. In the example above I've used the `Base`, `Orbit`, `Atmosphere`, and `Props` modules. A config file must have a `Base` and `Orbit` module, the rest are optional. - -Each `{` must match up with a closing `}` to denote its section. If you don't know how JSONs work then check Wikipedia. - -Modules look like this: - -```json -{ - "Star": { - "size": 3000, - "tint": { - "r": 201, - "g": 87, - "b": 55, - "a": 255 - } - } -} -``` - -In this example the `Star` module has a `size` field and a `tint` field. Since the colour is a complex object it needs -another set of `{` and `}` around it, and then it has its own fields inside it : `r`, `g`, `b`, and `a`. Don't forget to put -commas after each field. - -Most fields are either true/false, a decimal number, and integer number, or a string (word with quotation marks around -it). - -To see all the different things you can put into a config file check out the [Celestial Body schema]({{ 'Celestial Body Schema'|route}}). - -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 [planet creation template](https://github.com/xen-42/ow-new-horizons-config-template#readme){ target="_blank" } GitHub template. +For a guide on getting started an getting familiar with New Horizons, go to the [Getting Started]({{ "Getting Started"|route }}) page. ## Helpful Resources diff --git a/docs/content/pages/tutorials/api.md b/docs/content/pages/tutorials/api.md deleted file mode 100644 index 8689943c..00000000 --- a/docs/content/pages/tutorials/api.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -Title: API -Sort_Priority: 40 ---- - -## How to use the API - -First create the following interface in your mod: - -```cs -public interface INewHorizons -{ - void LoadConfigs(IModBehaviour mod); - - GameObject GetPlanet(string name); - - string GetCurrentStarSystem(); - - UnityEvent GetChangeStarSystemEvent(); - - UnityEvent GetStarSystemLoadedEvent(); - - GameObject SpawnObject(GameObject planet, Sector sector, string propToCopyPath, Vector3 position, Vector3 eulerAngles, float scale, bool alignWithNormal); - - string[] GetInstalledAddons(); -} -``` - -In your main `ModBehaviour` class you can get the NewHorizons API like so: - -```cs -public class MyMod : ModBehaviour -{ - void Start() - { - INewHorizons NewHorizonsAPI = ModHelper.Interaction.GetModApi("xen.NewHorizons"); - } -} -``` - -You can then use the APIs `LoadConfigs()` method to load from a "planets" folder, or use the `GetPlanet()` method to get planets and do whatever with them. Just make sure you create planets in the `Start()` method or at least before the SolarSystem scene loads, or they will not be created. - -The `GetChangeStarSystemEvent` and `GetStarSystemLoadedEvent` events let you listen in for when the player starts changing to a new system (called when entering a black hole or using the warp drive) and when the system is fully loaded in, respectively. - -You can also use the `GetInstalledAddons` method to get a list of addons that are installed and enabled. - -You can also use `SpawnObject` to directly copy a base-game GameObject to the specified position and rotation. diff --git a/docs/content/pages/tutorials/details.md b/docs/content/pages/tutorials/details.md deleted file mode 100644 index 41f704b3..00000000 --- a/docs/content/pages/tutorials/details.md +++ /dev/null @@ -1,167 +0,0 @@ ---- -Title: Detailing -Sort_Priority: 85 ---- - -# 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 -- Do not use the search functionality on Scene Explorer, it is really, really slow. Use the "Object Search" tab instead. -- Generally you can find planets by writing their name with no spaces/punctuation followed by "_Body". -- There's also [this community-maintained list of props](https://docs.google.com/spreadsheets/d/1VJaglB1kRL0VqaXhvXepIeymo93zqhWex-j7_QDm6NE/edit?usp=sharing) which you can use to find interesting props and check to see if they have collision. - -## Using the Prop Placer - -The Prop Placer is a convenience tool that lets you manually place details from inside the game. Once enabled, press "G" and your currently selected prop will be placed wherever your crosshair is pointing. - -### Enabling - -1. Pause the game. You will see an extra menu option titled "Toggle Prop Placer Menu". Click it -2. The prop placer menu should now be open. At the bottom of the menu, you will see a list of mods. Click yours. - 1. This menu scrolls. If you do not see your mod, it may be further down the list. -3. The Prop Placer is now active! Unpause the game and you can now place Nomai vases using "G" - -### How to Save - -1. In the Prop Placer Menu, you will see a greyed out button titled "Update your mod's configs". -2. Click the small button to the left of it. -3. Click "Update your mod's configs" to save! - -!!! alert-danger "IMPORTANT" - Your updated configs will save *only* to your mod's build folder, eg "AppData\Roaming\OuterWildsModManager\OWML\Mods\you.yourModName" - -!!! alert-warning "WARNING" - Dying in-game will cause you to lose all work since you last saved. Make sure to save often. - -What's that? You want to place something other than just vases? Well I can't say I agree with your choices, but here's how you would do that. - -### How to Select Props - -1. Pause the game again. The prop placer menu should still be visible. -2. At the top of the menu, you'll see a text box contianing the path for the vase. Replace this with the path for the prop you want to place. For example: `DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_1/Props_DreamZone_1/OtherComponentsGroup/Trees_Z1/DreamHouseIsland/Tree_DW_M_Var` -3. Tip: use the Unity Explorer mod to find the path for the object you want to place. You only have to do this once. -4. Unpause the game and press "G". Say hello to your new tree! -5. Pause the game again. You will now see the prop you just placed on the list of recently placed props just below the "path" text box. -6. Click on the button titled "Prefab_NOM_VaseThin". You can now place vases again. - -### Extra features - -1. Made a mistake? **Press the "-" key to undo.** Press the "+" key to redo. -2. If you have the Unity Explorer mod enabled, you can use this to tweak the position, rotation, and scale of your props. Your changes will be saved. -3. Want to save some recently placed props between game launches? On the recently placed props list, click the star next to the prop's name to favorite it. -4. Found a bug that ruined your configs? Check `AppData\Roaming\OuterWildsModManager\OWML\Mods\xen.NewHorizons\configBackups` for backup saves of your work. Folders are titled "\[date\]T\[time\]". -5. Want to add props to Ember Twin but don't feel like making a config file for it? We got you! Place that prop and the config file will be created automatically on your next save. -6. This even works for planets that were created by other mods! - -## Asset Bundles - -Here is a template project: [Outer Wilds Unity Template](https://github.com/xen-42/outer-wilds-unity-template){ target="_blank" } - -The template 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. - -If for whatever reason you want to set up a Unity project manually instead of using the template, follow these instructions: - -1. Start up a Unity 2017 project (I use Unity 2017.4.40f1 (64-bit), so if you use something else I can't guarantee it will work). The DLC updated Outer Wilds to 2019.4.27 so that probably works, but I personally haven't tried it. -2. In the "Assets" folder in Unity, create a new folder called "Editor". In it create a file called "CreateAssetBundle.cs" with the following code in it: - -```cs -using UnityEditor; -using UnityEngine; -using System.IO; - -public class CreateAssetBundles -{ - [MenuItem("Assets/Build AssetBundles")] - static void BuildAllAssetBundles() - { - string assetBundleDirectory = "Assets/StreamingAssets"; - if (!Directory.Exists(Application.streamingAssetsPath)) - { - Directory.CreateDirectory(assetBundleDirectory); - } - BuildPipeline.BuildAssetBundles(assetBundleDirectory, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64); - } -} -``` - -3. Create your object in the Unity scene and save it as a prefab. -4. Add all files used (models, prefabs, textures, materials, etc.) to an asset bundle by selecting them and using the dropdown in the bottom right. Here I am adding a rover model to my "rss" asset bundle for the Real Solar System add-on. -![setting asset bundle]({{ 'images/detailing/asset_bundle.webp'|static }}) - -5. In the top left click the "Assets" drop-down and select "Build AssetBundles". This should create your asset bundle in a folder in the root directory called "StreamingAssets". -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: - -```json -{ - "Props": { - "Details": [ - { - "assetBundle": "planets/assetbundle/rss", - "path": "Assets/RSS/Prefabs/Rover.prefab", - "position": { - "x": 146.5099, - "y": -10.83688, - "z": -36.02736 - }, - "alignToNormal": true - } - ] - } -} -``` - -To scatter 12 trees from the Dream World around Wetrock in [NH Examples](https://github.com/xen-42/ow-new-horizons-examples) , the following was put into the `Props` module: - -```json -{ - "Props": { - "Scatter": [ - { - "path": "DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_1/Props_DreamZone_1/OtherComponentsGroup/Trees_Z1/DreamHouseIsland/Tree_DW_M_Var", - "count": 12 - } - ] - } -} -``` - -You can swap these around too. The following would scatter 12 Mars rovers across the planet and place a single tree at a given position: - -```json -{ - "Props": { - "Details": [ - { - "path": "DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_1/Props_DreamZone_1/OtherComponentsGroup/Trees_Z1/DreamHouseIsland/Tree_DW_M_Var", - "position": { - "x": 146.5099, - "y": -10.83688, - "z": -36.02736 - }, - "alignToNormal": true - } - ], - "Scatter": [ - { - "assetBundle": "planets/assetbundle/rss", - "path": "Assets/RSS/Prefabs/Rover.prefab", - "count": 12 - } - ] - } -} -``` - -## Use the schema - -To view additional options for detailing, check [the schema]({{ "Celestial Body Schema"|route}}#Props_details) diff --git a/docs/content/pages/tutorials/dialogue.md b/docs/content/pages/tutorials/dialogue.md deleted file mode 100644 index 63a7fbb7..00000000 --- a/docs/content/pages/tutorials/dialogue.md +++ /dev/null @@ -1,164 +0,0 @@ ---- -Title: Dialogue -Description: Guide to making dialogue in New Horizons -Sort_Priority: 50 ---- - -# Dialogue - -This page goes over how to use dialogue in New Horizons. - -# Understanding Dialogue - -## Dialogue Tree - -A dialogue tree is an entire conversation, it's made up of dialogue nodes. - -## Dialogue Node - -A node is a set of pages shown to the player followed by options the player can choose from to change the flow of the conversation. - -## Condition - -A condition is a yes/no value stored **for this loop and this loop only**. It can be used to show new dialogue options, stop someone from talking to you (looking at you Slate), and more. - -## Persistent Condition - -A persistent condition is similar to a condition, except it *persists* through loops, and is saved on the player's save file. - -## Remote Trigger - -A remote trigger is used to have an NPC talk to you from a distance; ex: Slate stopping you for the umpteenth time to tell you information you already knew. - -# Example XML - -Here's an example dialogue XML: - -```xml - - - - EXAMPLE NPC - - - Start - DEFAULT - - Start - Start Part 2 - - - - - Goto 1 - 1 - - - - Goto 2 - 2 - - - Goto End - End - - - - - - 1 - - - This is 1 - - - - - Goto 2 - 2 - - - Goto End - End - - - - - - 2 - - This is 2 - - - - - Goto 1 - 1 - - - Goto End - End - - - - - - End - - This is the end - - - - -``` - -# Using the XML - -To use the dialogue XML you have created, you simply need to reference it in the `dialogue` prop - -```json -{ - "Props": { - "dialogue": [ - { - "position": {"x": 5, "y": 10, "z": 0}, - "xmlFile": "planets/path/to/your_file.xml" - } - ] - } -} -``` - -# Dialogue Config - -To view the options for the dialogue prop, check [the schema]({{ "Celestial Body Schema"|route }}#Props_dialogue) - -# Controlling Conditions - -You can set condition in dialogue with the `` and `` tags - -```xml - - - EXAMPLE_CONDITION - EXAMPLE_P_CONDITION - - -``` - -# Dialogue Options - -There are many control structures for dialogue options to hide/reveal them if conditions are met. Take a look at [the DialogueOption schema]({{ "Dialogue Schema"|route }}#DialogueTree-DialogueNode-DialogueOptionsList-DialogueOption-DialogueTarget) for more info. - -# Controlling Flow - -In addition to ``, there are other ways to control the flow of the conversation. - -## DialogueTarget - -Defining `` in the `` tag instead of a `` will make the conversation go directly to that target after the character is done talking. - -## DialogueTargetShipLogCondition - -Used in tandum with `DialogueTarget`, makes it so you must have a [ship log fact]({{ "Ship Log"|route }}#explore-facts) to go to the next node. diff --git a/docs/content/pages/tutorials/getting_started.md b/docs/content/pages/tutorials/getting_started.md new file mode 100644 index 00000000..8f06fcb8 --- /dev/null +++ b/docs/content/pages/tutorials/getting_started.md @@ -0,0 +1,255 @@ +--- +Title: Getting Started +Sort_Priority: 100 +--- + +# Getting Started + +Congrats on taking the first step to becoming an addon developer! +This tutorial will outline how to begin learning to use new horizons. + +## Recommended Tools + +It's strongly recommended you get [VSCode](https://code.visualstudio.com/){ target="_blank" } to edit your files, as it can provide syntax and error highlighting. + +## Using The Sandbox + +Making an entirely separate addon can get a little complicated, so New Horizons provides a way to play around without the need to set up a full addon. +To get started, navigate to your mod manager and click the ⋮ symbol, then select "Show In Explorer". + +![Select "Show in explorer"]({{ "images/getting_started/mod_manager_show_in_explorer.webp"|static }}) + +Now, in explorer and create a new folder named "planets". As the name suggests, New Horizons will search the files in this folder for planets to generate. + +## Making Your First Planet + +To get started, create a new file in this folder called `wetrock.json`, we'll explain what that .json at the end means soon. +Open this file in VSCode (you can do so by right-clicking the file and clicking "Open with Code") +Once in VSCode, paste this code into the file: + +```json +{ + "name": "Wetrock", + "$schema": "https://raw.githubusercontent.com/xen-42/outer-wilds-new-horizons/main/NewHorizons/Schemas/body_schema.json", + "starSystem": "SolarSystem", + "Base": { + "groundSize": 100, + "surfaceSize": 101, + "surfaceGravity": 12, + "hasMapMarker": true + }, + "Orbit": { + "semiMajorAxis": 1300, + "primaryBody": "TIMBER_HEARTH", + "isMoon": true, + "isTidallyLocked": true + }, + "Atmosphere": { + "size": 150, + "fogTint": { + "r": 200, + "g": 255, + "b": 255, + "a": 255 + }, + "fogSize": 150, + "fogDensity": 0.2, + "hasRain": true + } +} +``` + +This language is **J**ava**S**cript **O**bject **N**otation, or JSON. +It's a common way to convey data in many programs. + +## Understanding JSON + +All JSON files start out with an `object`, or a set of key value mappings, for example is we represent a person as JSON it might look like: + +```json +{ + "name": "Jim" +} +``` + +Those braces (`{}`) denote an object, and by doing `"name": "Jim"` we're saying that the name of this object (in this case person) is Jim +`"name"` is the key, and `"Jim"` is the value. + +Objects can have multiple keys as well, as long as you separate them by commas: + +```json +{ + "name": "Jim", + "age": 23 +} +``` + +But wait! why is `Jim` in quotation marks while `23` isn't? that's because of a little something called data types. +Each value has a datatype, in this case `"Jim"` is a `string`, because it represents a *string* of characters. +Age is a `number`, it represents a numerical value. If we put 23 in quotation marks, its data type switches from a number to a string. +And if we remove the quotation marks from `"Jim"` we get a syntax error. Datatypes are a common source of errors, which is why we recommend using an editor like VSCode. + +### JSON Data Types + +Here's a list of data types you'll use when making your addons: + +#### String + +A string of characters surrounded in quotation marks + +```json +"Im a string!" +``` + +If you need to use quotation marks within your string, place a backslash (`\`) before them + +```json +"\"Im a string!\" - Mr. String Stringerton" +``` + +#### Number + +A numerical value, can be negative and have decimals, **not** surrounded in quotation marks + +```json +-25.3 +``` + +#### Boolean + +A `true` or `false` value, think of it like an on or off switch + +```json +true +``` + +#### Array + +A set of values, values can be of any data type. Items are seperated by commas + +```json +[23, 45, 56] +``` + +```json +["Bob", "Suzy", "Mark"] +``` + +And they can be empty like so: + +```json +[] +``` + +#### Object + +A set of key value pairs, where each key is a string and each value can be of any data type (even other objects!) + +```json +{ + "name": "Jim", + "age": 23, + "isMarried": false, + "clothes": { + "shirtColor": "red", + "pantsColor": "blue" + }, + "friends": ["Bob", "Wade"], + "enemies": [] +} +``` + +## Back to Wetrock + +Now that we understand JSON better, let's look at that config file again: + +```json +{ + "name": "Wetrock", + "$schema": "https://raw.githubusercontent.com/xen-42/outer-wilds-new-horizons/main/NewHorizons/Schemas/body_schema.json", + "starSystem": "SolarSystem", + "Base": { + "groundSize": 100, + "surfaceSize": 101, + "surfaceGravity": 12, + "hasMapMarker": true + }, + "Orbit": { + "semiMajorAxis": 1300, + "primaryBody": "TIMBER_HEARTH", + "isMoon": true, + "isTidallyLocked": true + }, + "Atmosphere": { + "size": 150, + "fogTint": { + "r": 200, + "g": 255, + "b": 255, + "a": 255 + }, + "fogSize": 150, + "fogDensity": 0.2, + "hasRain": true + } +} +``` + +Here we can see we have a planet object, which name is "Wetrock", and is in the "SolarSystem" (Base-game) star system. +It has an object called Base, which has a groundSize of 100, and a surfaceSize of 101, and the list continues on. + +Alright so now that we understand how the file is structures, let's look into what each value actually does: + +- `name` simply sets the name of the planet +- `$schema` we'll get to in a second +- `starSystem` specifies what star system this planet is located in, in this case we're using the base game star system, so we put "SolarSystem" + - Then it has an object called `Base` + - Base has a `groundSize` of 100, this generates a perfect sphere that is 100 units in radius as the ground of our planet + - It also has a `surfaceSize` of 101, surface size is used in many calculations, it's generally good to set it to a bit bigger than ground size. + - `surfaceGravity` describes the strength of gravity on this planet, in this case it's 12 which is the same as Timber Hearth + - `hasMapMarker` tells new horizons that we want this planet to have a marker on the map screen + - Next it has another object called `Orbit` + - `semiMajorAxis` specifies the radius of the orbit (how far away the body is from its parent) + - `primaryBody` is set to TIMBER_HEARTH, this makes our planet orbit timber hearth + - `isMoon` simply tells the game how close you have to be to the planet in map mode before its name appears + - `isTidallyLocked` makes sure that one side of our planet is always facing timber hearth (the primary body) + - Finally, we have `Atmosphere` + - Its `size` is 150, this simply sets how far away from the planet our atmosphere stretches + - Its `fogTint` is set to a color which is an object with r, g, b, and a properties (properties is another word for keys) + - `fogSize` determines how far away the fog stretches from the planet + - `fogDensity` is simply how dense the fog is + - `hasRain` makes rainfall on the planet + +### What's a Schema? + +That `$schema` property is a bit special, it instructs VSCode to use a pre-made schema to provide a better editing experience. +With the schema you get: + +- Automatic descriptions for properties when hovering over keys +- Automatic error detection for incorrect data types or values +- Autocomplete, also called IntelliSense + +## Testing The Planet + +With the new planet created (*and saved!*), launch the game through the mod manager and click resume expedition. If all went well you should be able to open your map and see wetrock orbiting Timber Hearth. + +If you run into issues please make sure: + +- You placed the JSON file in a folder called `planets` in the New Horizons mod folder +- There are no red or yellow squiggly lines in your file + +## Experiment! + +With that, try tweaking some value like groundSize and semiMajorAxis, get a feel for how editing JSON works. + +## Reloading Configs + +It can get annoying when you have to keep closing and opening the game over and over again to test changes, that's why New Horizons has a "Reload Configs" feature. +To enable it, head to your Mods menu and select New Horizons and check the box that says Debug, this will cause a "Reload Configs" option to appear in your pause menu which will reload changes from your filesystem. +You may also notice blue and yellow logs start appearing in your console, this is New Horizons providing additional info on what it's currently doing, it can be helpful when you're trying to track down an issue. + +## More Objects + +Base, Atmosphere, and Orbit aren't all the objects (or "modules") there are to use, to learn about these other objects, you'll need to learn how to use the "Schemas" section of this site, which lists every possible property you can put in your files. + +**Next Up: [Reading Schemas]({{ "Reading Schemas"|route }})** diff --git a/docs/content/pages/tutorials/planet_gen.md b/docs/content/pages/tutorials/planet_gen.md new file mode 100644 index 00000000..a7c0e3ec --- /dev/null +++ b/docs/content/pages/tutorials/planet_gen.md @@ -0,0 +1,148 @@ +--- +Title: Planet Generation +Sort_Priority: 80 +--- + +# Planet Generation + +The first thing you'll need to create on a planet is its surface, this can be done in a variety of ways + +## Surface + +Ground of the planet + +### Ground Size + +`groundSize` is the absolute simplest way to make a planet's surface, you simply specify a radius and New Horizons will make a sphere for you. + +```json +{ + "name": "My Cool Planet", + "Base": { + "groundSize": 100 + } +} +``` + +### Heightmaps + +Heightmaps are a way to generate unique terrain on your planet. First you specify a maximum and minimum height, and then specify a [heightMap]({{ "Celestial Body Schema"|route }}#HeightMap_heightMap) image. The more white a section of that image is, the closer to `maxHeight` that part of the terrain will be. Finally, you specify a `textureMap` which is an image that gets applied to the terrain. + + + +```json +{ + "name": "My Cool Planet", + "HeightMap": { + "minHeight": 5, + "maxHeight": 100, + "heightMap": "planets/assets/my_cool_heightmap.png", + "textureMap": "planets/assets/my_cool_texturemap.png" + } +} +``` + +There are also tools to help generate these images for you such as [Textures For Planets](https://www.texturesforplanets.com/){ target="_blank" }. + +## Variable Size Modules + +The following modules support variable sizing, meaning they can change scale over the course of the loop. + +- Water +- Lava +- Star +- Sand +- Funnel +- Ring + +To do this, simply specify a `curve` property on the module + +```json +{ + "name": "My Cool Planet", + "Water": { + "curve": [ + { + "time": 0, + "value": 100 + }, + { + "time": 22, + "value": 0 + } + ] + } +} +``` + +This makes the water on this planet shrink over the course of 22 minutes. + +## Quantum Planets + +In order to create a quantum planet, first create a normal planet. Then, create a second planet config with the same `name` as the first and `isQuantumState` set to `true`. +This makes the second planet a quantum state of the first, anything you specify here will only apply when the planet is in this state. + +```json +{ + "name": "MyPlanet", + "Orbit": { + "semiMajorAxis": 5000, + "primaryBody": "Sun" + } +} +``` + +```json +{ + "name": "MyPlanet", + "isQuantumState": true, + "Orbit": { + "semiMajorAxis": 1300, + "primaryBody": "TIMBER_HEARTH" + } +} +``` + +## Barycenter (Focal Point) + +To create a binary system of planets (like ash twin and ember twin), first create a config with `FocalPoint` set + +```json +{ + "name": "My Focal Point", + "Orbit": { + "semiMajorAxis": 22000, + "primaryBody": "Sun" + }, + "FocalPoint": { + "primary": "Planet A", + "secondary": "Planet B" + } +} +``` + +Now in each config set the `primaryBody` to the focal point + +```json +{ + "name": "Planet A", + "Orbit": { + "primaryBody": "My Focal Point", + "semiMajorAxis": 0, + "isTidallyLocked": true, + "isMoon": true + } +} +``` + +```json +{ + "name": "Planet B", + "Orbit": { + "primaryBody": "My Focal Point", + "semiMajorAxis": 440, + "isTidallyLocked": true, + "isMoon": true + } +} +``` diff --git a/docs/content/pages/tutorials/reading_schemas.md b/docs/content/pages/tutorials/reading_schemas.md new file mode 100644 index 00000000..13a6a1cc --- /dev/null +++ b/docs/content/pages/tutorials/reading_schemas.md @@ -0,0 +1,78 @@ +--- +Title: Reading Schemas +Sort_Priority: 90 +--- + +# Reading Schema Pages + +Reading and understanding the schema pages are key to knowing how to create planets. While these tutorials may be helpful, they won't cover everything, and new features may be added before the tutorial on them can be written. + +## Celestial Body Schema + +The [celestial body schema]({{ "Celestial Body Schema"|route }}) is the schema for making planets, there are other schemas which will be explained later but for now let's focus on this one. + +![The Celestial Body Schema Page]({{ "images/reading_schemas/body_schema_1.webp"|static }}) + +As you can see the type of this is `object`, which we talked about in the previous section. +We can also observe a blue badge that says "No Additional Properties", this signifies that you can't add keys to the object that aren't in the schema, for example: + +```json +{ + "name": "Wetrock", + "coolKey": "Look at my cool key!" +} +``` + +Will result in a warning in VSCode. Now, this will *not* prevent the planet from being loaded, however you should still avoid doing it. + +## Simple Properties + +![The name property on the celestial body schema]({{ "images/reading_schemas/body_schema_2.webp"|static }}) + +Next up let's look at `name`, this field is required, meaning you *have* to have it for a planet to load. +When we click on name we first see a breadcrumb, this is essentially a guide of where you are in the schema, right now we're in the name property of the root (topmost) object. +We can also see it's description, its type is `string`, and that it requires at least one character (so you can't just put `""`). + +Badges can also show stuff such as the default value, the minimum and maximum values, and more. + +## Object Properties + +![The Base object on the celestial body schema]({{ "images/reading_schemas/body_schema_3.webp"|static }}) + +Next let's look at an `object` withing our root `object`, let's use `Base` as the example. + +Here we can see it's similar to our root object, in that it doesn't allow additional properties. +We can also see all of its properties listed out. + +## Array Properties + +Now let's take a look over at [removeChildren]({{ "Celestial Body Schema"|route }}#removeChildren) to see how arrays work (if you're wondering how you can get the page to scroll to a specific property, simply click on the property and copy the URL in your URL bar) + +![The curve property on a star in the celestial body schema]({{ "images/reading_schemas/body_schema_4.webp"|static }}) + +Here we can see that the type is an `array`, and each item in this array must be a `string` + +## Some Vocabulary + +- GameObject: Essentially just any object in, well, the game. You can view these object in a tree-like structure with the [Unity Explorer](https://outerwildsmods.com/mods/unityexplorer) mod. Every GameObject has a path, which is sort of like a file path in that it's a list of parent GameObjects seperated by forward slashes followed by the GameObject's name. +- Component: By themselves, a GameObject doesn't actually *do* anything, components provide stuff like collision, rendering, and logistics to GameObjects +- Config: Just another name for a JSON file "planet config" simply means a json file that describes a planet +- Module: A specific section of the config (e.g. Base, Atmosphere, etc), these usually start with capital letters + +## Note About File Paths + +Whenever a description refers to the "relative path" of a file, it means relative to the mod's directory, this means you **must** include the `planets` folder in the path: + +```json +"planets/assets/images/MyCoolImage.png" +``` + +## Other Schemas + +There are other schemas available, some are for JSON, and some are for XML. + +## Moving Forward + +Now that you know how to read the schema pages, you can understand the rest of this site. A lot of the other tutorials here will often tell you to take a look at schemas to explain what certain properties do. + +**Next Up: [Planet Generation]({{ "Planet Generation"|route }})** diff --git a/docs/content/pages/tutorials/star_system.md b/docs/content/pages/tutorials/star_system.md deleted file mode 100644 index 734a9b61..00000000 --- a/docs/content/pages/tutorials/star_system.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -Title: Star System -Description: A guide to editing a custom star system in New Horizons -Sort_Priority: 90 ---- - -# Intro - -Welcome! This page outlines how to edit a custom star system. - -## Getting Started - -Star Systems are placed in a folder called systems within your mod folder. - -The name of your star system config must be the same as the unique id used in the `starSystem` field of your planet configs. Example: `xen.RealSolarSystem.json`. - -A star system config file will look something like this: - -```json -{ - "$schema": "https://raw.githubusercontent.com/xen-42/outer-wilds-new-horizons/main/NewHorizons/Schemas/star_system_schema.json", - "coords": { - "x": [ 4, 0, 3, 1 ], - "y": [ 0, 5, 4 ], - "z": [ 5, 4, 0, 3, 1 ] - }, - "vesselPosition": { - "x": 0, - "y": 0, - "z": 8000 - } -} -``` - -To see all the different things you can put into a config file check out the [Star System Schema]({{ 'Star System Schema'|route}}). - -## Vessel Coordinates - -You can warp to custom star systems via the Nomai vessel. Each coordinate has to be 2-6 points long. -These are the points for each coordinate node. When making your unique coordinate you should only use each point once. -![nomaiCoordinateIndexes]({{ "images/star_system/nomai_coordinate_indexes.webp"|static }}) - -### Hearthian Solar System Vessel Coordinates - -You can use these coordinates to warp back to the hearthian solar system. -![hearthianSolarSystemCoordinates]({{ "images/star_system/hearthian_solar_system_coordinates.webp"|static }}) \ No newline at end of file diff --git a/docs/content/pages/tutorials/translation.md b/docs/content/pages/tutorials/translation.md deleted file mode 100644 index a4702901..00000000 --- a/docs/content/pages/tutorials/translation.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -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. - -All translations must go in a folder in the root directory called "translations". - -In this folder you can put json files with the name of the language you want to translate for. Inside this file just follow the translation schema. - -Here's an example, for `russian.json`: - -```json -{ - "$schema": "https://raw.githubusercontent.com/xen-42/outer-wilds-new-horizons/main/NewHorizons/Schemas/translation_schema.json", - "DialogueDictionary" : - { - "Fred" : "Фред", - "You looking at something?" : "Ты что-то искал?", - "Aren't you guys all supposed to be dead?" : "А разве номаи не вымерли?", - "OH MY GOD A LIVING NOMAI AHHH WHAT HOW?!" : "ААААА, ЖИВАЯ НОМАИ?!" - }, - "ShipLogDictionary" : - { - "Unexpected guests" : "Незванные гости", - "Visitors" : "Гости", - "When I went to sleep by the campfire only Slate was here, who are these characters?" : "Когда я ложился спать у костра здесь был только Сланец. Кто все остальные?", - "I met a talking jellyfish. His name is Geswaldo!" : "Я встретил говорящую медузу. Его зовут Гесвальдо!" - } -} -``` diff --git a/docs/content/pages/tutorials/update_existing.md b/docs/content/pages/tutorials/update_existing.md deleted file mode 100644 index 7e67ea0a..00000000 --- a/docs/content/pages/tutorials/update_existing.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -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). - -Only some of the above modules are supported (currently) for existing planets. Things you cannot modify for existing planets include: heightmaps, procedural generation, gravity, or their orbits. You also can't make them into stars or binary focal points (but why would you want to, just delete them and replace them entirely). However this still means there are many things you can do: completely change their atmospheres, give them rings, asteroid belts, comet tails, lava, water, prop details, or signals. - -You can also delete parts of an existing planet. Here's part of an example config which would delete the rising sand from Ember Twin: -```json -{ - "name": "Ember Twin", - "removeChildren": [ - "SandSphere_Rising" - ] -} -``` - -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 -{ - "name" : "Ember Twin", - "destroy" : true -} -``` - -Remember that if you destroy Timber Hearth you better put a `Spawn` module on another planet. If you want to entirely replace the solar system you can destroy everything, including the sun. Also, deleting a planet destroys anything orbiting it, so if you want to replace the solar system you can just destroy the sun. If you're making a brand new star system, you don't have to worry about deleting any existing planets; they won't be there. \ No newline at end of file diff --git a/docs/content/static/images/getting_started/mod_manager_show_in_explorer.webp b/docs/content/static/images/getting_started/mod_manager_show_in_explorer.webp new file mode 100644 index 0000000000000000000000000000000000000000..a54601f7be39bf8f8329b6d702ff839040b54778 GIT binary patch literal 14312 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

X{xwQe`&`5Ce1spHneXs-ws6 zD$Vlj*w}i%brm~NH$}w}dh>ezLMu0*P_}zj)5EU=!XNNN&0F0k8PQy~)3M|^2@VsPyjBCE2R=`j?kIhr%Qo;cq_8ns}`um$by zXDd1rpc^>U_t;+g^ujT#;ulZH#pbv$?tRoJKOWDX(U5{JB7zAqfBl2%EvH%l;#; z1&pOt`6hB4C#FL@C4d4QogQJ5-jEcMklWsi`f_)hI~eET=&BH4_OVYB?goJ|m+ zK#|!@sO;Krx-|SoT&|!2VAZn)CdZ?woA?F8`+2ndyc3X_1U{!_ZU2?z)U|P0nye7& zNFpJ6B-$R^R2dF5^81j8HTTGk=77s3g^*`rhrSQalI=VCQPG@u^;2m^QkrC&1}D&7 z*4(cfi#3r57+`zfXsVLxs|<72Y`v09>JAbCWfn|c|Hc!%u;LofE?cKDunyt2$%GCA z?99`w;pFNVhUD$+-D8yBDm)v+;O-w(4lV{k0zq8(<51=pfd8&|yZcj564G^h@uETV4d#N1f>lT9S~{TEC}KB?P-n9IBlx zYwhqm*ANt8J7Yy09lIgFhb#Jbv42gI{`WVtGu?P$TF~4tu&`Zf>LhnhQ)Vbyj=X3R zf{DkF?H(tU`OPo<`h)nvc?FibmA6R@MIIv80>P@Ie8&DqS-mN``;HjrhEC_Ai;lvl znxB~TBPIBZA&G0`PQXGBS0J^~z%#9lI9fSsbd}DmDN~Ogs%1~I)&pi8qt?9GgB14Pjmpu&QN;Sn}En-=L`1ZH9bU+k>v-Z z|GJxkETqMSlRvFbrgcNYoOYusDD*s394vm&I&10no`ZHLlkvtzIdyxxk4m=0NV>fzjx^NLgz=aTqVC zZ}stPEzD|c6^t-HOPqO*eZ1*=5^DiL`3$R`UgKH>Pc_yEHb+IRluM@@1tZx z(K}an2R)r@YE2@p08G9fc^0`iU1j#o;_?IzM-b$#rpJ#%UsStbhe+sO)ETf0#Rdb- zQVF1-L_`3wb%d7*>uY;V+F(+oeBG&af|g|G8VfkW-KBIpOIUM?pzoyL*WA?rQNiPt z)c{VyeT&tft;%`iOE_2-;fVXes7+DN6<9eeoZ?`=N9!X#cbWTv|3z0^*8HaF3~R@c zELZm*Mn0Q3%X+~jL#P4-h#shL9KkURe<6l9Xnn!U_oR0nu;z&z>QC!wDB?M%^`B9pm+Q@cr-bR#JCmSzz^x6||CxOJ~%% z5wdM_!ev~0LWoAhd+Fv>;WH&wQ!YP~nx`N@D2gQp8U99U zv$WN*p_*Fw!Y;VPK<(XtX?@`t~h1dOU!=Lx7hcC64X*#w6|X+D{~>Yvp_ z+h)UZm-JN+b~flgML)!{!awM=3bl z9@Ld=Ms+f)9ShH0_Q{6yr}&+BIy@UMdBz@a0q5qPy?z=g<>F%tn zSP1-*IL@h8STCqq1=o-Zv(8@d$AaW6-Pi`{s;OhY;|ZU@dJP%1MW1`_>2#J&^!$e! z+uQ@p?~CuyTGiK27D$FfEis*_+5zJ0mH?)RwvMef(*P&Dv|UFx2N*&LQ)S6ers)02 zl#Q32lYS;w@?{1FGI`z`>ZLSesFJ? z*kgDNYv2T!ht!TLmd~oAn`hAb!3-MRqZBMjG~b=WLM{@kfg$d;DtG{^j{$3eYY1ht zvNTMm6amh=ZlKF9!&$Tnt@*Bdx#0-JN0==xL+4I+$~yORS&L@^L7jCyLGF7hx`EZw z<6eI)jNuAJ&B|DDQ+?+cfg{&2^&i|8qW1dxJ3M7YN=9F58(`R)=36Eri18fW3{`NV z{e|pP9xXSVlG1BeCGn@IfC?GBIU+yl71}eQwCBXg;XJ&cSA6nNWXb0*ZWz=)-s4GF zO0_WDnIAms^BG56NE=$+VlAsuE`O`v#=_iOlxhtG+};v)k>@e!JDfmT84z}m3)Zq& zJ5j4RPy!MKI0BU(nhPKzzF_j^NO`RD%d}*6wv6Jjke1ZS0YbV6;{EVz7;U@ccdrtc z12{&@-b^Bw8IL1lgHFI;X7FhuT~Z;}?rx81HiU2AW1GB<*}coh{d`BfV3aFyOv-|7 z#iRk-KgvKb!vKVQl~9gTZxem1ucr7%EqCI-Yyctb0*UT-E06{=hmnuAY7C+ z-z+i;Djps4CdSN@pa#!O`8qQ3k_Ev}pF=u)VOdA+0 z;|W$Wj~R`MR6{-Zn<#N4?(NI#yV+XPCOmH8K||8bhABUNJmZo>a)(go2~Rh!8JN~z zls9>X-p8iNNysYgflIg+^ZecWdcNRz{AWvF6PvCKwbvXBD4Cj)YKVDt8X(i ze2xeL0wA83)j^t44@RGrupYKvcO5S_A(o!uL^EraOU#ee90cx_``okA6Us_vG>tV- z*0i5`?l$SQQ+CItor5!%l#Ku=9nQ_FBtA0agNf}EI&M-ReIY)Vr%9qw=)kXv2N}Nf zo0e5zK}Z0cUAw}9mvk}%Qv;+$f>j5bahTr=Z(37m*V?>E&RZ2x1y;lW6mt?91K^_o z(xHxt3#l& zH&B`xxa_`e!CC^VF@`bTiyg51mL`aYzNu4lqkm}GIK3LlW7A=G1kAR>LvraOG5XuM z86Fct(f7sk%S6!>AdpU%3MqdRGGwBxmxQE|^SnO6!!?MDBolfkPERK2M|7T;dWKM` z_{5*gs?0lf+d#l9HLC$I zd7)5hvSPh@p;#|TeuizkKa1K4D#4ynqD+J*;k#P}prQ{I8W8<&?8nKvjZubgf?&T0X(9%1o+9Z2 z9#|qn+g1P=lX>kZ8D~?+ksWO`AvTqDFSfew&Zhmtg>?mCTOuSdEC~7=sNx#>d{-8r z0##u#G(Cqu!)TBq+!je-QtbUEu-Z#U(^Wqd!!>X`=upA-F7TW&&ZXJa5M%)jurhBf z=MFzJbOkoKA&O4^){W=IF>LxtHKvLu*x34`3ZfvxVROIRn{1EI@{vx6QHce=95@YN z%A%8stykPQ6Gmt?cIyv-Q9)Tn5{ovx2j>HE@AqGCO*!?*KAe%)qhocB(=G?04;__^ z-Cn&I(a(;ubmuIRkBCf+MCZL89IR2*PLgX>9#f1fQ0_T*#u2HQ|FpOlfO%MbyrhQc z1(SNFcC9v%M4IUdPe1olxg9>@@UL~EAII39M&!%V&@}Zj}4Bn;ZHYRrpf@!W0O<%L1aL#Ch zyOV?d%Fq8-TlP%I7Tq!h0;`>HKQ9XPJ5#%r{au-WZpwBD(O~iOAm_(YA1AXwVz5>L zRKMKdSkllvQ-j7OnIpP@Sd~4Z_UN=9p4HC0D76kz2h+ahXh zSPvwuKEc{TE;-!)Lb+ockxgpZ`4w>eRT$4$U4T(55>0{CJPHZ)kDU{|Rh4}gvC`~v zLuQFy6R#yPrKSfi#A$qB&p>>Z4;lzyv<}g7!>oo;#p(8z=k(T#Pz+7x=c~OOvdNQZ zt*xU3_AhGHZAEal6QB-BctUP{^U%`jMEw2Fv}i{|r9aa@XegQ@zA9BJW0aq-9R$PT zoC^V^bpcPJ{1ft&LF_JO!KXzmmIM(;e6wamBqqB{YGo~dHP4elRla$xL)c?baztCB zr3ql33X?`x^zDq+SfX4*F1G`3I@fyUUTUs=h_DNYyO_Jb`p^)z=xYWM3muIDI3cep zkGQ0SU_y(YczuPkv`HzzXYLeJ-Gu`_UU%V#4JfqeR39q+>rK*e;tHy~!~wTUn@Ow8 z--}>ncu!oWiVhT*X@z?jf#wtw8T^jmjB()eDYD3D@F~d`kd#1Eemes?np5pwI$CQp zP2^VkiLcmuWqNeny z1iC6?8Z-y_rOK1+8X&5^>F*<7ATb0KM6lg?Jahg|H@w#B%5d%TX#q%TBFsoBmE3`5 z&73QnFXPhch=)ElZ#1ga_uz`*h=GxaVjpkJfZ#svQ~r5t3xQ_2LXEj@IS3nnp+>!R z`o)J!ouQ?G@f3ie2KhzNY|)pFZt{ui5*=^7W;-5SRVpxs5ZIw}+Pa)RjBG)xklsu- z3~Xt?4r68bD;)2~3@^dV(^u{BG+|ij5f`Vhu#yQ{iwmhA&wm4x88;tS7ozfNuWmF{_3HJV2E@AK&^k;oA9>anj@*(XY$Lw>F;Ed85O1 zpSz}7gH#ibi~sR)=-&BLrP+VvuO|&Aa~rEmxCxcBuIa>K@&ImJBb;3@iPG7|y7ix> zVK;zHbClgDh{m}d6wLk-2k}E{{(v=CSu-rN_PJmwDB>WhNs*9@Ku?MeT<4_GgJHei zml~Rn8SKkl)E9rXHTbZQSyNE#eR&}8ThV)jQ}Cr3BAfwU7ro*AzUnKc>awOj+klEg zi)h=-c3o}N=dT0f_-L2A8@;yA2&b||^ z88?R4V-v#SZR2v&rP9Pl+_aq}bV+oV2du@pTP=P0R3U}+j~es}@bSDg;ga{Y)Txw9 z(eJ8FsV4}5+3qm{7=w~kQlUT>>`$jqr>No!fBNY(XlDbEeTYS^YZYavAEn3%? zw*l0JQ2N&O+8Wdz7}4p#jr)z;atkt+m3SsA#`wdeKHb|z2VMqT0G~0H=hxQ? zx#tDb%KJ~il6GxC2hm+CIhErLX;4kws9LV*74m=WBB_lT>cK@=pA0b<2>MEC?2d1N zM^FcWDnWtwJ%+A@U*(n4}GPK~`*SWm{%cZ7FpTsiG6>T|%wcv$Z(5{0LSG*jAeGf@Obqm@kjj?0ANH`9^bWIE4Adc!f?Kay{g6wlI{gNB{r|Q%;~o z1_E#a5+0*cUz6vM}C8P+dIcQ7a)%G$v1Gk6P^XhwG#rV3EFyX21Y!g4FkHjF$~vi z>A3>-)x2_vTAEE!CbfbgaH3kX#ZiYFrm|eoD(?O*4fWms4N!eE_H6Gpjsqaq(Cs3p WAgT^R2%Zc&Py*RNEOqbz00020@+JQ>YFV#?tPc!vPE}Ft_nDg$qgm2kHXfi)iD7uK7J;XZn+>Z2NgDkLZz~&kGsY zdkpp%w;1&Cbomu17?iuzP0dg0w*dq`k$^$ zU*G%HN8|fbpdVIm*#qc%*h|_o@S?Y~cl$&0kawzp#A&NtNli< zO>Yh`894Uoy+iim_sH}rxZmB>3w%rXm;wSXhu-{v?^sU--gCm2lZsgIF2P-@PennS z>3NLUComr6S5i=&EJ8XgbEr2eTZu?+M*eNaMHGAXQ)lW;Fo(-kym-WYT{+;ExXWZU zpkT}sBAqRni!e5I7zn$_13bmzn*-s(Yen%9R#V4c=8zbZ($BwANQBf53fcVf_dUur#c)SRYS7%TShg9atU*hkdNY;6EXLGNXnI-Fr9jX#I|%CFxNDG=}E&m9kKrhujr< z#Ecd5WT#jDDp_v;IF|@)8+>e=aBw*CoC}#ytjaP0(NdeKTZe3=0QSmx8()7N`_j(C zUvHT@+E%nH4To!Uvr?_VAU*0gGVHz3Epe#&#nn3@B-?{^dWXaFK?BsKHz9$8M_fc> z^CB>(b411i%N;Fd`{4B%j5>DKi<^@ovJ7!XZsp(>EN&b#a0L&Rt~0 zg$=dPv)*79Dv@gB5KK}1MMQyz8B4||_#ArHLYkOXSm>=Tg5%?DBR>SaudYO&LF`zb zGap(u1W|vMm668^GhIvmDj)hxm-|&7l?T5rm2E)X9LHDVTz5{sq`g}=w8V7X7A9Hr zk!0dq(((_z^6oN(2Cdgg16HC?lFI3?eugf5EAp{X;1vI31%kWOm_1-q)$Ya-OZj5s z4e5;0siZ-7W8>2zbME848__=adOrd>`7*vdImeyQ<5Av?{-E9L{|p;MvOkpIxY2M( z`R}OOk^b)pYccSl4C)UjUE<#sYMduFw=kmPe~7CjZv7WP{0mq`nEnQWTjZzw!cws@ zRT60TLE_pyI zABW_sLoxN;C^d5b*O}2FeiD!{{R?7^X(+LO%=EB;D!w3wy|2gQ(SHKaHBCW6E&5T| z-2$872u_GKslvJg&xD34;a8uKJN_$;|HCC+KCkZ$^lFDA>N%GN*R|0Ij2aGn;-wvD z|1Wj=e_5Q3!S5lmNCKp14Ipd$Jx1B|ZU<3QQYf03-Tisg1hK6&vRsM9XSp~CGfu)8&UDlf;m`t#^DnpFpNbQn4NwBDYq4TTr z9^*oYi0mDAInxpsZk@U{%ht_ne-%ugt4!gUXeWdH-K_ja3JEhZEwh3%+MvSkv+SeC z0~h{<(*LJA;Q2Ebpj4Gm?QoX!nQDq*I}MrRAtw#1z&M?d0LFIq0 zLI2r}^J4D*_iFr~fasU%1NEm?`Hgty+8)^59^kcW{+&Z9{ay3gEoJt%Ir@+KiEI?4i?dPLD2O08RuDH=uilfu@f5&mZRY;} z_yUEk9CL$iA;c?&!;Mk`cYUGwRv3%=zxJE|QssZRH-TT>zDpj7opfI z?HWFBh>xLxj8{^)uV)`Hh8p?UU0@_5L~3(L!Q!^?+AVLz7!-6y7=zkg`{+Z+Afj&9GdB{X-MilXIR1#bxY9sv5(oZ)3>6!npQo0vxNCfXK zjnMOz>~9|FAG;LluQwoX{g=7spEvZMpXd+x1XgVU01^sb-;t3+J0lh-OB{ntu6J}<=OHj+D9S>)3=FCQ$DA$}~K~<%-$3DVed`eIR?n-g; z3>qCR|Hi=Ubj!CQ@~-APuI!y-@X&4agkemYcJFECxU!7~`($xwWEf>o_!6#x6=l1x z+~Q}0RMig4!x_eNV<>-uVTh>p|n)h zY`*8AXLoe5DESm~Gvlp27+0=#yQhv+|xZ~I8 zo`7hyz?yD;dfsbnU%~h1<-c zTWn712wq$=iEZ0oP?dL?(`nN|8FI=0!()UP@oDRr2RZB5m zdFQcopS9fMw%!|J#j1o00TPVRYxvw+8i<<2{v#&{tvwYjrB#i^x|{N^EYkQ9w@f2> zQK*m+cAXvNpz=e7pvt_4s&DEm>r_H|LFpM2ATug2A7PfL4{?o30bCq*+`XO@8*(ux z?^FjdoVeXz!*8_t-r2jqVP{22;VM_aCx4fe+f*{Oyj&-vehF69q(E8Jlf+hK;Jhu% zAC3~>nOZ_|W-*Udmt`_1G*9DWgXl1ODcOFIeH{q+rL5lkn1Ep&r^IDnW*0`VMbYGkrJz~H z=AX)cHC)^`lVrokryPPu>9e?Ls0!Y-#vP{`1tn9=tJikElM}LZHkJvI2bN73cy7F| zfWE@g(_Ct{kZi#ZpZAlfK6IMo1rCJ98a5jxh?@dzlt&8NYiyhOqyjp>n!;NI%A-lW;uRGH-V zrBVqK`!%-!6zRI&8&)`fO&Vw;BMQk~?g9@bb4ed&JShlImyMPj|DAR|&HKno`#_LK z%164(<96UTE*eUlW7)Un!wZ6uln^EkFbT6;*!vjWVx_$NW9K>5qgE>Wwwz6j-lCQ7 zHY{cDr1SfZcI`qLn%|>vj8epR>TzT1l==N#(a~a{ zCl(SuNX1EU*kDEt zI~GbiZlQ?yG6KAL9(Hq){AVuw2D!MSkk(YrWz5Mfh{d92kSEd-OQ11M5P`&{;qB3! z6@3u4T)w8lQdudR`$g*uCy`|@B@2lg=W6Y)t5C&sBIarxtRDdFSGd81c!f3etoJ+o zk_n;p%Stddp>uVDxDQ>UV_nh1Y2G*9J=}144Nz@IKM45@PQ$j2MH;LOPCyyl+5B_DEYFxHxd)*Wn;1-|~czWNYa)E!lH zYwg4spbXq-{ySlqMBp!)`Tg{}nK5?^?;T%$t7w)b#Iie@RQH$d1YS}kro5x@FCzEd zIlLcXofiOEKseqXrZfuc%#9HXwBG`tACID;I^{%yCWGbIR)i$q7ubM3#<9AV=l7z$ zuum+jq!k@YP|cUMD$^!3Z_qkrgp!+Y#_eJhcvQ_Jm_RWW&3Q}c@? z)~{2`u~GyZuXsr%obyCMm#~u4{2W8_Qe`esKlZ3){lmYDB!OB+-EvhCv?92GUp2*O zd4b7M+cuw|pjJ!Md4^w=-v!^4tLktV{Dv#^fX1P0G!_uzStwqKFQfL<%OcI!u-nOZ z$+T{ZoJwr+ly(d zsi%FM52FflyRz50z!&rQFs>GRwNJUcN~9Tn_fda+EKjv^Wf{e{NXfwquupo{pQU;) zMR@P&HsPD7%9=gx2%-qiG}ef*7chbb;AkATQz|_(GB(D75eoB4jM@qFdLs52PCO^J z0Y2hPcVCk-h`+a7abcc2VZO2l;dLvhvyO^sqTpVh5v;`=J=cF1)E@bx==U(XcxP7`u1z zPV=YRw*kd2?Nf9H4{teMv?@)Lo?ts&tl1Dg#7V_Jsoqzl`H95Hm4s%ZBY2;guQ*DP zcTog=kYM>actl<%tc6Tws4!l9neM{dm^RZAGVhsi!)>D;emKz=HbB-;r^^*1gS50|ZuYS|fU+RiSqlM4AG7FadN&=4XRl82 z7U@^}l8|o>I_k1FKGNq4z`LwH3O@ZEfE~<=7*;WCZhLLfK7+`+x|RF|d@7De2s}DC zP^kxNHVO-SW|ddZkZ7IS)wthz;Cm2RHEWe9+%Y+Za#7{G_3&5Jc%QGVk`p@);e{^- z@Z)2P;76}R7kn2HQz;HInkdNmyySJbAjTOnbN6JD^B~;^i7ljRD2KzS)MTw*QCVm< zV_L)U2YgQ75~MwNymZBS^EkJ_lJeBD34`Sxz?}dH3+F9iz%PAQ>Dn zLwhb}^W0VRa7}z^=eh0_7WyPUFZ0(h6)gAgh-FfetuKnI#Dq#U^y>3@MT~WD;1NP$x(mbPh+A1x!j=ErbtX9&fAMnv~(P( zCDz0Iemr2z1d9T9?J+9?wcYg#qZS97K0Sv91Cx`0;ENbxKyP;0mg_s3O*fO!+Vz9@AbiReL z%d<7A%rx6{%(jnZn69R0S@2Whwy;GbWiO?|AaOCaJL_$3`w?xy>eQJ=syE&?0wmc= zgQPMC!&lhz!ba7JTn1j>MONvU{%6)728tEmg>ms-2o6*3`kjYZtI!SE6rbjY_vCLd z@Afp-Aq3BDhrsuA6{1BsY)er5)WBGL&DLbe(g&T1CH7q-Aq8wqQzQ^5Q1C%KFxFu` z1CRmLc-~M8qML3O;ESZg{bQ=cIhSK?O_Ln0>-qoy5bXKR z4fpG*HuJOb0RDw?&T{99d;PG72dZsKxq85#1;%+j5tTfFOm9OF1gF^anU#B?+`cQy zmNo9yN|(|4;4;}PCdwXLUG8%hD_}7O5$}{hwq!8w(y{D8w z0W&97HdF&|EXT6CZm^kofN{0wx`BEmyc$B;oLwvq96T+7?2R$zW!lh`ii4u`jgn21k*k~M!+Mj&8NTg_EA3Xy{qTieG=5RYM_--R*A!4fkO zAw>q0K8vaS86v#HyX|IYG@J~Ki-W#=nAC0j#A3RWSKmP?!3#FAjy^mS9n?}&y{D_3 zPkQGD9@y$Y%e`Gp=Jn_!HK{stUB;iMTL`W(4vL>@qS0L1R&KCFNKg6$0D4YiqWI4U zLbd2n-u@2ZiU_#UK&~1^T$574BBBtxpJs&$*3>D%(6FWWfR4(*SIC)XV<~D#OI4CT zmHQQBhkFeGZK9axwfc2x=*pkji(|BGaC3-$)mb`@=&q)@kTw%%g)SDF z#A5Ez^ku4y_Dgkwa%znThX)Ws1FOdb*wSr1-_l#eCS! zy_49r5D1O%H;ENQVWOEBcyu*A+KuQ4g+3x~yn=ZHB384V#t<-N+;IXOlO=W^DU_Ww zFqRiVp?J7Y>cF$R+H80us;e^_1g%23?TM`;C>@mOmo0bxWXl`TJ=T&s%$ghi12F2v zZp?YP=NvTr=2x&_CxdzGpsFJMasCT0&$zoNB4X{!WgDoMas)Mm!?$dU8k$lF&bk z0U-fEJ-iwDgAR~XmXQ251XzDwOGdmHq9oc*_S;Od+f3uezM&x~E~%13E_(@YOm&HR zP4O{lNspeRQnGi;&&%3Q7{%!&xwrX4ZB&K!PE5uiv<$qsyN9S#*OzpSi(VhtCHgN0 zT4;QQ%2++>O}FI83!PA}asw8w{Q3YGq}7b9aBJ+vP-QjqlY)*$sJp8p=f+3!A9Ebe zx1<6;bBmeS%|G|FKB{8#h^NYqKu7SbYVC1#6wPwVSU6&nsC7S!J0YE5R~=w z5T*{=&72CNS!tiy?8dY?E6>LaLpQOrB#p69R$0V$;^!Ky-86-9Yu7tDB}BGdikvy^ zV0ws-?{75p7VQERG!u#HZ?`Vd+=KvWGbir;C}h%Vbvc2F*bM6U<1#f6AlJ(CXllZP zDNuPXyQXfC9Hg**y4KJTyqE3OQtC(Dq>o#1C2Ezd7!-YKmvl!K1?LK zh>I5|eA7A&oVl8|-7+I~c}&S0Ig!d-v^?ST;=fjT5B({;~OiQ}e(Sveh;$0ehQ*_l1D$SiXzD3OF z*b$yaVE6^__b(h=_U+r(UR8m2nk|98;7a12gnpGyhBF;&CgsAbw?Wc!Z$2i2=7P6L zvY=9ca{2=5<5y8&694uLR(r1}fQNcfW3@x99jg2B*j|mD_=39wZ^nk_LRw!P_<(3M z9caI#$=**F)Vzublk$Qm#-HYq$cgKCC={?G2ES7Jl;W%ByI=#y3!8kyh)c*XEu>$w z>C(Lhcc>gu52DxGIt(Lu#iqyJh#`eCjEYBHJVH(Idp+-5!)Jiauav+@?a> z#K<;=h|$FwwMjyW*_XP|$lS?357PsUkxPg*OmmJ+`%wq0GOMzLA=O=Fu1pZ|-P6?|1*Mk9fh`wmWPgA?~h8s+ev066L7zXm>%SxOo^se?Z zRJ@ChT_%(lr_qOgFkz<}YeRzqdlU9%BxLN}ybSPj?wO8Mw3i6(bKtQJ!CDD>th^y| z83-+eN-a15kuDprGGU#q=S`bf@rT(MZ`+3cMjb-XuLl9W2LNlWf z@tde##7O+e#&MKv)k%@1N0@W9-3rzI*#+b1U$(&+A}P=-qW-x6K%(PSDtewJMkIW# zJjJ?Am?|gp>a5M(O_YzF80~S!pSYHswCl7ZkS7?OrHXlZTPTOjXDE9+TK~S#vGSM) zKv5Lxe8^sp|9z~(2cz1bxPKSA-l7{y1}bBm@Po?{#+gPxa!h}$(ssG=TX8;qNKya^ z!{i>%3pAS@;L8QRUdsYC3<~@>KlwHs+x|ITd5S$ixx~^2+CvaN#Hu;N1@5YBm-M67hf{`1C4pDGV`60JqRD;wiG9a>xUZ=4fl$U3SDS(t|i90UF zh;(prqJb{ZS7klCd>YN0ZMedkx`F%K(rt~EP;yF_nL2|789@_?)!G!e-S%+4WKDG< z!$a(}nNKqY1;!DVwX%IiA9jlC{B|B#FpSKE(LnhS_v@1I-PWXsm<@G|1+%{K(vqXS zyAY}mfHrOwZlv4( zBFqN(9xtP1fBSc4Zww*|b!56*0$&pn5LTTAx05k$uE?|T<$!bg<++yB z_KvB{4q!v3e0Ud+r<9M2>dgL&Xo~XOef})dbkC*QBTwfzv=jCfzjNv&R_VmA}HR9 zTM>m*L+Qu~b_MdJ9A&`qBu3vM9lNf4HDe6)WT=gWl6I{{<`{crEoSK z_6JSJ20HJkPh5t$gH(cLA80Z`^s0ZG)#wpyit6hcZ%HFUbVTGc@mO7s#M-h=+80T| z!*|VKjaIEypVavQDu_-S-{GfB+?dnZt_PR{7vUt&xh3+Lz{$h)03lpxM$ftRSyW6B zx(KGHg$y{NdV5VjOrFq2EKD>$J}<;o1m3Qq!ILct2!n8&UIrhh)I@55RT(L@1V$uw zgJ(O5)jF<1`0i_b7w#a$>nnyIjl> zcFc?Ai)$D$7gxFC0<==zggpCJ`QA51O>H;lLwP(LVbjJ6X*_Dso;!6HVJczGJOBXu zFUcK9%$#6ZqggNoMSZ*!$0O$7zR#5kz2FPtpI95uf~D6E0I*q%4)AFOnsqn3KlWU2r07uCbo@|j za~FK8{1eA@1F5=De|Fy5L`Oe?6(EOL^5fKsXZnq-no1>V-;zMx&k|VzVUM-UmH)$g z`)Q=$Gj)$jd_U`nVwb=c#x}%v}4!ezCi>JLVk~NBSoi%*9Hn8op*c$ zIgA>DVhYC3JyF-!B0@{?jS+xzc2p@~`p>69nnbgM0+LJy&Z!WrUwH8u_c|syk2u1K zOPcr23nNFXF_8%YEu3d`S}OVD2ZXo?&JBpAw<&Xza~YAlRgN)Gx*Y9oLwPTbHQ-Fg#5a=s%~?BzX$@iO9*RsxJ0~kyi+(BpASW()0iFT_1Lny*Lj|A%CYFk zILZ0#fk7H>D3xkXwtn`xI9X<&CsTUug?zPtC?q!50Z{$NItlEJ!$Hj0` za6x4FGmYTai^%Or1)me*4JwpO3rL|{jo`V{OO3Ck2KCw#b?fMS1P_0&#P1oU8t&U2 znrnHrW@d4=8c2F=VG&{6^3C*gaO*~FBJ6uc`PM)w=v7fTevnm6QPPO4Z)?D??N9uY zaZqCY@@xc3H1=bUm z9}Mj+zB=}4BMls#OQ}W?0KrCdRW<2YrI1B?&qrdD)|>MN{!{OEtv_rY)ovEVplP<> z8Vf>nwZ(chYS@@k-#X1@ByKm_k8iRNqC$~{pXHTf3ogRm&pvJD=zWuZt<HJj&#kH`Qtw>CzVpKH zNm7>X8VXzLc5+U^E@2Z0d;#;_K(s6F6{Rb_@vAfqXmwk^sm;T z+vMuNQY06`^~+d@VI9O?kZtRhkc)3&ubiYV6MF9XM=ffX5Uk-k~CI~XUpI>R%gz8X{9|(01SK^(H7qcyr zI;Qo`bMxNsKCvQ&8B4WAs|T7Ekgfdw>fdRM8bba+4VNbJ!(O%(NfYq}6up3Y{ztLA zcW9;cA`WuUttL% ztd9CjVmIC-1vBKL(5P%SC=tg~;jx!Mw5D4{OZoSJyBp5+H%(yiI$#FbeP!^Q8hcZ0 zzTC;(7JJi~4vwhjHO4;h!28sk@|CIImJc&|m_*I*O@ zWF!D=s#*7U9h#CzT;d~Q{w00IZE6Q& zY7=lpW&a(L5gvis9VfX!m(|yVfI`pO3``c=-tkvgaO81Ai1j>(OMs9aB6?wLd?d?a z+(vI;yw&f9F8o3Y&r{oDyk12NH8?{-P;Q%mL4=!$K2RD>7BLs~YcAyKQokc}uTDea zfsR%Dc*&HCyXs{q8SmR;=UR&#B_c46`n1HLpJ<}VF}1bN^;B9klWJnNuZp7ShJuc9 zE?dkYY>G~4ow&B80j@;+3tV}PnjB@vGNg;yj2flMZkQS^j2w-|8&w4zyU@ET=n851 zQ{1VUV<+(zdyS%;*sr;T@~OnAr0pl;zp7+>({$i^Cx0vBhnUZPx(X|daf*vNd>az- zYZDXgn2Zmu`g|o@wx>Tmd0Pej>eLgGPiF`KNSVXJH%mptLnLumB5NgsWs?@x<(McH zXD?WN)e2@5|70dmzkg5e^ans}Pjg?e0gU!uHxgD45Gfza{XtV@f;&)dPwshRT2+jRH49ya)ZLnkwZz#Sp{BtKTI1gO=- zcEq4afD5S*$=UHZ+U}e)BUqVp;B8A~#sF;^SmTkT;gCl&cS}^_k99Wb5&g@{mVsGc z5TQ%+lzXV<0EcuqbvB2&9-Y&ILT#q@Fs1I)M1>0xs#DNFn}=y1u@|RT>g29!aNd+})vovCwHrGho1!6UO(c)j zZAsCwX064bj~8m+#wwPpq8)F42(=|$&daI>7h1nd{(uq#cFP9lbbBU6+g!Z?hEZ2f zS=nTILX_5~g4o^5G}aDb8T%XK%T1PE9jA5&mTKG;q<-^5~xCy0JlWTRH8qPUt z{*=W9fEP50PsmJjs>`7#<-~i@5ZdXsA9UOBa_qkS3~cJb(k0Sfg*Z`3MBtC9H*B!0E9`(r)d;pJ01YQfnl;+pK;$q z`0b8!tt)x=6V&myET6_QbB?K^M%aE1GN=84r1*;1he0gXqFxd#4R`m;7^ZGQ%A>EO zSN~DVJ7OkuLLk+>t8=3F#xS3wOp$=5eezxjT&PlRCenPlcm5r@bUWEnnbiDf?#GfO z-O_-=sVUCZW)Qc`^gWKL|AY4;tgGOs;)p=zO|263*Zz`A)uA7b3rGr~-kSHszts_- zk9Q8UGcOHRr~}%l3d5kFmyhtm_>!U-jF{LY{TzK~2yZ+3XIhQ^)RAM;PYg7ipYaF}GnvdO?Nd6~uD z)J_}*>99<6lSspYUrH)-l*>TdN?mM>w&V|Uq=NG`3*S(9Npn^DWLhHPdLjf=xQJNCF=)%Q^aT0(g++Y|AE~g;Tq` zO!$hmd&hDu<;y%}Fjs62u02*}bR+TT2i~3zMrf^3KH`)R{M9yKYy0Z)Vf-W;-phQt zRM&Lp9q-F;!rn4km{{~5qs0~31&f~~*}|c{XU1Nan&@2wd%GKjYFm&lV%Y{1z@ztI z9M!}WYAdqn-FUis1GEV<5PsjW@(J*F2`oKmUNrfX1kmZrPJR*(+nzSA#kw+68ktYO z8`;498lzZM(+BNidKn3$<^K|(?~y&4s?6feh~$OF6X~!L#-goHO2gNAKSj!!_jQXb zaa{=DKe;2O)2)Aras8*ubj81_>1+jlWnYjmB@8QfA3EyP0i~3-HI4iA-X3-mamMu7 zg-3J}AqhPI@(-2*0G54Q*as2_fByB>ntzWXb#Xlf1}VDH_aE>5?|)~R4SfBlb}bz@7Z~sARH>(y%}XJU-)GDi^+P)XIAfZ zJAOkkv6%Cgu%}5u*2yxkA-qG2NTMrtLU31+*N%7QYKjQnURZi;fCQSF0Elwl)8D>J zLm+YPP-*^n7*K>;s~bum%)jHts|Y)Lk@ujR1kQ2HcY9El3B)cX=;t!%@rM?!Xyh#F zOCIu3DJ=~tfJZ#`K=UNLo=H5oo&cDN1^vnOy=5G!X6pa|uFR9%=XM#FWyA|C>Fc0Z z>$o&ARGIVFlSf@1R`Lj>5M}H=!{JmdG-3!XJYRIdqjNu<<02@f<3|2d%G;vF0)3RP zQkc9OJhF>&$1u*wvDRPwdnF(7lk)`P@rdM zoRzwDEN+@$6zRMfmH5pL+#RBC+rck1QF8Y+dQ;5;-9WX~GR1ZNt1u@579Fxatc zJ<^YUM(4B}JTbgdR|6Y-ZGjX_a^Uc0$dJG z@TQ!z<0dCH$r&ek1MkEekCG?z^%MMyAaxgl`bjuks9Lgt_}iY;pZU*NEgBt+SwTu?lX<|}*CfIL;J#$4u}LfP1d%6P z8R;|eu(J%lm}<|GIWFIAG_}4Iuz<}HcR64K{@f7E6o_e0q9ANyCU<6Wnep!B4>E(i z;SBR8fX_i%dE;gPC`g5HP&&H-+BdP*cW#O;uJLzyRmHfUdT|v6vk^Crcih73cSt@D zi40f}Z;@*CNQfw>^BI~*fV)#~-Zxi$Ptae#arOhr@X6~i@zl@N6ri^v2sY&hN3a$a zHS6nlrnbUi-IpWkb3!DC(E@!52%Iz_K9x7!{xue)&(iL|v3MGx(`CYr{=6KVJDF54 zc*v`nlV9G=8h`R-jFcB+Y!KyY zqCxczzy0ccmYyb{30pd?q7*^veG(FVTFQU2nv@!HaZ$F}QmAdCCZRu| zb4I;BHGS$EklzF1t5}KlUkU>-dk;ds7x(Y=&vars-+*@Irrx986$Me9onJAoI4Zd= zL#BY?LyLHBKCKM#*c|TIfU5SQDvqtF@?IgVS2qh_qpDC}*{see+FrtuCrob=ec9G3 zX~p8Jl&dH5XRGlmPdBBWRU%IzN)Xbd?p8bYx?u1j4&EfB!N#~}E`|P#`ZGYIFb!vz zomZ?=_>-MR3|Gq;Zl(hmv;IGS1yTpIU5{Sra}%tfb)C5dwV{@l zCCO}~=(JPp*2>%G7~5QE{)Den`!s_Vd^J6W#n$w1vc z3bK>i_a< zf1E=;bj)5xkr|Xh%jxhAV}5Qy@q06wGUX^)cSEC2a91RjX4=)>gkPJKR|ycz_NIn_ zwdIgYY~-gf>&nk{_w?WE^velg1$PR}Fhf$l0b7qTgkX-lGHw}&j4B)F{yezTcyy;+ zKb|@kMmIn2;UT`tsPvtQQ&ZJyE{P#LI!tt*FZv$kl;as&!7W?&AjY= z`_7)-v-j?PeD$TfTB^E#^)FpjB_#kk5|W{!oVJ0s2&2RAxpo;65Sh~s9S2z|U8O|5 ztdxbBaV}1!kp$b(ZOf+#v(aL8I&j0Eke>ux7zPF%7(SlnVm3(2hgeBl^w|z<-z6OA zB4QDUHMV=h+YJPKOS-wgUHYLfec;QTI${xugg_i2BM^0nih-Zv;_&QS_kF@-EtUPq z)caR(;p^4QfmMk+g!Sgku(`|tJvgAq`ZMnX1(dVkFYD1l3Uf4;*>S zR#J+rj;RT~3jd<)W?kM+q5;}y@5>mEs7*|`Abq$YqB17V;i^R2Q@i7upJSg^!IFJb zU0H12=cTR3aQ;AxTK-siWVEi>_=xmZPoJ*bBRIt__ZJexE^aiYamGnQi}FW99+d@R z&?2EHK&|mF&YlMk#2|eoCCR-tt!4eJ`lfW>RNRqhI~q>A#t@o<%-so<)LBjLZAlpRi(N zrkclYai?<^MHy;t`)NFhk^L4~fvHJQNGBrZ0oXho`hVkgg-~M{Q;M6r%M%hGkIMMk z==cCha%nqj%W7vY5<&yD6`?kCrp7UV;{|-vc3!sIbqwGD6nqPu7aD`QN~JzhJmll^ zJ*4mj;+S!HrCB7W2#ru{fA{b0Qn)|UyO#g?q_@wRcU!ygwVUO2BTF{}h9yxvSbSCn z=?{y>Bx{pxoFpEewJKB)(Pfpt5%%HlI`RCk4f^ zB(`3edzHFGsANn8b1Xj>@XU(YgQF>cfFxc|I;1Z~8GAR2%O9i^77F$& z!hlAFy_W?Zd4){pHYX1nmyVXr1k-!RxhE}InklG7{6EaNJDr&1K~}CXSP3CoY@o8H zw06e*jVN!KIUT@?YM7*sA)M2lwHS{-3^T8n-M!Po0hIRQr_-mIIm}fkr84B36=%+^ zsQav&j@ZtAtnf9}(X}_INQXlBUX^FaYA4z8Q`1v(pgX3LW|;r5}WFiqpuvt=QXu1~-BP}+r+`J$5#wMYaPg`4Oz z-RvJR=EjNMRW-HHcVHf84PtY>)T}y8#O00$UTAp!?83hYbQ;+2t_hPdrZhW9N2*CV zoyhQG!=#{FSL6(qd9)H+xcMHv?dKFkeE7^fXk98Zdp^lNW=G}W_59q*ab2RGdDJ*0 z@8l!e`x#Quu~Mt;4)u5bGYaY#1$8JZIation;`=b9@iRs$gG&BFh^)R`*K$$vCD{a z*-oF@Ss1I`bW=+|y;48pIK+_ev$KqM`>~0i5 zn3coB`@DArLoThP{D=68PYk&zv1dM>*Jv9TP5JxA>7Vt1*W2SVzezlYr|dkr%pr}-J9a&Rl*9;n=x?`_qThBut zqf6(3%%O3&l}CH|BXno%u`}_W>WFnQkfw%<)nc?(v7X+3x|c2B2niHzzAc%ul+qmf zaf3q9jX|DvnRQE7r4j3q`_}PbS%`8(3kTSox=m^T=*8uGl(?%v6803i@}D5Ccpbh( zf6hC6R3v}$@-lm<3q8p(4Bi28IY_tD5Q~U2i)0~bcHQPVXWf`yD74tR4qHwzY727q zp?<&0PaGq0C*`BTHA+D11C;ANnIPzF!dG1i5guj7qW7QrrU5)Ew2&PvDjV(tBxE)8 zyB?Q``wiTs2(jyy#o!ZEDb}cyyA$E1*PaKZo3964e81j?GyM%4whD&@iak{EpoED) z{h&ILlVG2ke{#7Sa`XBad>+BqrRImMnN?jiVtNIW#&|xssEKDtLeE;E0iUxt9>$}J z@F0U5KR2{}hHd@$WWSGqecF;fgyINC(3^cn!EKB|gm2XB2ronZ#dQ}VCP3ouOjV?$ za5>$c5MU`blI86maj=Mg!)WTmxoo(*Bf3v~Ep8)}_YlZpfIwbL8~R41lF$7q78As0 zd-?&4y;CEaP%8s5u9$$e;%9xeg(ruL_0m!bmF-G;tqarPp*3)fgx9?}0CchVBuZs% zkmh?=;a?9z^x2&6+B6DQp(-;~y{_q;@$9TvY5XPpK_mGQIK_3Kg^ z=DY$~u?}~Uc~ql>{bq`{%*BZkZrrh$={uBMOM8*|_5)HFIWtKI#c_$r(Rp0{=L6k& zk89lDNB?dp*@q`4c;y)8oLN@0KHHJO^?mG4>(*Y2l@l+<`D5Dkgi8t3c@v_aS)zAN zB0{(wux^dsr0@?7`?-;Ak|C$;C!Fs`AtME*s%oVPsU&E{K?A12R^%A2-c#Y92qDM4 zyR1ErTQyJyXzX4rM^@A6ohgw+2Ty&>nxHi%>a6$F=o++7sQE)_cN0EuV-hERG=9~& zL*KK*4Uq$mt;)_Z#dAVAZe~6%pHN@LeFN7)gm#c*u`(!&rfL#uN5)IdUtRA28KFMh zFkY!$rS@xA4IbZw)kjVec>m<7K=&&f{i8gHq8(nQftgF| zFJ4wu&$P+)dW!t^!Zs}x@+FmA)@VCt7H@}P`Z?5DrG2WcoCzG$CjKEo2j>ElPz0eT z$j9h_6V5~l^|>|s3X~*?eB~In0~@t%zO}=hT+?N;hPrkKBDSY1`Fpr>Z&Kil+$cEa zoJ!x1`{a~BM$)ZqxvIchnRHzGsnWAI5X~v-J!7$x(!0SdXs|2sJMLIF@1(`ZMz->p z=eTgOzC7Nc2RQ!eni;;tlor#CWgwOaSDc|-F41W5;l
*JZJA0+f_1) z(`Ni@$N>GdX_6?#=_vp6)`N?gtm#llTYWN0iom+^2)JOK2$|Sldnbb(vP?H@{CFKzuslQ}LzpcSB*5pq8u8i6x&R=O`Xg1;lcDps7iKFp)$#1mYvg9V^GF zAF2UZDK{2gs^luY2TYyP3P8s)z46&RMgK=7g=?1-4AKF28oqo#<-MQHKa^ z1S|=*zID|PvKx&;@%^;iE}}6*H`b=AdF<<{)Z6ne{Gv^t^sJp(aJfE+yWk+qt3bYP zFMSB4?>*0x5hZb(Qd9AkC5&)V{cwpwm^~gPyr4SjivpFdwUVX;gNx}qrGcnZhizSc zkvg@!&K6q)4F$(>yE4&{FwxzLtWHBqP3*aBwceZh>ZpdNZ|zIGk6F^HRf;mMCwG-f zY63>%)%IKN*_OO6w`pBPD=9)18gS0yb)J<)ie@G;Yf&k-*B5HYg=m`CTS&_r>I(KpV3t z_!F|F?c<8F;yJx66QxKX?Ks`~NLd?G#+i6xKS1Qzh%R4SbM1j?tXBjKnR#E8eF-To ztE59)Klpb4gK&h3NYL7@kdalj{YvR~Wl>5f%JWrKZ;3|3_jG4EWPUFd?%yRzB`yD4_@fQ1AbB$GDJVnWd~24iY3V~u=rh2 zMiYvpvxB(WouaCr%jf&ugbvD6|HBuVmfNxJ;DD@8xC%soKkeC_ht06z2#C z6~26nj973Wz%;<>nL&`&1mkJel zBY3xc28W$v_9+MVhA)pGpN8WXkyo37<`g%5dbs$?7TK13S|U&9zuBat)e0WbACt;u zP&8d(l*DI;bqTFs$II$ee^lhq{|Wnos!YH!;uuqoAxf$pF=s2!cn8dCK%%QS8jkOI zDi#-tv6e8!MOFy7^HCe)je)M&2xde=%xw_X_I2mda$kEtr}&l|fyrm#3P zacyPwnmyPA`S#C=iI>L1oT^bu28At#girz^M}s&{C+0w|)tiHX=2CxNhQUfwT)xi@ z*Zl-{#Ti`ORn>XyZG~i=M*Ulz{@p_WaF^(NN{m{=h4eVykGQ;5R`prw(*i#{Mv~-h z(aNK{f^?Aixtn`wsh%*cpq_{3g2z3>SmGkPSdwq3x5M)!4)fFt=Og>CdKsXz>f-|j zks!Txhh4Qns?}_^OyehLYqf8~dkvheXek2WcB-$G1@dX*#Kt*@7FUTM%|B{7@mR|X zq2}-*@#AIWGe<%PN^yzLZpeS>%A;sd_$8)JDhxlf5(HX;1ALLKL0gxs6chR@T&PA&;78e zUX3p7N+sbN<6vPr>xj!>%lYgVqm&=TGczo5W|ghN|3=>6UG=LM-q((P zWqAgZccwb4X;h?oV^Llh#w~<}slTRL)KTL;#PzG60{PnJV9Zu5&kYj42+=7AQky;% zV3Z{VJ$z4N8fSOkXAh{BY4$Y*n4!6Mlj_(NHNA?PPxciGr+CqrhRQS5Y9uB0F;+9= zQ#KedC2~0EsQxBdTcgx<`rS;eu2Nls)FThhyUrWZ5-J%{VoKFki_6RA4|#cZ+U*5{ zi)M_#a!%BV%%oi*mDCn*8ktPXz1os; z8!Gy#Pbb&n<+qqePHZu=ZEakASwbNCe)FR~6U7qPa-5Ar;5Cn_KcTCh$M5b&=0of{ bPE@a3d}2hxjv9RZd+86uL}-}P{-5(d1c5z1 literal 0 HcmV?d00001 diff --git a/docs/content/static/images/reading_schemas/body_schema_3.webp b/docs/content/static/images/reading_schemas/body_schema_3.webp new file mode 100644 index 0000000000000000000000000000000000000000..3ca75b144517c9ec02dd049dff972086845b9885 GIT binary patch literal 16610 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 literal 0 HcmV?d00001 From 5e0f0c8faf71da55017deaf75b03f604065ab42a Mon Sep 17 00:00:00 2001 From: Ben C Date: Sat, 23 Jul 2022 18:26:58 -0400 Subject: [PATCH 002/176] Gorbo Mode --- docs/content/pages/tutorials/getting_started.md | 4 ++-- docs/content/pages/tutorials/reading_schemas.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/content/pages/tutorials/getting_started.md b/docs/content/pages/tutorials/getting_started.md index 8f06fcb8..df2de29d 100644 --- a/docs/content/pages/tutorials/getting_started.md +++ b/docs/content/pages/tutorials/getting_started.md @@ -248,8 +248,8 @@ It can get annoying when you have to keep closing and opening the game over and To enable it, head to your Mods menu and select New Horizons and check the box that says Debug, this will cause a "Reload Configs" option to appear in your pause menu which will reload changes from your filesystem. You may also notice blue and yellow logs start appearing in your console, this is New Horizons providing additional info on what it's currently doing, it can be helpful when you're trying to track down an issue. -## More Objects +## Modules -Base, Atmosphere, and Orbit aren't all the objects (or "modules") there are to use, to learn about these other objects, you'll need to learn how to use the "Schemas" section of this site, which lists every possible property you can put in your files. +Base, Atmosphere, and Orbit are all modules, which define the different aspects of your planet represented by objects **Next Up: [Reading Schemas]({{ "Reading Schemas"|route }})** diff --git a/docs/content/pages/tutorials/reading_schemas.md b/docs/content/pages/tutorials/reading_schemas.md index 13a6a1cc..d3b851d5 100644 --- a/docs/content/pages/tutorials/reading_schemas.md +++ b/docs/content/pages/tutorials/reading_schemas.md @@ -39,7 +39,7 @@ Badges can also show stuff such as the default value, the minimum and maximum va ![The Base object on the celestial body schema]({{ "images/reading_schemas/body_schema_3.webp"|static }}) -Next let's look at an `object` withing our root `object`, let's use `Base` as the example. +Next let's look at an `object` within our root `object`, let's use `Base` as the example. Here we can see it's similar to our root object, in that it doesn't allow additional properties. We can also see all of its properties listed out. From 6863ccdf31988e013ac144003f40230aabe363ec Mon Sep 17 00:00:00 2001 From: Ben C Date: Sun, 24 Jul 2022 18:06:58 -0400 Subject: [PATCH 003/176] Add Addon Creation Guide --- .../content/pages/tutorials/creating_addon.md | 83 ++++++++++++++++++ docs/content/pages/tutorials/planet_gen.md | 27 ++---- .../pages/tutorials/reading_schemas.md | 8 +- .../static/images/home/create_planets.webp | Bin 15468 -> 0 bytes .../static/images/home/mod_manager_dots.webp | Bin 10872 -> 0 bytes .../images/planet_gen/earth_heightmap.webp | Bin 0 -> 8504 bytes .../images/reading_schemas/body_schema_5.webp | Bin 0 -> 14938 bytes 7 files changed, 96 insertions(+), 22 deletions(-) create mode 100644 docs/content/pages/tutorials/creating_addon.md delete mode 100644 docs/content/static/images/home/create_planets.webp delete mode 100644 docs/content/static/images/home/mod_manager_dots.webp create mode 100644 docs/content/static/images/planet_gen/earth_heightmap.webp create mode 100644 docs/content/static/images/reading_schemas/body_schema_5.webp diff --git a/docs/content/pages/tutorials/creating_addon.md b/docs/content/pages/tutorials/creating_addon.md new file mode 100644 index 00000000..4a21dacf --- /dev/null +++ b/docs/content/pages/tutorials/creating_addon.md @@ -0,0 +1,83 @@ +--- +Title: Creating An Addon +Sort_Priority: 85 +--- + +# Creating An Addon + +Up until now, you've been using the sandbox feature of New Horizons (simply placing your files in the `xen.NewHorizons` folder). +While this is the easiest way to get started, you won't be able to publish your work like this. In this tutorial we will: + +- Create a new GitHub repository from a template +- Use GitHub Desktop to clone this repository to our computer +- Edit the files in this repository to make our addon + +## Making a GitHub Repository + +To get started, we need a place to store our code. GitHub is one of the most popular websites to store source code, and it's also what the mod database uses to let people access our mod. +First you're going to want to [create a GitHub account](https://github.com/signup){ target="_blank" }, and then head to [this repository](https://github.com/xen-42/ow-new-horizons-config-template){ target="_blank" }. +Now, click the green "Use This Template" button. + +- Set the Name to your username followed by a dot (`.`), followed by your mod's name in PascalCase (no spaces, new words have capital letters). So for example if my username was "Test" and my mod's name was "Really Cool Addon", I would name the repo `Test.ReallyCoolAddon`. +- The description is what will appear in the mod manager under the mod's name, you can always edit it later +- You can set the visibility to what you want; But when you go to publish your mod, it will need to be public + +## Cloning the Repository + +Now that we've created our GitHub repository (or "repo"), we need to clone (or download) it onto our computer. +To do this we recommend using the [GitHub Desktop App](https://desktop.github.com/){ target="_blank" }, as it's much easier to use than having to fight with the command line. + +Once we open GitHub desktop we're going to log in, select File -> Options -> Accounts and sign in to your newly created GitHub account. +Now we're ready to clone the repo, select File -> Clone Repository. Your repository should appear in the list. +Before you click "Clone", we need to select where to store the repo, open up the mod manager and go to "Settings", then copy the value located in the "OWML path" field and paste it in the "Local path" field on GitHub desktop. +This *will* show an error, and this is going to sound extremely stupid, but just click the "Choose..." button, and press "Select Folder" and it will be fixed. + +Our repository is now cloned to our computer! + +## Editing Files + +Now that our repo is clones, we're going to need to edit the files in it. +To get started editing the files, simply click "Open in Visual Studio Code" in GitHub Desktop. + +### Files Explanation + +- .github: This folder contains special files for use on GitHub, they aren't useful right now but will be when we go to publish the mod +- planets: This folder contains a single example config file that destroys the Quantum Moon, we'll keep it for now so we can test our addon later. +- .gitattributes: This is another file that will be useful when publishing +- default-config.json: This file is used in C#-based mods to allow a custom options menu, New Horizons doesn't support a custom options menu, but we still need the file here in order for the addon to work. +- manifest.json: This is the first file we're going to edit, we need to fill it out with information about our mod + - First you're going to set `author` to your author name, this should be the same name that you used when creating the GitHub repo. + - Next, set `name` to the name you want to appear in the mod manager and website. + - Now set `uniqueName` to the name of your GitHub Repo. + - You can leave `version`, `owmlVersion`, and `dependencies` alone +- NewHorizonsConfig.dll: This is the heart of your addon, make sure to never move or rename it. +- README.md: This file is what's displayed on the mod website when you go to a specific mod's page, you can delete the current contents. + - This file is a [markdown](https://www.markdowntutorial.com/){ target="_blank" } file, if you're not comfortable writing an entire README right now, just write a small description of your mod. + +### Committing The Changes + +Now that we have our files set up, switch back to GitHub desktop, you'll notice that the files you've changed have appeared in a list on the left. +What GitHub Desktop does is keep track of changes you make to your files over time. +Then, once you're ready, you commit these changes to your repo by filling out the "Summary" field with a small description of your changes, and then pressing the blue button that says "commit to main". + +Think of committing like taking a snapshot of your project at this moment in time. If you ever mess up your project, you can always revert to another commit to get back to a working version. It is highly recommended to commit often, there is no downside to committing too much. + +### Pushing The Changes + +OK, so we've committed our new changes, but these commits still only exist on our computer, to get these changes onto GitHub we can click the "Push Origin" button the right. + +## Testing The Addon + +Now that we have our manifest filled out, go take a look at the "Mods" tab in the manager and scroll to the bottom of the "Enabled Mods" list. + +You should see your mod there with the downloads counter set as a dash and the version set to "0.0.0". + +### Checking In-Game + +Now when you click "Start Game" and load into the solar system, you should be able to notice that the quantum moon is gone entirely, this means that your addon and its configs were successfully loaded. + +## Going Forward + +Now instead of using the New Horizons mod folder, you can use your own mod's folder instead. + +**Next Up: [Planet Generation]({{ "Planet Generation"|route }})** diff --git a/docs/content/pages/tutorials/planet_gen.md b/docs/content/pages/tutorials/planet_gen.md index a7c0e3ec..b10073c6 100644 --- a/docs/content/pages/tutorials/planet_gen.md +++ b/docs/content/pages/tutorials/planet_gen.md @@ -5,30 +5,15 @@ Sort_Priority: 80 # Planet Generation -The first thing you'll need to create on a planet is its surface, this can be done in a variety of ways +This guide covers some aspects of generating your planet, a lot of stuff is already explained in [the celestial body schema]({{ "Celestial Body Schema"|route }}). -## Surface - -Ground of the planet - -### Ground Size - -`groundSize` is the absolute simplest way to make a planet's surface, you simply specify a radius and New Horizons will make a sphere for you. - -```json -{ - "name": "My Cool Planet", - "Base": { - "groundSize": 100 - } -} -``` - -### Heightmaps +## Heightmaps Heightmaps are a way to generate unique terrain on your planet. First you specify a maximum and minimum height, and then specify a [heightMap]({{ "Celestial Body Schema"|route }}#HeightMap_heightMap) image. The more white a section of that image is, the closer to `maxHeight` that part of the terrain will be. Finally, you specify a `textureMap` which is an image that gets applied to the terrain. - +Here's an example heightmap or earth from the Real Solar System addon. + +![Earth's Heightmap]({{ "images/planet_gen/earth_heightmap.webp"|static }}) ```json { @@ -103,7 +88,7 @@ This makes the second planet a quantum state of the first, anything you specify } ``` -## Barycenter (Focal Point) +## Barycenters (Focal Points) To create a binary system of planets (like ash twin and ember twin), first create a config with `FocalPoint` set diff --git a/docs/content/pages/tutorials/reading_schemas.md b/docs/content/pages/tutorials/reading_schemas.md index d3b851d5..db162888 100644 --- a/docs/content/pages/tutorials/reading_schemas.md +++ b/docs/content/pages/tutorials/reading_schemas.md @@ -52,6 +52,12 @@ Now let's take a look over at [removeChildren]({{ "Celestial Body Schema"|route Here we can see that the type is an `array`, and each item in this array must be a `string` +## Enum Properties + +Enum properties simply mean that they must be of one of the values shown, for example [Ring fluid type]({{ "Celestial Body Schema"|route }}#Ring_fluidType) has to be one of these values. + +![The enum values of fluidType]({{ "images/reading_schemas/body_schema_5.webp"|static }}) + ## Some Vocabulary - GameObject: Essentially just any object in, well, the game. You can view these object in a tree-like structure with the [Unity Explorer](https://outerwildsmods.com/mods/unityexplorer) mod. Every GameObject has a path, which is sort of like a file path in that it's a list of parent GameObjects seperated by forward slashes followed by the GameObject's name. @@ -75,4 +81,4 @@ There are other schemas available, some are for JSON, and some are for XML. Now that you know how to read the schema pages, you can understand the rest of this site. A lot of the other tutorials here will often tell you to take a look at schemas to explain what certain properties do. -**Next Up: [Planet Generation]({{ "Planet Generation"|route }})** +**Next Up: [Creating An Addon]({{ "Creating An Addon"|route }})** diff --git a/docs/content/static/images/home/create_planets.webp b/docs/content/static/images/home/create_planets.webp deleted file mode 100644 index 7629e85df803cceeb12349fbc8027df91cafe3bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15468 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 O4ThGH(qUHhfB*oEFnAvT diff --git a/docs/content/static/images/planet_gen/earth_heightmap.webp b/docs/content/static/images/planet_gen/earth_heightmap.webp new file mode 100644 index 0000000000000000000000000000000000000000..3f822c92248142441f4f17393a32a4ca65f54e41 GIT binary patch literal 8504 zcmZ{JRa6{7w{;`I-JQYRVS*Ff-GU~#GcdRX4Gw|e!Ce9bclQw7Wsso3El421Prm!` z-G}?1URAYw*V(75PPMIWEk!vwZW;j4mzC1c(-3@%1^@u)r98s{&j6q(Ev=yf|FR3f zt2kOXxgl}`fRnR3R7+k8^wz)-guDkJ0VDtcKnF0HS-82VYRl>T^Zvi>W&wcv*E7rU zFV_F<^Z#YhEv?)v000i<#RF{N0(F1EZ(gv9m%Gb99Q%R^ENsmzUvS6f|viwUy%U)cPAVGCQR(~A%Dix0h%qsPk_{$u}Uf??&X ztNqfFy%Y+-4bTGQ0jZbtnFAhx9pDJK0~{}{^GnYDZ{15aUvz2!J1?JOC%2D3zsHTv$-R;kywO=Fvg zYN9R}O9RqnUazL4a1L8CsyTNId`1|rS98z5Z%*jdM@41x|4MwaKr2I2;9dx-a2cdf znH^fY6K9%Sn8815j;&bz+ zHD2*K}G{UCp-lIQvCJlnzvzJe?n|$6k2-KKG{XoCtt0I zP>V$FXQiDid;YM{M=cV%5&gP&oXVoPjfJd~Qgm*~B^p345A1RTbYF6rbHVv&r9mQM zKa`G??rSs%@9_n;3pGXu_6F*p(KexO9~a4e_Ot`An{DGqn76^RjT{deIukJ zrS8Z4jcRP!v{;)lu>HZf68miV;G!(WQ)sJg`W@7XJfbP4r-W&N?32 zfIhu3L&Tzano6$3_h3=5MfGWo?C9Z^waClAFOcgSv;t}|&y+`DwAw~bX6M&TPdrZf zVfs9ZzzCakEy?N61z}%QIunC>Q}y3qGw7?oSxrQ)tUcuu*5d{5ogZt+I@$P2(ko0S zXBzr29M-d)L1kk$FgJGXly!0)8jw`J(g|0V^`x%rRxi`&5VANsUGpl;JoPoTIl-JK zi*m*T%yL?`Y36MId=COEyH&sYIB*iW*$kW5$pYUhAb-Iv2 z6&U@!eQ7xyQAGPNmPb%qiwmakC?R~j^6jDu&U{=A+elZ=UO(nZFMAy%In(pIR;3#_ zxz03bP(e-D)1(uYg{or)XBV&!2YYM8kbz1M%F#16hFhqdLQ{4Yw1n6lip0fno`Temx)?19T5d-dW;nv^^xKxO@$h0{*#o@O28=r+6U;i@qi~@{c z_y;*nH^Have0I^1b&t{Q4zd2m`uB*Q&aWGqZOG@?FV398DC@XK=)J6-rN|QVA7)fM-pZkKf>{{g@hpXUUTbC&>vfSu8tAm5E}QB; z$N7)h6&)yqXwmrsz|-HpRg3f%r6_P^0EerbntJkgE3@ZgYR;G~gt=sH9IRpe>%F%~ zQkwVanMJi}Zn}Pt&k;Sq@Na?AQB`(l@vcZW9zVgobhK4x&u7Ott&v~lbtd9(AvnJf z5=+)zkx6G^(hCMqDR4(xymP0KJ57rw8vH^MSwlbsi!X; zn2BG4fQ($b{C7GoOWqM0mbU_TG`YhzMw1l*kMr=LcMc7f4dJ762T}rY>d)!A?Rk(C z*HF&qOkV+GcWp;odiUo0_+!5yhH?)K$pLl)`Rgc&ljYQ6?}w@Q-BiIu4-7~SI^Djp}y zljv3NIh;R92U;N8Bu!FS{#b@VM4{`>S0>3m=G` z*>88=)S%ojBvzOW^R~kI)p0PZ7Bq6wSI;gKOfR+=xVtij-A#(yzd2b4n_~~wCmqw} zlc2F(KV{Ni0P$0w%P;Ds174p_u|AJ!sL4*ph-I_Nt*#6xTFSwx<5b1}>U0m8) zDF~xO2xr~jO=ex5XAo#cq|tPbfh1i1D*Y6`OGP88N?6Be*TbL}A>MoG?rlAHa~Wbv z$~f>kUo)AJ*jso572e6>{LBzf8h5E>og6zY3N`T^nv}#3UNknV%Tj6K+xm~U%rJ2b z`_%C^wnoyi0kY=Z27EGhPMj5y5@R~C z53dQD;RO>}(WrELUv7P0YM&Q4vdg$F_|_-5RDL`EV5q4uDI-A|)~^8bYcQOlm|^xu zr(Vl6rA9pjv^IixPxUmfEj1C?nrj`zQ;6x4v}+W16&Vk;+JnHG_MrU@mhE@wj&1`Bq<0nF;XFBha%u9!%e6g?BB2QNHsI*EVX0}_#XEGZ;V;CSCY z&?t8eV~&yxjWa$Vyt6LVq^EbHyiQKvWD5lc1~YrKv}7iJ4q*Sla=DT5`cqg87Ora6 z$7s`S5_rAuIha<{w72yZaVGh-Tc6n;eonH-rJrSut;kW2;T_T^4Bil#AwoBZPm_AG z(og%iThv|gTS*{@q)4=m$?L;ePS4WooUwTA%+L<%Q0Gq`!HF5_zYpHlHToHWxYpLs_5-DPjt(5ceF3jX9w-Ot5aN5mp@oJVM= zSNN)ArfRDaBQ8K{QzrM?pB5gXrXR-j8f`d==RQh}MP96>hnKBt?a+>lN1NoU@nGxO< zt7cGEeEoyDq`<~z-BlIU+V?||e)XDxjYTpL5eh{EwLmt7kTHIxWnq9@S#13r8J>*m zL*mo2r(r{?rWo-9?Z_2gshfA-HC8@RDRYyu4i2r69CsSvh#l1X?L*0M`~t8RjhppE z!qmCIxnz&cta!$8BAyc65U=fK%YvfBs_O|)>$!;EEQzIbg$e#Xy)GN)7b}k(1Ap>D z6t#J`3Ra`kPAMfxPdHEtFe?yUQNpu4vU~i#;4S6B+)mxSavIA*uIfd=ALYh>=} z#tT1Z{M7hkLMEVCOg0P!p1KJq^D#nwap|H%kcyw&VmNv2gxWWQTogpp=Jrs3adSsIha z$jKCEsv1E7ypQm9oYiCd{Ut3#-qd8FAs_8RCpo^DDEMDcWHP?YgMVv6%n_^MM25Z$YBo-)?p!5 z+V}dpIB()uhnD~GA}C*UpzvCwcfL2ODo##>VWWLnhw6D%mtFKrO{8JYX_?!ip*m%7 z={maa8%@r5AKjAqxs`j&RAb){l@wlPxl%^Y4~J%Qw=+5t(Q6>C4l@{Y5zgM1nUn$i zx%p20ZPtlvrC;<{sJ&`hh7c{9CO3wkJ$o^~x=37zmHUTg79Al{kEz)Hf$C@|XU>i2 z=e$90Yp>S&s@)3qDhMpa4MQ_llkk+W`nlQ2#!lZorkqQvm*hrhB^>od(2(?R)xt?wFEVo+b+giJk;lNbfPiC{tGNlq&3n;0MMsj(I%vL5<|CBu9HmB! zlI^f%2O4ZWiY>Cy$x3{LM#SZ7qU-YOm;`wACMw}>**s7SPdi`v;gBp7$wYplu@JL6 zM|f;dVH?Med~KWvWYehaqX(7!ZZ}<)@z2FA^4nOvWu?l~#DGO$K7H<0?S@th)ID>w z=;Yk=X(C|VnEcRUPw-%;EgmBYgR>D{;o8MrGA$~>hDhKxgCs=Y;;Tb$W7oyRDYq9V zZB)qNfI5<6mbHN**&8{V9wqhiz*e)#YYPS>heC4JSg2gSr#Dd!deXY_#MfDT*?BW_ zyzvY!D}{ZRCLz{+L&>70yDe#HfmG8MHFc~pH`QSocv^G}Gqv(0>)y*l4_w-uSUj4){TU4pqrY#-ia6WkA_-No=K z8rubvh5SYBo7Cb*+{BO5XUpGpVxE=4*%l1#_C|uKrh|xGo?PDvn_kE`B`doyRJ!*k_3$pCvSkrAMTs^!u&Du>yly>v`u-QfhgES3_eSPE*bZ1nI>>v~x360Z-w zSrG7JdaC27&xp;^V###)hteWiUVi?%3o4iL$GBLK{o@~=VzL0q*ZWB)Gb8LUy)Hd;>b`}EdxoqMszXqwOD2eEj^A*alOrziU~#-mTGYGyxZ`@892%o(h35s6W27VV z?@xsL+gzPVfS-V)Jc-e(oiotbNn=_?w$ReT%1a*>6ZOv#gd?fj|M-itBl zEHf1}N+tIttbc&QJ+TaiCe89p*GRs$T~dEocYZuB5G%DoRdJKp1X13FSz62m2a zPu*9q?;e8RagY`bQ$CarL1*gzK)A-=EO?v@&E0-)GUJp$fUzJpU(a@XGu*Wh3YFiK z4MBLI-DV;3w|-oVrjU%89tG^bDYCM>q`aD1SwK z*e>cxYX+vcwP16cg8$4~9u*kt^c`DK0$Q|$bcrl3@remcRB~_Ly2Wtl4LrxS^65x_ zNTg?!AlxnlMKYQO0$eK?1cwk}gxsL6O3MS9tlwF%l*%n*< zrpshFSw49649Y!aFD&E68sz_Q#QJs-%_vpH+RgZSch-Th8qyt9q0rs{Da}U-!e^*^ z0-VNUhG|Qv&TObw)i-<x!-U*BI?F3XytMJB);p`%h9!* zTeTNAkoAT-uB$Gkf6X>fH^@hgrG$^$OvAR8^5r=g$eE5B*KD8p4XQb&t3?wVk^P;Y z#3>AQD)J){GEY&6169thR)tm(O=hb;shF_L-~A?*r*bLu+}Y^7Kn|rsaz|z53o_pL zny62)I5BTX`Rl-T%7308<)*kNR~)X9AP5!?CsJo7jEODHiVWu7rlD-nzj$+*r*B!L zO*zUw7;5CjYuK&QvpGtj4f=l=;cDr3}>#CM@#tjXKunvE7>iZVC)KU2-PKw|LGe1>3gvn0k)KFA6 zn58LMHFELr^Zl%l-pK3RF6&1OVwT;GGfCC2WDEQ^Ep!}cR5l-vLf77=kJ{;8^;l02 zgi9!OI){^@+-_!O%ov~W-SmSvn!1?KTS^W#P4m`ZVhn#vfZ&oT5HO%k%RE3_*trec56J&k z=Fi2lFht>t*1~Mgx%gO6IJZA4*ICJKye8e`z1Z}J_op9K-3KOVTd?Q(+nj#huMVkp zm(W?ftb%v1Zb=Nq#>UY+Y~&(gH%Fv3S!WmSU1>22{^l^M%IHtGrX7t9I&>MkiR)gxl<~r5=^`yfiiMauq>y`iRgq9s%A3$9 z+;R2gO|Hbw1;1++vCDk%P%S{xwOzX}-x$%$W^dY9_L)+HIhOEw+dXN>`d#av!gQfq ztf50jQ54zE#=j%2C?|sH<1B?s^o{e3y<~A{s0i7Osjf+ltOS@A+6abseRuRFoog4np`-Z(2f+aF32qE1Zsa zLoB=$YETfy4N5(BlMq2~;Nux;V z079RPMwGFm|3uROgR2TjJA4A(bM#UkdGZZElj_6-o9=swYto1lByUH|47~|cu}|LLg-6rIzyJ8>OvaE`)wlyFW%55 zT5sPf5XRnu&JvJ!YuO<&>K@R_k(0Wf0`}^7KzbCr^fbN_xU=eO2J2)!q(XGQ(qcUi zEN_wH6W<)WaakI9@c3F~{%Yt7yxi1BM;_zZps9K`>JqF<`?k~g@c!J1V{(dPU*)DC zbX-xlTG)=WEq|4GnPg$Tln}dIR7Lk;r`jFcT|%UnK;}fmhCX+=Vk|xPu1-ykHYRhL zCqL(LWzM3#J;84~WJmjty)mC=N6w>9W79j=WekKoynCY#bXYAiQ!J!Up}xm@oBb+& zU_>n(SDISvPjX*(&!WEfbplQlK@emT-9S+T3b+)ZtG03^}8YMug7{Ir+#{dO@OM-S==t&-YXiJavzNl*X)n zicI7Ipfk#@1J7gxVmw2!#p+lbnM9mc$)M+Buc#|s{YoiZ;x8sEu|-UOI;T#EN)nE) zbgxL+{aG2LioOyqM~-|d7=T?c5}VcW6aJ3e)L+2g8MUsUVzxZG!uRp=v4hkGnsD+C zNlEdLGvO7lT#sIAm`3ey0?%S+CDc z(|wr>FN4*DoPWoBtj#&^G94=;_MTv2c#y_>v6%2@-u9>s zkZheX*YHO|cE|S*Xlbs%G_Ye%rKAcbT2^-{(eOR@Ahj(r5-w90Zd4c{>pW=|GKTaO-eRg zN=(M{!R+fZCQbW^S7Bwtxt}5zPu-ST!>v9q?`T=jG^@2ODKZ;jmO@aR<40vSX@BqYR{XKVsNG zf@75mdfwNw2#N}HfhZDMiNs2X&g#uq^#-9Rxj)|^0f)X79dkceh?hr-5>wQ$l{9@2 zyun!kU1#O4YTwW|RVOkFb|qXn*t0*)N2=wcB)eepZDG#Hfcd@+z4h1jQokRaac0*r oGM}<4z$Vuu?}^`TRoZnNIv+9gE{wwVunq1P{%bz*@^Ab90VTk+LI3~& literal 0 HcmV?d00001 diff --git a/docs/content/static/images/reading_schemas/body_schema_5.webp b/docs/content/static/images/reading_schemas/body_schema_5.webp new file mode 100644 index 0000000000000000000000000000000000000000..9c88e15c0688f61be3c6400ed54ee98c6ed7d04b GIT binary patch literal 14938 zcma)iW0WRsmhGFav{7l>wr$(CRcYI(v~5=!m9}l$cHXb2XL`EtG`{!|>%>~Io;cil zM?9e@Au6iK2>_^x2+FItV?*g`uzsXa`6E#LMA$3FVxf1SPh`8MA#d|Az_rNXo6J$<$L9Q!tT7`@Vb z)qlNQe*d6*@BW&y=iBQ(>VEfB|GIk=`QrGhSrq${D~0&_l867ovHcMLba?)KD0QcI zlC$<@_|^O>wu?8j)%3ahbrAK^t`l4H~3}zG5I-n*Zi<`+r9DS;C1kP z@$s}j)kAlnH{m<{<$4RToR;mI`!#S1eX2I)yXtHC#qp+e%<*3Gpr^>k`Z@DC_cqi6 zaZ7iKck$Kj@$z~1`|0%Sq}$8~$T#iF{j2*E>pi8n`=6D+!2p27YaZS)?1K%>;N?L*MqXdjCKq2jd-Z z?D`s>-HjaC!j|m&IL`(Yus$3DI#iw${3la#=nzgpT}sbcfy;4ubQq_^iD3~!LP-rkYu-{lNk4W0LXqwTxvj}k%R(lF(l}4MXoYVBw(hk84gYtes`830{Q4J z;$NlWZ|jh;2IO#ej)ve>j|Gm>eZ@Hj(`0tmiZs3hI^x$QytL3wLU`n~+TJ4mQ=a0+ zW#sS&QPfKdwk0*uy#9S9{925|nB_dgc~&BTcVgmF{?+PKVz@Gb+Q@3#%*PFdQttV% zV$Iof8;V(sl0tmhZH^P1cn=dj{d`04E!Wg^;?OqYD0UX(d)w^sT;&VfFcVgU)&IN` z{u+MiD#FyO*o;0J9GEALm|N3eX~R<1>Di{MS&8KeLNMLJ|8^$*@_HMhdBtU2$`>q~G^_rE;pvp7(Qv!`X+g8}AIDezuyab`5FcyCbEUBz9+h@34FYn1 zHIfa2GyiWh^S9aiI|su%{YpBPEhOeQ2A3eiDKRVer+acjNnL*H0 zVGT5#)@($U;C~V`1RW7(_I!a%{~W&j6^sI{o~}h5E;EIEzM}NfmbmXcc-Hu8Po85d zm;k`c0E+{G5TARZH6gP=1$HiQR;rwL{0zrzv=g_9vw6cRLQ zgvTFrLLBK0h`*D)B_GaYY5gz32?rL*9}MbBY5st#zf0La3_WlC{3aB#_C!ksivQ&) z1{#i+_vdD^VKZBS$Vye;a9)z(x`m&0+JpkvLXblx&`95-(tC5})C7JRv&ZZFUzi>S+>2uxZ%^W zqbC#*&)OzRN^rZFdzE{8wxmGfc5AHKE}J9hgrD*qUBWT2+m}6@KxlZW6A+b7o}XVd|(iI^1&GaE`uf-bCP! z`e(TgbOTL>|1CcM9&i4t-2R#$Uw%2+UkN~x1`AD87G~~Zo_|s}1}{%viNkf#s@0_b zbK&|AY5-XR-96w>Q04#9saO~Q@R5uy7HF_lDvH}l48Yum-tr*e_biV;$l@e*z2&2| z+iF2a?EnCh_;Bi8Fptxix1`TIR)nfeQzIm2llo|PQ!NmS-4)X7tIX-f&x~Mmy93bk{on1jh{gdSdTsjV6U2) zkY~pZ7oeX)cZ@Vw?U9y#vN48cZ3BRES~Y##S`ttIlt>qRNP#Am!s|mhW@#p|p1*Xx zIoMpw1}+R&Su2Lvr3558b#6L0Dit}xO=e{*mnu>;VoGupU|vO^>jJWU`VGYm+=Elq zDR<7b34856_o(L@FLN9mnKUOo5gaNw-QIPVRwwNZ7NM5}l*`-$&;a@DI+^sxg-Nb) zdhtZD(F>^l@1&#F~7v9%73=&EogS;?lf7k@@VS!ck1!Nxt@{0P@ojqJ-%PVB@ ziXQMf1BAwpaZ{%GEw zWT$!=7nhN_9Sa4)>lUriWC}gY3xbzp{`?_gZVV~1JzGt4L6#YN;GSLxsG~m+j7t~x zD810yvS`iA)!z6Sfw0Fyr`&OwkkwmM6gd}Nc{F{A4m5lqc|Lu;@Ou56naoDE6T_Bs zPU~1imj2OzSSKj~k{sSbgsvMK>ua@*qMyZvSg!v1@hcsa)( za8WS=it2edvNUDiPWW3!sXx}DdE?gqH;?vVCNKpbep3J)dksSN;pSohIT4N4E*f zJZ6vVUvNh+{4w}BsT>Sxt7?MpK@*Qh&fiq}61a4zMp7yr?m{2r&^Z{FXuOx6Sstr{ z%G%i{M2h$U?3ajsDn0IlEnLwRV5?7D-E>GgGVPL0P`@@m;}~X z_rkFlMJ2Kdp#5RNfb!LsgH`o|%wI!fw9Q;KWw&Ehd2ynU+=f-)Hd1$;D~Ur<&ZA|p_B z?as;Bb>XX*s0&%2PkI>YbCBVyV*s6j11;KPo>a}6ZLLO%Xl{zh2(bZ^zJ4xbECQxC zzU!48KVOstt#yj)=bRY=*Uk6VCn8_@&G&nBRaLE&agDuuq0#!eJ7__N;StD0|Mk!B z4L>l}o=@sAcLc0vDgvNAIhdW=Ch{61vFb{rS2AUWQdHQFKo)j&tpO!q535{z^L5(jjit>*@gf z!NJh8n>tL_#Am25p3S)eDtU}6CGE;~Q8+PAGLCGtO$-pTpaS z-UW7olPbV2;?+OVGWt?hE)+A%rwihPOxmivfAfjYW1&JRM<*)bYjro}9q28!E|3uN zQp+Utikxv%UbCW?x@FSgl`(wB!7Ot`KL^5bP#5n9T(Mms66!b8*E=|&HVtD0jLp11 zIAzXFbTlkn(E!%?Si|Xz2Q7EQQ=Jc&ds`n@>9|(2!HN7c&A6=jmXP)&T&#|$`t6#~ zlT@{$&&9>{?mXyT=j}*0uLU=0V<<^(Nz-sL^4L5Fp^MCG*gg|9oPf{>-hmX;h9tU5 zVqcD^H@+jA73u917q}TVAG~}kLKI<-TX$1j^{TWcteRC-Uld-8#eFg6v?Pt$CG1r{ znpqqsP2R30yJ+8aktsl+Licl_qDHsxzV^VlS&|5TtHsJNK0Cv1@7k|B@V2(gg`#Zd zqAXB$x(^>1QYyETQtnSJVqhP<8~Tt+kY>r>f!pD`+W)l;7`QQ}e0>vk`WZJt)RKsa zTNdh2A9`CVmBiE}*PugmBLSMq9S|wbHYBC?F_pky9*l8*+SO)q1*nyPYq}UN;ZICB zIF=<{Oi`b9p*i1RY-pXBng_5M>#KslHL0SoG2#QWgcg;_m6vM{b&mPWDrq#l374Xy3LJLi{_e~VljpeR%$2XVu>>D(N$8^litgQSydK7Uaqq#0^$O^q>bUhpw0h9`d$&5iM~oDaVMo86^L@ zB#+l&eIR3xM(`iyeTf>6sabQs^?5ZY65?0waS3d*yXTxyl^zLV=;kC>QXw2flfT>i zruLnQG~cb`g+f1?Si1QQcXW9bLQi0P4U?>{fH3Ho|)%p5wz;>kPNP~O8u48ohKF;J8J9B>8a43__`PM!NJwS{`D(u<1 zGBM9GDrB(-izo0LzXl;Y(|_QV0?`RsqBqQkx--36QE+dtTmj%+-a_ISg0H@aV~5eM zL;z6Q0p#oul2Nlj;p(-9BON%en0XI$hSaD(xlxbHrC4H+wyp!Bc<4|P&iARv%M z$Y;z)v(&Y!OOlTe*NPB2*>kZQ!$=8C)ci<=O!V9ZJ8VnI#)#p=) zS#ZxK+>VbsV?-vrSI1W7iMkrt5>HURI`%VH>o^Fg5ZhZ3Hh~zv#-uF{t&QC!g@T6X zpsl+W)c^?1<2MsC0fUrv<%cTQXw9H|==!dBWg)0UCh4K`UFuc34`y(a4&ZRHT$1jJ*6YU?h(s%;>8$utqhmet${Ys)jvK%HNJi(f z`}5C)`Q9*)ls~t7?g>%*{VMqm=>u@Y^KFh-Y$H;ut@>i5O9#w+2R?f06rdFg9b^K4 z`MrNjQRe}GpYy~(sbeMd87>%E`shR_wTrqNFKXoFy|6_#6J?*h7nQk*Q+lNKaegor zJ5|}f=Hu&MC7Ax+uK*QdLe}M3waj@(>M0VCCkT>{xZC_4jo$C5b1kKwf}zMtZd9XO z?PJ=w1R!6vzKjAPmrBYg5o5^5UKo42`?k?~94yOYKqYPwYh51xghkI4hFfDmYrS{Z z0~|?&=jMOSS%QZZ2m^~Zba`ZNsng9YJDNv@5BI9yH$u@1C`>Ns`bbU$v5r+`ma-$+ zg$U*P?)BD01I`EDFU8B>hhF2_jc=qII}M`S;If9a@kUWoJx`W3&m0_EK^lW_jI#^6 z$p#XSD|;j2TcmJ$9|Bly7F1JDndSGb;ZO%atZ{;l*G2Cj0Rb6QDJqDXQBj@^%!Co|Tx8Msk7?l?01Qx(7Lo&9e=%$b6i_Vf9kbT48hdq+5it`dJYylQ8` zD>p2%`fcy~O{zBBhTt)p^W8(@2Gog`_EePRG0b6c7EYU3(TB9=QHXI2^e0bEzfV`s z?1#uX|5h&jv>wurqaQ7X@+!P~Cd9XxI;hzl8Ta$K*@}CjRk>hC_E+x9!1odl+@56H zxMiG;w)bd_lZ?!>X;O3EKenOVs25;g=sQpE?^>*@mQU-398Fp&R8A8B2fv3ZNNq7p zQl|G|Qu7vIz0B4R#@9$3Z_3aG8&>Y+M;J$OfYF4V-t#7IPi1v2Ic5uK2EPwLl`37+=&<&L52?!E*JdJq;=X?2knP-qLPmaG6Utae%s2tq z@L%(2cXI!bpv31?537DoqsP_1!wZ<&g8Z&Jb7F}_e<0A$_j4IaUK=xH^y+zbD#Yry;$aY3=-j8-F0`|a{ zDA`x;B(DoXHrVl#HY^)*aK*e_vLISqU%11+1XybFdOnIB?I_`~PC{i@7mZEwx-wd> z?KDl%kc9wg;u7_zuJ;r{lOLFX8RoGXfM)wKFiGun!j79g`HObR3K)Tb0>L8wvc3lj zBa-#z>pA%1Gh*ZKQLP+oJnzBXEeDS?$<>;{9Sh>HXd=18r|eYdU5sLk85)Em9~2%6 zwY4tY*=bdUbTKZ+rvR77CWe8ZNuT=5anGfAhylqwb* zK;a1Un9V4grQyXp_3cMBs-UZqA!|orxfV3t*=tFDDtj4p3khc=!8!o+_f!jRFP611 zq9|BG=$M{_own6I6eVmObAHz2@F6br;efkq)Vpc+M~n>pue`habIY_j8R{fn6rL7k zM?(6Y;m_!mHY1`vKrEAzR3o1ug-=3rA>!G)`D+j+*zfWCiE;9xZ+;Dq&#P70^3H$k z*;(jj%QkU)bf7fF9h1>fzMo^Vswk-Pt&yzHI^$mX`Jpt^U>MEXQ+?*IW74RC5HCRa zrvX5oHrcWuN<(fiGN}7cy;@`V-mg%6*yzlged|fy0#XD zqpTG=_ku2A3dwj+*{-?wXO}GaesQpfW*&kfIu)K-mP-Hw{u!I(cQ2gHk0YChep2{$ z>Gx$jp{BB>v6#5Ff}N}t1Xk3ls2~~IW@K$9>Z1)e9Y!V6{*sSh9WI7bk1q(tKRWo6 z{RJ)YNX4NB5O*LPS|(gvXGs?F4#*(u)kQyNdbu4Ym~!Ukg+=wVPqqWij-iwQIeJkn zUk*j-4{Z^Vfjn$hPt`!D4)oQi4Ui+0HbX?3U@Jy>4Cq3sPWEjH(7$%Esjm^Ka;4=39roH6J^bRId- zmn#f^?IM$$x2+lfof_Q-dz_I z%@?y*m>Keh*Tj}BMLK4V^+{P+K>A%7k-@`}5HC(zt=zJCbT2trb|OmyzCp>aNz0&C z+jnE>uH-W#g)*$YA$LBYu2w)#0u{lAAeyF>on6~WT9_}(;HX#~bJG(?J<5X+z zBBUx#POyb5Ne##l-e>d}Q3VE&5Ed3T#6e7o!>!cB)AH}7AThZfFo9(Vy|48tlRB3% z>HN@oZB5<^b)m&20c7CSK?ii}(-T1fRqY+%X(MQd z9IpWnp)&D4pP=rgQC*@bajQd51Hhdp^tRc9N$usnx9`rUzvrGtJgievgAzdcN>Bqf z3eLQHlz1cCeyGbn#WcN*5Zw1-A!O@#B?rkMbRHXccXsH7-G_7<(HHW(?$=c~v9LDR zC~(5l8q2Q`>lsY{${EO$URhiX<(RlzOc}G}3Vg@&elOVU8Dq1e=3M;-tQ{0zIeUVD zbx86tz$=@7GA4EOz$hbC;&_Y$rlNlZ^wDeRTWsok3?$#0b(`KQP269!D|_0U6Cxel z)B=yg+v2F6uGYXIzT9FKVt@C?{=lo?C;7J?sM2uXG`IeIB@pHIn6^wC!^3 zax9r-Meg)v!?}n0Up|KcDs9)ZUk)Y7&}MMYv_JlPnB;Y^^s0IqlP1)4f{o~&(@?ZukDs4#NCOYY!%XM{mkJq zpa40^f73QEEy6k$s}5kd!^{~%C_N$~IOl$m5AjZH7WePTJXNx5h)ji)YHMfS-Q+d>Ln@)rC7oOz7d}mFtp2&Z_^ie*dD2Lz0`qt1WjC$?p&GOBvokOKG21Q#*_GibIQqi zq^nXeiBu#pu92~1U^7d8DvoYzzZ|ly2%A}uV5)Nq6mGc+t-#7R{`?+Afta!qEspF7 z)qO8HGu8dINHp%v5U)$CX*u^LBX<|_(E?L|vAuURndLxpvYf(Q{$zMxSC-9oWriot zxWv1MVd&VfbKnGahHXJpz~UKYET4>~uZgX1x)!^(_Q{r;Zt$90D< zQGyYAEUmy`L}j*Q2R@SVGUbK(# zJ56l^@z$w1TbG)iPj>_ndH3`9_4+R2X}_?FRJFc{CHP28ieH~EarY{9e{6{tseg}e zX~mzVED>|ML^Ti!p?`bQDA9Rmkfo*BPwri)nrDEeV-A)o^BaUwy{VBZAn2hf@DaM? zFO$os7501K>9gOO4$a%cFfW;6H)gu^HF^04kwK-$zwm*)%fwW{ZIo%P@6j1frK~(I! zoc{AnxB^59(*~;?W4sefQyY)ARuw#|3_&|#IZJ$r?Q*Lr*;M)BlYhIHjw47!8XDDf zIZ8|JkzatK3n-xe)YFZSEko1H(*PGuU7{wyk>I>+Q8)w+l!_~}S>78$OC1Rs z&Xp$=_lVTNQ>JP|fKDoQeJiQk#vzvEM>3EHlC|`7lCzp2uFgP&+jMiEgKM)!cX8TK z-BvW1YOU$Y+a%c^R9U&I^)?tV(i{!zRuc6FqrOLr9$%}N>BV!cxB0_S%_`2Hgumai zB&V;$;$!?U8_yixee-u%hKcH$3XgF zvSsK9bn|i^YfrT`9WVtyH#cpO4ISYJ)pz*=Wtl{X=tL;Uu~(y#JfZ7Q_ahkCACD$W zJqQp$#Hq~j!ff1MJ}%sbOg+!$MVY+?<-2w|rGkKta!71T++1vO{c!Z%qsoRZ zTQf~74gK16vL{_Cy!`Os5Y*EOGwLioRP>8$Hy*5|RT~rAaf4pdi~K=H99kC-ibZBQ zp*MTUZe?L#24$W$2~5GFYo+oSUpc_+RL9gM+l(u%PJ3)>B~1z6oCBO+Cv4@F zAvEU7jZ49f+3!)Hg|k6Q*pFX5ztJCFWMdQis8g*Q#u5n$>4+p+4Bx9ODiiPLz{|LH zOn<0{HA!oPw@Rd=WP*V2xOG^6QI*K-FqyxwT!2U6($N_8 zV}u_#!mmIgtClpK=Ho2DxoWZ>rqk9=9!sj03O>{5163ICO&eRO)xJdjvUppRwFZ(e zM`}BOe$p*31dWiH~l*Tt<;`U9Q}hp5OhJI})fh ziLWHBIe!aSsm*JbA+aT95Xaw88N12r+TZbO+ zR+UlXmXgqAAWr?(hJR$w*AG0bvanjbiYu-yOV@oCZ#}oHTlxejySckp;q87P>o=z& z>^{8=It*}a!PqMpibIWv^J%0?YqU4zT2--4uzH^!3$}B)CDI3Z?sBWF?{)0e2$IMH zF*~NrRHRz~QgNtA?OS|u)M?r6n4+=*fd_>AC1JG0Bxv96hn8^1b|A@e@XH8jw?3#5 zG8iH}4_d6@`eFodGMRTB=DoyCzQas*Gq?OAC+7Xlvj)+IwQg4``uae@sfted#6F079kovI^aZ zd_zT+N5y9vXK0KD#Ig%NVGSvLICFSMLQPz0R~3x)W6lU2ZhxPZo4RnvTWI_>k3=YV z-SBg_!DsW}8ch#L(JVymi^y9R^SNL(B*}kl8(4Rb#973_-WbR|q=hCNuF8szC`jwm z{2sI1*=zga`^JW2Ro9zVtD4sL1`96CNekmqab^`%0b1Q`bMTYWGect@FquqYh{_Yv~|5AH@oaoQQg_9rp5d$?vhBl1QCM91LvBW%_)XF%F*28To)!yewjf>r+ z9XhVO!DLKmyvWj8Xq`EP4=7H#g6UEhuai)pM8o9U>*o;UIh~|B+~5KP^2O7GeZtOc z)!GS0zQoA1_ZAXQC0tkbb)cW08VLlYt7j%P8QtVu^Va3;V|+xXxS!^%I7v17i4kwe zqbqce#W0XpZ+5@LhI-@uw4|m59SK&B-c={>rGY9nG0Y`m7V{meM@zpc5vrX0WIck zub8jBn8lSS0|Zfz9eo&+5aLv^Sx%!88KZu=I&p1lG5w&oS^Tvcp&}OZi-5#E35E1j zK5D)ABl)}B2Sat^Lsij~0|gap3634K?ELPS)9M@ADlm424`fPh{=4q(s1ES z4U`c{1lqL1lR;21Wb3G}@g(Q?+A$7G@63KPSn2A8CZE{YrkFALdm85(7_`tjsO+j< zxw$^IQnrZZkxWMBB8h)rj(*EP3z1O}b0um(80SG=1TkX; z0>l`GRZU>tRj8ibq9g@v(B6pd(xeEKZ9}lHz-sU(zc?1_K0abJF%h?sU3ne~=jcnn z441|Rjc9l^*%Fcy+ku4lk)Dj$f^x-Mt2`=S#%DEhfU2*}mT;Eu$PV@-pfsEZr5*X< z-dxlwXR|ZP{BR-Fy$S(?vfhMJ^VUINC+CV<`lidBOHN!n*chH&u07VH<1%hbEemxB zDy77lbT7^NG+N$>2X1arf_H{IT0QwX&9kPdiMwWd7nnkisS4qXtbPa&B1gSFxZ+oB zh`$mq@DYby`kmT5Uh{QvULd?O8zrBB@PUl~vuoOfxB_N&qBqJQ(Nc%zPVW7%@Gfh* zcp*S1vNpAHt6q@0MI5qewU@Y87)Cy1heo+mrq%wlrN$(EAbMW9CnSvgNf0;-3O`5a zO9DYJ+VuDA5`sdB8bWN+&0;rWx%%7Q*bF@^Dr>M}=YGaid0P*d4^JSac0uU2EcTG- zq%zbMZt5OtgBC`wun!lnKe=ETuumBq*J`CnB%@sck<^7WE92+7HSZ9w-fmB$XRG$7 zG*;Q6jEOz~MQgS+x0eeitIDB}{0}ez%3t+7N{|>x$&Qz8Yaz>^*=&?x|AczjgfAWFVYCv zQohkjyl`#U^aJg9d`Sw)cB)^ng7u`r4vn^VTxueAh&4QNdXDv#qCbqbRQ9^Ffo;YD z;i2oLK1SYt?bw7!1Thk`^n83$qsiMx>u$ie$aw7D9q`YXhu702Fm?U*Zp5rU4S?hi zMT8n_{FbV;pQf{0VP?1&(Y%k~&VsxUaFFU2vUfvJ0MXoEUB$A=W{73YeeSC$Nw;hZ z4U*-N0SmQwPif!|?qdNzsQSi2oK7_QWEc06Vl^3fCdEyiVbI%02|!!DZ|dQm+pZIR z0(^tQ1s4e0Ga;BZzq{0*?dRX-8fRM~N;H*DDw4EK2+E?vpSF$jH61cV+_}z4A~yg3t1P9>D@VJ;5o^}<_5X}!ot1` zG?(lNcNXH%ebzBoj#xF{NsoWFBF;~g-)N+J6XuTs8qS0NOjQ2+^QJ>NQv}?t`7{Jw z_NMXzR8$pSEL1n!pXxe!1J2vafD~i2j`}3QwYqg01v7W}rEM^f$zMPOJ}+r)5ti z*wR>;9`$Kt=k(o1Thk=AZ&vxPQ_Sjy0rwtBTPm@rRF*}npqermW!cXLBuRAVX;Y-dm;yq310NI z18z#SLn}^VZD&?)-Al5>iIt=kmmoJ`j0s^5{sgDuE$aIs)D0m{ww`{xKB+{1ZacI% z5JnaT=R%r0MiB!k|heY=l(`Qo`}?S&c5-Z)g`vQX2E~g zzDUmbbFxxe7=5A0zjQ^`3;T*CXS73$^Ur>SY{eh!d9n;ZFLS*!2%K#clI96YQi!nLPZze zEXkZL%VFOBTYLH)^PbBur4K?P4lm*_PutIMel^-eq$+Du7eYb5vGdltv ze0jOmv5fteQYBCZ&We6J38DABYNS9Q*sVSAya2`=CX2mL*nV;-l`pP_ek`S-XVdvm zsM{}5>)|&mNl(YQWnSwvZIs&RR`Cdl?H6U}k0DxRi!YhAM-8wBVMU2OOR-nI84ywB zy<%O^nZhSu`!k*d%=NYbM!RmJ1*cJ5*4Z@mJvwfbU-3`&LXQUu{!L>==d{gtn;KyJ z?B#J@kx0v-!j}L*PP0EG+UK}Id=eY#ZLxp;8d>m)`$5`|^;{J|$hbTJ0Qzbua1DSw STJ#Pot+!1A0Ex!|0RIo0jOH)^ literal 0 HcmV?d00001 From b6f03bca88cab136e9e905f531db427420d6513a Mon Sep 17 00:00:00 2001 From: Ben C Date: Sun, 24 Jul 2022 18:45:43 -0400 Subject: [PATCH 004/176] Typos in Getting Started --- .../pages/tutorials/getting_started.md | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/docs/content/pages/tutorials/getting_started.md b/docs/content/pages/tutorials/getting_started.md index df2de29d..b9f5dcb5 100644 --- a/docs/content/pages/tutorials/getting_started.md +++ b/docs/content/pages/tutorials/getting_started.md @@ -19,7 +19,7 @@ To get started, navigate to your mod manager and click the ⋮ symbol, then sele ![Select "Show in explorer"]({{ "images/getting_started/mod_manager_show_in_explorer.webp"|static }}) -Now, in explorer and create a new folder named "planets". As the name suggests, New Horizons will search the files in this folder for planets to generate. +Now, create a new folder named "planets". As the name suggests, New Horizons will search the files in this folder for planets to generate. ## Making Your First Planet @@ -64,7 +64,7 @@ It's a common way to convey data in many programs. ## Understanding JSON -All JSON files start out with an `object`, or a set of key value mappings, for example is we represent a person as JSON it might look like: +All JSON files start out with an `object`, or a set of key value mappings, for example if we represent a person as JSON it might look like: ```json { @@ -72,7 +72,7 @@ All JSON files start out with an `object`, or a set of key value mappings, for e } ``` -Those braces (`{}`) denote an object, and by doing `"name": "Jim"` we're saying that the name of this object (in this case person) is Jim +Those braces (`{}`) denote an object, and by doing `"name": "Jim"` we're saying that the name of this person is Jim `"name"` is the key, and `"Jim"` is the value. Objects can have multiple keys as well, as long as you separate them by commas: @@ -84,10 +84,10 @@ Objects can have multiple keys as well, as long as you separate them by commas: } ``` -But wait! why is `Jim` in quotation marks while `23` isn't? that's because of a little something called data types. +But wait! why is `Jim` in quotation marks while `23` isn't? that's because of something called data types. Each value has a datatype, in this case `"Jim"` is a `string`, because it represents a *string* of characters. Age is a `number`, it represents a numerical value. If we put 23 in quotation marks, its data type switches from a number to a string. -And if we remove the quotation marks from `"Jim"` we get a syntax error. Datatypes are a common source of errors, which is why we recommend using an editor like VSCode. +And if we remove the quotation marks from `"Jim"` we get a syntax error (a red underline). Datatypes are a common source of errors, which is why we recommend using an editor like VSCode. ### JSON Data Types @@ -95,7 +95,7 @@ Here's a list of data types you'll use when making your addons: #### String -A string of characters surrounded in quotation marks +A set of characters surrounded in quotation marks ```json "Im a string!" @@ -117,15 +117,19 @@ A numerical value, can be negative and have decimals, **not** surrounded in quot #### Boolean -A `true` or `false` value, think of it like an on or off switch +A `true` or `false` value, think of it like an on or off switch, also not surrounded in quotation marks ```json true ``` +```json +false +``` + #### Array -A set of values, values can be of any data type. Items are seperated by commas +A set of values, values can be of any data type. Items are seperated by commas. ```json [23, 45, 56] @@ -250,6 +254,6 @@ You may also notice blue and yellow logs start appearing in your console, this i ## Modules -Base, Atmosphere, and Orbit are all modules, which define the different aspects of your planet represented by objects +Base, Atmosphere, and Orbit are all modules, which define the different aspects of your planet, modules are represented by JSON objects **Next Up: [Reading Schemas]({{ "Reading Schemas"|route }})** From 19f4f5709467bf45a47e1d5a0917b544d96e5c0e Mon Sep 17 00:00:00 2001 From: Ben C Date: Sun, 24 Jul 2022 18:47:05 -0400 Subject: [PATCH 005/176] Typos in Home --- docs/content/pages/home.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/pages/home.md b/docs/content/pages/home.md index fa228b44..6fc7c70e 100644 --- a/docs/content/pages/home.md +++ b/docs/content/pages/home.md @@ -12,7 +12,7 @@ This is the official documentation for [New Horizons](https://github.com/xen-42/ ## Getting Started -For a guide on getting started an getting familiar with New Horizons, go to the [Getting Started]({{ "Getting Started"|route }}) page. +For a guide on getting started with New Horizons, go to the [Getting Started]({{ "Getting Started"|route }}) page. ## Helpful Resources From b28576dfd63d4b1963d8d04632c54f31d37a88d7 Mon Sep 17 00:00:00 2001 From: Ben C Date: Sun, 24 Jul 2022 19:16:45 -0400 Subject: [PATCH 006/176] Typos in Getting Started --- docs/content/pages/tutorials/getting_started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/pages/tutorials/getting_started.md b/docs/content/pages/tutorials/getting_started.md index b9f5dcb5..1b2da307 100644 --- a/docs/content/pages/tutorials/getting_started.md +++ b/docs/content/pages/tutorials/getting_started.md @@ -72,7 +72,7 @@ All JSON files start out with an `object`, or a set of key value mappings, for e } ``` -Those braces (`{}`) denote an object, and by doing `"name": "Jim"` we're saying that the name of this person is Jim +Those braces (`{}`) denote an object, and by doing `"name": "Jim"` we're saying that the name of this person is Jim; `"name"` is the key, and `"Jim"` is the value. Objects can have multiple keys as well, as long as you separate them by commas: From e92c6b6404bbbdd1a16935f2795854c6e6cc91f1 Mon Sep 17 00:00:00 2001 From: JohnCorby Date: Sat, 27 Aug 2022 21:26:38 -0700 Subject: [PATCH 007/176] next update?????? --- NewHorizons/Builder/Body/BrambleDimensionBuilder.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/NewHorizons/Builder/Body/BrambleDimensionBuilder.cs b/NewHorizons/Builder/Body/BrambleDimensionBuilder.cs index 65fa1fcf..18927c1f 100644 --- a/NewHorizons/Builder/Body/BrambleDimensionBuilder.cs +++ b/NewHorizons/Builder/Body/BrambleDimensionBuilder.cs @@ -192,8 +192,12 @@ namespace NewHorizons.Builder.Body cloak.GetComponent().enabled = true; // Cull stuff - var cullController = go.AddComponent(); - cullController.SetSector(sector); + // Do next update so other nodes can be built first + Delay.FireOnNextUpdate(() => + { + var cullController = go.AddComponent(); + cullController.SetSector(sector); + }); // finalize atmo.SetActive(true); From 313267427b96da3ac3c69d0c9b219ce603421521 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Sun, 28 Aug 2022 15:03:35 -0400 Subject: [PATCH 008/176] Better backup --- NewHorizons/Utility/DebugMenu/DebugMenu.cs | 31 +++++++++++++--------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/NewHorizons/Utility/DebugMenu/DebugMenu.cs b/NewHorizons/Utility/DebugMenu/DebugMenu.cs index 0d25ebfe..aa58001d 100644 --- a/NewHorizons/Utility/DebugMenu/DebugMenu.cs +++ b/NewHorizons/Utility/DebugMenu/DebugMenu.cs @@ -227,6 +227,24 @@ namespace NewHorizons.Utility.DebugMenu var json = loadedConfigFiles[filePath].ToSerializedJson(); + try + { + var path = loadedMod.ModHelper.Manifest.ModFolderPath + backupFolderName + relativePath; + Logger.LogVerbose($"Backing up... {relativePath} to {path}"); + var oldPath = loadedMod.ModHelper.Manifest.ModFolderPath + relativePath; + var directoryName = Path.GetDirectoryName(path); + Directory.CreateDirectory(directoryName); + + if (File.Exists(oldPath)) + File.WriteAllBytes(path, File.ReadAllBytes(oldPath)); + else + File.WriteAllText(path, json); + } + catch (Exception e) + { + Logger.LogError($"Failed to save backup file {backupFolderName}{relativePath}:\n{e}"); + } + try { Logger.Log($"Saving... {relativePath} to {filePath}"); @@ -240,19 +258,6 @@ namespace NewHorizons.Utility.DebugMenu { Logger.LogError($"Failed to save file {relativePath}:\n{e}"); } - - try - { - var path = Main.Instance.ModHelper.Manifest.ModFolderPath + backupFolderName + relativePath; - var directoryName = Path.GetDirectoryName(path); - Directory.CreateDirectory(directoryName); - - File.WriteAllText(path, json); - } - catch (Exception e) - { - Logger.LogError($"Failed to save backup file {backupFolderName}{relativePath}:\n{e}"); - } } } From 57c145c048dd1d3e696ac86a7212e1a16571972a Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Sun, 28 Aug 2022 15:35:05 -0400 Subject: [PATCH 009/176] Only autosave if save button is unlocked --- NewHorizons/Utility/DebugMenu/DebugMenu.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/NewHorizons/Utility/DebugMenu/DebugMenu.cs b/NewHorizons/Utility/DebugMenu/DebugMenu.cs index aa58001d..be19c867 100644 --- a/NewHorizons/Utility/DebugMenu/DebugMenu.cs +++ b/NewHorizons/Utility/DebugMenu/DebugMenu.cs @@ -69,7 +69,13 @@ namespace NewHorizons.Utility.DebugMenu PauseMenuInitHook(); - Main.Instance.OnChangeStarSystem.AddListener((string s) => SaveLoadedConfigsForRecentSystem()); + Main.Instance.OnChangeStarSystem.AddListener((string s) => { + if (saveButtonUnlocked) + { + SaveLoadedConfigsForRecentSystem(); + saveButtonUnlocked = false; + } + }); } else { From 135aae99744784408a2e23da19cefafad8d1a73d Mon Sep 17 00:00:00 2001 From: Ben C Date: Sun, 28 Aug 2022 21:02:34 -0400 Subject: [PATCH 010/176] Add Support For Extra Modules --- NewHorizons/Handlers/PlanetCreationHandler.cs | 1 + NewHorizons/INewHorizons.cs | 11 +++++ NewHorizons/Main.cs | 2 + NewHorizons/NewHorizonsApi.cs | 40 ++++++++++++------- 4 files changed, 40 insertions(+), 14 deletions(-) diff --git a/NewHorizons/Handlers/PlanetCreationHandler.cs b/NewHorizons/Handlers/PlanetCreationHandler.cs index 73df7daa..fcb82a02 100644 --- a/NewHorizons/Handlers/PlanetCreationHandler.cs +++ b/NewHorizons/Handlers/PlanetCreationHandler.cs @@ -248,6 +248,7 @@ namespace NewHorizons.Handlers } } } + Main.Instance.OnPlanetLoaded?.Invoke(body.Config.name); return true; } diff --git a/NewHorizons/INewHorizons.cs b/NewHorizons/INewHorizons.cs index 7c647003..7b9936ba 100644 --- a/NewHorizons/INewHorizons.cs +++ b/NewHorizons/INewHorizons.cs @@ -43,6 +43,17 @@ namespace NewHorizons /// UnityEvent GetStarSystemLoadedEvent(); + ///

+ /// An event invoked when NH has finished a planet for a star system. + /// Gives the name of the planet that was just loaded. + /// + UnityEvent GetBodyLoadedEvent(); + + /// + /// Gets an object in the `extras` object of a config, returns null if the key doesn't exist + /// + object GetExtraModule(Type moduleType, string extrasModuleName, string planetName); + /// /// Allows you to overwrite the default system. This is where the player is respawned after dying. /// diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index cb64f092..000eb46b 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -68,6 +68,7 @@ namespace NewHorizons public class StarSystemEvent : UnityEvent { } public StarSystemEvent OnChangeStarSystem; public StarSystemEvent OnStarSystemLoaded; + public StarSystemEvent OnPlanetLoaded; // For warping to the eye system private GameObject _ship; @@ -170,6 +171,7 @@ namespace NewHorizons OnChangeStarSystem = new StarSystemEvent(); OnStarSystemLoaded = new StarSystemEvent(); + OnPlanetLoaded = new StarSystemEvent(); SceneManager.sceneLoaded += OnSceneLoaded; SceneManager.sceneUnloaded += OnSceneUnloaded; diff --git a/NewHorizons/NewHorizonsApi.cs b/NewHorizons/NewHorizonsApi.cs index 0ddb9f14..13a0fae1 100644 --- a/NewHorizons/NewHorizonsApi.cs +++ b/NewHorizons/NewHorizonsApi.cs @@ -5,14 +5,17 @@ using OWML.Common; using OWML.Utils; using System; using System.Collections.Generic; +using System.Data.SqlTypes; using System.IO; using System.Linq; +using Newtonsoft.Json.Linq; using UnityEngine; using UnityEngine.Events; using Logger = NewHorizons.Utility.Logger; namespace NewHorizons { + public class NewHorizonsApi : INewHorizons { [Obsolete("Create(Dictionary config) is deprecated, please use LoadConfigs(IModBehaviour mod) instead")] @@ -64,20 +67,10 @@ namespace NewHorizons return Main.BodyDict.Values.SelectMany(x => x)?.ToList()?.FirstOrDefault(x => x.Config.name == name)?.Object; } - public string GetCurrentStarSystem() - { - return Main.Instance.CurrentStarSystem; - } - - public UnityEvent GetChangeStarSystemEvent() - { - return Main.Instance.OnChangeStarSystem; - } - - public UnityEvent GetStarSystemLoadedEvent() - { - return Main.Instance.OnStarSystemLoaded; - } + public string GetCurrentStarSystem() => Main.Instance.CurrentStarSystem; + public UnityEvent GetChangeStarSystemEvent() => Main.Instance.OnChangeStarSystem; + public UnityEvent GetStarSystemLoadedEvent() => Main.Instance.OnStarSystemLoaded; + public UnityEvent GetBodyLoadedEvent() => Main.Instance.OnPlanetLoaded; public bool SetDefaultSystem(string name) { @@ -108,6 +101,25 @@ namespace NewHorizons } } + public object GetExtraModule(Type moduleType, string extraModuleKey, string planetName) + { + var planet = Main.BodyDict[Main.Instance.CurrentStarSystem].Find((b) => b.Config.name == planetName); + if (planet == null) + { + // Uh idk if we need this but ye it do be here etc. + Logger.LogVerbose($"Attempting To Get Extras On Planet That Doesn't Exist! ({planetName})"); + return null; + } + var jsonText = File.ReadAllText(planet.Mod.ModHelper.Manifest.ModFolderPath + planet.RelativePath); + var jsonData = JObject.Parse(jsonText); + var possibleExtras = jsonData.Property("extras")?.Value; + if (possibleExtras is JObject extras) + { + return extras.Property(extraModuleKey)?.Value.ToObject(moduleType); + } + return null; + } + public GameObject SpawnObject(GameObject planet, Sector sector, string propToCopyPath, Vector3 position, Vector3 eulerAngles, float scale, bool alignWithNormal) { From ffc12dde6d0714582c257dd329200f9d908486d2 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Mon, 29 Aug 2022 01:12:08 -0400 Subject: [PATCH 011/176] Resize volume layers --- NewHorizons/Patches/ShapePatches.cs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 NewHorizons/Patches/ShapePatches.cs diff --git a/NewHorizons/Patches/ShapePatches.cs b/NewHorizons/Patches/ShapePatches.cs new file mode 100644 index 00000000..cf4d19d0 --- /dev/null +++ b/NewHorizons/Patches/ShapePatches.cs @@ -0,0 +1,29 @@ +using HarmonyLib; +using System.Collections.Generic; + +namespace NewHorizons.Patches +{ + [HarmonyPatch] + public class ShapePatches + { + [HarmonyPrefix] + [HarmonyPatch(typeof(ShapeManager), nameof(ShapeManager.Initialize))] + public static bool ShapeManager_Initialize() + { + ShapeManager._exists = true; + + ShapeManager._detectors = new ShapeManager.Layer(256); + for (int index = 0; index < 256; ++index) + ShapeManager._detectors[index].contacts = new List(64); + + ShapeManager._volumes = new ShapeManager.Layer[4]; + for (int index = 0; index < 4; ++index) + ShapeManager._volumes[index] = new ShapeManager.Layer(2048); + + ShapeManager._locked = false; + ShapeManager._frameFlag = false; + + return false; + } + } +} From 60d26822298f5fcb4bdd90af8828f937ac0f988e Mon Sep 17 00:00:00 2001 From: Ben C Date: Mon, 29 Aug 2022 08:04:09 -0400 Subject: [PATCH 012/176] Add To Body Schema --- SchemaExporter/SchemaExporter.cs | 191 ++++++++++++++++--------------- 1 file changed, 96 insertions(+), 95 deletions(-) diff --git a/SchemaExporter/SchemaExporter.cs b/SchemaExporter/SchemaExporter.cs index f0c802f5..450d6833 100644 --- a/SchemaExporter/SchemaExporter.cs +++ b/SchemaExporter/SchemaExporter.cs @@ -1,96 +1,97 @@ -using System; -using System.Collections.Generic; -using System.IO; -using NewHorizons.External.Configs; -using NJsonSchema; -using NJsonSchema.Generation; - -namespace SchemaExporter; - -public static class SchemaExporter -{ - public static void Main(string[] args) - { - const string folderName = "NewHorizons/Schemas"; - - Directory.CreateDirectory(folderName); - Console.WriteLine("Schema Generator: We're winning!"); - var settings = new JsonSchemaGeneratorSettings - { - IgnoreObsoleteProperties = true, - DefaultReferenceTypeNullHandling = ReferenceTypeNullHandling.NotNull, - FlattenInheritanceHierarchy = true, - AllowReferencesWithProperties = true - }; - var bodySchema = new Schema("Celestial Body Schema", "Schema for a celestial body in New Horizons", $"{folderName}/body_schema", settings); - bodySchema.Output(); - var systemSchema = - new Schema("Star System Schema", "Schema for a star system in New Horizons", $"{folderName}/star_system_schema", settings); - systemSchema.Output(); - var addonSchema = new Schema("Addon Manifest Schema", - "Schema for an addon manifest in New Horizons", $"{folderName}/addon_manifest_schema", settings); - addonSchema.Output(); - var translationSchema = - new Schema("Translation Schema", "Schema for a translation file in New Horizons", $"{folderName}/translation_schema", settings); - translationSchema.Output(); - Console.WriteLine("Done!"); - } - - private readonly struct Schema - { - private readonly JsonSchemaGeneratorSettings _generatorSettings; - private readonly string _title, _description; - private readonly string _outFileName; - - public Schema(string schemaTitle, string schemaDescription, string fileName, JsonSchemaGeneratorSettings settings) - { - _title = schemaTitle; - _description = schemaDescription; - _outFileName = fileName; - _generatorSettings = settings; - } - - public void Output() - { - Console.WriteLine($"Outputting {_title}"); - File.WriteAllText($"{_outFileName}.json", ToString()); - } - - public override string ToString() - { - return GetJsonSchema().ToJson(); - } - - private JsonSchema GetJsonSchema() - { - var schema = JsonSchema.FromType(_generatorSettings); - schema.Title = _title; - var schemaLinkProp = new JsonSchemaProperty - { - Type = JsonObjectType.String, - Description = "The schema to validate with" - }; - schema.Properties.Add("$schema", schemaLinkProp); - schema.ExtensionData ??= new Dictionary(); - schema.ExtensionData.Add("$docs", new Dictionary - { - {"title", _title}, - {"description", _description} - }); - - if (_title == "Celestial Body Schema") - { - schema.Definitions["OrbitModule"].Properties["semiMajorAxis"].Default = 5000f; - } - - if (_title == "Star System Schema") - { - schema.Definitions["NomaiCoordinates"].Properties["x"].UniqueItems = true; - schema.Definitions["NomaiCoordinates"].Properties["y"].UniqueItems = true; - schema.Definitions["NomaiCoordinates"].Properties["z"].UniqueItems = true; - } - - return schema; - } - } +using System; +using System.Collections.Generic; +using System.IO; +using NewHorizons.External.Configs; +using NJsonSchema; +using NJsonSchema.Generation; + +namespace SchemaExporter; + +public static class SchemaExporter +{ + public static void Main(string[] args) + { + const string folderName = "NewHorizons/Schemas"; + + Directory.CreateDirectory(folderName); + Console.WriteLine("Schema Generator: We're winning!"); + var settings = new JsonSchemaGeneratorSettings + { + IgnoreObsoleteProperties = true, + DefaultReferenceTypeNullHandling = ReferenceTypeNullHandling.NotNull, + FlattenInheritanceHierarchy = true, + AllowReferencesWithProperties = true + }; + var bodySchema = new Schema("Celestial Body Schema", "Schema for a celestial body in New Horizons", $"{folderName}/body_schema", settings); + bodySchema.Output(); + var systemSchema = + new Schema("Star System Schema", "Schema for a star system in New Horizons", $"{folderName}/star_system_schema", settings); + systemSchema.Output(); + var addonSchema = new Schema("Addon Manifest Schema", + "Schema for an addon manifest in New Horizons", $"{folderName}/addon_manifest_schema", settings); + addonSchema.Output(); + var translationSchema = + new Schema("Translation Schema", "Schema for a translation file in New Horizons", $"{folderName}/translation_schema", settings); + translationSchema.Output(); + Console.WriteLine("Done!"); + } + + private readonly struct Schema + { + private readonly JsonSchemaGeneratorSettings _generatorSettings; + private readonly string _title, _description; + private readonly string _outFileName; + + public Schema(string schemaTitle, string schemaDescription, string fileName, JsonSchemaGeneratorSettings settings) + { + _title = schemaTitle; + _description = schemaDescription; + _outFileName = fileName; + _generatorSettings = settings; + } + + public void Output() + { + Console.WriteLine($"Outputting {_title}"); + File.WriteAllText($"{_outFileName}.json", ToString()); + } + + public override string ToString() + { + return GetJsonSchema().ToJson(); + } + + private JsonSchema GetJsonSchema() + { + var schema = JsonSchema.FromType(_generatorSettings); + schema.Title = _title; + var schemaLinkProp = new JsonSchemaProperty + { + Type = JsonObjectType.String, + Description = "The schema to validate with" + }; + schema.Properties.Add("$schema", schemaLinkProp); + schema.ExtensionData ??= new Dictionary(); + schema.ExtensionData.Add("$docs", new Dictionary + { + {"title", _title}, + {"description", _description} + }); + + if (_title == "Celestial Body Schema") + { + schema.Definitions["OrbitModule"].Properties["semiMajorAxis"].Default = 5000f; + schema.Properties.Add("extras", new Dictionary()); + } + + if (_title == "Star System Schema") + { + schema.Definitions["NomaiCoordinates"].Properties["x"].UniqueItems = true; + schema.Definitions["NomaiCoordinates"].Properties["y"].UniqueItems = true; + schema.Definitions["NomaiCoordinates"].Properties["z"].UniqueItems = true; + } + + return schema; + } + } } \ No newline at end of file From 549bea956713a5dfa49932e924ac941d91bb0bd8 Mon Sep 17 00:00:00 2001 From: Ben C Date: Mon, 29 Aug 2022 08:08:36 -0400 Subject: [PATCH 013/176] Ok try again --- SchemaExporter/SchemaExporter.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/SchemaExporter/SchemaExporter.cs b/SchemaExporter/SchemaExporter.cs index 450d6833..2dba6386 100644 --- a/SchemaExporter/SchemaExporter.cs +++ b/SchemaExporter/SchemaExporter.cs @@ -81,7 +81,10 @@ public static class SchemaExporter if (_title == "Celestial Body Schema") { schema.Definitions["OrbitModule"].Properties["semiMajorAxis"].Default = 5000f; - schema.Properties.Add("extras", new Dictionary()); + schema.Properties.Add("extras", new JsonSchemaProperty { + Type = JsonObjectType.Object, + Description = "Extra data that may be used by extension mods" + }); } if (_title == "Star System Schema") @@ -89,6 +92,10 @@ public static class SchemaExporter schema.Definitions["NomaiCoordinates"].Properties["x"].UniqueItems = true; schema.Definitions["NomaiCoordinates"].Properties["y"].UniqueItems = true; schema.Definitions["NomaiCoordinates"].Properties["z"].UniqueItems = true; + schema.Properties.Add("extras", new JsonSchemaProperty { + Type = JsonObjectType.Object, + Description = "Extra data that may be used by extension mods" + }); } return schema; From a8ca79896b519a82036b2b195d15477f40545fb4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 29 Aug 2022 12:13:22 +0000 Subject: [PATCH 014/176] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 4 ++++ NewHorizons/Schemas/star_system_schema.json | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index bc88b9f7..c4fe13eb 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -131,6 +131,10 @@ "$schema": { "type": "string", "description": "The schema to validate with" + }, + "extras": { + "type": "object", + "description": "Extra data that may be used by extension mods" } }, "definitions": { diff --git a/NewHorizons/Schemas/star_system_schema.json b/NewHorizons/Schemas/star_system_schema.json index b1f5e2cc..54770b00 100644 --- a/NewHorizons/Schemas/star_system_schema.json +++ b/NewHorizons/Schemas/star_system_schema.json @@ -74,6 +74,10 @@ "$schema": { "type": "string", "description": "The schema to validate with" + }, + "extras": { + "type": "object", + "description": "Extra data that may be used by extension mods" } }, "definitions": { From 21dc7fc8dd5769221855de124e1afffa2bf1957a Mon Sep 17 00:00:00 2001 From: Ben C Date: Mon, 29 Aug 2022 08:33:20 -0400 Subject: [PATCH 015/176] Updated Docs --- docs/content/pages/tutorials/api.md | 95 ++++++++++++++++++++--- docs/content/pages/tutorials/extending.md | 65 ++++++++++++++++ 2 files changed, 149 insertions(+), 11 deletions(-) create mode 100644 docs/content/pages/tutorials/extending.md diff --git a/docs/content/pages/tutorials/api.md b/docs/content/pages/tutorials/api.md index 8689943c..114cbe68 100644 --- a/docs/content/pages/tutorials/api.md +++ b/docs/content/pages/tutorials/api.md @@ -9,21 +9,94 @@ First create the following interface in your mod: ```cs public interface INewHorizons -{ - void LoadConfigs(IModBehaviour mod); + { + [Obsolete("Create(Dictionary config) is deprecated, please use LoadConfigs(IModBehaviour mod) instead")] + void Create(Dictionary config); - GameObject GetPlanet(string name); - - string GetCurrentStarSystem(); + [Obsolete("Create(Dictionary config) is deprecated, please use LoadConfigs(IModBehaviour mod) instead")] + void Create(Dictionary config, IModBehaviour mod); - UnityEvent GetChangeStarSystemEvent(); + /// + /// Will load all configs in the regular folders (planets, systems, translations, etc) for this mod. + /// The NH addon config template is just a single call to this API method. + /// + void LoadConfigs(IModBehaviour mod); - UnityEvent GetStarSystemLoadedEvent(); - - GameObject SpawnObject(GameObject planet, Sector sector, string propToCopyPath, Vector3 position, Vector3 eulerAngles, float scale, bool alignWithNormal); + /// + /// Retrieve the root GameObject of a custom planet made by creating configs. + /// Will only work if the planet has been created (see GetStarSystemLoadedEvent) + /// + GameObject GetPlanet(string name); - string[] GetInstalledAddons(); -} + /// + /// The name of the current star system loaded. + /// + string GetCurrentStarSystem(); + + /// + /// An event invoked when the player begins loading the new star system, before the scene starts to load. + /// Gives the name of the star system being switched to. + /// + UnityEvent GetChangeStarSystemEvent(); + + /// + /// An event invoked when NH has finished generating all planets for a new star system. + /// Gives the name of the star system that was just loaded. + /// + UnityEvent GetStarSystemLoadedEvent(); + + /// + /// An event invoked when NH has finished a planet for a star system. + /// Gives the name of the planet that was just loaded. + /// + UnityEvent GetBodyLoadedEvent(); + + /// + /// Gets an object in the `extras` object of a config, returns null if the key doesn't exist + /// + object GetExtraModule(Type moduleType, string extrasModuleName, string planetName); + + /// + /// Allows you to overwrite the default system. This is where the player is respawned after dying. + /// + bool SetDefaultSystem(string name); + + /// + /// Allows you to instantly begin a warp to a new star system. + /// Will return false if that system does not exist (cannot be warped to). + /// + bool ChangeCurrentStarSystem(string name); + + /// + /// Returns the uniqueIDs of each installed NH addon. + /// + string[] GetInstalledAddons(); + + /// + /// Allows you to spawn a copy of a prop by specifying its path. + /// This is the same as using Props->details in a config, but also returns the spawned gameObject to you. + /// + GameObject SpawnObject(GameObject planet, Sector sector, string propToCopyPath, Vector3 position, Vector3 eulerAngles, + float scale, bool alignWithNormal); + + /// + /// Allows you to spawn an AudioSignal on a planet. + /// This is the same as using Props->signals in a config, but also returns the spawned AudioSignal to you. + /// This method will not set its position. You will have to do that with the returned object. + /// + AudioSignal SpawnSignal(IModBehaviour mod, GameObject root, string audio, string name, string frequency, + float sourceRadius = 1f, float detectionRadius = 20f, float identificationRadius = 10f, bool insideCloak = false, + bool onlyAudibleToScope = true, string reveals = ""); + + /// + /// Allows you to spawn character dialogue on a planet. Also returns the RemoteDialogueTrigger if remoteTriggerRadius is specified. + /// This is the same as using Props->dialogue in a config, but also returns the spawned game objects to you. + /// This method will not set the position of the dialogue or remote trigger. You will have to do that with the returned objects. + /// + (CharacterDialogueTree, RemoteDialogueTrigger) SpawnDialogue(IModBehaviour mod, GameObject root, string xmlFile, float radius = 1f, + float range = 1f, string blockAfterPersistentCondition = null, float lookAtRadius = 1f, string pathToAnimController = null, + float remoteTriggerRadius = 0f); + } ``` In your main `ModBehaviour` class you can get the NewHorizons API like so: diff --git a/docs/content/pages/tutorials/extending.md b/docs/content/pages/tutorials/extending.md new file mode 100644 index 00000000..649c2473 --- /dev/null +++ b/docs/content/pages/tutorials/extending.md @@ -0,0 +1,65 @@ +--- +Title: Extending Configs +Description: A guide on extending config files with the New Horizons API +Sort_Priority: 5 +--- + + + +# Extending Configs + +This guide will explain how to use the API to add new features to New Horizons. + +## How Extending Works + +Addon developers will add a key to the `extras` object in the root of the config + +```json +{ + "name": "Wetrock", + "extras": { + "myCoolExtensionData": { + "myCoolExtensionProperty": 2 + } + } +} +``` + +Your mod will then use the API's `GetExtraModule` method to obtain the `myCoolExtensionData` object. + +## Extending Planets + +You can extend all planets by hooking into the `OnBodyLoaded` event of the API: + +```cs +var api = ModHelper.Interactions.TryGetModApi("xen.NewHorizons"); +api.GetBodyLoadedEvent().AddListener((name) => { + ModHelper.Console.WriteLine($"Body: {name} Loaded!"); +}); +``` + +In order to get your extra module, first define the module as a class: + +```cs +public class MyCoolExtensionData { + int myCoolExtensionProperty; +} +``` + +Then, use the `GetExtraModule` method: + +```cs +var api = ModHelper.Interactions.TryGetModApi("xen.NewHorizons"); +api.GetBodyLoadedEvent().AddListener((name) => { + ModHelper.Console.WriteLine($"Body: {name} Loaded!"); + var potentialData = api.GetExtraModule(typeof(MyCoolExtensionData), "myCoolExtensionData", name); + // Makes sure the module is valid and not null + if (potentialData is MyCoolExtensionData data) { + ModHelper.Console.WriteLine($"myCoolExtensionProperty for {name} is {data.myCoolExtensionProperty}!"); + } +}); +``` + +## Extending Systems + + From 44984bde2729c58cfe29a697b9edc24cd17bef8c Mon Sep 17 00:00:00 2001 From: Ben C Date: Mon, 29 Aug 2022 08:38:31 -0400 Subject: [PATCH 016/176] Update API docs to use TryGetModApi --- docs/content/pages/tutorials/api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/pages/tutorials/api.md b/docs/content/pages/tutorials/api.md index 114cbe68..817c89ba 100644 --- a/docs/content/pages/tutorials/api.md +++ b/docs/content/pages/tutorials/api.md @@ -106,7 +106,7 @@ public class MyMod : ModBehaviour { void Start() { - INewHorizons NewHorizonsAPI = ModHelper.Interaction.GetModApi("xen.NewHorizons"); + INewHorizons NewHorizonsAPI = ModHelper.Interaction.TryGetModApi("xen.NewHorizons"); } } ``` From 202eb808aa28addffc79610ca9e7df9ed7c31f50 Mon Sep 17 00:00:00 2001 From: Ben C Date: Mon, 29 Aug 2022 08:52:26 -0400 Subject: [PATCH 017/176] Add Mod Dep Warning --- docs/content/pages/tutorials/extending.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/content/pages/tutorials/extending.md b/docs/content/pages/tutorials/extending.md index 649c2473..67e7e5ca 100644 --- a/docs/content/pages/tutorials/extending.md +++ b/docs/content/pages/tutorials/extending.md @@ -25,6 +25,8 @@ Addon developers will add a key to the `extras` object in the root of the config } ``` +**It's up to the addon dev to list your mod as a dependency!** + Your mod will then use the API's `GetExtraModule` method to obtain the `myCoolExtensionData` object. ## Extending Planets From 6722ff7ad40fce6a84804e326514f9786fdfea09 Mon Sep 17 00:00:00 2001 From: Ben C Date: Mon, 29 Aug 2022 17:25:42 -0400 Subject: [PATCH 018/176] Added Support for Systems --- NewHorizons/INewHorizons.cs | 9 +- NewHorizons/Main.cs | 8 +- NewHorizons/NewHorizonsApi.cs | 41 +++-- NewHorizons/Utility/NewHorizonsSystem.cs | 4 +- SchemaExporter/SchemaExporter.cs | 211 ++++++++++++----------- 5 files changed, 152 insertions(+), 121 deletions(-) diff --git a/NewHorizons/INewHorizons.cs b/NewHorizons/INewHorizons.cs index 7b9936ba..aa014ea6 100644 --- a/NewHorizons/INewHorizons.cs +++ b/NewHorizons/INewHorizons.cs @@ -50,9 +50,14 @@ namespace NewHorizons UnityEvent GetBodyLoadedEvent(); /// - /// Gets an object in the `extras` object of a config, returns null if the key doesn't exist + /// Gets an object in the `extras` object of a body config, returns null if the key doesn't exist /// - object GetExtraModule(Type moduleType, string extrasModuleName, string planetName); + object GetExtraModuleForBody(Type moduleType, string extrasModuleName, string planetName); + + /// + /// Gets an object in the `extras` object of a system config, returns null if the key doesn't exist + /// + object GetExtraModuleForSystem(Type moduleType, string extrasModuleName, string systemName); /// /// Allows you to overwrite the default system. This is where the player is respawned after dying. diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index 000eb46b..97ba6124 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -127,7 +127,7 @@ namespace NewHorizons BodyDict["SolarSystem"] = new List(); BodyDict["EyeOfTheUniverse"] = new List(); // Keep this empty tho fr - SystemDict["SolarSystem"] = new NewHorizonsSystem("SolarSystem", new StarSystemConfig(), Instance) + SystemDict["SolarSystem"] = new NewHorizonsSystem("SolarSystem", new StarSystemConfig(), "", Instance) { Config = { @@ -143,7 +143,7 @@ namespace NewHorizons } } }; - SystemDict["EyeOfTheUniverse"] = new NewHorizonsSystem("EyeOfTheUniverse", new StarSystemConfig(), Instance) + SystemDict["EyeOfTheUniverse"] = new NewHorizonsSystem("EyeOfTheUniverse", new StarSystemConfig(), "", Instance) { Config = { @@ -517,7 +517,7 @@ namespace NewHorizons } else { - SystemDict[name] = new NewHorizonsSystem(name, starSystemConfig, mod); + SystemDict[name] = new NewHorizonsSystem(name, starSystemConfig, relativePath, mod); } } } @@ -618,7 +618,7 @@ namespace NewHorizons starSystemConfig.Migrate(); starSystemConfig.FixCoordinates(); - var system = new NewHorizonsSystem(config.starSystem, starSystemConfig, mod); + var system = new NewHorizonsSystem(config.starSystem, starSystemConfig, "", mod); SystemDict.Add(config.starSystem, system); diff --git a/NewHorizons/NewHorizonsApi.cs b/NewHorizons/NewHorizonsApi.cs index 13a0fae1..e8e36a24 100644 --- a/NewHorizons/NewHorizonsApi.cs +++ b/NewHorizons/NewHorizonsApi.cs @@ -101,23 +101,42 @@ namespace NewHorizons } } - public object GetExtraModule(Type moduleType, string extraModuleKey, string planetName) + private object GetExtraModule(Type moduleType, string key, string path) { - var planet = Main.BodyDict[Main.Instance.CurrentStarSystem].Find((b) => b.Config.name == planetName); - if (planet == null) + if (path == "") return null; + try { - // Uh idk if we need this but ye it do be here etc. - Logger.LogVerbose($"Attempting To Get Extras On Planet That Doesn't Exist! ({planetName})"); + var jsonText = File.ReadAllText(path); + var jsonData = JObject.Parse(jsonText); + var possibleExtras = jsonData.Property("extras")?.Value; + if (possibleExtras is JObject extras) + { + return extras.Property(key)?.Value.ToObject(moduleType); + } return null; } - var jsonText = File.ReadAllText(planet.Mod.ModHelper.Manifest.ModFolderPath + planet.RelativePath); - var jsonData = JObject.Parse(jsonText); - var possibleExtras = jsonData.Property("extras")?.Value; - if (possibleExtras is JObject extras) + catch (FileNotFoundException) { - return extras.Property(extraModuleKey)?.Value.ToObject(moduleType); + return null; } - return null; + } + + public object GetExtraModuleForBody(Type moduleType, string extraModuleKey, string planetName) + { + var planet = Main.BodyDict[Main.Instance.CurrentStarSystem].Find((b) => b.Config.name == planetName); + return planet == null + ? null + : GetExtraModule(moduleType, extraModuleKey, + planet.Mod.ModHelper.Manifest.ModFolderPath + planet.RelativePath); + } + + public object GetExtraModuleForSystem(Type moduleType, string extraModuleKey, string systemName) + { + var system = Main.SystemDict[Main.Instance.CurrentStarSystem]; + return system == null + ? null + : GetExtraModule(moduleType, extraModuleKey, + system.Mod.ModHelper.Manifest.ModFolderPath + system.RelativePath); } public GameObject SpawnObject(GameObject planet, Sector sector, string propToCopyPath, Vector3 position, Vector3 eulerAngles, diff --git a/NewHorizons/Utility/NewHorizonsSystem.cs b/NewHorizons/Utility/NewHorizonsSystem.cs index ae6bafa3..be9a539f 100644 --- a/NewHorizons/Utility/NewHorizonsSystem.cs +++ b/NewHorizons/Utility/NewHorizonsSystem.cs @@ -11,15 +11,17 @@ namespace NewHorizons.Utility public class NewHorizonsSystem { public string UniqueID; + public string RelativePath; public SpawnModule Spawn = null; public SpawnPoint SpawnPoint = null; public StarSystemConfig Config; public IModBehaviour Mod; - public NewHorizonsSystem(string uniqueID, StarSystemConfig config, IModBehaviour mod) + public NewHorizonsSystem(string uniqueID, StarSystemConfig config, string relativePath, IModBehaviour mod) { UniqueID = uniqueID; Config = config; + RelativePath = relativePath; Mod = mod; } } diff --git a/SchemaExporter/SchemaExporter.cs b/SchemaExporter/SchemaExporter.cs index 2dba6386..33c12666 100644 --- a/SchemaExporter/SchemaExporter.cs +++ b/SchemaExporter/SchemaExporter.cs @@ -1,104 +1,109 @@ -using System; -using System.Collections.Generic; -using System.IO; -using NewHorizons.External.Configs; -using NJsonSchema; -using NJsonSchema.Generation; - -namespace SchemaExporter; - -public static class SchemaExporter -{ - public static void Main(string[] args) - { - const string folderName = "NewHorizons/Schemas"; - - Directory.CreateDirectory(folderName); - Console.WriteLine("Schema Generator: We're winning!"); - var settings = new JsonSchemaGeneratorSettings - { - IgnoreObsoleteProperties = true, - DefaultReferenceTypeNullHandling = ReferenceTypeNullHandling.NotNull, - FlattenInheritanceHierarchy = true, - AllowReferencesWithProperties = true - }; - var bodySchema = new Schema("Celestial Body Schema", "Schema for a celestial body in New Horizons", $"{folderName}/body_schema", settings); - bodySchema.Output(); - var systemSchema = - new Schema("Star System Schema", "Schema for a star system in New Horizons", $"{folderName}/star_system_schema", settings); - systemSchema.Output(); - var addonSchema = new Schema("Addon Manifest Schema", - "Schema for an addon manifest in New Horizons", $"{folderName}/addon_manifest_schema", settings); - addonSchema.Output(); - var translationSchema = - new Schema("Translation Schema", "Schema for a translation file in New Horizons", $"{folderName}/translation_schema", settings); - translationSchema.Output(); - Console.WriteLine("Done!"); - } - - private readonly struct Schema - { - private readonly JsonSchemaGeneratorSettings _generatorSettings; - private readonly string _title, _description; - private readonly string _outFileName; - - public Schema(string schemaTitle, string schemaDescription, string fileName, JsonSchemaGeneratorSettings settings) - { - _title = schemaTitle; - _description = schemaDescription; - _outFileName = fileName; - _generatorSettings = settings; - } - - public void Output() - { - Console.WriteLine($"Outputting {_title}"); - File.WriteAllText($"{_outFileName}.json", ToString()); - } - - public override string ToString() - { - return GetJsonSchema().ToJson(); - } - - private JsonSchema GetJsonSchema() - { - var schema = JsonSchema.FromType(_generatorSettings); - schema.Title = _title; - var schemaLinkProp = new JsonSchemaProperty - { - Type = JsonObjectType.String, - Description = "The schema to validate with" - }; - schema.Properties.Add("$schema", schemaLinkProp); - schema.ExtensionData ??= new Dictionary(); - schema.ExtensionData.Add("$docs", new Dictionary - { - {"title", _title}, - {"description", _description} - }); - - if (_title == "Celestial Body Schema") - { - schema.Definitions["OrbitModule"].Properties["semiMajorAxis"].Default = 5000f; - schema.Properties.Add("extras", new JsonSchemaProperty { - Type = JsonObjectType.Object, - Description = "Extra data that may be used by extension mods" - }); - } - - if (_title == "Star System Schema") - { - schema.Definitions["NomaiCoordinates"].Properties["x"].UniqueItems = true; - schema.Definitions["NomaiCoordinates"].Properties["y"].UniqueItems = true; - schema.Definitions["NomaiCoordinates"].Properties["z"].UniqueItems = true; - schema.Properties.Add("extras", new JsonSchemaProperty { - Type = JsonObjectType.Object, - Description = "Extra data that may be used by extension mods" - }); - } - - return schema; - } - } +using System; +using System.Collections.Generic; +using System.IO; +using NewHorizons.External.Configs; +using NJsonSchema; +using NJsonSchema.Generation; + +namespace SchemaExporter; + +public static class SchemaExporter +{ + public static void Main(string[] args) + { + const string folderName = "NewHorizons/Schemas"; + + Directory.CreateDirectory(folderName); + Console.WriteLine("Schema Generator: We're winning!"); + var settings = new JsonSchemaGeneratorSettings + { + IgnoreObsoleteProperties = true, + DefaultReferenceTypeNullHandling = ReferenceTypeNullHandling.NotNull, + FlattenInheritanceHierarchy = true, + AllowReferencesWithProperties = true + }; + var bodySchema = new Schema("Celestial Body Schema", "Schema for a celestial body in New Horizons", $"{folderName}/body_schema", settings); + bodySchema.Output(); + var systemSchema = + new Schema("Star System Schema", "Schema for a star system in New Horizons", $"{folderName}/star_system_schema", settings); + systemSchema.Output(); + var addonSchema = new Schema("Addon Manifest Schema", + "Schema for an addon manifest in New Horizons", $"{folderName}/addon_manifest_schema", settings); + addonSchema.Output(); + var translationSchema = + new Schema("Translation Schema", "Schema for a translation file in New Horizons", $"{folderName}/translation_schema", settings); + translationSchema.Output(); + Console.WriteLine("Done!"); + } + + private readonly struct Schema + { + private readonly JsonSchemaGeneratorSettings _generatorSettings; + private readonly string _title, _description; + private readonly string _outFileName; + + public Schema(string schemaTitle, string schemaDescription, string fileName, JsonSchemaGeneratorSettings settings) + { + _title = schemaTitle; + _description = schemaDescription; + _outFileName = fileName; + _generatorSettings = settings; + } + + public void Output() + { + Console.WriteLine($"Outputting {_title}"); + File.WriteAllText($"{_outFileName}.json", ToString()); + } + + public override string ToString() + { + return GetJsonSchema().ToJson(); + } + + private JsonSchema GetJsonSchema() + { + var schema = JsonSchema.FromType(_generatorSettings); + schema.Title = _title; + var schemaLinkProp = new JsonSchemaProperty + { + Type = JsonObjectType.String, + Description = "The schema to validate with" + }; + schema.Properties.Add("$schema", schemaLinkProp); + schema.ExtensionData ??= new Dictionary(); + schema.ExtensionData.Add("$docs", new Dictionary + { + {"title", _title}, + {"description", _description} + }); + + switch (_title) + { + case "Celestial Body Schema": + schema.Definitions["OrbitModule"].Properties["semiMajorAxis"].Default = 5000f; + break; + case "Star System Schema": + schema.Definitions["NomaiCoordinates"].Properties["x"].UniqueItems = true; + schema.Definitions["NomaiCoordinates"].Properties["y"].UniqueItems = true; + schema.Definitions["NomaiCoordinates"].Properties["z"].UniqueItems = true; + break; + } + + if (_title is "Star System Schema" or "Celestial Body Schema") + { + schema.Properties.Add("extras", new JsonSchemaProperty { + Type = JsonObjectType.Object, + Description = "Extra data that may be used by extension mods", + AllowAdditionalProperties = true, + AdditionalPropertiesSchema = new JsonSchema + { + Type = JsonObjectType.Object + } + }); + } + + return schema; + } + } } \ No newline at end of file From 6aad0edd33b40b7980a9f422d06a2c63e2d26f85 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 29 Aug 2022 21:29:01 +0000 Subject: [PATCH 019/176] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 5 ++++- NewHorizons/Schemas/star_system_schema.json | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index c4fe13eb..02771c9b 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -134,7 +134,10 @@ }, "extras": { "type": "object", - "description": "Extra data that may be used by extension mods" + "description": "Extra data that may be used by extension mods", + "additionalProperties": { + "type": "object" + } } }, "definitions": { diff --git a/NewHorizons/Schemas/star_system_schema.json b/NewHorizons/Schemas/star_system_schema.json index 54770b00..d8073db0 100644 --- a/NewHorizons/Schemas/star_system_schema.json +++ b/NewHorizons/Schemas/star_system_schema.json @@ -77,7 +77,10 @@ }, "extras": { "type": "object", - "description": "Extra data that may be used by extension mods" + "description": "Extra data that may be used by extension mods", + "additionalProperties": { + "type": "object" + } } }, "definitions": { From 1c99f6c3d43e9d576b572a1fa87e6d5d52482f1a Mon Sep 17 00:00:00 2001 From: Ben C Date: Mon, 29 Aug 2022 17:31:01 -0400 Subject: [PATCH 020/176] Update Docs Again --- docs/content/pages/tutorials/api.md | 9 +++++++-- docs/content/pages/tutorials/extending.md | 12 +++++------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/docs/content/pages/tutorials/api.md b/docs/content/pages/tutorials/api.md index 817c89ba..0400351f 100644 --- a/docs/content/pages/tutorials/api.md +++ b/docs/content/pages/tutorials/api.md @@ -52,9 +52,14 @@ public interface INewHorizons UnityEvent GetBodyLoadedEvent(); /// - /// Gets an object in the `extras` object of a config, returns null if the key doesn't exist + /// Gets an object in the `extras` object of a body config, returns null if the key doesn't exist /// - object GetExtraModule(Type moduleType, string extrasModuleName, string planetName); + object GetExtraModuleForBody(Type moduleType, string extrasModuleName, string planetName); + + /// + /// Gets an object in the `extras` object of a system config, returns null if the key doesn't exist + /// + object GetExtraModuleForSystem(Type moduleType, string extrasModuleName, string systemName); /// /// Allows you to overwrite the default system. This is where the player is respawned after dying. diff --git a/docs/content/pages/tutorials/extending.md b/docs/content/pages/tutorials/extending.md index 67e7e5ca..406b5e0a 100644 --- a/docs/content/pages/tutorials/extending.md +++ b/docs/content/pages/tutorials/extending.md @@ -4,8 +4,6 @@ Description: A guide on extending config files with the New Horizons API Sort_Priority: 5 --- - - # Extending Configs This guide will explain how to use the API to add new features to New Horizons. @@ -25,9 +23,9 @@ Addon developers will add a key to the `extras` object in the root of the config } ``` -**It's up to the addon dev to list your mod as a dependency!** +Your mod will then use the API's `GetExtraModuleForBody` method to obtain the `myCoolExtensionData` object. -Your mod will then use the API's `GetExtraModule` method to obtain the `myCoolExtensionData` object. +**It's up to the addon dev to list your mod as a dependency!** ## Extending Planets @@ -48,13 +46,13 @@ public class MyCoolExtensionData { } ``` -Then, use the `GetExtraModule` method: +Then, use the `GetExtraModuleForBody` method: ```cs var api = ModHelper.Interactions.TryGetModApi("xen.NewHorizons"); api.GetBodyLoadedEvent().AddListener((name) => { ModHelper.Console.WriteLine($"Body: {name} Loaded!"); - var potentialData = api.GetExtraModule(typeof(MyCoolExtensionData), "myCoolExtensionData", name); + var potentialData = api.GetExtraModuleForBody(typeof(MyCoolExtensionData), "myCoolExtensionData", name); // Makes sure the module is valid and not null if (potentialData is MyCoolExtensionData data) { ModHelper.Console.WriteLine($"myCoolExtensionProperty for {name} is {data.myCoolExtensionProperty}!"); @@ -64,4 +62,4 @@ api.GetBodyLoadedEvent().AddListener((name) => { ## Extending Systems - +Extending systems is the exact same as extending planets, except you use the `GetExtraModuleForSystem` method instead. From 7cc26d66d972638044f7a7cd7d338859ffe8f4d7 Mon Sep 17 00:00:00 2001 From: Ben C Date: Mon, 29 Aug 2022 17:34:55 -0400 Subject: [PATCH 021/176] Add path for funny --- NewHorizons/Main.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index 97ba6124..8c9a7b34 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -618,7 +618,7 @@ namespace NewHorizons starSystemConfig.Migrate(); starSystemConfig.FixCoordinates(); - var system = new NewHorizonsSystem(config.starSystem, starSystemConfig, "", mod); + var system = new NewHorizonsSystem(config.starSystem, starSystemConfig, $"systems/{config.starSystem}.json", mod); SystemDict.Add(config.starSystem, system); From 1fcf21ff0e8134c4ae22c3339d6f420a89a01280 Mon Sep 17 00:00:00 2001 From: Ben C Date: Mon, 29 Aug 2022 17:38:00 -0400 Subject: [PATCH 022/176] Do da stanky leg 2 --- NewHorizons/Main.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index 8c9a7b34..a975b0ba 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -618,7 +618,7 @@ namespace NewHorizons starSystemConfig.Migrate(); starSystemConfig.FixCoordinates(); - var system = new NewHorizonsSystem(config.starSystem, starSystemConfig, $"systems/{config.starSystem}.json", mod); + var system = new NewHorizonsSystem(config.starSystem, starSystemConfig, $"", mod); SystemDict.Add(config.starSystem, system); From c31064c68ed7a1b7346e12bb0cea38f7601cdbfa Mon Sep 17 00:00:00 2001 From: Ben C Date: Mon, 29 Aug 2022 18:47:53 -0400 Subject: [PATCH 023/176] What if I like changed it LOL!!! --- NewHorizons/Handlers/PlanetCreationHandler.cs | 11 ++++++- NewHorizons/INewHorizons.cs | 8 ++--- NewHorizons/NewHorizonsApi.cs | 32 +++++++++---------- docs/content/pages/tutorials/api.md | 8 ++--- docs/content/pages/tutorials/extending.md | 23 +++++++++---- 5 files changed, 50 insertions(+), 32 deletions(-) diff --git a/NewHorizons/Handlers/PlanetCreationHandler.cs b/NewHorizons/Handlers/PlanetCreationHandler.cs index fcb82a02..4147fdb9 100644 --- a/NewHorizons/Handlers/PlanetCreationHandler.cs +++ b/NewHorizons/Handlers/PlanetCreationHandler.cs @@ -248,7 +248,16 @@ namespace NewHorizons.Handlers } } } - Main.Instance.OnPlanetLoaded?.Invoke(body.Config.name); + + try + { + Main.Instance.OnPlanetLoaded?.Invoke(body.Config.name); + } + catch (Exception e) + { + Logger.LogError($"Error in event handler for OnPlanetLoaded on body {body.Config.name}: {e.Message} : {e.StackTrace}"); + } + return true; } diff --git a/NewHorizons/INewHorizons.cs b/NewHorizons/INewHorizons.cs index aa014ea6..8f132ee7 100644 --- a/NewHorizons/INewHorizons.cs +++ b/NewHorizons/INewHorizons.cs @@ -50,14 +50,14 @@ namespace NewHorizons UnityEvent GetBodyLoadedEvent(); /// - /// Gets an object in the `extras` object of a body config, returns null if the key doesn't exist + /// Uses JSONPath to query a body /// - object GetExtraModuleForBody(Type moduleType, string extrasModuleName, string planetName); + object QueryBody(Type outType, string bodyName, string path); /// - /// Gets an object in the `extras` object of a system config, returns null if the key doesn't exist + /// Uses JSONPath to query a system /// - object GetExtraModuleForSystem(Type moduleType, string extrasModuleName, string systemName); + object QuerySystem(Type outType, string path); /// /// Allows you to overwrite the default system. This is where the player is respawned after dying. diff --git a/NewHorizons/NewHorizonsApi.cs b/NewHorizons/NewHorizonsApi.cs index e8e36a24..0ac47b68 100644 --- a/NewHorizons/NewHorizonsApi.cs +++ b/NewHorizons/NewHorizonsApi.cs @@ -8,6 +8,8 @@ using System.Collections.Generic; using System.Data.SqlTypes; using System.IO; using System.Linq; +using System.Reflection; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; using UnityEngine; using UnityEngine.Events; @@ -101,42 +103,40 @@ namespace NewHorizons } } - private object GetExtraModule(Type moduleType, string key, string path) + private static object QueryJson(Type outType, string filePath, string jsonPath) { - if (path == "") return null; + if (filePath == "") return null; try { - var jsonText = File.ReadAllText(path); + var jsonText = File.ReadAllText(filePath); var jsonData = JObject.Parse(jsonText); - var possibleExtras = jsonData.Property("extras")?.Value; - if (possibleExtras is JObject extras) - { - return extras.Property(key)?.Value.ToObject(moduleType); - } - return null; + return jsonData.SelectToken(jsonPath)?.ToObject(outType); } catch (FileNotFoundException) { return null; } + catch (JsonException e) + { + Logger.LogError($"{e.Message} : {e.StackTrace}"); + return null; + } } - public object GetExtraModuleForBody(Type moduleType, string extraModuleKey, string planetName) + public object QueryBody(Type outType, string bodyName, string jsonPath) { - var planet = Main.BodyDict[Main.Instance.CurrentStarSystem].Find((b) => b.Config.name == planetName); + var planet = Main.BodyDict[Main.Instance.CurrentStarSystem].Find((b) => b.Config.name == bodyName); return planet == null ? null - : GetExtraModule(moduleType, extraModuleKey, - planet.Mod.ModHelper.Manifest.ModFolderPath + planet.RelativePath); + : QueryJson(outType, planet.Mod.ModHelper.Manifest.ModFolderPath + planet.RelativePath, jsonPath); } - public object GetExtraModuleForSystem(Type moduleType, string extraModuleKey, string systemName) + public object QuerySystem(Type outType, string jsonPath) { var system = Main.SystemDict[Main.Instance.CurrentStarSystem]; return system == null ? null - : GetExtraModule(moduleType, extraModuleKey, - system.Mod.ModHelper.Manifest.ModFolderPath + system.RelativePath); + : QueryJson(outType, system.Mod.ModHelper.Manifest.ModFolderPath + system.RelativePath, jsonPath); } public GameObject SpawnObject(GameObject planet, Sector sector, string propToCopyPath, Vector3 position, Vector3 eulerAngles, diff --git a/docs/content/pages/tutorials/api.md b/docs/content/pages/tutorials/api.md index 0400351f..5c7a8996 100644 --- a/docs/content/pages/tutorials/api.md +++ b/docs/content/pages/tutorials/api.md @@ -52,14 +52,14 @@ public interface INewHorizons UnityEvent GetBodyLoadedEvent(); /// - /// Gets an object in the `extras` object of a body config, returns null if the key doesn't exist + /// Uses JSONPath to query a body /// - object GetExtraModuleForBody(Type moduleType, string extrasModuleName, string planetName); + object QueryBody(Type outType, string bodyName, string path); /// - /// Gets an object in the `extras` object of a system config, returns null if the key doesn't exist + /// Uses JSONPath to query a system /// - object GetExtraModuleForSystem(Type moduleType, string extrasModuleName, string systemName); + object QuerySystem(Type outType, string path); /// /// Allows you to overwrite the default system. This is where the player is respawned after dying. diff --git a/docs/content/pages/tutorials/extending.md b/docs/content/pages/tutorials/extending.md index 406b5e0a..7c3b4ec4 100644 --- a/docs/content/pages/tutorials/extending.md +++ b/docs/content/pages/tutorials/extending.md @@ -23,7 +23,7 @@ Addon developers will add a key to the `extras` object in the root of the config } ``` -Your mod will then use the API's `GetExtraModuleForBody` method to obtain the `myCoolExtensionData` object. +Your mod will then use the API's `QueryBody` method to obtain the `myCoolExtensionData` object. **It's up to the addon dev to list your mod as a dependency!** @@ -31,7 +31,7 @@ Your mod will then use the API's `GetExtraModuleForBody` method to obtain the `m You can extend all planets by hooking into the `OnBodyLoaded` event of the API: -```cs +```csharp var api = ModHelper.Interactions.TryGetModApi("xen.NewHorizons"); api.GetBodyLoadedEvent().AddListener((name) => { ModHelper.Console.WriteLine($"Body: {name} Loaded!"); @@ -40,19 +40,19 @@ api.GetBodyLoadedEvent().AddListener((name) => { In order to get your extra module, first define the module as a class: -```cs +```csharp public class MyCoolExtensionData { int myCoolExtensionProperty; } ``` -Then, use the `GetExtraModuleForBody` method: +Then, use the `QueryBody` method: -```cs +```csharp var api = ModHelper.Interactions.TryGetModApi("xen.NewHorizons"); api.GetBodyLoadedEvent().AddListener((name) => { ModHelper.Console.WriteLine($"Body: {name} Loaded!"); - var potentialData = api.GetExtraModuleForBody(typeof(MyCoolExtensionData), "myCoolExtensionData", name); + var potentialData = api.QueryBody(typeof(MyCoolExtensionData), "$.extras.myCoolExtensionData", name); // Makes sure the module is valid and not null if (potentialData is MyCoolExtensionData data) { ModHelper.Console.WriteLine($"myCoolExtensionProperty for {name} is {data.myCoolExtensionProperty}!"); @@ -62,4 +62,13 @@ api.GetBodyLoadedEvent().AddListener((name) => { ## Extending Systems -Extending systems is the exact same as extending planets, except you use the `GetExtraModuleForSystem` method instead. +Extending systems is the exact same as extending planets, except you use the `QuerySystem` method instead. + +## Accessing Other Values + +You can also use the `QueryBody` method to get values of the config outside of your extension object + +```csharp +var primaryBody = NHAPI.QueryBody(typeof(string), "Wetrock", "$.Orbit.primaryBody"); + ModHelper.Console.WriteLine($"Primary of {bodyName} is {primaryBody ?? "NULL"}!"); +``` From a9a9b20e31ae35e54717bbb2f536580c6f86723b Mon Sep 17 00:00:00 2001 From: Ben C Date: Mon, 29 Aug 2022 18:55:58 -0400 Subject: [PATCH 024/176] Fix name inconsistency in docs --- docs/content/pages/tutorials/extending.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/pages/tutorials/extending.md b/docs/content/pages/tutorials/extending.md index 7c3b4ec4..0e9b30bc 100644 --- a/docs/content/pages/tutorials/extending.md +++ b/docs/content/pages/tutorials/extending.md @@ -69,6 +69,6 @@ Extending systems is the exact same as extending planets, except you use the `Qu You can also use the `QueryBody` method to get values of the config outside of your extension object ```csharp -var primaryBody = NHAPI.QueryBody(typeof(string), "Wetrock", "$.Orbit.primaryBody"); +var primaryBody = api.QueryBody(typeof(string), "Wetrock", "$.Orbit.primaryBody"); ModHelper.Console.WriteLine($"Primary of {bodyName} is {primaryBody ?? "NULL"}!"); ``` From 5c04f456b6af51d3fc6bfa19ff3c658ffc48a8b5 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Mon, 29 Aug 2022 20:51:41 -0400 Subject: [PATCH 025/176] Just log the exception entirely so that it includes inner exceptions --- NewHorizons/Handlers/PlanetCreationHandler.cs | 2 +- NewHorizons/NewHorizonsApi.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/NewHorizons/Handlers/PlanetCreationHandler.cs b/NewHorizons/Handlers/PlanetCreationHandler.cs index 4147fdb9..ba0ea8f8 100644 --- a/NewHorizons/Handlers/PlanetCreationHandler.cs +++ b/NewHorizons/Handlers/PlanetCreationHandler.cs @@ -255,7 +255,7 @@ namespace NewHorizons.Handlers } catch (Exception e) { - Logger.LogError($"Error in event handler for OnPlanetLoaded on body {body.Config.name}: {e.Message} : {e.StackTrace}"); + Logger.LogError($"Error in event handler for OnPlanetLoaded on body {body.Config.name}: {e}"); } return true; diff --git a/NewHorizons/NewHorizonsApi.cs b/NewHorizons/NewHorizonsApi.cs index 0ac47b68..2c551f59 100644 --- a/NewHorizons/NewHorizonsApi.cs +++ b/NewHorizons/NewHorizonsApi.cs @@ -118,7 +118,7 @@ namespace NewHorizons } catch (JsonException e) { - Logger.LogError($"{e.Message} : {e.StackTrace}"); + Logger.LogError(e.ToString()); return null; } } From 11dc7551d8b369f5d56b0ca268bd7d6f0a156277 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Mon, 29 Aug 2022 21:14:20 -0400 Subject: [PATCH 026/176] Remove unused namespaces --- NewHorizons/NewHorizonsApi.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/NewHorizons/NewHorizonsApi.cs b/NewHorizons/NewHorizonsApi.cs index 2c551f59..4924834f 100644 --- a/NewHorizons/NewHorizonsApi.cs +++ b/NewHorizons/NewHorizonsApi.cs @@ -5,10 +5,8 @@ using OWML.Common; using OWML.Utils; using System; using System.Collections.Generic; -using System.Data.SqlTypes; using System.IO; using System.Linq; -using System.Reflection; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using UnityEngine; From 192c22733c919d5c3134fb4da0a70fc12580f285 Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 29 Aug 2022 22:22:37 -0400 Subject: [PATCH 027/176] GetProfileName method (QSB compat) --- NewHorizons/External/NewHorizonsData.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/NewHorizons/External/NewHorizonsData.cs b/NewHorizons/External/NewHorizonsData.cs index a6273dd7..f90cb479 100644 --- a/NewHorizons/External/NewHorizonsData.cs +++ b/NewHorizons/External/NewHorizonsData.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using NewHorizons.Utility; @@ -11,9 +11,12 @@ namespace NewHorizons.External private static string _activeProfileName; private static readonly string FileName = "save.json"; + // This is its own method so it can be patched by NH-QSB compat + public static string GetProfileName() => StandaloneProfileManager.SharedInstance?.currentProfile?.profileName; + public static void Load() { - _activeProfileName = StandaloneProfileManager.SharedInstance?.currentProfile?.profileName; + _activeProfileName = GetProfileName(); if (_activeProfileName == null) { Logger.LogError("Couldn't find active profile, are you on Gamepass?"); From 0b1325ab85f5b5613aee33926286f343dc725d55 Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 29 Aug 2022 22:37:12 -0400 Subject: [PATCH 028/176] Instead of killing player just freeze --- NewHorizons/Main.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index cb64f092..09d20227 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -647,6 +647,13 @@ namespace NewHorizons #region Change star system public void ChangeCurrentStarSystem(string newStarSystem, bool warp = false, bool vessel = false) { + // If we're just on the title screen set the system for later + if (LoadManager.GetCurrentScene() == OWScene.TitleScreen) + { + _currentStarSystem = newStarSystem; + return; + } + if (IsChangingStarSystem) return; IsWarpingFromShip = warp; @@ -658,9 +665,6 @@ namespace NewHorizons IsChangingStarSystem = true; WearingSuit = PlayerState.IsWearingSuit(); - // We kill them so they don't move as much - Locator.GetDeathManager().KillPlayer(DeathType.Meditation); - OWScene sceneToLoad; if (newStarSystem == "EyeOfTheUniverse") @@ -678,12 +682,15 @@ namespace NewHorizons _currentStarSystem = newStarSystem; + // Freeze player inputs + OWInput.ChangeInputMode(InputMode.None); + LoadManager.LoadSceneAsync(sceneToLoad, !vessel, LoadManager.FadeType.ToBlack, 0.1f, true); } void OnDeath(DeathType _) { - // We reset the solar system on death (unless we just killed the player) + // We reset the solar system on death if (!IsChangingStarSystem) { // If the override is a valid system then we go there From 83c562c4a4037c9e262503970aa7ac6f5fcab877 Mon Sep 17 00:00:00 2001 From: TerrificTrifid <99054745+TerrificTrifid@users.noreply.github.com> Date: Mon, 29 Aug 2022 21:38:12 -0500 Subject: [PATCH 029/176] Make rain/snow heights more robust --- .../Builder/Atmosphere/EffectsBuilder.cs | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/NewHorizons/Builder/Atmosphere/EffectsBuilder.cs b/NewHorizons/Builder/Atmosphere/EffectsBuilder.cs index fb405705..c177a9ed 100644 --- a/NewHorizons/Builder/Atmosphere/EffectsBuilder.cs +++ b/NewHorizons/Builder/Atmosphere/EffectsBuilder.cs @@ -20,6 +20,16 @@ namespace NewHorizons.Builder.Atmosphere SCG._dynamicCullingBounds = false; SCG._waitForStreaming = false; + var minHeight = surfaceSize; + var maxHeight = config.Atmosphere.size; + if (config.HeightMap?.minHeight != null) + { + if (config.Water?.size >= config.HeightMap.minHeight) minHeight = config.Water.size; // use sea level if its higher + else minHeight = config.HeightMap.minHeight; + } + else if (config.Water?.size != null) minHeight = config.Water.size; + else if (config.Lava?.size != null) minHeight = config.Lava.size; + if (config.Atmosphere.hasRain) { var rainGO = GameObject.Instantiate(SearchUtilities.Find("GiantsDeep_Body/Sector_GD/Sector_GDInterior/Effects_GDInterior/Effects_GD_Rain"), effectsGO.transform); @@ -29,9 +39,9 @@ namespace NewHorizons.Builder.Atmosphere var pvc = rainGO.GetComponent(); pvc._densityByHeight = new AnimationCurve(new Keyframe[] { - new Keyframe(surfaceSize - 0.5f, 0), - new Keyframe(surfaceSize, 10f), - new Keyframe(config.Atmosphere.size, 0f) + new Keyframe(minHeight - 0.5f, 0), + new Keyframe(minHeight, 10f), + new Keyframe(maxHeight, 0f) }); rainGO.GetComponent()._activeInSector = sector; @@ -53,9 +63,9 @@ namespace NewHorizons.Builder.Atmosphere var pvc = snowEmitter.GetComponent(); pvc._densityByHeight = new AnimationCurve(new Keyframe[] { - new Keyframe(surfaceSize - 0.5f, 0), - new Keyframe(surfaceSize, 10f), - new Keyframe(config.Atmosphere.size, 0f) + new Keyframe(minHeight - 0.5f, 0), + new Keyframe(minHeight, 10f), + new Keyframe(maxHeight, 0f) }); snowEmitter.GetComponent()._activeInSector = sector; From 2d03fe04a42efd2ec90903c957beb1730bbb557c Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Tue, 30 Aug 2022 00:48:39 -0400 Subject: [PATCH 030/176] No - for image key --- NewHorizons/OtherMods/OWRichPresence/RichPresenceHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/OtherMods/OWRichPresence/RichPresenceHandler.cs b/NewHorizons/OtherMods/OWRichPresence/RichPresenceHandler.cs index a1e4da27..7b94ca6c 100644 --- a/NewHorizons/OtherMods/OWRichPresence/RichPresenceHandler.cs +++ b/NewHorizons/OtherMods/OWRichPresence/RichPresenceHandler.cs @@ -47,7 +47,7 @@ namespace NewHorizons.OtherMods.OWRichPresence var localizedName = TranslationHandler.GetTranslation(name, TranslationHandler.TextType.UI); var message = TranslationHandler.GetTranslation("RICH_PRESENCE_EXPLORING", TranslationHandler.TextType.UI).Replace("{0}", localizedName); - API.CreateTrigger(go, sector, message, name.Replace(" ", "").Replace("'", "").ToLowerInvariant()); + API.CreateTrigger(go, sector, message, name.Replace(" ", "").Replace("'", "").Replace("-", "").ToLowerInvariant()); } public static void OnStarSystemLoaded(string name) From 12726c97bb536c6486bd277133f021a4d1e9b3eb Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Tue, 30 Aug 2022 02:44:40 -0400 Subject: [PATCH 031/176] Add star atmosphere and fog to proxy --- NewHorizons/Builder/Body/ProxyBuilder.cs | 20 +++++-- NewHorizons/Builder/Body/StarBuilder.cs | 57 +++++++++++++++---- .../Builder/Body/StellarRemnantBuilder.cs | 4 +- 3 files changed, 62 insertions(+), 19 deletions(-) diff --git a/NewHorizons/Builder/Body/ProxyBuilder.cs b/NewHorizons/Builder/Body/ProxyBuilder.cs index 6c823c20..4e028acf 100644 --- a/NewHorizons/Builder/Body/ProxyBuilder.cs +++ b/NewHorizons/Builder/Body/ProxyBuilder.cs @@ -54,7 +54,7 @@ namespace NewHorizons.Builder.Body remnantGO.transform.parent = proxy.transform; remnantGO.transform.localPosition = Vector3.zero; - SharedMake(planetGO, remnantGO, proxyController, remnant); + SharedMake(planetGO, remnantGO, null, remnant); proxyController.stellarRemnantGO = remnantGO; } @@ -125,9 +125,11 @@ namespace NewHorizons.Builder.Body if (realSize < body.Config.Ring.outerRadius) realSize = body.Config.Ring.outerRadius; } + Renderer starAtmosphere = null; + Renderer starFog = null; if (body.Config.Star != null) { - StarBuilder.MakeStarProxy(planetGO, proxy, body.Config.Star, body.Mod, body.Config.isStellarRemnant); + (_, starAtmosphere, starFog) = StarBuilder.MakeStarProxy(planetGO, proxy, body.Config.Star, body.Mod, body.Config.isStellarRemnant); if (realSize < body.Config.Star.size) realSize = body.Config.Star.size; } @@ -217,9 +219,17 @@ namespace NewHorizons.Builder.Body if (proxyController != null) { - proxyController._atmosphere = atmosphere; - proxyController._fog = fog; - proxyController._fogCurveMaxVal = fogCurveMaxVal; + proxyController._atmosphere = atmosphere ?? starAtmosphere; + if (fog != null) + { + proxyController._fog = fog; + proxyController._fogCurveMaxVal = fogCurveMaxVal; + } + else if (starFog != null) + { + proxyController._fog = starFog; + proxyController._fogCurveMaxVal = 0.05f; + } proxyController.topClouds = topClouds; proxyController.lightningGenerator = lightningGenerator; proxyController.supernovaPlanetEffectController = supernovaPlanetEffect; diff --git a/NewHorizons/Builder/Body/StarBuilder.cs b/NewHorizons/Builder/Body/StarBuilder.cs index e976f800..7e32c3d3 100644 --- a/NewHorizons/Builder/Body/StarBuilder.cs +++ b/NewHorizons/Builder/Body/StarBuilder.cs @@ -46,23 +46,29 @@ namespace NewHorizons.Builder.Body sunAtmosphere.transform.position = planetGO.transform.position; sunAtmosphere.transform.localScale = Vector3.one * OuterRadiusRatio; sunAtmosphere.name = "Atmosphere_Star"; + + var atmospheres = sunAtmosphere.transform.Find("AtmoSphere"); + atmospheres.transform.localScale = Vector3.one; + var lods = atmospheres.GetComponentsInChildren(); + foreach (var lod in lods) + { + lod.material.SetFloat(InnerRadius, starModule.size); + lod.material.SetFloat(OuterRadius, starModule.size * OuterRadiusRatio); + } + var fog = sunAtmosphere.transform.Find("FogSphere").GetComponent(); + fog.transform.localScale = Vector3.one; + fog.fogRadius = starModule.size * OuterRadiusRatio; + fog.lodFadeDistance = fog.fogRadius * (StarBuilder.OuterRadiusRatio - 1f); + + fog.fogImpostor.material.SetFloat(Radius, starModule.size * OuterRadiusRatio); if (starModule.tint != null) { fog.fogTint = starModule.tint.ToColor(); fog.fogImpostor.material.SetColor(Tint, starModule.tint.ToColor()); - sunAtmosphere.transform.Find("AtmoSphere").transform.localScale = Vector3.one; - foreach (var lod in sunAtmosphere.transform.Find("AtmoSphere").GetComponentsInChildren()) - { + foreach (var lod in lods) lod.material.SetColor(SkyColor, starModule.tint.ToColor()); - lod.material.SetFloat(InnerRadius, starModule.size); - lod.material.SetFloat(OuterRadius, starModule.size * OuterRadiusRatio); - } } - fog.transform.localScale = Vector3.one; - fog.fogRadius = starModule.size * OuterRadiusRatio; - fog.lodFadeDistance = fog.fogRadius * (StarBuilder.OuterRadiusRatio - 1f); - fog.fogImpostor.material.SetFloat(Radius, starModule.size * OuterRadiusRatio); } var ambientLightGO = Object.Instantiate(SearchUtilities.Find("Sun_Body/AmbientLight_SUN"), starGO.transform); @@ -179,10 +185,37 @@ namespace NewHorizons.Builder.Body return (starGO, starController, starEvolutionController); } - public static GameObject MakeStarProxy(GameObject planet, GameObject proxyGO, StarModule starModule, IModBehaviour mod, bool isStellarRemnant) + public static (GameObject, Renderer, Renderer) MakeStarProxy(GameObject planet, GameObject proxyGO, StarModule starModule, IModBehaviour mod, bool isStellarRemnant) { var (starGO, controller, supernova) = SharedStarGeneration(proxyGO, null, mod, starModule, isStellarRemnant); + Renderer atmosphere = null; + Renderer fog = null; + if (starModule.hasAtmosphere) + { + GameObject sunAtmosphere = Object.Instantiate(SearchUtilities.Find("SunProxy/Sun_Proxy_Body/Atmosphere_SUN", false) ?? SearchUtilities.Find("SunProxy(Clone)/Sun_Proxy_Body/Atmosphere_SUN"), starGO.transform); + sunAtmosphere.transform.position = proxyGO.transform.position; + sunAtmosphere.transform.localScale = Vector3.one * OuterRadiusRatio; + sunAtmosphere.name = "Atmosphere_Star"; + + atmosphere = sunAtmosphere.transform.Find("Atmosphere_LOD2").GetComponent(); + atmosphere.transform.localScale = Vector3.one; + atmosphere.material.SetFloat(InnerRadius, starModule.size); + atmosphere.material.SetFloat(OuterRadius, starModule.size * OuterRadiusRatio); + + fog = sunAtmosphere.transform.Find("FogSphere").GetComponent(); + fog.transform.localScale = Vector3.one; + fog.material.SetFloat(Radius, starModule.size * OuterRadiusRatio); + + if (starModule.tint != null) + { + fog.material.SetColor(Tint, starModule.tint.ToColor()); + atmosphere.material.SetColor(SkyColor, starModule.tint.ToColor()); + } + + controller.atmosphere = sunAtmosphere; + } + controller.isProxy = true; // Planet can have multiple stars on them, so find the one that is also a remnant / not a remnant @@ -198,7 +231,7 @@ namespace NewHorizons.Builder.Body supernova.mainStellerDeathController = mainController.supernova; } - return starGO; + return (starGO, atmosphere, fog); } private static (GameObject, StarEvolutionController, StellarDeathController) SharedStarGeneration(GameObject planetGO, Sector sector, IModBehaviour mod, StarModule starModule, bool isStellarRemnant) diff --git a/NewHorizons/Builder/Body/StellarRemnantBuilder.cs b/NewHorizons/Builder/Body/StellarRemnantBuilder.cs index 31ea505a..6e06c5dc 100644 --- a/NewHorizons/Builder/Body/StellarRemnantBuilder.cs +++ b/NewHorizons/Builder/Body/StellarRemnantBuilder.cs @@ -88,7 +88,7 @@ namespace NewHorizons.Builder.Body lightRadius = 10000, solarLuminosity = 0.5f }; - if (proxy != null) return StarBuilder.MakeStarProxy(planetGO, proxy, whiteDwarfModule, mod, true); + if (proxy != null) return StarBuilder.MakeStarProxy(planetGO, proxy, whiteDwarfModule, mod, true).Item1; else return StarBuilder.Make(planetGO, sector, whiteDwarfModule, mod, true).Item1; } @@ -107,7 +107,7 @@ namespace NewHorizons.Builder.Body // Instead of showing the typical star surface we use a tinted singularity GameObject neutronStar; - if (proxy != null) neutronStar = StarBuilder.MakeStarProxy(planetGO, proxy, neutronStarModule, mod, true); + if (proxy != null) neutronStar = StarBuilder.MakeStarProxy(planetGO, proxy, neutronStarModule, mod, true).Item1; else (neutronStar, _, _) = StarBuilder.Make(planetGO, sector, neutronStarModule, mod, true); neutronStar.FindChild("Surface").SetActive(false); From 3ecd8e745a7255337c05611f25c5a74701daff55 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Tue, 30 Aug 2022 03:14:08 -0400 Subject: [PATCH 032/176] Loop de Loop --- NewHorizons/Builder/Props/AudioVolumeBuilder.cs | 2 +- NewHorizons/External/Modules/PropModule.cs | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/NewHorizons/Builder/Props/AudioVolumeBuilder.cs b/NewHorizons/Builder/Props/AudioVolumeBuilder.cs index 8e45efc8..9c5d0acc 100644 --- a/NewHorizons/Builder/Props/AudioVolumeBuilder.cs +++ b/NewHorizons/Builder/Props/AudioVolumeBuilder.cs @@ -26,7 +26,7 @@ namespace NewHorizons.Builder.Props var owAudioSource = go.AddComponent(); owAudioSource._audioSource = audioSource; - owAudioSource.loop = true; + owAudioSource.loop = info.loop; owAudioSource.SetTrack((OWAudioMixer.TrackName)Enum.Parse(typeof(OWAudioMixer.TrackName), Enum.GetName(typeof(AudioMixerTrackName), info.track))); AudioUtilities.SetAudioClip(owAudioSource, info.audio, mod); diff --git a/NewHorizons/External/Modules/PropModule.cs b/NewHorizons/External/Modules/PropModule.cs index 6ed70204..163040a0 100644 --- a/NewHorizons/External/Modules/PropModule.cs +++ b/NewHorizons/External/Modules/PropModule.cs @@ -840,6 +840,11 @@ namespace NewHorizons.External.Modules /// The audio track of this audio volume /// [DefaultValue("environment")] public AudioMixerTrackName track = AudioMixerTrackName.Environment; + + /// + /// Whether to loop this audio while in this audio volume + /// + [DefaultValue(true)] public bool loop = true; } [JsonObject] From e64ad0f9efcf8bea31c0d0c0fb2485865e4fa6ed Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 30 Aug 2022 07:17:30 +0000 Subject: [PATCH 033/176] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index bc88b9f7..5f638234 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -1822,6 +1822,11 @@ "description": "The audio track of this audio volume", "default": "environment", "$ref": "#/definitions/AudioMixerTrackName" + }, + "loop": { + "type": "boolean", + "description": "Whether to loop this audio while in this audio volume", + "default": true } } }, From 11b3381ac291ded01dfc55f403d842f1fab43309 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Tue, 30 Aug 2022 03:13:45 -0400 Subject: [PATCH 034/176] Stop dev tools from deleting extras --- NewHorizons/External/Configs/PlanetConfig.cs | 5 +++++ NewHorizons/External/Configs/StarSystemConfig.cs | 5 +++++ SchemaExporter/SchemaExporter.cs | 4 ++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/NewHorizons/External/Configs/PlanetConfig.cs b/NewHorizons/External/Configs/PlanetConfig.cs index 4a353011..2b071244 100644 --- a/NewHorizons/External/Configs/PlanetConfig.cs +++ b/NewHorizons/External/Configs/PlanetConfig.cs @@ -169,6 +169,11 @@ namespace NewHorizons.External.Configs /// public WaterModule Water; + /// + /// Extra data that may be used by extension mods + /// + public object extras; + public PlanetConfig() { // Always have to have a base module diff --git a/NewHorizons/External/Configs/StarSystemConfig.cs b/NewHorizons/External/Configs/StarSystemConfig.cs index 2df0dc8c..ce311174 100644 --- a/NewHorizons/External/Configs/StarSystemConfig.cs +++ b/NewHorizons/External/Configs/StarSystemConfig.cs @@ -102,6 +102,11 @@ namespace NewHorizons.External.Configs /// public CuriosityColorInfo[] curiosities; + /// + /// Extra data that may be used by extension mods + /// + public object extras; + public class NomaiCoordinates { [MinLength(2)] diff --git a/SchemaExporter/SchemaExporter.cs b/SchemaExporter/SchemaExporter.cs index 33c12666..c2e94e25 100644 --- a/SchemaExporter/SchemaExporter.cs +++ b/SchemaExporter/SchemaExporter.cs @@ -92,7 +92,7 @@ public static class SchemaExporter if (_title is "Star System Schema" or "Celestial Body Schema") { - schema.Properties.Add("extras", new JsonSchemaProperty { + schema.Properties["extras"] = new JsonSchemaProperty { Type = JsonObjectType.Object, Description = "Extra data that may be used by extension mods", AllowAdditionalProperties = true, @@ -100,7 +100,7 @@ public static class SchemaExporter { Type = JsonObjectType.Object } - }); + }; } return schema; From 5f2beb7aee7e5c949562717d7a3783f1e29175b7 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Tue, 30 Aug 2022 03:21:03 -0400 Subject: [PATCH 035/176] or just play it once --- NewHorizons/External/Modules/PropModule.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/External/Modules/PropModule.cs b/NewHorizons/External/Modules/PropModule.cs index 163040a0..f4275a6e 100644 --- a/NewHorizons/External/Modules/PropModule.cs +++ b/NewHorizons/External/Modules/PropModule.cs @@ -842,7 +842,7 @@ namespace NewHorizons.External.Modules [DefaultValue("environment")] public AudioMixerTrackName track = AudioMixerTrackName.Environment; /// - /// Whether to loop this audio while in this audio volume + /// Whether to loop this audio while in this audio volume or just play it once /// [DefaultValue(true)] public bool loop = true; } From 9709601048decd8c64acb56d17873458b8fe97b8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 30 Aug 2022 07:22:48 +0000 Subject: [PATCH 036/176] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 8 ++++---- NewHorizons/Schemas/star_system_schema.json | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index 02771c9b..3783d7e7 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -128,16 +128,16 @@ "description": "Add water to this planet", "$ref": "#/definitions/WaterModule" }, - "$schema": { - "type": "string", - "description": "The schema to validate with" - }, "extras": { "type": "object", "description": "Extra data that may be used by extension mods", "additionalProperties": { "type": "object" } + }, + "$schema": { + "type": "string", + "description": "The schema to validate with" } }, "definitions": { diff --git a/NewHorizons/Schemas/star_system_schema.json b/NewHorizons/Schemas/star_system_schema.json index d8073db0..032fb8f1 100644 --- a/NewHorizons/Schemas/star_system_schema.json +++ b/NewHorizons/Schemas/star_system_schema.json @@ -71,16 +71,16 @@ "$ref": "#/definitions/CuriosityColorInfo" } }, - "$schema": { - "type": "string", - "description": "The schema to validate with" - }, "extras": { "type": "object", "description": "Extra data that may be used by extension mods", "additionalProperties": { "type": "object" } + }, + "$schema": { + "type": "string", + "description": "The schema to validate with" } }, "definitions": { From 4f15d21592c3d51a771ec332294a7f376a927dc9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 30 Aug 2022 07:23:24 +0000 Subject: [PATCH 037/176] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index 5f638234..9b3db160 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -1825,7 +1825,7 @@ }, "loop": { "type": "boolean", - "description": "Whether to loop this audio while in this audio volume", + "description": "Whether to loop this audio while in this audio volume or just play it once", "default": true } } From 890b496e7ab5f88a8dd0695a4a085b0fc96c729a Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Wed, 31 Aug 2022 03:09:13 -0400 Subject: [PATCH 038/176] Add notification volumes --- .../Props/NotificationVolumeBuilder.cs | 43 ++++++ NewHorizons/Builder/Props/PropBuildManager.cs | 7 + NewHorizons/Components/NotificationVolume.cs | 129 ++++++++++++++++++ NewHorizons/External/Modules/PropModule.cs | 57 ++++++++ NewHorizons/Schemas/body_schema.json | 65 +++++++++ 5 files changed, 301 insertions(+) create mode 100644 NewHorizons/Builder/Props/NotificationVolumeBuilder.cs create mode 100644 NewHorizons/Components/NotificationVolume.cs diff --git a/NewHorizons/Builder/Props/NotificationVolumeBuilder.cs b/NewHorizons/Builder/Props/NotificationVolumeBuilder.cs new file mode 100644 index 00000000..eabb1519 --- /dev/null +++ b/NewHorizons/Builder/Props/NotificationVolumeBuilder.cs @@ -0,0 +1,43 @@ +using NewHorizons.Components; +using NewHorizons.External.Modules; +using NewHorizons.Utility; +using OWML.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using Logger = NewHorizons.Utility.Logger; +using NHNotificationVolume = NewHorizons.Components.NotificationVolume; + +namespace NewHorizons.Builder.Props +{ + public static class NotificationVolumeBuilder + { + public static NHNotificationVolume Make(GameObject planetGO, Sector sector, PropModule.NotificationVolumeInfo info, IModBehaviour mod) + { + var go = new GameObject("NotificationVolume"); + go.SetActive(false); + + go.transform.parent = sector?.transform ?? planetGO.transform; + go.transform.position = planetGO.transform.TransformPoint(info.position != null ? (Vector3)info.position : Vector3.zero); + go.layer = LayerMask.NameToLayer("BasicEffectVolume"); + + var shape = go.AddComponent(); + shape.radius = info.radius; + + var owTriggerVolume = go.AddComponent(); + owTriggerVolume._shape = shape; + + var notificationVolume = go.AddComponent(); + notificationVolume.SetTarget(info.target); + if (info.entryNotification != null) notificationVolume.SetEntryNotification(info.entryNotification.displayMessage, info.entryNotification.duration); + if (info.exitNotification != null) notificationVolume.SetExitNotification(info.exitNotification.displayMessage, info.exitNotification.duration); + + go.SetActive(true); + + return notificationVolume; + } + } +} diff --git a/NewHorizons/Builder/Props/PropBuildManager.cs b/NewHorizons/Builder/Props/PropBuildManager.cs index 8f442041..1c5afe4b 100644 --- a/NewHorizons/Builder/Props/PropBuildManager.cs +++ b/NewHorizons/Builder/Props/PropBuildManager.cs @@ -235,6 +235,13 @@ namespace NewHorizons.Builder.Props } } } + if (config.Props.notificationVolumes != null) + { + foreach (var notificationVolume in config.Props.notificationVolumes) + { + NotificationVolumeBuilder.Make(go, sector, notificationVolume, mod); + } + } } } } diff --git a/NewHorizons/Components/NotificationVolume.cs b/NewHorizons/Components/NotificationVolume.cs new file mode 100644 index 00000000..99a35705 --- /dev/null +++ b/NewHorizons/Components/NotificationVolume.cs @@ -0,0 +1,129 @@ +using NewHorizons.Handlers; +using OWML.Utils; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace NewHorizons.Components +{ + [RequireComponent(typeof(OWTriggerVolume))] + public class NotificationVolume : MonoBehaviour + { + private NotificationTarget _target = NotificationTarget.All; + private bool _pin = false; + private OWTriggerVolume _triggerVolume; + private NotificationData _entryNotification; + private NotificationData _exitNotification; + + public void Awake() + { + _triggerVolume = this.GetRequiredComponent(); + _triggerVolume.OnEntry += OnTriggerVolumeEntry; + _triggerVolume.OnExit += OnTriggerVolumeExit; + } + + public void OnDestroy() + { + if (_triggerVolume == null) return; + _triggerVolume.OnEntry -= OnTriggerVolumeEntry; + _triggerVolume.OnExit -= OnTriggerVolumeExit; + } + + public void SetPinned(bool pin) => _pin = pin; + + public void SetTarget(NewHorizons.External.Modules.PropModule.NotificationVolumeInfo.NotificationTarget target) => SetTarget(EnumUtils.Parse(target.ToString(), NotificationTarget.All)); + + public void SetTarget(NotificationTarget target) => _target = target; + + public void SetEntryNotification(string displayMessage, float duration = 5) + { + _entryNotification = new NotificationData(_target, TranslationHandler.GetTranslation(displayMessage, TranslationHandler.TextType.UI), duration); + } + + public void SetExitNotification(string displayMessage, float duration = 5) + { + _exitNotification = new NotificationData(_target, TranslationHandler.GetTranslation(displayMessage, TranslationHandler.TextType.UI), duration); + } + + public void OnTriggerVolumeEntry(GameObject hitObj) + { + if (_target == NotificationTarget.All) + { + if (hitObj.CompareTag("PlayerDetector") || hitObj.CompareTag("ShipDetector")) + { + PostEntryNotification(); + } + } + else if (_target == NotificationTarget.Player) + { + if (hitObj.CompareTag("PlayerDetector")) + { + PostEntryNotification(); + } + } + else if (_target == NotificationTarget.Ship) + { + if (hitObj.CompareTag("ShipDetector")) + { + PostEntryNotification(); + } + } + } + + public void OnTriggerVolumeExit(GameObject hitObj) + { + if (_target == NotificationTarget.All) + { + if (hitObj.CompareTag("PlayerDetector") || hitObj.CompareTag("ShipDetector")) + { + PostExitNotification(); + } + } + else if (_target == NotificationTarget.Player) + { + if (hitObj.CompareTag("PlayerDetector")) + { + PostExitNotification(); + } + } + else if (_target == NotificationTarget.Ship) + { + if (hitObj.CompareTag("ShipDetector")) + { + PostExitNotification(); + } + } + } + + public void PostEntryNotification() + { + if (_entryNotification == null) return; + NotificationManager.SharedInstance.PostNotification(_entryNotification, _pin); + } + + public void PostExitNotification() + { + if (_exitNotification == null) return; + NotificationManager.SharedInstance.PostNotification(_exitNotification, _pin); + } + + public void UnpinEntryNotification() + { + if (_entryNotification == null) return; + if (NotificationManager.SharedInstance.IsPinnedNotification(_entryNotification)) + { + NotificationManager.SharedInstance.UnpinNotification(_entryNotification); + } + } + + public void UnpinExitNotification() + { + if (_exitNotification == null) return; + if (NotificationManager.SharedInstance.IsPinnedNotification(_exitNotification)) + { + NotificationManager.SharedInstance.UnpinNotification(_exitNotification); + } + } + } +} diff --git a/NewHorizons/External/Modules/PropModule.cs b/NewHorizons/External/Modules/PropModule.cs index f4275a6e..9471df4c 100644 --- a/NewHorizons/External/Modules/PropModule.cs +++ b/NewHorizons/External/Modules/PropModule.cs @@ -98,6 +98,11 @@ namespace NewHorizons.External.Modules /// public RemoteInfo[] remotes; + /// + /// Add notification volumes to this planet + /// + public NotificationVolumeInfo[] notificationVolumes; + [JsonObject] public class ScatterInfo { @@ -1006,6 +1011,58 @@ namespace NewHorizons.External.Modules public string rename; } } + + [JsonObject] + public class NotificationVolumeInfo + { + /// + /// What the notification will show for. + /// + [DefaultValue("all")] public NotificationTarget target = NotificationTarget.All; + + /// + /// The location of this notification volume. Optional (will default to 0,0,0). + /// + public MVector3 position; + + /// + /// The radius of this notification volume. + /// + public float radius; + + /// + /// The notification that will play when you enter this volume. + /// + public NotificationInfo entryNotification; + + /// + /// The notification that will play when you exit this volume. + /// + public NotificationInfo exitNotification; + + + [JsonObject] + public class NotificationInfo + { + /// + /// The message that will be displayed. + /// + public string displayMessage; + + /// + /// The duration this notification will be displayed. + /// + [DefaultValue(5f)] public float duration = 5f; + } + + [JsonConverter(typeof(StringEnumConverter))] + public enum NotificationTarget + { + [EnumMember(Value = @"all")] All = 0, + [EnumMember(Value = @"ship")] Ship = 1, + [EnumMember(Value = @"player")] Player = 2, + } + } } [JsonConverter(typeof(StringEnumConverter))] diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index 4e56aedb..9b9bbb0d 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -1001,6 +1001,13 @@ "items": { "$ref": "#/definitions/RemoteInfo" } + }, + "notificationVolumes": { + "type": "array", + "description": "Add notification volumes to this planet", + "items": { + "$ref": "#/definitions/NotificationVolumeInfo" + } } } }, @@ -2090,6 +2097,64 @@ } } }, + "NotificationVolumeInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "target": { + "description": "What the notification will show for.", + "default": "all", + "$ref": "#/definitions/NotificationTarget" + }, + "position": { + "description": "The location of this notification volume. Optional (will default to 0,0,0).", + "$ref": "#/definitions/MVector3" + }, + "radius": { + "type": "number", + "description": "The radius of this notification volume.", + "format": "float" + }, + "entryNotification": { + "description": "The notification that will play when you enter this volume.", + "$ref": "#/definitions/NotificationInfo" + }, + "exitNotification": { + "description": "The notification that will play when you exit this volume.", + "$ref": "#/definitions/NotificationInfo" + } + } + }, + "NotificationTarget": { + "type": "string", + "description": "", + "x-enumNames": [ + "All", + "Ship", + "Player" + ], + "enum": [ + "all", + "ship", + "player" + ] + }, + "NotificationInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "displayMessage": { + "type": "string", + "description": "The message that will be displayed." + }, + "duration": { + "type": "number", + "description": "The duration this notification will be displayed.", + "format": "float", + "default": 5.0 + } + } + }, "ReferenceFrameModule": { "type": "object", "additionalProperties": false, From 94a304ffb54abf015e134f150c8af11bb661d01d Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Wed, 31 Aug 2022 04:43:25 -0400 Subject: [PATCH 039/176] Allow there to be no shock layer for atmospheres. --- NewHorizons/Builder/Atmosphere/AirBuilder.cs | 19 +++++++++++-------- .../External/Modules/AtmosphereModule.cs | 5 +++++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/NewHorizons/Builder/Atmosphere/AirBuilder.cs b/NewHorizons/Builder/Atmosphere/AirBuilder.cs index 7b5ab84d..7b961d14 100644 --- a/NewHorizons/Builder/Atmosphere/AirBuilder.cs +++ b/NewHorizons/Builder/Atmosphere/AirBuilder.cs @@ -23,15 +23,18 @@ namespace NewHorizons.Builder.Atmosphere sfv._allowShipAutoroll = true; sfv._disableOnStart = false; - // Try to parent it to the same as other rulesets to match vanilla but if its null put it on the root object - var ruleSetGO = planetGO.GetComponentInChildren()?.gameObject; - if (ruleSetGO == null) ruleSetGO = planetGO; + if (config.Atmosphere.hasShockLayer) + { + // Try to parent it to the same as other rulesets to match vanilla but if its null put it on the root object + var ruleSetGO = planetGO.GetComponentInChildren()?.gameObject; + if (ruleSetGO == null) ruleSetGO = planetGO; - var shockLayerRuleset = ruleSetGO.AddComponent(); - shockLayerRuleset._type = ShockLayerRuleset.ShockType.Atmospheric; - shockLayerRuleset._radialCenter = airGO.transform; - shockLayerRuleset._minShockSpeed = config.Atmosphere.minShockSpeed; - shockLayerRuleset._maxShockSpeed = config.Atmosphere.maxShockSpeed; + var shockLayerRuleset = ruleSetGO.AddComponent(); + shockLayerRuleset._type = ShockLayerRuleset.ShockType.Atmospheric; + shockLayerRuleset._radialCenter = airGO.transform; + shockLayerRuleset._minShockSpeed = config.Atmosphere.minShockSpeed; + shockLayerRuleset._maxShockSpeed = config.Atmosphere.maxShockSpeed; + } if (config.Atmosphere.clouds != null) { diff --git a/NewHorizons/External/Modules/AtmosphereModule.cs b/NewHorizons/External/Modules/AtmosphereModule.cs index f03f3ceb..d2b835ee 100644 --- a/NewHorizons/External/Modules/AtmosphereModule.cs +++ b/NewHorizons/External/Modules/AtmosphereModule.cs @@ -100,6 +100,11 @@ namespace NewHorizons.External.Modules /// public bool useAtmosphereShader; + /// + /// Whether this atmosphere will have flames appear when your ship goes a certain speed. + /// + [DefaultValue(true)] public bool hasShockLayer = true; + /// /// Minimum speed that your ship can go in the atmosphere where flames will appear. /// From 66a08c7468a2b81fccd9dcf4d66fca4cd94a26f7 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Wed, 31 Aug 2022 09:51:38 -0400 Subject: [PATCH 040/176] Forgot to move this --- NewHorizons/Builder/Atmosphere/AirBuilder.cs | 24 ++++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/NewHorizons/Builder/Atmosphere/AirBuilder.cs b/NewHorizons/Builder/Atmosphere/AirBuilder.cs index 7b961d14..209a2b26 100644 --- a/NewHorizons/Builder/Atmosphere/AirBuilder.cs +++ b/NewHorizons/Builder/Atmosphere/AirBuilder.cs @@ -34,20 +34,20 @@ namespace NewHorizons.Builder.Atmosphere shockLayerRuleset._radialCenter = airGO.transform; shockLayerRuleset._minShockSpeed = config.Atmosphere.minShockSpeed; shockLayerRuleset._maxShockSpeed = config.Atmosphere.maxShockSpeed; - } - if (config.Atmosphere.clouds != null) - { - shockLayerRuleset._innerRadius = config.Atmosphere.clouds.innerCloudRadius; - shockLayerRuleset._outerRadius = config.Atmosphere.clouds.outerCloudRadius; - } - else - { - var bottom = config.Base.surfaceSize; - var top = config.Atmosphere.size; + if (config.Atmosphere.clouds != null) + { + shockLayerRuleset._innerRadius = config.Atmosphere.clouds.innerCloudRadius; + shockLayerRuleset._outerRadius = config.Atmosphere.clouds.outerCloudRadius; + } + else + { + var bottom = config.Base.surfaceSize; + var top = config.Atmosphere.size; - shockLayerRuleset._innerRadius = (bottom + top) / 2f; - shockLayerRuleset._outerRadius = top; + shockLayerRuleset._innerRadius = (bottom + top) / 2f; + shockLayerRuleset._outerRadius = top; + } } if (config.Atmosphere.hasOxygen) From ef5e08445511d8fa5f298b02cfaf16393733725d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 31 Aug 2022 13:54:20 +0000 Subject: [PATCH 041/176] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index 4e56aedb..c0be4ee4 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -309,6 +309,11 @@ "type": "boolean", "description": "Whether we use an atmospheric shader on the planet. Doesn't affect clouds, fog, rain, snow, oxygen, etc. Purely\nvisual." }, + "hasShockLayer": { + "type": "boolean", + "description": "Whether this atmosphere will have flames appear when your ship goes a certain speed.", + "default": true + }, "minShockSpeed": { "type": "number", "description": "Minimum speed that your ship can go in the atmosphere where flames will appear.", From 4ae90dcc25ddf5161e113717ef74653adbaebe9b Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Wed, 31 Aug 2022 13:10:20 -0400 Subject: [PATCH 042/176] Reorganize to a volume module --- NewHorizons/Builder/Props/PropBuildManager.cs | 28 --- NewHorizons/Builder/ShipLog/RevealBuilder.cs | 18 +- .../{Props => Volumes}/AudioVolumeBuilder.cs | 4 +- .../NotificationVolumeBuilder.cs | 4 +- .../Builder/Volumes/VolumesBuildManager.cs | 47 +++++ NewHorizons/Components/NotificationVolume.cs | 2 +- NewHorizons/External/Configs/PlanetConfig.cs | 19 ++ NewHorizons/External/Modules/PropModule.cs | 168 +--------------- NewHorizons/External/Modules/VolumesModule.cs | 183 ++++++++++++++++++ NewHorizons/Handlers/PlanetCreationHandler.cs | 6 + 10 files changed, 272 insertions(+), 207 deletions(-) rename NewHorizons/Builder/{Props => Volumes}/AudioVolumeBuilder.cs (94%) rename NewHorizons/Builder/{Props => Volumes}/NotificationVolumeBuilder.cs (93%) create mode 100644 NewHorizons/Builder/Volumes/VolumesBuildManager.cs create mode 100644 NewHorizons/External/Modules/VolumesModule.cs diff --git a/NewHorizons/Builder/Props/PropBuildManager.cs b/NewHorizons/Builder/Props/PropBuildManager.cs index 1c5afe4b..bda9684d 100644 --- a/NewHorizons/Builder/Props/PropBuildManager.cs +++ b/NewHorizons/Builder/Props/PropBuildManager.cs @@ -109,20 +109,6 @@ namespace NewHorizons.Builder.Props } } } - if (config.Props.reveal != null) - { - foreach (var revealInfo in config.Props.reveal) - { - try - { - RevealBuilder.Make(go, sector, revealInfo, mod); - } - catch (Exception ex) - { - Logger.LogError($"Couldn't make reveal location [{revealInfo.reveals}] for [{go.name}]:\n{ex}"); - } - } - } if (config.Props.entryLocation != null) { foreach (var entryLocationInfo in config.Props.entryLocation) @@ -207,13 +193,6 @@ namespace NewHorizons.Builder.Props } } } - if (config.Props.audioVolumes != null) - { - foreach (var audioVolume in config.Props.audioVolumes) - { - AudioVolumeBuilder.Make(go, sector, audioVolume, mod); - } - } if (config.Props.signals != null) { foreach (var signal in config.Props.signals) @@ -235,13 +214,6 @@ namespace NewHorizons.Builder.Props } } } - if (config.Props.notificationVolumes != null) - { - foreach (var notificationVolume in config.Props.notificationVolumes) - { - NotificationVolumeBuilder.Make(go, sector, notificationVolume, mod); - } - } } } } diff --git a/NewHorizons/Builder/ShipLog/RevealBuilder.cs b/NewHorizons/Builder/ShipLog/RevealBuilder.cs index 43133771..95f118b6 100644 --- a/NewHorizons/Builder/ShipLog/RevealBuilder.cs +++ b/NewHorizons/Builder/ShipLog/RevealBuilder.cs @@ -7,18 +7,18 @@ namespace NewHorizons.Builder.ShipLog { public static class RevealBuilder { - public static void Make(GameObject go, Sector sector, PropModule.RevealInfo info, IModBehaviour mod) + public static void Make(GameObject go, Sector sector, VolumesModule.RevealVolumeInfo info, IModBehaviour mod) { var newRevealGO = MakeGameObject(go, sector, info, mod); switch (info.revealOn) { - case PropModule.RevealInfo.RevealVolumeType.Enter: + case VolumesModule.RevealVolumeInfo.RevealVolumeType.Enter: MakeTrigger(newRevealGO, sector, info, mod); break; - case PropModule.RevealInfo.RevealVolumeType.Observe: + case VolumesModule.RevealVolumeInfo.RevealVolumeType.Observe: MakeObservable(newRevealGO, sector, info, mod); break; - case PropModule.RevealInfo.RevealVolumeType.Snapshot: + case VolumesModule.RevealVolumeInfo.RevealVolumeType.Snapshot: MakeSnapshot(newRevealGO, sector, info, mod); break; default: @@ -28,7 +28,7 @@ namespace NewHorizons.Builder.ShipLog newRevealGO.SetActive(true); } - private static SphereShape MakeShape(GameObject go, PropModule.RevealInfo info, Shape.CollisionMode collisionMode) + private static SphereShape MakeShape(GameObject go, VolumesModule.RevealVolumeInfo info, Shape.CollisionMode collisionMode) { SphereShape newShape = go.AddComponent(); newShape.radius = info.radius; @@ -36,7 +36,7 @@ namespace NewHorizons.Builder.ShipLog return newShape; } - private static GameObject MakeGameObject(GameObject planetGO, Sector sector, PropModule.RevealInfo info, IModBehaviour mod) + private static GameObject MakeGameObject(GameObject planetGO, Sector sector, VolumesModule.RevealVolumeInfo info, IModBehaviour mod) { GameObject revealTriggerVolume = new GameObject("Reveal Volume (" + info.revealOn + ")"); revealTriggerVolume.SetActive(false); @@ -45,7 +45,7 @@ namespace NewHorizons.Builder.ShipLog return revealTriggerVolume; } - private static void MakeTrigger(GameObject go, Sector sector, PropModule.RevealInfo info, IModBehaviour mod) + private static void MakeTrigger(GameObject go, Sector sector, VolumesModule.RevealVolumeInfo info, IModBehaviour mod) { var shape = MakeShape(go, info, Shape.CollisionMode.Volume); @@ -65,7 +65,7 @@ namespace NewHorizons.Builder.ShipLog } } - private static void MakeObservable(GameObject go, Sector sector, PropModule.RevealInfo info, IModBehaviour mod) + private static void MakeObservable(GameObject go, Sector sector, VolumesModule.RevealVolumeInfo info, IModBehaviour mod) { go.layer = LayerMask.NameToLayer("Interactible"); @@ -96,7 +96,7 @@ namespace NewHorizons.Builder.ShipLog } } - private static void MakeSnapshot(GameObject go, Sector sector, PropModule.RevealInfo info, IModBehaviour mod) + private static void MakeSnapshot(GameObject go, Sector sector, VolumesModule.RevealVolumeInfo info, IModBehaviour mod) { var shape = MakeShape(go, info, Shape.CollisionMode.Manual); diff --git a/NewHorizons/Builder/Props/AudioVolumeBuilder.cs b/NewHorizons/Builder/Volumes/AudioVolumeBuilder.cs similarity index 94% rename from NewHorizons/Builder/Props/AudioVolumeBuilder.cs rename to NewHorizons/Builder/Volumes/AudioVolumeBuilder.cs index 9c5d0acc..b487670a 100644 --- a/NewHorizons/Builder/Props/AudioVolumeBuilder.cs +++ b/NewHorizons/Builder/Volumes/AudioVolumeBuilder.cs @@ -9,11 +9,11 @@ using System.Threading.Tasks; using UnityEngine; using Logger = NewHorizons.Utility.Logger; -namespace NewHorizons.Builder.Props +namespace NewHorizons.Builder.Volumes { public static class AudioVolumeBuilder { - public static AudioVolume Make(GameObject planetGO, Sector sector, PropModule.AudioVolumeInfo info, IModBehaviour mod) + public static AudioVolume Make(GameObject planetGO, Sector sector, VolumesModule.AudioVolumeInfo info, IModBehaviour mod) { var go = new GameObject("AudioVolume"); go.SetActive(false); diff --git a/NewHorizons/Builder/Props/NotificationVolumeBuilder.cs b/NewHorizons/Builder/Volumes/NotificationVolumeBuilder.cs similarity index 93% rename from NewHorizons/Builder/Props/NotificationVolumeBuilder.cs rename to NewHorizons/Builder/Volumes/NotificationVolumeBuilder.cs index eabb1519..c91e4161 100644 --- a/NewHorizons/Builder/Props/NotificationVolumeBuilder.cs +++ b/NewHorizons/Builder/Volumes/NotificationVolumeBuilder.cs @@ -11,11 +11,11 @@ using UnityEngine; using Logger = NewHorizons.Utility.Logger; using NHNotificationVolume = NewHorizons.Components.NotificationVolume; -namespace NewHorizons.Builder.Props +namespace NewHorizons.Builder.Volumes { public static class NotificationVolumeBuilder { - public static NHNotificationVolume Make(GameObject planetGO, Sector sector, PropModule.NotificationVolumeInfo info, IModBehaviour mod) + public static NHNotificationVolume Make(GameObject planetGO, Sector sector, VolumesModule.NotificationVolumeInfo info, IModBehaviour mod) { var go = new GameObject("NotificationVolume"); go.SetActive(false); diff --git a/NewHorizons/Builder/Volumes/VolumesBuildManager.cs b/NewHorizons/Builder/Volumes/VolumesBuildManager.cs new file mode 100644 index 00000000..5197aaf4 --- /dev/null +++ b/NewHorizons/Builder/Volumes/VolumesBuildManager.cs @@ -0,0 +1,47 @@ +using NewHorizons.Builder.Body; +using NewHorizons.Builder.ShipLog; +using NewHorizons.Builder.Volumes; +using NewHorizons.External.Configs; +using OWML.Common; +using System; +using System.Collections.Generic; +using UnityEngine; +using Logger = NewHorizons.Utility.Logger; + +namespace NewHorizons.Builder.Volumes +{ + public static class VolumesBuildManager + { + public static void Make(GameObject go, Sector sector, PlanetConfig config, IModBehaviour mod) + { + if (config.Volumes.revealVolumes != null) + { + foreach (var revealInfo in config.Volumes.revealVolumes) + { + try + { + RevealBuilder.Make(go, sector, revealInfo, mod); + } + catch (Exception ex) + { + Logger.LogError($"Couldn't make reveal location [{revealInfo.reveals}] for [{go.name}]:\n{ex}"); + } + } + } + if (config.Volumes.audioVolumes != null) + { + foreach (var audioVolume in config.Volumes.audioVolumes) + { + AudioVolumeBuilder.Make(go, sector, audioVolume, mod); + } + } + if (config.Volumes.notificationVolumes != null) + { + foreach (var notificationVolume in config.Volumes.notificationVolumes) + { + NotificationVolumeBuilder.Make(go, sector, notificationVolume, mod); + } + } + } + } +} diff --git a/NewHorizons/Components/NotificationVolume.cs b/NewHorizons/Components/NotificationVolume.cs index 99a35705..2dc81ed1 100644 --- a/NewHorizons/Components/NotificationVolume.cs +++ b/NewHorizons/Components/NotificationVolume.cs @@ -32,7 +32,7 @@ namespace NewHorizons.Components public void SetPinned(bool pin) => _pin = pin; - public void SetTarget(NewHorizons.External.Modules.PropModule.NotificationVolumeInfo.NotificationTarget target) => SetTarget(EnumUtils.Parse(target.ToString(), NotificationTarget.All)); + public void SetTarget(External.Modules.VolumesModule.NotificationVolumeInfo.NotificationTarget target) => SetTarget(EnumUtils.Parse(target.ToString(), NotificationTarget.All)); public void SetTarget(NotificationTarget target) => _target = target; diff --git a/NewHorizons/External/Configs/PlanetConfig.cs b/NewHorizons/External/Configs/PlanetConfig.cs index 2b071244..4ea8a886 100644 --- a/NewHorizons/External/Configs/PlanetConfig.cs +++ b/NewHorizons/External/Configs/PlanetConfig.cs @@ -169,6 +169,11 @@ namespace NewHorizons.External.Configs /// public WaterModule Water; + /// + /// Add various volumes on this body + /// + public VolumesModule Volumes; + /// /// Extra data that may be used by extension mods /// @@ -317,6 +322,20 @@ namespace NewHorizons.External.Configs if (tornado.downwards) tornado.type = PropModule.TornadoInfo.TornadoType.Downwards; + if (Props?.audioVolumes != null) + { + if (Volumes == null) Volumes = new VolumesModule(); + if (Volumes.audioVolumes == null) Volumes.audioVolumes = new VolumesModule.AudioVolumeInfo[0]; + Volumes.audioVolumes = Volumes.audioVolumes.Concat(Props.audioVolumes).ToArray(); + } + + if (Props?.reveal != null) + { + if (Volumes == null) Volumes = new VolumesModule(); + if (Volumes.revealVolumes == null) Volumes.revealVolumes = new VolumesModule.RevealVolumeInfo[0]; + Volumes.revealVolumes = Volumes.revealVolumes.Concat(Props.reveal).ToArray(); + } + if (Base.sphereOfInfluence != 0f) Base.soiOverride = Base.sphereOfInfluence; // Moved a bunch of stuff off of shiplog module to star system module because it didnt exist when we made this diff --git a/NewHorizons/External/Modules/PropModule.cs b/NewHorizons/External/Modules/PropModule.cs index 9471df4c..932b1bc0 100644 --- a/NewHorizons/External/Modules/PropModule.cs +++ b/NewHorizons/External/Modules/PropModule.cs @@ -48,11 +48,6 @@ namespace NewHorizons.External.Modules /// public RaftInfo[] rafts; - /// - /// Add triggers that reveal parts of the ship log on this planet - /// - public RevealInfo[] reveal; - /// /// Scatter props around this planet's surface /// @@ -83,11 +78,6 @@ namespace NewHorizons.External.Modules /// public SingularityModule[] singularities; - /// - /// Add audio volumes to this planet - /// - public AudioVolumeInfo[] audioVolumes; - /// /// Add signalscope signals to this planet /// @@ -98,10 +88,9 @@ namespace NewHorizons.External.Modules /// public RemoteInfo[] remotes; - /// - /// Add notification volumes to this planet - /// - public NotificationVolumeInfo[] notificationVolumes; + [Obsolete("reveal is deprecated. Use Volumes->revealVolumes instead.")] public VolumesModule.RevealVolumeInfo[] reveal; + + [Obsolete("audioVolumes is deprecated. Use Volumes->audioVolumes instead.")] public VolumesModule.AudioVolumeInfo[] audioVolumes; [JsonObject] public class ScatterInfo @@ -438,55 +427,6 @@ namespace NewHorizons.External.Modules public string xmlFile; } - [JsonObject] - public class RevealInfo - { - [JsonConverter(typeof(StringEnumConverter))] - public enum RevealVolumeType - { - [EnumMember(Value = @"enter")] Enter = 0, - - [EnumMember(Value = @"observe")] Observe = 1, - - [EnumMember(Value = @"snapshot")] Snapshot = 2 - } - - /// - /// The max view angle (in degrees) the player can see the volume with to unlock the fact (`observe` only) - /// - public float maxAngle = 180f; // Observe Only - - /// - /// The max distance the user can be away from the volume to reveal the fact (`snapshot` and `observe` only) - /// - public float maxDistance = -1f; // Snapshot & Observe Only - - /// - /// The position to place this volume at - /// - public MVector3 position; - - /// - /// The radius of this reveal volume - /// - public float radius = 1f; - - /// - /// What needs to be done to the volume to unlock the facts - /// - [DefaultValue("enter")] public RevealVolumeType revealOn = RevealVolumeType.Enter; - - /// - /// A list of facts to reveal - /// - public string[] reveals; - - /// - /// An achievement to unlock. Optional. - /// - public string achievementID; - } - [JsonObject] public class EntryLocationInfo { @@ -823,35 +763,6 @@ namespace NewHorizons.External.Modules [DefaultValue(1f)] public float probability = 1f; } - [JsonObject] - public class AudioVolumeInfo - { - /// - /// The location of this audio volume. Optional (will default to 0,0,0). - /// - public MVector3 position; - - /// - /// The radius of this audio volume - /// - public float radius; - - /// - /// The audio to use. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list. - /// - public string audio; - - /// - /// The audio track of this audio volume - /// - [DefaultValue("environment")] public AudioMixerTrackName track = AudioMixerTrackName.Environment; - - /// - /// Whether to loop this audio while in this audio volume or just play it once - /// - [DefaultValue(true)] public bool loop = true; - } - [JsonObject] public class RemoteInfo { @@ -1011,78 +922,5 @@ namespace NewHorizons.External.Modules public string rename; } } - - [JsonObject] - public class NotificationVolumeInfo - { - /// - /// What the notification will show for. - /// - [DefaultValue("all")] public NotificationTarget target = NotificationTarget.All; - - /// - /// The location of this notification volume. Optional (will default to 0,0,0). - /// - public MVector3 position; - - /// - /// The radius of this notification volume. - /// - public float radius; - - /// - /// The notification that will play when you enter this volume. - /// - public NotificationInfo entryNotification; - - /// - /// The notification that will play when you exit this volume. - /// - public NotificationInfo exitNotification; - - - [JsonObject] - public class NotificationInfo - { - /// - /// The message that will be displayed. - /// - public string displayMessage; - - /// - /// The duration this notification will be displayed. - /// - [DefaultValue(5f)] public float duration = 5f; - } - - [JsonConverter(typeof(StringEnumConverter))] - public enum NotificationTarget - { - [EnumMember(Value = @"all")] All = 0, - [EnumMember(Value = @"ship")] Ship = 1, - [EnumMember(Value = @"player")] Player = 2, - } - } - } - - [JsonConverter(typeof(StringEnumConverter))] - public enum AudioMixerTrackName - { - [EnumMember(Value = @"undefined")] Undefined = 0, - [EnumMember(Value = @"menu")] Menu = 1, - [EnumMember(Value = @"music")] Music = 2, - [EnumMember(Value = @"environment")] Environment = 4, - [EnumMember(Value = @"environmentUnfiltered")] Environment_Unfiltered = 5, - [EnumMember(Value = @"endTimesSfx")] EndTimes_SFX = 8, - [EnumMember(Value = @"signal")] Signal = 16, - [EnumMember(Value = @"death")] Death = 32, - [EnumMember(Value = @"player")] Player = 64, - [EnumMember(Value = @"playerExternal")] Player_External = 65, - [EnumMember(Value = @"ship")] Ship = 128, - [EnumMember(Value = @"map")] Map = 256, - [EnumMember(Value = @"endTimesMusic")] EndTimes_Music = 512, - [EnumMember(Value = @"muffleWhileRafting")] MuffleWhileRafting = 1024, - [EnumMember(Value = @"muffleIndoors")] MuffleIndoors = 2048, - [EnumMember(Value = @"slideReelMusic")] SlideReelMusic = 4096, } } \ No newline at end of file diff --git a/NewHorizons/External/Modules/VolumesModule.cs b/NewHorizons/External/Modules/VolumesModule.cs new file mode 100644 index 00000000..25bba3a5 --- /dev/null +++ b/NewHorizons/External/Modules/VolumesModule.cs @@ -0,0 +1,183 @@ +using NewHorizons.Utility; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; + +namespace NewHorizons.External.Modules +{ + [JsonObject] + public class VolumesModule + { + /// + /// Add audio volumes to this planet + /// + public AudioVolumeInfo[] audioVolumes; + + /// + /// Add notification volumes to this planet + /// + public NotificationVolumeInfo[] notificationVolumes; + + /// + /// Add triggers that reveal parts of the ship log on this planet + /// + public RevealVolumeInfo[] revealVolumes; + + [JsonObject] + public class RevealVolumeInfo + { + [JsonConverter(typeof(StringEnumConverter))] + public enum RevealVolumeType + { + [EnumMember(Value = @"enter")] Enter = 0, + + [EnumMember(Value = @"observe")] Observe = 1, + + [EnumMember(Value = @"snapshot")] Snapshot = 2 + } + + /// + /// The max view angle (in degrees) the player can see the volume with to unlock the fact (`observe` only) + /// + public float maxAngle = 180f; // Observe Only + + /// + /// The max distance the user can be away from the volume to reveal the fact (`snapshot` and `observe` only) + /// + public float maxDistance = -1f; // Snapshot & Observe Only + + /// + /// The position to place this volume at + /// + public MVector3 position; + + /// + /// The radius of this reveal volume + /// + public float radius = 1f; + + /// + /// What needs to be done to the volume to unlock the facts + /// + [DefaultValue("enter")] public RevealVolumeType revealOn = RevealVolumeType.Enter; + + /// + /// A list of facts to reveal + /// + public string[] reveals; + + /// + /// An achievement to unlock. Optional. + /// + public string achievementID; + } + + [JsonObject] + public class AudioVolumeInfo + { + /// + /// The location of this audio volume. Optional (will default to 0,0,0). + /// + public MVector3 position; + + /// + /// The radius of this audio volume + /// + public float radius; + + /// + /// The audio to use. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list. + /// + public string audio; + + /// + /// The audio track of this audio volume + /// + [DefaultValue("environment")] public AudioMixerTrackName track = AudioMixerTrackName.Environment; + + /// + /// Whether to loop this audio while in this audio volume or just play it once + /// + [DefaultValue(true)] public bool loop = true; + } + + [JsonObject] + public class NotificationVolumeInfo + { + /// + /// What the notification will show for. + /// + [DefaultValue("all")] public NotificationTarget target = NotificationTarget.All; + + /// + /// The location of this notification volume. Optional (will default to 0,0,0). + /// + public MVector3 position; + + /// + /// The radius of this notification volume. + /// + public float radius; + + /// + /// The notification that will play when you enter this volume. + /// + public NotificationInfo entryNotification; + + /// + /// The notification that will play when you exit this volume. + /// + public NotificationInfo exitNotification; + + + [JsonObject] + public class NotificationInfo + { + /// + /// The message that will be displayed. + /// + public string displayMessage; + + /// + /// The duration this notification will be displayed. + /// + [DefaultValue(5f)] public float duration = 5f; + } + + [JsonConverter(typeof(StringEnumConverter))] + public enum NotificationTarget + { + [EnumMember(Value = @"all")] All = 0, + [EnumMember(Value = @"ship")] Ship = 1, + [EnumMember(Value = @"player")] Player = 2, + } + } + } + + [JsonConverter(typeof(StringEnumConverter))] + public enum AudioMixerTrackName + { + [EnumMember(Value = @"undefined")] Undefined = 0, + [EnumMember(Value = @"menu")] Menu = 1, + [EnumMember(Value = @"music")] Music = 2, + [EnumMember(Value = @"environment")] Environment = 4, + [EnumMember(Value = @"environmentUnfiltered")] Environment_Unfiltered = 5, + [EnumMember(Value = @"endTimesSfx")] EndTimes_SFX = 8, + [EnumMember(Value = @"signal")] Signal = 16, + [EnumMember(Value = @"death")] Death = 32, + [EnumMember(Value = @"player")] Player = 64, + [EnumMember(Value = @"playerExternal")] Player_External = 65, + [EnumMember(Value = @"ship")] Ship = 128, + [EnumMember(Value = @"map")] Map = 256, + [EnumMember(Value = @"endTimesMusic")] EndTimes_Music = 512, + [EnumMember(Value = @"muffleWhileRafting")] MuffleWhileRafting = 1024, + [EnumMember(Value = @"muffleIndoors")] MuffleIndoors = 2048, + [EnumMember(Value = @"slideReelMusic")] SlideReelMusic = 4096, + } +} diff --git a/NewHorizons/Handlers/PlanetCreationHandler.cs b/NewHorizons/Handlers/PlanetCreationHandler.cs index ba0ea8f8..f30c4778 100644 --- a/NewHorizons/Handlers/PlanetCreationHandler.cs +++ b/NewHorizons/Handlers/PlanetCreationHandler.cs @@ -3,6 +3,7 @@ using NewHorizons.Builder.Body; using NewHorizons.Builder.General; using NewHorizons.Builder.Orbital; using NewHorizons.Builder.Props; +using NewHorizons.Builder.Volumes; using NewHorizons.Components; using NewHorizons.Components.Orbital; using NewHorizons.OtherMods.OWRichPresence; @@ -608,6 +609,11 @@ namespace NewHorizons.Handlers PropBuildManager.Make(go, sector, rb, body.Config, body.Mod); } + if (body.Config.Volumes != null) + { + VolumesBuildManager.Make(go, sector, body.Config, body.Mod); + } + if (body.Config.Funnel != null) { FunnelBuilder.Make(go, go.GetComponentInChildren(), rb, body.Config.Funnel); From 0921b23f8fec1013a1c1b0a00b011d7a7c1a4810 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 31 Aug 2022 17:28:38 +0000 Subject: [PATCH 043/176] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 416 ++++++++++++++------------- 1 file changed, 213 insertions(+), 203 deletions(-) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index 9b9bbb0d..a032ac40 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -128,6 +128,10 @@ "description": "Add water to this planet", "$ref": "#/definitions/WaterModule" }, + "Volumes": { + "description": "Add various volumes on this body", + "$ref": "#/definitions/VolumesModule" + }, "extras": { "type": "object", "description": "Extra data that may be used by extension mods", @@ -932,13 +936,6 @@ "$ref": "#/definitions/RaftInfo" } }, - "reveal": { - "type": "array", - "description": "Add triggers that reveal parts of the ship log on this planet", - "items": { - "$ref": "#/definitions/RevealInfo" - } - }, "scatter": { "type": "array", "description": "Scatter props around this planet's surface", @@ -981,13 +978,6 @@ "$ref": "#/definitions/SingularityModule" } }, - "audioVolumes": { - "type": "array", - "description": "Add audio volumes to this planet", - "items": { - "$ref": "#/definitions/AudioVolumeInfo" - } - }, "signals": { "type": "array", "description": "Add signalscope signals to this planet", @@ -1001,13 +991,6 @@ "items": { "$ref": "#/definitions/RemoteInfo" } - }, - "notificationVolumes": { - "type": "array", - "description": "Add notification volumes to this planet", - "items": { - "$ref": "#/definitions/NotificationVolumeInfo" - } } } }, @@ -1351,61 +1334,6 @@ } } }, - "RevealInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "maxAngle": { - "type": "number", - "description": "The max view angle (in degrees) the player can see the volume with to unlock the fact (`observe` only)", - "format": "float" - }, - "maxDistance": { - "type": "number", - "description": "The max distance the user can be away from the volume to reveal the fact (`snapshot` and `observe` only)", - "format": "float" - }, - "position": { - "description": "The position to place this volume at", - "$ref": "#/definitions/MVector3" - }, - "radius": { - "type": "number", - "description": "The radius of this reveal volume", - "format": "float" - }, - "revealOn": { - "description": "What needs to be done to the volume to unlock the facts", - "default": "enter", - "$ref": "#/definitions/RevealVolumeType" - }, - "reveals": { - "type": "array", - "description": "A list of facts to reveal", - "items": { - "type": "string" - } - }, - "achievementID": { - "type": "string", - "description": "An achievement to unlock. Optional." - } - } - }, - "RevealVolumeType": { - "type": "string", - "description": "", - "x-enumNames": [ - "Enter", - "Observe", - "Snapshot" - ], - "enum": [ - "enter", - "observe", - "snapshot" - ] - }, "ScatterInfo": { "type": "object", "additionalProperties": false, @@ -1815,75 +1743,6 @@ "whiteHole" ] }, - "AudioVolumeInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "position": { - "description": "The location of this audio volume. Optional (will default to 0,0,0).", - "$ref": "#/definitions/MVector3" - }, - "radius": { - "type": "number", - "description": "The radius of this audio volume", - "format": "float" - }, - "audio": { - "type": "string", - "description": "The audio to use. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." - }, - "track": { - "description": "The audio track of this audio volume", - "default": "environment", - "$ref": "#/definitions/AudioMixerTrackName" - }, - "loop": { - "type": "boolean", - "description": "Whether to loop this audio while in this audio volume or just play it once", - "default": true - } - } - }, - "AudioMixerTrackName": { - "type": "string", - "description": "", - "x-enumNames": [ - "Undefined", - "Menu", - "Music", - "Environment", - "Environment_Unfiltered", - "EndTimes_SFX", - "Signal", - "Death", - "Player", - "Player_External", - "Ship", - "Map", - "EndTimes_Music", - "MuffleWhileRafting", - "MuffleIndoors", - "SlideReelMusic" - ], - "enum": [ - "undefined", - "menu", - "music", - "environment", - "environmentUnfiltered", - "endTimesSfx", - "signal", - "death", - "player", - "playerExternal", - "ship", - "map", - "endTimesMusic", - "muffleWhileRafting", - "muffleIndoors", - "slideReelMusic" - ] - }, "SignalInfo": { "type": "object", "additionalProperties": false, @@ -2097,64 +1956,6 @@ } } }, - "NotificationVolumeInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "target": { - "description": "What the notification will show for.", - "default": "all", - "$ref": "#/definitions/NotificationTarget" - }, - "position": { - "description": "The location of this notification volume. Optional (will default to 0,0,0).", - "$ref": "#/definitions/MVector3" - }, - "radius": { - "type": "number", - "description": "The radius of this notification volume.", - "format": "float" - }, - "entryNotification": { - "description": "The notification that will play when you enter this volume.", - "$ref": "#/definitions/NotificationInfo" - }, - "exitNotification": { - "description": "The notification that will play when you exit this volume.", - "$ref": "#/definitions/NotificationInfo" - } - } - }, - "NotificationTarget": { - "type": "string", - "description": "", - "x-enumNames": [ - "All", - "Ship", - "Player" - ], - "enum": [ - "all", - "ship", - "player" - ] - }, - "NotificationInfo": { - "type": "object", - "additionalProperties": false, - "properties": { - "displayMessage": { - "type": "string", - "description": "The message that will be displayed." - }, - "duration": { - "type": "number", - "description": "The duration this notification will be displayed.", - "format": "float", - "default": 5.0 - } - } - }, "ReferenceFrameModule": { "type": "object", "additionalProperties": false, @@ -2580,6 +2381,215 @@ "$ref": "#/definitions/MColor" } } + }, + "VolumesModule": { + "type": "object", + "additionalProperties": false, + "properties": { + "audioVolumes": { + "type": "array", + "description": "Add audio volumes to this planet", + "items": { + "$ref": "#/definitions/AudioVolumeInfo" + } + }, + "notificationVolumes": { + "type": "array", + "description": "Add notification volumes to this planet", + "items": { + "$ref": "#/definitions/NotificationVolumeInfo" + } + }, + "revealVolumes": { + "type": "array", + "description": "Add triggers that reveal parts of the ship log on this planet", + "items": { + "$ref": "#/definitions/RevealVolumeInfo" + } + } + } + }, + "AudioVolumeInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "position": { + "description": "The location of this audio volume. Optional (will default to 0,0,0).", + "$ref": "#/definitions/MVector3" + }, + "radius": { + "type": "number", + "description": "The radius of this audio volume", + "format": "float" + }, + "audio": { + "type": "string", + "description": "The audio to use. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." + }, + "track": { + "description": "The audio track of this audio volume", + "default": "environment", + "$ref": "#/definitions/AudioMixerTrackName" + }, + "loop": { + "type": "boolean", + "description": "Whether to loop this audio while in this audio volume or just play it once", + "default": true + } + } + }, + "AudioMixerTrackName": { + "type": "string", + "description": "", + "x-enumNames": [ + "Undefined", + "Menu", + "Music", + "Environment", + "Environment_Unfiltered", + "EndTimes_SFX", + "Signal", + "Death", + "Player", + "Player_External", + "Ship", + "Map", + "EndTimes_Music", + "MuffleWhileRafting", + "MuffleIndoors", + "SlideReelMusic" + ], + "enum": [ + "undefined", + "menu", + "music", + "environment", + "environmentUnfiltered", + "endTimesSfx", + "signal", + "death", + "player", + "playerExternal", + "ship", + "map", + "endTimesMusic", + "muffleWhileRafting", + "muffleIndoors", + "slideReelMusic" + ] + }, + "NotificationVolumeInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "target": { + "description": "What the notification will show for.", + "default": "all", + "$ref": "#/definitions/NotificationTarget" + }, + "position": { + "description": "The location of this notification volume. Optional (will default to 0,0,0).", + "$ref": "#/definitions/MVector3" + }, + "radius": { + "type": "number", + "description": "The radius of this notification volume.", + "format": "float" + }, + "entryNotification": { + "description": "The notification that will play when you enter this volume.", + "$ref": "#/definitions/NotificationInfo" + }, + "exitNotification": { + "description": "The notification that will play when you exit this volume.", + "$ref": "#/definitions/NotificationInfo" + } + } + }, + "NotificationTarget": { + "type": "string", + "description": "", + "x-enumNames": [ + "All", + "Ship", + "Player" + ], + "enum": [ + "all", + "ship", + "player" + ] + }, + "NotificationInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "displayMessage": { + "type": "string", + "description": "The message that will be displayed." + }, + "duration": { + "type": "number", + "description": "The duration this notification will be displayed.", + "format": "float", + "default": 5.0 + } + } + }, + "RevealVolumeInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxAngle": { + "type": "number", + "description": "The max view angle (in degrees) the player can see the volume with to unlock the fact (`observe` only)", + "format": "float" + }, + "maxDistance": { + "type": "number", + "description": "The max distance the user can be away from the volume to reveal the fact (`snapshot` and `observe` only)", + "format": "float" + }, + "position": { + "description": "The position to place this volume at", + "$ref": "#/definitions/MVector3" + }, + "radius": { + "type": "number", + "description": "The radius of this reveal volume", + "format": "float" + }, + "revealOn": { + "description": "What needs to be done to the volume to unlock the facts", + "default": "enter", + "$ref": "#/definitions/RevealVolumeType" + }, + "reveals": { + "type": "array", + "description": "A list of facts to reveal", + "items": { + "type": "string" + } + }, + "achievementID": { + "type": "string", + "description": "An achievement to unlock. Optional." + } + } + }, + "RevealVolumeType": { + "type": "string", + "description": "", + "x-enumNames": [ + "Enter", + "Observe", + "Snapshot" + ], + "enum": [ + "enter", + "observe", + "snapshot" + ] } }, "$docs": { From 2f7eb0c6957dc1b263b1d662ab64a8207a02b9b5 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Wed, 31 Aug 2022 13:44:35 -0400 Subject: [PATCH 044/176] Add hazard volumes --- .../Builder/Volumes/HazardVolumeBuilder.cs | 40 +++++++++++++ .../Builder/Volumes/VolumesBuildManager.cs | 9 ++- NewHorizons/External/Modules/VolumesModule.cs | 60 +++++++++++++++++++ NewHorizons/Handlers/PlanetCreationHandler.cs | 2 +- 4 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 NewHorizons/Builder/Volumes/HazardVolumeBuilder.cs diff --git a/NewHorizons/Builder/Volumes/HazardVolumeBuilder.cs b/NewHorizons/Builder/Volumes/HazardVolumeBuilder.cs new file mode 100644 index 00000000..2f35c6ab --- /dev/null +++ b/NewHorizons/Builder/Volumes/HazardVolumeBuilder.cs @@ -0,0 +1,40 @@ +using NewHorizons.External.Modules; +using OWML.Common; +using OWML.Utils; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace NewHorizons.Builder.Volumes +{ + public static class HazardVolumeBuilder + { + public static HazardVolume Make(GameObject planetGO, Sector sector, OWRigidbody owrb, VolumesModule.HazardVolumeInfo info, IModBehaviour mod) + { + var go = new GameObject("HazardVolume"); + go.SetActive(false); + + go.transform.parent = sector?.transform ?? planetGO.transform; + go.transform.position = planetGO.transform.TransformPoint(info.position != null ? (Vector3)info.position : Vector3.zero); + go.layer = LayerMask.NameToLayer("BasicEffectVolume"); + + var shape = go.AddComponent(); + shape.radius = info.radius; + + var owTriggerVolume = go.AddComponent(); + owTriggerVolume._shape = shape; + + var hazardVolume = go.AddComponent(); + hazardVolume._attachedBody = owrb; + hazardVolume._type = EnumUtils.Parse(info.type.ToString(), HazardVolume.HazardType.GENERAL); + hazardVolume._damagePerSecond = info.damagePerSecond; + hazardVolume._firstContactDamageType = EnumUtils.Parse(info.firstContactDamage.ToString(), InstantDamageType.Impact); + hazardVolume._firstContactDamage = info.firstContactDamage; + + go.SetActive(true); + + return hazardVolume; + } + } +} diff --git a/NewHorizons/Builder/Volumes/VolumesBuildManager.cs b/NewHorizons/Builder/Volumes/VolumesBuildManager.cs index 5197aaf4..f7a664b9 100644 --- a/NewHorizons/Builder/Volumes/VolumesBuildManager.cs +++ b/NewHorizons/Builder/Volumes/VolumesBuildManager.cs @@ -12,7 +12,7 @@ namespace NewHorizons.Builder.Volumes { public static class VolumesBuildManager { - public static void Make(GameObject go, Sector sector, PlanetConfig config, IModBehaviour mod) + public static void Make(GameObject go, Sector sector, OWRigidbody planetBody, PlanetConfig config, IModBehaviour mod) { if (config.Volumes.revealVolumes != null) { @@ -42,6 +42,13 @@ namespace NewHorizons.Builder.Volumes NotificationVolumeBuilder.Make(go, sector, notificationVolume, mod); } } + if (config.Volumes.hazardVolumes != null) + { + foreach (var hazardVolume in config.Volumes.hazardVolumes) + { + HazardVolumeBuilder.Make(go, sector, planetBody, hazardVolume, mod); + } + } } } } diff --git a/NewHorizons/External/Modules/VolumesModule.cs b/NewHorizons/External/Modules/VolumesModule.cs index 25bba3a5..fe8d29fb 100644 --- a/NewHorizons/External/Modules/VolumesModule.cs +++ b/NewHorizons/External/Modules/VolumesModule.cs @@ -19,6 +19,11 @@ namespace NewHorizons.External.Modules /// public AudioVolumeInfo[] audioVolumes; + /// + /// Add hazard volumes to this planet + /// + public HazardVolumeInfo[] hazardVolumes; + /// /// Add notification volumes to this planet /// @@ -158,6 +163,61 @@ namespace NewHorizons.External.Modules [EnumMember(Value = @"player")] Player = 2, } } + + [JsonObject] + public class HazardVolumeInfo + { + /// + /// The location of this hazard volume. Optional (will default to 0,0,0). + /// + public MVector3 position; + + /// + /// The radius of this hazard volume. + /// + public float radius; + + /// + /// The type of hazard for this volume. + /// + [DefaultValue("general")] public HazardType type = HazardType.GENERAL; + + /// + /// The amount of damage you will take per second while inside this volume. + /// + [DefaultValue(10f)] public float damagePerSecond = 10f; + + /// + /// The type of damage you will take when you first touch this volume. + /// + [DefaultValue("impact")] public InstantDamageType _firstContactDamageType = InstantDamageType.Impact; + + /// + /// The amount of damage you will take when you first touch this volume. + /// + public float firstContactDamage; + + [JsonConverter(typeof(StringEnumConverter))] + public enum HazardType + { + [EnumMember(Value = @"none")] NONE = 0, + [EnumMember(Value = @"general")] GENERAL = 1, + [EnumMember(Value = @"darkMatter")] DARKMATTER = 2, + [EnumMember(Value = @"heat")] HEAT = 4, + [EnumMember(Value = @"fire")] FIRE = 8, + [EnumMember(Value = @"sandfall")] SANDFALL = 16, + [EnumMember(Value = @"electricity")] ELECTRICITY = 32, + [EnumMember(Value = @"rapids")] RAPIDS = 64 + } + + [JsonConverter(typeof(StringEnumConverter))] + public enum InstantDamageType + { + [EnumMember(Value = @"impact")] Impact, + [EnumMember(Value = @"puncture")] Puncture, + [EnumMember(Value = @"electrical")] Electrical + } + } } [JsonConverter(typeof(StringEnumConverter))] diff --git a/NewHorizons/Handlers/PlanetCreationHandler.cs b/NewHorizons/Handlers/PlanetCreationHandler.cs index f30c4778..67700c36 100644 --- a/NewHorizons/Handlers/PlanetCreationHandler.cs +++ b/NewHorizons/Handlers/PlanetCreationHandler.cs @@ -611,7 +611,7 @@ namespace NewHorizons.Handlers if (body.Config.Volumes != null) { - VolumesBuildManager.Make(go, sector, body.Config, body.Mod); + VolumesBuildManager.Make(go, sector, rb, body.Config, body.Mod); } if (body.Config.Funnel != null) From 1158863bb4ab36283d95fafcb81498544bbb8bc6 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Wed, 31 Aug 2022 13:47:17 -0400 Subject: [PATCH 045/176] Oops --- NewHorizons/Builder/Volumes/HazardVolumeBuilder.cs | 2 +- NewHorizons/External/Modules/VolumesModule.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/NewHorizons/Builder/Volumes/HazardVolumeBuilder.cs b/NewHorizons/Builder/Volumes/HazardVolumeBuilder.cs index 2f35c6ab..67c6f22c 100644 --- a/NewHorizons/Builder/Volumes/HazardVolumeBuilder.cs +++ b/NewHorizons/Builder/Volumes/HazardVolumeBuilder.cs @@ -29,7 +29,7 @@ namespace NewHorizons.Builder.Volumes hazardVolume._attachedBody = owrb; hazardVolume._type = EnumUtils.Parse(info.type.ToString(), HazardVolume.HazardType.GENERAL); hazardVolume._damagePerSecond = info.damagePerSecond; - hazardVolume._firstContactDamageType = EnumUtils.Parse(info.firstContactDamage.ToString(), InstantDamageType.Impact); + hazardVolume._firstContactDamageType = EnumUtils.Parse(info.firstContactDamageType.ToString(), InstantDamageType.Impact); hazardVolume._firstContactDamage = info.firstContactDamage; go.SetActive(true); diff --git a/NewHorizons/External/Modules/VolumesModule.cs b/NewHorizons/External/Modules/VolumesModule.cs index fe8d29fb..43059fbf 100644 --- a/NewHorizons/External/Modules/VolumesModule.cs +++ b/NewHorizons/External/Modules/VolumesModule.cs @@ -190,7 +190,7 @@ namespace NewHorizons.External.Modules /// /// The type of damage you will take when you first touch this volume. /// - [DefaultValue("impact")] public InstantDamageType _firstContactDamageType = InstantDamageType.Impact; + [DefaultValue("impact")] public InstantDamageType firstContactDamageType = InstantDamageType.Impact; /// /// The amount of damage you will take when you first touch this volume. From 31014ee2220867bdc6aba000103cc0c40ec799dd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 31 Aug 2022 17:49:51 +0000 Subject: [PATCH 046/176] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 81 ++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index a032ac40..85fabcd8 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -2393,6 +2393,13 @@ "$ref": "#/definitions/AudioVolumeInfo" } }, + "hazardVolumes": { + "type": "array", + "description": "Add hazard volumes to this planet", + "items": { + "$ref": "#/definitions/HazardVolumeInfo" + } + }, "notificationVolumes": { "type": "array", "description": "Add notification volumes to this planet", @@ -2478,6 +2485,80 @@ "slideReelMusic" ] }, + "HazardVolumeInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "position": { + "description": "The location of this hazard volume. Optional (will default to 0,0,0).", + "$ref": "#/definitions/MVector3" + }, + "radius": { + "type": "number", + "description": "The radius of this hazard volume.", + "format": "float" + }, + "type": { + "description": "The type of hazard for this volume.", + "default": "general", + "$ref": "#/definitions/HazardType" + }, + "damagePerSecond": { + "type": "number", + "description": "The amount of damage you will take per second while inside this volume.", + "format": "float", + "default": 10.0 + }, + "firstContactDamageType": { + "description": "The type of damage you will take when you first touch this volume.", + "default": "impact", + "$ref": "#/definitions/InstantDamageType" + }, + "firstContactDamage": { + "type": "number", + "description": "The amount of damage you will take when you first touch this volume.", + "format": "float" + } + } + }, + "HazardType": { + "type": "string", + "description": "", + "x-enumNames": [ + "NONE", + "GENERAL", + "DARKMATTER", + "HEAT", + "FIRE", + "SANDFALL", + "ELECTRICITY", + "RAPIDS" + ], + "enum": [ + "none", + "general", + "darkMatter", + "heat", + "fire", + "sandfall", + "electricity", + "rapids" + ] + }, + "InstantDamageType": { + "type": "string", + "description": "", + "x-enumNames": [ + "Impact", + "Puncture", + "Electrical" + ], + "enum": [ + "impact", + "puncture", + "electrical" + ] + }, "NotificationVolumeInfo": { "type": "object", "additionalProperties": false, From 8d04dc23b47826e186d625db40d2addf2e8ed6cc Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Wed, 31 Aug 2022 15:19:25 -0400 Subject: [PATCH 047/176] Make a base volume info --- NewHorizons/External/Modules/VolumesModule.cs | 62 ++++++------------- 1 file changed, 18 insertions(+), 44 deletions(-) diff --git a/NewHorizons/External/Modules/VolumesModule.cs b/NewHorizons/External/Modules/VolumesModule.cs index 43059fbf..e9659f2a 100644 --- a/NewHorizons/External/Modules/VolumesModule.cs +++ b/NewHorizons/External/Modules/VolumesModule.cs @@ -35,7 +35,21 @@ namespace NewHorizons.External.Modules public RevealVolumeInfo[] revealVolumes; [JsonObject] - public class RevealVolumeInfo + public class VolumeInfo + { + /// + /// The location of this volume. Optional (will default to 0,0,0). + /// + public MVector3 position; + + /// + /// The radius of this volume. + /// + public float radius = 1f; + } + + [JsonObject] + public class RevealVolumeInfo : VolumeInfo { [JsonConverter(typeof(StringEnumConverter))] public enum RevealVolumeType @@ -57,16 +71,6 @@ namespace NewHorizons.External.Modules /// public float maxDistance = -1f; // Snapshot & Observe Only - /// - /// The position to place this volume at - /// - public MVector3 position; - - /// - /// The radius of this reveal volume - /// - public float radius = 1f; - /// /// What needs to be done to the volume to unlock the facts /// @@ -84,18 +88,8 @@ namespace NewHorizons.External.Modules } [JsonObject] - public class AudioVolumeInfo + public class AudioVolumeInfo : VolumeInfo { - /// - /// The location of this audio volume. Optional (will default to 0,0,0). - /// - public MVector3 position; - - /// - /// The radius of this audio volume - /// - public float radius; - /// /// The audio to use. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list. /// @@ -113,23 +107,13 @@ namespace NewHorizons.External.Modules } [JsonObject] - public class NotificationVolumeInfo + public class NotificationVolumeInfo : VolumeInfo { /// /// What the notification will show for. /// [DefaultValue("all")] public NotificationTarget target = NotificationTarget.All; - /// - /// The location of this notification volume. Optional (will default to 0,0,0). - /// - public MVector3 position; - - /// - /// The radius of this notification volume. - /// - public float radius; - /// /// The notification that will play when you enter this volume. /// @@ -165,18 +149,8 @@ namespace NewHorizons.External.Modules } [JsonObject] - public class HazardVolumeInfo + public class HazardVolumeInfo : VolumeInfo { - /// - /// The location of this hazard volume. Optional (will default to 0,0,0). - /// - public MVector3 position; - - /// - /// The radius of this hazard volume. - /// - public float radius; - /// /// The type of hazard for this volume. /// From 6e843ee09b5eef6f55647cf82394bf67f1c9fd6c Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Wed, 31 Aug 2022 15:26:05 -0400 Subject: [PATCH 048/176] Add map restriction volume --- .../Volumes/MapRestrictionVolumeBuilder.cs | 35 +++++++++++++++ .../Builder/Volumes/VolumesBuildManager.cs | 7 +++ .../Components/MapRestrictionVolume.cs | 43 +++++++++++++++++++ NewHorizons/External/Modules/VolumesModule.cs | 5 +++ 4 files changed, 90 insertions(+) create mode 100644 NewHorizons/Builder/Volumes/MapRestrictionVolumeBuilder.cs create mode 100644 NewHorizons/Components/MapRestrictionVolume.cs diff --git a/NewHorizons/Builder/Volumes/MapRestrictionVolumeBuilder.cs b/NewHorizons/Builder/Volumes/MapRestrictionVolumeBuilder.cs new file mode 100644 index 00000000..53e5944f --- /dev/null +++ b/NewHorizons/Builder/Volumes/MapRestrictionVolumeBuilder.cs @@ -0,0 +1,35 @@ +using NewHorizons.Components; +using NewHorizons.External.Modules; +using OWML.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace NewHorizons.Builder.Volumes +{ + public static class MapRestrictionVolumeBuilder + { + public static MapRestrictionVolume Make(GameObject planetGO, Sector sector, VolumesModule.VolumeInfo info) + { + var go = new GameObject("MapRestrictionVolume"); + go.SetActive(false); + + go.transform.parent = sector?.transform ?? planetGO.transform; + go.transform.position = planetGO.transform.TransformPoint(info.position != null ? (Vector3)info.position : Vector3.zero); + go.layer = LayerMask.NameToLayer("BasicEffectVolume"); + + var shape = go.AddComponent(); + shape.radius = info.radius; + + var owTriggerVolume = go.AddComponent(); + owTriggerVolume._shape = shape; + + var mapRestrictionVolume = go.AddComponent(); + + go.SetActive(true); + + return mapRestrictionVolume; + } + } +} diff --git a/NewHorizons/Builder/Volumes/VolumesBuildManager.cs b/NewHorizons/Builder/Volumes/VolumesBuildManager.cs index f7a664b9..343ccc0c 100644 --- a/NewHorizons/Builder/Volumes/VolumesBuildManager.cs +++ b/NewHorizons/Builder/Volumes/VolumesBuildManager.cs @@ -49,6 +49,13 @@ namespace NewHorizons.Builder.Volumes HazardVolumeBuilder.Make(go, sector, planetBody, hazardVolume, mod); } } + if (config.Volumes.mapRestrictionVolumes != null) + { + foreach (var mapRestrictionVolume in config.Volumes.mapRestrictionVolumes) + { + MapRestrictionVolumeBuilder.Make(go, sector, mapRestrictionVolume); + } + } } } } diff --git a/NewHorizons/Components/MapRestrictionVolume.cs b/NewHorizons/Components/MapRestrictionVolume.cs new file mode 100644 index 00000000..43ebd8d3 --- /dev/null +++ b/NewHorizons/Components/MapRestrictionVolume.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace NewHorizons.Components +{ + [RequireComponent(typeof(OWTriggerVolume))] + public class MapRestrictionVolume : MonoBehaviour + { + private OWTriggerVolume _triggerVolume; + + public void Awake() + { + _triggerVolume = this.GetRequiredComponent(); + _triggerVolume.OnEntry += OnTriggerVolumeEntry; + _triggerVolume.OnExit += OnTriggerVolumeExit; + } + + public void OnDestroy() + { + if (_triggerVolume == null) return; + _triggerVolume.OnEntry -= OnTriggerVolumeEntry; + _triggerVolume.OnExit -= OnTriggerVolumeExit; + } + + public void OnTriggerVolumeEntry(GameObject hitObj) + { + if (hitObj.CompareTag("PlayerDetector")) + { + Locator.GetMapController()?.OnPlayerEnterMapRestriction(); + } + } + + public void OnTriggerVolumeExit(GameObject hitObj) + { + if (hitObj.CompareTag("PlayerDetector")) + { + Locator.GetMapController()?.OnPlayerExitMapRestriction(); + } + } + } +} diff --git a/NewHorizons/External/Modules/VolumesModule.cs b/NewHorizons/External/Modules/VolumesModule.cs index e9659f2a..0dd9e0fb 100644 --- a/NewHorizons/External/Modules/VolumesModule.cs +++ b/NewHorizons/External/Modules/VolumesModule.cs @@ -24,6 +24,11 @@ namespace NewHorizons.External.Modules /// public HazardVolumeInfo[] hazardVolumes; + /// + /// Add map restriction volumes to this planet + /// + public VolumeInfo[] mapRestrictionVolumes; + /// /// Add notification volumes to this planet /// From 55640c157f88d9a3f98b34c4f6ca12449e6b2e82 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 31 Aug 2022 19:28:30 +0000 Subject: [PATCH 049/176] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 64 +++++++++++++++++++--------- 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index 85fabcd8..c095374d 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -2400,6 +2400,13 @@ "$ref": "#/definitions/HazardVolumeInfo" } }, + "mapRestrictionVolumes": { + "type": "array", + "description": "Add map restriction volumes to this planet", + "items": { + "$ref": "#/definitions/VolumeInfo" + } + }, "notificationVolumes": { "type": "array", "description": "Add notification volumes to this planet", @@ -2421,12 +2428,12 @@ "additionalProperties": false, "properties": { "position": { - "description": "The location of this audio volume. Optional (will default to 0,0,0).", + "description": "The location of this volume. Optional (will default to 0,0,0).", "$ref": "#/definitions/MVector3" }, "radius": { "type": "number", - "description": "The radius of this audio volume", + "description": "The radius of this volume.", "format": "float" }, "audio": { @@ -2490,12 +2497,12 @@ "additionalProperties": false, "properties": { "position": { - "description": "The location of this hazard volume. Optional (will default to 0,0,0).", + "description": "The location of this volume. Optional (will default to 0,0,0).", "$ref": "#/definitions/MVector3" }, "radius": { "type": "number", - "description": "The radius of this hazard volume.", + "description": "The radius of this volume.", "format": "float" }, "type": { @@ -2559,23 +2566,38 @@ "electrical" ] }, - "NotificationVolumeInfo": { + "VolumeInfo": { "type": "object", "additionalProperties": false, "properties": { - "target": { - "description": "What the notification will show for.", - "default": "all", - "$ref": "#/definitions/NotificationTarget" - }, "position": { - "description": "The location of this notification volume. Optional (will default to 0,0,0).", + "description": "The location of this volume. Optional (will default to 0,0,0).", "$ref": "#/definitions/MVector3" }, "radius": { "type": "number", - "description": "The radius of this notification volume.", + "description": "The radius of this volume.", "format": "float" + } + } + }, + "NotificationVolumeInfo": { + "type": "object", + "additionalProperties": false, + "properties": { + "position": { + "description": "The location of this volume. Optional (will default to 0,0,0).", + "$ref": "#/definitions/MVector3" + }, + "radius": { + "type": "number", + "description": "The radius of this volume.", + "format": "float" + }, + "target": { + "description": "What the notification will show for.", + "default": "all", + "$ref": "#/definitions/NotificationTarget" }, "entryNotification": { "description": "The notification that will play when you enter this volume.", @@ -2621,6 +2643,15 @@ "type": "object", "additionalProperties": false, "properties": { + "position": { + "description": "The location of this volume. Optional (will default to 0,0,0).", + "$ref": "#/definitions/MVector3" + }, + "radius": { + "type": "number", + "description": "The radius of this volume.", + "format": "float" + }, "maxAngle": { "type": "number", "description": "The max view angle (in degrees) the player can see the volume with to unlock the fact (`observe` only)", @@ -2631,15 +2662,6 @@ "description": "The max distance the user can be away from the volume to reveal the fact (`snapshot` and `observe` only)", "format": "float" }, - "position": { - "description": "The position to place this volume at", - "$ref": "#/definitions/MVector3" - }, - "radius": { - "type": "number", - "description": "The radius of this reveal volume", - "format": "float" - }, "revealOn": { "description": "What needs to be done to the volume to unlock the facts", "default": "enter", From 2352d5f2d89980e6e8ecd33a41bdc6c4de10de64 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Wed, 31 Aug 2022 15:51:11 -0400 Subject: [PATCH 050/176] Add base volume abstract class --- NewHorizons/Components/BaseVolume.cs | 28 +++++++++++++++++++ .../Components/MapRestrictionVolume.cs | 23 ++------------- NewHorizons/Components/NotificationVolume.cs | 22 ++------------- 3 files changed, 34 insertions(+), 39 deletions(-) create mode 100644 NewHorizons/Components/BaseVolume.cs diff --git a/NewHorizons/Components/BaseVolume.cs b/NewHorizons/Components/BaseVolume.cs new file mode 100644 index 00000000..6ddccada --- /dev/null +++ b/NewHorizons/Components/BaseVolume.cs @@ -0,0 +1,28 @@ +using UnityEngine; + +namespace NewHorizons.Components +{ + [RequireComponent(typeof(OWTriggerVolume))] + public abstract class BaseVolume : MonoBehaviour + { + private OWTriggerVolume _triggerVolume; + + public virtual void Awake() + { + _triggerVolume = this.GetRequiredComponent(); + _triggerVolume.OnEntry += OnTriggerVolumeEntry; + _triggerVolume.OnExit += OnTriggerVolumeExit; + } + + public virtual void OnDestroy() + { + if (_triggerVolume == null) return; + _triggerVolume.OnEntry -= OnTriggerVolumeEntry; + _triggerVolume.OnExit -= OnTriggerVolumeExit; + } + + public abstract void OnTriggerVolumeEntry(GameObject hitObj); + + public abstract void OnTriggerVolumeExit(GameObject hitObj); + } +} diff --git a/NewHorizons/Components/MapRestrictionVolume.cs b/NewHorizons/Components/MapRestrictionVolume.cs index 43ebd8d3..04442840 100644 --- a/NewHorizons/Components/MapRestrictionVolume.cs +++ b/NewHorizons/Components/MapRestrictionVolume.cs @@ -5,26 +5,9 @@ using UnityEngine; namespace NewHorizons.Components { - [RequireComponent(typeof(OWTriggerVolume))] - public class MapRestrictionVolume : MonoBehaviour + public class MapRestrictionVolume : BaseVolume { - private OWTriggerVolume _triggerVolume; - - public void Awake() - { - _triggerVolume = this.GetRequiredComponent(); - _triggerVolume.OnEntry += OnTriggerVolumeEntry; - _triggerVolume.OnExit += OnTriggerVolumeExit; - } - - public void OnDestroy() - { - if (_triggerVolume == null) return; - _triggerVolume.OnEntry -= OnTriggerVolumeEntry; - _triggerVolume.OnExit -= OnTriggerVolumeExit; - } - - public void OnTriggerVolumeEntry(GameObject hitObj) + public override void OnTriggerVolumeEntry(GameObject hitObj) { if (hitObj.CompareTag("PlayerDetector")) { @@ -32,7 +15,7 @@ namespace NewHorizons.Components } } - public void OnTriggerVolumeExit(GameObject hitObj) + public override void OnTriggerVolumeExit(GameObject hitObj) { if (hitObj.CompareTag("PlayerDetector")) { diff --git a/NewHorizons/Components/NotificationVolume.cs b/NewHorizons/Components/NotificationVolume.cs index 2dc81ed1..6fe7b746 100644 --- a/NewHorizons/Components/NotificationVolume.cs +++ b/NewHorizons/Components/NotificationVolume.cs @@ -7,29 +7,13 @@ using UnityEngine; namespace NewHorizons.Components { - [RequireComponent(typeof(OWTriggerVolume))] - public class NotificationVolume : MonoBehaviour + public class NotificationVolume : BaseVolume { private NotificationTarget _target = NotificationTarget.All; private bool _pin = false; - private OWTriggerVolume _triggerVolume; private NotificationData _entryNotification; private NotificationData _exitNotification; - public void Awake() - { - _triggerVolume = this.GetRequiredComponent(); - _triggerVolume.OnEntry += OnTriggerVolumeEntry; - _triggerVolume.OnExit += OnTriggerVolumeExit; - } - - public void OnDestroy() - { - if (_triggerVolume == null) return; - _triggerVolume.OnEntry -= OnTriggerVolumeEntry; - _triggerVolume.OnExit -= OnTriggerVolumeExit; - } - public void SetPinned(bool pin) => _pin = pin; public void SetTarget(External.Modules.VolumesModule.NotificationVolumeInfo.NotificationTarget target) => SetTarget(EnumUtils.Parse(target.ToString(), NotificationTarget.All)); @@ -46,7 +30,7 @@ namespace NewHorizons.Components _exitNotification = new NotificationData(_target, TranslationHandler.GetTranslation(displayMessage, TranslationHandler.TextType.UI), duration); } - public void OnTriggerVolumeEntry(GameObject hitObj) + public override void OnTriggerVolumeEntry(GameObject hitObj) { if (_target == NotificationTarget.All) { @@ -71,7 +55,7 @@ namespace NewHorizons.Components } } - public void OnTriggerVolumeExit(GameObject hitObj) + public override void OnTriggerVolumeExit(GameObject hitObj) { if (_target == NotificationTarget.All) { From a1e39303e40f1f652afad9790b7f8fbed084f228 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Wed, 31 Aug 2022 15:57:24 -0400 Subject: [PATCH 051/176] Add base volume builder --- ...estrictionVolumeBuilder.cs => VolumeBuilder.cs} | 14 +++++--------- NewHorizons/Builder/Volumes/VolumesBuildManager.cs | 3 ++- 2 files changed, 7 insertions(+), 10 deletions(-) rename NewHorizons/Builder/Volumes/{MapRestrictionVolumeBuilder.cs => VolumeBuilder.cs} (62%) diff --git a/NewHorizons/Builder/Volumes/MapRestrictionVolumeBuilder.cs b/NewHorizons/Builder/Volumes/VolumeBuilder.cs similarity index 62% rename from NewHorizons/Builder/Volumes/MapRestrictionVolumeBuilder.cs rename to NewHorizons/Builder/Volumes/VolumeBuilder.cs index 53e5944f..c8fc563f 100644 --- a/NewHorizons/Builder/Volumes/MapRestrictionVolumeBuilder.cs +++ b/NewHorizons/Builder/Volumes/VolumeBuilder.cs @@ -1,18 +1,14 @@ using NewHorizons.Components; using NewHorizons.External.Modules; -using OWML.Common; -using System; -using System.Collections.Generic; -using System.Linq; using UnityEngine; namespace NewHorizons.Builder.Volumes { - public static class MapRestrictionVolumeBuilder + public static class VolumeBuilder { - public static MapRestrictionVolume Make(GameObject planetGO, Sector sector, VolumesModule.VolumeInfo info) + public static TVolume Make(GameObject planetGO, Sector sector, VolumesModule.VolumeInfo info) where TVolume : BaseVolume { - var go = new GameObject("MapRestrictionVolume"); + var go = new GameObject(typeof(TVolume).Name); go.SetActive(false); go.transform.parent = sector?.transform ?? planetGO.transform; @@ -25,11 +21,11 @@ namespace NewHorizons.Builder.Volumes var owTriggerVolume = go.AddComponent(); owTriggerVolume._shape = shape; - var mapRestrictionVolume = go.AddComponent(); + var volume = go.AddComponent(); go.SetActive(true); - return mapRestrictionVolume; + return volume; } } } diff --git a/NewHorizons/Builder/Volumes/VolumesBuildManager.cs b/NewHorizons/Builder/Volumes/VolumesBuildManager.cs index 343ccc0c..f4878c80 100644 --- a/NewHorizons/Builder/Volumes/VolumesBuildManager.cs +++ b/NewHorizons/Builder/Volumes/VolumesBuildManager.cs @@ -1,6 +1,7 @@ using NewHorizons.Builder.Body; using NewHorizons.Builder.ShipLog; using NewHorizons.Builder.Volumes; +using NewHorizons.Components; using NewHorizons.External.Configs; using OWML.Common; using System; @@ -53,7 +54,7 @@ namespace NewHorizons.Builder.Volumes { foreach (var mapRestrictionVolume in config.Volumes.mapRestrictionVolumes) { - MapRestrictionVolumeBuilder.Make(go, sector, mapRestrictionVolume); + VolumeBuilder.Make(go, sector, mapRestrictionVolume); } } } From 26c78e59d30361f4b2359765c52aacad93dfb42d Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Wed, 31 Aug 2022 15:58:41 -0400 Subject: [PATCH 052/176] Add interference volume --- .../Builder/Volumes/VolumesBuildManager.cs | 7 ++++ NewHorizons/Components/InterferenceVolume.cs | 37 +++++++++++++++++++ NewHorizons/External/Modules/VolumesModule.cs | 5 +++ NewHorizons/Handlers/InterferenceHandler.cs | 23 ++++++++++++ NewHorizons/Patches/HUDPatches.cs | 3 +- 5 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 NewHorizons/Components/InterferenceVolume.cs create mode 100644 NewHorizons/Handlers/InterferenceHandler.cs diff --git a/NewHorizons/Builder/Volumes/VolumesBuildManager.cs b/NewHorizons/Builder/Volumes/VolumesBuildManager.cs index f4878c80..bc88df33 100644 --- a/NewHorizons/Builder/Volumes/VolumesBuildManager.cs +++ b/NewHorizons/Builder/Volumes/VolumesBuildManager.cs @@ -57,6 +57,13 @@ namespace NewHorizons.Builder.Volumes VolumeBuilder.Make(go, sector, mapRestrictionVolume); } } + if (config.Volumes.interferenceVolumes != null) + { + foreach (var interferenceVolume in config.Volumes.interferenceVolumes) + { + VolumeBuilder.Make(go, sector, interferenceVolume); + } + } } } } diff --git a/NewHorizons/Components/InterferenceVolume.cs b/NewHorizons/Components/InterferenceVolume.cs new file mode 100644 index 00000000..7daa0238 --- /dev/null +++ b/NewHorizons/Components/InterferenceVolume.cs @@ -0,0 +1,37 @@ +using NewHorizons.Handlers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace NewHorizons.Components +{ + public class InterferenceVolume : BaseVolume + { + public override void OnTriggerVolumeEntry(GameObject hitObj) + { + if (hitObj.CompareTag("PlayerDetector")) + { + InterferenceHandler.OnPlayerEnterInterferenceVolume(); + } + else if (hitObj.CompareTag("ProbeDetector")) + { + InterferenceHandler.OnProbeEnterInterferenceVolume(); + } + } + + public override void OnTriggerVolumeExit(GameObject hitObj) + { + if (hitObj.CompareTag("PlayerDetector")) + { + InterferenceHandler.OnPlayerExitInterferenceVolume(); + } + else if (hitObj.CompareTag("ProbeDetector")) + { + InterferenceHandler.OnProbeExitInterferenceVolume(); + } + } + } +} diff --git a/NewHorizons/External/Modules/VolumesModule.cs b/NewHorizons/External/Modules/VolumesModule.cs index 0dd9e0fb..7bbf651c 100644 --- a/NewHorizons/External/Modules/VolumesModule.cs +++ b/NewHorizons/External/Modules/VolumesModule.cs @@ -24,6 +24,11 @@ namespace NewHorizons.External.Modules /// public HazardVolumeInfo[] hazardVolumes; + /// + /// Add interference volumes to this planet + /// + public VolumeInfo[] interferenceVolumes; + /// /// Add map restriction volumes to this planet /// diff --git a/NewHorizons/Handlers/InterferenceHandler.cs b/NewHorizons/Handlers/InterferenceHandler.cs new file mode 100644 index 00000000..cc0d5a90 --- /dev/null +++ b/NewHorizons/Handlers/InterferenceHandler.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NewHorizons.Handlers +{ + public static class InterferenceHandler + { + public static bool _playerInterference; + public static bool _probeInterference; + + public static bool PlayerHasInterference() => _playerInterference; + public static bool ProbeHasInterference() => _probeInterference; + + public static void OnPlayerEnterInterferenceVolume() => _playerInterference = true; + public static void OnPlayerExitInterferenceVolume() => _playerInterference = false; + + public static void OnProbeEnterInterferenceVolume() => _probeInterference = true; + public static void OnProbeExitInterferenceVolume() => _probeInterference = false; + } +} diff --git a/NewHorizons/Patches/HUDPatches.cs b/NewHorizons/Patches/HUDPatches.cs index bbb0061a..adcde38e 100644 --- a/NewHorizons/Patches/HUDPatches.cs +++ b/NewHorizons/Patches/HUDPatches.cs @@ -1,4 +1,5 @@ using HarmonyLib; +using NewHorizons.Handlers; namespace NewHorizons.Patches { @@ -57,7 +58,7 @@ namespace NewHorizons.Patches [HarmonyPatch(typeof(ProbeCamera), nameof(ProbeCamera.HasInterference))] public static void ProbeCamera_HasInterference(ProbeCamera __instance, ref bool __result) { - __result = __result || Components.CloakSectorController.isPlayerInside != Components.CloakSectorController.isProbeInside; + __result = __result || (__instance._id != ProbeCamera.ID.PreLaunch && (Components.CloakSectorController.isPlayerInside != Components.CloakSectorController.isProbeInside || InterferenceHandler.PlayerHasInterference() != InterferenceHandler.ProbeHasInterference())); } } } From af935639d88e38ec88bedbaa790ba0154e85f43a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 31 Aug 2022 20:01:11 +0000 Subject: [PATCH 053/176] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index c095374d..a717caa9 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -2400,6 +2400,13 @@ "$ref": "#/definitions/HazardVolumeInfo" } }, + "interferenceVolumes": { + "type": "array", + "description": "Add interference volumes to this planet", + "items": { + "$ref": "#/definitions/VolumeInfo" + } + }, "mapRestrictionVolumes": { "type": "array", "description": "Add map restriction volumes to this planet", From 3f6b6fab99e55f7b8b4bd1dba94f013231cf7474 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Wed, 31 Aug 2022 16:15:16 -0400 Subject: [PATCH 054/176] Add reverb volumes. --- NewHorizons/Builder/Volumes/VolumeBuilder.cs | 2 +- NewHorizons/Builder/Volumes/VolumesBuildManager.cs | 7 +++++++ NewHorizons/External/Modules/VolumesModule.cs | 5 +++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/NewHorizons/Builder/Volumes/VolumeBuilder.cs b/NewHorizons/Builder/Volumes/VolumeBuilder.cs index c8fc563f..0cf3ebe8 100644 --- a/NewHorizons/Builder/Volumes/VolumeBuilder.cs +++ b/NewHorizons/Builder/Volumes/VolumeBuilder.cs @@ -6,7 +6,7 @@ namespace NewHorizons.Builder.Volumes { public static class VolumeBuilder { - public static TVolume Make(GameObject planetGO, Sector sector, VolumesModule.VolumeInfo info) where TVolume : BaseVolume + public static TVolume Make(GameObject planetGO, Sector sector, VolumesModule.VolumeInfo info) where TVolume : MonoBehaviour { var go = new GameObject(typeof(TVolume).Name); go.SetActive(false); diff --git a/NewHorizons/Builder/Volumes/VolumesBuildManager.cs b/NewHorizons/Builder/Volumes/VolumesBuildManager.cs index bc88df33..c5ec9d90 100644 --- a/NewHorizons/Builder/Volumes/VolumesBuildManager.cs +++ b/NewHorizons/Builder/Volumes/VolumesBuildManager.cs @@ -64,6 +64,13 @@ namespace NewHorizons.Builder.Volumes VolumeBuilder.Make(go, sector, interferenceVolume); } } + if (config.Volumes.reverbVolumes != null) + { + foreach (var reverbVolume in config.Volumes.reverbVolumes) + { + VolumeBuilder.Make(go, sector, reverbVolume); + } + } } } } diff --git a/NewHorizons/External/Modules/VolumesModule.cs b/NewHorizons/External/Modules/VolumesModule.cs index 7bbf651c..bbd3b798 100644 --- a/NewHorizons/External/Modules/VolumesModule.cs +++ b/NewHorizons/External/Modules/VolumesModule.cs @@ -44,6 +44,11 @@ namespace NewHorizons.External.Modules /// public RevealVolumeInfo[] revealVolumes; + /// + /// Add reverb volumes to this planet. Great for echoes in caves. + /// + public VolumeInfo[] reverbVolumes; + [JsonObject] public class VolumeInfo { From 9d5b955bdf0f568a8b56542fe47a9d560cb30063 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 31 Aug 2022 20:18:06 +0000 Subject: [PATCH 055/176] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index a717caa9..b6e5d364 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -2427,6 +2427,13 @@ "items": { "$ref": "#/definitions/RevealVolumeInfo" } + }, + "reverbVolumes": { + "type": "array", + "description": "Add reverb volumes to this planet. Great for echoes in caves.", + "items": { + "$ref": "#/definitions/VolumeInfo" + } } } }, From b25d13e5e825d41a2a8f20d354067691c6b4794a Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Wed, 31 Aug 2022 16:28:00 -0400 Subject: [PATCH 056/176] Add insulating volumes --- NewHorizons/Builder/Volumes/VolumesBuildManager.cs | 7 +++++++ NewHorizons/External/Modules/VolumesModule.cs | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/NewHorizons/Builder/Volumes/VolumesBuildManager.cs b/NewHorizons/Builder/Volumes/VolumesBuildManager.cs index c5ec9d90..9e9a9c36 100644 --- a/NewHorizons/Builder/Volumes/VolumesBuildManager.cs +++ b/NewHorizons/Builder/Volumes/VolumesBuildManager.cs @@ -71,6 +71,13 @@ namespace NewHorizons.Builder.Volumes VolumeBuilder.Make(go, sector, reverbVolume); } } + if (config.Volumes.insulatingVolumes != null) + { + foreach (var insulatingVolume in config.Volumes.insulatingVolumes) + { + VolumeBuilder.Make(go, sector, insulatingVolume); + } + } } } } diff --git a/NewHorizons/External/Modules/VolumesModule.cs b/NewHorizons/External/Modules/VolumesModule.cs index bbd3b798..7823692b 100644 --- a/NewHorizons/External/Modules/VolumesModule.cs +++ b/NewHorizons/External/Modules/VolumesModule.cs @@ -29,6 +29,11 @@ namespace NewHorizons.External.Modules /// public VolumeInfo[] interferenceVolumes; + /// + /// Add insulating volumes to this planet + /// + public VolumeInfo[] insulatingVolumes; + /// /// Add map restriction volumes to this planet /// From 9f0f055ff8f5291d665a675ccedc5e33d2e69843 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 31 Aug 2022 20:30:42 +0000 Subject: [PATCH 057/176] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index b6e5d364..3dc6a35b 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -2407,6 +2407,13 @@ "$ref": "#/definitions/VolumeInfo" } }, + "insulatingVolumes": { + "type": "array", + "description": "Add insulating volumes to this planet", + "items": { + "$ref": "#/definitions/VolumeInfo" + } + }, "mapRestrictionVolumes": { "type": "array", "description": "Add map restriction volumes to this planet", From 9ad1612c0f0b9db2759f68acbe7963a5289ae4ed Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Wed, 31 Aug 2022 16:40:05 -0400 Subject: [PATCH 058/176] . --- NewHorizons/External/Modules/VolumesModule.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/NewHorizons/External/Modules/VolumesModule.cs b/NewHorizons/External/Modules/VolumesModule.cs index 7823692b..c2920951 100644 --- a/NewHorizons/External/Modules/VolumesModule.cs +++ b/NewHorizons/External/Modules/VolumesModule.cs @@ -15,37 +15,37 @@ namespace NewHorizons.External.Modules public class VolumesModule { /// - /// Add audio volumes to this planet + /// Add audio volumes to this planet. /// public AudioVolumeInfo[] audioVolumes; /// - /// Add hazard volumes to this planet + /// Add hazard volumes to this planet. /// public HazardVolumeInfo[] hazardVolumes; /// - /// Add interference volumes to this planet + /// Add interference volumes to this planet. /// public VolumeInfo[] interferenceVolumes; /// - /// Add insulating volumes to this planet + /// Add insulating volumes to this planet. These will stop electricty hazard volumes from affecting you (just like the jellyfish). /// public VolumeInfo[] insulatingVolumes; /// - /// Add map restriction volumes to this planet + /// Add map restriction volumes to this planet. /// public VolumeInfo[] mapRestrictionVolumes; /// - /// Add notification volumes to this planet + /// Add notification volumes to this planet. /// public NotificationVolumeInfo[] notificationVolumes; /// - /// Add triggers that reveal parts of the ship log on this planet + /// Add triggers that reveal parts of the ship log on this planet. /// public RevealVolumeInfo[] revealVolumes; From 796e02c6a240d6db85639407e8effae9387a6ce1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 31 Aug 2022 20:41:52 +0000 Subject: [PATCH 059/176] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index 3dc6a35b..45726d35 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -2388,49 +2388,49 @@ "properties": { "audioVolumes": { "type": "array", - "description": "Add audio volumes to this planet", + "description": "Add audio volumes to this planet.", "items": { "$ref": "#/definitions/AudioVolumeInfo" } }, "hazardVolumes": { "type": "array", - "description": "Add hazard volumes to this planet", + "description": "Add hazard volumes to this planet.", "items": { "$ref": "#/definitions/HazardVolumeInfo" } }, "interferenceVolumes": { "type": "array", - "description": "Add interference volumes to this planet", + "description": "Add interference volumes to this planet.", "items": { "$ref": "#/definitions/VolumeInfo" } }, "insulatingVolumes": { "type": "array", - "description": "Add insulating volumes to this planet", + "description": "Add insulating volumes to this planet. These will stop electricty hazard volumes from affecting you (just like the jellyfish).", "items": { "$ref": "#/definitions/VolumeInfo" } }, "mapRestrictionVolumes": { "type": "array", - "description": "Add map restriction volumes to this planet", + "description": "Add map restriction volumes to this planet.", "items": { "$ref": "#/definitions/VolumeInfo" } }, "notificationVolumes": { "type": "array", - "description": "Add notification volumes to this planet", + "description": "Add notification volumes to this planet.", "items": { "$ref": "#/definitions/NotificationVolumeInfo" } }, "revealVolumes": { "type": "array", - "description": "Add triggers that reveal parts of the ship log on this planet", + "description": "Add triggers that reveal parts of the ship log on this planet.", "items": { "$ref": "#/definitions/RevealVolumeInfo" } From 3528141900c16d82cf0894b2217ff7a05f3dedef Mon Sep 17 00:00:00 2001 From: TerrificTrifid <99054745+TerrificTrifid@users.noreply.github.com> Date: Wed, 31 Aug 2022 16:06:55 -0500 Subject: [PATCH 060/176] Use standard shader for basic clouds --- NewHorizons/Builder/Atmosphere/CloudsBuilder.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs b/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs index 65039e3a..6732b18f 100644 --- a/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs +++ b/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs @@ -9,7 +9,6 @@ namespace NewHorizons.Builder.Atmosphere { public static class CloudsBuilder { - private static Shader _sphereShader = null; private static Material[] _gdCloudMaterials; private static Material[] _qmCloudMaterials; private static GameObject _lightningPrefab; @@ -91,8 +90,6 @@ namespace NewHorizons.Builder.Atmosphere // Fix the rotations once the rest is done cloudsMainGO.transform.rotation = planetGO.transform.TransformRotation(Quaternion.Euler(0, 0, 0)); - // For the base shader it has to be rotated idk - if (atmo.clouds.cloudsPrefab == CloudPrefabType.Basic) cloudsMainGO.transform.rotation = planetGO.transform.TransformRotation(Quaternion.Euler(90, 0, 0)); // Lightning if (atmo.clouds.hasLightning) @@ -170,7 +167,6 @@ namespace NewHorizons.Builder.Atmosphere MeshRenderer topMR = cloudsTopGO.AddComponent(); - if (_sphereShader == null) _sphereShader = Main.NHAssetBundle.LoadAsset("Assets/Shaders/SphereTextureWrapper.shader"); if (_gdCloudMaterials == null) _gdCloudMaterials = SearchUtilities.Find("CloudsTopLayer_GD").GetComponent().sharedMaterials; if (_qmCloudMaterials == null) _qmCloudMaterials = SearchUtilities.Find("CloudsTopLayer_QM").GetComponent().sharedMaterials; Material[] prefabMaterials = atmo.clouds.cloudsPrefab == CloudPrefabType.GiantsDeep ? _gdCloudMaterials : _qmCloudMaterials; @@ -178,10 +174,10 @@ namespace NewHorizons.Builder.Atmosphere if (atmo.clouds.cloudsPrefab == CloudPrefabType.Basic) { - var material = new Material(_sphereShader); + var material = new Material(Shader.Find("Standard")); if (atmo.clouds.unlit) material.renderQueue = 3000; material.name = atmo.clouds.unlit ? "BasicCloud" : "BasicShadowCloud"; - + material.SetFloat(279, 0f); // smoothness tempArray[0] = material; } else @@ -211,8 +207,7 @@ namespace NewHorizons.Builder.Atmosphere if (atmo.clouds.rotationSpeed != 0f) { var topRT = cloudsTopGO.AddComponent(); - // Idk why but the axis is weird - topRT._localAxis = atmo.clouds.cloudsPrefab == CloudPrefabType.Basic ? Vector3.forward : Vector3.up; + topRT._localAxis = Vector3.up; topRT._degreesPerSecond = atmo.clouds.rotationSpeed; topRT._randomizeRotationRate = false; } From c45783bd29d5c50c8995f32fcf80ccf3f12be035 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Wed, 31 Aug 2022 17:19:20 -0400 Subject: [PATCH 061/176] Ship Interference --- NewHorizons/Components/InterferenceVolume.cs | 8 ++++++++ NewHorizons/Handlers/InterferenceHandler.cs | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/NewHorizons/Components/InterferenceVolume.cs b/NewHorizons/Components/InterferenceVolume.cs index 7daa0238..b13276c5 100644 --- a/NewHorizons/Components/InterferenceVolume.cs +++ b/NewHorizons/Components/InterferenceVolume.cs @@ -20,6 +20,10 @@ namespace NewHorizons.Components { InterferenceHandler.OnProbeEnterInterferenceVolume(); } + else if (hitObj.CompareTag("ShipDetector")) + { + InterferenceHandler.OnShipEnterInterferenceVolume(); + } } public override void OnTriggerVolumeExit(GameObject hitObj) @@ -32,6 +36,10 @@ namespace NewHorizons.Components { InterferenceHandler.OnProbeExitInterferenceVolume(); } + else if (hitObj.CompareTag("ShipDetector")) + { + InterferenceHandler.OnShipExitInterferenceVolume(); + } } } } diff --git a/NewHorizons/Handlers/InterferenceHandler.cs b/NewHorizons/Handlers/InterferenceHandler.cs index cc0d5a90..71384f02 100644 --- a/NewHorizons/Handlers/InterferenceHandler.cs +++ b/NewHorizons/Handlers/InterferenceHandler.cs @@ -10,14 +10,19 @@ namespace NewHorizons.Handlers { public static bool _playerInterference; public static bool _probeInterference; + public static bool _shipInterference; public static bool PlayerHasInterference() => _playerInterference; public static bool ProbeHasInterference() => _probeInterference; + public static bool ShipHasInterference() => _shipInterference; public static void OnPlayerEnterInterferenceVolume() => _playerInterference = true; public static void OnPlayerExitInterferenceVolume() => _playerInterference = false; public static void OnProbeEnterInterferenceVolume() => _probeInterference = true; public static void OnProbeExitInterferenceVolume() => _probeInterference = false; + + public static void OnShipEnterInterferenceVolume() => _shipInterference = true; + public static void OnShipExitInterferenceVolume() => _shipInterference = false; } } From f4b05940e32791c5dc5e9872168352e87000e6c1 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 31 Aug 2022 18:31:26 -0400 Subject: [PATCH 062/176] Fibonacci deadass thought his sequence counted rabbits --- NewHorizons/Builder/Props/ScatterBuilder.cs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/NewHorizons/Builder/Props/ScatterBuilder.cs b/NewHorizons/Builder/Props/ScatterBuilder.cs index 26af9d33..2024d3ae 100644 --- a/NewHorizons/Builder/Props/ScatterBuilder.cs +++ b/NewHorizons/Builder/Props/ScatterBuilder.cs @@ -25,7 +25,7 @@ namespace NewHorizons.Builder.Props // Works up to planets with 575 radius before capping var numPoints = Math.Min((int)(area * 10), 41666666); - var points = RandomUtility.FibonacciSphere(numPoints); + //var points = RandomUtility.FibonacciSphere(numPoints); Texture2D heightMapTexture = null; if (heightMap != null) @@ -57,11 +57,7 @@ namespace NewHorizons.Builder.Props else prefab = SearchUtilities.Find(propInfo.path); for (int i = 0; i < propInfo.count; i++) { - // Failsafe - if (points.Count == 0) break; - - var randomInd = (int)Random.Range(0, points.Count - 1); - var point = points[randomInd]; + var point = Random.insideUnitSphere; var height = radius; if (heightMapTexture != null) @@ -105,9 +101,6 @@ namespace NewHorizons.Builder.Props // Rotate around normal prop.transform.localRotation *= Quaternion.AngleAxis(Random.Range(0, 360), Vector3.up); - - points.QuickRemoveAt(randomInd); - if (points.Count == 0) return; } } } From e73c48e5fc17aafcb0142d9bf8a636f0ef9a1b20 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 31 Aug 2022 19:00:27 -0400 Subject: [PATCH 063/176] Only run detail builder once --- NewHorizons/Builder/Props/ScatterBuilder.cs | 27 +++++++++------------ 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/NewHorizons/Builder/Props/ScatterBuilder.cs b/NewHorizons/Builder/Props/ScatterBuilder.cs index 2024d3ae..e5d14c8a 100644 --- a/NewHorizons/Builder/Props/ScatterBuilder.cs +++ b/NewHorizons/Builder/Props/ScatterBuilder.cs @@ -19,14 +19,6 @@ namespace NewHorizons.Builder.Props { var heightMap = config.HeightMap; - var area = 4f * Mathf.PI * radius * radius; - - // To not use more than 0.5GB of RAM while doing this - // Works up to planets with 575 radius before capping - var numPoints = Math.Min((int)(area * 10), 41666666); - - //var points = RandomUtility.FibonacciSphere(numPoints); - Texture2D heightMapTexture = null; if (heightMap != null) { @@ -55,6 +47,14 @@ namespace NewHorizons.Builder.Props GameObject prefab; if (propInfo.assetBundle != null) prefab = AssetBundleUtilities.LoadPrefab(propInfo.assetBundle, propInfo.path, mod); else prefab = SearchUtilities.Find(propInfo.path); + + // Run all the make detail stuff on it early and just copy it over and over instead + var detailInfo = new PropModule.DetailInfo() + { + scale = propInfo.scale + }; + prefab = DetailBuilder.Make(go, sector, prefab, detailInfo); + for (int i = 0; i < propInfo.count; i++) { var point = Random.insideUnitSphere; @@ -88,13 +88,10 @@ namespace NewHorizons.Builder.Props point = Quaternion.Euler(90, 0, 0) * point; } - var detailInfo = new PropModule.DetailInfo() - { - position = point.normalized * height, - scale = propInfo.scale, - alignToNormal = true - }; - var prop = DetailBuilder.Make(go, sector, prefab, detailInfo); + var prop = prefab.InstantiateInactive(); + prop.transform.localPosition = go.transform.TransformPoint(point); + var up = go.transform.InverseTransformPoint(prop.transform.position).normalized; + prop.transform.rotation = Quaternion.FromToRotation(Vector3.up, up); if (propInfo.offset != null) prop.transform.localPosition += prop.transform.TransformVector(propInfo.offset); if (propInfo.rotation != null) prop.transform.rotation *= Quaternion.Euler(propInfo.rotation); From 3f6a0f04f55b25966060c4c96f9cbf597c5f16fd Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 31 Aug 2022 19:04:36 -0400 Subject: [PATCH 064/176] Set active dingus --- NewHorizons/Builder/Props/ScatterBuilder.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NewHorizons/Builder/Props/ScatterBuilder.cs b/NewHorizons/Builder/Props/ScatterBuilder.cs index e5d14c8a..57efa403 100644 --- a/NewHorizons/Builder/Props/ScatterBuilder.cs +++ b/NewHorizons/Builder/Props/ScatterBuilder.cs @@ -98,6 +98,8 @@ namespace NewHorizons.Builder.Props // Rotate around normal prop.transform.localRotation *= Quaternion.AngleAxis(Random.Range(0, 360), Vector3.up); + + prop.SetActive(true); } } } From fb85ecdb35ba5b50093d2dbdd1df92e2021baa4e Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 31 Aug 2022 19:07:44 -0400 Subject: [PATCH 065/176] Set parent dingus --- NewHorizons/Builder/Props/ScatterBuilder.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/NewHorizons/Builder/Props/ScatterBuilder.cs b/NewHorizons/Builder/Props/ScatterBuilder.cs index 57efa403..571d48db 100644 --- a/NewHorizons/Builder/Props/ScatterBuilder.cs +++ b/NewHorizons/Builder/Props/ScatterBuilder.cs @@ -89,6 +89,7 @@ namespace NewHorizons.Builder.Props } var prop = prefab.InstantiateInactive(); + prop.transform.SetParent(sector?.transform ?? go.transform); prop.transform.localPosition = go.transform.TransformPoint(point); var up = go.transform.InverseTransformPoint(prop.transform.position).normalized; prop.transform.rotation = Quaternion.FromToRotation(Vector3.up, up); From 93ef061b4b64c0ca201112822bf8c47a07e89524 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 31 Aug 2022 19:09:05 -0400 Subject: [PATCH 066/176] Destroy prefab --- NewHorizons/Builder/Props/ScatterBuilder.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NewHorizons/Builder/Props/ScatterBuilder.cs b/NewHorizons/Builder/Props/ScatterBuilder.cs index 571d48db..49a4e1c9 100644 --- a/NewHorizons/Builder/Props/ScatterBuilder.cs +++ b/NewHorizons/Builder/Props/ScatterBuilder.cs @@ -102,6 +102,8 @@ namespace NewHorizons.Builder.Props prop.SetActive(true); } + + GameObject.Destroy(prefab); } } } From 422489f8de5fd82cfd6754856f4d96d2a9eed64a Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 31 Aug 2022 19:16:40 -0400 Subject: [PATCH 067/176] Forgot to multiply by height --- NewHorizons/Builder/Props/ScatterBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/Builder/Props/ScatterBuilder.cs b/NewHorizons/Builder/Props/ScatterBuilder.cs index 49a4e1c9..f1cb00d5 100644 --- a/NewHorizons/Builder/Props/ScatterBuilder.cs +++ b/NewHorizons/Builder/Props/ScatterBuilder.cs @@ -90,7 +90,7 @@ namespace NewHorizons.Builder.Props var prop = prefab.InstantiateInactive(); prop.transform.SetParent(sector?.transform ?? go.transform); - prop.transform.localPosition = go.transform.TransformPoint(point); + prop.transform.localPosition = go.transform.TransformPoint(point * height); var up = go.transform.InverseTransformPoint(prop.transform.position).normalized; prop.transform.rotation = Quaternion.FromToRotation(Vector3.up, up); From fe6577c79e38cdfa13a794f44540df96eb6c5d21 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 31 Aug 2022 21:13:01 -0400 Subject: [PATCH 068/176] Updated NH unity project --- .gitignore | 5 +++++ NewHorizons/Assets/xen.newhorizons | Bin 39856 -> 56871 bytes NewHorizons/Assets/xen.newhorizons.manifest | 23 ++++++++++++++++++++ 3 files changed, 28 insertions(+) create mode 100644 NewHorizons/Assets/xen.newhorizons.manifest diff --git a/.gitignore b/.gitignore index 94675367..4e3a3cec 100644 --- a/.gitignore +++ b/.gitignore @@ -417,4 +417,9 @@ docs/.m_cache/** # Unity project new-horizons-unity new-horizons-unity/** +nh-unity +nh-unity/** +NewHorizons/Assets/Assets +NewHorizons/Assets/Assets.manifest + docs/.env diff --git a/NewHorizons/Assets/xen.newhorizons b/NewHorizons/Assets/xen.newhorizons index 84e681f3c4fe832e136c2e1b00c3259e44b31030..1a754842360b55eeb0b2280c696eff4354db9e5d 100644 GIT binary patch literal 56871 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%0DP4$=QHV09F}7GPsg|LM|g> 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

1KB(i7@a;=)8TE&$lT(ZAt!mUn$IM$jV7?~^*? z+}bmAM%Lv(oJ=J==xNQLLH)=71JkvrD1U8F_NSfR<%PEV8QZrIDG-(tYcm&$7kUF` z>0UR0qzd%&ro454!g&_9nWD0EYL17DZ{H$R!t9`Oe2$>G7vvoRxrd^G>ClmP&FpOo zz8)k|Mv1*})(}2jppmV=!qbSg~4B11+#Xj0{c3%jw*vnU^uU@U7Rntlqw6WxlInMI6mUUAacqsLwI>U(tYN$tm5^~2s!b#QSb{!RcOR>{} z+Oifs#ju$y{GQuvQFA`#N3GCK*Y4Z+a3#>PHN(8oZF0lrX;pF3a|Adh!;G{a{B40O z=}Xn8tWWzAW!YhA8QI#-;ytB;DOcxbW)Ub6Ba~ALvk~tm^JNIEDQFx)c0gHLy5XaeqaPBiVH~=gZ*-jQ4!1sxzqK7Mw5fo_ zW60rdwS`a~)Y**NA?rqSTbZ7We5CNp0Nw?VSb6@Ye~IA7+>60NvX4Ko?NA~$Yo>*$ z%Doy?^cgJu528t;eeF^spT|Iw{&*Jm?Jl(&>fnd00GAN#M(1w@=ptVZ_0uJ7*lBq- z+P8^jL<9Dv*!z(C7U8mLWIS!z<7SM;{4FYE^I$@5Q2kBz*U2>VpppU|ioEZEOT+-S z9qr96Fge?nF$f;-Qmz>DT^Y@dbZl(d=#yM%#3vy;x_s26+jx6J>gzsIM@|Ui9&~Gy z1mE^TIWuu}V&3%=DAqwtDC1QzI{(-ySfdESfb;FS-Ob&Lsxb>K5(pOS@y=raQSmjk zrRrdGnU1v2#YiHds$088gB2O=m$z7)pVjOfmwBFti=FfIf*nY39JQdleh I{}3ev0C4;nasU7T literal 39856 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 diff --git a/NewHorizons/Assets/xen.newhorizons.manifest b/NewHorizons/Assets/xen.newhorizons.manifest new file mode 100644 index 00000000..d45ddec5 --- /dev/null +++ b/NewHorizons/Assets/xen.newhorizons.manifest @@ -0,0 +1,23 @@ +ManifestFileVersion: 0 +CRC: 1014555239 +Hashes: + AssetFileHash: + serializedVersion: 2 + Hash: 45fa3430ee7bea1e8384e57927fc0f76 + TypeTreeHash: + serializedVersion: 2 + Hash: 55d48f4ad9c3b13330b9eb5ee5686477 +HashAppended: 0 +ClassTypes: +- Class: 48 + Script: {instanceID: 0} +SerializeReferenceClassIdentifiers: [] +Assets: +- Assets/Shaders/SphereTextureWrapper.shader +- Assets/Shaders/Ring.shader +- Assets/Shaders/SphereTextureWrapperNormal.shader +- Assets/Shaders/UnlitRing1Pixel.shader +- Assets/Shaders/UnlitTransparent.shader +- Assets/Shaders/StandardCullOFF.shader +- Assets/Shaders/Ring1Pixel.shader +Dependencies: [] From 4517d30337711165536dfd3bdcb4acc2a4624b1e Mon Sep 17 00:00:00 2001 From: TerrificTrifid <99054745+TerrificTrifid@users.noreply.github.com> Date: Wed, 31 Aug 2022 21:02:16 -0500 Subject: [PATCH 069/176] Transparent clouds --- .../Builder/Atmosphere/AtmosphereBuilder.cs | 2 +- .../Builder/Atmosphere/CloudsBuilder.cs | 65 ++++++++++++++++++- NewHorizons/Builder/Body/ProxyBuilder.cs | 3 +- .../External/Modules/AtmosphereModule.cs | 2 + 4 files changed, 67 insertions(+), 5 deletions(-) diff --git a/NewHorizons/Builder/Atmosphere/AtmosphereBuilder.cs b/NewHorizons/Builder/Atmosphere/AtmosphereBuilder.cs index 7646a205..9dd1e8a8 100644 --- a/NewHorizons/Builder/Atmosphere/AtmosphereBuilder.cs +++ b/NewHorizons/Builder/Atmosphere/AtmosphereBuilder.cs @@ -59,7 +59,7 @@ namespace NewHorizons.Builder.Atmosphere } } - material.SetFloat(InnerRadius, atmosphereModule.clouds != null ? atmosphereModule.size : surfaceSize); + material.SetFloat(InnerRadius, (atmosphereModule.clouds != null && atmosphereModule.clouds.cloudsPrefab != CloudPrefabType.Transparent) ? atmosphereModule.size : surfaceSize); material.SetFloat(OuterRadius, atmosphereModule.size * 1.2f); if (atmosphereModule.atmosphereTint != null) material.SetColor(SkyColor, atmosphereModule.atmosphereTint.ToColor()); diff --git a/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs b/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs index 6732b18f..8b301488 100644 --- a/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs +++ b/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs @@ -13,11 +13,14 @@ namespace NewHorizons.Builder.Atmosphere private static Material[] _qmCloudMaterials; private static GameObject _lightningPrefab; private static Texture2D _colorRamp; + private static Shader transparentCloudShader; private static readonly int Color = Shader.PropertyToID("_Color"); private static readonly int ColorRamp = Shader.PropertyToID("_ColorRamp"); private static readonly int MainTex = Shader.PropertyToID("_MainTex"); private static readonly int RampTex = Shader.PropertyToID("_RampTex"); private static readonly int CapTex = Shader.PropertyToID("_CapTex"); + private static readonly int Smoothness = Shader.PropertyToID("_Glossiness"); + private static readonly int Mode = Shader.PropertyToID("_Mode"); public static void Make(GameObject planetGO, Sector sector, AtmosphereModule atmo, bool cloaked, IModBehaviour mod) { @@ -28,7 +31,15 @@ namespace NewHorizons.Builder.Atmosphere cloudsMainGO.SetActive(false); cloudsMainGO.transform.parent = sector?.transform ?? planetGO.transform; - MakeTopClouds(cloudsMainGO, atmo, mod); + if (atmo.clouds.cloudsPrefab != CloudPrefabType.Transparent) MakeTopClouds(cloudsMainGO, atmo, mod); + else + { + MakeTransparentClouds(cloudsMainGO, atmo, mod); + if (atmo.clouds.hasLightning) MakeLightning(cloudsMainGO, sector, atmo); + cloudsMainGO.transform.position = planetGO.transform.TransformPoint(Vector3.zero); + cloudsMainGO.SetActive(true); + return; + } GameObject cloudsBottomGO = new GameObject("BottomClouds"); cloudsBottomGO.SetActive(false); @@ -114,7 +125,7 @@ namespace NewHorizons.Builder.Atmosphere lightning.transform.localPosition = Vector3.zero; var lightningGenerator = lightning.GetComponent(); - lightningGenerator._altitude = (atmo.clouds.outerCloudRadius + atmo.clouds.innerCloudRadius) / 2f; + lightningGenerator._altitude = atmo.clouds.cloudsPrefab != CloudPrefabType.Transparent ? (atmo.clouds.outerCloudRadius + atmo.clouds.innerCloudRadius) / 2f : atmo.clouds.outerCloudRadius; if (noAudio) { lightningGenerator._audioPrefab = null; @@ -177,7 +188,7 @@ namespace NewHorizons.Builder.Atmosphere var material = new Material(Shader.Find("Standard")); if (atmo.clouds.unlit) material.renderQueue = 3000; material.name = atmo.clouds.unlit ? "BasicCloud" : "BasicShadowCloud"; - material.SetFloat(279, 0f); // smoothness + material.SetFloat(Smoothness, 0f); tempArray[0] = material; } else @@ -218,5 +229,53 @@ namespace NewHorizons.Builder.Atmosphere return cloudsTopGO; } + + public static GameObject MakeTransparentClouds(GameObject rootObject, AtmosphereModule atmo, IModBehaviour mod, bool isProxy = false) + { + Texture2D image; + + try + { + image = ImageUtilities.GetTexture(mod, atmo.clouds.texturePath); + } + catch (Exception e) + { + Logger.LogError($"Couldn't load Cloud texture for [{atmo.clouds.texturePath}]:\n{e}"); + return null; + } + + GameObject cloudsTransparentGO = new GameObject("TransparentClouds"); + cloudsTransparentGO.SetActive(false); + cloudsTransparentGO.transform.parent = rootObject.transform; + cloudsTransparentGO.transform.localScale = Vector3.one * atmo.clouds.outerCloudRadius; + + MeshFilter filter = cloudsTransparentGO.AddComponent(); + filter.mesh = SearchUtilities.Find("CloudsTopLayer_GD").GetComponent().mesh; + + MeshRenderer renderer = cloudsTransparentGO.AddComponent(); + if (transparentCloudShader == null) transparentCloudShader = Main.NHAssetBundle.LoadAsset("Assets/Shaders/StandardCullOFF.shader"); + var material = new Material(transparentCloudShader); + material.renderQueue = 2999; + material.name = "TransparentCloud"; + material.SetFloat(Smoothness, 0f); + material.SetFloat(Mode, 2); + material.SetTexture(MainTex, image); + + renderer.sharedMaterial = material; + + if (atmo.clouds.rotationSpeed != 0f) + { + var rt = cloudsTransparentGO.AddComponent(); + rt._localAxis = Vector3.up; + rt._degreesPerSecond = atmo.clouds.rotationSpeed; + rt._randomizeRotationRate = false; + } + + cloudsTransparentGO.transform.localPosition = Vector3.zero; + + cloudsTransparentGO.SetActive(true); + + return cloudsTransparentGO; + } } } diff --git a/NewHorizons/Builder/Body/ProxyBuilder.cs b/NewHorizons/Builder/Body/ProxyBuilder.cs index 4e028acf..32f7d355 100644 --- a/NewHorizons/Builder/Body/ProxyBuilder.cs +++ b/NewHorizons/Builder/Body/ProxyBuilder.cs @@ -111,7 +111,8 @@ namespace NewHorizons.Builder.Body if (body.Config.Atmosphere.clouds != null) { - topClouds = CloudsBuilder.MakeTopClouds(proxy, body.Config.Atmosphere, body.Mod).GetComponent(); + if (body.Config.Atmosphere.clouds.cloudsPrefab != External.Modules.CloudPrefabType.Transparent) topClouds = CloudsBuilder.MakeTopClouds(proxy, body.Config.Atmosphere, body.Mod).GetComponent(); + else topClouds = CloudsBuilder.MakeTransparentClouds(proxy, body.Config.Atmosphere, body.Mod, true).GetAddComponent(); if (body.Config.Atmosphere.clouds.hasLightning) lightningGenerator = CloudsBuilder.MakeLightning(proxy, null, body.Config.Atmosphere, true); diff --git a/NewHorizons/External/Modules/AtmosphereModule.cs b/NewHorizons/External/Modules/AtmosphereModule.cs index d2b835ee..33c0ccef 100644 --- a/NewHorizons/External/Modules/AtmosphereModule.cs +++ b/NewHorizons/External/Modules/AtmosphereModule.cs @@ -30,6 +30,8 @@ namespace NewHorizons.External.Modules [EnumMember(Value = @"quantumMoon")] QuantumMoon = 1, [EnumMember(Value = @"basic")] Basic = 2, + + [EnumMember(Value = @"transparent")] Transparent = 3, } [JsonObject] From f177f36f2973f01e551154d9449294b755628687 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 1 Sep 2022 02:04:23 +0000 Subject: [PATCH 070/176] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index 7ea40e04..2a4b8881 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -400,12 +400,14 @@ "x-enumNames": [ "GiantsDeep", "QuantumMoon", - "Basic" + "Basic", + "Transparent" ], "enum": [ "giantsDeep", "quantumMoon", - "basic" + "basic", + "transparent" ] }, "FluidType": { From 9d9275b2643baa06ba67ccc2248a6eb90e32c19b Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 31 Aug 2022 22:42:23 -0400 Subject: [PATCH 071/176] Updated unity project --- NewHorizons/Assets/xen.newhorizons | Bin 56871 -> 57078 bytes NewHorizons/Assets/xen.newhorizons.manifest | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/NewHorizons/Assets/xen.newhorizons b/NewHorizons/Assets/xen.newhorizons index 1a754842360b55eeb0b2280c696eff4354db9e5d..3fd40a93a49d6755e6641f0a1b7e1968330219d0 100644 GIT binary patch literal 57078 zcmV)9K*hgRZfSIRMpFO)000LyE_g0@05UK!IW9CVGB;*1000000002q_5c6?K>z>% zTL1t6LjV8(00000000000000U009880RRLHtN;MsbpSyQ00;p90U7}C7ytxAK|(Dz zH8nXmGiG6CGB+|eH8(ghGBPw`W;r-BH8L|dG&48=T>tpuLDBHie8+BotN$-b3U5hd_Cc}>)Uq@2SeAHuJydxg!G9f)_^tN z)qB`%S;Qh39EaAP!h$$x8yFpy)tC0#Zb<<-Y!z+My|J_P&$ObBYGv|)P;z&Qn3+Y*QwIb0xjqOYpncg^f{M_Wi%;0GCT#k02G(fl{A{@ zQVmrA;PJAn0yCEl7w=|kf5N|K;$sw((#`^fAEd}4fqJ>O~A9DMhpjBM?D!Q#ns}SdB)y*HyuJ&XO}+7fm1SVXj+tI&BL%KQTS@#b zM0~zyKr=f{O4Tr+F{0+JPI=z<<_U}DX-Xx%qpXJTp%+w9g&fPHtay9fyeTX|7~W2x zEh^@evaRi2vzmQ~-j=@RG0ihMOGQx?>6l6Aa~!50_<_yth^wP=4r$WQ>*gRn2}H~| zVOhkuD+u{{7KC4-9hD13E`wHLK%L(85y3iu&bm|MT-fG$sfaHUhIGv7&sFJ8@@INy zX?u@mb|9Ta9ZMQgKn9^O6hd6vgX|#+CD4ccaILmPiC+W#DxUw-UxcWCN1Nt}rcxUT zcmwB}ApDa87$uGmAcCt;U&fO?zyG@xvtr)6rAzb1K~PBY_xeHa(iQnb=`VhQGg)I6I+y#;iCmwYOMeGB^}2ZGyHRt zGyoIc_gLEwFJLqJ<~Qg_DCcYj1WPy|vPGlSBMN)`K59TopZVR!Ikf~-pM{Chc%B^J zLonU_C1;`NVY$p#F~FxM&(b;9#Qq77jpFk)mr)@qp@828aV*?!MAD%F^F~JvK}+k}pOwk!^h`VLPip30D)Yp`y%X(NZJqVC z&l?qmz6s%jdI$Qb!93A)=uHnAJn zmqtj#21QZBPqw0o`67oI8cE6r88)9G_f+I46(*Z1M8t8|GyF02@@@aHIxaH>r4J|% zCKze4}&!xm78Wbw2_2_j1ZtELtlXW|@`Y7XXJ&Z&zEqd2r13TY*u zU8yfnBJ|-}lwbz`tQpR~;AnEU1;La~HsZTa3)9|vQi})M5 zv=GlM@Hb=1RSvqrvZM3B=JDTx7-jTV9umDWfSw5zE~Dxhou-`{+JCBP?Fm%15MXeD z+DxyFPGTx4Qucu|aSjF!Es=`ztJVIFKXj7juUGq|fXj7J@pShp=#<95!FQGV7dK~_Ke2PrB1Jy4t(8=wY!8mgiRRv|ly?`y(bacpEkQ`pnJJO#XHxMfi~ozQJl>*cgC+$(Tl!07t>QZTma`eBHsF7X`$vAS3pfJ4$X`+fhWaEmr}8Js+a~(H-{*jy|6? zJ2i){4_=fkRvKwpuGJfDwrvGhz1I^wPJpqK)tZ2ny=GyPuu?3SWXR>yhg9c$PI1FF zT`)^;4@NT7Ir#M^$a8B>^mrZqz!rgST(h%A)qZ zmI3W%SIF+W7gikTCG?3Nl-GDt(nk(xa6~?Zv%TJFG=l2=n<=a-9?Bw~kdMgPOPty7 zZG4Ag(36KSnEqA_zhug*B$bPFuOO@e`okvJh=&f#1k}EM{p!g0^LS?{;*WsgUWcx+ z?#pi;X~a|>PrjlW$b4x;F`5$y9i-Ddmxn2aFwUG1W|(8ZdXDfqCIB#vNxh1<2#Kgo zYule+<0LPGh3KbI*K*0+JFPVI@iTJ{-qUwH1}83?j6`8>)FqLj=-LHJ=kaz=$Y>Es zjr+c`sEd5@zc+_Qt%u^d3xRaA1vucr{+@sJ7!KQw+czZ@rf^Rf=i#c(AO~d?7|i6w ze~nuGPvIf~cR`HXOrV43a;84FJ-p^12$het`VR+AkLLe5-@%+bm1?&AH%SUD8WONI z1l#aXS|Du5pN(&uRV=8u(;is11rjRBp}d&1e$N^~{%kgD%veQ(jb$G@N9GnnBMVj0 zNoZr8dkHrx#~Tgx%7@OMR|UoL#uO!ow4IS&nE3Nx>ST+RTN4fp$p7;5Z(0gOj20G1 zFK{E)()L4+>?F_xyyL#qWEjNQ*#D*mZs*_tOL@!_N@O>f2kpq z!|1}N6U_`Kx6I*5ZPJJ)T3ci166=zb8A@Sru-iP3W)#*=oasdJVE>Kwg8=e+%-$>^ zbUS=lnd_ROw{zvble7SE5F_PhG^^~Ey%R^6P62+Gv60cnRQxlZk}9L?S(4dEWugt5 z>vCb!!KEJh!;b*{#8!ts;f_~2yb6qsZq_J3c~n$*2k{y3X=*7ju?VH;2fut|Z+aRR zA5*A6d}g66*9|V)p%hb+$Fo#!~vyT{1pD5H;?)jLW?*QpKyb0 zu*uo_c~vv5&k?6jWjX|S@oEx;N(LEDjK2X_^(51Se*#6F(H*+EG-=q44b3%GGh!El zf01pP*Uy;N+aat-i_`n7fJd#_cX9|Lu6y`{(zut204HCj0vdX#h!!!EU)tb<95*aY z&g7g{xDK*iyd#B2U?~x-2hc@9|NL+oD9%&dANomcW>}JUD1D#KzhnrH;W6+xTf;*C z7Voj;7M)sOyx9=~lay-kdK~R^jsiGm-(%8zn=lq)e3D*uw+TM255%7L>s{~PtY8tB zDGi%g&(trl8ZCFEjGy$6$IDN__2XrCtjTod@$X5{W>f>b_N=+~jjgw9PhVB#Ew0)3 zQ9cQF6LuWy!#4dvBrW2_qY%ycF&tgxjP{f>#n6msy71b9UMdf|^tFzCMq$T8yOz-Q z9SzkW{(16~DbrAUcK!;|Im8uuG_k7Rye4Rl^=gaor{UoLnXJyL$219zO|5pu= zuH#2L|MLySdcFA53TTt_&mWbxO2mfa7|^**YbE*M_ha<|F*NG;W6qvE8Q=zMKW7vRd4mUdAkuht><-_xbuyZp;L)B2s_h=5#8Hh|(_)13Dlo2lvAYi`D( zr}l=OH^Rj6Skc_y=Z0|@OfJjG0vQ4-?!U$F6VI7R0HVrT>28x2y}IfV={xAK<~r+x_Wr6JD_Mak^n z9L0NAZNgasq8*FoFB1U}2V6n|O3!hgfeO6Uopnx=9rzWinmb9D;2aqVCnRE@Xcyk% zR*(b)r?eLIUf(ZO$CN>!GDSuet|K7s-Z2%QR@-4+b+M~kA>b+bOd@LOwHlUGVTUml zQzptQB&h6ABtPFI&=M)b_d8|xJ%bcY5W}&IPe);jYEVSrMN0IJJr{a#Ts1uLZt6K6 z{bi3)x((?>(#2l+x_D?Zq+&@veWoQ2hmg8${Y_Tn3xc|pAYGdAMaOL>HL(H55fLIj zcG5ljh7qe!5jE$1!vRk-q{4rLSEcDB1Cx80_7T8Y6NxkkhoToU?9lWtvW6O4SjLx0 zeDi(0&EgMxQB$|cYGym;NP76m&2(g4&*oPC^x12LTZeymBttP7_n+kH5o!C2Ph8F= z`@>;?VeSyN-SW}mmbXGYWR44_9#x5J**nwQu$7pARJ`YqHED-@9L-TCR?3>B5y z0O1+?0UiWE7PHIWcIbE;EA|bn7dMM`ya9)V&*+nIB;sQU?}wkprt;yTW3bbp1T7ag zC1xNlYkiJ?j3UEiYN+@(_P0h>NixH37Rn;62R^>-103T>>IdG=-TYObPSxioX7_g; zidb!pQiBo6<{&5zE$9|lxnT)F`f3%JO=cw37bKNNBRAy%bD9Z6V1j2(Qk5N0zFCpV z-uNb=I@dKK4|ZckrFwCcA=gq-j{~d9M!k6YJ$RT=JI~pp=JlqDI=iz_U;aV?C%`U@ zfVu7e79`)l=xf?RdJLUcW-atZ-0PWvS(q35IwmyaXJnuPCk|iSLGN76meYjR=!B*6 zEN62>ZaqY3+?e^%qkd{Rh_ZKXK&m|_G^jcP&!4MA$NH2iM*NQm#^YvlJ>gRt>fs;Y z9TE0w6VTTI=Q3501;d1R15U7PhySEcpen7hh}-J9CC5%eXE`2vkGP^ckp@IzQy}*x zFrD}#hCurgg?#m*_|h+7!vt=C7I`V3({d8^Y06>%+umKs@P~HcLyPVk^0z;hH6C+4 z71!Kqd|~B4`Y8ug+dDl&3UE1ZHA?2obPP&$9j%R?)G3*?1JJJNn3^6(+SE8NI8U@Z z#5~hG^Kr1&$9aqwW*`)=uF+en=$5}t=;}=m_iF#^`eeDWC~>?q>&sBddA5T>*GN@8 zkN40q{mVZEUh0LQlaVHVhI8C+ekxdFMr-jD$iE>QC&o8zPtl7PFrGWIe4PVJ?1nfH zJFkyLTqv#=Av)ioqd7?_Fk@KY7DL7wpJ|S1UCVse>o4+njjrHvI5DzLchJse{i%ZX zD3F`eof~MDt6q>*6kt~W`$E8&(MK5UB6%ss+7+{AA3rAq;JEKNvBF+W(`rj5cvC{k zg&9X^Yr>`7k*(=_sU^GKlDRDND&-3^vSZ>B=;B7NzI?2P)VqNxU2ttlls4ae|;zh5f>O>f6+mWXbtEyDu{N{TBMT{u4%m@U~wgi+nya)hIJg z{=C>LTU30S3rgi`6D7EBb^G^qK5em$B(^Lpq*Xk+bMuIukX*}|Q9KTb%dj^kV( ze`g|Z*zud?p#1qc|79iGZa~k8+>;BcvJq=`Fx6}ee8-Kj58X;8%31yS?4 z!S#M;uA_ZqQoF*zSRi5R&5C$DbTR?J3b;wXZ=d3>oBZ>C0N$L-=PtT7k5Itm2|k_M|op`sm^J=Lt&Q6c|*oB+G$j6&V&BF9F-z@3A1n&4P?oz$bY_iRM4o z!Nvg;oREDxya|c`Liy4##(>_X@`Tf?ziWI^t^YZdx)@QPi?Kg z3hCHnUjnAP{NmG^-su&+4>aC`?ggWXm-q*Y5)ccQ*v0@GoLk|BBXov7ujHh2(192P zY?dF+M0ygmW~bU?w!3*c6l$jTK!Bk(T*evJ4IPF#gtmyw0^MnT4_OdeC*88P2Vhz8 zY?&C|vryY3`?)p5cL+M)+urRPa6jsZBVi<*Uhr;E+rcp?LT@^>X>oO!u09}9JTBN$ zi|e)QTI$w@$Ng&Nx_fdw90k7@es}nq7Yj?ztn_*7hG6g_7^8!s;*1l#>@no4}Hdzm-U?S1(h*Z3Q90 zZ3%movHro~1`DZIPIKC8(~$UH0et0~-!>f%FHH(akcoFDCzjnOLRXR}xUCAO9b)%e zB+mK`nsgC>BR&oU&J8PW)kfYf<&OEn(1$f@A(eW%?Mu_LBWWyJF3_zdD>v7Zxl0+Z ziFataV;2Ez9v0Wo%?djpt}JAauOl(0Z5HUy1an{VVLtA9{J^%$CgNSi76CWMB3S^C z10jRTkz&GzHsXHp)e~{X@y0=)^UDrMmUlN4JYF}6*c}z{i6s-`MZUhIxut1#a)~9| z?+EYGx^|1e&|AO)Hm4?#+bP9o+ms`oQuWS+Zzp4cZEX3}(g-X=L&NXTnukHapgXxU z16ELZ>h(<~fd9)zV4Ck3D?`?A#N209@mmpuKYZYEOnTpVe<@{wm`Y8go^<*w4$amW zNJ2W$So?{(Vu)v8w;%PS@ap)NTgk_`_Z=V`*@10T>IGv{sWf{C=*;I)^=`a9OK1p# zttt{-rPF(7{vo!!XMfm9PO_^wtd#nTcm-|I3W5$jC@JO?xk7WV7f6d=BLO1lQTL49 zq+c6LDS~aFvyse3E?)FAsBl$B*edC=M9S-tE@*vuT7)X(9MJg=wYOb5y}Z4-{xXg) zU4FJvjFr?L%x~omjW< zVhEiBoAk`6|CYl@;`tW;%wyc+#qP7otE|RYOrE{xAJqR>idyeg-5?`_Z!#V3F=4IX zPQMFC!C#ylE1e46!?A!cL}q+=MZi1kmpM9AYGtGo5BC%gMjQZCLwl9guvVW8W&Vnl zazV}9X|$X?e*oaq>5L(ww0++)W;s+Tc?R+$+xLO!gh4@8$%fpeC4-aK57*W>0%aEY%i#k+FZj)mthYML!KpMp zrsGLNrM~o(LSp>X5m(B3l8~VAk_m@Hf4{%!KvT4Ri8{=!AW?3TFM8WH)?g0 z$}TMAl~aQGn6l}Ei|yBtZY#H-fZ8IjiYVxFj4j(bA;4gRGk@tWJ2bS z67Zc234cYxSP)`Sy|SH$>bz{{X^vzfgl7@J0#=o^6_|>dc5Lc?{|i%`f~ zqPfY2We}OpxL~Yr2lB&hEV50Ck@-%@chH`%Q|mYmH3o#JP$(h_Pa5xtCRdnTe?P|^ zoSy7ji?=kzelke~+|10Tm);9IoT>Ft;R8~z9LX^xnh+k~cS4>P0FK19`*Z)V$8@2- z(g6G_Hi*jMFR{3;m14+#=*@DeI}u?op8)-DK&xh3RFM>+=}6nqUDJO;UF@KA?lTpi zZN69_%_9}{*9$g(ANLA`Yl_G%@IZfm4CpF*$8=Z<5|nK(bNKcs!-%iDAj^8C#7cz} zaMkk0A%nsclQ(ll-t5C&!)nExKtzt!REUNgiin8KQ>J{;7e5E5E~geb*zqXV&Mmyh zBSrv^8q7fUECB~&A_71A%9;5LgyWU6pBV8A$k!N{(dqa0eYZLg>DyH;>l`l|u+ zpHk#IZtQe8FwMv)c@Ji4l77k)Cd4(fPdld2w($s<3LX`PpxZ*vY_N~OK}6VWiny{D zI`_}1wwA9+j_2g%60#mKZ|_ryvhc3~E?p{ScpHK9@BfO{uyntkMI#s8S`J0N$^l)j z8?T~WXnkr$iDh9id4F-5TMg?gAn_d*vA(_qDF()mk7+pCwx=lRJcuXQQ6h9$u+D_Y zuC$AgTF9lzzjk~0{nfI$D28OlYHp{qc_$+{K>7Q5sDjD`#k;JleVc7z2Z)KtK7@>8 zHeNwpH*8V2QRJC9{IW#}=>?#SMHDO?Ck8_7sfQu!F1}A~Tb#SpRl`OnzQ}`^MG*#c z{0z?Bhfk^~_TMEL+@R_x5|;S#ac#wzDu=o_LOqJ{R-dzSh9F@f)WxB=564(oiKV-A zy+Dsw4U7y3Tjn8hmDtg+cDo`nQMDiJh6BLRtVhlH0d@<1;ETbVR; z0pJ$YT6UkPcN{@EFnDLc?^0uNBlV;tCg|d{*rANm`!Ic zS6_Ktq?itfceYvdS#>q4SI9BM*3$EB9O?)}>XRG7=kh+L8X}fV%ma|U9@&I|CD2*9 zBA6&9kapp0CO;o2U7FF$ zWiCz(fpj%tevXO-{E_x%Q$*q6mGgKaNj)bky271?R_@zRW~ObHp6oGUqANBe5}cir z()w-+?IofcHd=)2Ba{$tOY(F!vTG2zmVfoB2gL}b#Nk*vE_&Z8qsNMxDssU=fL|+% ztB~Q(396Cvr@@bzfvWNNsKx-|j*{Q5NHw8~n7?#S-gPWoy^`24M2eI^+?7$=378_i zD8P;mFbN%c1?Jf4DRF|{VN!|Wsi6Z*^J6p>29{;{dq}$luf+(XsTjoTn)Q}g|G&$S z9^6J#Z7DG=UN-5>9}&}?QpoPbc*8V^XFs4%$}!($MP`wJiGMF z)KDh%%*wb%03B>qjBq(%Z0<3X1iDjMxLv0Vek{&{Ic$%~IwT8;)%U8_(7$1(wxdT1 zf)1B$!3ZC+M3Z32Hrq`B*_glUYdy6cVSKe!6{74 zb-h`(t;zgOxdEqDLoC@PLce~a&?{?WX56DbLI>R)`nF36(RJ_*W$TA-C-$@vU^2#Q zsmKQg-PuviW+3AYm%}#8*#&nd7~^2xlF>+voi8m|EBB$F9+`_D9c%18O&=DMAQHbh zRM=t0P2)sdQ%HkeFe7$L7M$YR{P5{N(f}K)&S&PdR#In)FpAc__0F|TkT`WkGZ9w^ zz?bF!txzBSCuyQUu+PWkB?fAqH0*nPRzQ{|UzcVkQnm(spjZ_3D;NaJ@(XGQ-4 zkx)fU7I_phHzt~BDa8Z(wuQHe2D#S7?mVP}Dg=g%C=qA+BJUm08DYwIwoOAY76E>E zGS*|8;wEu+d$(13+UfDrDn9B}p4`*#(lKAsH9RHNwlBww$E%!h&5H;gwrMEm~F&=5o>LKHsSg`|Dp zsD!umVDutJ>pZz$F(1iCG-NRTEMDh z-Dm7BMB4$ik$o;0=X z^u}s63SA}mX+vG62NJA_r~(N%Dc~vu+bY-!bx$&$U?mJPtUg#CCM&&4b+IS70kaw;2(LIl3moK}PM)vY zfrGbPcjP7mQ{IEXYAhcSyhcy`&ro*u*w9Fk2i!I)yJbQ%lnB{P%$BQx&(Ag8hTAXP zEc5 zX3f5eW3S4hIRuL^3L{4ax1~)68WGr|T-LgcNh~`Ni}V#B@umh_dq#B0U1&xe1CFze zB2fVT8Gbek58`Zz(uRu5frLk9EQ$dQFMlQ#z>LbkagoU+?ad$k|B&gm_y@z$SW?e0 zTU9QD6q1{fU2xX!)oe)BtRD>GZL_oAO_j(tuNeWcNI`8dh_R|+avN)PD+9DZsZrW)umi@jPsAQ4bN}i;*fpbK&)6rO88H7P>hz875G&n zlDneM#boQ&0J;)yxww<=*~PL66FIieoNQ=lvClYB_iLe^n3jo&QtPwluLOZ z>NPU7-Tc{jj?4x3eu9B9iqZob`}5qxOBcq0LfEG5%uBwrn(kDfd6wPGIQ%kUus_mr z>zJFE8Q3X+Q$SeYJ5_p;7u{q|1IL+_8`pkMRj_tq3>;$aD!!sXp$&VeIbrVClC!odxmE206~+Ygyw3=(1J@F9MF5QAjo4oubPWX*t83-Ao*k?nbf%9?(nHF94ko;M;CJt|TAjcWEJ3lZGI3LZiHb&P0(i9rT^2m# zU^T;x&1?hFBs(|EAH|U0^?9Wz8gwvQU9{1s&tGtIos(T^Jkokcs0u{XGXRE*3=Zbp z{N)3uCTQ^E=D-0?@Vv(gh0RHNIgk0=Z6Dxf}!=yE!P$yy3 zg-anGZ`!opJMdIY^*-az$%>m!z4;8?VDCAvRQ38F*y7K(rRQ%oyn_9vuulU-WQ3pm zx2|O0I7nc3Fb5F*TD}+S<0K!1E)XoRv+CPBmlx;FZ^imeqb4nrsMd2jb{nnRqzY1GIaI=Ot z|IW0^kBd*Cp;^{_AmZ27L=fn#jbrGiez(sMWRIj@O=7??aZTJTqsOWNQv~#Wh&(A` zF?S&zZ>)wEaWNZl(E+3rTY4Ns>HR8a%{_$RFdyD1KyC8myuVoTgU`osw4&+(|Ig_0I&SUrEHU(>lYh8{zV;jCg=v3%13e4$#Tdf?~% zj}wff&}v0>np{(b!QSvZ)z+Qimo_8MO!lvb!T)GHD;sHuA-%dTl04rxN^@B(e%^vL zepP9C?`hl3+3=)YnaTJ`(Bhz)|eeeMT{SO$6Kf=3QC{9u5u?M5je5gX!Lh%H-WOsOm` zFLwGW!2+zqUVCFoexjl%HW5cB4!5O&thE9N$n@2+ms!q3N~6dyx}Ws(pXVXU(`RLS zfU}A<)9}mg&94#BB$~q}78Ur|ah!WSTU;77rms_{Ll)rvu(uS|+HJ0K_)*{H%!^(O zA)5tGeEC%FPgedNL^=PwwTdXOx?f2!RSVAac`QJ0CF&Ivr^h&%*=?6+&!PiA=Q651 z%4lryk`1u1l4V?<2&5K^q$4Rfe+N5O&!pP=Y^~DdRmbzAj5jcIApH$JjMi;g!gBj z!(8cDslWo?{#&5T0hf&mZ6$?CSb#qW)wa|-3Oe?a&jxFxq?*+UkGNZlQ-&N8mRpzG9%W&A|$5h zj+6wy1H|vd8Ha2gsRnV-$Q@QrHf-0>w@dN?p>E=$akw_o+cjTBoDeg^^O8p#lyvx~LZ_ zi#ml)uVU;o{8O$PWl$y)mwmXk!ky-8*ffvsq%_lsoKJyq&u%G|r4!i?u8x{Z1d|yA z53TRb1Q-QG&bv8OymrD$3baQgri{uCxOS=Px8Z(n5SNqnXc_Nc-2k04HpN!c$ea~j zIsj=SZ4n2jFa!BmUukQ0_H{&_kbyg|b=!Rg&Wf0+Bq|*0pl(|zs_#htiG;YRORA?0 z)tfhO0|OzELqpal3YV2SOxLK|WNPc;E!sqJ5+FfOYwEzUBtOq^XN-lJ^^)oIgBo?c zgt%Up&}TPr?p>|p_kecN*R_r0A#hp0{j;^tdH(YojF?bUP za3PDP;}iIuql}*B5jSV(0n;z`Jcg}i>`A&od&c{bpaJck7xy(}6OVc`jv+$CFnHDq zmPE-sY^e+@NLHcnHy4baJ-K3wZhGPre?X0bL~McB5jGtHqPW?5zh#nJiHbQ&ZO^o6 z^8&YwhR3L)CeCHvCpM5n=4-YRnVG0l(S^!nFUg>jFI-xQzU*qNxWrpU-V%EqP3Qfa zI~AWtyn~)r^3XiyouxymbU;r^5jD)CZZ{*ZTSCNXTRbkOg|HW z%qQ+Xt_mv*iaWRVypgfy+0hn}tAK&X8>?tSq}F25z4R_kMS8Cbo{R4|$kujXasOk? z-TD8=aw0Gb;@MW|O(c-*=AKG0c0ZBYS&{M@`0kp4V>s!I=)?&Xjz+|aJTtblMdXgp zEiXR;L|b?~t%9~zYaGeo1xgpV4m=t7iS8Y-2>AtEk^kws$fC&CGjQ0pBg4l z*3}w(+fq=`c2e%?9UZOe;P56U3*vLtRET+;cg2)Bl$3Q&+Xr%v0IkNF!3LTfAlm|Y zAM79gMyIsuVFGz5VAsCbILKd^IFG;QErm02di$0W*sK0&1&d#46AY7x&CG%$Mwp+3 z>ZU(p{8?T6h_3h=NA|0tV_?h6JL`681JBQ${_e9Qov8YR# z_78*m8z$u0g3n(0j__>AG%7c0bKex&W6GE)trw(4ZsRX^YyeErl+z3VN(@qDXM^y< zr&^}VL2%w4ECd{+$l&tPl2&9``Z82C;IruDIo*HbRx50gRX@RMogZ-dcXLY)kn+rR z%N2*n>xQ2DLSW9+-n2#E@o#|QA1Y>-+CKfNBJvp{SVgx&?L!Y^WYcoqGb`Q{Pin&= zIBs9lvCyHn5@H$u&gdS7KZSc24FG*#$cFA%Y023d0YTlNUn9g{WVQe-R}k7d!5We8 ztmaRLl#Kr}i+ah19M%2k{PT+elNCrU?>wUos=Ts_L9$Wt3nFj3>^dN0q1O3mj$P}k z$M#58sr!r@^%2M-{j5hgli@kM`Hk;AG zHlF4Y#(S{2-a|H+pN%*mhi>A#s$M4y))aQsnRi+Ryd`q00&%CjTUFJ0jp!gJt8D0l zno*(_FSd#UiksOKZF9G0P4~@~ydP;gb$DvF)Ef!y^$!E^b;H%0eCQN=F49Lg)Z&h( z{r*+_UxGt}!0>0Qw$ed=4K+h3=vy#|Xb|=oxN+NMDC(;^&`m94URdY2Ap zg0viKVD*0Zba(_jrEmRv?`)r##kMle&AR3UDjevPB{OWASuJCosM2W%)N6RMfrIkQ zyzv#KPK84`K0Jap244*7h0b9$Cz2F{@H;(iqY%T!-ru@9z62!F+v8uf+Kp6_BEK$_ zOmwEbqk%)c76x&6hI8c7nocg%>+4geW$TJZ!F3q!yf-ph>b~Yux3aU|vo3=pTs!UaV(beoDDT$Dd%h)=RG_8B*u9mr|fOEwf%&r}SKrPm>H#A+}&kabl- zi`=@s5M0p}Psj4V4u;s*(@OXN_xDIjB8Y{SlSY{ov~^Hn2!K`%Y~|eqZB(%*k~R`l zuuQd2hLUBINQR-JUXTlaUF_aPJz+qkhmeEFbVD5a`FmpU>&-i#v`AL^=EnmGk}l@a z``HEYj(6*O^Sdj(d33whvP1VpbkqDWV_a|ulS&12%iPd2Z*1#!$TKNaiav9--XVtf z?erayMj!WRfXHnltzxI|Uue|#Kk94R&>-3laH>?|R0l){+;sHCx+x`PCPaVd%?6`(MjN7Z}~`8n?s zF7yk@qM81Dib~D1gf&TLGgcbNFMN7$R|6ZxH<;G=y)=E-%m4PQy|3;9x%#AuziR}; z?6}NM1oNpVKLH4o1l?k;1c1UJd%^JYIOQk*C<0{@^w_6U7z||d1!&%E zGcXdJB1B@7T(r~=mAsSZ%x{Pg^Af4nyEA!)mMXwwu!zo0Zd?Qz3KMYcjPBjF!Jc>8 zhgR?zm`3%V{)|(iNHv8MIjR~TztM@zxf9td$1X~ej&R3;?rV4D{9A49Wj`>P(MZ`9 z@;8(VBN+-7E-$4T)p0~}tSVm;`ufoUHxLF{s^)UohMGnRmUxE#GihPlb3|+U-}q)N z>^?qgoE@|nD8*^aZ2Y()nyH^ikvHF1`3M!!B)JDAZAEy3kTvNoQPk4I^eoXB0WX2K ziXXg$**G-9m4%L}W%xI?YaBMA+vc+(DF5YY z)c6!Qq0B-o8N|W8s$m7a2^oeMXlmK%FxB<6aYNjjCM!GGnzb5e4vG>r2nsR@6G4-| zllFwfhsy@&gpaLIgOfonUaxO+cWSJi=DczAN$Zt`JMtmIGNQ%zY-EQ1JitXSyuRPj zPcbQeZkZtD9=1j^7J#`O+&P1aV^etz<8t}|xPCiRfh$?hN`{=N0ZGmJTV!raUARWU zu7EUZ7>f}OBhB;O{XBvn?_~0`;)R9+Y6q;JW5k{nLPr0m@bRO*+EPY1S5mnwaZh4SF6>#r4ldoC&u4F2el7eYrTunlto2GtsgbmZjIB3M=7$4<^M=i@^d_y0n6M$!!XK+!2+f+&WSa5*W_66;`wBjRr^@$Mr5op3c8!4cK(myR2z z<|e?6f;8kgLR5_C?K5_uVd_fyaO5rDPqqm~!}|B5an#a)Jp47VTmt=a&{?)iiZ^=# z+4~gEoV5qMHjr~oFL`D0;S&6|50_Mi2Cfa*%^?b_6TlvOKOl1{7!iQ`93shcw50qX7�cOM;D$^pci`^a_> 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 H{t!)csfY^K literal 56871 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%0DP4$=QHV09F}7GPsg|LM|g> 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

1KB(i7@a;=)8TE&$lT(ZAt!mUn$IM$jV7?~^*? z+}bmAM%Lv(oJ=J==xNQLLH)=71JkvrD1U8F_NSfR<%PEV8QZrIDG-(tYcm&$7kUF` z>0UR0qzd%&ro454!g&_9nWD0EYL17DZ{H$R!t9`Oe2$>G7vvoRxrd^G>ClmP&FpOo zz8)k|Mv1*})(}2jppmV=!qbSg~4B11+#Xj0{c3%jw*vnU^uU@U7Rntlqw6WxlInMI6mUUAacqsLwI>U(tYN$tm5^~2s!b#QSb{!RcOR>{} z+Oifs#ju$y{GQuvQFA`#N3GCK*Y4Z+a3#>PHN(8oZF0lrX;pF3a|Adh!;G{a{B40O z=}Xn8tWWzAW!YhA8QI#-;ytB;DOcxbW)Ub6Ba~ALvk~tm^JNIEDQFx)c0gHLy5XaeqaPBiVH~=gZ*-jQ4!1sxzqK7Mw5fo_ zW60rdwS`a~)Y**NA?rqSTbZ7We5CNp0Nw?VSb6@Ye~IA7+>60NvX4Ko?NA~$Yo>*$ z%Doy?^cgJu528t;eeF^spT|Iw{&*Jm?Jl(&>fnd00GAN#M(1w@=ptVZ_0uJ7*lBq- z+P8^jL<9Dv*!z(C7U8mLWIS!z<7SM;{4FYE^I$@5Q2kBz*U2>VpppU|ioEZEOT+-S z9qr96Fge?nF$f;-Qmz>DT^Y@dbZl(d=#yM%#3vy;x_s26+jx6J>gzsIM@|Ui9&~Gy z1mE^TIWuu}V&3%=DAqwtDC1QzI{(-ySfdESfb;FS-Ob&Lsxb>K5(pOS@y=raQSmjk zrRrdGnU1v2#YiHds$088gB2O=m$z7)pVjOfmwBFti=FfIf*nY39JQdleh I{}3ev0C4;nasU7T diff --git a/NewHorizons/Assets/xen.newhorizons.manifest b/NewHorizons/Assets/xen.newhorizons.manifest index d45ddec5..7b2df6e1 100644 --- a/NewHorizons/Assets/xen.newhorizons.manifest +++ b/NewHorizons/Assets/xen.newhorizons.manifest @@ -1,9 +1,9 @@ ManifestFileVersion: 0 -CRC: 1014555239 +CRC: 3775427237 Hashes: AssetFileHash: serializedVersion: 2 - Hash: 45fa3430ee7bea1e8384e57927fc0f76 + Hash: b13d8ad399be3ebd0d2dbea38e0962db TypeTreeHash: serializedVersion: 2 Hash: 55d48f4ad9c3b13330b9eb5ee5686477 From 9039c00908e1cecfff8b2db645b46d26a8b2f6ea Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 31 Aug 2022 22:57:50 -0400 Subject: [PATCH 072/176] Add preventOverlap option for better performance, fixed heights --- NewHorizons/Builder/Props/ScatterBuilder.cs | 30 ++++++++++++++++++++- NewHorizons/External/Modules/PropModule.cs | 5 ++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/NewHorizons/Builder/Props/ScatterBuilder.cs b/NewHorizons/Builder/Props/ScatterBuilder.cs index f1cb00d5..bcbc4899 100644 --- a/NewHorizons/Builder/Props/ScatterBuilder.cs +++ b/NewHorizons/Builder/Props/ScatterBuilder.cs @@ -3,6 +3,8 @@ using NewHorizons.External.Modules; using NewHorizons.Utility; using OWML.Common; using System; +using System.Collections.Generic; +using System.Linq; using UnityEngine; using Object = UnityEngine.Object; using Random = UnityEngine.Random; @@ -19,6 +21,21 @@ namespace NewHorizons.Builder.Props { var heightMap = config.HeightMap; + var makeFibonacciSphere = scatterInfo.Any(x => x.preventOverlap); + + List points = new(); + + if (makeFibonacciSphere) + { + var area = 4f * Mathf.PI * radius * radius; + + // To not use more than 0.5GB of RAM while doing this + // Works up to planets with 575 radius before capping + var numPoints = Math.Min((int)(area * 10), 41666666); + + points = RandomUtility.FibonacciSphere(numPoints); + } + Texture2D heightMapTexture = null; if (heightMap != null) { @@ -57,7 +74,18 @@ namespace NewHorizons.Builder.Props for (int i = 0; i < propInfo.count; i++) { - var point = Random.insideUnitSphere; + Vector3 point; + if (propInfo.preventOverlap) + { + if (points.Count == 0) break; + var randomInd = (int)Random.Range(0, points.Count - 1); + point = points[randomInd]; + points.QuickRemoveAt(randomInd); + } + else + { + point = Random.onUnitSphere; + } var height = radius; if (heightMapTexture != null) diff --git a/NewHorizons/External/Modules/PropModule.cs b/NewHorizons/External/Modules/PropModule.cs index 932b1bc0..1a7c6473 100644 --- a/NewHorizons/External/Modules/PropModule.cs +++ b/NewHorizons/External/Modules/PropModule.cs @@ -139,6 +139,11 @@ namespace NewHorizons.External.Modules /// The highest height that these objects will be placed at (only relevant if there's a heightmap) /// public float? maxHeight; + + ///

+ /// Should we try to prevent overlap between the scattered details? True by default. If it's affecting load times turn it off. + /// + [DefaultValue(true)] public bool preventOverlap = true; } [JsonObject] From 2967565653ba27a4637f2d4a81405047f72719f5 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 31 Aug 2022 22:59:31 -0400 Subject: [PATCH 073/176] Remove redundant cast --- NewHorizons/Builder/Props/ScatterBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/Builder/Props/ScatterBuilder.cs b/NewHorizons/Builder/Props/ScatterBuilder.cs index bcbc4899..b8aac0e0 100644 --- a/NewHorizons/Builder/Props/ScatterBuilder.cs +++ b/NewHorizons/Builder/Props/ScatterBuilder.cs @@ -78,7 +78,7 @@ namespace NewHorizons.Builder.Props if (propInfo.preventOverlap) { if (points.Count == 0) break; - var randomInd = (int)Random.Range(0, points.Count - 1); + var randomInd = Random.Range(0, points.Count - 1); point = points[randomInd]; points.QuickRemoveAt(randomInd); } From b987e7833b698f737a373e466f00c464fef74c72 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 1 Sep 2022 03:01:34 +0000 Subject: [PATCH 074/176] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index 7ea40e04..328e550a 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -1389,6 +1389,11 @@ ], "description": "The highest height that these objects will be placed at (only relevant if there's a heightmap)", "format": "float" + }, + "preventOverlap": { + "type": "boolean", + "description": "Should we try to prevent overlap between the scattered details? True by default. If it's affecting load times turn it off.", + "default": true } } }, From 4d5d82739813547ef5af651c7a213a05fd883cf8 Mon Sep 17 00:00:00 2001 From: TerrificTrifid <99054745+TerrificTrifid@users.noreply.github.com> Date: Wed, 31 Aug 2022 23:21:54 -0500 Subject: [PATCH 075/176] Manual fade --- NewHorizons/Builder/Atmosphere/CloudsBuilder.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs b/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs index 8b301488..a3ea402a 100644 --- a/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs +++ b/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs @@ -258,9 +258,17 @@ namespace NewHorizons.Builder.Atmosphere material.renderQueue = 2999; material.name = "TransparentCloud"; material.SetFloat(Smoothness, 0f); - material.SetFloat(Mode, 2); material.SetTexture(MainTex, image); + material.SetFloat(Mode, 2); + material.SetOverrideTag("RenderType", "Transparent"); + material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha); + material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); + material.SetInt("_ZWrite", 0); + material.DisableKeyword("_ALPHATEST_ON"); + material.EnableKeyword("_ALPHABLEND_ON"); + material.DisableKeyword("_ALPHAPREMULTIPLY_ON"); + renderer.sharedMaterial = material; if (atmo.clouds.rotationSpeed != 0f) From f5b40db0b2e5f21a4595c789201530f0d5cc68e7 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Thu, 1 Sep 2022 08:13:33 -0400 Subject: [PATCH 076/176] Rename enum member to ghost matter --- NewHorizons/External/Modules/VolumesModule.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/External/Modules/VolumesModule.cs b/NewHorizons/External/Modules/VolumesModule.cs index c2920951..d144b595 100644 --- a/NewHorizons/External/Modules/VolumesModule.cs +++ b/NewHorizons/External/Modules/VolumesModule.cs @@ -196,7 +196,7 @@ namespace NewHorizons.External.Modules { [EnumMember(Value = @"none")] NONE = 0, [EnumMember(Value = @"general")] GENERAL = 1, - [EnumMember(Value = @"darkMatter")] DARKMATTER = 2, + [EnumMember(Value = @"ghostMatter")] DARKMATTER = 2, [EnumMember(Value = @"heat")] HEAT = 4, [EnumMember(Value = @"fire")] FIRE = 8, [EnumMember(Value = @"sandfall")] SANDFALL = 16, From ae1e74fedcc3d62e43b26747f710e889cef78068 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 1 Sep 2022 12:15:57 +0000 Subject: [PATCH 077/176] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index 45726d35..155650f1 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -2565,7 +2565,7 @@ "enum": [ "none", "general", - "darkMatter", + "ghostMatter", "heat", "fire", "sandfall", From 5ef1b5b7bee790813eb8d1f10697fbefc82eba64 Mon Sep 17 00:00:00 2001 From: TerrificTrifid <99054745+TerrificTrifid@users.noreply.github.com> Date: Thu, 1 Sep 2022 16:12:46 -0500 Subject: [PATCH 078/176] Don't add sun override --- NewHorizons/Handlers/PlanetCreationHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/Handlers/PlanetCreationHandler.cs b/NewHorizons/Handlers/PlanetCreationHandler.cs index 67700c36..ec75a23c 100644 --- a/NewHorizons/Handlers/PlanetCreationHandler.cs +++ b/NewHorizons/Handlers/PlanetCreationHandler.cs @@ -590,7 +590,7 @@ namespace NewHorizons.Handlers if (!string.IsNullOrEmpty(body.Config.Atmosphere?.clouds?.texturePath)) { CloudsBuilder.Make(go, sector, body.Config.Atmosphere, willHaveCloak, body.Mod); - SunOverrideBuilder.Make(go, sector, body.Config.Atmosphere, body.Config.Water, surfaceSize); + if (body.Config.Atmosphere.clouds.cloudsPrefab != External.Modules.CloudPrefabType.Transparent) SunOverrideBuilder.Make(go, sector, body.Config.Atmosphere, body.Config.Water, surfaceSize); } if (body.Config.Atmosphere.hasRain || body.Config.Atmosphere.hasSnow) From 3ad7f470710ef53c7e603ac4db1fe80c854f64b2 Mon Sep 17 00:00:00 2001 From: TerrificTrifid <99054745+TerrificTrifid@users.noreply.github.com> Date: Thu, 1 Sep 2022 16:14:02 -0500 Subject: [PATCH 079/176] Just use bundle for material --- NewHorizons/Assets/xen.newhorizons | Bin 57078 -> 62665 bytes NewHorizons/Assets/xen.newhorizons.manifest | 9 +++++--- .../Builder/Atmosphere/CloudsBuilder.cs | 20 +++--------------- 3 files changed, 9 insertions(+), 20 deletions(-) diff --git a/NewHorizons/Assets/xen.newhorizons b/NewHorizons/Assets/xen.newhorizons index 3fd40a93a49d6755e6641f0a1b7e1968330219d0..de0815e92fc440f9032ee2994f484811572edb5e 100644 GIT binary patch literal 62665 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 literal 57078 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�cOM;D$^pci`^a_> zS1Fp`5%(32?dc>pAuK4{zW1aQQWA$3SD=ea4416@b z`zjoa&Ep4oKhZXSfs675^1Hx2`yrM;+W*;AcRuaTp*s%fxFo0^_oy;SkTPN>9z%nM zLP*xW%$YRw$?r;cQC~U_Z*~>0TL!%SXoV`(qna{hYxV3gvM1I=ZE;>(6q>2ZZ|H2~ z2M&ps$VRzi_4c><{~W}Fu6~puo2X}M?YH9GD^!hweMeLoH*FGAcj%+%!{)8ofms3k zv&`kDi?6N|S=mFaRo+yDRP~1;F1=(G z5{GR~uA7inPf)w{#o`f;z~`2L))Hin%o3!p^_t8`^7qGsJeZ}5c{Nbc8(a4-D+N2D ziv{vc9woX_?0R?MYj3JG&5xD(;>`u2)B%WcU3migSmk1f#2Ubek*#NF9c+o$wIb;W zfeW$!ZKwH6f{8t9FFyE}bdQuPkPa%;Of=;MkaTQ6ZRR z81x3a+p*U8av`dfm{)3Cgt&i3m+UC!hcH?Yd_=aBRJYJa(E{%V3mE&b{fpwScb#Ts zRYc@%15ZNHP%5(623Fjx=&Hw)Bk@CO(oY(~x9=8BjSyk?8qB}!V5+hxzwJoubLqss z*f7-txty-UP}z@F+^868mtJx4qw3(U|C~9s>?#_}_B7*4NiAL#2LLHR*1vPnAE7?m1naHCg6i~ce>2vKI8O#?g7v{;F=895 znmzzGpsd7$@&W@2870X?%;|#~+-mDWg~q>J56Iai8VfuBkk^_^?I1DYX(+E&!w&S>g;fro>8ZZy>s))9z~>JSR~7PZM~ zMyB6Z=7v*;RBH#BIU;YG8`_SYS193Bf_fm?#Ha9H`|t)>1KrR*2;eDwKJ~!CUFQ?I9YVR`^o z&iqNFeIWAbjJ5%6;;bEHlAmyIb*BCCxS~ECq4dJFBD$0PAH~79U**~6T*=HuSCE_L zivh0#>z{-IC;Xp7#ZVK6{K{n7e9WO)fg@4FePPqLl+NOg7p@FQpjA5&n2Nd(NP9Ld zPzLKeT-?t0#o1QMqLAKTJpmNEm@s3Dyb*cXO8aaA{SaTS?DB zCW{JOOM&=bkU_AlyJH|96BMp(qQW?Ud;*F}(7nmEEYdjZGrHf6npaM^NDzipsh2XV zbEWrZwLSQq#w7?9Ku89*{{QFL47VFTHb&wUxdXo-lwdt2U-DXM?~ffJqb*F4{i+qZ zV<|(X=!Y_bptB^uhNvLq--*=tbX534GiW~G&602sYwF-a)f!vnyn?3DwG4^eE481y zJO)TJy%oQPlzK#o_)($M$!JO2n>KWVY(KPgE_wEgxDs}FI*UL^>V>CXfZ-m42_W_> zVSyW1((;Q5%9}2!dG)4fE|DbKno6aj!d^z*; zTFlDEy&AmUzpS;AjX#)oo}~_iGPb9}`GqhvCqD5;06UPA$q_mlTh*=D3oK&nEg+%& z>iv&FYSyp~#^5TDmq6swR%sOYK`@2XxyYJgsMMg6kmp^s^p7q6^M$bvst(28{O5re z&MJQWIAQUAPVZl71AqsVhJbj@&v7F^Ydrbsfu!|=L-|8s!C(V>{B84q0De;;RpuV) z6)2(ADsbxpA`*Jc;j+$oPpGH(48W|2a9T?rFhD|G2?*~<)OGibQDiuo*muCU{M&RH z)K?$+L7FxtB8m^ATz0oYcF}I2yyw2S^eC9FWo7jCc}(nZ#rj~G^1V4xIu$Wb`PF9Z zlPBFiqf?u(+DEjxLUwA7~d2KAA13X%en@Y*EoO3Qxw)5Eh${dYw&m#dU-c0_CcXnkkz`; zWC-s5*+-X=$rlc4#s2Xv&V@DDfs}z;P36;Hs7Qc7b-`7Ma#l9g+-=?}HbAWyN+qxiiX#SyrmqFA3^hhlW@>+?H`)Ks6OIa`DFU(Nfp30VAV?hVyZjblbn|K z+lQJ@rSZfs+v3>352zJEl_TCXQn)}8s|xM21V<1qCfw|Pn>Bm#Gu#dmA0tRMChse} z^T!NR2ace_x*KCuzlqmbCM%SU=)h8H)TZ|`WGTinM+>~Y{;va!+VZlBPT{PQ#T;bz zmSiMusx}Jeam@e1)$wh-bLJ?6bll?=;^>-BRTY=mPOL|y)=Iege?4*u+pT0}@uVFT z8(|KjL4{RQYD(PT`usJvPNHMUga6j^%r?Z6-)2^JRk0n7-xY42_6y5}a3@U^z?DuS z>ENg@=2p47Zi9lNHP?h^sfFrEpwSbEAH>r;pHnbo*aJA+LcG*z)rXiv;R|oWRh|l5 zE~l1f%N4GVIBt5UkDb}xfN;5wYEW~$Rt;X;ryW!PJ$XDkweN3TOzAoba#sQ1J8^@D zoyDX%J`%QUNS+iN!d|S$b9x<=f*S`f%9_7TMQSpfi}B8I{1oB7-p~IM=f(@dk7qZ1 z>JO+}lm1ve=5+-}y&Oq^yoSedj6&Zu0MGIb{_@*+egLrCIy%K(pdAB;6tm!=(4Yzv;SLQL zt^ygdiimhAQ1AxJe;ujym2(Zt6qv`g95Ad6RMNB+Hr_Kx$aHTJycxe)`@We0l9zGQ zfS?)31!9V@YAI{VIUXLmGbdo`5sYvqB+Jtj!IAqfVUsBRlhz97C%6X?Uw8N-asFWP z7y+D@0#CIpvE==3bU? zD#AAvU%IrTaWGgrp?nrrWX9J}5aQC5UhbW&wo_%W<=v=Uq-~|)&ORve`M}cSuSj`iJrdUB;YdmI9QlevA7(Jt1jJj~3Pu zePGebnXWr#R>VkPvH@x=?CLuw)!H=@fV;*!JY_MBfd;oz z+QPBpCp@DbO{gt=b?1@M$wl|408gf?(2!-ltzc&Fg_FVmH@_zuvkkLbN@HCwg9-Jr zu-}wg%8TLLZ~yDPp<^JjqH&4*YsJEmd?SN7Z!o1ge6+{PuG3FAK1-Ci zKF{Kv=O<|WGAl|rp2=XF$>H#_2snCw%1>mbO@6qU?VK(gl0}wCDL?i<7%%Qs^a}g>>t5yh+O& zdilc?nq~CP1EANS3r)e~57PctY`3ud$ot;#JtHxgTu=NvqjF&}UV1z)*xT}0&FA(* zGwcKZ2itdAeh^C65{ltVgn=ofC{ZpF=rKJBG_w*xm$%hQqRwmZ{OSPkX3d^2_yaO6 z=dgkmL9;6hub@aW2KD7IiPGJ33>^O$KURb)LSxxHL?^A~tvKA+zvf9iQx8`r`(2>f z4GG`)U9=9N(gqjT8KIVu{x(pUo_^940yjjbIpgdohOuU&z!Lvd6QBq%R69K-+N#Y* z(p4E|nQ>ab4k2@+XudPPlh&p3Eq;==nLa?k95j_J`0w#eO$bnTTaZ2Iu6ERSjEHO2 z_tnGUQ^Tk=v@i*TaE$gT^E?!)K+z?9ojN(XX=GJIM&J*$NRJ`%db0`FZGomQ)u#$e z!#)-yX;4JwgWbp|05O0|>ShT0w-yLuNy9iLe{Jl4efhlN6Rn&1=clq=arRenZ1Heu zTE3r*O)^>0uM%e2nX-@S3Xe&+SV$GOwF&Q3Ql3EfZd;$&RfGF)l_SD4`k6%bhcNf< z*rpT78vQVc@?kzuV4x$k7Al@;rf-f4@@(ctY{3eaEE#C7CcC=|Tmj_eJlgcPw$LIEYq78(Btw!!YjIctjpKDnPU03lL1t8C0TYkd!@z2!)CN+X5GS|`t%2@ zJWv~mSYD)vLA^%r@5Dij_2R&UNYi)ntIvmmQLbo3?A7m0tv`a`qs~IvJtl&orDL&%9s)ABJmE*9c`S-6 zsXl-b124L?j-JLw(xn9)R0E3cSo@jf#*gPK&R%KWSg)p!1qYpig0of{x-g$4rboC; z0_tD*n#K0_w|9yi8v1ZDNS@v34g>(z_|usQ8ZVLskKE@qQ3OIGsjOZi=K7u(igX2; z$N6Hw^6?pDjo=G&Svwy5m#BPq7x$p%;>K`wHV2du6NGtH`=br&w)Myz)y#mA8|R8XIYG^+YXIq%sT4K(+)uoo^q`(0raV^;n8CIrJE$H@T_pB1OLtR zoJFXz*&g6=Y+_MV6^HauOO1R*$ucmnucRY-12aq*MTy0og^WulPz5ZczqZqn6$TWt zdiKG~UQ&_zuAeF^d(Y>RsT_=QsfFjYwNRF#{9!+wp_9o-2S3N$mOJ!xJdcX7;|+&u zk;#CtB>{n?kf#TE#=<4ktBGUA8idasbN?9q##A{rIx@GFKIv;3NTOQk!tM}~OtXgI zk{KmoVB5j;(%FahWVVQeUd}!s_#|7EVe_@VXVBM}d6gIt-TW2d6y})e%J9kHpA6?Kp(9RJTr_4B$Ci3%O z(%Y9$(1bkk`uS1iQKslQNsCB#hnAW?9GfbDbZeLpxxPK$R;u{LAs24djR_G(C=hZLrvx2#LaPAV+r5Nxbps zxAI_afU$!Q?o9$VGMraA8jM=R1$v<3e)zXh0C797 za?8i-iwUiqe1;!qJdu%*EawEN4)?5T_6J}F#pF|*#Cf>`hhpv+3JeEe{jTi=7x55vnLqR*XvzF2<;SJWels$QCQdlQ1Co_ z(O|$^={*Se(XEy@a*XLv3XGP8RH{f)93z`WIWiG68LILCV7^;zO3A=k4h+-OxD3EO zqecMC@`e$NX^29vZCZr-_nMot3z-|Ok;uVdr4K!;Pg{#)9ChsFd41KXwblkpwUnzob6)rzqL7bTAAD=-B`yh(OWXe@f48Nra+*koe)-s*7{Vpi!m~k;b%jf-Ub+`8vzArIiG~r3og6&`G z0R^@e$n1kQwbD@QUN-Tdjd7~snPpb1yKX7Md-H7BpB7nU5U>hqK6A!kqxQA8$(m;1 z8l=;EoxtngxWsyO?E(P{6E2GJit>e#ukTmVK=sU46iDrAEz(48P&aE^6&^h66mUdN z0pCKXH_+Kqp}4iTp1S^Rw8Lf-#I(W~^Qvqo$}eeN-fDw?X*?6o)7FJ7;#!?L>$N`} zw0YznFCP0aOb_F?Oz3b9SFnWnpj%|T)ep%p#!ct_C~#e!k*c8c6V=C3TtpkXw~|S{ zB(2!k(K^HWL?U+{i=NjG{NF)>&sD}`E~^My8n(uX;sjFQkS0poR~#uw`dE|-#FV{H zkp)8u1FF2%aIlpRV59|?GNNAPFYJzuj<@N#^?ev(ry8_BuMv{-1rM-ncOi^YP{iY) zm$iE|hi5M&WC$Qz6iLtxg#9*@nc6u#<1kpm7!un|QRkX`?S)`y7`qT)r@~N*Rd|6n2YBObBM!nwA2kxqj1)(DUz=M0Img&4GzTVF3!?t)= zYzg@|MBw9XyjpJS>q_(cy;RX_i-3C|R@oBS!@CfB_Xn1+Jh=|gG3`QC0R*rNXVK`I z`n@AM8)h)b)TZYGL~-8J>LL`el|O1}{a|U5YI_BHsVq%rusp1h!MB!z-CYp+C#Wa2 z6MoL!J`r9tYMU$T?|8DwI48(*w?QG2hz+kO?QSFM&M>sVbkH>^3VG(;^2N}8U@Nd0 zUrf4y$MCv!LJ3}c#Ac3=;^TVLV<24>j+XHeLZ%wCY)aB9!7+>Ec_|(^0`A0XF`ljKG z#WS9@ytH-}l13luLGj@=A~sDLVm^NE_*dyqi*aC_`y9M9Qi#E~skb_DHF9C9i-VgQ z3wzm~F%D13ZWwH^37g2rP+a`8zjY-m(qk+-W78(ahbMcs!leO{IGRLM%x5~AC~+>t zK6e#k%G>exixA@y@3E(IgzSol5r~~~EY@-F`6APT&1@JyU-k@fMNMzxaT25-iC^PW z&o4*PK4q|w9M6|i^g9iEC8}~oY+n25gjD*ko#RFeBi~!GjmeIF=L@ZI3r|AJB*HSg zp?bO|UkhGR?q5HG{R4?j{m|rPK>40tKY;IDhJ9F1rsx0DFrbeKI9ag=mwAC zvGS2CaUNLB(Qe#%@q!ZzA(=x%T34Oq?ExV^zdGMxcT&L%QPBOIb?sS5qhrK4%vUXq zf3AkZ?p1g$!>R4}W947;Z!Ma-3UTK}z~U3XO6Lg7kUZU{Dxl1aI- zLJp=2!~C-$nU>GD0I#(9iBZjcQ6mMccf8a7-GfZwe(67ASz0J1|5iXAbl1o=VCbS-q`>u|x77Ig8nxA6Txm;qX9XStcZTW=VoFbkJS4O2lFNWO1M)5|l zTAqEX>GN5L@iRDqPD9=3mU2%-U32vvF*fTww{6cBPED6VYzF3JrYEttc?;D|Q=fdc zyUjU?x~}ce^PWTa#6y;hY?E+Ty!o`n7lz{tn03}g%iSJ1kKFT%mF&pn1p;_Iy0^wD zxH3xNGX+^w z-#XN6VM5)y1YLc>K+LshLsXFkPKB)u-oYZ^$(RqZgE3-l0M30M*Cn(o zCDaCdgL*E&+AmV1k&Lc`1_(dQ4Mhz+~RK@;XwzMwa}!qfPEqrlIEFv z{tD&P+XkCcvVDr=uDQcJfef}OzvQVuZk}MB)(T2Lt#_+|KZPxm0giMCfPm0sH6j^L z+5vBbhL2XDk#sNHVP&g+Q@!B4NZj3{F^3?>8Ps6b;f)aKJPmp{B4iA}Gs(Ray^rXN zO_-DR3nvk=f%R8vDvkk|q#DX*c02%$OGGaTK-{{iiZ~L$X~b=LA-iFWOYj2Rr7ee+ z*<|rv$&vD~S00jFe#du-zTT2lv!IQtT(EfIGo+vocM=d+QF* zy}%vqR;f|(7Kq*&(MYBugL6n;n0p7Kx-si2J?uR*h#dfN^8$9NbS$uCJ31&e2a4u? zxZXFm^Js;3AEO^h{twjINACb`uq;KHMGMuvz$w0Y3>)6XVO} zLl|Kw2x*v>`MN`K(H{7k_d2_RghG@kx?oSw3BH6Q)!~9ro9r=c&qMS1OgriAc>Ym?2wOezW`h6we%m+&!MQS zS^+I+Tj(fH{rC5(zEd(|soJz^x06LHZXCMs$B9$Zh$rG7h0{AQD-u{V(>+x28e1@F z_=HCh9aeWt!SwhloERkyl*;KD zK*NyGc6hkFG-su(oQPqPFs(VmF=!9JZk`v{jl#ou{DK7tNn8Gj9ktS*&RS+M52Jut zq9Cg(4e&+Qah>MrB8v*gJ3D5L2=_CqfV0bE{Md*iz4pFUU5Y*Zx{{|M^j=I#B{ z*Z`=-+sl+l@oA@3t1$1V5g*wpZ(Z#&r(rZyp7(%7RFFTko%g9g0be`y8?a z*Pj(d9026T=FV1uF%37m5H?4zZn#r+b0oTc#{9*A0?3}GXL`H!5)i3DBw0zj2-#AO zfobL^podtnl$oHWdHG1tuUXKU8J0F~`Q`U@CqugfK`dZ~fqC~MgAlHXP>M_#0HEf8 z3U;}_ZR5+%L$>4Mi>N*SX=BE8)H??bWPPY^a4fPbxY=|3k@OGhDm1Z9|Iu=V^zy5U z@{lfld-Vo{7+g_UNogiPW&iOA4mQ9|w+5@_ z!0)B#Qp5eiN+fYvK0UzQv2r1Di?fTz>!1e9{>(Z)p>zECIrmXZ)*P1(DqYcGn}$m^ zyc&o9>cf5LB6H@GvdbHsX68is8fH~`VG_08li}Z+1G~rPvMct#5-a7}#xFGCZPKvmE>yc98#=&ZMZW)TjgrXK(V_f-2P1hC9x zY$s7=t*q2=Iu}CCY?qSFz;wMGGzXiC3Rw5uKt4X}Pl9a`AFIbl8nuo@VmscTFfld; z=A9Gc*Sv(Vc{F~WeK!ccT6pe=&3#BN2V+;PWboO|-GMZL6{!Loc3MFMAd6s51(e-9 zDPN!;6wOj0-g|*@F8^hfPPorHMw#S+FbJKgmAoUH*C1;3&+Ab(+(p zrrKakbO3xtl0Qp!DPI#mTdnOk=xnPcR->;{Q%>m?v$kY;&c%_J*&|Pj&_{j+_XgX`!OulMO5y*xQ@HBLZo>> zaX1I!D5JzxdlFyU)>uuk(-C?99@m1KSoclaM#!L;AK^lV<)%ZUTG!^0>R=Md^oyrL zK;r>%{cjt*sAG}(HT?-psEXr-7*10O+-=vPRDiJ@U-?`UrY~@U^NJ=u#y`Drx%Jjxnf|a&0Q#CmFHK1+n4CThKgvCaKOvIf?0O0^A8tHnfY}=# zzAMDUJ*PSxk+b`kAc}f*<&BMdI|Y*khc;^U3D`{aa^$P=KH3L~=~eL2_7JEscRuu^ z+8)c~_JQ(-sAg<5xCV)G};v1vPaIq@pvYf|p! zR*29Ht%3i7-S-2hcI*dXqq;U&wv-XytrBu&o)Z#sNHMF-5WY|_E6lvh=<0Ruj9reZ zvUoM;+|}wI8n!`*U+dgHRM!@Rt_qns`%{}5BD*zyHH8T?`R^TNmY$+Km$)3?g&&7ukn70?*PjQ5w(OL)ZB}y==16h{qe3DR z35Ndnzc=1JjbM@IC<%DE59gey=OWm2F?H5s2V}3W;1?*A|9(e>_^B#UFER)8GZ`oC zNsMC&p~3y*2{Pt+SG&L>k`PCKs0y&7DEd>|*Y2-^naV_`+M8rt7Fc3Wtf`L$Lq#1T zt{UZEE2&SNuvdmwRjDu<;;6!9k)$uFsCWFrCT;ve^=Gwdss!j1^fs39&0n zzE#s##h4_z8Wl<90^>7_MHa__!u9r%ctE7KSDVH@e%T>p@E*$PH1h@ydBmrioZjPU zTMg;d#_~iTUUYNWrzuSM6R*5ylTR&0ky-sB@NsakPOclwKzltYJdcZ)WWx^}&DyV@ ziJoac9rEWC4fMSrAk_fU!gHO+oW?l4U^CfFSho9<=7OJ~<^Us#t!7FlJV8v=E(B>L zjO7;rN(WvxMqkFW1vD~(dH)vP*b_7QtiYUOqgP}_;&LcByu#G- zU~Z!$g*IC;nVnR~z|#mGMChCq-TfNxx3lX<0d+fttBROTSo5FFGzgAaF0g**!;eNz zXJ-N#Gm6Y|4EfMsx)I468$MAouKyq>vqQ~{%{{nLoXy|&mk4oQk%HEGK<+X;tu^D1 z)*kaY_{L)0Tb_X1xBAmP`g*f4e-wv-s&LkRLr9WZ2zhs^tRw}``rlM7dqy;w){ zbTondH7>BkDDptGDc<7*p{qGR6 zWM&=Rga3j?&Ns zkOC~6cV{^IEk4}+HI7QGQrR;rDH1WkDP?n9A``~@ARv&KjS1w3y)KLJ(rffXplT0w z7Tw>_lT0B2xcCuY)S^S{Py8Tg-cj5d!%klliIZW;SYM-qJkkb!Qk8^^wR*6lXmzz+ zRJCg~;?V$2U_wWPIoQu?Exm|OrFi#f-!mlCifKfQp&tlRER2z2G@{s(w^HtxX3R^H ztt6_-qc5x)HGn`2!@31tUDuJ{vhQzdp{O2j9j2aaZMId@2|YC2#uLJj z|7%>Vt?7VdYlt!cE2-}4g%M@zWmJyI@VM^e0WM0jRN|!ED4A?=p|#xYIiaa2{u~3U zstX^_UyLftd9rIXtW}JS1l`SmB4J>ii+rZmZHH7b9Gsgbox{Fp-5TcAey(jeGOI-X zLd6JsB2VzDlcrw(NPvDpr7wGSno*7M>^OV6OBb`&vo&q6-ZMcCm5tjC5-$28H9Ha+GY(zD=Y!2C|Yp;gqcb`}=#*AtCI&Wf~B{1ja*@p5fkci=B5g zlw5w5Sx8dm=>Udr7q68PPU5I6M|+o#uiK~EJf@7INrRE;vXNEL&lnT03@L6GrV@$F zCtHtWuXat+%z`3NKOmwb-f$?eWT_{MVMQ$02TEng}=~BJ`L*3FEaF|2M}n=Gu)< zf|9@>7-U6#QC+Y`o;p#$sbI>t*)#T6?BK#ufYXFin43DDeFIY6Ff{I2qz76Y-mLfPV(dNVLq2yO9ST*kQ^h z6FjvqjU*OnsQZk3JLQv39$d-W-J`a%${G{1H2_UgA{2>Er-wD#blSLxIFZBmPK3R6 zx~ulhuK{=9PC^F|uvAr_CuYfZEX{f>PGv8uKI(Nq9MWW$by*dO4*d8sfzx-MH8l_^ z-E(&qdZFZKU+(NFqmI-Iu?7#M-aDo6_t^^=eNVLFpqw&?Ri^`vd(0#eV^Xy>GA_1N z820c1sI;y|EdP`#cn8_GuxFV3G8kfOVd-R+Z1`UvLw{HHBWOp$y@AB8Nv{l+9_pBCh|$`wM=8knYI+!5O67Y?IaT1l~3@87rk*~w65QG8&$ry!%Y zIW3a#AfBdiIp`PA<)oyY3=}5H^7=^Y{EzJ$VQ?){ZR1*~V+-(H4921U#8^W959kO4 zUzv`9}9U>z-wulw$H^R4`wDH4D_zG4l?gPq+goo7Y6@N_ z(sZ5(@q|=9$OaZL?fcC5l<3mJHuNW0(orB)r(*P_!AggnZ9gTV?GQv?elAXYto$9+ zX2u+g|45Im$4oV=$xMm`Hn)8;J}y`)qlXakVg{l=5#%K0cU40T77;P5Dapw{p>+#MQzJWhjZF7 zz8geok*jaGWUKawrFRC^m8m+nQpDSFd++VM>F@=lg|N%g-(gh6M71g*bvUz4IZaQ7 ztEE-yTd(Z|o!CB#uf$4`ROm`F8hvLAYb>3Yl<0}OvyoW*?cM}U8DAX$ zfi|+Sw<5a;EbU7AdW%tV=P!QGC?|wE`K4F@mb(uL%c%kaL9G! z4if+}409jXrPYgnDM-6cpu(4(%F5%%B0BxgP75|x7W`3VztTC>>eeiJMsAU?sFRHQ z@@)r<Lou7cj=^ z6P`dolfKs69O&%o(4&XVqgru`&46=uTmmfwCjcAJa+Bl=M}lR500)@XlKJB2!aiFV zU(~M_rMXpU^jd&&w5}|WNhg-Oq_Yv35SM;uBEj^vD2xz@yf%P|Cm>R$aRxIZK(7Q> zH*=i9ArPz9KKQh3a@hGvb2hKkHw~wS96!;o9X8OJ!APO*>9z({DrGP-2Mit^G3nt^(P z@-pmbv{mJIIWcM5i0?=7Z zOJ$$rD0LhM_t9D!_L+vUt$P<5_A`%$kpC z`}cpz+38>~mTlMg3(QWSf#bmAHC@37q~%ydYI3Y#mw!v819`zPKFpLNf`BM=Ry3S9 zB$K{GBSQy{)*Z}B1t(RNLRTca3Z6IK14R}+aHb|^fF?mv816FGP}e>;6`xHx0uK}@ zjUbIUwVOwp2m2Q@7wi(Ko^N?evvgDh1^ejDshk-4*#atL@5b!{ny50jtSFK63jqO| z`lr#VIfHc?byA!f_6cZPpkpq_o|k!@sXBREg+fhjD`p<@RAGX`Sh$Z8tnXKJ10DeOhv^sPBt-_b=dHUOsnpkoYp9nyy}ndg<#v3NnW6f z76^!uh`~j|w}rjRFzX!WOjx|c%!9g`hU}(wEwCTg^RWkIP1c*&o#2!t{hnC!ha*Zu z(Cjhpz*TwhYD)qDJrgU^O6dC!oj;kUK2n9f+O)x7mA+h?3Y#HD@=qvhZ(q|WJ7uCH zaNwtg(SAM*{qj|=g!tcPQ@0{b{7Erk!Pr1UXYGWXCG&@4(Ufw`Z4pWFeK-cpc#kfL z832?j%hP#}BD3OyaDBiyjF&HmSGv~b((&_SY_hkoJDQ-2+yYrwY)3bf4EAWWyUHA*0AVXx52#AFmV{@*cOzI8i+{Vg@>Qm`?$3 z)lkd1u7yG_O!7er4SZKm%lO78ix#lT--SGlz2TO?;D^68ZeGy1+jF*nHGK&*2$G1e z2o*u9y|BX*%u1^onQ1h=@cXN{pO9PuqWCg=%Xk z}?~u`CSC-llZT&6`aFqHcH#7ob06 zP33#3NxlSabyxIBr@y7Ptt=UwkE+#~C%=@oUBsKP<-Q8LrS;>t?Pd+W@}lTHgH7xjKui(77``BR3f=NXMr zi2Eyvi^W7o?_D(MYG^XplzczntPb_fIHlpN2bYjlbA1ZvUiB~IEB?<7UaKy1!GmZ| zt}Je!1}rEU7Kgr4)%M@3)OgjYBuEC^@moIn{|1hh6VmpbCtDdK-5!hYio;pYs+Rl6 z^1L8c87?d*<*kR9SDQCUH9IjjOJG&t%d<50&pWW)bS$3pZ&xa>xRlmakrd=VC6$@D z|B1gRD_aKbFoBB4)X1$1!wEO-yr^TQ!#KVb^c~g4AAuJh8`-Q$NeQfvtVLpak$YB0 ziZRZkU?6kfNZsj9t;@X7Q*kk#vR2--!UHdSfR?r`9~uW`gj2ILM!qmD6_CePhPz4t z4Gc{)gxnTS`EHrDf_-!ECAt+WiY0iAxXT(kfuAQBOLTwgBrwBh1;$gjQdnHI^Yw+o zcvJfx;%d`gqCWp>HYJW1abR%DgD){^B?2#K0zZ%7u^@>fdl2i~OVbgS09pOt^X7O$=QIKfQ&L4`xZ?q+yt^u0L@tRdP^x z^Inj6ub`!auUllO_h*|~o`l7it1kx`n?ttR&h5SN1{CwS^I`TCV%%9 zU1*=uiQn7v?m0rTEyFGORTPVSUU(E>z~T>hE>6K?5a`=VAXGy(P)NUk6fk1lgIfEC zb<=hH!DR}^PhikoQz9N+Dj0n57H)_$)U9WJ3L+tN-qrT3C(X6>hUleW`l9d!J|eyc zoI{dkdB(LrGUNN<|6EaC0RLP~vbg>LhzP|Sy@H~MfL_T)$74{^yA@82QMD6lbn|_H zwX4-UO}ia$Nt6YYe7~OH_-mK8zF7t9dzlP|U~_XEe?z1AT4)~e_$g2Rbyp6bZb0`X ztchwFtntza65C{pIMX2B-wWp*geFBOo8v?QpxPGC2nya;pM&@EE-8I$ua-MOHFhk4 zoq<8v6KP%)m_NFge63wr=g*L+ATDJ*@381fNtGpJN05#d-h{5~>5bkPBJHVA!IuHA z%11PXaKeg>Nk_L~U6do}wmaHmSzoPHSWGt^kKDDR)ZbCX@dvEU&wv)CE+ksB*5>G2 zjrB7l*iy{Uny?S~^42Fff4WW^1Ct7HQ+DbwC)3+zZIVuUi0)|Cb6zlOoUL0bV#VmC z&}tMK(gBoxBx5acvS<2-K=VAczgu55U~#^5mF+B=H-m|gx=W$NLML6dIx++_b+Z7q zV{j*qCbT>mhPyAI8V~^bMD(?D#(5}bi2jr1WF61OX7(g%fABxyj9{vPOoQ1@xwx%fsJ4da%y?X| zU48%%=W~37uewcr{vZjirI4ozHh_^Zl#(ox-!$=Y%OIH9VGe-XE8Uc=Oi*c0QWu4x&Hsa(;uZjM$oE-ek zDnwXV&&A%6&ewukQYJ|D8hM$IGvX>&P27Vp;F)N~)uSEAal6arDde)W><+X-I-yzA zT_AVgXEoo;(2;I-sRQ$MDXc&t9+cYU@{x*76=-70bsyH^k^un-0oFK~J9(eK3TS@X zfnNYgHodg4Ffj}m2ga>Vfcu!Fy2xX~hwD8dGaxe=p@dRl*M~5d1id z3sKVKu#tvGmDVfx*Vr~pFBscJ(2&@zvZk%{8F}b?Y~Viw%cReorkeRMvOdqLDgXqI zmB3FxweAojjd}Oq%Bf*|d?vCi=1iQkUlCpGddl%SmGhB^Ns;RJjp`x1wgeO!dPl=K z&l(AhB84X6lnQJ}iC{EU=%`>1%kus)oN$X<2ZOmk2FL(Ks4uc+>NO$K2#S)d8^%Pe zusC4TMG>LRBVbx`#Y(=1I8W6}nm=m^oYUHA0+hP+XAyztfO~v-jO28XEK8-S)v9Y6 zzM}q922n^P9pwN|-McVl)n~hHY$vw?=_Da+dcR;dhs!;(!>^=s+6ZyjTm~ZEZA$}K zDk&i}&+bq#U1Gy!ZsxpY&c(=TTYO4bJjlZYD(|siUw_D9b>S?xf_pFA_2OVd{*uK< zgMg@0{R>z;rO+W5`%|+C*}%>6h`89a$rGB>gE&a&}~OI*YvS%)Llkn4BVewR$67 zg6dDGMLKR*@YfNOa9?DEt=?(EnYBkTP+6`iN~Drg@&1}A6YchK6hdNox1T|>47p1h zcsy)vq-)5EqV^r1iLZ5nA9X5NT_ciwuG1A7J0UnI@`%Mh6sTdVT!?v3>;F;o>K$J7 zEjxwQH8=B6)-O$uwRmY%Q`UX#8ytBuJPVuLof?yYj;NWZb?f! z+})gTML^L3q2zM&LYx3uLbo*V26n}A^O5OuLVZ(dAV7g&qiVE<*R-e_#z$KBF%JTA zI5S2;!6LccnJ9LXXjTkSg>6bZULnnd~TD)%nGUVax3P z1MXA<`E*Pe4a`H#20Du*X0HaRGrsyk`)kK^WUq55O|Y!i`xQ~!?mcDgwCV^?>} z+e?{pScVy1%;MdLBb=Lvw$UT+mE!pLcRV|uMu;@Hb7)RlYxG+EGVq+{RV8i=K)!;g zTZCe5M!xli4|#PVeXEv9bVb`$gY!6#Q&x9!0Gh6GKgIYiMnz^vJ!4BUXm!`l;OOJN zmtL-UzS&^`8o575cxy^A4<;Y4X9;16K7-GyIowOc9Q?K=7Rxgj=^tspyhIRi`)tDE zLPS^9V`gbG(JUP7EGtNrS}VuZ*|DyTgQ$Cg0nH|yE#aB7ab1^Rt%r8PgWw>zkQN74 zwJHL~*$6hZaD|e5CFklLpM~|{s(wp*X8c0w+rT~-TlPvMfZM9(7U?z(Xl-h5p>}I^H}V)M4eh;`6z+jOtbQ{EkPh3D})0} zn6PlmBeAY$QNZlDUcFyH2ZOpTz&A{N*;~7{1XAR1`v2PHk?rx84Y{g^KjP}NVhDQV zK`<6|^mK|etv8!OjW=iisE=a+ntsf&b)lhz5t26f0+8q$y>a91esaAQusyHjXw*xv zKfQ<(Y`;w1}~llTfLhfi#p8!O8nMy^K+O0$s&;s^A?8ee0@ zl2R2+sC}S9SKLdI*Essm8ri)cG=VE1UR^6|U{}(E_omj4r3v%7Iw>9 zosxL%9Yf}7e|*WGd9A998{G?dx21F9mH$^uC+&@U>Q4oAFn^ct@MiU|7(@mn%5?vGs$B z6yoBz3PKr?M_Ds_(pc(Jk0r(H?nF6(R4Ic8De6RwNCA(UPhQvEkhV`AsWC6!2#GhY zzD56JS-tL8n40n?{)9a zb{)=E(jRiVldHI~sv*hHsJD7*r5(#=@`ZTVLE(JyoxB=u z9&?Uj=Jvr;z3`9H`8p(&Eg02hDFROzk_cklrKU2Dr+WBGF+j22a)O2rI#`(Pt)vZO z$>NiZdn-hcgyefl5-^6jtFGd?Sx!FjcLK#vZSc0s8y@j%jf<*lOc|rKuUZ!1C&NUf zazjt4FNN$DV8bvmMWoGkk{G=RR>guFbj}R$z9drC+Sq)x)K6x{)t17;-Wg3V?|X)3 z<%`Ah077eU41hl7D$7$fhX>Su>*QwQ89@=(6qbjbPR6~w0lgl&MRjkmzPaT9ab4ma&-HCG2$WX)!f+XCzB! z;btq0F$@4B{>88gSB(_XB7N|mNmKg+ob zqfGgK%p1m!L~`ex8Gz=tad?&rA%9pPl_n-fSuVsHYZr3yJ2pL8i-x5LvHs9m^{DPU zYbVs5yup3HRNiYfASdCveUW^@Z)IN#WxxgE;WIAzEdTUr1w!BG{C#(2^FPGA7C+vM zGnfp7s`Pfe;*_nl^PSuqs$+VMbV)|lMm;{Ez(P(GynZ6HiSW2jSVw(%p8$Z^B)JQU zbY)yqAB-P_#Ic^#jI`^%G4${(A4aXIHFY^!x!h=lk;A`pwl$+n!YfiXe;3;-hfm;N zQsY#_SRC1?eUdGZ{lz>L+OuPL~4UPaW~H2YAo^5przD_=(k+Ef!(sv~F`DRa&(lc?UH# zOch)0z@LB%C}|@WJ}!5 z3f+Vyq;oNK#IP;TJTP?eQqf9DO(E-a0 zUrSTYuH*Gp6#A`p@ZTo8DS1Y|YUGiOQjawrG=RKy60=bn-uNO?=;BaAVVGJA-P#uDi0&S`E>NoiIHyp?#rK8N9LoERQ|*MrxTebohxWkg zn0DyO9M;$g0vxSm!3!aWLjklG`P_kNm&UDpc|=FC>7bB*-97vpQtymt4taC~p& zuJ199(5QIklGDMB1K%xhAsweHQF6)A)F>_j;^cepm65=7p4O!AEK{$JsbMYsJ|C`FoI(!*3-|0F%P@I*2RIa!ijOn%uggd1-*Ru;Sd&$&h?%I8gW-Quo+RX)> zT5T2hBnMw0zYQ!G9L1?TC(?uz55f?&rmp3#sOTn`nmz0ksqlN58)BEptexgL-HQ(! zCd_}AuaG?kbzrM=qLMC}mX_nBx%5?xmWnwT=_@p$HM8+}z;h^=L0`F~v1u>+l0JlLUxZO%A<96** zJ-kKnyLf{Rbf0=jYcJ9gj)ci2$;r?}5du<*aq>w~gVqLW?S@05!=H$)081?1#md=- z>}+i8$z9*dV?pLyO%4>Fxx%3Ux5m7qgIo#Rd0$i*dRhmzETI1pB&Lp>mP zm%)sY_#qltk)Xh3V1$G6PX>iVduZ`KH=H7$rR!+DHwc4RiF>I(r_-_k|n@>1Uit|}Z)v#2eHa6o! zitm*>Vag+VJ*&J0Q)|DAGI%H~bh>R}oxCK6^!YJY0-7|ZUGDX=4f>jL^X`Te!D*hr zscn_N4AkxjM4S-FP1em}wWoosQ4Z~Cil9CC#f1@2a|iOaKgyd6{a|F zHheWetvXKC+|>RGRZP+0;IVA$Fj&l7mh-;9*t-;LTBV3Rd}LN}p77W(>dN>@XmF+6 z5v3ZW1d0}Zc1l7!Vr`nhR$V7gQhOxq<3~s*{JRHr*82};DbFH_zpZPg*pVOlzcD&7)ovjsiB~@WC523R zLst+Nmh5xzw$x)3jrhEK6#}5AYV#XA?PG%%A#)mIPC-3qOOPW{fNxClwIC)}HBi^- zB`e7SE_3!w2Hs;SXrdo{nRln^yKB z%%K$1@-(Do&zKeFI}1M`M)!v3FAowpdpV)7lo~M!P*WC-ryZqDW8U+CWbeNT1&nw_7>7T z$ew(Bo@AQ9f3|33Tmrf#y^W3!&m3oJGZWzFnvE_ng<7)gX@&^q z+{Pt6W&=B7G4Ktd+=WppuM~#YdN_@Ar7{O(9i|_0B{a3~h=0RqzlLh%y@tf3tE&;~ z^B=X;DH=Ty-wWm|sO(6D!{B_C6LP!Q?wjfme4h^11u!3l@+)nkAK6rg=)7Q`mqwiD zc_ju!ye*d6Amdpdabt=hJcg=P`^--%%=J;(1zSO8e)Q(r1(_g3)Di=D^#w{pC1;RVjKBQXZ+U!ijuCNtnJ3NhqH0F$nz~a9zx1x`GQCsZ z{vUx-(3P?{C-F0&>P(qy`HeUqh+=$9l1pk6==&rfk}A_(8!UX6uU6O#nv2vXG)QRpk4q zWy}~7=D3^!wSIZqM6}NILA&T7V9ooksT4+bhC$qI>$TrQPZ()4o;xMhS>0w=zQiJ- z+nXO*HR2ky_ow~g^i&5y>W#;c|MFGcZ3n5W?7Dq(sI0`u)$Lcm7kbs6b2l z8W)8p=AA^$d_(kEOWT&n!FEqGxaWTQ|7gWh3P2%xx?0~~<0Sk~;|Gk6o=ElgDPkhL zIDb`s@s;hpJb>IsogHN$gpqS+i*0F)=3%TN4J62IXN4PdU!jjQ8{G2;q&8&ICEfNM z1~~7%)Zq*zUQ6S(HG#P{*8mn9th^a2Phmht9bl}1`_u^OgVOO91>TGcuv!7sR6HRF zLUXNcW{Ob@GbNi;050hjKX@pc!(|czxg!716CbhbIs?7=$TD+;Sz_){Zi7gns`bzY0CF}6qShgQunUaRacD$y232HB z*xvNmE}&8(K=^YT1fl#|BN^TlP-rt>?<+9eD-lh?ddz0KbI*5xg<6H<3ka08oYQ1ARV9JVav3ON}cypjLhFG-lS)%7NQ3NAWu;Pr;$H|6JF!F*eK-eMXDl3J9n<(uFmRuy| z*Li}5@Rd}NQvYbIwEz|WMk8F@i7wD`lx_p?wEO#(Q3T@4XyBR!+mf0P6^pBxm}!o~ zv$HCU81+f?Z>6WilGeA0w}szrg_KiN`}?WTz*JDujPiO&ytp19xXlzuD)~*>;$c=!~>jMO~=`ilm0Z2@}9G^icNW_-4;b^4Cl z)I^OP7>$T8gxEEKj>ZQeDW48AFV}I{&mm?Z^x+z}v|F{xy}54K{sUM4ApajnN&B+H}>@Y0i%k3(Y2i~4f zy;isuKeit$u4*V*z)<}1U_qa|BD%MGkLEX3$xI)lKz!iJ5xH_xf*S5Mz_W)ncAaTDU1}uNXMiNy74<*E52+mpvj+JdQ$B?4?;wg5Y}OHpcnV z|9^ml!G=KRK&?AumSlL}|9A`i)j&QTRA$Z!RvnoS4F0q+poUsR z8q3Llxzhh?7H_>5%tduBcd?KyRt>x!1t<|fbN@_ER&$czXbR3O#P%-@%yEQ>K4)GA zTNx$vmynBx5i6(qxg=)TzmyCg;)-_KmR_Zv+U^~dQ46SG>9r%g5GxQo z+1elRtjJOWDJuQf@#;Lq;_oloXAnPMhr6BD9U*3F1U!SmZ?^UjyQO$pyUSZ84~7mr zzu<_h6h@;KnJ}PG0s*vF+n?EgQ7Kqs!eMay#AY3e@LRI}MKqx_L@b1sm&jt`g7+$_ zzEov)8$_gdsF~D3RwGp35Jv+NC`O8btfCgSH0RLdhFWXT(M;x)rGojVUtD-Ahj?4~ zVhcf8j#1l{%QS&!l^$O8V;=`i2WR5mRlM zIM3UDbXLw8Uy$$E%)sTx#N19YxXUAwLrpC$w$$JT{{Sal3KSU5p#}?8($6?R zJAJ&ZuR>B{7Aq6`HeO@LUuKPI+1?QDGfxrIPLa4k`#$@b7#>Y4T2!FXjCe3nj|kh0 zIZJB4w6c!JxHp|OmkS?(LiaEGv+kLfE$)f^KK%W6IBNo;%|m}FM^P|tZ&u!i%1a;< z?i-MuR()G$aqHrDVcCnTBKcY|G&UM(cLd0iyjdM?s+3wP8gH!HO}4G^Q8Dbv{6%$B zG)gD9sr#|<*9+&y>LQ1MmSm}HqsxTrY*8hOVb;U~*dg`}xVlELyt(6<<1oQdRqmN8 zWd{=sb=HSWn?OW?8!#T#&}7i`8L$K@qclZ60MW1S4T|L&HmAJrtbnucEnO^_Wc_s7 z&TK`K=2h6hP9_**7gsI9sp42(yM?+eM0IfE-C@=2ABuu;iXV|9pnkQ|N>7NQ=bW#) z;_J$RBa-vvbkBb)lJN$mAyX0s7qgS02poW@4usi-HUE_{Z;jQ_rBEr<4x>2pVC|u@ z;09$quBuNcKe)5UzE!%fb^rDCM3>kTyRdrQwkYoR>}D%l9_mzM_6SGFw6a)G$d*P`gf``{i(^RnSO*xnu67+01{ii;!HoBIm8e16E)g+I+?GDAO|=JPx($;UpUrmfaa6$ zil8;sd^HDDo6c57;p%?0d(?dFL*Sr7Zpf}s_;u58Dn#G&X(Y?y4PraMy8xHL1tqN$ zIxR7p#K%$&QBX)3e}7=nuu6`inAH9>-%V{VvXJMDgGtW^)5ImKc1U`8PZ?6GJrcp< zLTwtug(|X*c}c3qFnIh@-K{O{YDm*%wCk>H5jK-fGB-wMc)jUhwnJ%TudnpsZnQLr zD{0ELioDO=PTu`$hxK(d#A4!lK=d`2FWgoruvuf?0Y%oj&%<^W!TT?Gs7FlMWfo~e!L>Uit$T4+b0yoz!?CT$URoC=wV$-rEM*cYc;6!zC2`*=(>nD;3M}3 z5eZqc*;p{aCnD^VDW9NE$HEr+C@_!T3AdAMtbK@a$vQ({>6x9*Z33k>l#Q1O=~7M> z)9TWE5fj8$=|AWCwwglImIjqiP*k`q~Q~077H`)`w*M z{m@%)>q(au`am{Gl3~~%8+o`7oeJ7$OC+?ph4UFXONOvUE6jOoV!6R;vIqU}pC6O& zue)6YeR{M9rwdzMQV(*SZ%e2O{oy|WG^jK?6w)K7hb-H(k4A_iYN4CG*;F;1F&q*H zTI7-qPaxokKkoN39*g_xleET?SjBi>8Ehdi6}%(QjZ4Zh`ZwIp~-nCG-C| z*Mpa~j;tUNuYs8g!aX*?q3gY98t3kl+*e~ccSia#l6;btv$!|s%31j7s_gI?&X8F z;Q~MR3T0ri%sZ}>wJy|bP+F-Xfs#|-D+jCNvRF$H_b^t0m;5)xzh9Yk+Vsnulk7RP zbMa0*4cvE!t41nnGE+j{#1WAfks{@jyG(?)+kGvpx!&v&RwB){iNqjKhDws}FIn66 zu0^_*iiCA-{-nlNYRab=Ozr(r6uUY&5)+juRhxN|k^dfxo(j)#OjKskS>fzK7;=#q zD#?lszErcEdU{h=ds{>IqBayG&*9rAB@8gjqB)!xz$9clrOTCK5fQLC* zcZZT5k+k@?3a#yjJ%~O6T+LDWm>WzY*3v`gqMA^5%fga|F*7=}ZQy6~r2zu3dW^{3 zY;1&)+2)wM5>);38Mi$iN zGs%`Owm{?(7a(h2+a~VZ>U`pu3NOJ_y$fK`AYf7;?^R~DNCepqj>$0i(5WoVQX&$i zP5T77i$Dk|!g(}c2b>Ct>ZXni_>hd3)2p2R*=ShJQe3<_Jch=?*1Gd|<`oS(M|=_7 z+Q1~Uj33bN76GaZ8t_BVwbYT51 z70?tk!J|6UK@5;%;lDXQudp!+r7U8Zq8ue*JOY12a0&R(YAE`F)y9gHD5nWLWmsWc z78Gg`MT(*?2({uByj&c)1HA?pcYwP?b91+bxSU>;^a&2eR-f2jmKEb7)>b!Nwg0TT zc-*tKN;s08r=j^VV|Qowf=_pK;vtrzk(iRU_2rqKCtA~NTnm#~vv6LXpwY-0Xoa9D z27moa;C-IrV z&_b64lRo!j`vuF!{3+JQBV60r91>8`tgI0$WxH35$`oSV`NtL;C9%65$cC=m7dB7> z!qW7)5kUtD*a*5&!|{=EUca@9zP#cVaDa(e4l^2lGno@J0H3SYRV^xvRCW=Nye4@mbWt;t*hj*e z;M}cA!E@@znXZgEhJG8SG5s~iAk()N=xKiuLO1u{iKn9&7mre^J^fy9M>Y|uoEXi* zdQW>2=uvr7)Uq_wFmy;KHu7=ekTu~3QZ%lIGC?+={;z6|opnK`5-l|Qcrjnz0EpA# z9r8m5nTh?63@w*7%)0Tx$;XmqfWiaO&=1bwJK(A!MWJLq?hpM3O;mNe!zOQR3yYAm zYV*Sp!Hx16T zo7ZhY<^Fg6MqdJL!BQrtr;*wG7o|QXQ%p2<%U>g=X}2(*LAeOA$yh{wcN! zV3^~ax17yPn<*Fd?PA_{1uFYeMfP`npqx8EkhDCfm+VYAqa#zSoHyq3_iqxE@E#g$ ziudXF8~f3nZ(*}pvfhxEEX5svG45D>|@849!HKN%UHI2Um&pgxQ~{uGCj2QV_-i3qS?m+>drP*2gWL=1@6k4Pqvs) znGgi(YV+j7lEBbT;)u;`oz+uDf$1a;lmnLCPS^2G0Se4QROb-Qi6cu&l@wZ3&RZpzv+44 zRyKeqX3V53z2Jvv=$OkZC&(O7QhYFq8fU||1KObj>j+;IK0UZQLH_kr0=h_ z`frxUS>v%zR4x%Y_96tpZyBA(`sX82J~37HkOJ@zT(MW@iD>Q1(Oj>fw;sv~i|=km z9MQ(8E*^}^N?PhDpZvv3yg;HzPSwwJ-Y2joPI+D#fH95m6LV2l=(|0J#4B9ceiYoT zL(Z43KPBBoA9C(^M^4wpzy97>;0A3+Sz_z;;!M>6WR+)EIv zoIijuLi)??BKWqwy^~ZeA3~`JFB0nxmjgRKFLV*)3N_rswEbv^g+|YnFh;64hD`Zu z1=}>Z2<m#RrviZOq zR!S#K(!uK?BDpeRnXk|P9l!8L#4l+CT@bgxyEEt%VYbhD%7^k*ZG@;WmctjQ67`W@gL`E!9qqs5+m? z8%qj*&}A(?XsW}3tQ0Tsb08+0zLZVtdlqbH4cq2vT~psgPP3u^{lvwJr=oE3u#-w7 zd1wrQXRv!MZ2jmt>`o>X^-e4B>{ufZP$zg;r|J4&q%qy#BsMiJ*5 z_kHP24t9-Sim{F>X9KulZOd`(gA?Z3^tTmLlL*`wjktDZh?+H^Hwtk%D(D?_oulY- zA5UTf0DSj?JVtK;Sfd;P>c((qnES~O8oVFBHNtP{8w0eJMf8PuNY{P*h{qg=c8v=T zhi(1=x*&di>}xHB7~GmChR(vs01I%q9WTL;0j|Qp$fLuV0uSMVGfUW7|F~43=03fD z)c*WXYR>!j!Rqou5(BxlUJp*kpON-A%07p5B2$VSVP7An`)rJ`&}LEu-td1ooGjD_ z-^yG7J#?kzCszt5NTjr>>B=+jdD6K7wtx27S^;V0mvb-0LbcbQzk>h*xx@f=i25Si z;cYvbO(RZm=y`%5jWQ%YmW?$^)W7t&$90p7b_(3JKcYOzSQEnP zDr&`~j@e}YJV@J%1e47S50b3DI>foa7G<8}0=-Wvk4sjie9Z87`!{sH)L3^n%sY`J zTx;1=$71lpui_y|TeBD!rX#S0tg63+X_@An;Df7FxH>jU+1-Y!*^OL_ACzEbYoVeGiVz&% z5OC=5gW?<5w=er_5ijN$ea8oKRiPTvqAw};@gyP;hgGwizqI+L!8EEuglkY5g3C_O z`cx-2A0z5zwLYUE(uv9rwG^l7o3Hx+L;-|AUCoecVL`Eq7Lcr5kG>5PFACq7UifE@ z?;j-Ww{Snr=KJW#4Tc%Z7~{Y_{3t~ze==pLr$_yGw`3+F@!Wq~%aLYT{A61E?Z$8u zhWoK1t}qJHD;Gh=^+2_Zu9Cg?sPz)xIiMrlo!rZ)+_k8B1`BBOEqz|%wR82jMx%6o z5fC>^4SY;@+x|4|yuQE3%VH5%t9Vdwy&t4u_`{#9261z@B#iy#Nkaq;(7$3{Evl0* zXGCDv8u1|ux+2KBXJzQ^YlaqEtzY60*KuHVB;Qa9;nU`m^W4os=I<#24jBlmAc#km zyb!z~gD3fGy)*0kPJ-JPRouozYAFT>2}iN^g7xG9>2_-^ujN30+QQrm?MDY(^qhae-@XMwAoQi0}H{PR7}b4HlkRMU1EWCV@^_++djT z8_?9*ljgfq25q|fs~RoX-_EEFXa4SlnFME&J$%5~Hu!CzsIE!rYP^dmAn>o~^z(ND zxYZf^nGr{eYM6aSf)2d@#$&kS=h5Fb8VeEs&18c5o?Yb3;_f8)@68A{gqpN%cP zD4AbvJ(PZxNn7O?z>`KX$>^xW8mE_gIVVKh89<==kb0Bp|8lH$p?58`A@h;sYIqYHAI$T^WC)HLWjP4HzVxI93o+A&i@vTod(2a$j? zWeX*vv~a2-jYtdtppWX42{9PgfaBo}u(&DGu|W0K+521GZU}zTtv7Us9{2Bv96z$X zLBd79>(@9-)I)rezan7T_5vGw;vNEzm&;@VzsfXbqK}b!XPS!KP+kOjbNo_3dDx9> z9J>foIvp)Jd)WC?5|O4eQROzlG19Y?%zePEH2vt3X7TPJtkBMFD!}?A%%)bOOVbU9 zRAgeF-0o5ObC;C8);p)TMVW-8f3bg9IuIKqBnymKadX>Y)ma0^Ml6M7@Apyy7?G#$ zKz9Kq14G;{ZY!|48@Bh0NC8`=UVFXIW70k$#(M^H9_t0(UseaHV<9i&u#WLwuy8Om zI{;gj#PP{*as{EM(w`ZMCj~Xy;NkH;410=-4uRDM-Ob+j zb1svTT${iGR{#aEdA)EBHuqKOl?u~(9gA^l22RH-H zB^z_Z+;QFrV{BY^<{S?Ww*YVwL^D<2>j_~)q@3!N7h}FyV&VomLi7;T1ee*`Yx3Q4 z5ukxs)MRB>G7W3kpJ4HU(v6xTd9T2DGax1P($j+J51M+PqQ_C^TP{ucwL_5KU zS@RKy--MCy7owb_1U=+OoF$LNi;%$mX-Zv^dMutksncU=8A9HY*$P6bh!GE8Uv)+!|Xx z{rSfbM6Vzvt(+xCiC5s}CS1dM&U*Vzm`a(@;{d8F;zyqoGsHzl4ohhKSWOA-LlJVm*{WN=p@#cUUOUL~5P}l8%|etV#yMZ|EZAB`s<}NL zq{@gU`w#!ocj~-shfXOGV1okt%hB$J8p*=*H9<7K9Ldv(vIW4rnqoU|XrZc7KeP%8a2a#Z5ETj4GtZ*)LOmu0%CwBd=rG52CzY zd$~*P>R{PJ6pC6y%Y1(ubnZOa)C%&@>qqy-vr~ws)vI<0?Q#=la}h_da8b^NDZ-|_ zXO*Cq@5t9#0-Q6ChR!;fT@SDH=Y*^jt*yLt(TLehvuYjQHukLHfo+@P@r|^jfOi{u)V61osX zv4a!5%DT+C`(_BnM{Se)Po;f&$*g0>aiNU?l;C9zKce%r0|+;I@FxDY;TqjNgfHMP6q8)#7fbK{DUu%eFZ3np%U*VW zmF+t^fMu3>dYnt>crS~5LvTJhw!4Ttve6#c(U;7fBF8Qzb~%C$PFCPM1oz(d2FW+x zo5daG8y7wDiTmiC04sMsoRa*(Un^-T@&*HND)*z3lHxLQC&mX#Vvw;3n&azJp5`(J zi9GUeEeQtxI@2hl&;)V+{Y?}RS2 z8}>vHIkCQHo*?_yISW!B17LJ9YyRN8?s?bv+gWumU!_8|>O&*>#wr}^rN0LY=}|}J zFt@Jwm8(*k@H7F~THS{JHBvRgQVmQ{t>;#GSk>wU{qHDGlfhHuC#VWrf9@(m&rdq8 z46-ghA86}`ScUUtzfW@^LpJcGE(9l2`XjL54Pe+%X6NT605P$Lj`#lIT z$*V6kX7F-y!1gf!q$*&m<^>ApaFS#b)=l7L89{a7;*?S;kxg zYDFT5MRc$6D4pf`)4R6P>2;5G8s$~TA|+oU5LFHj-o`t=#D7)=owyP6tGo%UZ#3hS z2rCGoKbZj=c5+{|(*a>HMW*93S>!@I%n|;svD^}#Z|k1`rm}YM53DOU2(BGDx}sw{ zOB6gA4NNFz2qK%A0vv#s5s4o-_6y;it}5C91soyg`|!yw*l9tv|NEWtcAPi;o1e@K z&L7MRS_JnlgX9%|*1K-`mW|c(NaM%r3vYatX|Y_``uUKBoEON5!;Y|s@E?Czky4{H&7p0= zLXeWvCMTdE)#&p&M*3u7@TGQ~Hj@REmpbnh#_a@}>SoXT?99o~6|(`rk*+NqT@2@2 zWz%^!h}!KM1MNyXffuN}TwAGgt2V(+KhN1PENFrE^ZG4ZlySf847)3*uc{$f{QOVt zae=>7(+L#pnSl?#Av0U}n7J@(`LKk%xfLGsh>ahLG_-zhhmn{pTNHz640Q>T4z1n4r)ofp-zmCh{!fwMPotNtddG~HQOM{F5 zaq_Ye`RsXrlH!?(D*gfT{qVyPp&53J^khH%&w{`iTfNvKjmnLO9Zzx(LQeP4D5TL60>$+Fi@W?b7`dI^xtl8o`q1S~NZHp>7~ zz6vM+dA71>K@7A)#qSUT4OK3-vH!YT|Ss9Hs=%4f#qJ5nGUN#9f$?tv`u;T;I~IkoX3 zW~WQT1U(;#jcY0RX=@Jqv)~f(0kkz`)Ec%QC$<(c>g!KH&KPHk@C4~k(_}31w?+@W zUMeiAkBAd7#5(U#qa3iP$OUluIDCcM$tUgOHw8d%U$-HODen&QG4~IGO}t2W-OKCw z6EXQAzUDK9-m?!zf4YMO4LWt5+}N6|L^~rWH`|9iVHEOGQw4O=S#sCmJM>-xhro{M z|Dt==frThjEdfaYdyHDh{IyAcqCRbmm97XZ{THn6YfY-mdF;0}_Aac5ZDiNB8gKy2 zzR}we8iZ6+;nW_{ix>HU_{&x)K!wdja2EDB;(1Myw|uf^U5`%{9=ew-B9KwT2KKbi z&@@1EXD#k&aA#Jnl_xvAQ_isd!vhGDG=NBNCJV4@PawZ3L1CVs776=eenXrI;0&3N z5bK7!s%pa$_%X2|F)eOEJeB5eJ(0VLc!<)%-VFj=hO1`Uj6tE!u1hppTeba5;0u$} zbpOB%_*7kSt}su;r^Mfw%tUeeBcnk5^eC+Xx>Ra(qZc(rsXJQ#!xbBGfEdZ0L@%e5 zx%%$p)g^zV5&%A}!|u}$C0u^kh5L|TplW%ypKv z#%EX0C}n^4mYG-IrI_88kiJM`0?QL9!SSmP(dOjMzJ)iqngt+;X{BK|-9nBH3a*8= za39}LZdjLLK73zuiGw+Nh_Ipt5WQlu4{*=$gG={5ww+$kA9s1*rvDz51an5kA&UEq z?Q;9AUU>800erMm>uhS8mDe+-r1<8qQRp%$&_~P*)rysxK^ZRTHZ-SPa_KkYw-*0X z_h}*{kXu3e%?`X_OB@3ow}DPLOy=MC5AKu+rBuBiZD|7ppp#4XwUOXe|FzoFX?3oC zXrSi3nz?a61xm*lI;qF+fI^?w{-J^Gn$Z-!f+;@+Zdq)+O0!k#{EOO+*H0g(gK+Ow z*nCR>!O2OxdFtdW{e)yJDahJ+oNw*utHyZl0GNFB9htGj&3*hm;;)zVR z`*G|)?w0(;b+U!cP5ZCm`9>%CQPGu>B#Fa}5{wJ=mqd(p!4m$^;RA!A+^-a!Ns){X z#_>Zh+a2%NvPpOSKN!JuVJQ)JDT$NKJy?>43 z(eq5Tx3mXwilgO_R|}+8)a*wrRb)~lezETq1j_X2{^KbPvLNtzF|85Wjup7Uc6>~w5TdHjI{D$=DB^+N; zTwUB+5fnEY1%g634y!@9cz?C)4u(6&3e5t2%A&&zJ=Bvs1RoZnky#_D0Kr|kJf`bP z^1~snjuqLqUoeQwhW(evayf>2MRRKu!sUbpcyZ`Mp5mFz)P3YP(VV*FsR9q0nII_N zwQ#rOTqMHhB9sJ0X(XHSo~{r(Qw!^<_MtGmhzB;Oe^As`g)~l1{iaWJ@vkv| zjFD~T?eXq!4JTQyh)v<8RNc?=%Bq7~?@QM@fD+(kguYFKBvaPvn?{_hAO(&V8tX_S z&8F<_A}C3*qOYAvSZ-tWO+d62ibP;RSFx+KEetN#$Y#Uz1~-jUO4r-$-La8) z*H=IXC9!h-uoLht>zXk=kG@or`p4&O*SXkHm#dtAG)-9*!>d zs@yVzDj+D#ewY0Gj2W=6s>ARwno5<2C^;UWcWNV;oy28;S6d+^Gy+MJLaeV+_|72Y{Bqi0vre~dfKF;O-hL~fB`Fcjs4bu{ zi?GFEIa*A|ZYXSXgqbVPWL;++zckx5Txfk6{9}C~61qrSV#2wxswoKvE*$DoyY4x< zeEr32%AoriJw*YkaX@;r$TM$)7m;j;jTQHBML|Ll>~+uGIOTW^e-*E5lSxG*R@6Fx z+;A7UCLX)DuE%r$*R|Bt)XJ<+`+xO1Gl~9KRGO{RXTL~S;APT;7QCJnBfN+kpHOvXj*4KYF3}U@5ujNc%jp~8<=7++LCi&=E2Op#p6>tLfy{kB z9m8k$;=@+K=gN4NGLJ4>bytK$mOMX!5Kp#p!S_4^H@ctMdh{nbQJ=6sPtqEo8(hDPbU28f>ginH>4Fb}J3%YbFn37O3ktC&O931-Sj zwuOD)Y$l!_c=j{H_l6t#_fkm;9mqP1W+p8bZUsK#s`bXFm>5n@JVFmw5Tv0R-R~8) z_Ft~mUJ^@)0)qyN84{^jtwskO_Hi*_vy5%+oUB=pyc)?RqL{ErTd{4~&0}5M!b?zch|**h;_qW}RG6A4kuBR@R>+eV_2T&p8pCKB8paAgKQHnungbp% z#H9Ny>KV^?tUe6%7jp&O8)s?{vPV1t?-?Q`d^@E~9O2PRlp7fZ(^koFMm^g++v1Ah z0Ik!k4PZzxK1V#s^BUn%?FUyHJfIYEiftw6cr1UU$i-PhJ-XIT_o1sMK0OL7BNqN( zV4>pR*=OP>rMF6P?mMF5yJuADE|M|mmAptb6TkK8eebAz7<%>Vn5%$5ALDSg*tGW<(&?e{vTL1a%sL|Q4hZ1U9vF+VWRjM=N@D0Mk91%kZ8x z3LK>Zs9R+R46T;U{W(CewRrFFuH|Z}OT_m2Nn{hI=Xrb}MyGf^86d9WI{R!47cQ#c zFR!4|y_uDe*g=D=$yDad0VUiLsXe^%C_}-6f|i>P<-}0x3RciLsZB~}wGJEz@mrNp znyOj;5cSUctO}RQ@Ytmk{YbPIJ;I;U>;Va+9V*t}e>CN6y-tA8WNNB1 z&_D`WWRd`2D>-^oUkRcY?#QGj9Q8fz^iDc0O9Bkr08x^fl<`u*3H-Jd5WpU9A_uX8 z%Llb}ysVveICa@aje=mBa*vU|wPqpWTo?*u#A$y8y@bZ?B3$1p#6es@4}PC9u-L!f zp`EcCD@4)10d~@SrGmSzK!VF;=Tlv{G zpbMys?*2F-L$)G_7I6q^DTPYYy}OoK(>?hT@IMqWS+uM`H@l(K_WXJ1Vd21>At}E7 z&XC~{^BQU0dE9EZoNjt{J0}nJQ@o0)JwUa52)5eol)^7Oa-#jW{NCc;(U4E=oGNT2;&l4~T5kfi0y@K%(y}-k9&U zX}Iv5DqIO?6^O&7k+czYTiyEk>QlHg>lWFXxWpK@RSY4>>8G=}l-A)VqRyhLLDpL} zdh9+jT4q!)97(|X8=YngjZup+f}=iLTXwRxkpCN#lhII5n?2tR5~-_Bo2%Y@WyQBz z(ZXNJyql7;5-71)fd9h}zzkOz;svgI?`NJssBG@x-!NMEWQZY$GlkF2t*)UVEoIpy zhn-$&{~F$z=KSKllur@1x>R6x6$dPooTE)@B;^q+zl+>(y1JTcni%iibx>@qgZ}E$ z1si_uGEv4cnVchp^92;7A3DCwCbIL?%z-OauXbx!a?>hL!aU2_ibI{&HJe&$$>E4- zRIEafku+5rEv#~AH}QnzQ9ywH9IT_R{%R84+VeFu7V2S-^qEH z{1djBYmT7Ur}Ai?#=)cRh`!x+ZU}~#8~kPtz}awtq#OU@ztCyz08fGpqcj|d_6auo z*Ig)F8wWE6T!VEc_l__E!EnP6AYPztFkC-#;4S0fZ;w?O;_5}=Yi4ZPP#Y9D*h8b8 zM**rkXO)v*VKHv1VCxQ^fB%tp@`?Y>8_9~MT1jZvroyD=uT8j4^GGY0(z@8G|Jgkiy0-1|Nl$>jXu}3_D zi(ric3V8*O2t|p)$X*w#=k=m?|9|?WE2--y;7xd9xe;%Gdh=|N>?;DeC{kq|g#=QH zDvzkd8rj;+$#8Mpcf9>idAFXKAD+^GFW9*u8wW}mHgdkbR&3osI)RTTC#vus>58EnIV%|?$MUl9%ic}-IPDfUnr$nBRN!Bd@^g(gzv+hCAOIW>!tYRpD+>*Ep_IBhh^4$7@PL zv0^wa%X~3f$!%ks3Ss^PQaq4AcKLG+ppZ(>TFbEZWc*X5#60jP$3L>K`k>xhupvDc;(3R}2}YarhULQOHGapusJNt! zoLZ%}n&q~4cdWg1i=&<#NZpKMqO_3(EpFcT8S3HvTW$i&Gnf8@`4~m|%rV?wRe`8IG& z>;OljDSo86$qi3>1d0%hw}tUky)B%r!HO18gV}B=27kn-IU@pmog~+M@xaow%$2vj z^cS89rki{SJmx(0C3|Jq7|DH&Ll<2Ll{Jxa>p$HvEDfL|T!PVBj3KEhX=KEas~} z-O6h;fl|ai(+qTO1^$n`<-L82#J=Nlx*^<6Qo6c&5__vk_l9RncM#-H}}@!;e!D;#uuKuH26{nyq`+TJ=vT5>S3P>m@GQE=qh&P+4Kae znhi6}AAxBnXEho?Ow`)oN3Ka429~TU_b}lAn9U5)(zGh)Jd#MI3ff=vcr{_l*1COO#QT zQcr^{bDDx+++O%YB!G1RwH>_LuS+s^cxwBfWgu4V3@ zWMelbDP763y@bx5{;Vo;(#!R;m&1)l;NuQb$#bb@CO5HS%V3kj=$?jGT7EIzlL-0| zAzVIyk~Y=ab+|uHgded_=@FTW!1n?Rhw~ljrw=5o85QFthJKP+V2P@z-sH+h&1ZJy z(jv`3TvryD&uu$z-|q24H<<&UI{;#_Nl{+BWIQUHp?e+4u}WOaqC_ZGk<#-zOON&! zy1`w1uaRC@MMX9R&OkbS#XvD@tj|ItUN@Jnfy3OSq%A&Qp}1+br~m>8fQSIFc`KOJUY z93l4O=CdJ)fla%Jj$9Q3+c&{l7KlucN82uM>+3KTaa%LICIcbly;oQz7KXkSoE2#_ zEovF^XB~KC84Zc@8F)s`tBN$tq=2(pZ65^tlhb58yX~;$fc-8v2*-Mx6X=131XtPA zH};J33yNA1Lt0LJVu}4l)_02SljY*qZj4hO#bN(f`Uyi`^XFM-%)}QLQ&k)<6RSbz?JYq=ot@r z;CZwqRVEWJ-V@{=J426-FDZiM7x_1KZ?|5DpQSf6W8_;;JH~Mh`@6EB@~919q3@}C z8B8G}ct-x6wSjudme%NPm#=NfKSBd8Md4W=F-d&e*H>c!W1vU@m;tcE(OS(4fE)(1 zxQ@FZ5asszWA$FQR^Ak~BwjCay!2nsjNS)3excdczgtM-pQq@MSY?-A$F(d_ARZ|hI? zx;0ZQlU~1@B_DGg4`;l-4Wy)8$^y(+~wMH@gGiHJiw5lU8$Ti?_@vUEGVr1;>#Za}+~hr74J z#f;K?lWpiuISz;fa_dfMOtV}h=wfbGXbX@M&LHkRd#9ciR8xTJxi7{9hufeZ?T&&i zEw~`q>?Uy;|j)13+WVGAI1E+nMGa zG0f(@B7Hz)$FsV@z$qZFo>*C#ymv^?Qtla7?nYR|p2~8z=olHhvTapwEEjgSjggHY z6DtV{&p##v^-V#?tfYnHt&L#G0DThdWJf#!*9;S&mL1x{zzVF_nl!9`Di}zSjr>x| z3&wO)Cy#_;0Oi&|#RIpHjfL$u!|AwQe%LXM{Kdz1h%*Pw9O4(rVRN}(2;rd;s<%F! zp&xsu<`MNlx<&O&dOXfG6(;4UTcr}*rKQW975RYeY6r31hf?;wbv5ic(f>E)V1R0~ zrM|cGb*deElt^onDkk2F$hC>CH#?)^NSMz;hLFGWrav-0?e`o3_3YL_t6DT+^Mfwe|7&3v~NJjW7(C5+Oi!dZmeLSb9 z1wYOuNli<&RhXE}gX+MQ`E;Pmu-3PVOmk;b#flEP8Wfn@4ypj|02gJFeh5|Pr%wRU z2Rr&i{PPx^=-}Tg;_lDZMlwWwojoG|uzKeG(pgmKzB+tl46LSOBGf6vNH#OfB(87-(N@n5YmWTf5s`%7?x#p(#`UPM$ zSPX8k->MAL7VQKlhPJ?SV5qvs76fS)2ARI-o%QUl$BOFbU1_xe;Bf~uPfqMf*2EIG zWL)QQGPhSkdUokmgm`x^EUHZdy6^l_m$#I|(3(6mp&euckbq5<$Qc&%ZzvN>ZzgQ+ z#kItkgrr!LD690kygd@&A@I1{aH}N)qO`JxDkUL{h&is=@m<*Ag$1-02Qy3h98Vvk zQrIq1@K*6k#|m=l{FCbiCU~V{C9|0LhGHnb35`;38avhqD5)2}d+t)kZoqf}Fn%@i zOzeAJPqIdxxRVlJSYS2x7H_mwo;OK9G=2G56s(aRk&ybTy3<{k@gGGyin+$$yA z9tJ(lL0&4Jva%&!`okga zM9Y;id+(#q(-7SaiC(?Y;#C%z`4uk9fKUQ!7${hQE{_1%r25W)!zfp59{+XE! z3?Q$%QOmXHswO7l zz#~DvbTRjP9X1;_kq0V$4r1$ z12T^&>D!>1)x9ebGRrxCsX#WVG$Vsr{n*mrtDN_jvpQi(zz$}140N(|>n_up2+jj< zK2Y(xTIU;xxFyd^oCZQo!`nEhAR%mYH6<(cDCQ0xe^RA=269Ez*2JcQ`Fz*7M~+yEOOf*3Gp6_d$Dd$@Wfg9_B(<);HTp1*g zUPevjtmBn5`3rk5r)Rjuv4KGJsjFX8s{MF+F1?KR)hT$Y==gx9s$RuU#67ulSt>RJ z-fSHgeFN_gaN!X+i=fsUct*vHFTF@JL<@FjK56j(B_5sJjqtK9kx}H?ILV{fn-xR? zpD}UjE|lbEbOGGRrAn{lm^D{ovYdH(UTg8E1?qi9#Eo-l6mqc}kT}(%4vsF25GP@+ zTQz6?aUNC}5jd6IuNW|Rw7NZrtt{Nf!jjQgm~sSuLx7O6CnMs7(EC|9NRFz@jc3p` z`8tyT1jSM@?k)ynGIh#RNvgv(Z{O5mshMU22s*vS{iuD-as2%S+_2g)NWe+OZHopYwuMoe1|9QhA6aPwnuo;g=i-%+4Au$% H{t!)csfY^K diff --git a/NewHorizons/Assets/xen.newhorizons.manifest b/NewHorizons/Assets/xen.newhorizons.manifest index 7b2df6e1..8902bd7f 100644 --- a/NewHorizons/Assets/xen.newhorizons.manifest +++ b/NewHorizons/Assets/xen.newhorizons.manifest @@ -1,14 +1,16 @@ ManifestFileVersion: 0 -CRC: 3775427237 +CRC: 2022446871 Hashes: AssetFileHash: serializedVersion: 2 - Hash: b13d8ad399be3ebd0d2dbea38e0962db + Hash: 083882699617744b8fc49234bb8cb795 TypeTreeHash: serializedVersion: 2 - Hash: 55d48f4ad9c3b13330b9eb5ee5686477 + Hash: 10a6a558690295dadb3dd990eda0821a HashAppended: 0 ClassTypes: +- Class: 21 + Script: {instanceID: 0} - Class: 48 Script: {instanceID: 0} SerializeReferenceClassIdentifiers: [] @@ -18,6 +20,7 @@ Assets: - Assets/Shaders/SphereTextureWrapperNormal.shader - Assets/Shaders/UnlitRing1Pixel.shader - Assets/Shaders/UnlitTransparent.shader +- Assets/Resources/TransparentCloud.mat - Assets/Shaders/StandardCullOFF.shader - Assets/Shaders/Ring1Pixel.shader Dependencies: [] diff --git a/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs b/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs index a3ea402a..14fb8c46 100644 --- a/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs +++ b/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs @@ -11,16 +11,15 @@ namespace NewHorizons.Builder.Atmosphere { private static Material[] _gdCloudMaterials; private static Material[] _qmCloudMaterials; + private static Material _transparentCloud; private static GameObject _lightningPrefab; private static Texture2D _colorRamp; - private static Shader transparentCloudShader; private static readonly int Color = Shader.PropertyToID("_Color"); private static readonly int ColorRamp = Shader.PropertyToID("_ColorRamp"); private static readonly int MainTex = Shader.PropertyToID("_MainTex"); private static readonly int RampTex = Shader.PropertyToID("_RampTex"); private static readonly int CapTex = Shader.PropertyToID("_CapTex"); private static readonly int Smoothness = Shader.PropertyToID("_Glossiness"); - private static readonly int Mode = Shader.PropertyToID("_Mode"); public static void Make(GameObject planetGO, Sector sector, AtmosphereModule atmo, bool cloaked, IModBehaviour mod) { @@ -253,22 +252,9 @@ namespace NewHorizons.Builder.Atmosphere filter.mesh = SearchUtilities.Find("CloudsTopLayer_GD").GetComponent().mesh; MeshRenderer renderer = cloudsTransparentGO.AddComponent(); - if (transparentCloudShader == null) transparentCloudShader = Main.NHAssetBundle.LoadAsset("Assets/Shaders/StandardCullOFF.shader"); - var material = new Material(transparentCloudShader); - material.renderQueue = 2999; - material.name = "TransparentCloud"; - material.SetFloat(Smoothness, 0f); + if (_transparentCloud == null) _transparentCloud = Main.NHAssetBundle.LoadAsset("Assets/Resources/TransparentCloud.mat"); + var material = _transparentCloud; material.SetTexture(MainTex, image); - - material.SetFloat(Mode, 2); - material.SetOverrideTag("RenderType", "Transparent"); - material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha); - material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); - material.SetInt("_ZWrite", 0); - material.DisableKeyword("_ALPHATEST_ON"); - material.EnableKeyword("_ALPHABLEND_ON"); - material.DisableKeyword("_ALPHAPREMULTIPLY_ON"); - renderer.sharedMaterial = material; if (atmo.clouds.rotationSpeed != 0f) From a1ba3a6beeae22c340f63cb613150e7bc986ded1 Mon Sep 17 00:00:00 2001 From: JohnCorby Date: Thu, 1 Sep 2022 21:17:31 -0700 Subject: [PATCH 080/176] improve SearchUtilities.Find --- NewHorizons/Utility/SearchUtilities.cs | 59 ++++++++++++++------------ 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/NewHorizons/Utility/SearchUtilities.cs b/NewHorizons/Utility/SearchUtilities.cs index 970c33d4..6f4d1be5 100644 --- a/NewHorizons/Utility/SearchUtilities.cs +++ b/NewHorizons/Utility/SearchUtilities.cs @@ -95,38 +95,41 @@ namespace NewHorizons.Utility { if (CachedGameObjects.TryGetValue(path, out var go)) return go; + // 1: normal find go = GameObject.Find(path); - if (go == null) + if (go) { - // find inactive use root + transform.find - var names = path.Split('/'); - var rootName = names[0]; - var root = SceneManager.GetActiveScene().GetRootGameObjects().FirstOrDefault(x => x.name == rootName); - if (root == null) - { - if (warn) Logger.LogWarning($"Couldn't find root object in path {path}"); - return null; - } - - var childPath = string.Join("/", names.Skip(1)); - go = root.FindChild(childPath); - if (go == null) - { - var name = names.Last(); - if (warn) Logger.LogWarning($"Couldn't find object in path {path}, will look for potential matches for name {name}"); - // find resource to include inactive objects - // also includes prefabs but hopefully thats okay - go = FindResourceOfTypeAndName(name); - if (go == null) - { - if (warn) Logger.LogWarning($"Couldn't find object with name {name}"); - return null; - } - } + CachedGameObjects.Add(path, go); + return go; } - CachedGameObjects.Add(path, go); - return go; + // 2: find inactive using root + transform.find + var names = path.Split('/'); + + var rootName = names[0]; + var root = SceneManager.GetActiveScene().GetRootGameObjects().FirstOrDefault(x => x.name == rootName); + + var childPath = string.Join("/", names.Skip(1)); + go = root ? root.FindChild(childPath) : null; + if (go) + { + CachedGameObjects.Add(path, go); + return go; + } + + var name = names.Last(); + if (warn) Logger.LogWarning($"Couldn't find object in path {path}, will look for potential matches for name {name}"); + // 3: find resource to include inactive objects + // also includes prefabs but hopefully thats okay + go = FindResourceOfTypeAndName(name); + if (go) + { + CachedGameObjects.Add(path, go); + return go; + } + + if (warn) Logger.LogWarning($"Couldn't find object with name {name}"); + return null; } public static List GetAllChildren(this GameObject parent) From d6b91acce54ced7599a622fc20eb50c764fe6869 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Fri, 2 Sep 2022 07:56:42 -0400 Subject: [PATCH 081/176] Add render queue controller --- .../Builder/Atmosphere/CloudsBuilder.cs | 16 ++++++ .../TransparentCloudRenderQueueController.cs | 54 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 NewHorizons/Components/TransparentCloudRenderQueueController.cs diff --git a/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs b/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs index 14fb8c46..7d06fc0c 100644 --- a/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs +++ b/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs @@ -257,6 +257,22 @@ namespace NewHorizons.Builder.Atmosphere material.SetTexture(MainTex, image); renderer.sharedMaterial = material; + if (!isProxy) + { + GameObject tcrqcGO = new GameObject("TransparentCloudRenderQueueController"); + tcrqcGO.transform.SetParent(cloudsTransparentGO.transform, false); + tcrqcGO.layer = LayerMask.NameToLayer("BasicEffectVolume"); + + var shape = tcrqcGO.AddComponent(); + shape.radius = 1; + + var owTriggerVolume = tcrqcGO.AddComponent(); + owTriggerVolume._shape = shape; + + TransparentCloudRenderQueueController tcrqc = tcrqcGO.AddComponent(); + tcrqc.renderer = renderer; + } + if (atmo.clouds.rotationSpeed != 0f) { var rt = cloudsTransparentGO.AddComponent(); diff --git a/NewHorizons/Components/TransparentCloudRenderQueueController.cs b/NewHorizons/Components/TransparentCloudRenderQueueController.cs new file mode 100644 index 00000000..d78259b1 --- /dev/null +++ b/NewHorizons/Components/TransparentCloudRenderQueueController.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace NewHorizons.Components +{ + [RequireComponent(typeof(OWTriggerVolume))] + public class TransparentCloudRenderQueueController : MonoBehaviour + { + public int insideQueue = 3001; + public int outsideQueue = 2999; + + private OWTriggerVolume _triggerVolume; + public Renderer renderer; + + public void Awake() + { + _triggerVolume = this.GetRequiredComponent(); + if (_triggerVolume == null) return; + _triggerVolume.OnEntry += OnTriggerVolumeEntry; + _triggerVolume.OnExit += OnTriggerVolumeExit; + } + + public void OnDestroy() + { + if (_triggerVolume == null) return; + _triggerVolume.OnEntry -= OnTriggerVolumeEntry; + _triggerVolume.OnExit -= OnTriggerVolumeExit; + } + + public void OnTriggerVolumeEntry(GameObject hitObj) + { + if (hitObj.CompareTag("PlayerDetector")) SetQueueToInside(); + } + + public void OnTriggerVolumeExit(GameObject hitObj) + { + if (hitObj.CompareTag("PlayerDetector")) SetQueueToOutside(); + } + + public void SetQueueToInside() + { + if (renderer == null) return; + renderer.sharedMaterial.renderQueue = insideQueue; + } + + public void SetQueueToOutside() + { + if (renderer == null) return; + renderer.sharedMaterial.renderQueue = outsideQueue; + } + } +} From e1136b39b19531e08260c577b8d824f86f233579 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Fri, 2 Sep 2022 10:30:57 -0400 Subject: [PATCH 082/176] Make entry locations work in dimensions --- NewHorizons/Builder/ShipLog/EntryLocationBuilder.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NewHorizons/Builder/ShipLog/EntryLocationBuilder.cs b/NewHorizons/Builder/ShipLog/EntryLocationBuilder.cs index 4efd3ca1..b35ec450 100644 --- a/NewHorizons/Builder/ShipLog/EntryLocationBuilder.cs +++ b/NewHorizons/Builder/ShipLog/EntryLocationBuilder.cs @@ -1,4 +1,4 @@ -using NewHorizons.External.Modules; +using NewHorizons.External.Modules; using OWML.Common; using System.Collections.Generic; using UnityEngine; @@ -15,6 +15,7 @@ namespace NewHorizons.Builder.ShipLog entryLocationGameObject.transform.position = go.transform.TransformPoint(info.position ?? Vector3.zero); ShipLogEntryLocation newLocation = entryLocationGameObject.AddComponent(); newLocation._entryID = info.id; + newLocation._outerFogWarpVolume = go.GetComponentInChildren(); newLocation._isWithinCloakField = info.cloaked; _locationsToInitialize.Add(newLocation); entryLocationGameObject.SetActive(true); From fdfe477508723a358a5aa550a94ba3d625c30846 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Fri, 2 Sep 2022 12:57:30 -0400 Subject: [PATCH 083/176] Replace < > and CDATA in value as well --- NewHorizons/Handlers/TranslationHandler.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/NewHorizons/Handlers/TranslationHandler.cs b/NewHorizons/Handlers/TranslationHandler.cs index aa253c3e..03910e2b 100644 --- a/NewHorizons/Handlers/TranslationHandler.cs +++ b/NewHorizons/Handlers/TranslationHandler.cs @@ -66,9 +66,10 @@ namespace NewHorizons.Handlers foreach (var originalKey in config.ShipLogDictionary.Keys) { var key = originalKey.Replace("<", "<").Replace(">", ">").Replace("", ""); + var value = config.ShipLogDictionary[originalKey].Replace("<", "<").Replace(">", ">").Replace("", ""); - if (!_shipLogTranslationDictionary[language].ContainsKey(key)) _shipLogTranslationDictionary[language].Add(key, config.ShipLogDictionary[originalKey]); - else _shipLogTranslationDictionary[language][key] = config.ShipLogDictionary[originalKey]; + if (!_shipLogTranslationDictionary[language].ContainsKey(key)) _shipLogTranslationDictionary[language].Add(key, value); + else _shipLogTranslationDictionary[language][key] = value; } } @@ -78,9 +79,10 @@ namespace NewHorizons.Handlers foreach (var originalKey in config.DialogueDictionary.Keys) { var key = originalKey.Replace("<", "<").Replace(">", ">").Replace("", ""); + var value = config.DialogueDictionary[originalKey].Replace("<", "<").Replace(">", ">").Replace("", ""); - if (!_dialogueTranslationDictionary[language].ContainsKey(key)) _dialogueTranslationDictionary[language].Add(key, config.DialogueDictionary[originalKey]); - else _dialogueTranslationDictionary[language][key] = config.DialogueDictionary[originalKey]; + if (!_dialogueTranslationDictionary[language].ContainsKey(key)) _dialogueTranslationDictionary[language].Add(key, value); + else _dialogueTranslationDictionary[language][key] = value; } } @@ -90,9 +92,10 @@ namespace NewHorizons.Handlers foreach (var originalKey in config.UIDictionary.Keys) { var key = originalKey.Replace("<", "<").Replace(">", ">").Replace("", ""); + var value = config.UIDictionary[originalKey].Replace("<", "<").Replace(">", ">").Replace("", ""); - if (!_uiTranslationDictionary[language].ContainsKey(key)) _uiTranslationDictionary[language].Add(key, config.UIDictionary[originalKey]); - else _uiTranslationDictionary[language][key] = config.UIDictionary[originalKey]; + if (!_uiTranslationDictionary[language].ContainsKey(key)) _uiTranslationDictionary[language].Add(key, value); + else _uiTranslationDictionary[language][key] = value; } } } From 7a457b8bb1ee12fe8fa95ce4f23f4e7d81c20850 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Fri, 2 Sep 2022 14:38:28 -0400 Subject: [PATCH 084/176] I guess I forgot to instantiate inactive --- NewHorizons/Builder/Props/NomaiTextBuilder.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NewHorizons/Builder/Props/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiTextBuilder.cs index 7743d754..06a32eed 100644 --- a/NewHorizons/Builder/Props/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiTextBuilder.cs @@ -99,7 +99,7 @@ namespace NewHorizons.Builder.Props _preCrashRecorderPrefab.name = "Prefab_NOM_Recorder_Vessel"; _preCrashRecorderPrefab.transform.rotation = Quaternion.identity; - _trailmarkerPrefab = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_NorthHemisphere/Sector_NorthPole/Sector_HangingCity/Sector_HangingCity_District2/Interactables_HangingCity_District2/Prefab_NOM_Sign"); + _trailmarkerPrefab = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_NorthHemisphere/Sector_NorthPole/Sector_HangingCity/Sector_HangingCity_District2/Interactables_HangingCity_District2/Prefab_NOM_Sign").InstantiateInactive(); _trailmarkerPrefab.name = "Prefab_NOM_Trailmarker"; _trailmarkerPrefab.transform.rotation = Quaternion.identity; } @@ -491,6 +491,7 @@ namespace NewHorizons.Builder.Props } trailmarkerObject.transform.position = planetGO.transform.TransformPoint(info?.position ?? Vector3.zero); + trailmarkerObject.transform.localScale = Vector3.one * 0.75f; if (info.rotation != null) { From af89532c4ef1b0c985027808d976e4236f9faffb Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Fri, 2 Sep 2022 15:45:01 -0400 Subject: [PATCH 085/176] Don't do funny entry name prepend for modded facts --- NewHorizons/Patches/ShipLogPatches.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/NewHorizons/Patches/ShipLogPatches.cs b/NewHorizons/Patches/ShipLogPatches.cs index ba588f12..96a89c01 100644 --- a/NewHorizons/Patches/ShipLogPatches.cs +++ b/NewHorizons/Patches/ShipLogPatches.cs @@ -230,5 +230,20 @@ namespace NewHorizons.Patches AchievementHandler.OnRevealFact(); } + + [HarmonyPrefix] + [HarmonyPatch(typeof(ShipLogFact), nameof(ShipLogFact.GetText))] + public static bool ShipLogFact_GetText(ShipLogFact __instance, ref string __result) + { + if (ShipLogHandler.IsModdedFact(__instance.GetID())) + { + __result = TranslationHandler.GetTranslation(__instance._text, TranslationHandler.TextType.SHIPLOG); + return false; + } + else + { + return true; + } + } } } \ No newline at end of file From 18d575513339708da3f53ab5ddf9e7e7c71d37d3 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Fri, 2 Sep 2022 17:55:39 -0400 Subject: [PATCH 086/176] Better interference --- NewHorizons/Components/InterferenceVolume.cs | 23 ++++++-- NewHorizons/Handlers/InterferenceHandler.cs | 60 +++++++++++++++---- NewHorizons/Main.cs | 1 + NewHorizons/Patches/HUDPatches.cs | 62 +++++++++++++++++++- 4 files changed, 127 insertions(+), 19 deletions(-) diff --git a/NewHorizons/Components/InterferenceVolume.cs b/NewHorizons/Components/InterferenceVolume.cs index b13276c5..2b2881fd 100644 --- a/NewHorizons/Components/InterferenceVolume.cs +++ b/NewHorizons/Components/InterferenceVolume.cs @@ -10,19 +10,21 @@ namespace NewHorizons.Components { public class InterferenceVolume : BaseVolume { + private string _id = Guid.NewGuid().ToString(); + public override void OnTriggerVolumeEntry(GameObject hitObj) { if (hitObj.CompareTag("PlayerDetector")) { - InterferenceHandler.OnPlayerEnterInterferenceVolume(); + InterferenceHandler.OnPlayerEnterInterferenceVolume(_id); } else if (hitObj.CompareTag("ProbeDetector")) { - InterferenceHandler.OnProbeEnterInterferenceVolume(); + InterferenceHandler.OnProbeEnterInterferenceVolume(_id); } else if (hitObj.CompareTag("ShipDetector")) { - InterferenceHandler.OnShipEnterInterferenceVolume(); + InterferenceHandler.OnShipEnterInterferenceVolume(_id); } } @@ -30,16 +32,25 @@ namespace NewHorizons.Components { if (hitObj.CompareTag("PlayerDetector")) { - InterferenceHandler.OnPlayerExitInterferenceVolume(); + InterferenceHandler.OnPlayerExitInterferenceVolume(_id); } else if (hitObj.CompareTag("ProbeDetector")) { - InterferenceHandler.OnProbeExitInterferenceVolume(); + InterferenceHandler.OnProbeExitInterferenceVolume(_id); } else if (hitObj.CompareTag("ShipDetector")) { - InterferenceHandler.OnShipExitInterferenceVolume(); + InterferenceHandler.OnShipExitInterferenceVolume(_id); } } + + public void OnPlayerEnter() => InterferenceHandler.OnPlayerEnterInterferenceVolume(_id); + public void OnPlayerExit() => InterferenceHandler.OnPlayerExitInterferenceVolume(_id); + + public void OnProbeEnter() => InterferenceHandler.OnProbeEnterInterferenceVolume(_id); + public void OnProbeExit() => InterferenceHandler.OnProbeExitInterferenceVolume(_id); + + public void OnShipEnter() => InterferenceHandler.OnShipEnterInterferenceVolume(_id); + public void OnShipExit() => InterferenceHandler.OnShipExitInterferenceVolume(_id); } } diff --git a/NewHorizons/Handlers/InterferenceHandler.cs b/NewHorizons/Handlers/InterferenceHandler.cs index 71384f02..2e854afa 100644 --- a/NewHorizons/Handlers/InterferenceHandler.cs +++ b/NewHorizons/Handlers/InterferenceHandler.cs @@ -8,21 +8,57 @@ namespace NewHorizons.Handlers { public static class InterferenceHandler { - public static bool _playerInterference; - public static bool _probeInterference; - public static bool _shipInterference; + private static List _playerInterference; + private static List _probeInterference; + private static List _shipInterference; - public static bool PlayerHasInterference() => _playerInterference; - public static bool ProbeHasInterference() => _probeInterference; - public static bool ShipHasInterference() => _shipInterference; + public static void Init() + { + _playerInterference = new List(); + _probeInterference = new List(); + _shipInterference = new List(); + } - public static void OnPlayerEnterInterferenceVolume() => _playerInterference = true; - public static void OnPlayerExitInterferenceVolume() => _playerInterference = false; + public static bool PlayerHasInterference() => _playerInterference.Any(); + public static bool ProbeHasInterference() => _probeInterference.Any(); + public static bool ShipHasInterference() => _shipInterference.Any(); - public static void OnProbeEnterInterferenceVolume() => _probeInterference = true; - public static void OnProbeExitInterferenceVolume() => _probeInterference = false; + public static bool IsPlayerSameAsProbe() => _playerInterference.All(_probeInterference.Contains) && _playerInterference.Count == _probeInterference.Count; + public static bool IsPlayerSameAsShip() => _playerInterference.All(_shipInterference.Contains) && _playerInterference.Count == _shipInterference.Count; - public static void OnShipEnterInterferenceVolume() => _shipInterference = true; - public static void OnShipExitInterferenceVolume() => _shipInterference = false; + public static void OnPlayerEnterInterferenceVolume(string id) + { + _playerInterference.SafeAdd(id); + GlobalMessenger.FireEvent("RefreshHUDVisibility"); + } + + public static void OnPlayerExitInterferenceVolume(string id) + { + _playerInterference.Remove(id); + GlobalMessenger.FireEvent("RefreshHUDVisibility"); + } + + public static void OnProbeEnterInterferenceVolume(string id) + { + _probeInterference.SafeAdd(id); + GlobalMessenger.FireEvent("RefreshHUDVisibility"); + } + + public static void OnProbeExitInterferenceVolume(string id) + { + _probeInterference.Remove(id); + GlobalMessenger.FireEvent("RefreshHUDVisibility"); + } + + public static void OnShipEnterInterferenceVolume(string id) + { + _shipInterference.SafeAdd(id); + GlobalMessenger.FireEvent("RefreshHUDVisibility"); + } + public static void OnShipExitInterferenceVolume(string id) + { + _shipInterference.Remove(id); + GlobalMessenger.FireEvent("RefreshHUDVisibility"); + } } } diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index 80f5dc53..9204ed45 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -305,6 +305,7 @@ namespace NewHorizons AstroObjectLocator.Init(); StreamingHandler.Init(); AudioTypeHandler.Init(); + InterferenceHandler.Init(); RemoteHandler.Init(); AtmosphereBuilder.Init(); BrambleNodeBuilder.Init(BodyDict[CurrentStarSystem].Select(x => x.Config).Where(x => x.Bramble?.dimension != null).ToArray()); diff --git a/NewHorizons/Patches/HUDPatches.cs b/NewHorizons/Patches/HUDPatches.cs index adcde38e..fc37e4e0 100644 --- a/NewHorizons/Patches/HUDPatches.cs +++ b/NewHorizons/Patches/HUDPatches.cs @@ -10,6 +10,8 @@ namespace NewHorizons.Patches [HarmonyPatch(typeof(HUDMarker), nameof(HUDMarker.Awake))] public static void HUDMarker_Awake(HUDMarker __instance) { + GlobalMessenger.AddListener("RefreshHUDVisibility", __instance.RefreshOwnVisibility); + GlobalMessenger.AddListener("RefreshHUDVisibility", __instance.RefreshOwnVisibility); GlobalMessenger.AddListener("PlayerEnterCloakField", __instance.OnPlayerEnterCloakField); GlobalMessenger.AddListener("PlayerExitCloakField", __instance.OnPlayerExitCloakField); } @@ -18,6 +20,8 @@ namespace NewHorizons.Patches [HarmonyPatch(typeof(HUDMarker), nameof(HUDMarker.OnDestroy))] public static void HUDMarker_OnDestroy(HUDMarker __instance) { + GlobalMessenger.RemoveListener("RefreshHUDVisibility", __instance.RefreshOwnVisibility); + GlobalMessenger.RemoveListener("RefreshHUDVisibility", __instance.RefreshOwnVisibility); GlobalMessenger.RemoveListener("PlayerEnterCloakField", __instance.OnPlayerEnterCloakField); GlobalMessenger.RemoveListener("PlayerExitCloakField", __instance.OnPlayerExitCloakField); } @@ -54,11 +58,67 @@ namespace NewHorizons.Patches GlobalMessenger.RemoveListener("ShipExitCloakField", __instance.RefreshOwnVisibility); } + [HarmonyPrefix] + [HarmonyPatch(typeof(ProbeHUDMarker), nameof(ProbeHUDMarker.RefreshOwnVisibility))] + public static bool ProbeHUDMarker_RefreshOwnVisibility(ProbeHUDMarker __instance) + { + bool insideEYE = Locator.GetEyeStateManager() != null && Locator.GetEyeStateManager().IsInsideTheEye(); + bool insideQM = __instance._quantumMoon != null && (__instance._quantumMoon.IsPlayerInside() || __instance._quantumMoon.IsProbeInside()); + bool insideRW = Locator.GetRingWorldController() != null && Locator.GetRingWorldController().isPlayerInside == Locator.GetRingWorldController().isProbeInside; + bool insideIP = Locator.GetCloakFieldController() != null && Locator.GetCloakFieldController().isPlayerInsideCloak == Locator.GetCloakFieldController().isProbeInsideCloak; + bool insideCloak = Components.CloakSectorController.isPlayerInside == Components.CloakSectorController.isProbeInside; + bool sameInterference = InterferenceHandler.IsPlayerSameAsProbe(); + bool isActive = __instance.gameObject.activeInHierarchy || __instance._isTLCDuplicate; + + __instance._isVisible = isActive && !insideEYE && !insideQM && !__instance._translatorEquipped && !__instance._inConversation && __instance._launched && (__instance._isWearingHelmet || __instance._atFlightConsole) && insideRW && insideIP && insideCloak && sameInterference; + + if (__instance._canvasMarker != null) __instance._canvasMarker.SetVisibility(__instance._isVisible); + + return false; + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(ShipHUDMarker), nameof(ShipHUDMarker.RefreshOwnVisibility))] + public static bool ShipHUDMarker_RefreshOwnVisibility(ShipHUDMarker __instance) + { + bool insideEYE = Locator.GetEyeStateManager() != null && Locator.GetEyeStateManager().IsInsideTheEye(); + bool insideQM = __instance._quantumMoon != null && (__instance._quantumMoon.IsPlayerInside() || __instance._quantumMoon.IsShipInside()); + bool insideRW = Locator.GetRingWorldController() != null && Locator.GetRingWorldController().isPlayerInside; + bool insideIP = Locator.GetCloakFieldController() != null ? true : Locator.GetCloakFieldController().isPlayerInsideCloak == Locator.GetCloakFieldController().isShipInsideCloak; + bool insideCloak = Components.CloakSectorController.isPlayerInside == Components.CloakSectorController.isShipInside; + bool sameInterference = InterferenceHandler.IsPlayerSameAsShip(); + + __instance._isVisible = !insideEYE && !insideQM && !insideRW && !__instance._translatorEquipped && !__instance._inConversation && !__instance._shipDestroyed && !__instance._playerInShip && PlayerState.HasPlayerEnteredShip() && __instance._isWearingHelmet && insideIP && insideCloak && sameInterference; + + if (__instance._canvasMarker != null) __instance._canvasMarker.SetVisibility(__instance._isVisible); + + return false; + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(ShipLogEntryHUDMarker), nameof(ShipLogEntryHUDMarker.RefreshOwnVisibility))] + public static bool ShipLogEntryHUDMarker_RefreshOwnVisibility(ShipLogEntryHUDMarker __instance) + { + bool hasEntryLocation = ShipLogEntryHUDMarker.s_entryLocation != null; + bool insideEYE = Locator.GetEyeStateManager() != null && Locator.GetEyeStateManager().IsInsideTheEye(); + bool insideQM = __instance._quantumMoon != null && __instance._quantumMoon.IsPlayerInside(); + bool insideRW = Locator.GetRingWorldController() != null && Locator.GetRingWorldController().isPlayerInside && ShipLogEntryHUDMarker.s_entryLocationID == "IP_RING_WORLD"; + bool insideIP = (hasEntryLocation && ShipLogEntryHUDMarker.s_entryLocation.IsWithinCloakField()) || !(Locator.GetCloakFieldController() != null && Locator.GetCloakFieldController().isPlayerInsideCloak); + bool insideCloak = (hasEntryLocation && ShipLogEntryHUDMarker.s_entryLocation.IsWithinCloakField()) || !Components.CloakSectorController.isPlayerInside; + + __instance._isVisible = (!insideEYE && !insideQM && !insideRW && !__instance._translatorEquipped && !__instance._inConversation && hasEntryLocation && (__instance._isWearingHelmet || __instance._atFlightConsole) && insideIP && insideCloak); + + if (__instance._canvasMarker != null) __instance._canvasMarker.SetVisibility(__instance._isVisible); + + return false; + } + + [HarmonyPostfix] [HarmonyPatch(typeof(ProbeCamera), nameof(ProbeCamera.HasInterference))] public static void ProbeCamera_HasInterference(ProbeCamera __instance, ref bool __result) { - __result = __result || (__instance._id != ProbeCamera.ID.PreLaunch && (Components.CloakSectorController.isPlayerInside != Components.CloakSectorController.isProbeInside || InterferenceHandler.PlayerHasInterference() != InterferenceHandler.ProbeHasInterference())); + __result = __result || (__instance._id != ProbeCamera.ID.PreLaunch && (Components.CloakSectorController.isPlayerInside != Components.CloakSectorController.isProbeInside || !InterferenceHandler.IsPlayerSameAsProbe())); } } } From 3d1e450d6d68c6812b20f499e2570796f5af1a7b Mon Sep 17 00:00:00 2001 From: TerrificTrifid <99054745+TerrificTrifid@users.noreply.github.com> Date: Fri, 2 Sep 2022 19:15:49 -0500 Subject: [PATCH 087/176] Disable starlights when out of range --- NewHorizons/Builder/Body/StarBuilder.cs | 1 - .../StarEvolutionController.cs | 3 ++ NewHorizons/Components/StarLightController.cs | 33 +++++++++++++++++-- NewHorizons/Handlers/PlanetCreationHandler.cs | 4 +++ .../Handlers/PlanetDestructionHandler.cs | 1 + 5 files changed, 39 insertions(+), 3 deletions(-) diff --git a/NewHorizons/Builder/Body/StarBuilder.cs b/NewHorizons/Builder/Body/StarBuilder.cs index 7e32c3d3..99a6ee9f 100644 --- a/NewHorizons/Builder/Body/StarBuilder.cs +++ b/NewHorizons/Builder/Body/StarBuilder.cs @@ -126,7 +126,6 @@ namespace NewHorizons.Builder.Body light.CopyPropertiesFrom(SearchUtilities.Find("Sun_Body/Sector_SUN/Effects_SUN/SunLight").GetComponent()); light.intensity *= starModule.solarLuminosity; light.range = starModule.lightRadius; - light.range *= Mathf.Sqrt(starModule.solarLuminosity); Color lightColour = light.color; if (starModule.lightTint != null) lightColour = starModule.lightTint.ToColor(); diff --git a/NewHorizons/Components/SizeControllers/StarEvolutionController.cs b/NewHorizons/Components/SizeControllers/StarEvolutionController.cs index ce904cca..164c2a20 100644 --- a/NewHorizons/Components/SizeControllers/StarEvolutionController.cs +++ b/NewHorizons/Components/SizeControllers/StarEvolutionController.cs @@ -287,6 +287,8 @@ namespace NewHorizons.Components.SizeControllers _stellarRemnant.SetActive(true); var remnantStarController = _stellarRemnant.GetComponentInChildren(); if (remnantStarController != null) StarLightController.AddStar(remnantStarController); + var remnantStarLight = _stellarRemnant.FindChild("SunLight"); + if (remnantStarLight != null) StarLightController.AddStarLight(remnantStarLight.GetComponent()); } if (Time.time > _supernovaStartTime + supernovaTime) @@ -300,6 +302,7 @@ namespace NewHorizons.Components.SizeControllers private void DisableStar(bool start = false) { if (controller != null) StarLightController.RemoveStar(controller); + StarLightController.RemoveStarLight(gameObject.FindChild("SunLight").GetAddComponent()); if (_stellarRemnant != null) { diff --git a/NewHorizons/Components/StarLightController.cs b/NewHorizons/Components/StarLightController.cs index 55a79074..a9e2b000 100644 --- a/NewHorizons/Components/StarLightController.cs +++ b/NewHorizons/Components/StarLightController.cs @@ -14,6 +14,7 @@ namespace NewHorizons.Components public static StarLightController Instance { get; private set; } private List _stars = new List(); + private List _lights = new List(); private StarController _activeStar; private SunLightController _sunLightController; @@ -53,6 +54,20 @@ namespace NewHorizons.Components } } + public static void AddStarLight(Light light) + { + if (light == null) return; + + Instance._lights.Add(light); + } + + public static void RemoveStarLight(Light light) + { + if (light == null) return; + + if (Instance._lights.Contains(light)) Instance._lights.Remove(light); + } + public void Update() { if (_activeStar == null || !_activeStar.gameObject.activeInHierarchy) @@ -78,13 +93,14 @@ namespace NewHorizons.Components material.SetFloat(SunIntensity, intensity); } + // Player is always at 0,0,0 more or less so if they arent using the map camera then wtv + var origin = Vector3.zero; + foreach (var star in _stars) { if (star == null) continue; if (!(star.gameObject.activeSelf && star.gameObject.activeInHierarchy)) continue; - // Player is always at 0,0,0 more or less so if they arent using the map camera then wtv - var origin = Vector3.zero; if (PlayerState.InMapView()) { origin = Locator.GetActiveCamera().transform.position; @@ -96,6 +112,19 @@ namespace NewHorizons.Components break; } } + + if (PlayerState.InMapView()) + { + foreach (var light in _lights) light.enabled = true; + return; + } + + foreach (var light in _lights) + { + // Minimum 50km range so it's not badly noticeable for dim stars + if ((light.transform.position - origin).sqrMagnitude <= Mathf.Max(light.range * light.range, 2500000000)) light.enabled = true; + else light.enabled = false; + } } private void ChangeActiveStar(StarController star) diff --git a/NewHorizons/Handlers/PlanetCreationHandler.cs b/NewHorizons/Handlers/PlanetCreationHandler.cs index 67700c36..84f97aea 100644 --- a/NewHorizons/Handlers/PlanetCreationHandler.cs +++ b/NewHorizons/Handlers/PlanetCreationHandler.cs @@ -59,6 +59,7 @@ namespace NewHorizons.Handlers starLightGO.AddComponent(); StarLightController.AddStar(starController); + StarLightController.AddStarLight(starController.Light); starLightGO.SetActive(true); @@ -500,6 +501,9 @@ namespace NewHorizons.Handlers if (starController != null) StarLightController.AddStar(starController); + var starLight = star.FindChild("SunLight"); + if (starLight != null) StarLightController.AddStarLight(starLight.GetComponent()); + // If it has an evolution controller that means it will die -> we make a remnant (unless its a remnant) if (starEvolutionController != null && !body.Config.isStellarRemnant) { diff --git a/NewHorizons/Handlers/PlanetDestructionHandler.cs b/NewHorizons/Handlers/PlanetDestructionHandler.cs index 4d60a271..b92e7972 100644 --- a/NewHorizons/Handlers/PlanetDestructionHandler.cs +++ b/NewHorizons/Handlers/PlanetDestructionHandler.cs @@ -118,6 +118,7 @@ namespace NewHorizons.Handlers case AstroObject.Name.Sun: var starController = ao.gameObject.GetComponent(); StarLightController.RemoveStar(starController); + StarLightController.RemoveStarLight(ao.gameObject.FindChild("SunLight").GetComponent()); GameObject.Destroy(starController); var audio = ao.GetComponentInChildren(); From 680314645d868d49769f5a12adb8a37112af9e53 Mon Sep 17 00:00:00 2001 From: JohnCorby Date: Fri, 2 Sep 2022 21:21:40 -0700 Subject: [PATCH 088/176] fix find again by not including prefabs --- NewHorizons/Utility/SearchUtilities.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/NewHorizons/Utility/SearchUtilities.cs b/NewHorizons/Utility/SearchUtilities.cs index 6f4d1be5..7ecee7fe 100644 --- a/NewHorizons/Utility/SearchUtilities.cs +++ b/NewHorizons/Utility/SearchUtilities.cs @@ -119,9 +119,9 @@ namespace NewHorizons.Utility var name = names.Last(); if (warn) Logger.LogWarning($"Couldn't find object in path {path}, will look for potential matches for name {name}"); - // 3: find resource to include inactive objects - // also includes prefabs but hopefully thats okay - go = FindResourceOfTypeAndName(name); + // 3: find resource to include inactive objects (but skip prefabs + go = Resources.FindObjectsOfTypeAll() + .FirstOrDefault(x => x.scene.name != null && x.name == name); if (go) { CachedGameObjects.Add(path, go); From 5cf4150deb75f5c093cef7bd25b51aa5c8921ac8 Mon Sep 17 00:00:00 2001 From: Will Corby Date: Fri, 2 Sep 2022 21:27:05 -0700 Subject: [PATCH 089/176] Update SearchUtilities.cs --- NewHorizons/Utility/SearchUtilities.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/Utility/SearchUtilities.cs b/NewHorizons/Utility/SearchUtilities.cs index 7ecee7fe..8346077b 100644 --- a/NewHorizons/Utility/SearchUtilities.cs +++ b/NewHorizons/Utility/SearchUtilities.cs @@ -121,7 +121,7 @@ namespace NewHorizons.Utility if (warn) Logger.LogWarning($"Couldn't find object in path {path}, will look for potential matches for name {name}"); // 3: find resource to include inactive objects (but skip prefabs go = Resources.FindObjectsOfTypeAll() - .FirstOrDefault(x => x.scene.name != null && x.name == name); + .FirstOrDefault(x.name == name && x => x.scene.name != null); if (go) { CachedGameObjects.Add(path, go); From 7f801039badb7ac80b0f6ac7a66ee335196b3719 Mon Sep 17 00:00:00 2001 From: Will Corby Date: Fri, 2 Sep 2022 21:27:30 -0700 Subject: [PATCH 090/176] Update SearchUtilities.cs --- NewHorizons/Utility/SearchUtilities.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/Utility/SearchUtilities.cs b/NewHorizons/Utility/SearchUtilities.cs index 8346077b..97cb4a5e 100644 --- a/NewHorizons/Utility/SearchUtilities.cs +++ b/NewHorizons/Utility/SearchUtilities.cs @@ -121,7 +121,7 @@ namespace NewHorizons.Utility if (warn) Logger.LogWarning($"Couldn't find object in path {path}, will look for potential matches for name {name}"); // 3: find resource to include inactive objects (but skip prefabs go = Resources.FindObjectsOfTypeAll() - .FirstOrDefault(x.name == name && x => x.scene.name != null); + .FirstOrDefault(x => x.name == name && x.scene.name != null); if (go) { CachedGameObjects.Add(path, go); From c3019cafcfc9aa16a91cf67d8250e644be382ae1 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Sat, 3 Sep 2022 10:17:16 -0400 Subject: [PATCH 091/176] Comment --- NewHorizons/Builder/Props/NomaiTextBuilder.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NewHorizons/Builder/Props/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiTextBuilder.cs index 06a32eed..f0e093d2 100644 --- a/NewHorizons/Builder/Props/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiTextBuilder.cs @@ -491,6 +491,8 @@ namespace NewHorizons.Builder.Props } trailmarkerObject.transform.position = planetGO.transform.TransformPoint(info?.position ?? Vector3.zero); + + // shrink because that is what mobius does on all trailmarkers or else they are the size of the player trailmarkerObject.transform.localScale = Vector3.one * 0.75f; if (info.rotation != null) From 57e26794d9b4fa9a281184cd7282cacff4c921d4 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Sat, 3 Sep 2022 10:53:49 -0400 Subject: [PATCH 092/176] Add parentPath to volumes --- NewHorizons/Builder/ShipLog/RevealBuilder.cs | 15 +++++++++++++++ NewHorizons/Builder/Volumes/AudioVolumeBuilder.cs | 14 ++++++++++++++ .../Builder/Volumes/HazardVolumeBuilder.cs | 14 ++++++++++++++ .../Builder/Volumes/NotificationVolumeBuilder.cs | 14 ++++++++++++++ NewHorizons/Builder/Volumes/VolumeBuilder.cs | 15 +++++++++++++++ NewHorizons/External/Modules/VolumesModule.cs | 5 +++++ 6 files changed, 77 insertions(+) diff --git a/NewHorizons/Builder/ShipLog/RevealBuilder.cs b/NewHorizons/Builder/ShipLog/RevealBuilder.cs index 95f118b6..5678b1d3 100644 --- a/NewHorizons/Builder/ShipLog/RevealBuilder.cs +++ b/NewHorizons/Builder/ShipLog/RevealBuilder.cs @@ -41,7 +41,22 @@ namespace NewHorizons.Builder.ShipLog GameObject revealTriggerVolume = new GameObject("Reveal Volume (" + info.revealOn + ")"); revealTriggerVolume.SetActive(false); revealTriggerVolume.transform.parent = sector?.transform ?? planetGO.transform; + + if (!string.IsNullOrEmpty(info.parentPath)) + { + var newParent = planetGO.transform.Find(info.parentPath); + if (newParent != null) + { + go.transform.parent = newParent; + } + else + { + Logger.LogWarning($"Cannot find parent object at path: {planetGO.name}/{info.parentPath}"); + } + } + revealTriggerVolume.transform.position = planetGO.transform.TransformPoint(info.position ?? Vector3.zero); + return revealTriggerVolume; } diff --git a/NewHorizons/Builder/Volumes/AudioVolumeBuilder.cs b/NewHorizons/Builder/Volumes/AudioVolumeBuilder.cs index b487670a..2952f590 100644 --- a/NewHorizons/Builder/Volumes/AudioVolumeBuilder.cs +++ b/NewHorizons/Builder/Volumes/AudioVolumeBuilder.cs @@ -19,6 +19,20 @@ namespace NewHorizons.Builder.Volumes go.SetActive(false); go.transform.parent = sector?.transform ?? planetGO.transform; + + if (!string.IsNullOrEmpty(info.parentPath)) + { + var newParent = planetGO.transform.Find(info.parentPath); + if (newParent != null) + { + go.transform.parent = newParent; + } + else + { + Logger.LogWarning($"Cannot find parent object at path: {planetGO.name}/{info.parentPath}"); + } + } + go.transform.position = planetGO.transform.TransformPoint(info.position != null ? (Vector3)info.position : Vector3.zero); go.layer = LayerMask.NameToLayer("AdvancedEffectVolume"); diff --git a/NewHorizons/Builder/Volumes/HazardVolumeBuilder.cs b/NewHorizons/Builder/Volumes/HazardVolumeBuilder.cs index 67c6f22c..ef652a12 100644 --- a/NewHorizons/Builder/Volumes/HazardVolumeBuilder.cs +++ b/NewHorizons/Builder/Volumes/HazardVolumeBuilder.cs @@ -16,6 +16,20 @@ namespace NewHorizons.Builder.Volumes go.SetActive(false); go.transform.parent = sector?.transform ?? planetGO.transform; + + if (!string.IsNullOrEmpty(info.parentPath)) + { + var newParent = planetGO.transform.Find(info.parentPath); + if (newParent != null) + { + go.transform.parent = newParent; + } + else + { + Logger.LogWarning($"Cannot find parent object at path: {planetGO.name}/{info.parentPath}"); + } + } + go.transform.position = planetGO.transform.TransformPoint(info.position != null ? (Vector3)info.position : Vector3.zero); go.layer = LayerMask.NameToLayer("BasicEffectVolume"); diff --git a/NewHorizons/Builder/Volumes/NotificationVolumeBuilder.cs b/NewHorizons/Builder/Volumes/NotificationVolumeBuilder.cs index c91e4161..496a9ea7 100644 --- a/NewHorizons/Builder/Volumes/NotificationVolumeBuilder.cs +++ b/NewHorizons/Builder/Volumes/NotificationVolumeBuilder.cs @@ -21,6 +21,20 @@ namespace NewHorizons.Builder.Volumes go.SetActive(false); go.transform.parent = sector?.transform ?? planetGO.transform; + + if (!string.IsNullOrEmpty(info.parentPath)) + { + var newParent = planetGO.transform.Find(info.parentPath); + if (newParent != null) + { + go.transform.parent = newParent; + } + else + { + Logger.LogWarning($"Cannot find parent object at path: {planetGO.name}/{info.parentPath}"); + } + } + go.transform.position = planetGO.transform.TransformPoint(info.position != null ? (Vector3)info.position : Vector3.zero); go.layer = LayerMask.NameToLayer("BasicEffectVolume"); diff --git a/NewHorizons/Builder/Volumes/VolumeBuilder.cs b/NewHorizons/Builder/Volumes/VolumeBuilder.cs index 0cf3ebe8..70e6c1f7 100644 --- a/NewHorizons/Builder/Volumes/VolumeBuilder.cs +++ b/NewHorizons/Builder/Volumes/VolumeBuilder.cs @@ -1,6 +1,7 @@ using NewHorizons.Components; using NewHorizons.External.Modules; using UnityEngine; +using Logger = NewHorizons.Utility.Logger; namespace NewHorizons.Builder.Volumes { @@ -12,6 +13,20 @@ namespace NewHorizons.Builder.Volumes go.SetActive(false); go.transform.parent = sector?.transform ?? planetGO.transform; + + if (!string.IsNullOrEmpty(info.parentPath)) + { + var newParent = planetGO.transform.Find(info.parentPath); + if (newParent != null) + { + go.transform.parent = newParent; + } + else + { + Logger.LogWarning($"Cannot find parent object at path: {planetGO.name}/{info.parentPath}"); + } + } + go.transform.position = planetGO.transform.TransformPoint(info.position != null ? (Vector3)info.position : Vector3.zero); go.layer = LayerMask.NameToLayer("BasicEffectVolume"); diff --git a/NewHorizons/External/Modules/VolumesModule.cs b/NewHorizons/External/Modules/VolumesModule.cs index d144b595..a3c810a0 100644 --- a/NewHorizons/External/Modules/VolumesModule.cs +++ b/NewHorizons/External/Modules/VolumesModule.cs @@ -66,6 +66,11 @@ namespace NewHorizons.External.Modules /// The radius of this volume. /// public float radius = 1f; + + /// + /// The relative path from the planet to the parent of this object. Optional (will default to the root sector). + /// + public string parentPath; } [JsonObject] From f909832cb23288cf9303e413089418bb59ee3d50 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Sat, 3 Sep 2022 11:01:07 -0400 Subject: [PATCH 093/176] comment --- NewHorizons/Builder/Volumes/VolumeBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/Builder/Volumes/VolumeBuilder.cs b/NewHorizons/Builder/Volumes/VolumeBuilder.cs index 70e6c1f7..afeffd73 100644 --- a/NewHorizons/Builder/Volumes/VolumeBuilder.cs +++ b/NewHorizons/Builder/Volumes/VolumeBuilder.cs @@ -7,7 +7,7 @@ namespace NewHorizons.Builder.Volumes { public static class VolumeBuilder { - public static TVolume Make(GameObject planetGO, Sector sector, VolumesModule.VolumeInfo info) where TVolume : MonoBehaviour + public static TVolume Make(GameObject planetGO, Sector sector, VolumesModule.VolumeInfo info) where TVolume : MonoBehaviour //Could be BaseVolume but I need to create vanilla volumes too. { var go = new GameObject(typeof(TVolume).Name); go.SetActive(false); From 12e95bce0bd0e7e60821f093f6cce6794d4dc899 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Sat, 3 Sep 2022 11:04:01 -0400 Subject: [PATCH 094/176] Indeed --- NewHorizons/Builder/ShipLog/RevealBuilder.cs | 2 +- NewHorizons/Builder/Volumes/HazardVolumeBuilder.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/NewHorizons/Builder/ShipLog/RevealBuilder.cs b/NewHorizons/Builder/ShipLog/RevealBuilder.cs index 5678b1d3..59662218 100644 --- a/NewHorizons/Builder/ShipLog/RevealBuilder.cs +++ b/NewHorizons/Builder/ShipLog/RevealBuilder.cs @@ -47,7 +47,7 @@ namespace NewHorizons.Builder.ShipLog var newParent = planetGO.transform.Find(info.parentPath); if (newParent != null) { - go.transform.parent = newParent; + revealTriggerVolume.transform.parent = newParent; } else { diff --git a/NewHorizons/Builder/Volumes/HazardVolumeBuilder.cs b/NewHorizons/Builder/Volumes/HazardVolumeBuilder.cs index ef652a12..d52b4352 100644 --- a/NewHorizons/Builder/Volumes/HazardVolumeBuilder.cs +++ b/NewHorizons/Builder/Volumes/HazardVolumeBuilder.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using UnityEngine; +using Logger = NewHorizons.Utility.Logger; namespace NewHorizons.Builder.Volumes { From ad79680388d8b70721778e39b7d2d8516bc7c0fb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 3 Sep 2022 15:06:19 +0000 Subject: [PATCH 095/176] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index 155650f1..63a70345 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -2457,6 +2457,10 @@ "description": "The radius of this volume.", "format": "float" }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "audio": { "type": "string", "description": "The audio to use. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." @@ -2526,6 +2530,10 @@ "description": "The radius of this volume.", "format": "float" }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "type": { "description": "The type of hazard for this volume.", "default": "general", @@ -2599,6 +2607,10 @@ "type": "number", "description": "The radius of this volume.", "format": "float" + }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." } } }, @@ -2615,6 +2627,10 @@ "description": "The radius of this volume.", "format": "float" }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "target": { "description": "What the notification will show for.", "default": "all", @@ -2673,6 +2689,10 @@ "description": "The radius of this volume.", "format": "float" }, + "parentPath": { + "type": "string", + "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, "maxAngle": { "type": "number", "description": "The max view angle (in degrees) the player can see the volume with to unlock the fact (`observe` only)", From a422bfbc7156fd0f2cd3f9c0bbfdee417781cc66 Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 3 Sep 2022 11:30:01 -0400 Subject: [PATCH 096/176] Invoke the interaction event to equip the suit --- .../Builder/General/SpawnPointBuilder.cs | 52 ++++++++----------- 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/NewHorizons/Builder/General/SpawnPointBuilder.cs b/NewHorizons/Builder/General/SpawnPointBuilder.cs index f094a57a..a224333d 100644 --- a/NewHorizons/Builder/General/SpawnPointBuilder.cs +++ b/NewHorizons/Builder/General/SpawnPointBuilder.cs @@ -1,5 +1,7 @@ using NewHorizons.External.Modules; using NewHorizons.Utility; +using System; +using System.Reflection; using UnityEngine; using Logger = NewHorizons.Utility.Logger; namespace NewHorizons.Builder.General @@ -89,38 +91,28 @@ namespace NewHorizons.Builder.General public static void SuitUp() { suitUpQueued = false; - if (Locator.GetPlayerController()._isWearingSuit) return; - - Locator.GetPlayerTransform().GetComponent().SuitUp(false, true, true); - - // Make the ship act as if the player took the suit - var spv = SearchUtilities.Find("Ship_Body/Module_Supplies/Systems_Supplies/ExpeditionGear")?.GetComponent(); - - if (spv == null) return; - - spv._containsSuit = false; - - if (spv._allowSuitReturn) + if (!Locator.GetPlayerController()._isWearingSuit) { - spv._interactVolume.ChangePrompt(UITextType.ReturnSuitPrompt, spv._pickupSuitCommandIndex); + var spv = SearchUtilities.Find("Ship_Body/Module_Supplies/Systems_Supplies/ExpeditionGear")?.GetComponent(); + if (spv != null) + { + var command = spv._interactVolume.GetInteractionAt(spv._pickupSuitCommandIndex).inputCommand; + + // Make the ship act as if the player took the suit + var eventDelegate = (MulticastDelegate)typeof(MultipleInteractionVolume).GetField( + nameof(MultipleInteractionVolume.OnPressInteract), + BindingFlags.Instance | BindingFlags.NonPublic) + .GetValue(spv._interactVolume); + foreach (var handler in eventDelegate.GetInvocationList()) + { + handler.Method.Invoke(handler.Target, new object[] { command }); + } + } + else + { + Locator.GetPlayerTransform().GetComponent().SuitUp(false, true, true); + } } - else - { - spv._interactVolume.EnableSingleInteraction(false, spv._pickupSuitCommandIndex); - } - - spv._timer = 0f; - spv._index = 0; - - spv.OnSuitUp(); - - GameObject suitGeometry = spv._suitGeometry; - if (suitGeometry != null) suitGeometry.SetActive(false); - - OWCollider suitOWCollider = spv._suitOWCollider; - if (suitOWCollider != null) suitOWCollider.SetActivation(false); - - spv.enabled = true; } } } From 1c67f9ce7f10962d93cb9e958c62d6c125fef564 Mon Sep 17 00:00:00 2001 From: TerrificTrifid <99054745+TerrificTrifid@users.noreply.github.com> Date: Sat, 3 Sep 2022 15:19:08 -0500 Subject: [PATCH 097/176] Use cloud height for effects --- NewHorizons/Builder/Atmosphere/EffectsBuilder.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/NewHorizons/Builder/Atmosphere/EffectsBuilder.cs b/NewHorizons/Builder/Atmosphere/EffectsBuilder.cs index c177a9ed..6394e02f 100644 --- a/NewHorizons/Builder/Atmosphere/EffectsBuilder.cs +++ b/NewHorizons/Builder/Atmosphere/EffectsBuilder.cs @@ -21,7 +21,6 @@ namespace NewHorizons.Builder.Atmosphere SCG._waitForStreaming = false; var minHeight = surfaceSize; - var maxHeight = config.Atmosphere.size; if (config.HeightMap?.minHeight != null) { if (config.Water?.size >= config.HeightMap.minHeight) minHeight = config.Water.size; // use sea level if its higher @@ -30,6 +29,9 @@ namespace NewHorizons.Builder.Atmosphere else if (config.Water?.size != null) minHeight = config.Water.size; else if (config.Lava?.size != null) minHeight = config.Lava.size; + var maxHeight = config.Atmosphere.size; + if (config.Atmosphere.clouds?.outerCloudRadius != null) maxHeight = config.Atmosphere.clouds.outerCloudRadius; + if (config.Atmosphere.hasRain) { var rainGO = GameObject.Instantiate(SearchUtilities.Find("GiantsDeep_Body/Sector_GD/Sector_GDInterior/Effects_GDInterior/Effects_GD_Rain"), effectsGO.transform); From 29c109e28c6d1eb20fad17df0998100529134f72 Mon Sep 17 00:00:00 2001 From: TerrificTrifid <99054745+TerrificTrifid@users.noreply.github.com> Date: Sat, 3 Sep 2022 17:01:44 -0500 Subject: [PATCH 098/176] Don't show on title screen --- NewHorizons/Handlers/TitleSceneHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/Handlers/TitleSceneHandler.cs b/NewHorizons/Handlers/TitleSceneHandler.cs index b4fb22dc..ea435305 100644 --- a/NewHorizons/Handlers/TitleSceneHandler.cs +++ b/NewHorizons/Handlers/TitleSceneHandler.cs @@ -89,7 +89,7 @@ namespace NewHorizons.Handlers heightMap.minHeight = body.Config.HeightMap.minHeight * size / body.Config.HeightMap.maxHeight; heightMap.stretch = body.Config.HeightMap.stretch; } - if (body.Config.Atmosphere?.clouds?.texturePath != null) + if (body.Config.Atmosphere?.clouds?.texturePath != null && body.Config.Atmosphere?.clouds?.cloudsPrefab != CloudPrefabType.Transparent) { // Hacky but whatever I just want a sphere size = Mathf.Clamp(body.Config.Atmosphere.size / 10, minSize, maxSize); From f9882b5b00938f12835a1fd8ad214649f53ae153 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Sun, 4 Sep 2022 01:41:25 -0400 Subject: [PATCH 099/176] Use InterferenceVolume instead of id --- NewHorizons/Components/InterferenceVolume.cs | 26 ++++----- NewHorizons/Handlers/InterferenceHandler.cs | 59 ++++++++++++-------- 2 files changed, 47 insertions(+), 38 deletions(-) diff --git a/NewHorizons/Components/InterferenceVolume.cs b/NewHorizons/Components/InterferenceVolume.cs index 2b2881fd..43cf34ac 100644 --- a/NewHorizons/Components/InterferenceVolume.cs +++ b/NewHorizons/Components/InterferenceVolume.cs @@ -10,21 +10,19 @@ namespace NewHorizons.Components { public class InterferenceVolume : BaseVolume { - private string _id = Guid.NewGuid().ToString(); - public override void OnTriggerVolumeEntry(GameObject hitObj) { if (hitObj.CompareTag("PlayerDetector")) { - InterferenceHandler.OnPlayerEnterInterferenceVolume(_id); + OnPlayerEnter(); } else if (hitObj.CompareTag("ProbeDetector")) { - InterferenceHandler.OnProbeEnterInterferenceVolume(_id); + OnProbeEnter(); } else if (hitObj.CompareTag("ShipDetector")) { - InterferenceHandler.OnShipEnterInterferenceVolume(_id); + OnShipEnter(); } } @@ -32,25 +30,25 @@ namespace NewHorizons.Components { if (hitObj.CompareTag("PlayerDetector")) { - InterferenceHandler.OnPlayerExitInterferenceVolume(_id); + OnPlayerExit(); } else if (hitObj.CompareTag("ProbeDetector")) { - InterferenceHandler.OnProbeExitInterferenceVolume(_id); + OnProbeExit(); } else if (hitObj.CompareTag("ShipDetector")) { - InterferenceHandler.OnShipExitInterferenceVolume(_id); + OnShipExit(); } } - public void OnPlayerEnter() => InterferenceHandler.OnPlayerEnterInterferenceVolume(_id); - public void OnPlayerExit() => InterferenceHandler.OnPlayerExitInterferenceVolume(_id); + public void OnPlayerEnter() => InterferenceHandler.OnPlayerEnterInterferenceVolume(this); + public void OnPlayerExit() => InterferenceHandler.OnPlayerExitInterferenceVolume(this); - public void OnProbeEnter() => InterferenceHandler.OnProbeEnterInterferenceVolume(_id); - public void OnProbeExit() => InterferenceHandler.OnProbeExitInterferenceVolume(_id); + public void OnProbeEnter() => InterferenceHandler.OnProbeEnterInterferenceVolume(this); + public void OnProbeExit() => InterferenceHandler.OnProbeExitInterferenceVolume(this); - public void OnShipEnter() => InterferenceHandler.OnShipEnterInterferenceVolume(_id); - public void OnShipExit() => InterferenceHandler.OnShipExitInterferenceVolume(_id); + public void OnShipEnter() => InterferenceHandler.OnShipEnterInterferenceVolume(this); + public void OnShipExit() => InterferenceHandler.OnShipExitInterferenceVolume(this); } } diff --git a/NewHorizons/Handlers/InterferenceHandler.cs b/NewHorizons/Handlers/InterferenceHandler.cs index 2e854afa..97ff0046 100644 --- a/NewHorizons/Handlers/InterferenceHandler.cs +++ b/NewHorizons/Handlers/InterferenceHandler.cs @@ -6,58 +6,69 @@ using System.Threading.Tasks; namespace NewHorizons.Handlers { + using InterferenceVolume = NewHorizons.Components.InterferenceVolume; + public static class InterferenceHandler { - private static List _playerInterference; - private static List _probeInterference; - private static List _shipInterference; + private static List _playerInterference; + private static List _probeInterference; + private static List _shipInterference; public static void Init() { - _playerInterference = new List(); - _probeInterference = new List(); - _shipInterference = new List(); + _playerInterference = new List(); + _probeInterference = new List(); + _shipInterference = new List(); } - public static bool PlayerHasInterference() => _playerInterference.Any(); - public static bool ProbeHasInterference() => _probeInterference.Any(); - public static bool ShipHasInterference() => _shipInterference.Any(); + public static bool PlayerHasInterference() => _playerInterference.Any(volume => volume != null); + public static bool ProbeHasInterference() => _probeInterference.Any(volume => volume != null); + public static bool ShipHasInterference() => _shipInterference.Any(volume => volume != null); - public static bool IsPlayerSameAsProbe() => _playerInterference.All(_probeInterference.Contains) && _playerInterference.Count == _probeInterference.Count; - public static bool IsPlayerSameAsShip() => _playerInterference.All(_shipInterference.Contains) && _playerInterference.Count == _shipInterference.Count; - - public static void OnPlayerEnterInterferenceVolume(string id) + public static bool IsPlayerSameAsProbe() { - _playerInterference.SafeAdd(id); + _playerInterference.RemoveAll(volume => volume == null); + return _playerInterference.All(_probeInterference.Contains) && _playerInterference.Count == _probeInterference.Count; + } + + public static bool IsPlayerSameAsShip() + { + _playerInterference.RemoveAll(volume => volume == null); + return _playerInterference.All(_shipInterference.Contains) && _playerInterference.Count == _shipInterference.Count; + } + + public static void OnPlayerEnterInterferenceVolume(InterferenceVolume interferenceVolume) + { + _playerInterference.SafeAdd(interferenceVolume); GlobalMessenger.FireEvent("RefreshHUDVisibility"); } - public static void OnPlayerExitInterferenceVolume(string id) + public static void OnPlayerExitInterferenceVolume(InterferenceVolume interferenceVolume) { - _playerInterference.Remove(id); + _playerInterference.Remove(interferenceVolume); GlobalMessenger.FireEvent("RefreshHUDVisibility"); } - public static void OnProbeEnterInterferenceVolume(string id) + public static void OnProbeEnterInterferenceVolume(InterferenceVolume interferenceVolume) { - _probeInterference.SafeAdd(id); + _probeInterference.SafeAdd(interferenceVolume); GlobalMessenger.FireEvent("RefreshHUDVisibility"); } - public static void OnProbeExitInterferenceVolume(string id) + public static void OnProbeExitInterferenceVolume(InterferenceVolume interferenceVolume) { - _probeInterference.Remove(id); + _probeInterference.Remove(interferenceVolume); GlobalMessenger.FireEvent("RefreshHUDVisibility"); } - public static void OnShipEnterInterferenceVolume(string id) + public static void OnShipEnterInterferenceVolume(InterferenceVolume interferenceVolume) { - _shipInterference.SafeAdd(id); + _shipInterference.SafeAdd(interferenceVolume); GlobalMessenger.FireEvent("RefreshHUDVisibility"); } - public static void OnShipExitInterferenceVolume(string id) + public static void OnShipExitInterferenceVolume(InterferenceVolume interferenceVolume) { - _shipInterference.Remove(id); + _shipInterference.Remove(interferenceVolume); GlobalMessenger.FireEvent("RefreshHUDVisibility"); } } From 15d8d107e8cd130bc650fad339ad98e8db25bfbf Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Sun, 4 Sep 2022 20:51:17 -0400 Subject: [PATCH 100/176] Create a new material for that --- NewHorizons/Builder/Atmosphere/CloudsBuilder.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs b/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs index 7d06fc0c..d5b15f89 100644 --- a/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs +++ b/NewHorizons/Builder/Atmosphere/CloudsBuilder.cs @@ -253,7 +253,8 @@ namespace NewHorizons.Builder.Atmosphere MeshRenderer renderer = cloudsTransparentGO.AddComponent(); if (_transparentCloud == null) _transparentCloud = Main.NHAssetBundle.LoadAsset("Assets/Resources/TransparentCloud.mat"); - var material = _transparentCloud; + var material = new Material(_transparentCloud); + material.name = "TransparentClouds_" + image.name; material.SetTexture(MainTex, image); renderer.sharedMaterial = material; From 11e4bc70225c12ee6b2966ab6e996abce98a373c Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 5 Sep 2022 00:34:25 -0400 Subject: [PATCH 101/176] MenuFramework support, outdated version popup, one shot popups for addons --- NewHorizons/Assets/translations/english.json | 3 +- NewHorizons/Assets/translations/french.json | 3 +- NewHorizons/External/Configs/AddonConfig.cs | 5 ++ NewHorizons/External/NewHorizonsData.cs | 21 ++++++- NewHorizons/Main.cs | 38 ++++++++---- .../OtherMods/MenuFramework/IMenuAPI.cs | 20 +++++++ .../OtherMods/MenuFramework/MenuHandler.cs | 60 +++++++++++++++++++ NewHorizons/Utility/VersionUtility.cs | 25 ++++++++ NewHorizons/manifest.json | 2 +- 9 files changed, 160 insertions(+), 17 deletions(-) create mode 100644 NewHorizons/OtherMods/MenuFramework/IMenuAPI.cs create mode 100644 NewHorizons/OtherMods/MenuFramework/MenuHandler.cs create mode 100644 NewHorizons/Utility/VersionUtility.cs diff --git a/NewHorizons/Assets/translations/english.json b/NewHorizons/Assets/translations/english.json index b7aae651..ef553f33 100644 --- a/NewHorizons/Assets/translations/english.json +++ b/NewHorizons/Assets/translations/english.json @@ -14,7 +14,8 @@ "WARP_LOCKED": "AUTOPILOT LOCKED TO:\n{0}", "LOCK_AUTOPILOT_WARP": "Lock Autopilot to Star System", "RICH_PRESENCE_EXPLORING": "Exploring {0}.", - "RICH_PRESENCE_WARPING": "Warping to {0}." + "RICH_PRESENCE_WARPING": "Warping to {0}.", + "OUTDATED_VERSION_WARNING": "WARNING\n\nNew Horizons only works on version {0} or higher. You're on version {1}.\n\nPlease update your game or uninstall NH." }, "AchievementTranslations": { "NH_EATEN_OUTSIDE_BRAMBLE": { diff --git a/NewHorizons/Assets/translations/french.json b/NewHorizons/Assets/translations/french.json index 37adf8ac..48dc1d4c 100644 --- a/NewHorizons/Assets/translations/french.json +++ b/NewHorizons/Assets/translations/french.json @@ -14,7 +14,8 @@ "WARP_LOCKED": "PILOTE AUTOMATIQUE VISÉ SUR:\n{0}", "LOCK_AUTOPILOT_WARP": "Visez le pilote automatique", "RICH_PRESENCE_EXPLORING": "En explorant {0}.", - "RICH_PRESENCE_WARPING": "En route vers {0}." + "RICH_PRESENCE_WARPING": "En route vers {0}.", + "OUTDATED_VERSION_WARNING": "AVERTISSEMENT\n\nNew Horizons fonctionne seulement sur la version {0} ou plus récente. Vous êtes sur la version {1}.\n\nVeuillez mettre à jour votre jeu ou désinstaller NH." }, "AchievementTranslations": { "NH_EATEN_OUTSIDE_BRAMBLE": { diff --git a/NewHorizons/External/Configs/AddonConfig.cs b/NewHorizons/External/Configs/AddonConfig.cs index 08069e00..00257080 100644 --- a/NewHorizons/External/Configs/AddonConfig.cs +++ b/NewHorizons/External/Configs/AddonConfig.cs @@ -28,5 +28,10 @@ namespace NewHorizons.External.Configs /// Credits info for this mod. A list of contributors and their roles separated by #. For example: xen#New Horizons dev. /// public string[] credits; + + /// + /// A pop-up message for the first time a user runs the add-on + /// + public string popupMessage; } } diff --git a/NewHorizons/External/NewHorizonsData.cs b/NewHorizons/External/NewHorizonsData.cs index f90cb479..52a8cbf1 100644 --- a/NewHorizons/External/NewHorizonsData.cs +++ b/NewHorizons/External/NewHorizonsData.cs @@ -19,7 +19,7 @@ namespace NewHorizons.External _activeProfileName = GetProfileName(); if (_activeProfileName == null) { - Logger.LogError("Couldn't find active profile, are you on Gamepass?"); + Logger.LogWarning("Couldn't find active profile, are you on Gamepass?"); _activeProfileName = "XboxGamepassDefaultProfile"; } @@ -82,12 +82,13 @@ namespace NewHorizons.External KnownFrequencies = new List(); KnownSignals = new List(); NewlyRevealedFactIDs = new List(); + PopupsRead = new List(); } public List KnownFrequencies { get; } public List KnownSignals { get; } - public List NewlyRevealedFactIDs { get; } + public List PopupsRead { get; } } #region Frequencies @@ -155,5 +156,21 @@ namespace NewHorizons.External } #endregion + + #region Read popups + + public static void ReadOneTimePopup(string id) + { + _activeProfile?.PopupsRead.Add(id); + Save(); + } + + public static bool HasReadOneTimePopup(string id) + { + // To avoid spam, we'll just say the popup has been read if we can't load the profile + return _activeProfile?.PopupsRead.Contains(id) ?? true; + } + + #endregion } } \ No newline at end of file diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index 80f5dc53..5eaaeb8f 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -24,6 +24,7 @@ using UnityEngine.SceneManagement; using Logger = NewHorizons.Utility.Logger; using NewHorizons.OtherMods.OWRichPresence; using NewHorizons.Components.SizeControllers; +using NewHorizons.OtherMods.MenuFramework; namespace NewHorizons { @@ -164,11 +165,21 @@ namespace NewHorizons TextTranslation.Get().SetLanguage(TextTranslation.Get().GetLanguage()); } + public void Awake() + { + Instance = this; + } + public void Start() { // Patches Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly()); + // Has to go before popups + LoadTranslations(ModHelper.Manifest.ModFolderPath + "Assets/", this); + + MenuHandler.Init(); + OnChangeStarSystem = new StarSystemEvent(); OnStarSystemLoaded = new StarSystemEvent(); OnPlanetLoaded = new StarSystemEvent(); @@ -176,7 +187,6 @@ namespace NewHorizons SceneManager.sceneLoaded += OnSceneLoaded; SceneManager.sceneUnloaded += OnSceneUnloaded; - Instance = this; GlobalMessenger.AddListener("PlayerDeath", OnDeath); GlobalMessenger.AddListener("WakeUp", OnWakeUp); @@ -310,9 +320,6 @@ namespace NewHorizons BrambleNodeBuilder.Init(BodyDict[CurrentStarSystem].Select(x => x.Config).Where(x => x.Bramble?.dimension != null).ToArray()); StarEvolutionController.Init(); - // Has to go before loading planets else the Discord Rich Presence mod won't show the right text - LoadTranslations(ModHelper.Manifest.ModFolderPath + "Assets/", this); - if (isSolarSystem) { foreach (var supernovaPlanetEffectController in GameObject.FindObjectsOfType()) @@ -539,16 +546,19 @@ namespace NewHorizons } } } - // Has to go before translations for achievements + if (Directory.Exists(folder + @"translations\")) + { + LoadTranslations(folder, mod); + } + // Has to go before translations for achievements but after regular ones (for popups) if (File.Exists(folder + "addon-manifest.json")) { LoadAddonManifest("addon-manifest.json", mod); } if (Directory.Exists(folder + @"translations\")) { - LoadTranslations(folder, mod); + LoadAchievementTranslations(mod); } - } catch (Exception ex) { @@ -564,6 +574,7 @@ namespace NewHorizons if (addonConfig.achievements != null) AchievementHandler.RegisterAddon(addonConfig, mod as ModBehaviour); if (addonConfig.credits != null) CreditsHandler.RegisterCredits(mod.ModHelper.Manifest.Name, addonConfig.credits); + if (!string.IsNullOrEmpty(addonConfig.popupMessage)) MenuHandler.RegisterOneTimePopup(mod, addonConfig.popupMessage); } private void LoadTranslations(string folder, IModBehaviour mod) @@ -584,16 +595,19 @@ namespace NewHorizons foundFile = true; TranslationHandler.RegisterTranslation(language, config); - - if (AchievementHandler.Enabled) - { - AchievementHandler.RegisterTranslationsFromFiles(mod as ModBehaviour, "translations"); - } } } if (!foundFile) Logger.LogWarning($"{mod.ModHelper.Manifest.Name} has a folder for translations but none were loaded"); } + private void LoadAchievementTranslations(IModBehaviour mod) + { + if (AchievementHandler.Enabled) + { + AchievementHandler.RegisterTranslationsFromFiles(mod as ModBehaviour, "translations"); + } + } + public NewHorizonsBody LoadConfig(IModBehaviour mod, string relativePath) { NewHorizonsBody body = null; diff --git a/NewHorizons/OtherMods/MenuFramework/IMenuAPI.cs b/NewHorizons/OtherMods/MenuFramework/IMenuAPI.cs new file mode 100644 index 00000000..f44aecdf --- /dev/null +++ b/NewHorizons/OtherMods/MenuFramework/IMenuAPI.cs @@ -0,0 +1,20 @@ +using UnityEngine; +using UnityEngine.UI; + +namespace NewHorizons.OtherMods.MenuFramework +{ + public interface IMenuAPI + { + GameObject TitleScreen_MakeMenuOpenButton(string name, int index, Menu menuToOpen); + GameObject TitleScreen_MakeSceneLoadButton(string name, int index, SubmitActionLoadScene.LoadableScenes sceneToLoad, PopupMenu confirmPopup = null); + Button TitleScreen_MakeSimpleButton(string name, int index); + GameObject PauseMenu_MakeMenuOpenButton(string name, Menu menuToOpen, Menu customMenu = null); + GameObject PauseMenu_MakeSceneLoadButton(string name, SubmitActionLoadScene.LoadableScenes sceneToLoad, PopupMenu confirmPopup = null, Menu customMenu = null); + Button PauseMenu_MakeSimpleButton(string name, Menu customMenu = null); + Menu PauseMenu_MakePauseListMenu(string title); + PopupMenu MakeTwoChoicePopup(string message, string confirmText, string cancelText); + PopupInputMenu MakeInputFieldPopup(string message, string placeholderMessage, string confirmText, string cancelText); + PopupMenu MakeInfoPopup(string message, string continueButtonText); + void RegisterStartupPopup(string message); + } +} diff --git a/NewHorizons/OtherMods/MenuFramework/MenuHandler.cs b/NewHorizons/OtherMods/MenuFramework/MenuHandler.cs new file mode 100644 index 00000000..7d66258e --- /dev/null +++ b/NewHorizons/OtherMods/MenuFramework/MenuHandler.cs @@ -0,0 +1,60 @@ +using NewHorizons.External; +using NewHorizons.Handlers; +using NewHorizons.Utility; +using OWML.Common; +using System.Collections.Generic; +using System.Net.Mail; +using UnityEngine; +using static UnityEngine.InputSystem.InputRemoting; +using Logger = NewHorizons.Utility.Logger; + +namespace NewHorizons.OtherMods.MenuFramework +{ + public static class MenuHandler + { + private static IMenuAPI _menuApi; + + private static List<(IModBehaviour mod, string message)> _registeredPopups; + + public static void Init() + { + _menuApi = Main.Instance.ModHelper.Interaction.TryGetModApi("_nebula.MenuFramework"); + + TextTranslation.Get().OnLanguageChanged += OnLanguageChanged; + + _registeredPopups = new(); + } + + public static void OnLanguageChanged() + { + // Have to load save data before doing popups + NewHorizonsData.Load(); + + if (!VersionUtility.CheckUpToDate()) + { + var warning = string.Format(TranslationHandler.GetTranslation("OUTDATED_VERSION_WARNING", TranslationHandler.TextType.UI), + VersionUtility.RequiredVersionString, + Application.version); + + Logger.LogError(warning); + _menuApi.RegisterStartupPopup(warning); + } + + foreach(var (mod, message) in _registeredPopups) + { + if (!NewHorizonsData.HasReadOneTimePopup(mod.ModHelper.Manifest.UniqueName)) + { + _menuApi.RegisterStartupPopup(TranslationHandler.GetTranslation(message, TranslationHandler.TextType.UI)); + NewHorizonsData.ReadOneTimePopup(mod.ModHelper.Manifest.UniqueName); + } + } + + _registeredPopups.Clear(); + + // Just wanted to do this when the language is loaded in initially + TextTranslation.Get().OnLanguageChanged -= OnLanguageChanged; + } + + public static void RegisterOneTimePopup(IModBehaviour mod, string message) => _registeredPopups.Add((mod, message)); + } +} diff --git a/NewHorizons/Utility/VersionUtility.cs b/NewHorizons/Utility/VersionUtility.cs new file mode 100644 index 00000000..e7c0ea23 --- /dev/null +++ b/NewHorizons/Utility/VersionUtility.cs @@ -0,0 +1,25 @@ +using System.Linq; +using UnityEngine; + +namespace NewHorizons.Utility +{ + internal static class VersionUtility + { + public static int[] RequiredVersion => new int[] {1, 1, 12}; + public static string RequiredVersionString => string.Join(".", RequiredVersion); + + public static bool CheckUpToDate() + { + // If they're using an outdated game version we create an error popup here + var version = Application.version.Split('.').Select(x => int.Parse(x)).ToArray(); + var major = version[0]; + var minor = version[1]; + var patch = version[2]; + + // Must be at least 1.1.12 + return major > RequiredVersion[0] || + (major == RequiredVersion[0] && minor > RequiredVersion[1]) || + (major == RequiredVersion[0] && minor == RequiredVersion[1] && patch >= RequiredVersion[2]); + } + } +} diff --git a/NewHorizons/manifest.json b/NewHorizons/manifest.json index 5696ae38..a4be9c22 100644 --- a/NewHorizons/manifest.json +++ b/NewHorizons/manifest.json @@ -6,7 +6,7 @@ "uniqueName": "xen.NewHorizons", "version": "1.5.1", "owmlVersion": "2.6.0", - "dependencies": [ "JohnCorby.VanillaFix" ], + "dependencies": [ "JohnCorby.VanillaFix", "_nebula.MenuFramework" ], "conflicts": [ "Raicuparta.QuantumSpaceBuddies", "PacificEngine.OW_Randomizer" ], "pathsToPreserve": [ "planets", "systems", "translations" ] } From 332238c298dfbbf271ebaa56df2bfecf70f9ae16 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 5 Sep 2022 04:37:07 +0000 Subject: [PATCH 102/176] Updated Schemas --- NewHorizons/Schemas/addon_manifest_schema.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NewHorizons/Schemas/addon_manifest_schema.json b/NewHorizons/Schemas/addon_manifest_schema.json index bfe88b86..8d3c0da7 100644 --- a/NewHorizons/Schemas/addon_manifest_schema.json +++ b/NewHorizons/Schemas/addon_manifest_schema.json @@ -19,6 +19,10 @@ "type": "string" } }, + "popupMessage": { + "type": "string", + "description": "A pop-up message for the first time a user runs the add-on" + }, "$schema": { "type": "string", "description": "The schema to validate with" From ba65c949a69a0a32899aef7a02a4c53ac50c6f5b Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 5 Sep 2022 00:49:14 -0400 Subject: [PATCH 103/176] Clean up --- NewHorizons/Main.cs | 49 ++++++++++++++++++--------------------------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index f06bc48a..c1a541e8 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -1,16 +1,19 @@ using HarmonyLib; -using NewHorizons.OtherMods.AchievementsPlus; using NewHorizons.Builder.Atmosphere; using NewHorizons.Builder.Body; using NewHorizons.Builder.Props; using NewHorizons.Components; +using NewHorizons.Components.SizeControllers; using NewHorizons.External; using NewHorizons.External.Configs; using NewHorizons.Handlers; +using NewHorizons.OtherMods.AchievementsPlus; +using NewHorizons.OtherMods.MenuFramework; +using NewHorizons.OtherMods.OWRichPresence; +using NewHorizons.OtherMods.VoiceActing; using NewHorizons.Utility; using NewHorizons.Utility.DebugMenu; using NewHorizons.Utility.DebugUtilities; -using NewHorizons.OtherMods.VoiceActing; using OWML.Common; using OWML.ModHelper; using System; @@ -22,9 +25,6 @@ using UnityEngine; using UnityEngine.Events; using UnityEngine.SceneManagement; using Logger = NewHorizons.Utility.Logger; -using NewHorizons.OtherMods.OWRichPresence; -using NewHorizons.Components.SizeControllers; -using NewHorizons.OtherMods.MenuFramework; namespace NewHorizons { @@ -96,9 +96,9 @@ namespace NewHorizons DebugMenu.UpdatePauseMenuButton(); } - if (VerboseLogs) Logger.UpdateLogLevel(Logger.LogType.Verbose); - else if (Debug) Logger.UpdateLogLevel(Logger.LogType.Log); - else Logger.UpdateLogLevel(Logger.LogType.Error); + if (VerboseLogs) Logger.UpdateLogLevel(Logger.LogType.Verbose); + else if (Debug) Logger.UpdateLogLevel(Logger.LogType.Log); + else Logger.UpdateLogLevel(Logger.LogType.Error); _defaultSystemOverride = config.GetSettingsValue("Default System Override"); @@ -175,11 +175,6 @@ namespace NewHorizons // Patches Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly()); - // Has to go before popups - LoadTranslations(ModHelper.Manifest.ModFolderPath + "Assets/", this); - - MenuHandler.Init(); - OnChangeStarSystem = new StarSystemEvent(); OnStarSystemLoaded = new StarSystemEvent(); OnPlanetLoaded = new StarSystemEvent(); @@ -210,12 +205,14 @@ namespace NewHorizons Instance.ModHelper.Events.Unity.FireOnNextUpdate(() => _firstLoad = false); Instance.ModHelper.Menus.PauseMenu.OnInit += DebugReload.InitializePauseMenu; + MenuHandler.Init(); AchievementHandler.Init(); VoiceHandler.Init(); RichPresenceHandler.Init(); OnStarSystemLoaded.AddListener(RichPresenceHandler.OnStarSystemLoaded); OnChangeStarSystem.AddListener(RichPresenceHandler.OnChangeStarSystem); + LoadTranslations(ModHelper.Manifest.ModFolderPath + "Assets/", this); LoadAddonManifest("Assets/addon-manifest.json", this); } @@ -419,7 +416,7 @@ namespace NewHorizons var ssrLight = solarSystemRoot.AddComponent(); ssrLight.innerSpotAngle = 0; ssrLight.spotAngle = 179; - ssrLight.range = Main.FurthestOrbit * (4f/3f); + ssrLight.range = Main.FurthestOrbit * (4f / 3f); ssrLight.intensity = 0.001f; var fluid = playerBody.FindChild("PlayerDetector").GetComponent(); @@ -510,7 +507,7 @@ namespace NewHorizons if (starSystemConfig.startHere) { // We always want to allow mods to overwrite setting the main SolarSystem as default but not the other way around - if (name != "SolarSystem") + if (name != "SolarSystem") { SetDefaultSystem(name); _currentStarSystem = name; @@ -547,19 +544,16 @@ namespace NewHorizons } } } - if (Directory.Exists(folder + @"translations\")) - { - LoadTranslations(folder, mod); - } - // Has to go before translations for achievements but after regular ones (for popups) + // Has to go before translations for achievements if (File.Exists(folder + "addon-manifest.json")) { LoadAddonManifest("addon-manifest.json", mod); } if (Directory.Exists(folder + @"translations\")) { - LoadAchievementTranslations(mod); + LoadTranslations(folder, mod); } + } catch (Exception ex) { @@ -596,19 +590,16 @@ namespace NewHorizons foundFile = true; TranslationHandler.RegisterTranslation(language, config); + + if (AchievementHandler.Enabled) + { + AchievementHandler.RegisterTranslationsFromFiles(mod as ModBehaviour, "translations"); + } } } if (!foundFile) Logger.LogWarning($"{mod.ModHelper.Manifest.Name} has a folder for translations but none were loaded"); } - private void LoadAchievementTranslations(IModBehaviour mod) - { - if (AchievementHandler.Enabled) - { - AchievementHandler.RegisterTranslationsFromFiles(mod as ModBehaviour, "translations"); - } - } - public NewHorizonsBody LoadConfig(IModBehaviour mod, string relativePath) { NewHorizonsBody body = null; From 026891c5d8d4eab167ea7dfe24f1215986a0bdd1 Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 5 Sep 2022 01:11:54 -0400 Subject: [PATCH 104/176] Implement #167 + update schemas --- NewHorizons/Assets/translations/english.json | 5 +++-- NewHorizons/Assets/translations/french.json | 5 +++-- NewHorizons/Assets/translations/german.json | 2 +- NewHorizons/Assets/translations/russian.json | 2 +- NewHorizons/Assets/translations/spanish_la.json | 2 +- NewHorizons/Main.cs | 2 ++ .../OtherMods/MenuFramework/MenuHandler.cs | 17 ++++++++++++++--- 7 files changed, 25 insertions(+), 10 deletions(-) diff --git a/NewHorizons/Assets/translations/english.json b/NewHorizons/Assets/translations/english.json index ef553f33..82cb193e 100644 --- a/NewHorizons/Assets/translations/english.json +++ b/NewHorizons/Assets/translations/english.json @@ -1,5 +1,5 @@ { - "$schema": "https://raw.githubusercontent.com/xen-42/outer-wilds-new-horizons/main/NewHorizons/Schemas/translation_schema.json", + "$schema": "https://raw.githubusercontent.com/outer-wilds-new-horizons/new-horizons/main/NewHorizons/Schemas/translation_schema.json", "DialogueDictionary": { "NEW_HORIZONS_WARP_DRIVE_DIALOGUE_1": "Your ship is now equiped with a warp drive!", "NEW_HORIZONS_WARP_DRIVE_DIALOGUE_2": "You can use the new \"Interstellar Mode\" page in the ship log to lock-on your autopilot to another star system.", @@ -15,7 +15,8 @@ "LOCK_AUTOPILOT_WARP": "Lock Autopilot to Star System", "RICH_PRESENCE_EXPLORING": "Exploring {0}.", "RICH_PRESENCE_WARPING": "Warping to {0}.", - "OUTDATED_VERSION_WARNING": "WARNING\n\nNew Horizons only works on version {0} or higher. You're on version {1}.\n\nPlease update your game or uninstall NH." + "OUTDATED_VERSION_WARNING": "WARNING\n\nNew Horizons only works on version {0} or higher. You're on version {1}.\n\nPlease update your game or uninstall NH.", + "JSON_FAILED_TO_LOAD": "Invalid file(s): {0}" }, "AchievementTranslations": { "NH_EATEN_OUTSIDE_BRAMBLE": { diff --git a/NewHorizons/Assets/translations/french.json b/NewHorizons/Assets/translations/french.json index 48dc1d4c..5c6c1d19 100644 --- a/NewHorizons/Assets/translations/french.json +++ b/NewHorizons/Assets/translations/french.json @@ -1,5 +1,5 @@ { - "$schema": "https://raw.githubusercontent.com/xen-42/outer-wilds-new-horizons/main/NewHorizons/Schemas/translation_schema.json", + "$schema": "https://raw.githubusercontent.com/outer-wilds-new-horizons/new-horizons/main/NewHorizons/Schemas/translation_schema.json", "DialogueDictionary": { "NEW_HORIZONS_WARP_DRIVE_DIALOGUE_1": "Votre fusée est maintenant équipé d'un moteur de distorsion!", "NEW_HORIZONS_WARP_DRIVE_DIALOGUE_2": "Vous pouvez utiliser la nouvelle page \"Mode Interstellaire\" dans le journal de bord pour diriger votre pilote automatique vers un autre système solaire", @@ -15,7 +15,8 @@ "LOCK_AUTOPILOT_WARP": "Visez le pilote automatique", "RICH_PRESENCE_EXPLORING": "En explorant {0}.", "RICH_PRESENCE_WARPING": "En route vers {0}.", - "OUTDATED_VERSION_WARNING": "AVERTISSEMENT\n\nNew Horizons fonctionne seulement sur la version {0} ou plus récente. Vous êtes sur la version {1}.\n\nVeuillez mettre à jour votre jeu ou désinstaller NH." + "OUTDATED_VERSION_WARNING": "AVERTISSEMENT\n\nNew Horizons fonctionne seulement sur la version {0} ou plus récente. Vous êtes sur la version {1}.\n\nVeuillez mettre à jour votre jeu ou désinstaller NH.", + "JSON_FAILED_TO_LOAD": "Fichier(s) invalide(s): {0}" }, "AchievementTranslations": { "NH_EATEN_OUTSIDE_BRAMBLE": { diff --git a/NewHorizons/Assets/translations/german.json b/NewHorizons/Assets/translations/german.json index 460adc9f..3762146c 100644 --- a/NewHorizons/Assets/translations/german.json +++ b/NewHorizons/Assets/translations/german.json @@ -1,5 +1,5 @@ { - "$schema": "https://raw.githubusercontent.com/xen-42/outer-wilds-new-horizons/main/NewHorizons/Schemas/translation_schema.json", + "$schema": "https://raw.githubusercontent.com/outer-wilds-new-horizons/new-horizons/main/NewHorizons/Schemas/translation_schema.json", "DialogueDictionary": { "NEW_HORIZONS_WARP_DRIVE_DIALOGUE_1": "Dein Schiff ist nun mit einem Warpantrieb ausgestattet!", "NEW_HORIZONS_WARP_DRIVE_DIALOGUE_2": "Du kannst den neuen \"Interstellar Modus\" aus dem Schiffslogbuch auswählen um mit dem Autopilot in andere Sternensysteme zu reisen.", diff --git a/NewHorizons/Assets/translations/russian.json b/NewHorizons/Assets/translations/russian.json index 7063f448..a33d7f56 100644 --- a/NewHorizons/Assets/translations/russian.json +++ b/NewHorizons/Assets/translations/russian.json @@ -1,5 +1,5 @@ { - "$schema": "https://raw.githubusercontent.com/xen-42/outer-wilds-new-horizons/main/NewHorizons/Schemas/translation_schema.json", + "$schema": "https://raw.githubusercontent.com/outer-wilds-new-horizons/new-horizons/main/NewHorizons/Schemas/translation_schema.json", "DialogueDictionary": { "NEW_HORIZONS_WARP_DRIVE_DIALOGUE_1": "Твой корабль теперь экипирован варп-двигателем!", "NEW_HORIZONS_WARP_DRIVE_DIALOGUE_2": "Ты можешь использовать новый \"Режим Interstellar\" на своем корабле, что-бы закрепить автопилот на выбранную звёздную систему.", diff --git a/NewHorizons/Assets/translations/spanish_la.json b/NewHorizons/Assets/translations/spanish_la.json index 787342ba..d79d5a12 100644 --- a/NewHorizons/Assets/translations/spanish_la.json +++ b/NewHorizons/Assets/translations/spanish_la.json @@ -1,5 +1,5 @@ { - "$schema": "https://raw.githubusercontent.com/xen-42/outer-wilds-new-horizons/main/NewHorizons/Schemas/translation_schema.json", + "$schema": "https://raw.githubusercontent.com/outer-wilds-new-horizons/new-horizons/main/NewHorizons/Schemas/translation_schema.json", "DialogueDictionary": { "NEW_HORIZONS_WARP_DRIVE_DIALOGUE_1": "¡Tu nave ahora tiene un motor de curvatura!", "NEW_HORIZONS_WARP_DRIVE_DIALOGUE_2": "Puedes usar la nueva página de \"modo interestelar\" en el registro de la nave para fijar el piloto automático a otro sistema solar.", diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index c1a541e8..4c0f4403 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -609,6 +609,7 @@ namespace NewHorizons if (config == null) { Logger.LogError($"Couldn't load {relativePath}. Is your Json formatted correctly?"); + MenuHandler.RegisterFailedConfig(Path.GetFileName(relativePath)); return null; } @@ -640,6 +641,7 @@ namespace NewHorizons catch (Exception e) { Logger.LogError($"Error encounter when loading {relativePath}:\n{e}"); + MenuHandler.RegisterFailedConfig(Path.GetFileName(relativePath)); } return body; diff --git a/NewHorizons/OtherMods/MenuFramework/MenuHandler.cs b/NewHorizons/OtherMods/MenuFramework/MenuHandler.cs index 7d66258e..45682279 100644 --- a/NewHorizons/OtherMods/MenuFramework/MenuHandler.cs +++ b/NewHorizons/OtherMods/MenuFramework/MenuHandler.cs @@ -3,6 +3,7 @@ using NewHorizons.Handlers; using NewHorizons.Utility; using OWML.Common; using System.Collections.Generic; +using System.Linq; using System.Net.Mail; using UnityEngine; using static UnityEngine.InputSystem.InputRemoting; @@ -14,15 +15,14 @@ namespace NewHorizons.OtherMods.MenuFramework { private static IMenuAPI _menuApi; - private static List<(IModBehaviour mod, string message)> _registeredPopups; + private static List<(IModBehaviour mod, string message)> _registeredPopups = new(); + private static List _failedFiles = new(); public static void Init() { _menuApi = Main.Instance.ModHelper.Interaction.TryGetModApi("_nebula.MenuFramework"); TextTranslation.Get().OnLanguageChanged += OnLanguageChanged; - - _registeredPopups = new(); } public static void OnLanguageChanged() @@ -49,12 +49,23 @@ namespace NewHorizons.OtherMods.MenuFramework } } + if (_failedFiles.Count > 0) + { + var message = TranslationHandler.GetTranslation("JSON_FAILED_TO_LOAD", TranslationHandler.TextType.UI); + var mods = string.Join(",", _failedFiles.Take(10)); + if (_failedFiles.Count > 10) mods += "..."; + _menuApi.RegisterStartupPopup(string.Format(message, mods)); + } + _registeredPopups.Clear(); + _failedFiles.Clear(); // Just wanted to do this when the language is loaded in initially TextTranslation.Get().OnLanguageChanged -= OnLanguageChanged; } + public static void RegisterFailedConfig(string filename) => _failedFiles.Add(filename); + public static void RegisterOneTimePopup(IModBehaviour mod, string message) => _registeredPopups.Add((mod, message)); } } From 29ccf772bf5c9176b6ca08b4d3aed79eeab3fc74 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Mon, 5 Sep 2022 01:09:11 -0400 Subject: [PATCH 105/176] Add screen prompts for debug stuff --- NewHorizons/Assets/translations/english.json | 7 ++++- .../DebugUtilities/DebugNomaiTextPlacer.cs | 19 +++++++++++- .../Utility/DebugUtilities/DebugPropPlacer.cs | 31 +++++++++++++++++++ .../Utility/DebugUtilities/DebugRaycaster.cs | 19 ++++++++++-- NewHorizons/Utility/ImageUtilities.cs | 11 ++++++- 5 files changed, 82 insertions(+), 5 deletions(-) diff --git a/NewHorizons/Assets/translations/english.json b/NewHorizons/Assets/translations/english.json index b7aae651..3c3d3126 100644 --- a/NewHorizons/Assets/translations/english.json +++ b/NewHorizons/Assets/translations/english.json @@ -14,7 +14,12 @@ "WARP_LOCKED": "AUTOPILOT LOCKED TO:\n{0}", "LOCK_AUTOPILOT_WARP": "Lock Autopilot to Star System", "RICH_PRESENCE_EXPLORING": "Exploring {0}.", - "RICH_PRESENCE_WARPING": "Warping to {0}." + "RICH_PRESENCE_WARPING": "Warping to {0}.", + "DEBUG_RAYCAST": "Raycast", + "DEBUG_PLACE": "Place Object", + "DEBUG_PLACE_TEXT": "Place Nomai Text", + "DEBUG_UNDO": "Undo", + "DEBUG_REDO": "Redo" }, "AchievementTranslations": { "NH_EATEN_OUTSIDE_BRAMBLE": { diff --git a/NewHorizons/Utility/DebugUtilities/DebugNomaiTextPlacer.cs b/NewHorizons/Utility/DebugUtilities/DebugNomaiTextPlacer.cs index d26815e8..13bf0523 100644 --- a/NewHorizons/Utility/DebugUtilities/DebugNomaiTextPlacer.cs +++ b/NewHorizons/Utility/DebugUtilities/DebugNomaiTextPlacer.cs @@ -1,3 +1,4 @@ +using NewHorizons.Handlers; using System; using System.Collections.Generic; using System.Linq; @@ -15,13 +16,24 @@ namespace NewHorizons.Utility.DebugUtilities private DebugRaycaster _rc; + private ScreenPrompt _placePrompt; + private void Awake() { _rc = this.GetComponent(); + + _placePrompt = new ScreenPrompt(TranslationHandler.GetTranslation("DEBUG_PLACE_TEXT", TranslationHandler.TextType.UI) + " ", ImageUtilities.GetButtonSprite(KeyCode.G)); + Locator.GetPromptManager().AddScreenPrompt(_placePrompt, PromptPosition.UpperRight, false); } - void Update() + private void OnDestroy() { + Locator.GetPromptManager()?.RemoveScreenPrompt(_placePrompt, PromptPosition.UpperRight); + } + + private void Update() + { + UpdatePromptVisibility(); if (!Main.Debug) return; if (!active) return; @@ -31,5 +43,10 @@ namespace NewHorizons.Utility.DebugUtilities if (onRaycast != null) onRaycast.Invoke(data); } } + + public void UpdatePromptVisibility() + { + _placePrompt.SetVisibility(!OWTime.IsPaused() && Main.Debug && active); + } } } diff --git a/NewHorizons/Utility/DebugUtilities/DebugPropPlacer.cs b/NewHorizons/Utility/DebugUtilities/DebugPropPlacer.cs index c3bb644c..70e85fcc 100644 --- a/NewHorizons/Utility/DebugUtilities/DebugPropPlacer.cs +++ b/NewHorizons/Utility/DebugUtilities/DebugPropPlacer.cs @@ -1,6 +1,7 @@ using NewHorizons.Builder.Props; using NewHorizons.External.Configs; using NewHorizons.External.Modules; +using NewHorizons.Handlers; using System.Collections.Generic; using System.Linq; using UnityEngine; @@ -39,14 +40,36 @@ namespace NewHorizons.Utility.DebugUtilities public GameObject mostRecentlyPlacedPropGO { get { return props.Count() <= 0 ? null : props[props.Count() - 1].gameObject; } } public string mostRecentlyPlacedPropPath { get { return props.Count() <= 0 ? "" : props[props.Count() - 1].detailInfo.path; } } + private ScreenPrompt _placePrompt; + private ScreenPrompt _undoPrompt; + private ScreenPrompt _redoPrompt; + private void Awake() { _rc = this.GetRequiredComponent(); currentObject = DEFAULT_OBJECT; + + _placePrompt = new ScreenPrompt(TranslationHandler.GetTranslation("DEBUG_PLACE", TranslationHandler.TextType.UI) + " ", ImageUtilities.GetButtonSprite(KeyCode.G)); + _undoPrompt = new ScreenPrompt(TranslationHandler.GetTranslation("DEBUG_UNDO", TranslationHandler.TextType.UI) + " ", ImageUtilities.GetButtonSprite(KeyCode.Minus)); + _redoPrompt = new ScreenPrompt(TranslationHandler.GetTranslation("DEBUG_REDO", TranslationHandler.TextType.UI) + " ", ImageUtilities.GetButtonSprite(KeyCode.Equals)); + + Locator.GetPromptManager().AddScreenPrompt(_placePrompt, PromptPosition.UpperRight, false); + Locator.GetPromptManager().AddScreenPrompt(_undoPrompt, PromptPosition.UpperRight, false); + Locator.GetPromptManager().AddScreenPrompt(_redoPrompt, PromptPosition.UpperRight, false); + } + + private void OnDestroy() + { + var promptManager = Locator.GetPromptManager(); + if (promptManager == null) return; + promptManager.RemoveScreenPrompt(_placePrompt, PromptPosition.UpperRight); + promptManager.RemoveScreenPrompt(_undoPrompt, PromptPosition.UpperRight); + promptManager.RemoveScreenPrompt(_redoPrompt, PromptPosition.UpperRight); } private void Update() { + UpdatePromptVisibility(); if (!Main.Debug) return; if (!active) return; @@ -66,6 +89,14 @@ namespace NewHorizons.Utility.DebugUtilities } } + public void UpdatePromptVisibility() + { + var visible = !OWTime.IsPaused() && Main.Debug && active; + _placePrompt.SetVisibility(visible); + _undoPrompt.SetVisibility(visible && props.Count > 0); + _redoPrompt.SetVisibility(visible && deletedProps.Count > 0); + } + public void SetCurrentObject(string s) { currentObject = s; diff --git a/NewHorizons/Utility/DebugUtilities/DebugRaycaster.cs b/NewHorizons/Utility/DebugUtilities/DebugRaycaster.cs index b01e68e5..16f502e8 100644 --- a/NewHorizons/Utility/DebugUtilities/DebugRaycaster.cs +++ b/NewHorizons/Utility/DebugUtilities/DebugRaycaster.cs @@ -1,4 +1,4 @@ -using NewHorizons.Components.Orbital; +using NewHorizons.Handlers; using UnityEngine; using UnityEngine.InputSystem; @@ -17,15 +17,25 @@ namespace NewHorizons.Utility.DebugUtilities private GameObject _planeDownRightSphere; private GameObject _planeDownLeftSphere; + private ScreenPrompt _raycastPrompt; private void Awake() { _rb = this.GetRequiredComponent(); + + _raycastPrompt = new ScreenPrompt(TranslationHandler.GetTranslation("DEBUG_RAYCAST", TranslationHandler.TextType.UI) + " ", ImageUtilities.GetButtonSprite(KeyCode.P)); + + Locator.GetPromptManager().AddScreenPrompt(_raycastPrompt, PromptPosition.UpperRight, false); } + private void OnDestroy() + { + Locator.GetPromptManager()?.RemoveScreenPrompt(_raycastPrompt, PromptPosition.UpperRight); + } private void Update() { + UpdatePromptVisibility(); if (!Main.Debug) return; if (Keyboard.current == null) return; @@ -35,7 +45,12 @@ namespace NewHorizons.Utility.DebugUtilities } } - + + public void UpdatePromptVisibility() + { + _raycastPrompt.SetVisibility(!OWTime.IsPaused() && Main.Debug); + } + internal void PrintRaycast() { diff --git a/NewHorizons/Utility/ImageUtilities.cs b/NewHorizons/Utility/ImageUtilities.cs index 37bc267b..e875341f 100644 --- a/NewHorizons/Utility/ImageUtilities.cs +++ b/NewHorizons/Utility/ImageUtilities.cs @@ -351,7 +351,16 @@ namespace NewHorizons.Utility newTexture.Apply(); return newTexture; } - + + public static Sprite GetButtonSprite(JoystickButton button) => GetButtonSprite(ButtonPromptLibrary.SharedInstance.GetButtonTexture(button)); + public static Sprite GetButtonSprite(KeyCode key) => GetButtonSprite(ButtonPromptLibrary.SharedInstance.GetButtonTexture(key)); + private static Sprite GetButtonSprite(Texture2D texture) + { + var sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f), 100, 0, SpriteMeshType.FullRect, Vector4.zero, false); + sprite.name = texture.name; + return sprite; + } + // Modified from https://stackoverflow.com/a/69141085/9643841 public class AsyncImageLoader : MonoBehaviour { From 331d33ac77762def3a2932de4685b7409b84a7dd Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Mon, 5 Sep 2022 23:35:24 -0400 Subject: [PATCH 106/176] Add rename option to volumes --- NewHorizons/Builder/ShipLog/RevealBuilder.cs | 5 +++++ NewHorizons/Builder/Volumes/AudioVolumeBuilder.cs | 5 +++++ NewHorizons/Builder/Volumes/HazardVolumeBuilder.cs | 5 +++++ NewHorizons/Builder/Volumes/NotificationVolumeBuilder.cs | 5 +++++ NewHorizons/Builder/Volumes/VolumeBuilder.cs | 5 +++++ NewHorizons/External/Modules/VolumesModule.cs | 5 +++++ 6 files changed, 30 insertions(+) diff --git a/NewHorizons/Builder/ShipLog/RevealBuilder.cs b/NewHorizons/Builder/ShipLog/RevealBuilder.cs index 59662218..3bfcdd69 100644 --- a/NewHorizons/Builder/ShipLog/RevealBuilder.cs +++ b/NewHorizons/Builder/ShipLog/RevealBuilder.cs @@ -42,6 +42,11 @@ namespace NewHorizons.Builder.ShipLog revealTriggerVolume.SetActive(false); revealTriggerVolume.transform.parent = sector?.transform ?? planetGO.transform; + if (!string.IsNullOrEmpty(info.rename)) + { + revealTriggerVolume.name = info.rename; + } + if (!string.IsNullOrEmpty(info.parentPath)) { var newParent = planetGO.transform.Find(info.parentPath); diff --git a/NewHorizons/Builder/Volumes/AudioVolumeBuilder.cs b/NewHorizons/Builder/Volumes/AudioVolumeBuilder.cs index 2952f590..bbb201d8 100644 --- a/NewHorizons/Builder/Volumes/AudioVolumeBuilder.cs +++ b/NewHorizons/Builder/Volumes/AudioVolumeBuilder.cs @@ -20,6 +20,11 @@ namespace NewHorizons.Builder.Volumes go.transform.parent = sector?.transform ?? planetGO.transform; + if (!string.IsNullOrEmpty(info.rename)) + { + go.name = info.rename; + } + if (!string.IsNullOrEmpty(info.parentPath)) { var newParent = planetGO.transform.Find(info.parentPath); diff --git a/NewHorizons/Builder/Volumes/HazardVolumeBuilder.cs b/NewHorizons/Builder/Volumes/HazardVolumeBuilder.cs index d52b4352..705570d5 100644 --- a/NewHorizons/Builder/Volumes/HazardVolumeBuilder.cs +++ b/NewHorizons/Builder/Volumes/HazardVolumeBuilder.cs @@ -18,6 +18,11 @@ namespace NewHorizons.Builder.Volumes go.transform.parent = sector?.transform ?? planetGO.transform; + if (!string.IsNullOrEmpty(info.rename)) + { + go.name = info.rename; + } + if (!string.IsNullOrEmpty(info.parentPath)) { var newParent = planetGO.transform.Find(info.parentPath); diff --git a/NewHorizons/Builder/Volumes/NotificationVolumeBuilder.cs b/NewHorizons/Builder/Volumes/NotificationVolumeBuilder.cs index 496a9ea7..13386e79 100644 --- a/NewHorizons/Builder/Volumes/NotificationVolumeBuilder.cs +++ b/NewHorizons/Builder/Volumes/NotificationVolumeBuilder.cs @@ -22,6 +22,11 @@ namespace NewHorizons.Builder.Volumes go.transform.parent = sector?.transform ?? planetGO.transform; + if (!string.IsNullOrEmpty(info.rename)) + { + go.name = info.rename; + } + if (!string.IsNullOrEmpty(info.parentPath)) { var newParent = planetGO.transform.Find(info.parentPath); diff --git a/NewHorizons/Builder/Volumes/VolumeBuilder.cs b/NewHorizons/Builder/Volumes/VolumeBuilder.cs index afeffd73..966c3c25 100644 --- a/NewHorizons/Builder/Volumes/VolumeBuilder.cs +++ b/NewHorizons/Builder/Volumes/VolumeBuilder.cs @@ -14,6 +14,11 @@ namespace NewHorizons.Builder.Volumes go.transform.parent = sector?.transform ?? planetGO.transform; + if (!string.IsNullOrEmpty(info.rename)) + { + go.name = info.rename; + } + if (!string.IsNullOrEmpty(info.parentPath)) { var newParent = planetGO.transform.Find(info.parentPath); diff --git a/NewHorizons/External/Modules/VolumesModule.cs b/NewHorizons/External/Modules/VolumesModule.cs index a3c810a0..49bbc542 100644 --- a/NewHorizons/External/Modules/VolumesModule.cs +++ b/NewHorizons/External/Modules/VolumesModule.cs @@ -71,6 +71,11 @@ namespace NewHorizons.External.Modules /// The relative path from the planet to the parent of this object. Optional (will default to the root sector). /// public string parentPath; + + /// + /// An optional rename of this volume. + /// + public string rename; } [JsonObject] From 70f07c20a907dda2e28950d531e091cd00e1788b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 6 Sep 2022 03:37:40 +0000 Subject: [PATCH 107/176] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index b1a756cd..18185e87 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -2468,6 +2468,10 @@ "type": "string", "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." }, + "rename": { + "type": "string", + "description": "An optional rename of this volume." + }, "audio": { "type": "string", "description": "The audio to use. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list." @@ -2541,6 +2545,10 @@ "type": "string", "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." }, + "rename": { + "type": "string", + "description": "An optional rename of this volume." + }, "type": { "description": "The type of hazard for this volume.", "default": "general", @@ -2618,6 +2626,10 @@ "parentPath": { "type": "string", "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." + }, + "rename": { + "type": "string", + "description": "An optional rename of this volume." } } }, @@ -2638,6 +2650,10 @@ "type": "string", "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." }, + "rename": { + "type": "string", + "description": "An optional rename of this volume." + }, "target": { "description": "What the notification will show for.", "default": "all", @@ -2700,6 +2716,10 @@ "type": "string", "description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)." }, + "rename": { + "type": "string", + "description": "An optional rename of this volume." + }, "maxAngle": { "type": "number", "description": "The max view angle (in degrees) the player can see the volume with to unlock the fact (`observe` only)", From c568787278e02125916f79fdf93c8d1f498d45d0 Mon Sep 17 00:00:00 2001 From: Ben C Date: Tue, 6 Sep 2022 11:31:32 -0400 Subject: [PATCH 108/176] Add Editor Page --- docs/content/pages/tutorials/editor.md | 65 ++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 docs/content/pages/tutorials/editor.md diff --git a/docs/content/pages/tutorials/editor.md b/docs/content/pages/tutorials/editor.md new file mode 100644 index 00000000..2366b950 --- /dev/null +++ b/docs/content/pages/tutorials/editor.md @@ -0,0 +1,65 @@ +--- +Title: Config Editor +Sort_Priority: 50 +--- + +# Config Editor + +Are you tired of manually editting JSON? Do you want richer validation than a JSON schema? Well then the config editor may be for you! + +This page outlines how to install and use the config editor. + +## Installation + +To get started, head over to the [releases page for the editor](https://github.com/Outer-Wilds-New-Horizons/nh-config-editor/releases/latest) and install the file for your OS: + +- Windows: The .msi file (not the .msi.zip and .msi.zip.sig file) +- MacOS: The .AppImage file (not the .AppImage.tar.gz or the .AppImage.tar.gx.sig file) + +Follow the installer instructions to complete setup + +## Creating a New Project + +Creating a new project is as simple as clicking the button. +Fill out the form with thr info for your mod and a new project will be made at the specified path. + +## Editing Files + +To edit a file, navigate to it in the left panel and click on it. + +### JSON files + +JSON files (planets, systems, etc) have a graphical interface for editing, **this will clear comments!** + +If you don't want comments to be cleared, use the text editor + +#### Using the Text Editor + +Already familiar with JSON and prefer text-based editing? Simply open up settings (File -> Settings) and turn on the "Always use Text Editor" option. + +### Image and Audio Files + +You can view images and play audio files with this editor. + +### XML Files + +Right now, XML support is limited. You'll get syntax highlighting but no error checking or auto-fill. + + +## Running the Game + +You can start the game from the editor by selecting Project -> Run Project this will open a new window where you can run the game + +### Log Port + +If you're using the mod manager and would like logs to appear there, you need to get the log port from the console, it's always the first entry in the logs. Keep in mind this port changes whenever you restart the manager. + +![TODO: Get Log Screenshot]() + + +## Building + +The editor also provides a system for building your mod to a zip file, which can then be uploaded to GitHub in a release. To do this, press Project -> Build (Release) + + + From 8cdc3f842ff8d3eee2540a9781db0e378cb2fbff Mon Sep 17 00:00:00 2001 From: Ben C Date: Tue, 6 Sep 2022 15:22:06 -0400 Subject: [PATCH 109/176] Fixed Typos --- docs/content/pages/tutorials/creating_addon.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/content/pages/tutorials/creating_addon.md b/docs/content/pages/tutorials/creating_addon.md index 4a21dacf..fc88919f 100644 --- a/docs/content/pages/tutorials/creating_addon.md +++ b/docs/content/pages/tutorials/creating_addon.md @@ -36,7 +36,7 @@ Our repository is now cloned to our computer! ## Editing Files -Now that our repo is clones, we're going to need to edit the files in it. +Now that our repo is cloned, we're going to need to edit the files in it. To get started editing the files, simply click "Open in Visual Studio Code" in GitHub Desktop. ### Files Explanation @@ -51,7 +51,7 @@ To get started editing the files, simply click "Open in Visual Studio Code" in G - Now set `uniqueName` to the name of your GitHub Repo. - You can leave `version`, `owmlVersion`, and `dependencies` alone - NewHorizonsConfig.dll: This is the heart of your addon, make sure to never move or rename it. -- README.md: This file is what's displayed on the mod website when you go to a specific mod's page, you can delete the current contents. +- README.md: This file is displayed on the mod website when you go to a specific mod's page, you can delete the current contents. - This file is a [markdown](https://www.markdowntutorial.com/){ target="_blank" } file, if you're not comfortable writing an entire README right now, just write a small description of your mod. ### Committing The Changes @@ -64,7 +64,7 @@ Think of committing like taking a snapshot of your project at this moment in tim ### Pushing The Changes -OK, so we've committed our new changes, but these commits still only exist on our computer, to get these changes onto GitHub we can click the "Push Origin" button the right. +OK, so we've committed our new changes, but these commits still only exist on our computer, to get these changes onto GitHub we can click the "Push Origin" button on the top right. ## Testing The Addon From c75162137f9ba1feb387f8471eabc343f73ce8ec Mon Sep 17 00:00:00 2001 From: Ben C Date: Wed, 7 Sep 2022 07:48:27 -0400 Subject: [PATCH 110/176] Add Orbit Section to Planet Gen --- docs/content/pages/{tutorials => }/editor.md | 0 docs/content/pages/tutorials/planet_gen.md | 4 ++++ 2 files changed, 4 insertions(+) rename docs/content/pages/{tutorials => }/editor.md (100%) diff --git a/docs/content/pages/tutorials/editor.md b/docs/content/pages/editor.md similarity index 100% rename from docs/content/pages/tutorials/editor.md rename to docs/content/pages/editor.md diff --git a/docs/content/pages/tutorials/planet_gen.md b/docs/content/pages/tutorials/planet_gen.md index b10073c6..978424f3 100644 --- a/docs/content/pages/tutorials/planet_gen.md +++ b/docs/content/pages/tutorials/planet_gen.md @@ -7,6 +7,10 @@ Sort_Priority: 80 This guide covers some aspects of generating your planet, a lot of stuff is already explained in [the celestial body schema]({{ "Celestial Body Schema"|route }}). +## Orbits + +First thing you should specify about your planet is its orbit. `primaryBody` will specify what planet this body will orbit. If you're in a new solar system and want this planet to be the center, set `centerOfSolarSystem` to `true` (keep in mind `centerOfSolarSystem` is in the `Base` module, not `Orbit`). Next up you'll need to specify the [orbital parameters](https://en.wikipedia.org/wiki/Orbital_elements). + ## Heightmaps Heightmaps are a way to generate unique terrain on your planet. First you specify a maximum and minimum height, and then specify a [heightMap]({{ "Celestial Body Schema"|route }}#HeightMap_heightMap) image. The more white a section of that image is, the closer to `maxHeight` that part of the terrain will be. Finally, you specify a `textureMap` which is an image that gets applied to the terrain. From 3259a7e206d1b64c5f03674f4dc8a57f8e5154f7 Mon Sep 17 00:00:00 2001 From: Ben C Date: Wed, 7 Sep 2022 08:10:57 -0400 Subject: [PATCH 111/176] Added XML Guide --- docs/content/pages/tutorials/ship_log.md | 4 +- docs/content/pages/tutorials/xml.md | 64 ++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 docs/content/pages/tutorials/xml.md diff --git a/docs/content/pages/tutorials/ship_log.md b/docs/content/pages/tutorials/ship_log.md index d1405eee..ac032ed4 100644 --- a/docs/content/pages/tutorials/ship_log.md +++ b/docs/content/pages/tutorials/ship_log.md @@ -1,13 +1,15 @@ --- Title: Ship Log Description: A guide to editing the ship log in New Horizons -Sort_Priority: 70 +Sort_Priority: 40 --- # Intro Welcome! this page outlines how to create a custom ship log. +If you haven't already, you may want to take a look at [Understanding XML]({{ "Understanding XML"|route }}) to get a better idea of how XML works. + # Understanding Ship Logs First thing's first, I'll define some terminology regarding ship logs in the game, and how ship logs are structured. diff --git a/docs/content/pages/tutorials/xml.md b/docs/content/pages/tutorials/xml.md new file mode 100644 index 00000000..62f3965e --- /dev/null +++ b/docs/content/pages/tutorials/xml.md @@ -0,0 +1,64 @@ +--- +Title: Understanding XML +Sort_Priority: 50 +--- + +# Understanding XML + +XML is the other language New Horizons uses for content. +XML files are usually passed straight to the game's code instead of going through New Horizons. + +## Syntax + +XML is comprised of tags, a tag can represent a section or attribute + +```xml + + Jim + 32 + + +``` + +Notice how each tag is closed by an identical tag with a slash at the front (i.e `` is closed by ``). + +If the tag has no content you can use the self-closing tag shorthand (i.e. `` doesn't need a closing tag because of the `/` at the end). + +This XML could be written in JSON as: + +```json +{ + "name": "Jim", + "age": 32, + "isMarried": true +} +``` + +XML is a lot more descriptive, you can actually tell that the object is supposed to be a person by the name of the tag. + +## Structure + +All XML files must have **one** top-level tag, this varies depending on what you're using it for (like how ship logs use a `` tag). + +## Schemas + +XML files can also have schemas, you specify them by adding attributes to the top-level tag: + +```xml + + +``` + +In order to get schema validation and auto-fill you'll need the [Redhat XML VSCode extension](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-xml). + + +## Uses + +XML is used for the following: + +- [Shiplog Entries]({{ "Ship Log"|route }}) +- [Dialogue](#) +- [Translatable Text](#) + + + From bf4fe574ea947446673d61acc643623fcf7c35b1 Mon Sep 17 00:00:00 2001 From: Ben C Date: Wed, 7 Sep 2022 09:31:54 -0400 Subject: [PATCH 112/176] Added some Pages --- docs/content/pages/tutorials/dialogue.md | 168 ++++++++++++++++++++ docs/content/pages/tutorials/translation.md | 34 ++++ docs/content/pages/tutorials/xml.md | 3 +- 3 files changed, 203 insertions(+), 2 deletions(-) create mode 100644 docs/content/pages/tutorials/dialogue.md create mode 100644 docs/content/pages/tutorials/translation.md diff --git a/docs/content/pages/tutorials/dialogue.md b/docs/content/pages/tutorials/dialogue.md new file mode 100644 index 00000000..aa1b5a02 --- /dev/null +++ b/docs/content/pages/tutorials/dialogue.md @@ -0,0 +1,168 @@ +--- +Title: Dialogue +Description: Guide to making dialogue in New Horizons +Sort_Priority: 30 +--- + + + +# Dialogue + +This page goes over how to use dialogue in New Horizons. + +You may want to view [Understanding XML]({{ "Understanding XML"|route }}) if you haven't already. + +# Understanding Dialogue + +## Dialogue Tree + +A dialogue tree is an entire conversation, it's made up of dialogue nodes. + +## Dialogue Node + +A node is a set of pages shown to the player followed by options the player can choose from to change the flow of the conversation. + +## Condition + +A condition is a yes/no value stored **for this loop and this loop only**. It can be used to show new dialogue options, stop someone from talking to you (looking at you Slate), and more. + +## Persistent Condition + +A persistent condition is similar to a condition, except it *persists* through loops, and is saved on the player's save file. + +## Remote Trigger + +A remote trigger is used to have an NPC talk to you from a distance; ex: Slate stopping you for the umpteenth time to tell you information you already knew. + +# Example XML + +Here's an example dialogue XML: + +```xml + + + + EXAMPLE NPC + + + Start + DEFAULT + + Start + Start Part 2 + + + + + Goto 1 + 1 + + + + Goto 2 + 2 + + + Goto End + End + + + + + + 1 + + + This is 1 + + + + + Goto 2 + 2 + + + Goto End + End + + + + + + 2 + + This is 2 + + + + + Goto 1 + 1 + + + Goto End + End + + + + + + End + + This is the end + + + + +``` + +# Using the XML + +To use the dialogue XML you have created, you simply need to reference it in the `dialogue` prop + +```json +{ + "Props": { + "dialogue": [ + { + "position": {"x": 5, "y": 10, "z": 0}, + "xmlFile": "planets/path/to/your_file.xml" + } + ] + } +} +``` + +# Dialogue Config + +To view the options for the dialogue prop, check [the schema]({{ "Celestial Body Schema"|route }}#Props_dialogue) + +# Controlling Conditions + +You can set condition in dialogue with the `` and `` tags + +```xml + + + EXAMPLE_CONDITION + EXAMPLE_P_CONDITION + + +``` + +# Dialogue Options + +There are many control structures for dialogue options to hide/reveal them if conditions are met. Take a look at [the DialogueOption schema]({{ "Dialogue Schema"|route }}#DialogueTree-DialogueNode-DialogueOptionsList-DialogueOption-DialogueTarget) for more info. + +# Controlling Flow + +In addition to ``, there are other ways to control the flow of the conversation. + +## DialogueTarget + +Defining `` in the `` tag instead of a `` will make the conversation go directly to that target after the character is done talking. + +## DialogueTargetShipLogCondition + +Used in tandum with `DialogueTarget`, makes it so you must have a [ship log fact]({{ "Ship Log"|route }}#explore-facts) to go to the next node. diff --git a/docs/content/pages/tutorials/translation.md b/docs/content/pages/tutorials/translation.md new file mode 100644 index 00000000..095a9f01 --- /dev/null +++ b/docs/content/pages/tutorials/translation.md @@ -0,0 +1,34 @@ +--- +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. + +All translations must go in a folder in the root directory called "translations". + +In this folder you can put json files with the name of the language you want to translate for. Inside this file just follow the translation schema. + +Here's an example, for `russian.json`: + +```json +{ + "$schema": "https://raw.githubusercontent.com/Outer-Wilds-New-Horizons/new-horizons/main/NewHorizons/Schemas/translation_schema.json", + "DialogueDictionary" : + { + "Fred" : "Фред", + "You looking at something?" : "Ты что-то искал?", + "Aren't you guys all supposed to be dead?" : "А разве номаи не вымерли?", + "OH MY GOD A LIVING NOMAI AHHH WHAT HOW?!" : "ААААА, ЖИВАЯ НОМАИ?!" + }, + "ShipLogDictionary" : + { + "Unexpected guests" : "Незванные гости", + "Visitors" : "Гости", + "When I went to sleep by the campfire only Slate was here, who are these characters?" : "Когда я ложился спать у костра здесь был только Сланец. Кто все остальные?", + "I met a talking jellyfish. His name is Geswaldo!" : "Я встретил говорящую медузу. Его зовут Гесвальдо!" + } +} +``` diff --git a/docs/content/pages/tutorials/xml.md b/docs/content/pages/tutorials/xml.md index 62f3965e..3f182a2b 100644 --- a/docs/content/pages/tutorials/xml.md +++ b/docs/content/pages/tutorials/xml.md @@ -49,8 +49,7 @@ XML files can also have schemas, you specify them by adding attributes to the to ``` -In order to get schema validation and auto-fill you'll need the [Redhat XML VSCode extension](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-xml). - +In order to get schema validation and auto-fill you'll need the [Redhat XML VSCode extension](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-xml){ target="_blank" }. ## Uses From b9a5a8abfef0305a61c3385abf559f9850cccb15 Mon Sep 17 00:00:00 2001 From: Ben C Date: Wed, 7 Sep 2022 09:36:11 -0400 Subject: [PATCH 113/176] Fix typo in editor.md --- docs/content/pages/editor.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/pages/editor.md b/docs/content/pages/editor.md index 2366b950..4f40cb27 100644 --- a/docs/content/pages/editor.md +++ b/docs/content/pages/editor.md @@ -14,7 +14,7 @@ This page outlines how to install and use the config editor. To get started, head over to the [releases page for the editor](https://github.com/Outer-Wilds-New-Horizons/nh-config-editor/releases/latest) and install the file for your OS: - Windows: The .msi file (not the .msi.zip and .msi.zip.sig file) -- MacOS: The .AppImage file (not the .AppImage.tar.gz or the .AppImage.tar.gx.sig file) +- MacOS: The .AppImage file (not the .AppImage.tar.gz or the .AppImage.tar.gz.sig file) Follow the installer instructions to complete setup From bf6745d351216dfb7ea6d3bbace9de45ba01277c Mon Sep 17 00:00:00 2001 From: JohnCorby Date: Wed, 7 Sep 2022 17:21:42 -0700 Subject: [PATCH 114/176] groups on details --- NewHorizons/Builder/General/GroupBuilder.cs | 30 +++++++++++++++++++++ NewHorizons/Builder/Props/DetailBuilder.cs | 2 ++ 2 files changed, 32 insertions(+) create mode 100644 NewHorizons/Builder/General/GroupBuilder.cs diff --git a/NewHorizons/Builder/General/GroupBuilder.cs b/NewHorizons/Builder/General/GroupBuilder.cs new file mode 100644 index 00000000..828a7117 --- /dev/null +++ b/NewHorizons/Builder/General/GroupBuilder.cs @@ -0,0 +1,30 @@ +using UnityEngine; +using Logger = NewHorizons.Utility.Logger; + +namespace NewHorizons.Builder.General; + +public static class GroupBuilder +{ + /// + /// puts groups on objects. + /// run this before the gameobject is active. + /// + public static void Make(GameObject go, Sector sector) + { + Logger.LogVerbose($"putting groups on {go} (linked to {sector})"); + if (!sector) + { + Logger.LogWarning("tried to put groups on a null sector"); + return; + } + if (go.activeInHierarchy) + { + Logger.LogWarning("tried to put groups on an active gameobject"); + return; + } + + go.GetAddComponent()._sector = sector; + go.GetAddComponent()._sector = sector; + go.GetAddComponent()._sector = sector; + } +} \ No newline at end of file diff --git a/NewHorizons/Builder/Props/DetailBuilder.cs b/NewHorizons/Builder/Props/DetailBuilder.cs index ae7a63f7..2c849ecf 100644 --- a/NewHorizons/Builder/Props/DetailBuilder.cs +++ b/NewHorizons/Builder/Props/DetailBuilder.cs @@ -1,3 +1,4 @@ +using NewHorizons.Builder.General; using NewHorizons.External.Configs; using NewHorizons.External.Modules; using NewHorizons.Handlers; @@ -100,6 +101,7 @@ namespace NewHorizons.Builder.Props prop.transform.localScale = detail.scale != 0 ? Vector3.one * detail.scale : prefab.transform.localScale; + if (!detail.keepLoaded) GroupBuilder.Make(prop, sector); prop.SetActive(true); if (prop == null) return null; From 01419392f6a7b380c1b4f1913536b69e6d59a577 Mon Sep 17 00:00:00 2001 From: Ben C Date: Wed, 7 Sep 2022 20:29:25 -0400 Subject: [PATCH 115/176] More Pages --- docs/content/pages/404.md | 10 +++ docs/content/pages/tutorials/dialogue.md | 30 ++++----- docs/content/pages/tutorials/extending.md | 74 +++++++++++++++++++++ docs/content/pages/tutorials/planet_gen.md | 2 +- docs/content/pages/tutorials/star_system.md | 49 ++++++++++++++ docs/content/pages/tutorials/xml.md | 6 +- 6 files changed, 151 insertions(+), 20 deletions(-) create mode 100644 docs/content/pages/404.md create mode 100644 docs/content/pages/tutorials/extending.md create mode 100644 docs/content/pages/tutorials/star_system.md diff --git a/docs/content/pages/404.md b/docs/content/pages/404.md new file mode 100644 index 00000000..a999f185 --- /dev/null +++ b/docs/content/pages/404.md @@ -0,0 +1,10 @@ +--- +Title: Page not Found +Hide_In_Nav: True +Render_TOC: False +--- + +# Page Not Found + +The page you requested could not be found. + diff --git a/docs/content/pages/tutorials/dialogue.md b/docs/content/pages/tutorials/dialogue.md index aa1b5a02..50063480 100644 --- a/docs/content/pages/tutorials/dialogue.md +++ b/docs/content/pages/tutorials/dialogue.md @@ -4,37 +4,35 @@ Description: Guide to making dialogue in New Horizons Sort_Priority: 30 --- - - # Dialogue This page goes over how to use dialogue in New Horizons. You may want to view [Understanding XML]({{ "Understanding XML"|route }}) if you haven't already. -# Understanding Dialogue +## Understanding Dialogue -## Dialogue Tree +### Dialogue Tree A dialogue tree is an entire conversation, it's made up of dialogue nodes. -## Dialogue Node +### Dialogue Node A node is a set of pages shown to the player followed by options the player can choose from to change the flow of the conversation. -## Condition +### Condition A condition is a yes/no value stored **for this loop and this loop only**. It can be used to show new dialogue options, stop someone from talking to you (looking at you Slate), and more. -## Persistent Condition +### Persistent Condition A persistent condition is similar to a condition, except it *persists* through loops, and is saved on the player's save file. -## Remote Trigger +### Remote Trigger A remote trigger is used to have an NPC talk to you from a distance; ex: Slate stopping you for the umpteenth time to tell you information you already knew. -# Example XML +## Example XML Here's an example dialogue XML: @@ -117,7 +115,7 @@ Here's an example dialogue XML: ``` -# Using the XML +## Using the XML To use the dialogue XML you have created, you simply need to reference it in the `dialogue` prop @@ -134,11 +132,11 @@ To use the dialogue XML you have created, you simply need to reference it in the } ``` -# Dialogue Config +## Dialogue Config To view the options for the dialogue prop, check [the schema]({{ "Celestial Body Schema"|route }}#Props_dialogue) -# Controlling Conditions +## Controlling Conditions You can set condition in dialogue with the `` and `` tags @@ -151,18 +149,18 @@ You can set condition in dialogue with the `` and ` ``` -# Dialogue Options +## Dialogue Options There are many control structures for dialogue options to hide/reveal them if conditions are met. Take a look at [the DialogueOption schema]({{ "Dialogue Schema"|route }}#DialogueTree-DialogueNode-DialogueOptionsList-DialogueOption-DialogueTarget) for more info. -# Controlling Flow +## Controlling Flow In addition to ``, there are other ways to control the flow of the conversation. -## DialogueTarget +### DialogueTarget Defining `` in the `` tag instead of a `` will make the conversation go directly to that target after the character is done talking. -## DialogueTargetShipLogCondition +### DialogueTargetShipLogCondition Used in tandum with `DialogueTarget`, makes it so you must have a [ship log fact]({{ "Ship Log"|route }}#explore-facts) to go to the next node. diff --git a/docs/content/pages/tutorials/extending.md b/docs/content/pages/tutorials/extending.md new file mode 100644 index 00000000..0e9b30bc --- /dev/null +++ b/docs/content/pages/tutorials/extending.md @@ -0,0 +1,74 @@ +--- +Title: Extending Configs +Description: A guide on extending config files with the New Horizons API +Sort_Priority: 5 +--- + +# Extending Configs + +This guide will explain how to use the API to add new features to New Horizons. + +## How Extending Works + +Addon developers will add a key to the `extras` object in the root of the config + +```json +{ + "name": "Wetrock", + "extras": { + "myCoolExtensionData": { + "myCoolExtensionProperty": 2 + } + } +} +``` + +Your mod will then use the API's `QueryBody` method to obtain the `myCoolExtensionData` object. + +**It's up to the addon dev to list your mod as a dependency!** + +## Extending Planets + +You can extend all planets by hooking into the `OnBodyLoaded` event of the API: + +```csharp +var api = ModHelper.Interactions.TryGetModApi("xen.NewHorizons"); +api.GetBodyLoadedEvent().AddListener((name) => { + ModHelper.Console.WriteLine($"Body: {name} Loaded!"); +}); +``` + +In order to get your extra module, first define the module as a class: + +```csharp +public class MyCoolExtensionData { + int myCoolExtensionProperty; +} +``` + +Then, use the `QueryBody` method: + +```csharp +var api = ModHelper.Interactions.TryGetModApi("xen.NewHorizons"); +api.GetBodyLoadedEvent().AddListener((name) => { + ModHelper.Console.WriteLine($"Body: {name} Loaded!"); + var potentialData = api.QueryBody(typeof(MyCoolExtensionData), "$.extras.myCoolExtensionData", name); + // Makes sure the module is valid and not null + if (potentialData is MyCoolExtensionData data) { + ModHelper.Console.WriteLine($"myCoolExtensionProperty for {name} is {data.myCoolExtensionProperty}!"); + } +}); +``` + +## Extending Systems + +Extending systems is the exact same as extending planets, except you use the `QuerySystem` method instead. + +## Accessing Other Values + +You can also use the `QueryBody` method to get values of the config outside of your extension object + +```csharp +var primaryBody = api.QueryBody(typeof(string), "Wetrock", "$.Orbit.primaryBody"); + ModHelper.Console.WriteLine($"Primary of {bodyName} is {primaryBody ?? "NULL"}!"); +``` diff --git a/docs/content/pages/tutorials/planet_gen.md b/docs/content/pages/tutorials/planet_gen.md index 978424f3..c634b1fd 100644 --- a/docs/content/pages/tutorials/planet_gen.md +++ b/docs/content/pages/tutorials/planet_gen.md @@ -15,7 +15,7 @@ First thing you should specify about your planet is its orbit. `primaryBody` wil Heightmaps are a way to generate unique terrain on your planet. First you specify a maximum and minimum height, and then specify a [heightMap]({{ "Celestial Body Schema"|route }}#HeightMap_heightMap) image. The more white a section of that image is, the closer to `maxHeight` that part of the terrain will be. Finally, you specify a `textureMap` which is an image that gets applied to the terrain. -Here's an example heightmap or earth from the Real Solar System addon. +Here's an example heightmap of earth from the Real Solar System addon. ![Earth's Heightmap]({{ "images/planet_gen/earth_heightmap.webp"|static }}) diff --git a/docs/content/pages/tutorials/star_system.md b/docs/content/pages/tutorials/star_system.md new file mode 100644 index 00000000..aa3218de --- /dev/null +++ b/docs/content/pages/tutorials/star_system.md @@ -0,0 +1,49 @@ +--- +Title: Star Systems +Description: A guide to editing a custom star system in New Horizons +Sort_Priority: 65 +--- + +# Intro + +Welcome! This page outlines how to edit a custom star system. + +## Getting Started + +Star Systems are placed in a folder called systems within your mod folder. + +The name of your star system config must be the same as the unique id used in the `starSystem` field of your planet configs. Example: `xen.RealSolarSystem.json`. + +A star system config file will look something like this: + +```json +{ + "$schema": "https://raw.githubusercontent.com/Outer-Wilds-New-Horizons/new-horizons/main/NewHorizons/Schemas/star_system_schema.json", + "travelAudio": "assets/Travel.mp3", + "Vessel": { + "coords": { + "x": [ 4, 0, 3, 1 ], + "y": [ 0, 5, 4 ], + "z": [ 5, 4, 0, 3, 1 ] + }, + "vesselPosition": { + "x": 0, + "y": 0, + "z": 8000 + } + } +} +``` + +To see all the different things you can put into a config file check out the [Star System Schema]({{ 'Star System Schema'|route}}). + +## Vessel Coordinates + +You can warp to custom star systems via the Nomai vessel. Each coordinate has to be 2-6 points long. +These are the points for each coordinate node. When making your unique coordinate you should only use each point once. +![nomaiCoordinateIndexes]({{ "images/star_system/nomai_coordinate_indexes.webp"|static }}) + +### Hearthian Solar System Vessel Coordinates + +You can use these coordinates to warp back to the hearthian solar system. +![hearthianSolarSystemCoordinates]({{ "images/star_system/hearthian_solar_system_coordinates.webp"|static }}) \ No newline at end of file diff --git a/docs/content/pages/tutorials/xml.md b/docs/content/pages/tutorials/xml.md index 3f182a2b..6b4f2fbd 100644 --- a/docs/content/pages/tutorials/xml.md +++ b/docs/content/pages/tutorials/xml.md @@ -49,14 +49,14 @@ XML files can also have schemas, you specify them by adding attributes to the to ``` -In order to get schema validation and auto-fill you'll need the [Redhat XML VSCode extension](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-xml){ target="_blank" }. +In order to get schema validation and autofill you'll need the [Redhat XML VSCode extension](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-xml){ target="_blank" }. ## Uses XML is used for the following: -- [Shiplog Entries]({{ "Ship Log"|route }}) -- [Dialogue](#) +- [Ship log Entries]({{ "Ship Log"|route }}) +- [Dialogue]({{ "Dialogue"|route }}) - [Translatable Text](#) From 7a67a1f9807c20590fe353e6482ad8666d38bc2a Mon Sep 17 00:00:00 2001 From: JohnCorby Date: Wed, 7 Sep 2022 17:30:22 -0700 Subject: [PATCH 116/176] improve warnings --- NewHorizons/Builder/General/GroupBuilder.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/NewHorizons/Builder/General/GroupBuilder.cs b/NewHorizons/Builder/General/GroupBuilder.cs index 828a7117..c9227ce3 100644 --- a/NewHorizons/Builder/General/GroupBuilder.cs +++ b/NewHorizons/Builder/General/GroupBuilder.cs @@ -11,15 +11,14 @@ public static class GroupBuilder /// public static void Make(GameObject go, Sector sector) { - Logger.LogVerbose($"putting groups on {go} (linked to {sector})"); if (!sector) { - Logger.LogWarning("tried to put groups on a null sector"); + Logger.LogWarning($"tried to put groups on {go.name} when sector is null"); return; } if (go.activeInHierarchy) { - Logger.LogWarning("tried to put groups on an active gameobject"); + Logger.LogWarning($"tried to put groups on an active gameobject {go.name}"); return; } From da211eff755eb15fa1561b9abbf5664e8eb6be92 Mon Sep 17 00:00:00 2001 From: JohnCorby Date: Wed, 7 Sep 2022 17:33:38 -0700 Subject: [PATCH 117/176] scatter builder keep loaded --- NewHorizons/Builder/Props/ScatterBuilder.cs | 1 + NewHorizons/External/Modules/PropModule.cs | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/NewHorizons/Builder/Props/ScatterBuilder.cs b/NewHorizons/Builder/Props/ScatterBuilder.cs index 26af9d33..3ce71605 100644 --- a/NewHorizons/Builder/Props/ScatterBuilder.cs +++ b/NewHorizons/Builder/Props/ScatterBuilder.cs @@ -96,6 +96,7 @@ namespace NewHorizons.Builder.Props { position = point.normalized * height, scale = propInfo.scale, + keepLoaded = propInfo.keepLoaded, alignToNormal = true }; var prop = DetailBuilder.Make(go, sector, prefab, detailInfo); diff --git a/NewHorizons/External/Modules/PropModule.cs b/NewHorizons/External/Modules/PropModule.cs index 932b1bc0..b02f762d 100644 --- a/NewHorizons/External/Modules/PropModule.cs +++ b/NewHorizons/External/Modules/PropModule.cs @@ -139,6 +139,11 @@ namespace NewHorizons.External.Modules /// The highest height that these objects will be placed at (only relevant if there's a heightmap) /// public float? maxHeight; + + /// + /// Should this detail stay loaded even if you're outside the sector (good for very large props) + /// + public bool keepLoaded; } [JsonObject] From e12de937cd2d58b3da7ae4bb1a35c0be1945a86a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 8 Sep 2022 00:40:31 +0000 Subject: [PATCH 118/176] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index 18185e87..9a5b05f9 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -1391,6 +1391,10 @@ ], "description": "The highest height that these objects will be placed at (only relevant if there's a heightmap)", "format": "float" + }, + "keepLoaded": { + "type": "boolean", + "description": "Should this detail stay loaded even if you're outside the sector (good for very large props)" } } }, From 5dcaac14f58c694b500a3e48217db97f8e09f0eb Mon Sep 17 00:00:00 2001 From: JohnCorby Date: Wed, 7 Sep 2022 17:56:41 -0700 Subject: [PATCH 119/176] rename --- .../Builder/General/{GroupBuilder.cs => GroupsBuilder.cs} | 4 ++-- NewHorizons/Builder/Props/DetailBuilder.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename NewHorizons/Builder/General/{GroupBuilder.cs => GroupsBuilder.cs} (89%) diff --git a/NewHorizons/Builder/General/GroupBuilder.cs b/NewHorizons/Builder/General/GroupsBuilder.cs similarity index 89% rename from NewHorizons/Builder/General/GroupBuilder.cs rename to NewHorizons/Builder/General/GroupsBuilder.cs index c9227ce3..e37e7eab 100644 --- a/NewHorizons/Builder/General/GroupBuilder.cs +++ b/NewHorizons/Builder/General/GroupsBuilder.cs @@ -3,10 +3,10 @@ using Logger = NewHorizons.Utility.Logger; namespace NewHorizons.Builder.General; -public static class GroupBuilder +public static class GroupsBuilder { /// - /// puts groups on objects. + /// puts groups on an object, activated by sector. /// run this before the gameobject is active. /// public static void Make(GameObject go, Sector sector) diff --git a/NewHorizons/Builder/Props/DetailBuilder.cs b/NewHorizons/Builder/Props/DetailBuilder.cs index 2c849ecf..25e22eb2 100644 --- a/NewHorizons/Builder/Props/DetailBuilder.cs +++ b/NewHorizons/Builder/Props/DetailBuilder.cs @@ -101,7 +101,7 @@ namespace NewHorizons.Builder.Props prop.transform.localScale = detail.scale != 0 ? Vector3.one * detail.scale : prefab.transform.localScale; - if (!detail.keepLoaded) GroupBuilder.Make(prop, sector); + if (!detail.keepLoaded) GroupsBuilder.Make(prop, sector); prop.SetActive(true); if (prop == null) return null; From 44b181a3879b817543e0ac9565c04fd4939d19fb Mon Sep 17 00:00:00 2001 From: JohnCorby Date: Wed, 7 Sep 2022 18:01:54 -0700 Subject: [PATCH 120/176] dont use file scoped namespace --- NewHorizons/Builder/General/GroupsBuilder.cs | 42 ++++++++++---------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/NewHorizons/Builder/General/GroupsBuilder.cs b/NewHorizons/Builder/General/GroupsBuilder.cs index e37e7eab..5d8ef6a4 100644 --- a/NewHorizons/Builder/General/GroupsBuilder.cs +++ b/NewHorizons/Builder/General/GroupsBuilder.cs @@ -1,29 +1,31 @@ using UnityEngine; using Logger = NewHorizons.Utility.Logger; -namespace NewHorizons.Builder.General; - -public static class GroupsBuilder +namespace NewHorizons.Builder.General { - /// - /// puts groups on an object, activated by sector. - /// run this before the gameobject is active. - /// - public static void Make(GameObject go, Sector sector) + public static class GroupsBuilder { - if (!sector) + /// + /// puts groups on an object, activated by sector. + /// run this before the gameobject is active. + /// + public static void Make(GameObject go, Sector sector) { - Logger.LogWarning($"tried to put groups on {go.name} when sector is null"); - return; - } - if (go.activeInHierarchy) - { - Logger.LogWarning($"tried to put groups on an active gameobject {go.name}"); - return; - } + if (!sector) + { + Logger.LogWarning($"tried to put groups on {go.name} when sector is null"); + return; + } + if (go.activeInHierarchy) + { + Logger.LogWarning($"tried to put groups on an active gameobject {go.name}"); + return; + } + Logger.LogVerbose($"putting groups on {go.name}"); - go.GetAddComponent()._sector = sector; - go.GetAddComponent()._sector = sector; - go.GetAddComponent()._sector = sector; + go.GetAddComponent()._sector = sector; + go.GetAddComponent()._sector = sector; + go.GetAddComponent()._sector = sector; + } } } \ No newline at end of file From 5847a751c6ae78c953b89960bba8fc1a1f8151db Mon Sep 17 00:00:00 2001 From: Ben C Date: Wed, 7 Sep 2022 21:02:13 -0400 Subject: [PATCH 121/176] Add Log Port Image --- docs/content/pages/editor.md | 2 +- docs/content/pages/tutorials/api.md | 2 +- docs/content/pages/tutorials/details.md | 2 +- docs/content/pages/tutorials/planet_gen.md | 2 +- docs/content/static/images/editor/log_port.webp | Bin 0 -> 14282 bytes 5 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 docs/content/static/images/editor/log_port.webp diff --git a/docs/content/pages/editor.md b/docs/content/pages/editor.md index 4f40cb27..100b8409 100644 --- a/docs/content/pages/editor.md +++ b/docs/content/pages/editor.md @@ -54,7 +54,7 @@ You can start the game from the editor by selecting Project -> Run Project this If you're using the mod manager and would like logs to appear there, you need to get the log port from the console, it's always the first entry in the logs. Keep in mind this port changes whenever you restart the manager. -![TODO: Get Log Screenshot]() +![Get the log port]({{ "images/editor/log_port.webp"|static }}) ## Building diff --git a/docs/content/pages/tutorials/api.md b/docs/content/pages/tutorials/api.md index 5c7a8996..2afd6ffe 100644 --- a/docs/content/pages/tutorials/api.md +++ b/docs/content/pages/tutorials/api.md @@ -1,6 +1,6 @@ --- Title: API -Sort_Priority: 40 +Sort_Priority: 20 --- ## How to use the API diff --git a/docs/content/pages/tutorials/details.md b/docs/content/pages/tutorials/details.md index c8483065..0e96a484 100644 --- a/docs/content/pages/tutorials/details.md +++ b/docs/content/pages/tutorials/details.md @@ -1,6 +1,6 @@ --- Title: Detailing -Sort_Priority: 85 +Sort_Priority: 80 --- # Details/Scatterer diff --git a/docs/content/pages/tutorials/planet_gen.md b/docs/content/pages/tutorials/planet_gen.md index c634b1fd..0dc589f0 100644 --- a/docs/content/pages/tutorials/planet_gen.md +++ b/docs/content/pages/tutorials/planet_gen.md @@ -1,6 +1,6 @@ --- Title: Planet Generation -Sort_Priority: 80 +Sort_Priority: 85 --- # Planet Generation diff --git a/docs/content/static/images/editor/log_port.webp b/docs/content/static/images/editor/log_port.webp new file mode 100644 index 0000000000000000000000000000000000000000..0382c847282d306bfa7df8b80e9ca4d2668142fc GIT binary patch literal 14282 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 z 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 Date: Wed, 7 Sep 2022 18:06:20 -0700 Subject: [PATCH 122/176] Revert "dont use file scoped namespace" This reverts commit 44b181a3879b817543e0ac9565c04fd4939d19fb. --- NewHorizons/Builder/General/GroupsBuilder.cs | 46 ++++++++++---------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/NewHorizons/Builder/General/GroupsBuilder.cs b/NewHorizons/Builder/General/GroupsBuilder.cs index 5d8ef6a4..e37e7eab 100644 --- a/NewHorizons/Builder/General/GroupsBuilder.cs +++ b/NewHorizons/Builder/General/GroupsBuilder.cs @@ -1,31 +1,29 @@ using UnityEngine; using Logger = NewHorizons.Utility.Logger; -namespace NewHorizons.Builder.General -{ - public static class GroupsBuilder - { - /// - /// puts groups on an object, activated by sector. - /// run this before the gameobject is active. - /// - public static void Make(GameObject go, Sector sector) - { - if (!sector) - { - Logger.LogWarning($"tried to put groups on {go.name} when sector is null"); - return; - } - if (go.activeInHierarchy) - { - Logger.LogWarning($"tried to put groups on an active gameobject {go.name}"); - return; - } - Logger.LogVerbose($"putting groups on {go.name}"); +namespace NewHorizons.Builder.General; - go.GetAddComponent()._sector = sector; - go.GetAddComponent()._sector = sector; - go.GetAddComponent()._sector = sector; +public static class GroupsBuilder +{ + /// + /// puts groups on an object, activated by sector. + /// run this before the gameobject is active. + /// + public static void Make(GameObject go, Sector sector) + { + if (!sector) + { + Logger.LogWarning($"tried to put groups on {go.name} when sector is null"); + return; } + if (go.activeInHierarchy) + { + Logger.LogWarning($"tried to put groups on an active gameobject {go.name}"); + return; + } + + go.GetAddComponent()._sector = sector; + go.GetAddComponent()._sector = sector; + go.GetAddComponent()._sector = sector; } } \ No newline at end of file From 33e28078a6a6ae2e8b09f21e9701d266e6f62d97 Mon Sep 17 00:00:00 2001 From: Ben C Date: Thu, 8 Sep 2022 08:08:26 -0400 Subject: [PATCH 123/176] Added Publishing Page --- docs/content/pages/tutorials/publishing.md | 56 +++++++++++++++++++ .../pages/tutorials/update_existing.md | 34 +++++++++++ 2 files changed, 90 insertions(+) create mode 100644 docs/content/pages/tutorials/publishing.md create mode 100644 docs/content/pages/tutorials/update_existing.md diff --git a/docs/content/pages/tutorials/publishing.md b/docs/content/pages/tutorials/publishing.md new file mode 100644 index 00000000..8cd36c01 --- /dev/null +++ b/docs/content/pages/tutorials/publishing.md @@ -0,0 +1,56 @@ +--- +Title: Publishing Addons +Sort_Priority: 1 +--- + +# Publishing Your Addon + +This page goes over how to publish a release for your mod and submit your mod to the [outer wilds mod database](https://github.com/ow-mods/ow-mod-db) for review. + +This guide assumes you've created your addon by following [the addon creation guide]({{ "Creating An Addon"|route }}). + +## Housekeeping + +Before you release anything, you'll want to make sure: + +- Your mod has a descriptive `README.md`. (This will be shown on the website) +- Your repo has the description field (click the cog in the right column on the "Code" tab) set. (this will be shown in the manager) +- There's no `config.json` in your addon. (Not super important, but good practice) +- Your manifest has a valid name, author, and unique name. + + +## Releasing + +First things first we're going to create a release on GitHub. To do this, first make sure all your changes are committed and pushed in GitHub desktop. + +Then, edit your `manifest.json` and set the version number to `0.1.0` (or any version number that's higher than `0.0.0`). + +Finally, push your changes to GitHub, head to the "Actions" tab of your repository and you should see an action running. + +Once the action finishes head back to the "Code" tab and you should see a Version 0.1.0 (or whatever version you put) in the column on the right. + +Double check the release, it should have a zip file in the assets with your mod's unique name. + +## Submitting + +The hard part is over now, all that's left is to submit your mod to the database. + +[Head to the mod database and make a new "Add/Update Existing Mod" issue](#) + +Fill this out with your mod's info and make sure to put `xen.NewHorizons` in the parent mod field. + +Once you're done filling out the form, an admin will review your mod, make sure it works, and approve it into the database. + +Congrats! You just published your addon! + +## Updating + +If you want to update your mod, you can simply bump the version number in `manifest.json` again. + +To edit the release notes displayed in discord, enter them in the "Description" field before you commit in GitHub desktop. The most recent commit's description is used for the relase notes. + +**You don't need to create a new issue on the database to update your mod, it will be updated automatically after a few minutes** + + + + diff --git a/docs/content/pages/tutorials/update_existing.md b/docs/content/pages/tutorials/update_existing.md new file mode 100644 index 00000000..229a2f1a --- /dev/null +++ b/docs/content/pages/tutorials/update_existing.md @@ -0,0 +1,34 @@ +--- +Title: Update Planets +Sort_Priority: 85 +--- + +# 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). + +Only some of the above modules are supported (currently) for existing planets. Things you cannot modify for existing planets include: heightmaps, procedural generation, gravity, or their orbits. You also can't make them into stars or binary focal points (but why would you want to, just delete them and replace them entirely). However this still means there are many things you can do: completely change their atmospheres, give them rings, asteroid belts, comet tails, lava, water, prop details, or signals. + +You can also delete parts of an existing planet. Here's part of an example config which would delete the rising sand from Ember Twin: +```json +{ + "name": "Ember Twin", + "removeChildren": [ + "SandSphere_Rising" + ] +} +``` + +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 +{ + "name" : "Ember Twin", + "destroy" : true +} +``` + +Remember that if you destroy Timber Hearth you better put a `Spawn` module on another planet. If you want to entirely replace the solar system you can destroy everything, including the sun. Also, deleting a planet destroys anything orbiting it, so if you want to replace the solar system you can just destroy the sun. If you're making a brand new star system, you don't have to worry about deleting any existing planets; they won't be there. From 694496f9539e7beb4dce04945ba9aadc32339979 Mon Sep 17 00:00:00 2001 From: Ben C Date: Thu, 8 Sep 2022 08:09:41 -0400 Subject: [PATCH 124/176] Paste Link --- docs/content/pages/tutorials/publishing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/pages/tutorials/publishing.md b/docs/content/pages/tutorials/publishing.md index 8cd36c01..9090e4de 100644 --- a/docs/content/pages/tutorials/publishing.md +++ b/docs/content/pages/tutorials/publishing.md @@ -35,7 +35,7 @@ Double check the release, it should have a zip file in the assets with your mod' The hard part is over now, all that's left is to submit your mod to the database. -[Head to the mod database and make a new "Add/Update Existing Mod" issue](#) +[Head to the mod database and make a new "Add/Update Existing Mod" issue](https://github.com/ow-mods/ow-mod-db/issues/new?assignees=&labels=add-mod&template=add-mod.yml&title=%5BYour+mod+name+here%5D) Fill this out with your mod's info and make sure to put `xen.NewHorizons` in the parent mod field. From 9b482bd2570316dd0dadfaaa01aac38daa4484d3 Mon Sep 17 00:00:00 2001 From: Ben C Date: Thu, 8 Sep 2022 17:02:16 -0400 Subject: [PATCH 125/176] Fix Typos --- docs/content/pages/editor.md | 4 ++-- docs/content/pages/tutorials/details.md | 4 ++-- docs/content/pages/tutorials/dialogue.md | 4 ++-- docs/content/pages/tutorials/extending.md | 4 ++-- docs/content/pages/tutorials/publishing.md | 8 ++++---- docs/content/pages/tutorials/update_existing.md | 2 +- docs/content/pages/tutorials/xml.md | 4 ++-- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/content/pages/editor.md b/docs/content/pages/editor.md index 100b8409..fd314789 100644 --- a/docs/content/pages/editor.md +++ b/docs/content/pages/editor.md @@ -5,7 +5,7 @@ Sort_Priority: 50 # Config Editor -Are you tired of manually editting JSON? Do you want richer validation than a JSON schema? Well then the config editor may be for you! +Are you tired of manually editing JSON? Do you want richer validation than a JSON schema? Well then the config editor may be for you! This page outlines how to install and use the config editor. @@ -43,7 +43,7 @@ You can view images and play audio files with this editor. ### XML Files -Right now, XML support is limited. You'll get syntax highlighting but no error checking or auto-fill. +Right now, XML support is limited. You'll get syntax highlighting but no error checking or autofill. ## Running the Game diff --git a/docs/content/pages/tutorials/details.md b/docs/content/pages/tutorials/details.md index 0e96a484..1c4c0cae 100644 --- a/docs/content/pages/tutorials/details.md +++ b/docs/content/pages/tutorials/details.md @@ -20,7 +20,7 @@ The Prop Placer is a convenience tool that lets you manually place details from 1. Pause the game. You will see an extra menu option titled "Toggle Prop Placer Menu". Click it 2. The prop placer menu should now be open. At the bottom of the menu, you will see a list of mods. Click yours. 1. This menu scrolls. If you do not see your mod, it may be further down the list. -3. The Prop Placer is now active! Unpause the game and you can now place Nomai vases using "G" +3. The Prop Placer is now active! Unpause the game, and you can now place Nomai vases using "G" ### How to Save @@ -39,7 +39,7 @@ What's that? You want to place something other than just vases? Well I can't say ### How to Select Props 1. Pause the game again. The prop placer menu should still be visible. -2. At the top of the menu, you'll see a text box contianing the path for the vase. Replace this with the path for the prop you want to place. For example: `DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_1/Props_DreamZone_1/OtherComponentsGroup/Trees_Z1/DreamHouseIsland/Tree_DW_M_Var` +2. At the top of the menu, you'll see a text box containing the path for the vase. Replace this with the path for the prop you want to place. For example: `DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_1/Props_DreamZone_1/OtherComponentsGroup/Trees_Z1/DreamHouseIsland/Tree_DW_M_Var` 3. Tip: use the Unity Explorer mod to find the path for the object you want to place. You only have to do this once. 4. Unpause the game and press "G". Say hello to your new tree! 5. Pause the game again. You will now see the prop you just placed on the list of recently placed props just below the "path" text box. diff --git a/docs/content/pages/tutorials/dialogue.md b/docs/content/pages/tutorials/dialogue.md index 50063480..bd44c160 100644 --- a/docs/content/pages/tutorials/dialogue.md +++ b/docs/content/pages/tutorials/dialogue.md @@ -26,7 +26,7 @@ A condition is a yes/no value stored **for this loop and this loop only**. It c ### Persistent Condition -A persistent condition is similar to a condition, except it *persists* through loops, and is saved on the player's save file. +A persistent condition is similar to a condition, except it *persists* through loops, and is saved on the players save file. ### Remote Trigger @@ -163,4 +163,4 @@ Defining `` in the `` tag instead of a ` @@ -20,7 +20,7 @@ XML is comprised of tags, a tag can represent a section or attribute ``` -Notice how each tag is closed by an identical tag with a slash at the front (i.e `` is closed by ``). +Notice how each tag is closed by an identical tag with a slash at the front (i.e. `` is closed by ``). If the tag has no content you can use the self-closing tag shorthand (i.e. `` doesn't need a closing tag because of the `/` at the end). From fafdb1c48b82fbc33f6c6a6c62e584219d783018 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Thu, 8 Sep 2022 21:12:27 -0400 Subject: [PATCH 126/176] Allow ship log details to be added to vanilla planets --- NewHorizons/Builder/ShipLog/MapModeBuilder.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs index b377bf51..12e55581 100644 --- a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs +++ b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs @@ -219,7 +219,7 @@ namespace NewHorizons.Builder.ShipLog foreach (NewHorizonsBody body in bodies) { - if (body.Config.ShipLog?.mapMode?.manualNavigationPosition == null) continue; + if (body.Config.ShipLog?.mapMode?.manualNavigationPosition == null && body.Config.ShipLog?.mapMode?.details == null) continue; // Sometimes they got other names idk var name = body.Config.name.Replace(" ", ""); @@ -283,6 +283,7 @@ namespace NewHorizons.Builder.ShipLog { gameObject.transform.localScale = Vector3.one * body.Config.ShipLog.mapMode.scale; } + MakeDetails(body, gameObject.transform, greyScaleMaterial); } } } From 1f224b3e72800549339ce1b1e07a090290b724fc Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Thu, 8 Sep 2022 22:48:28 -0400 Subject: [PATCH 127/176] Set shuttle landing radius --- NewHorizons/Builder/Atmosphere/VolumesBuilder.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/NewHorizons/Builder/Atmosphere/VolumesBuilder.cs b/NewHorizons/Builder/Atmosphere/VolumesBuilder.cs index cba3845d..ceb9b758 100644 --- a/NewHorizons/Builder/Atmosphere/VolumesBuilder.cs +++ b/NewHorizons/Builder/Atmosphere/VolumesBuilder.cs @@ -31,6 +31,7 @@ namespace NewHorizons.Builder.Atmosphere PlanetoidRuleset PR = rulesetGO.AddComponent(); PR._altitudeFloor = innerRadius; PR._altitudeCeiling = sphereOfInfluence; + PR._shuttleLandingRadius = sphereOfInfluence; PR._useMinimap = config.Base.showMinimap; PR._useAltimeter = config.Base.showMinimap; From f38aacf7bfe36c9acb2e450946c43e5e26793d22 Mon Sep 17 00:00:00 2001 From: Dominik Date: Fri, 9 Sep 2022 15:13:28 +0200 Subject: [PATCH 128/176] Fix JSON file lookup for Proton * Add separate lookup for JSON and JSONC files * Add warning when an empty folder is found --- NewHorizons/Main.cs | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index 4c0f4403..a2178860 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -490,10 +490,22 @@ namespace NewHorizons } var folder = mod.ModHelper.Manifest.ModFolderPath; + var systemsFolder = Path.Combine(folder, "systems"); + var planetsFolder = Path.Combine(folder, "planets"); + // Load systems first so that when we load bodies later we can check for missing ones - if (Directory.Exists(folder + @"systems\")) + if (Directory.Exists(systemsFolder)) { - foreach (var file in Directory.GetFiles(folder + @"systems\", "*.json?", SearchOption.AllDirectories)) + var systemFiles = Directory.GetFiles(systemsFolder, "*.json", SearchOption.AllDirectories) + .Concat(Directory.GetFiles(systemsFolder, "*.jsonc", SearchOption.AllDirectories)) + .ToArray(); + + if(systemFiles.Length == 0) + { + Logger.LogWarning($"Found no JSON files in systems folder: {systemsFolder}"); + } + + foreach (var file in systemFiles) { var name = Path.GetFileNameWithoutExtension(file); @@ -526,9 +538,18 @@ namespace NewHorizons } } } - if (Directory.Exists(folder + "planets")) + if (Directory.Exists(planetsFolder)) { - foreach (var file in Directory.GetFiles(folder + @"planets\", "*.json?", SearchOption.AllDirectories)) + var planetFiles = Directory.GetFiles(planetsFolder, "*.json", SearchOption.AllDirectories) + .Concat(Directory.GetFiles(planetsFolder, "*.jsonc", SearchOption.AllDirectories)) + .ToArray(); + + if(planetFiles.Length == 0) + { + Logger.LogWarning($"Found no JSON files in planets folder: {planetsFolder}"); + } + + foreach (var file in planetFiles) { var relativeDirectory = file.Replace(folder, ""); var body = LoadConfig(mod, relativeDirectory); From 6bd84bef8353d8408e5f9f0bea62328aa47355f8 Mon Sep 17 00:00:00 2001 From: Dominik Date: Fri, 9 Sep 2022 15:39:07 +0200 Subject: [PATCH 129/176] Change log level to verbose --- NewHorizons/Main.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index a2178860..b86c8d65 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -502,7 +502,7 @@ namespace NewHorizons if(systemFiles.Length == 0) { - Logger.LogWarning($"Found no JSON files in systems folder: {systemsFolder}"); + Logger.LogVerbose($"Found no JSON files in systems folder: {systemsFolder}"); } foreach (var file in systemFiles) @@ -546,7 +546,7 @@ namespace NewHorizons if(planetFiles.Length == 0) { - Logger.LogWarning($"Found no JSON files in planets folder: {planetsFolder}"); + Logger.LogVerbose($"Found no JSON files in planets folder: {planetsFolder}"); } foreach (var file in planetFiles) From e450a2b4593e6b25d9cb43bd3522a3cce1b81ab6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Garro?= Date: Fri, 9 Sep 2022 18:48:31 -0300 Subject: [PATCH 130/176] Fix dialog text not trimmed in translation table key --- NewHorizons/Builder/Props/DialogueBuilder.cs | 6 ++++-- NewHorizons/Handlers/TranslationHandler.cs | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/NewHorizons/Builder/Props/DialogueBuilder.cs b/NewHorizons/Builder/Props/DialogueBuilder.cs index f917c973..7d1beb0c 100644 --- a/NewHorizons/Builder/Props/DialogueBuilder.cs +++ b/NewHorizons/Builder/Props/DialogueBuilder.cs @@ -211,7 +211,8 @@ namespace NewHorizons.Builder.Props { XmlNode pageData = (XmlNode)Page; var text = pageData.InnerText; - TranslationHandler.AddDialogue(text, name); + // The text is trimmed in CharacterDialogueTree.LoadXml, so we also need to trim it for the key + TranslationHandler.AddDialogue(text, true, name); } xmlText = xmlNode2.SelectNodes("DialogueOptionsList/DialogueOption/Text"); @@ -219,7 +220,8 @@ namespace NewHorizons.Builder.Props { XmlNode pageData = (XmlNode)Page; var text = pageData.InnerText; - TranslationHandler.AddDialogue(text, characterName, name); + // The text is trimmed in DialogueText constructor (_listTextBlocks), so we also need to trim it for the key + TranslationHandler.AddDialogue(text, true, characterName, name); } } } diff --git a/NewHorizons/Handlers/TranslationHandler.cs b/NewHorizons/Handlers/TranslationHandler.cs index 03910e2b..38c6e76e 100644 --- a/NewHorizons/Handlers/TranslationHandler.cs +++ b/NewHorizons/Handlers/TranslationHandler.cs @@ -100,9 +100,9 @@ namespace NewHorizons.Handlers } } - public static void AddDialogue(string rawText, params string[] rawPreText) + public static void AddDialogue(string rawText, bool trimRawTextForKey = false, params string[] rawPreText) { - var key = string.Join(string.Empty, rawPreText) + rawText; + var key = string.Join(string.Empty, rawPreText) + (trimRawTextForKey? rawText.Trim() : rawText); var text = GetTranslation(rawText, TextType.DIALOGUE); From 79b842cc24ea2c8c52a234c0d92e6a112a51d1a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Garro?= Date: Fri, 9 Sep 2022 18:53:28 -0300 Subject: [PATCH 131/176] Swap comments --- NewHorizons/Builder/Props/DialogueBuilder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NewHorizons/Builder/Props/DialogueBuilder.cs b/NewHorizons/Builder/Props/DialogueBuilder.cs index 7d1beb0c..74918287 100644 --- a/NewHorizons/Builder/Props/DialogueBuilder.cs +++ b/NewHorizons/Builder/Props/DialogueBuilder.cs @@ -211,7 +211,7 @@ namespace NewHorizons.Builder.Props { XmlNode pageData = (XmlNode)Page; var text = pageData.InnerText; - // The text is trimmed in CharacterDialogueTree.LoadXml, so we also need to trim it for the key + // The text is trimmed in DialogueText constructor (_listTextBlocks), so we also need to trim it for the key TranslationHandler.AddDialogue(text, true, name); } @@ -220,7 +220,7 @@ namespace NewHorizons.Builder.Props { XmlNode pageData = (XmlNode)Page; var text = pageData.InnerText; - // The text is trimmed in DialogueText constructor (_listTextBlocks), so we also need to trim it for the key + // The text is trimmed in CharacterDialogueTree.LoadXml, so we also need to trim it for the key TranslationHandler.AddDialogue(text, true, characterName, name); } } From 867c180ae99ffba471e5235469a84d369ff379cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Garro?= Date: Fri, 9 Sep 2022 18:55:21 -0300 Subject: [PATCH 132/176] Rename variables --- NewHorizons/Builder/Props/DialogueBuilder.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/NewHorizons/Builder/Props/DialogueBuilder.cs b/NewHorizons/Builder/Props/DialogueBuilder.cs index 74918287..39712108 100644 --- a/NewHorizons/Builder/Props/DialogueBuilder.cs +++ b/NewHorizons/Builder/Props/DialogueBuilder.cs @@ -207,19 +207,19 @@ namespace NewHorizons.Builder.Props var name = xmlNode2.SelectSingleNode("Name").InnerText; XmlNodeList xmlText = xmlNode2.SelectNodes("Dialogue/Page"); - foreach (object Page in xmlText) + foreach (object page in xmlText) { - XmlNode pageData = (XmlNode)Page; + XmlNode pageData = (XmlNode)page; var text = pageData.InnerText; // The text is trimmed in DialogueText constructor (_listTextBlocks), so we also need to trim it for the key TranslationHandler.AddDialogue(text, true, name); } xmlText = xmlNode2.SelectNodes("DialogueOptionsList/DialogueOption/Text"); - foreach (object Page in xmlText) + foreach (object option in xmlText) { - XmlNode pageData = (XmlNode)Page; - var text = pageData.InnerText; + XmlNode optionData = (XmlNode)option; + var text = optionData.InnerText; // The text is trimmed in CharacterDialogueTree.LoadXml, so we also need to trim it for the key TranslationHandler.AddDialogue(text, true, characterName, name); } From 5fee72e649472f09bd250262ec9e12ba5fab20f9 Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 9 Sep 2022 19:38:27 -0400 Subject: [PATCH 133/176] Patch CharacterDialogueTree to end conversation when attaching --- .../Patches/CharacterDialogueTreePatches.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 NewHorizons/Patches/CharacterDialogueTreePatches.cs diff --git a/NewHorizons/Patches/CharacterDialogueTreePatches.cs b/NewHorizons/Patches/CharacterDialogueTreePatches.cs new file mode 100644 index 00000000..6493eba1 --- /dev/null +++ b/NewHorizons/Patches/CharacterDialogueTreePatches.cs @@ -0,0 +1,21 @@ +using HarmonyLib; + +namespace NewHorizons.Patches; + +[HarmonyPatch] +internal class CharacterDialogueTreePatches +{ + [HarmonyPrefix] + [HarmonyPatch(typeof(CharacterDialogueTree), nameof(CharacterDialogueTree.Awake))] + private static void CharacterDialogueTree_Awake(CharacterDialogueTree __instance) + { + GlobalMessenger.AddListener("AttachPlayerToPoint", (_) => __instance.EndConversation()); + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(CharacterDialogueTree), nameof(CharacterDialogueTree.OnDestroy))] + private static void CharacterDialogueTree_OnDestroy(CharacterDialogueTree __instance) + { + GlobalMessenger.RemoveListener("AttachPlayerToPoint", (_) => __instance.EndConversation()); + } +} From 44cf3d3fdcf5e150e563057afb064c789e3780c9 Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 10 Sep 2022 10:07:26 -0400 Subject: [PATCH 134/176] Remove parentheses --- NewHorizons/Patches/CharacterDialogueTreePatches.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NewHorizons/Patches/CharacterDialogueTreePatches.cs b/NewHorizons/Patches/CharacterDialogueTreePatches.cs index 6493eba1..20bcaa3a 100644 --- a/NewHorizons/Patches/CharacterDialogueTreePatches.cs +++ b/NewHorizons/Patches/CharacterDialogueTreePatches.cs @@ -9,13 +9,13 @@ internal class CharacterDialogueTreePatches [HarmonyPatch(typeof(CharacterDialogueTree), nameof(CharacterDialogueTree.Awake))] private static void CharacterDialogueTree_Awake(CharacterDialogueTree __instance) { - GlobalMessenger.AddListener("AttachPlayerToPoint", (_) => __instance.EndConversation()); + GlobalMessenger.AddListener("AttachPlayerToPoint", _ => __instance.EndConversation()); } [HarmonyPrefix] [HarmonyPatch(typeof(CharacterDialogueTree), nameof(CharacterDialogueTree.OnDestroy))] private static void CharacterDialogueTree_OnDestroy(CharacterDialogueTree __instance) { - GlobalMessenger.RemoveListener("AttachPlayerToPoint", (_) => __instance.EndConversation()); + GlobalMessenger.RemoveListener("AttachPlayerToPoint", _ => __instance.EndConversation()); } } From eb3dd9a827f375d68ca63809db68a227e4be1ac8 Mon Sep 17 00:00:00 2001 From: Ben C Date: Sat, 10 Sep 2022 10:51:54 -0400 Subject: [PATCH 135/176] Update faq.jinja2 --- docs/content/pages/faq.jinja2 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/content/pages/faq.jinja2 b/docs/content/pages/faq.jinja2 index c5b95209..717f0d4a 100644 --- a/docs/content/pages/faq.jinja2 +++ b/docs/content/pages/faq.jinja2 @@ -46,14 +46,14 @@ faq( "ui-program", "Will you make a UI program to generate json files in the future?", - "Maybe! Will have to wait until New Horizons gets to version 1.0.0." + "Yes! It's available [on GitHub](https://github.com/Outer-Wilds-New-Horizons/nh-config-editor){ target='_blank' }." ) }} {{ faq( "when-version-1", "When will New Horizons get to version 1.0.0.", - "Soon/eventually/never/yesterday." + "It already did **BOZO**!!!!!" ) }} {{ From 5ab727384b59f2336283b1093f011d4ce3d2824a Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Sat, 10 Sep 2022 12:11:29 -0400 Subject: [PATCH 136/176] Allow more than one ring --- NewHorizons/Builder/Body/ProxyBuilder.cs | 9 ++++++--- NewHorizons/Builder/Body/RingBuilder.cs | 2 +- NewHorizons/External/Configs/PlanetConfig.cs | 8 ++++++-- NewHorizons/External/Modules/RingModule.cs | 5 +++++ NewHorizons/Handlers/PlanetCreationHandler.cs | 7 +++++-- NewHorizons/Handlers/TitleSceneHandler.cs | 15 +++++++++------ 6 files changed, 32 insertions(+), 14 deletions(-) diff --git a/NewHorizons/Builder/Body/ProxyBuilder.cs b/NewHorizons/Builder/Body/ProxyBuilder.cs index 32f7d355..89820072 100644 --- a/NewHorizons/Builder/Body/ProxyBuilder.cs +++ b/NewHorizons/Builder/Body/ProxyBuilder.cs @@ -120,10 +120,13 @@ namespace NewHorizons.Builder.Body } } - if (body.Config.Ring != null) + if (body.Config.Rings != null) { - RingBuilder.MakeRingGraphics(proxy, null, body.Config.Ring, body.Mod); - if (realSize < body.Config.Ring.outerRadius) realSize = body.Config.Ring.outerRadius; + foreach (var ring in body.Config.Rings) + { + RingBuilder.MakeRingGraphics(proxy, null, ring, body.Mod); + if (realSize < ring.outerRadius) realSize = ring.outerRadius; + } } Renderer starAtmosphere = null; diff --git a/NewHorizons/Builder/Body/RingBuilder.cs b/NewHorizons/Builder/Body/RingBuilder.cs index 9d2e6b05..a0918a56 100644 --- a/NewHorizons/Builder/Body/RingBuilder.cs +++ b/NewHorizons/Builder/Body/RingBuilder.cs @@ -73,7 +73,7 @@ namespace NewHorizons.Builder.Body return null; } - var ringGO = new GameObject("Ring"); + var ringGO = new GameObject(!string.IsNullOrEmpty(ring.rename) ? ring.rename : "Ring"); ringGO.transform.parent = sector?.transform ?? rootObject.transform; ringGO.transform.position = rootObject.transform.position; ringGO.transform.rotation = rootObject.transform.rotation; diff --git a/NewHorizons/External/Configs/PlanetConfig.cs b/NewHorizons/External/Configs/PlanetConfig.cs index 4ea8a886..d429d405 100644 --- a/NewHorizons/External/Configs/PlanetConfig.cs +++ b/NewHorizons/External/Configs/PlanetConfig.cs @@ -67,6 +67,10 @@ namespace NewHorizons.External.Configs [Obsolete("Signal is deprecated, please use Props->signals")] public SignalModule Signal; + + [Obsolete("Ring is deprecated, please use Rings")] + public RingModule Ring; + #endregion Obsolete /// @@ -135,9 +139,9 @@ namespace NewHorizons.External.Configs public string[] removeChildren; /// - /// Creates a ring around the planet + /// Create rings around the planet /// - public RingModule Ring; + public RingModule[] Rings; /// /// Add sand to this planet diff --git a/NewHorizons/External/Modules/RingModule.cs b/NewHorizons/External/Modules/RingModule.cs index 5f21a8a6..a5c93c4c 100644 --- a/NewHorizons/External/Modules/RingModule.cs +++ b/NewHorizons/External/Modules/RingModule.cs @@ -62,5 +62,10 @@ namespace NewHorizons.External.Modules /// Fade rings in/out over time. Optional. Value between 0-1, time is in minutes. /// public TimeValuePair[] opacityCurve; + + /// + /// An optional rename of this object + /// + public string rename; } } \ No newline at end of file diff --git a/NewHorizons/Handlers/PlanetCreationHandler.cs b/NewHorizons/Handlers/PlanetCreationHandler.cs index ec75a23c..4e4bab9b 100644 --- a/NewHorizons/Handlers/PlanetCreationHandler.cs +++ b/NewHorizons/Handlers/PlanetCreationHandler.cs @@ -547,9 +547,12 @@ namespace NewHorizons.Handlers } } - if (body.Config.Ring != null) + if (body.Config.Rings != null) { - RingBuilder.Make(go, sector, body.Config.Ring, body.Mod); + foreach (var ring in body.Config.Rings) + { + RingBuilder.Make(go, sector, ring, body.Mod); + } } if (body.Config.AsteroidBelt != null) diff --git a/NewHorizons/Handlers/TitleSceneHandler.cs b/NewHorizons/Handlers/TitleSceneHandler.cs index ea435305..9faee29c 100644 --- a/NewHorizons/Handlers/TitleSceneHandler.cs +++ b/NewHorizons/Handlers/TitleSceneHandler.cs @@ -107,13 +107,16 @@ namespace NewHorizons.Handlers } pivot.name = "Pivot"; - if (body.Config.Ring != null) + if (body.Config.Rings != null && body.Config.Rings.Length > 0) { - RingModule newRing = new RingModule(); - newRing.innerRadius = size * 1.2f; - newRing.outerRadius = size * 2f; - newRing.texture = body.Config.Ring.texture; - var ring = RingBuilder.Make(titleScreenGO, null, newRing, body.Mod); + foreach (var ring in body.Config.Rings) + { + RingModule newRing = new RingModule(); + newRing.innerRadius = size * 1.2f; + newRing.outerRadius = size * 2f; + newRing.texture = ring.texture; + RingBuilder.Make(titleScreenGO, null, newRing, body.Mod); + } titleScreenGO.transform.localScale = Vector3.one * 0.8f; } From a4dc54712a754d6ae92596e8afc692bcd6458f16 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 10 Sep 2022 16:13:41 +0000 Subject: [PATCH 137/176] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index 9a5b05f9..5d000e07 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -100,9 +100,12 @@ "type": "string" } }, - "Ring": { - "description": "Creates a ring around the planet", - "$ref": "#/definitions/RingModule" + "Rings": { + "type": "array", + "description": "Create rings around the planet", + "items": { + "$ref": "#/definitions/RingModule" + } }, "Sand": { "description": "Add sand to this planet", @@ -2062,6 +2065,10 @@ "items": { "$ref": "#/definitions/TimeValuePair" } + }, + "rename": { + "type": "string", + "description": "An optional rename of this object" } } }, From 8705bd54b641be7994d03255ddeb208db3b9eabd Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Sat, 10 Sep 2022 12:15:21 -0400 Subject: [PATCH 138/176] Migrate --- NewHorizons/External/Configs/PlanetConfig.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/NewHorizons/External/Configs/PlanetConfig.cs b/NewHorizons/External/Configs/PlanetConfig.cs index d429d405..55c59231 100644 --- a/NewHorizons/External/Configs/PlanetConfig.cs +++ b/NewHorizons/External/Configs/PlanetConfig.cs @@ -405,10 +405,20 @@ namespace NewHorizons.External.Configs if (!string.IsNullOrEmpty(Cloak.audioFilePath)) Cloak.audio = Cloak.audioFilePath; } - // Rings are no longer variable size module + // Ring is now a list so you can have many per planet if (Ring != null) { - if (Ring.curve != null) Ring.scaleCurve = Ring.curve; + if (Rings == null) Rings = new RingModule[0]; + Rings = Rings.Append(Ring).ToArray(); + } + + // Rings are no longer variable size module + if (Rings != null) + { + foreach (var ring in Rings) + { + if (ring.curve != null) ring.scaleCurve = ring.curve; + } } } } From 53e8b281a0f21aee8fb36e6c77554f571cbae93f Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Sat, 10 Sep 2022 13:24:33 -0400 Subject: [PATCH 139/176] Me when --- NewHorizons/Patches/CharacterDialogueTreePatches.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/NewHorizons/Patches/CharacterDialogueTreePatches.cs b/NewHorizons/Patches/CharacterDialogueTreePatches.cs index 20bcaa3a..2d4a8831 100644 --- a/NewHorizons/Patches/CharacterDialogueTreePatches.cs +++ b/NewHorizons/Patches/CharacterDialogueTreePatches.cs @@ -3,19 +3,24 @@ using HarmonyLib; namespace NewHorizons.Patches; [HarmonyPatch] -internal class CharacterDialogueTreePatches +internal static class CharacterDialogueTreePatches { [HarmonyPrefix] [HarmonyPatch(typeof(CharacterDialogueTree), nameof(CharacterDialogueTree.Awake))] private static void CharacterDialogueTree_Awake(CharacterDialogueTree __instance) { - GlobalMessenger.AddListener("AttachPlayerToPoint", _ => __instance.EndConversation()); + GlobalMessenger.AddListener("AttachPlayerToPoint", __instance.OnAttachPlayerToPoint); } [HarmonyPrefix] [HarmonyPatch(typeof(CharacterDialogueTree), nameof(CharacterDialogueTree.OnDestroy))] private static void CharacterDialogueTree_OnDestroy(CharacterDialogueTree __instance) { - GlobalMessenger.RemoveListener("AttachPlayerToPoint", _ => __instance.EndConversation()); + GlobalMessenger.RemoveListener("AttachPlayerToPoint", __instance.OnAttachPlayerToPoint); + } + + private static void OnAttachPlayerToPoint(this CharacterDialogueTree characterDialogueTree, OWRigidbody rigidbody) + { + characterDialogueTree.EndConversation(); } } From e5b652ba22babd2b42d6c396ba5d3d617443cf99 Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 10 Sep 2022 20:00:09 -0400 Subject: [PATCH 140/176] Allow overriding far clip plane --- .../External/Configs/StarSystemConfig.cs | 5 ++++ NewHorizons/Patches/OWCameraPatch.cs | 27 +++++-------------- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/NewHorizons/External/Configs/StarSystemConfig.cs b/NewHorizons/External/Configs/StarSystemConfig.cs index ce311174..5d3963aa 100644 --- a/NewHorizons/External/Configs/StarSystemConfig.cs +++ b/NewHorizons/External/Configs/StarSystemConfig.cs @@ -14,6 +14,11 @@ namespace NewHorizons.External.Configs [JsonObject] public class StarSystemConfig { + /// + /// An override value for the far clip plane. Allows you to see farther. + /// + public float farClipPlaneOverride; + /// /// Whether this system can be warped to via the warp drive. If you set factRequiredForWarp, this will be true. /// diff --git a/NewHorizons/Patches/OWCameraPatch.cs b/NewHorizons/Patches/OWCameraPatch.cs index 863b56bd..ccdafafc 100644 --- a/NewHorizons/Patches/OWCameraPatch.cs +++ b/NewHorizons/Patches/OWCameraPatch.cs @@ -1,4 +1,4 @@ -using HarmonyLib; +using HarmonyLib; namespace NewHorizons.Patches { [HarmonyPatch] @@ -8,25 +8,12 @@ namespace NewHorizons.Patches [HarmonyPatch(typeof(OWCamera), nameof(OWCamera.Awake))] public static void OnOWCameraAwake(OWCamera __instance) { - // var oldDist = __instance.farClipPlane; - // var newDist = __instance.farClipPlane * 10f; - // if (__instance.useFarCamera) Mathf.Clamp(newDist, oldDist, 50000f); - // else newDist = Mathf.Clamp(newDist, oldDist, 10000000f); - // __instance.farClipPlane = newDist; - // __instance.farCameraDistance = newDist; - // __instance.mainCamera.farClipPlane = newDist; + if (Main.SystemDict.TryGetValue(Main.Instance.CurrentStarSystem, out var system) && system?.Config?.farClipPlaneOverride != 0f) + { + __instance.farClipPlane = system.Config.farClipPlaneOverride; + __instance.farCameraDistance = system.Config.farClipPlaneOverride; + __instance.mainCamera.farClipPlane = system.Config.farClipPlaneOverride; + } } - - // [HarmonyPrefix] - // [HarmonyPatch(typeof(OWCamera), nameof(OWCamera.RebuildSkybox))] - // public static bool OnOWCameraRebuildSkybox(OWCamera __instance) - // { - // __instance._skyboxCommandBuffer = new CommandBuffer(); - // __instance._skyboxCommandBuffer.name = "Skybox"; - // var camera = __instance._useFarCamera && !SystemInfo.usesReversedZBuffer ? __instance._farCamera : __instance._mainCamera; - // CameraEvent evt = CameraEvent.BeforeSkybox; - // camera.AddCommandBuffer(evt, __instance._skyboxCommandBuffer); - // return false; - // } } } From 7c54d20d63cda45965388e51eb85f61cf44ea24d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 11 Sep 2022 00:02:09 +0000 Subject: [PATCH 141/176] Updated Schemas --- NewHorizons/Schemas/star_system_schema.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/NewHorizons/Schemas/star_system_schema.json b/NewHorizons/Schemas/star_system_schema.json index 032fb8f1..f1f8785e 100644 --- a/NewHorizons/Schemas/star_system_schema.json +++ b/NewHorizons/Schemas/star_system_schema.json @@ -5,6 +5,11 @@ "description": "Configuration for a specific star system", "additionalProperties": false, "properties": { + "farClipPlaneOverride": { + "type": "number", + "description": "An override value for the far clip plane. Allows you to see farther.", + "format": "float" + }, "canEnterViaWarpDrive": { "type": "boolean", "description": "Whether this system can be warped to via the warp drive. If you set factRequiredForWarp, this will be true.", From a31c62ba37f26d28285b9e91d140f664e3c90678 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Sat, 3 Sep 2022 23:00:00 -0400 Subject: [PATCH 142/176] Fix credits --- NewHorizons/Handlers/CreditsHandler.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/NewHorizons/Handlers/CreditsHandler.cs b/NewHorizons/Handlers/CreditsHandler.cs index 9d7e7405..f71a73ef 100644 --- a/NewHorizons/Handlers/CreditsHandler.cs +++ b/NewHorizons/Handlers/CreditsHandler.cs @@ -39,7 +39,7 @@ namespace NewHorizons.Handlers private static void AddCreditsSection(string sectionName, string[] entries, ref XmlDocument xml) { - var finalCredits = xml.SelectSingleNode("Credits/section"); + var finalCredits = xml.SelectSingleNode("Credits/section[@name='CreditsFinal']"); /* * Looks bad, would need more customization, complicated, messes up music timing, wont do for now @@ -134,11 +134,8 @@ namespace NewHorizons.Handlers { var rootSection = MakeNode(doc, "section", new Dictionary() { - { "platform", "All" }, - { "type", "Scroll" }, - { "scrollDuration", "214" }, - { "spacing", "12" }, - { "width", "1590" } + { "name", "Custom" }, + { "credits-type", "Final Fast Krazy" } }); var titleLayout = MakeNode(doc, "layout", new Dictionary() From 5aba437596a96a1ed563b38f0e40091c08837f7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Garro?= Date: Tue, 13 Sep 2022 19:30:23 -0300 Subject: [PATCH 143/176] Fix race conditions with slide textures loading --- .../Builder/Props/ProjectionBuilder.cs | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/NewHorizons/Builder/Props/ProjectionBuilder.cs b/NewHorizons/Builder/Props/ProjectionBuilder.cs index f6cb399d..58edb896 100644 --- a/NewHorizons/Builder/Props/ProjectionBuilder.cs +++ b/NewHorizons/Builder/Props/ProjectionBuilder.cs @@ -4,8 +4,8 @@ using NewHorizons.Utility; using OWML.Common; using System; using System.Collections.Generic; +using System.Threading; using UnityEngine; -using static NewHorizons.External.Modules.PropModule; using Logger = NewHorizons.Utility.Logger; namespace NewHorizons.Builder.Props { @@ -113,24 +113,22 @@ namespace NewHorizons.Builder.Props slideCollection.slides[index]._image = ImageUtilities.Invert(tex); // Track the first 15 to put on the slide reel object - if (index < 15) + if (index < textures.Length) { textures[index] = tex; - displaySlidesLoaded++; // threading moment - } + if (Interlocked.Increment(ref displaySlidesLoaded) == textures.Length) + { + // all textures required to build the reel's textures have been loaded + var slidesBack = slideReelObj.transform.Find("Props_IP_SlideReel_7/Slides_Back").GetComponent(); + var slidesFront = slideReelObj.transform.Find("Props_IP_SlideReel_7/Slides_Front").GetComponent(); - if (displaySlidesLoaded >= textures.Length) - { - // all textures required to build the reel's textures have been loaded - var slidesBack = slideReelObj.transform.Find("Props_IP_SlideReel_7/Slides_Back").GetComponent(); - var slidesFront = slideReelObj.transform.Find("Props_IP_SlideReel_7/Slides_Front").GetComponent(); - - // Now put together the textures into a 4x4 thing for the materials - var reelTexture = ImageUtilities.MakeReelTexture(textures); - slidesBack.material.mainTexture = reelTexture; - slidesBack.material.SetTexture(EmissionMap, reelTexture); - slidesFront.material.mainTexture = reelTexture; - slidesFront.material.SetTexture(EmissionMap, reelTexture); + // Now put together the textures into a 4x4 thing for the materials + var reelTexture = ImageUtilities.MakeReelTexture(textures); + slidesBack.material.mainTexture = reelTexture; + slidesBack.material.SetTexture(EmissionMap, reelTexture); + slidesFront.material.mainTexture = reelTexture; + slidesFront.material.SetTexture(EmissionMap, reelTexture); + } } } ); @@ -352,9 +350,8 @@ namespace NewHorizons.Builder.Props (Texture2D tex, int index) => { slideCollection.slides[index]._image = tex; - displaySlidesLoaded++; // threading moment - if (displaySlidesLoaded >= slides.Length) + if (Interlocked.Increment(ref displaySlidesLoaded) == slides.Length) { mindSlideProjector.enabled = true; visionBeamEffect.SetActive(true); From d3e44d0e690e2540352c50d0cc8491aacbf7f0b7 Mon Sep 17 00:00:00 2001 From: Nick Date: Tue, 13 Sep 2022 19:06:50 -0400 Subject: [PATCH 144/176] Add locks to async image loading, count how many have loaded, fix error log --- .../Builder/Props/ProjectionBuilder.cs | 8 +- NewHorizons/Utility/ImageUtilities.cs | 83 ++++++++++++------- 2 files changed, 58 insertions(+), 33 deletions(-) diff --git a/NewHorizons/Builder/Props/ProjectionBuilder.cs b/NewHorizons/Builder/Props/ProjectionBuilder.cs index f6cb399d..fc14a3f0 100644 --- a/NewHorizons/Builder/Props/ProjectionBuilder.cs +++ b/NewHorizons/Builder/Props/ProjectionBuilder.cs @@ -95,7 +95,7 @@ namespace NewHorizons.Builder.Props var slide = new Slide(); var slideInfo = info.slides[i]; - imageLoader.pathsToLoad.Add(mod.ModHelper.Manifest.ModFolderPath + slideInfo.imagePath); + imageLoader.PathsToLoad.Add(mod.ModHelper.Manifest.ModFolderPath + slideInfo.imagePath); AddModules(slideInfo, ref slide, mod); @@ -197,7 +197,7 @@ namespace NewHorizons.Builder.Props var slide = new Slide(); var slideInfo = info.slides[i]; - imageLoader.pathsToLoad.Add(mod.ModHelper.Manifest.ModFolderPath + slideInfo.imagePath); + imageLoader.PathsToLoad.Add(mod.ModHelper.Manifest.ModFolderPath + slideInfo.imagePath); AddModules(slideInfo, ref slide, mod); @@ -263,7 +263,7 @@ namespace NewHorizons.Builder.Props var slide = new Slide(); var slideInfo = slides[i]; - imageLoader.pathsToLoad.Add(mod.ModHelper.Manifest.ModFolderPath + slideInfo.imagePath); + imageLoader.PathsToLoad.Add(mod.ModHelper.Manifest.ModFolderPath + slideInfo.imagePath); AddModules(slideInfo, ref slide, mod); @@ -336,7 +336,7 @@ namespace NewHorizons.Builder.Props var slide = new Slide(); var slideInfo = slides[i]; - imageLoader.pathsToLoad.Add(mod.ModHelper.Manifest.ModFolderPath + slideInfo.imagePath); + imageLoader.PathsToLoad.Add(mod.ModHelper.Manifest.ModFolderPath + slideInfo.imagePath); AddModules(slideInfo, ref slide, mod); diff --git a/NewHorizons/Utility/ImageUtilities.cs b/NewHorizons/Utility/ImageUtilities.cs index e875341f..fa8cd7d2 100644 --- a/NewHorizons/Utility/ImageUtilities.cs +++ b/NewHorizons/Utility/ImageUtilities.cs @@ -3,10 +3,10 @@ using System; using System.Collections; using System.Collections.Generic; using System.IO; +using System.Linq; using UnityEngine; using UnityEngine.Events; using UnityEngine.Networking; -using UnityEngine.UIElements; namespace NewHorizons.Utility { @@ -128,7 +128,7 @@ namespace NewHorizons.Utility var texture = (new Texture2D(size * 4, size * 4, TextureFormat.ARGB32, false)); texture.name = "SlideReelAtlas"; - Color[] fillPixels = new Color[size * size * 4 * 4]; + var fillPixels = new Color[size * size * 4 * 4]; for (int xIndex = 0; xIndex < 4; xIndex++) { for (int yIndex = 0; yIndex < 4; yIndex++) @@ -276,8 +276,8 @@ namespace NewHorizons.Utility { var tex = (new Texture2D(1, 1, TextureFormat.ARGB32, false)); tex.name = "Clear"; - Color fillColor = Color.clear; - Color[] fillPixels = new Color[tex.width * tex.height]; + var fillColor = Color.clear; + var fillPixels = new Color[tex.width * tex.height]; for (int i = 0; i < fillPixels.Length; i++) { fillPixels[i] = fillColor; @@ -296,7 +296,7 @@ namespace NewHorizons.Utility { var tex = (new Texture2D(width, height, TextureFormat.ARGB32, false)); tex.name = src.name + "CanvasScaled"; - Color[] fillPixels = new Color[tex.width * tex.height]; + var fillPixels = new Color[tex.width * tex.height]; for (int i = 0; i < tex.width; i++) { for (int j = 0; j < tex.height; j++) @@ -339,14 +339,14 @@ namespace NewHorizons.Utility } public static Texture2D MakeSolidColorTexture(int width, int height, Color color) { - Color[] pixels = new Color[width*height]; + var pixels = new Color[width*height]; for(int i = 0; i < pixels.Length; i++) { pixels[i] = color; } - Texture2D newTexture = new Texture2D(width, height); + var newTexture = new Texture2D(width, height); newTexture.SetPixels(pixels); newTexture.Apply(); return newTexture; @@ -364,10 +364,15 @@ namespace NewHorizons.Utility // Modified from https://stackoverflow.com/a/69141085/9643841 public class AsyncImageLoader : MonoBehaviour { - public List pathsToLoad = new List(); + public List PathsToLoad { get; private set; } = new (); public class ImageLoadedEvent : UnityEvent { } - public ImageLoadedEvent imageLoadedEvent = new ImageLoadedEvent(); + public ImageLoadedEvent imageLoadedEvent = new (); + + private readonly object _lockObj = new(); + + public bool FinishedLoading { get; private set; } + private int _loadedCount = 0; // TODO: set up an optional “StartLoading” and “StartUnloading” condition on AsyncTextureLoader, // and make use of that for at least for projector stuff (require player to be in the same sector as the slides @@ -375,39 +380,59 @@ namespace NewHorizons.Utility void Start() { - for (int i = 0; i < pathsToLoad.Count; i++) + imageLoadedEvent.AddListener(OnImageLoaded); + for (int i = 0; i < PathsToLoad.Count; i++) { - StartCoroutine(DownloadTexture(pathsToLoad[i], i)); + StartCoroutine(DownloadTexture(PathsToLoad[i], i)); + } + } + + private void OnImageLoaded(Texture texture, int index) + { + lock (_lockObj) + { + _loadedCount++; + + if (_loadedCount >= PathsToLoad.Count) + { + Logger.LogVerbose($"Finished loading all textures for {gameObject.name} (one was {PathsToLoad.FirstOrDefault()}"); + FinishedLoading = true; + } } } IEnumerator DownloadTexture(string url, int index) { - if (_loadedTextures.ContainsKey(url)) + lock(_loadedTextures) { - Logger.LogVerbose($"Already loaded image at path: {url}"); - var texture = _loadedTextures[url]; - imageLoadedEvent.Invoke(texture, index); - yield break; + if (_loadedTextures.ContainsKey(url)) + { + Logger.LogVerbose($"Already loaded image {index}:{url}"); + var texture = _loadedTextures[url]; + imageLoadedEvent?.Invoke(texture, index); + yield break; + } } - using (UnityWebRequest uwr = UnityWebRequestTexture.GetTexture(url)) + using UnityWebRequest uwr = UnityWebRequestTexture.GetTexture(url); + + yield return uwr.SendWebRequest(); + + var hasError = uwr.error != null && uwr.error != ""; + + if (hasError) { - yield return uwr.SendWebRequest(); + Logger.LogError($"Failed to load {index}:{url} - {uwr.error}"); + } + else + { + var texture = DownloadHandlerTexture.GetContent(uwr); - var hasError = uwr.error != null && uwr.error != ""; - - if (hasError) // (uwr.result != UnityWebRequest.Result.Success) + lock(_loadedTextures) { - Debug.Log(uwr.error); - } - else - { - var texture = DownloadHandlerTexture.GetContent(uwr); - if (_loadedTextures.ContainsKey(url)) { - Logger.LogVerbose($"Already loaded image at path: {url}"); + Logger.LogVerbose($"Already loaded image {index}:{url}"); Destroy(texture); texture = _loadedTextures[url]; } @@ -416,7 +441,7 @@ namespace NewHorizons.Utility _loadedTextures.Add(url, texture); } - imageLoadedEvent.Invoke(texture, index); + imageLoadedEvent?.Invoke(texture, index); } } } From 13b714ac889a626872fd6420be6cf9ece40b7bd8 Mon Sep 17 00:00:00 2001 From: Nick Date: Tue, 13 Sep 2022 19:12:43 -0400 Subject: [PATCH 145/176] Default to black frame if no path supplied for slide --- NewHorizons/Utility/ImageUtilities.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/NewHorizons/Utility/ImageUtilities.cs b/NewHorizons/Utility/ImageUtilities.cs index fa8cd7d2..536eeb66 100644 --- a/NewHorizons/Utility/ImageUtilities.cs +++ b/NewHorizons/Utility/ImageUtilities.cs @@ -403,6 +403,12 @@ namespace NewHorizons.Utility IEnumerator DownloadTexture(string url, int index) { + if (string.IsNullOrEmpty(url)) + { + imageLoadedEvent?.Invoke(Texture2D.blackTexture, index); + yield break; + } + lock(_loadedTextures) { if (_loadedTextures.ContainsKey(url)) From 7c183d73335686f2dd77ffdffa0969b355a97d0d Mon Sep 17 00:00:00 2001 From: Nick Date: Tue, 13 Sep 2022 19:53:58 -0400 Subject: [PATCH 146/176] Fix up black images --- .../Builder/Props/ProjectionBuilder.cs | 81 ++++++++----------- NewHorizons/Utility/ImageUtilities.cs | 14 ++-- 2 files changed, 37 insertions(+), 58 deletions(-) diff --git a/NewHorizons/Builder/Props/ProjectionBuilder.cs b/NewHorizons/Builder/Props/ProjectionBuilder.cs index fc14a3f0..0f001728 100644 --- a/NewHorizons/Builder/Props/ProjectionBuilder.cs +++ b/NewHorizons/Builder/Props/ProjectionBuilder.cs @@ -2,8 +2,10 @@ using NewHorizons.External.Modules; using NewHorizons.Handlers; using NewHorizons.Utility; using OWML.Common; +using OWML.Common.Menus; using System; using System.Collections.Generic; +using System.IO; using UnityEngine; using static NewHorizons.External.Modules.PropModule; using Logger = NewHorizons.Utility.Logger; @@ -89,19 +91,8 @@ namespace NewHorizons.Builder.Props // The base game ones only have 15 slides max var textures = new Texture2D[slidesCount >= 15 ? 15 : slidesCount]; - var imageLoader = slideReelObj.AddComponent(); - for (int i = 0; i < slidesCount; i++) - { - var slide = new Slide(); - var slideInfo = info.slides[i]; + var imageLoader = AddAsyncLoader(slideReelObj, mod, info.slides, ref slideCollection); - imageLoader.PathsToLoad.Add(mod.ModHelper.Manifest.ModFolderPath + slideInfo.imagePath); - - AddModules(slideInfo, ref slide, mod); - - slideCollection.slides[i] = slide; - } - // this variable just lets us track how many of the first 15 slides have been loaded. // this way as soon as the last one is loaded (due to async loading, this may be // slide 7, or slide 3, or whatever), we can build the slide reel texture. This allows us @@ -191,18 +182,7 @@ namespace NewHorizons.Builder.Props int slidesCount = info.slides.Length; var slideCollection = new SlideCollection(slidesCount); - var imageLoader = projectorObj.AddComponent(); - for (int i = 0; i < slidesCount; i++) - { - var slide = new Slide(); - var slideInfo = info.slides[i]; - - imageLoader.PathsToLoad.Add(mod.ModHelper.Manifest.ModFolderPath + slideInfo.imagePath); - - AddModules(slideInfo, ref slide, mod); - - slideCollection.slides[i] = slide; - } + var imageLoader = AddAsyncLoader(projectorObj, mod, info.slides, ref slideCollection); imageLoader.imageLoadedEvent.AddListener((Texture2D tex, int index) => { slideCollection.slides[index]._image = ImageUtilities.Invert(tex); }); slideCollectionContainer.slideCollection = slideCollection; @@ -256,19 +236,7 @@ namespace NewHorizons.Builder.Props var slidesCount = slides.Length; var slideCollection = new SlideCollection(slidesCount); - - var imageLoader = g.AddComponent(); - for (int i = 0; i < slidesCount; i++) - { - var slide = new Slide(); - var slideInfo = slides[i]; - - imageLoader.PathsToLoad.Add(mod.ModHelper.Manifest.ModFolderPath + slideInfo.imagePath); - - AddModules(slideInfo, ref slide, mod); - - slideCollection.slides[i] = slide; - } + var imageLoader = AddAsyncLoader(g, mod, info.slides, ref slideCollection); imageLoader.imageLoadedEvent.AddListener((Texture2D tex, int index) => { slideCollection.slides[index]._image = tex; }); // attach a component to store all the data for the slides that play when a vision torch scans this target @@ -330,19 +298,8 @@ namespace NewHorizons.Builder.Props var slidesCount = slides.Length; var slideCollection = new SlideCollection(slidesCount); - var imageLoader = standingTorch.AddComponent(); - for (int i = 0; i < slidesCount; i++) - { - var slide = new Slide(); - var slideInfo = slides[i]; + var imageLoader = AddAsyncLoader(standingTorch, mod, slides, ref slideCollection); - imageLoader.PathsToLoad.Add(mod.ModHelper.Manifest.ModFolderPath + slideInfo.imagePath); - - AddModules(slideInfo, ref slide, mod); - - slideCollection.slides[i] = slide; - } - // This variable just lets us track how many of the slides have been loaded. // This way as soon as the last one is loaded (due to async loading, this may be // slide 7, or slide 3, or whatever), we can enable the vision torch. This allows us @@ -378,6 +335,32 @@ namespace NewHorizons.Builder.Props return standingTorch; } + private static ImageUtilities.AsyncImageLoader AddAsyncLoader(GameObject gameObject, IModBehaviour mod, SlideInfo[] slides, ref SlideCollection slideCollection) + { + var imageLoader = gameObject.AddComponent(); + for (int i = 0; i < slides.Length; i++) + { + var slide = new Slide(); + var slideInfo = slides[i]; + + if (string.IsNullOrEmpty(slideInfo.imagePath)) + { + imageLoader.imageLoadedEvent?.Invoke(Texture2D.blackTexture, i); + } + else + { + // Don't use Path.Combine here else you break the Vision + imageLoader.PathsToLoad.Add((i, mod.ModHelper.Manifest.ModFolderPath + slideInfo.imagePath)); + } + + AddModules(slideInfo, ref slide, mod); + + slideCollection.slides[i] = slide; + } + + return imageLoader; + } + private static void AddModules(PropModule.SlideInfo slideInfo, ref Slide slide, IModBehaviour mod) { var modules = new List(); diff --git a/NewHorizons/Utility/ImageUtilities.cs b/NewHorizons/Utility/ImageUtilities.cs index 536eeb66..1f6ae9d8 100644 --- a/NewHorizons/Utility/ImageUtilities.cs +++ b/NewHorizons/Utility/ImageUtilities.cs @@ -1,9 +1,11 @@ using OWML.Common; +using OWML.ModHelper; using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Security.Policy; using UnityEngine; using UnityEngine.Events; using UnityEngine.Networking; @@ -364,7 +366,7 @@ namespace NewHorizons.Utility // Modified from https://stackoverflow.com/a/69141085/9643841 public class AsyncImageLoader : MonoBehaviour { - public List PathsToLoad { get; private set; } = new (); + public List<(int index, string path)> PathsToLoad { get; private set; } = new (); public class ImageLoadedEvent : UnityEvent { } public ImageLoadedEvent imageLoadedEvent = new (); @@ -381,9 +383,9 @@ namespace NewHorizons.Utility void Start() { imageLoadedEvent.AddListener(OnImageLoaded); - for (int i = 0; i < PathsToLoad.Count; i++) + foreach (var (index, path) in PathsToLoad) { - StartCoroutine(DownloadTexture(PathsToLoad[i], i)); + StartCoroutine(DownloadTexture(path, index)); } } @@ -403,12 +405,6 @@ namespace NewHorizons.Utility IEnumerator DownloadTexture(string url, int index) { - if (string.IsNullOrEmpty(url)) - { - imageLoadedEvent?.Invoke(Texture2D.blackTexture, index); - yield break; - } - lock(_loadedTextures) { if (_loadedTextures.ContainsKey(url)) From 86957f2116b11b5dc233c5b075277d8079870e90 Mon Sep 17 00:00:00 2001 From: Nick Date: Tue, 13 Sep 2022 20:00:08 -0400 Subject: [PATCH 147/176] Ok john --- NewHorizons/Patches/MapControllerPatches.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/NewHorizons/Patches/MapControllerPatches.cs b/NewHorizons/Patches/MapControllerPatches.cs index dd53f603..3c13673e 100644 --- a/NewHorizons/Patches/MapControllerPatches.cs +++ b/NewHorizons/Patches/MapControllerPatches.cs @@ -1,4 +1,5 @@ using HarmonyLib; +using UnityEngine; using UnityEngine.SceneManagement; namespace NewHorizons.Patches @@ -10,11 +11,11 @@ namespace NewHorizons.Patches [HarmonyPatch(typeof(MapController), nameof(MapController.Awake))] public static void MapController_Awake(MapController __instance) { - __instance._maxPanDistance = Main.FurthestOrbit * 1.5f; + __instance._maxPanDistance = Mathf.Max(__instance._maxPanDistance, Main.FurthestOrbit * 1.5f); __instance._maxZoomDistance *= 6f; __instance._minPitchAngle = -90f; __instance._zoomSpeed *= 4f; - __instance._mapCamera.farClipPlane = Main.FurthestOrbit * 10f; + __instance._mapCamera.farClipPlane = Mathf.Max(__instance._mapCamera.farClipPlane, Main.FurthestOrbit * 10f); } [HarmonyPostfix] From e074d2ecb1451857642f0f9953337e40ea659d11 Mon Sep 17 00:00:00 2001 From: Nick Date: Tue, 13 Sep 2022 20:18:02 -0400 Subject: [PATCH 148/176] Fix bad merge --- NewHorizons/Builder/Props/ProjectionBuilder.cs | 4 ++-- NewHorizons/Utility/ImageUtilities.cs | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/NewHorizons/Builder/Props/ProjectionBuilder.cs b/NewHorizons/Builder/Props/ProjectionBuilder.cs index cbd607d2..e80d451d 100644 --- a/NewHorizons/Builder/Props/ProjectionBuilder.cs +++ b/NewHorizons/Builder/Props/ProjectionBuilder.cs @@ -2,13 +2,13 @@ using NewHorizons.External.Modules; using NewHorizons.Handlers; using NewHorizons.Utility; using OWML.Common; -using OWML.Common.Menus; using System; using System.Collections.Generic; using System.Threading; -using System.IO; using UnityEngine; +using static NewHorizons.External.Modules.PropModule; using Logger = NewHorizons.Utility.Logger; + namespace NewHorizons.Builder.Props { public static class ProjectionBuilder diff --git a/NewHorizons/Utility/ImageUtilities.cs b/NewHorizons/Utility/ImageUtilities.cs index 1f6ae9d8..cf884938 100644 --- a/NewHorizons/Utility/ImageUtilities.cs +++ b/NewHorizons/Utility/ImageUtilities.cs @@ -1,11 +1,9 @@ using OWML.Common; -using OWML.ModHelper; using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Security.Policy; using UnityEngine; using UnityEngine.Events; using UnityEngine.Networking; From 7bcddf4261c9a1b0fcc6df6bd06367efb90f6741 Mon Sep 17 00:00:00 2001 From: Nick Date: Tue, 13 Sep 2022 21:58:31 -0400 Subject: [PATCH 149/176] Fix collision issue --- NewHorizons/Builder/Props/DetailBuilder.cs | 102 ++++++++++++++------ NewHorizons/Builder/Props/ScatterBuilder.cs | 6 +- 2 files changed, 77 insertions(+), 31 deletions(-) diff --git a/NewHorizons/Builder/Props/DetailBuilder.cs b/NewHorizons/Builder/Props/DetailBuilder.cs index ae7a63f7..f49c8289 100644 --- a/NewHorizons/Builder/Props/DetailBuilder.cs +++ b/NewHorizons/Builder/Props/DetailBuilder.cs @@ -5,8 +5,10 @@ using NewHorizons.Utility; using OWML.Common; using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using UnityEngine; +using Component = UnityEngine.Component; using Logger = NewHorizons.Utility.Logger; namespace NewHorizons.Builder.Props { @@ -71,6 +73,9 @@ namespace NewHorizons.Builder.Props var isTorch = prop.GetComponent() != null; + // Fix a bunch of stuff when done loading + var fixes = new List(); + foreach (var component in prop.GetComponentsInChildren(true)) { if (sector == null) @@ -80,6 +85,16 @@ namespace NewHorizons.Builder.Props else FixSectoredComponent(component, sector, isTorch); FixComponent(component, go, prefab.name); + + if (DetailFixer.fixes.Keys.Any(x => x.IsAssignableFrom(component.GetType()))) + { + fixes.Add(component); + } + } + + if (fixes.Count > 0) + { + prop.AddComponent().SetFixes(fixes); } prop.transform.position = detail.position == null ? go.transform.position : go.transform.TransformPoint(detail.position); @@ -277,44 +292,75 @@ namespace NewHorizons.Builder.Props torchItem.mindProjectorTrigger.enabled = true; torchItem.mindSlideProjector._mindProjectorImageEffect = SearchUtilities.Find("Player_Body/PlayerCamera").GetComponent(); } + } - // Fix a bunch of stuff when done loading - Delay.RunWhen(() => Main.IsSystemReady, () => + /// + /// Performs fixes that have to be done after the system loads + /// Has to be done this way to ensure that scatter works + /// + private class DetailFixer : MonoBehaviour + { + public static Dictionary> fixes = new() { - try + [typeof(AnglerfishAnimController)] = (x) => { - if (component == null) return; - if (component is Animator animator) animator.enabled = true; - else if (component is Collider collider) collider.enabled = true; - else if (component is Renderer renderer) renderer.enabled = true; - else if (component is Shape shape) shape.enabled = true; - else if (component is SectorCullGroup sectorCullGroup) + var angler = x as AnglerfishAnimController; + + Logger.LogVerbose("Enabling anglerfish animation"); + // Remove any reference to its angler + if (angler._anglerfishController) { - sectorCullGroup._inMapView = false; - sectorCullGroup._isFastForwarding = false; - sectorCullGroup.SetVisible(sectorCullGroup.ShouldBeVisible(), true, false); + angler._anglerfishController.OnChangeAnglerState -= angler.OnChangeAnglerState; + angler._anglerfishController.OnAnglerTurn -= angler.OnAnglerTurn; + angler._anglerfishController.OnAnglerSuspended -= angler.OnAnglerSuspended; + angler._anglerfishController.OnAnglerUnsuspended -= angler.OnAnglerUnsuspended; } - // If it's not a moving anglerfish make sure the anim controller is regular - else if (component is AnglerfishAnimController angler && angler.GetComponentInParent() == null) + angler.enabled = true; + angler.OnChangeAnglerState(AnglerfishController.AnglerState.Lurking); + }, + [typeof(SectorCullGroup)] = (x) => + { + var sectorCullGroup = x as SectorCullGroup; + sectorCullGroup._inMapView = false; + sectorCullGroup._isFastForwarding = false; + sectorCullGroup.SetVisible(sectorCullGroup.ShouldBeVisible(), true, false); + }, + [typeof(Shape)] = (x) => (x as Shape).enabled = true, + [typeof(Renderer)] = (x) => (x as Renderer).enabled = true, + [typeof(Collider)] = (x) => (x as Collider).enabled = true, + [typeof(Animator)] = (x) => (x as Animator).enabled = true + }; + + // Have to be public to be copied by Instantiate + public Component[] componentsToFix; + + public void SetFixes(List fixes) + { + // Components must be in a list for unity to properly deep copy + componentsToFix = fixes.ToArray(); + } + + public void Start() + { + for (int i = 0; i < componentsToFix.Length; i++) + { + var component = componentsToFix[i]; + + try { - Logger.LogVerbose("Enabling anglerfish animation"); - // Remove any reference to its angler - if (angler._anglerfishController) + if (component != null) { - angler._anglerfishController.OnChangeAnglerState -= angler.OnChangeAnglerState; - angler._anglerfishController.OnAnglerTurn -= angler.OnAnglerTurn; - angler._anglerfishController.OnAnglerSuspended -= angler.OnAnglerSuspended; - angler._anglerfishController.OnAnglerUnsuspended -= angler.OnAnglerUnsuspended; + var key = fixes.Keys.FirstOrDefault(x => x.IsAssignableFrom(component.GetType())); + var fix = fixes[key]; + fix(component); } - angler.enabled = true; - angler.OnChangeAnglerState(AnglerfishController.AnglerState.Lurking); + } + catch (Exception) + { + Logger.LogWarning($"Failed to fix component {component} on {gameObject.name}"); } } - catch (Exception e) - { - Logger.LogWarning($"Exception when modifying component [{component.GetType().Name}] on [{planetGO.name}] for prop [{prefab}]:\n{e}"); - } - }); + } } } } \ No newline at end of file diff --git a/NewHorizons/Builder/Props/ScatterBuilder.cs b/NewHorizons/Builder/Props/ScatterBuilder.cs index b8aac0e0..2deea2c8 100644 --- a/NewHorizons/Builder/Props/ScatterBuilder.cs +++ b/NewHorizons/Builder/Props/ScatterBuilder.cs @@ -70,7 +70,7 @@ namespace NewHorizons.Builder.Props { scale = propInfo.scale }; - prefab = DetailBuilder.Make(go, sector, prefab, detailInfo); + var scatterPrefab = DetailBuilder.Make(go, sector, prefab, detailInfo); for (int i = 0; i < propInfo.count; i++) { @@ -116,7 +116,7 @@ namespace NewHorizons.Builder.Props point = Quaternion.Euler(90, 0, 0) * point; } - var prop = prefab.InstantiateInactive(); + var prop = scatterPrefab.InstantiateInactive(); prop.transform.SetParent(sector?.transform ?? go.transform); prop.transform.localPosition = go.transform.TransformPoint(point * height); var up = go.transform.InverseTransformPoint(prop.transform.position).normalized; @@ -131,7 +131,7 @@ namespace NewHorizons.Builder.Props prop.SetActive(true); } - GameObject.Destroy(prefab); + GameObject.Destroy(scatterPrefab); } } } From 2cabbc27b193e0fb0e13054bc146a415e6ea48a0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 14 Sep 2022 02:03:22 +0000 Subject: [PATCH 150/176] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index 2d1984c0..a3d243d1 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -1399,7 +1399,7 @@ "type": "boolean", "description": "Should we try to prevent overlap between the scattered details? True by default. If it's affecting load times turn it off.", "default": true - }, + }, "keepLoaded": { "type": "boolean", "description": "Should this detail stay loaded even if you're outside the sector (good for very large props)" From d509e11379ad04c14417c059323764f079644ff5 Mon Sep 17 00:00:00 2001 From: Nick Date: Tue, 13 Sep 2022 23:03:59 -0400 Subject: [PATCH 151/176] Use CommonCameraUtility to fix projection pool remote cameras --- .../CommonCameraHandler.cs | 16 +++++++++++++ .../CommonCameraUtility/ICommonCameraAPI.cs | 13 ++++++++++ .../CameraPatches/NomaiRemoteCameraPatches.cs | 24 +++++++++++++++++++ NewHorizons/manifest.json | 2 +- 4 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 NewHorizons/OtherMods/CommonCameraUtility/CommonCameraHandler.cs create mode 100644 NewHorizons/OtherMods/CommonCameraUtility/ICommonCameraAPI.cs create mode 100644 NewHorizons/Patches/CameraPatches/NomaiRemoteCameraPatches.cs diff --git a/NewHorizons/OtherMods/CommonCameraUtility/CommonCameraHandler.cs b/NewHorizons/OtherMods/CommonCameraUtility/CommonCameraHandler.cs new file mode 100644 index 00000000..2aab1526 --- /dev/null +++ b/NewHorizons/OtherMods/CommonCameraUtility/CommonCameraHandler.cs @@ -0,0 +1,16 @@ +using NewHorizons.OtherMods.MenuFramework; + +namespace NewHorizons.OtherMods.CommonCameraUtility +{ + public static class CommonCameraHandler + { + private static ICommonCameraAPI _cameraAPI; + + static CommonCameraHandler() + { + _cameraAPI = Main.Instance.ModHelper.Interaction.TryGetModApi("xen.CommonCameraUtility"); + } + + public static void RegisterCustomCamera(OWCamera camera) => _cameraAPI.RegisterCustomCamera(camera); + } +} diff --git a/NewHorizons/OtherMods/CommonCameraUtility/ICommonCameraAPI.cs b/NewHorizons/OtherMods/CommonCameraUtility/ICommonCameraAPI.cs new file mode 100644 index 00000000..bd49375b --- /dev/null +++ b/NewHorizons/OtherMods/CommonCameraUtility/ICommonCameraAPI.cs @@ -0,0 +1,13 @@ +using UnityEngine; +using UnityEngine.Events; + +namespace NewHorizons.OtherMods.CommonCameraUtility +{ + public interface ICommonCameraAPI + { + void RegisterCustomCamera(OWCamera OWCamera); + (OWCamera, Camera) CreateCustomCamera(string name); + UnityEvent EquipTool(); + UnityEvent UnequipTool(); + } +} diff --git a/NewHorizons/Patches/CameraPatches/NomaiRemoteCameraPatches.cs b/NewHorizons/Patches/CameraPatches/NomaiRemoteCameraPatches.cs new file mode 100644 index 00000000..29719c0e --- /dev/null +++ b/NewHorizons/Patches/CameraPatches/NomaiRemoteCameraPatches.cs @@ -0,0 +1,24 @@ +using HarmonyLib; +using NewHorizons.OtherMods.CommonCameraUtility; +using UnityEngine; + +namespace NewHorizons.Patches.CameraPatches +{ + [HarmonyPatch] + public static class NomaiRemoteCameraPatches + { + [HarmonyPostfix] + [HarmonyPatch(typeof(NomaiRemoteCamera), nameof(NomaiRemoteCamera.Awake))] + public static void NomaiRemoteCamera_Awake(NomaiRemoteCamera __instance) + { + // Ensures that if the player is visible from the remote camera they look normal + CommonCameraHandler.RegisterCustomCamera(__instance._camera); + + // These layers were left on because it doesnt come up in base game (Dreamworld is inactive, player is far away) + __instance._camera.mainCamera.cullingMask &= ~(1 << LayerMask.NameToLayer("DreamSimulation")); + __instance._camera.mainCamera.cullingMask &= ~(1 < Date: Tue, 13 Sep 2022 23:04:11 -0400 Subject: [PATCH 152/176] Reorganize camera patches --- NewHorizons/Patches/{ => CameraPatches}/OWCameraPatch.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) rename NewHorizons/Patches/{ => CameraPatches}/OWCameraPatch.cs (93%) diff --git a/NewHorizons/Patches/OWCameraPatch.cs b/NewHorizons/Patches/CameraPatches/OWCameraPatch.cs similarity index 93% rename from NewHorizons/Patches/OWCameraPatch.cs rename to NewHorizons/Patches/CameraPatches/OWCameraPatch.cs index ccdafafc..db9251c1 100644 --- a/NewHorizons/Patches/OWCameraPatch.cs +++ b/NewHorizons/Patches/CameraPatches/OWCameraPatch.cs @@ -1,5 +1,6 @@ using HarmonyLib; -namespace NewHorizons.Patches + +namespace NewHorizons.Patches.CameraPatches { [HarmonyPatch] public static class OWCameraPatch From 0b2be0cfcce1a4c39e6c8339833d941f627d48c5 Mon Sep 17 00:00:00 2001 From: Nick Date: Tue, 13 Sep 2022 23:05:36 -0400 Subject: [PATCH 153/176] Revert "Reorganize camera patches" This reverts commit 461cdd6349b64932e8ffe2df87bc0bd7426bd5de. --- NewHorizons/Patches/{CameraPatches => }/OWCameraPatch.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) rename NewHorizons/Patches/{CameraPatches => }/OWCameraPatch.cs (93%) diff --git a/NewHorizons/Patches/CameraPatches/OWCameraPatch.cs b/NewHorizons/Patches/OWCameraPatch.cs similarity index 93% rename from NewHorizons/Patches/CameraPatches/OWCameraPatch.cs rename to NewHorizons/Patches/OWCameraPatch.cs index db9251c1..ccdafafc 100644 --- a/NewHorizons/Patches/CameraPatches/OWCameraPatch.cs +++ b/NewHorizons/Patches/OWCameraPatch.cs @@ -1,6 +1,5 @@ using HarmonyLib; - -namespace NewHorizons.Patches.CameraPatches +namespace NewHorizons.Patches { [HarmonyPatch] public static class OWCameraPatch From 829fedb6a3c02befe556271d9ada804fba15d9e5 Mon Sep 17 00:00:00 2001 From: Nick Date: Tue, 13 Sep 2022 23:05:41 -0400 Subject: [PATCH 154/176] Revert "Use CommonCameraUtility to fix projection pool remote cameras" This reverts commit d509e11379ad04c14417c059323764f079644ff5. --- .../CommonCameraHandler.cs | 16 ------------- .../CommonCameraUtility/ICommonCameraAPI.cs | 13 ---------- .../CameraPatches/NomaiRemoteCameraPatches.cs | 24 ------------------- NewHorizons/manifest.json | 2 +- 4 files changed, 1 insertion(+), 54 deletions(-) delete mode 100644 NewHorizons/OtherMods/CommonCameraUtility/CommonCameraHandler.cs delete mode 100644 NewHorizons/OtherMods/CommonCameraUtility/ICommonCameraAPI.cs delete mode 100644 NewHorizons/Patches/CameraPatches/NomaiRemoteCameraPatches.cs diff --git a/NewHorizons/OtherMods/CommonCameraUtility/CommonCameraHandler.cs b/NewHorizons/OtherMods/CommonCameraUtility/CommonCameraHandler.cs deleted file mode 100644 index 2aab1526..00000000 --- a/NewHorizons/OtherMods/CommonCameraUtility/CommonCameraHandler.cs +++ /dev/null @@ -1,16 +0,0 @@ -using NewHorizons.OtherMods.MenuFramework; - -namespace NewHorizons.OtherMods.CommonCameraUtility -{ - public static class CommonCameraHandler - { - private static ICommonCameraAPI _cameraAPI; - - static CommonCameraHandler() - { - _cameraAPI = Main.Instance.ModHelper.Interaction.TryGetModApi("xen.CommonCameraUtility"); - } - - public static void RegisterCustomCamera(OWCamera camera) => _cameraAPI.RegisterCustomCamera(camera); - } -} diff --git a/NewHorizons/OtherMods/CommonCameraUtility/ICommonCameraAPI.cs b/NewHorizons/OtherMods/CommonCameraUtility/ICommonCameraAPI.cs deleted file mode 100644 index bd49375b..00000000 --- a/NewHorizons/OtherMods/CommonCameraUtility/ICommonCameraAPI.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UnityEngine; -using UnityEngine.Events; - -namespace NewHorizons.OtherMods.CommonCameraUtility -{ - public interface ICommonCameraAPI - { - void RegisterCustomCamera(OWCamera OWCamera); - (OWCamera, Camera) CreateCustomCamera(string name); - UnityEvent EquipTool(); - UnityEvent UnequipTool(); - } -} diff --git a/NewHorizons/Patches/CameraPatches/NomaiRemoteCameraPatches.cs b/NewHorizons/Patches/CameraPatches/NomaiRemoteCameraPatches.cs deleted file mode 100644 index 29719c0e..00000000 --- a/NewHorizons/Patches/CameraPatches/NomaiRemoteCameraPatches.cs +++ /dev/null @@ -1,24 +0,0 @@ -using HarmonyLib; -using NewHorizons.OtherMods.CommonCameraUtility; -using UnityEngine; - -namespace NewHorizons.Patches.CameraPatches -{ - [HarmonyPatch] - public static class NomaiRemoteCameraPatches - { - [HarmonyPostfix] - [HarmonyPatch(typeof(NomaiRemoteCamera), nameof(NomaiRemoteCamera.Awake))] - public static void NomaiRemoteCamera_Awake(NomaiRemoteCamera __instance) - { - // Ensures that if the player is visible from the remote camera they look normal - CommonCameraHandler.RegisterCustomCamera(__instance._camera); - - // These layers were left on because it doesnt come up in base game (Dreamworld is inactive, player is far away) - __instance._camera.mainCamera.cullingMask &= ~(1 << LayerMask.NameToLayer("DreamSimulation")); - __instance._camera.mainCamera.cullingMask &= ~(1 < Date: Tue, 13 Sep 2022 23:08:16 -0400 Subject: [PATCH 155/176] Revert revert revert revert --- .../CommonCameraHandler.cs | 16 +++++++++++++ .../CommonCameraUtility/ICommonCameraAPI.cs | 13 ++++++++++ .../CameraPatches/NomaiRemoteCameraPatches.cs | 24 +++++++++++++++++++ .../{ => CameraPatches}/OWCameraPatch.cs | 3 ++- NewHorizons/manifest.json | 2 +- 5 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 NewHorizons/OtherMods/CommonCameraUtility/CommonCameraHandler.cs create mode 100644 NewHorizons/OtherMods/CommonCameraUtility/ICommonCameraAPI.cs create mode 100644 NewHorizons/Patches/CameraPatches/NomaiRemoteCameraPatches.cs rename NewHorizons/Patches/{ => CameraPatches}/OWCameraPatch.cs (93%) diff --git a/NewHorizons/OtherMods/CommonCameraUtility/CommonCameraHandler.cs b/NewHorizons/OtherMods/CommonCameraUtility/CommonCameraHandler.cs new file mode 100644 index 00000000..2aab1526 --- /dev/null +++ b/NewHorizons/OtherMods/CommonCameraUtility/CommonCameraHandler.cs @@ -0,0 +1,16 @@ +using NewHorizons.OtherMods.MenuFramework; + +namespace NewHorizons.OtherMods.CommonCameraUtility +{ + public static class CommonCameraHandler + { + private static ICommonCameraAPI _cameraAPI; + + static CommonCameraHandler() + { + _cameraAPI = Main.Instance.ModHelper.Interaction.TryGetModApi("xen.CommonCameraUtility"); + } + + public static void RegisterCustomCamera(OWCamera camera) => _cameraAPI.RegisterCustomCamera(camera); + } +} diff --git a/NewHorizons/OtherMods/CommonCameraUtility/ICommonCameraAPI.cs b/NewHorizons/OtherMods/CommonCameraUtility/ICommonCameraAPI.cs new file mode 100644 index 00000000..bd49375b --- /dev/null +++ b/NewHorizons/OtherMods/CommonCameraUtility/ICommonCameraAPI.cs @@ -0,0 +1,13 @@ +using UnityEngine; +using UnityEngine.Events; + +namespace NewHorizons.OtherMods.CommonCameraUtility +{ + public interface ICommonCameraAPI + { + void RegisterCustomCamera(OWCamera OWCamera); + (OWCamera, Camera) CreateCustomCamera(string name); + UnityEvent EquipTool(); + UnityEvent UnequipTool(); + } +} diff --git a/NewHorizons/Patches/CameraPatches/NomaiRemoteCameraPatches.cs b/NewHorizons/Patches/CameraPatches/NomaiRemoteCameraPatches.cs new file mode 100644 index 00000000..29719c0e --- /dev/null +++ b/NewHorizons/Patches/CameraPatches/NomaiRemoteCameraPatches.cs @@ -0,0 +1,24 @@ +using HarmonyLib; +using NewHorizons.OtherMods.CommonCameraUtility; +using UnityEngine; + +namespace NewHorizons.Patches.CameraPatches +{ + [HarmonyPatch] + public static class NomaiRemoteCameraPatches + { + [HarmonyPostfix] + [HarmonyPatch(typeof(NomaiRemoteCamera), nameof(NomaiRemoteCamera.Awake))] + public static void NomaiRemoteCamera_Awake(NomaiRemoteCamera __instance) + { + // Ensures that if the player is visible from the remote camera they look normal + CommonCameraHandler.RegisterCustomCamera(__instance._camera); + + // These layers were left on because it doesnt come up in base game (Dreamworld is inactive, player is far away) + __instance._camera.mainCamera.cullingMask &= ~(1 << LayerMask.NameToLayer("DreamSimulation")); + __instance._camera.mainCamera.cullingMask &= ~(1 < Date: Tue, 13 Sep 2022 21:11:12 -0700 Subject: [PATCH 156/176] use da thing thankies rider --- NewHorizons/Builder/Props/DetailBuilder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NewHorizons/Builder/Props/DetailBuilder.cs b/NewHorizons/Builder/Props/DetailBuilder.cs index 74b19515..fb4ae2df 100644 --- a/NewHorizons/Builder/Props/DetailBuilder.cs +++ b/NewHorizons/Builder/Props/DetailBuilder.cs @@ -87,7 +87,7 @@ namespace NewHorizons.Builder.Props FixComponent(component, go, prefab.name); - if (DetailFixer.fixes.Keys.Any(x => x.IsAssignableFrom(component.GetType()))) + if (DetailFixer.fixes.Keys.Any(x => x.IsInstanceOfType(component))) { fixes.Add(component); } @@ -352,7 +352,7 @@ namespace NewHorizons.Builder.Props { if (component != null) { - var key = fixes.Keys.FirstOrDefault(x => x.IsAssignableFrom(component.GetType())); + var key = fixes.Keys.FirstOrDefault(x => x.IsInstanceOfType(component)); var fix = fixes[key]; fix(component); } From 5ff9f09684953187680a7e5ec71a2c85d3957c95 Mon Sep 17 00:00:00 2001 From: JohnCorby Date: Wed, 14 Sep 2022 16:34:10 -0700 Subject: [PATCH 157/176] revert da thingy --- NewHorizons/Builder/Props/DetailBuilder.cs | 104 ++++++--------------- 1 file changed, 28 insertions(+), 76 deletions(-) diff --git a/NewHorizons/Builder/Props/DetailBuilder.cs b/NewHorizons/Builder/Props/DetailBuilder.cs index fb4ae2df..ae7a63f7 100644 --- a/NewHorizons/Builder/Props/DetailBuilder.cs +++ b/NewHorizons/Builder/Props/DetailBuilder.cs @@ -1,4 +1,3 @@ -using NewHorizons.Builder.General; using NewHorizons.External.Configs; using NewHorizons.External.Modules; using NewHorizons.Handlers; @@ -6,10 +5,8 @@ using NewHorizons.Utility; using OWML.Common; using System; using System.Collections.Generic; -using System.ComponentModel; using System.Linq; using UnityEngine; -using Component = UnityEngine.Component; using Logger = NewHorizons.Utility.Logger; namespace NewHorizons.Builder.Props { @@ -74,9 +71,6 @@ namespace NewHorizons.Builder.Props var isTorch = prop.GetComponent() != null; - // Fix a bunch of stuff when done loading - var fixes = new List(); - foreach (var component in prop.GetComponentsInChildren(true)) { if (sector == null) @@ -86,16 +80,6 @@ namespace NewHorizons.Builder.Props else FixSectoredComponent(component, sector, isTorch); FixComponent(component, go, prefab.name); - - if (DetailFixer.fixes.Keys.Any(x => x.IsInstanceOfType(component))) - { - fixes.Add(component); - } - } - - if (fixes.Count > 0) - { - prop.AddComponent().SetFixes(fixes); } prop.transform.position = detail.position == null ? go.transform.position : go.transform.TransformPoint(detail.position); @@ -116,7 +100,6 @@ namespace NewHorizons.Builder.Props prop.transform.localScale = detail.scale != 0 ? Vector3.one * detail.scale : prefab.transform.localScale; - if (!detail.keepLoaded) GroupsBuilder.Make(prop, sector); prop.SetActive(true); if (prop == null) return null; @@ -294,75 +277,44 @@ namespace NewHorizons.Builder.Props torchItem.mindProjectorTrigger.enabled = true; torchItem.mindSlideProjector._mindProjectorImageEffect = SearchUtilities.Find("Player_Body/PlayerCamera").GetComponent(); } - } - /// - /// Performs fixes that have to be done after the system loads - /// Has to be done this way to ensure that scatter works - /// - private class DetailFixer : MonoBehaviour - { - public static Dictionary> fixes = new() + // Fix a bunch of stuff when done loading + Delay.RunWhen(() => Main.IsSystemReady, () => { - [typeof(AnglerfishAnimController)] = (x) => + try { - var angler = x as AnglerfishAnimController; - - Logger.LogVerbose("Enabling anglerfish animation"); - // Remove any reference to its angler - if (angler._anglerfishController) + if (component == null) return; + if (component is Animator animator) animator.enabled = true; + else if (component is Collider collider) collider.enabled = true; + else if (component is Renderer renderer) renderer.enabled = true; + else if (component is Shape shape) shape.enabled = true; + else if (component is SectorCullGroup sectorCullGroup) { - angler._anglerfishController.OnChangeAnglerState -= angler.OnChangeAnglerState; - angler._anglerfishController.OnAnglerTurn -= angler.OnAnglerTurn; - angler._anglerfishController.OnAnglerSuspended -= angler.OnAnglerSuspended; - angler._anglerfishController.OnAnglerUnsuspended -= angler.OnAnglerUnsuspended; + sectorCullGroup._inMapView = false; + sectorCullGroup._isFastForwarding = false; + sectorCullGroup.SetVisible(sectorCullGroup.ShouldBeVisible(), true, false); } - angler.enabled = true; - angler.OnChangeAnglerState(AnglerfishController.AnglerState.Lurking); - }, - [typeof(SectorCullGroup)] = (x) => - { - var sectorCullGroup = x as SectorCullGroup; - sectorCullGroup._inMapView = false; - sectorCullGroup._isFastForwarding = false; - sectorCullGroup.SetVisible(sectorCullGroup.ShouldBeVisible(), true, false); - }, - [typeof(Shape)] = (x) => (x as Shape).enabled = true, - [typeof(Renderer)] = (x) => (x as Renderer).enabled = true, - [typeof(Collider)] = (x) => (x as Collider).enabled = true, - [typeof(Animator)] = (x) => (x as Animator).enabled = true - }; - - // Have to be public to be copied by Instantiate - public Component[] componentsToFix; - - public void SetFixes(List fixes) - { - // Components must be in a list for unity to properly deep copy - componentsToFix = fixes.ToArray(); - } - - public void Start() - { - for (int i = 0; i < componentsToFix.Length; i++) - { - var component = componentsToFix[i]; - - try + // If it's not a moving anglerfish make sure the anim controller is regular + else if (component is AnglerfishAnimController angler && angler.GetComponentInParent() == null) { - if (component != null) + Logger.LogVerbose("Enabling anglerfish animation"); + // Remove any reference to its angler + if (angler._anglerfishController) { - var key = fixes.Keys.FirstOrDefault(x => x.IsInstanceOfType(component)); - var fix = fixes[key]; - fix(component); + angler._anglerfishController.OnChangeAnglerState -= angler.OnChangeAnglerState; + angler._anglerfishController.OnAnglerTurn -= angler.OnAnglerTurn; + angler._anglerfishController.OnAnglerSuspended -= angler.OnAnglerSuspended; + angler._anglerfishController.OnAnglerUnsuspended -= angler.OnAnglerUnsuspended; } - } - catch (Exception) - { - Logger.LogWarning($"Failed to fix component {component} on {gameObject.name}"); + angler.enabled = true; + angler.OnChangeAnglerState(AnglerfishController.AnglerState.Lurking); } } - } + catch (Exception e) + { + Logger.LogWarning($"Exception when modifying component [{component.GetType().Name}] on [{planetGO.name}] for prop [{prefab}]:\n{e}"); + } + }); } } } \ No newline at end of file From 8ec41b58d592c2fe90dd4de34b1c44c5a1d9aa58 Mon Sep 17 00:00:00 2001 From: JohnCorby Date: Wed, 14 Sep 2022 16:59:48 -0700 Subject: [PATCH 158/176] remove the delay (things still work lol) --- NewHorizons/Builder/Props/DetailBuilder.cs | 62 ++++++++++------------ 1 file changed, 27 insertions(+), 35 deletions(-) diff --git a/NewHorizons/Builder/Props/DetailBuilder.cs b/NewHorizons/Builder/Props/DetailBuilder.cs index ae7a63f7..209ca540 100644 --- a/NewHorizons/Builder/Props/DetailBuilder.cs +++ b/NewHorizons/Builder/Props/DetailBuilder.cs @@ -226,7 +226,7 @@ namespace NewHorizons.Builder.Props { // Fix other components // I forget why this is here - if (component is GhostIK || component is GhostEffects) + if (component is GhostIK or GhostEffects) { Component.DestroyImmediate(component); return; @@ -278,43 +278,35 @@ namespace NewHorizons.Builder.Props torchItem.mindSlideProjector._mindProjectorImageEffect = SearchUtilities.Find("Player_Body/PlayerCamera").GetComponent(); } - // Fix a bunch of stuff when done loading - Delay.RunWhen(() => Main.IsSystemReady, () => + if (component is Animator animator) animator.enabled = true; + if (component is Collider collider) collider.enabled = true; + if (component is Renderer renderer) renderer.enabled = true; + if (component is Shape shape) shape.enabled = true; + + // fixes sector cull group deactivating renderers on map view enter and fast foward + // TODO: does this actually worK? what? how? + if (component is SectorCullGroup sectorCullGroup) { - try + sectorCullGroup._inMapView = false; + sectorCullGroup._isFastForwarding = false; + sectorCullGroup.SetVisible(sectorCullGroup.ShouldBeVisible(), true, false); + } + + // If it's not a moving anglerfish make sure the anim controller is regular + if (component is AnglerfishAnimController angler2 && angler2.GetComponentInParent() == null) + { + Logger.LogVerbose("Enabling anglerfish animation"); + // Remove any reference to its angler + if (angler2._anglerfishController) { - if (component == null) return; - if (component is Animator animator) animator.enabled = true; - else if (component is Collider collider) collider.enabled = true; - else if (component is Renderer renderer) renderer.enabled = true; - else if (component is Shape shape) shape.enabled = true; - else if (component is SectorCullGroup sectorCullGroup) - { - sectorCullGroup._inMapView = false; - sectorCullGroup._isFastForwarding = false; - sectorCullGroup.SetVisible(sectorCullGroup.ShouldBeVisible(), true, false); - } - // If it's not a moving anglerfish make sure the anim controller is regular - else if (component is AnglerfishAnimController angler && angler.GetComponentInParent() == null) - { - Logger.LogVerbose("Enabling anglerfish animation"); - // 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); - } + angler2._anglerfishController.OnChangeAnglerState -= angler2.OnChangeAnglerState; + angler2._anglerfishController.OnAnglerTurn -= angler2.OnAnglerTurn; + angler2._anglerfishController.OnAnglerSuspended -= angler2.OnAnglerSuspended; + angler2._anglerfishController.OnAnglerUnsuspended -= angler2.OnAnglerUnsuspended; } - catch (Exception e) - { - Logger.LogWarning($"Exception when modifying component [{component.GetType().Name}] on [{planetGO.name}] for prop [{prefab}]:\n{e}"); - } - }); + angler2.enabled = true; + angler2.OnChangeAnglerState(AnglerfishController.AnglerState.Lurking); + } } } } \ No newline at end of file From 436cfa317520fce55e408472293bd982b83e3866 Mon Sep 17 00:00:00 2001 From: JohnCorby Date: Wed, 14 Sep 2022 17:04:53 -0700 Subject: [PATCH 159/176] remove unused parameter --- NewHorizons/Builder/Props/DetailBuilder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NewHorizons/Builder/Props/DetailBuilder.cs b/NewHorizons/Builder/Props/DetailBuilder.cs index 209ca540..0066a85d 100644 --- a/NewHorizons/Builder/Props/DetailBuilder.cs +++ b/NewHorizons/Builder/Props/DetailBuilder.cs @@ -79,7 +79,7 @@ namespace NewHorizons.Builder.Props } else FixSectoredComponent(component, sector, isTorch); - FixComponent(component, go, prefab.name); + FixComponent(component, go); } prop.transform.position = detail.position == null ? go.transform.position : go.transform.TransformPoint(detail.position); @@ -222,7 +222,7 @@ namespace NewHorizons.Builder.Props return false; } - private static void FixComponent(Component component, GameObject planetGO, string prefab) + private static void FixComponent(Component component, GameObject planetGO) { // Fix other components // I forget why this is here From cce97acddc326299881adfd4da8525e30125cbf1 Mon Sep 17 00:00:00 2001 From: JohnCorby Date: Wed, 14 Sep 2022 17:38:16 -0700 Subject: [PATCH 160/176] angler fixer yay --- NewHorizons/Builder/Props/DetailBuilder.cs | 36 ++++++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/NewHorizons/Builder/Props/DetailBuilder.cs b/NewHorizons/Builder/Props/DetailBuilder.cs index 0066a85d..016e5f61 100644 --- a/NewHorizons/Builder/Props/DetailBuilder.cs +++ b/NewHorizons/Builder/Props/DetailBuilder.cs @@ -284,7 +284,7 @@ namespace NewHorizons.Builder.Props if (component is Shape shape) shape.enabled = true; // fixes sector cull group deactivating renderers on map view enter and fast foward - // TODO: does this actually worK? what? how? + // TODO: does this actually work? what? how? if (component is SectorCullGroup sectorCullGroup) { sectorCullGroup._inMapView = false; @@ -293,19 +293,35 @@ namespace NewHorizons.Builder.Props } // If it's not a moving anglerfish make sure the anim controller is regular - if (component is AnglerfishAnimController angler2 && angler2.GetComponentInParent() == null) + if (component is AnglerfishAnimController && component.GetComponentInParent() == null) + component.gameObject.AddComponent(); + } + + /// + /// needs to happen later to remove the funny angler anim events yippee. + /// + /// cant do delay cuz it needs to work with scatter (which does copy detail thing). + /// + [RequireComponent(typeof(AnglerfishAnimController))] + private class AnglerAnimFixer : MonoBehaviour + { + private void Start() { - Logger.LogVerbose("Enabling anglerfish animation"); + var angler = GetComponent(); + + Logger.LogVerbose("Fixing anglerfish animation"); // Remove any reference to its angler - if (angler2._anglerfishController) + if (angler._anglerfishController) { - angler2._anglerfishController.OnChangeAnglerState -= angler2.OnChangeAnglerState; - angler2._anglerfishController.OnAnglerTurn -= angler2.OnAnglerTurn; - angler2._anglerfishController.OnAnglerSuspended -= angler2.OnAnglerSuspended; - angler2._anglerfishController.OnAnglerUnsuspended -= angler2.OnAnglerUnsuspended; + angler._anglerfishController.OnChangeAnglerState -= angler.OnChangeAnglerState; + angler._anglerfishController.OnAnglerTurn -= angler.OnAnglerTurn; + angler._anglerfishController.OnAnglerSuspended -= angler.OnAnglerSuspended; + angler._anglerfishController.OnAnglerUnsuspended -= angler.OnAnglerUnsuspended; } - angler2.enabled = true; - angler2.OnChangeAnglerState(AnglerfishController.AnglerState.Lurking); + angler.enabled = true; + angler.OnChangeAnglerState(AnglerfishController.AnglerState.Lurking); + + Destroy(this); } } } From 2f49b61dd20ca840e7e8c09258ac85d8e9a7ffe6 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 14 Sep 2022 20:48:55 -0400 Subject: [PATCH 161/176] Remove stupid usings wtf are these --- NewHorizons/OtherMods/MenuFramework/MenuHandler.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/NewHorizons/OtherMods/MenuFramework/MenuHandler.cs b/NewHorizons/OtherMods/MenuFramework/MenuHandler.cs index 45682279..bb88e7b4 100644 --- a/NewHorizons/OtherMods/MenuFramework/MenuHandler.cs +++ b/NewHorizons/OtherMods/MenuFramework/MenuHandler.cs @@ -4,9 +4,7 @@ using NewHorizons.Utility; using OWML.Common; using System.Collections.Generic; using System.Linq; -using System.Net.Mail; using UnityEngine; -using static UnityEngine.InputSystem.InputRemoting; using Logger = NewHorizons.Utility.Logger; namespace NewHorizons.OtherMods.MenuFramework From d71b016c8a9d4726f2cf38b4d797b9bffb774252 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 14 Sep 2022 20:51:07 -0400 Subject: [PATCH 162/176] Explained why the fixer is needed --- NewHorizons/Builder/Props/DetailBuilder.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/NewHorizons/Builder/Props/DetailBuilder.cs b/NewHorizons/Builder/Props/DetailBuilder.cs index 016e5f61..660956b6 100644 --- a/NewHorizons/Builder/Props/DetailBuilder.cs +++ b/NewHorizons/Builder/Props/DetailBuilder.cs @@ -298,9 +298,9 @@ namespace NewHorizons.Builder.Props } /// - /// needs to happen later to remove the funny angler anim events yippee. - /// - /// cant do delay cuz it needs to work with scatter (which does copy detail thing). + /// Has to happen after AnglerfishAnimController awake to remove the events it has set up. + /// Otherwise results in the anglerfish 1) having its animations controlled by an actual fish 2) randomly having different animations on solarsystem load + /// Can't do delay because it needs to work with scatter (copies a prefab made using MakeDetail). /// [RequireComponent(typeof(AnglerfishAnimController))] private class AnglerAnimFixer : MonoBehaviour @@ -310,7 +310,8 @@ namespace NewHorizons.Builder.Props var angler = GetComponent(); Logger.LogVerbose("Fixing anglerfish animation"); - // Remove any reference to its angler + + // Remove any event reference to its angler if (angler._anglerfishController) { angler._anglerfishController.OnChangeAnglerState -= angler.OnChangeAnglerState; From 27386e1730cb0bc20b4fd19142f01b8acbfa0143 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 14 Sep 2022 21:08:28 -0400 Subject: [PATCH 163/176] Fix reload configs breaking NH translations --- NewHorizons/Main.cs | 13 ++++++++----- NewHorizons/Utility/DebugUtilities/DebugReload.cs | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index b86c8d65..678852bb 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -121,7 +121,7 @@ namespace NewHorizons _wasConfigured = true; } - public static void ResetConfigs(bool resetTranslation = true) + public void ResetConfigs(bool resetTranslation = true) { BodyDict.Clear(); SystemDict.Clear(); @@ -160,9 +160,13 @@ namespace NewHorizons } }; - if (!resetTranslation) return; - TranslationHandler.ClearTables(); - TextTranslation.Get().SetLanguage(TextTranslation.Get().GetLanguage()); + if (resetTranslation) + { + TranslationHandler.ClearTables(); + TextTranslation.Get().SetLanguage(TextTranslation.Get().GetLanguage()); + } + + LoadTranslations(Instance.ModHelper.Manifest.ModFolderPath + "Assets/", this); } public void Awake() @@ -212,7 +216,6 @@ namespace NewHorizons OnStarSystemLoaded.AddListener(RichPresenceHandler.OnStarSystemLoaded); OnChangeStarSystem.AddListener(RichPresenceHandler.OnChangeStarSystem); - LoadTranslations(ModHelper.Manifest.ModFolderPath + "Assets/", this); LoadAddonManifest("Assets/addon-manifest.json", this); } diff --git a/NewHorizons/Utility/DebugUtilities/DebugReload.cs b/NewHorizons/Utility/DebugUtilities/DebugReload.cs index b8a513c9..1bb6e639 100644 --- a/NewHorizons/Utility/DebugUtilities/DebugReload.cs +++ b/NewHorizons/Utility/DebugUtilities/DebugReload.cs @@ -31,7 +31,7 @@ namespace NewHorizons.Utility.DebugUtilities { Logger.Log("Begin reload of config files..."); - Main.ResetConfigs(); + Main.Instance.ResetConfigs(); try { From 235f62ffc7aa08bb78c79911a53af238e4c3dd42 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Wed, 14 Sep 2022 21:33:41 -0400 Subject: [PATCH 164/176] null check ig idk why a required mod wouldn't be downloaded --- .../CommonCameraUtility/CommonCameraHandler.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/NewHorizons/OtherMods/CommonCameraUtility/CommonCameraHandler.cs b/NewHorizons/OtherMods/CommonCameraUtility/CommonCameraHandler.cs index 2aab1526..e344a57f 100644 --- a/NewHorizons/OtherMods/CommonCameraUtility/CommonCameraHandler.cs +++ b/NewHorizons/OtherMods/CommonCameraUtility/CommonCameraHandler.cs @@ -1,4 +1,5 @@ using NewHorizons.OtherMods.MenuFramework; +using Logger = NewHorizons.Utility.Logger; namespace NewHorizons.OtherMods.CommonCameraUtility { @@ -11,6 +12,16 @@ namespace NewHorizons.OtherMods.CommonCameraUtility _cameraAPI = Main.Instance.ModHelper.Interaction.TryGetModApi("xen.CommonCameraUtility"); } - public static void RegisterCustomCamera(OWCamera camera) => _cameraAPI.RegisterCustomCamera(camera); + public static void RegisterCustomCamera(OWCamera camera) + { + if (_cameraAPI != null) + { + _cameraAPI.RegisterCustomCamera(camera); + } + else + { + Logger.LogError("Tried to register custom camera but Common Camera Utility was missing."); + } + } } } From 13dfe1c535047e374ad5551032bddb5d702c3319 Mon Sep 17 00:00:00 2001 From: TerrificTrifid <99054745+TerrificTrifid@users.noreply.github.com> Date: Wed, 14 Sep 2022 23:10:05 -0500 Subject: [PATCH 165/176] Fix RemoveStarLight bugs --- .../Components/SizeControllers/StarEvolutionController.cs | 2 +- NewHorizons/Handlers/PlanetDestructionHandler.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/NewHorizons/Components/SizeControllers/StarEvolutionController.cs b/NewHorizons/Components/SizeControllers/StarEvolutionController.cs index 164c2a20..ced3222a 100644 --- a/NewHorizons/Components/SizeControllers/StarEvolutionController.cs +++ b/NewHorizons/Components/SizeControllers/StarEvolutionController.cs @@ -302,7 +302,7 @@ namespace NewHorizons.Components.SizeControllers private void DisableStar(bool start = false) { if (controller != null) StarLightController.RemoveStar(controller); - StarLightController.RemoveStarLight(gameObject.FindChild("SunLight").GetAddComponent()); + if (!isProxy) StarLightController.RemoveStarLight(gameObject.FindChild("SunLight").GetAddComponent()); if (_stellarRemnant != null) { diff --git a/NewHorizons/Handlers/PlanetDestructionHandler.cs b/NewHorizons/Handlers/PlanetDestructionHandler.cs index b92e7972..7f8fbf28 100644 --- a/NewHorizons/Handlers/PlanetDestructionHandler.cs +++ b/NewHorizons/Handlers/PlanetDestructionHandler.cs @@ -118,7 +118,7 @@ namespace NewHorizons.Handlers case AstroObject.Name.Sun: var starController = ao.gameObject.GetComponent(); StarLightController.RemoveStar(starController); - StarLightController.RemoveStarLight(ao.gameObject.FindChild("SunLight").GetComponent()); + StarLightController.RemoveStarLight(ao.gameObject.FindChild("Sector_SUN/Effects_SUN/SunLight").GetComponent()); GameObject.Destroy(starController); var audio = ao.GetComponentInChildren(); From 6dfe59e962602e7c096e190a70e4f7b0312822fc Mon Sep 17 00:00:00 2001 From: Will Corby Date: Wed, 14 Sep 2022 21:15:55 -0700 Subject: [PATCH 166/176] Revert "Disable starlights when out of range" --- NewHorizons/Builder/Body/StarBuilder.cs | 1 + .../StarEvolutionController.cs | 3 -- NewHorizons/Components/StarLightController.cs | 33 ++----------------- NewHorizons/Handlers/PlanetCreationHandler.cs | 4 --- .../Handlers/PlanetDestructionHandler.cs | 1 - 5 files changed, 3 insertions(+), 39 deletions(-) diff --git a/NewHorizons/Builder/Body/StarBuilder.cs b/NewHorizons/Builder/Body/StarBuilder.cs index 99a6ee9f..7e32c3d3 100644 --- a/NewHorizons/Builder/Body/StarBuilder.cs +++ b/NewHorizons/Builder/Body/StarBuilder.cs @@ -126,6 +126,7 @@ namespace NewHorizons.Builder.Body light.CopyPropertiesFrom(SearchUtilities.Find("Sun_Body/Sector_SUN/Effects_SUN/SunLight").GetComponent()); light.intensity *= starModule.solarLuminosity; light.range = starModule.lightRadius; + light.range *= Mathf.Sqrt(starModule.solarLuminosity); Color lightColour = light.color; if (starModule.lightTint != null) lightColour = starModule.lightTint.ToColor(); diff --git a/NewHorizons/Components/SizeControllers/StarEvolutionController.cs b/NewHorizons/Components/SizeControllers/StarEvolutionController.cs index ced3222a..ce904cca 100644 --- a/NewHorizons/Components/SizeControllers/StarEvolutionController.cs +++ b/NewHorizons/Components/SizeControllers/StarEvolutionController.cs @@ -287,8 +287,6 @@ namespace NewHorizons.Components.SizeControllers _stellarRemnant.SetActive(true); var remnantStarController = _stellarRemnant.GetComponentInChildren(); if (remnantStarController != null) StarLightController.AddStar(remnantStarController); - var remnantStarLight = _stellarRemnant.FindChild("SunLight"); - if (remnantStarLight != null) StarLightController.AddStarLight(remnantStarLight.GetComponent()); } if (Time.time > _supernovaStartTime + supernovaTime) @@ -302,7 +300,6 @@ namespace NewHorizons.Components.SizeControllers private void DisableStar(bool start = false) { if (controller != null) StarLightController.RemoveStar(controller); - if (!isProxy) StarLightController.RemoveStarLight(gameObject.FindChild("SunLight").GetAddComponent()); if (_stellarRemnant != null) { diff --git a/NewHorizons/Components/StarLightController.cs b/NewHorizons/Components/StarLightController.cs index a9e2b000..55a79074 100644 --- a/NewHorizons/Components/StarLightController.cs +++ b/NewHorizons/Components/StarLightController.cs @@ -14,7 +14,6 @@ namespace NewHorizons.Components public static StarLightController Instance { get; private set; } private List _stars = new List(); - private List _lights = new List(); private StarController _activeStar; private SunLightController _sunLightController; @@ -54,20 +53,6 @@ namespace NewHorizons.Components } } - public static void AddStarLight(Light light) - { - if (light == null) return; - - Instance._lights.Add(light); - } - - public static void RemoveStarLight(Light light) - { - if (light == null) return; - - if (Instance._lights.Contains(light)) Instance._lights.Remove(light); - } - public void Update() { if (_activeStar == null || !_activeStar.gameObject.activeInHierarchy) @@ -93,14 +78,13 @@ namespace NewHorizons.Components material.SetFloat(SunIntensity, intensity); } - // Player is always at 0,0,0 more or less so if they arent using the map camera then wtv - var origin = Vector3.zero; - foreach (var star in _stars) { if (star == null) continue; if (!(star.gameObject.activeSelf && star.gameObject.activeInHierarchy)) continue; + // Player is always at 0,0,0 more or less so if they arent using the map camera then wtv + var origin = Vector3.zero; if (PlayerState.InMapView()) { origin = Locator.GetActiveCamera().transform.position; @@ -112,19 +96,6 @@ namespace NewHorizons.Components break; } } - - if (PlayerState.InMapView()) - { - foreach (var light in _lights) light.enabled = true; - return; - } - - foreach (var light in _lights) - { - // Minimum 50km range so it's not badly noticeable for dim stars - if ((light.transform.position - origin).sqrMagnitude <= Mathf.Max(light.range * light.range, 2500000000)) light.enabled = true; - else light.enabled = false; - } } private void ChangeActiveStar(StarController star) diff --git a/NewHorizons/Handlers/PlanetCreationHandler.cs b/NewHorizons/Handlers/PlanetCreationHandler.cs index 3c7c2d1d..4e4bab9b 100644 --- a/NewHorizons/Handlers/PlanetCreationHandler.cs +++ b/NewHorizons/Handlers/PlanetCreationHandler.cs @@ -59,7 +59,6 @@ namespace NewHorizons.Handlers starLightGO.AddComponent(); StarLightController.AddStar(starController); - StarLightController.AddStarLight(starController.Light); starLightGO.SetActive(true); @@ -501,9 +500,6 @@ namespace NewHorizons.Handlers if (starController != null) StarLightController.AddStar(starController); - var starLight = star.FindChild("SunLight"); - if (starLight != null) StarLightController.AddStarLight(starLight.GetComponent()); - // If it has an evolution controller that means it will die -> we make a remnant (unless its a remnant) if (starEvolutionController != null && !body.Config.isStellarRemnant) { diff --git a/NewHorizons/Handlers/PlanetDestructionHandler.cs b/NewHorizons/Handlers/PlanetDestructionHandler.cs index 7f8fbf28..4d60a271 100644 --- a/NewHorizons/Handlers/PlanetDestructionHandler.cs +++ b/NewHorizons/Handlers/PlanetDestructionHandler.cs @@ -118,7 +118,6 @@ namespace NewHorizons.Handlers case AstroObject.Name.Sun: var starController = ao.gameObject.GetComponent(); StarLightController.RemoveStar(starController); - StarLightController.RemoveStarLight(ao.gameObject.FindChild("Sector_SUN/Effects_SUN/SunLight").GetComponent()); GameObject.Destroy(starController); var audio = ao.GetComponentInChildren(); From 6d2dbf72d5c7e32ed7a2bee168bfa9db11485b94 Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 15 Sep 2022 00:25:07 -0400 Subject: [PATCH 167/176] Revert "Revert "Disable starlights when out of range" (#385)" This reverts commit a1b1bd9a4052a0ad212cf3570d79c6d37e197d08, reversing changes made to acb235d4788ed6d993a6b23f918e2ae94cce0493. --- NewHorizons/Builder/Body/StarBuilder.cs | 1 - .../StarEvolutionController.cs | 3 ++ NewHorizons/Components/StarLightController.cs | 33 +++++++++++++++++-- NewHorizons/Handlers/PlanetCreationHandler.cs | 4 +++ .../Handlers/PlanetDestructionHandler.cs | 1 + 5 files changed, 39 insertions(+), 3 deletions(-) diff --git a/NewHorizons/Builder/Body/StarBuilder.cs b/NewHorizons/Builder/Body/StarBuilder.cs index 7e32c3d3..99a6ee9f 100644 --- a/NewHorizons/Builder/Body/StarBuilder.cs +++ b/NewHorizons/Builder/Body/StarBuilder.cs @@ -126,7 +126,6 @@ namespace NewHorizons.Builder.Body light.CopyPropertiesFrom(SearchUtilities.Find("Sun_Body/Sector_SUN/Effects_SUN/SunLight").GetComponent()); light.intensity *= starModule.solarLuminosity; light.range = starModule.lightRadius; - light.range *= Mathf.Sqrt(starModule.solarLuminosity); Color lightColour = light.color; if (starModule.lightTint != null) lightColour = starModule.lightTint.ToColor(); diff --git a/NewHorizons/Components/SizeControllers/StarEvolutionController.cs b/NewHorizons/Components/SizeControllers/StarEvolutionController.cs index ce904cca..ced3222a 100644 --- a/NewHorizons/Components/SizeControllers/StarEvolutionController.cs +++ b/NewHorizons/Components/SizeControllers/StarEvolutionController.cs @@ -287,6 +287,8 @@ namespace NewHorizons.Components.SizeControllers _stellarRemnant.SetActive(true); var remnantStarController = _stellarRemnant.GetComponentInChildren(); if (remnantStarController != null) StarLightController.AddStar(remnantStarController); + var remnantStarLight = _stellarRemnant.FindChild("SunLight"); + if (remnantStarLight != null) StarLightController.AddStarLight(remnantStarLight.GetComponent()); } if (Time.time > _supernovaStartTime + supernovaTime) @@ -300,6 +302,7 @@ namespace NewHorizons.Components.SizeControllers private void DisableStar(bool start = false) { if (controller != null) StarLightController.RemoveStar(controller); + if (!isProxy) StarLightController.RemoveStarLight(gameObject.FindChild("SunLight").GetAddComponent()); if (_stellarRemnant != null) { diff --git a/NewHorizons/Components/StarLightController.cs b/NewHorizons/Components/StarLightController.cs index 55a79074..a9e2b000 100644 --- a/NewHorizons/Components/StarLightController.cs +++ b/NewHorizons/Components/StarLightController.cs @@ -14,6 +14,7 @@ namespace NewHorizons.Components public static StarLightController Instance { get; private set; } private List _stars = new List(); + private List _lights = new List(); private StarController _activeStar; private SunLightController _sunLightController; @@ -53,6 +54,20 @@ namespace NewHorizons.Components } } + public static void AddStarLight(Light light) + { + if (light == null) return; + + Instance._lights.Add(light); + } + + public static void RemoveStarLight(Light light) + { + if (light == null) return; + + if (Instance._lights.Contains(light)) Instance._lights.Remove(light); + } + public void Update() { if (_activeStar == null || !_activeStar.gameObject.activeInHierarchy) @@ -78,13 +93,14 @@ namespace NewHorizons.Components material.SetFloat(SunIntensity, intensity); } + // Player is always at 0,0,0 more or less so if they arent using the map camera then wtv + var origin = Vector3.zero; + foreach (var star in _stars) { if (star == null) continue; if (!(star.gameObject.activeSelf && star.gameObject.activeInHierarchy)) continue; - // Player is always at 0,0,0 more or less so if they arent using the map camera then wtv - var origin = Vector3.zero; if (PlayerState.InMapView()) { origin = Locator.GetActiveCamera().transform.position; @@ -96,6 +112,19 @@ namespace NewHorizons.Components break; } } + + if (PlayerState.InMapView()) + { + foreach (var light in _lights) light.enabled = true; + return; + } + + foreach (var light in _lights) + { + // Minimum 50km range so it's not badly noticeable for dim stars + if ((light.transform.position - origin).sqrMagnitude <= Mathf.Max(light.range * light.range, 2500000000)) light.enabled = true; + else light.enabled = false; + } } private void ChangeActiveStar(StarController star) diff --git a/NewHorizons/Handlers/PlanetCreationHandler.cs b/NewHorizons/Handlers/PlanetCreationHandler.cs index 4e4bab9b..3c7c2d1d 100644 --- a/NewHorizons/Handlers/PlanetCreationHandler.cs +++ b/NewHorizons/Handlers/PlanetCreationHandler.cs @@ -59,6 +59,7 @@ namespace NewHorizons.Handlers starLightGO.AddComponent(); StarLightController.AddStar(starController); + StarLightController.AddStarLight(starController.Light); starLightGO.SetActive(true); @@ -500,6 +501,9 @@ namespace NewHorizons.Handlers if (starController != null) StarLightController.AddStar(starController); + var starLight = star.FindChild("SunLight"); + if (starLight != null) StarLightController.AddStarLight(starLight.GetComponent()); + // If it has an evolution controller that means it will die -> we make a remnant (unless its a remnant) if (starEvolutionController != null && !body.Config.isStellarRemnant) { diff --git a/NewHorizons/Handlers/PlanetDestructionHandler.cs b/NewHorizons/Handlers/PlanetDestructionHandler.cs index 4d60a271..7f8fbf28 100644 --- a/NewHorizons/Handlers/PlanetDestructionHandler.cs +++ b/NewHorizons/Handlers/PlanetDestructionHandler.cs @@ -118,6 +118,7 @@ namespace NewHorizons.Handlers case AstroObject.Name.Sun: var starController = ao.gameObject.GetComponent(); StarLightController.RemoveStar(starController); + StarLightController.RemoveStarLight(ao.gameObject.FindChild("Sector_SUN/Effects_SUN/SunLight").GetComponent()); GameObject.Destroy(starController); var audio = ao.GetComponentInChildren(); From 5a5ea46b05755a6debc7a7386c5b6459a7d0b082 Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 15 Sep 2022 00:41:05 -0400 Subject: [PATCH 168/176] Categorized some stuff in components and fixed Trifids star thing maybe --- NewHorizons/Builder/Body/RingBuilder.cs | 1 + .../Builder/Body/SingularityBuilder.cs | 2 +- NewHorizons/Builder/Body/StarBuilder.cs | 2 +- NewHorizons/Builder/Body/WaterBuilder.cs | 2 +- NewHorizons/Builder/Props/QuantumBuilder.cs | 2 +- NewHorizons/Builder/Props/RaftBuilder.cs | 2 +- NewHorizons/Builder/ShipLog/MapModeBuilder.cs | 3 +- .../Volumes/NotificationVolumeBuilder.cs | 3 +- .../Builder/Volumes/VolumesBuildManager.cs | 4 +- .../{ => Fixers}/MapSatelliteOrbitFix.cs | 2 +- .../NHMultiStateQuantumObject.cs | 168 +++++++++--------- .../Components/{ => Quantum}/QuantumPlanet.cs | 6 +- .../Components/RingOpacityController.cs | 1 + .../Components/{ => ShipLog}/ShipLogDetail.cs | 6 +- .../{ => ShipLog}/ShipLogStarChartMode.cs | 16 +- .../StarEvolutionController.cs | 9 +- .../Components/{ => Stars}/StarController.cs | 4 +- .../{ => Stars}/StarDestructionVolume.cs | 2 +- .../Components/{ => Stars}/StarFluidVolume.cs | 2 +- .../{ => Stars}/StarLightController.cs | 131 ++++++++------ .../{ => Stars}/StarSurfaceAudioController.cs | 4 +- .../{ => Stars}/StellarDeathController.cs | 4 +- .../Components/{ => Volumes}/BaseVolume.cs | 2 +- .../BlackHoleDestructionVolume.cs | 4 +- .../{ => Volumes}/ChangeStarSystemVolume.cs | 2 +- .../{ => Volumes}/InterferenceVolume.cs | 2 +- .../{ => Volumes}/MapRestrictionVolume.cs | 2 +- .../Components/{ => Volumes}/NHFluidVolume.cs | 4 +- .../{ => Volumes}/NHInnerFogWarpVolume.cs | 2 +- .../{ => Volumes}/NotificationVolume.cs | 4 +- .../{ => Volumes}/RingFluidVolume.cs | 4 +- NewHorizons/Handlers/InterferenceHandler.cs | 2 +- NewHorizons/Handlers/PlanetCreationHandler.cs | 13 +- .../Handlers/PlanetDestructionHandler.cs | 6 +- NewHorizons/Handlers/StarChartHandler.cs | 2 +- .../Handlers/VesselCoordinatePromptHandler.cs | 2 +- NewHorizons/Main.cs | 1 + .../OWRichPresence/RichPresenceHandler.cs | 2 +- .../Patches/DestructionVolumePatches.cs | 2 +- NewHorizons/Patches/RaftPatches.cs | 2 +- NewHorizons/Patches/ShipLogPatches.cs | 3 +- 41 files changed, 233 insertions(+), 204 deletions(-) rename NewHorizons/Components/{ => Fixers}/MapSatelliteOrbitFix.cs (94%) rename NewHorizons/Components/{ => Quantum}/NHMultiStateQuantumObject.cs (54%) rename NewHorizons/Components/{ => Quantum}/QuantumPlanet.cs (98%) rename NewHorizons/Components/{ => ShipLog}/ShipLogDetail.cs (92%) rename NewHorizons/Components/{ => ShipLog}/ShipLogStarChartMode.cs (95%) rename NewHorizons/Components/{ => Stars}/StarController.cs (94%) rename NewHorizons/Components/{ => Stars}/StarDestructionVolume.cs (98%) rename NewHorizons/Components/{ => Stars}/StarFluidVolume.cs (96%) rename NewHorizons/Components/{ => Stars}/StarLightController.cs (55%) rename NewHorizons/Components/{ => Stars}/StarSurfaceAudioController.cs (88%) rename NewHorizons/Components/{ => Stars}/StellarDeathController.cs (96%) rename NewHorizons/Components/{ => Volumes}/BaseVolume.cs (95%) rename NewHorizons/Components/{ => Volumes}/BlackHoleDestructionVolume.cs (84%) rename NewHorizons/Components/{ => Volumes}/ChangeStarSystemVolume.cs (94%) rename NewHorizons/Components/{ => Volumes}/InterferenceVolume.cs (97%) rename NewHorizons/Components/{ => Volumes}/MapRestrictionVolume.cs (94%) rename NewHorizons/Components/{ => Volumes}/NHFluidVolume.cs (86%) rename NewHorizons/Components/{ => Volumes}/NHInnerFogWarpVolume.cs (83%) rename NewHorizons/Components/{ => Volumes}/NotificationVolume.cs (97%) rename NewHorizons/Components/{ => Volumes}/RingFluidVolume.cs (95%) diff --git a/NewHorizons/Builder/Body/RingBuilder.cs b/NewHorizons/Builder/Body/RingBuilder.cs index a0918a56..40d5953f 100644 --- a/NewHorizons/Builder/Body/RingBuilder.cs +++ b/NewHorizons/Builder/Body/RingBuilder.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using NewHorizons.External.Modules; using UnityEngine; using Logger = NewHorizons.Utility.Logger; +using NewHorizons.Components.Volumes; namespace NewHorizons.Builder.Body { diff --git a/NewHorizons/Builder/Body/SingularityBuilder.cs b/NewHorizons/Builder/Body/SingularityBuilder.cs index 4b9d4cdf..c9cd4d4d 100644 --- a/NewHorizons/Builder/Body/SingularityBuilder.cs +++ b/NewHorizons/Builder/Body/SingularityBuilder.cs @@ -1,4 +1,3 @@ -using NewHorizons.Components; using NewHorizons.External.Configs; using NewHorizons.Utility; using System; @@ -10,6 +9,7 @@ using System.Linq; using NewHorizons.Components.SizeControllers; using System.Drawing; using Color = UnityEngine.Color; +using NewHorizons.Components.Volumes; namespace NewHorizons.Builder.Body { diff --git a/NewHorizons/Builder/Body/StarBuilder.cs b/NewHorizons/Builder/Body/StarBuilder.cs index 99a6ee9f..eb9cac25 100644 --- a/NewHorizons/Builder/Body/StarBuilder.cs +++ b/NewHorizons/Builder/Body/StarBuilder.cs @@ -1,4 +1,3 @@ -using NewHorizons.Components; using NewHorizons.Components.SizeControllers; using NewHorizons.Utility; using OWML.Utils; @@ -9,6 +8,7 @@ using OWML.ModHelper; using OWML.Common; using UnityEngine.InputSystem.XR; using System.Linq; +using NewHorizons.Components.Stars; namespace NewHorizons.Builder.Body { diff --git a/NewHorizons/Builder/Body/WaterBuilder.cs b/NewHorizons/Builder/Body/WaterBuilder.cs index 5cbc5d70..a05dd63e 100644 --- a/NewHorizons/Builder/Body/WaterBuilder.cs +++ b/NewHorizons/Builder/Body/WaterBuilder.cs @@ -1,9 +1,9 @@ -using NewHorizons.Components; using NewHorizons.Components.SizeControllers; using NewHorizons.Utility; using UnityEngine; using NewHorizons.External.Modules.VariableSize; using Tessellation; +using NewHorizons.Components.Volumes; namespace NewHorizons.Builder.Body { diff --git a/NewHorizons/Builder/Props/QuantumBuilder.cs b/NewHorizons/Builder/Props/QuantumBuilder.cs index 16484b8f..3dd81c3b 100644 --- a/NewHorizons/Builder/Props/QuantumBuilder.cs +++ b/NewHorizons/Builder/Props/QuantumBuilder.cs @@ -1,5 +1,5 @@ using HarmonyLib; -using NewHorizons.Components; +using NewHorizons.Components.Quantum; using NewHorizons.External.Configs; using NewHorizons.External.Modules; using NewHorizons.Utility; diff --git a/NewHorizons/Builder/Props/RaftBuilder.cs b/NewHorizons/Builder/Props/RaftBuilder.cs index 6ad6277f..2e8c6a15 100644 --- a/NewHorizons/Builder/Props/RaftBuilder.cs +++ b/NewHorizons/Builder/Props/RaftBuilder.cs @@ -1,4 +1,4 @@ -using NewHorizons.Components; +using NewHorizons.Components.Volumes; using NewHorizons.External.Modules; using NewHorizons.Handlers; using NewHorizons.Utility; diff --git a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs index 12e55581..0eaf91aa 100644 --- a/NewHorizons/Builder/ShipLog/MapModeBuilder.cs +++ b/NewHorizons/Builder/ShipLog/MapModeBuilder.cs @@ -1,4 +1,3 @@ -using NewHorizons.Components; using NewHorizons.External.Modules; using NewHorizons.Handlers; using NewHorizons.Utility; @@ -9,6 +8,8 @@ using NewHorizons.External.Modules.VariableSize; using UnityEngine; using UnityEngine.UI; using Logger = NewHorizons.Utility.Logger; +using NewHorizons.Components.ShipLog; + namespace NewHorizons.Builder.ShipLog { public static class MapModeBuilder diff --git a/NewHorizons/Builder/Volumes/NotificationVolumeBuilder.cs b/NewHorizons/Builder/Volumes/NotificationVolumeBuilder.cs index 13386e79..dab95143 100644 --- a/NewHorizons/Builder/Volumes/NotificationVolumeBuilder.cs +++ b/NewHorizons/Builder/Volumes/NotificationVolumeBuilder.cs @@ -1,4 +1,3 @@ -using NewHorizons.Components; using NewHorizons.External.Modules; using NewHorizons.Utility; using OWML.Common; @@ -9,7 +8,7 @@ using System.Text; using System.Threading.Tasks; using UnityEngine; using Logger = NewHorizons.Utility.Logger; -using NHNotificationVolume = NewHorizons.Components.NotificationVolume; +using NHNotificationVolume = NewHorizons.Components.Volumes.NotificationVolume; namespace NewHorizons.Builder.Volumes { diff --git a/NewHorizons/Builder/Volumes/VolumesBuildManager.cs b/NewHorizons/Builder/Volumes/VolumesBuildManager.cs index 9e9a9c36..9b13db4f 100644 --- a/NewHorizons/Builder/Volumes/VolumesBuildManager.cs +++ b/NewHorizons/Builder/Volumes/VolumesBuildManager.cs @@ -1,7 +1,7 @@ using NewHorizons.Builder.Body; using NewHorizons.Builder.ShipLog; using NewHorizons.Builder.Volumes; -using NewHorizons.Components; +using NewHorizons.Components.Volumes; using NewHorizons.External.Configs; using OWML.Common; using System; @@ -61,7 +61,7 @@ namespace NewHorizons.Builder.Volumes { foreach (var interferenceVolume in config.Volumes.interferenceVolumes) { - VolumeBuilder.Make(go, sector, interferenceVolume); + VolumeBuilder.Make(go, sector, interferenceVolume); } } if (config.Volumes.reverbVolumes != null) diff --git a/NewHorizons/Components/MapSatelliteOrbitFix.cs b/NewHorizons/Components/Fixers/MapSatelliteOrbitFix.cs similarity index 94% rename from NewHorizons/Components/MapSatelliteOrbitFix.cs rename to NewHorizons/Components/Fixers/MapSatelliteOrbitFix.cs index 1e8e0778..4b1a9b30 100644 --- a/NewHorizons/Components/MapSatelliteOrbitFix.cs +++ b/NewHorizons/Components/Fixers/MapSatelliteOrbitFix.cs @@ -1,7 +1,7 @@ using NewHorizons.Builder.General; using NewHorizons.External.Configs; using UnityEngine; -namespace NewHorizons.Components +namespace NewHorizons.Components.Fixers { public class MapSatelliteOrbitFix : MonoBehaviour { diff --git a/NewHorizons/Components/NHMultiStateQuantumObject.cs b/NewHorizons/Components/Quantum/NHMultiStateQuantumObject.cs similarity index 54% rename from NewHorizons/Components/NHMultiStateQuantumObject.cs rename to NewHorizons/Components/Quantum/NHMultiStateQuantumObject.cs index 6c73cef5..4abd57b1 100644 --- a/NewHorizons/Components/NHMultiStateQuantumObject.cs +++ b/NewHorizons/Components/Quantum/NHMultiStateQuantumObject.cs @@ -7,60 +7,60 @@ using UnityEngine; using Logger = NewHorizons.Utility.Logger; -namespace NewHorizons.Components +namespace NewHorizons.Components.Quantum { public class NHMultiStateQuantumObject : MultiStateQuantumObject { - - public override bool ChangeQuantumState(bool skipInstantVisibilityCheck) - { - for (int i = 0; i < _prerequisiteObjects.Length; i++) - { - if (!_prerequisiteObjects[i].HasCollapsed()) - { - return false; - } - } - int stateIndex = _stateIndex; - if (_stateIndex == -1 && _initialState != -1) - { - _stateIndex = _initialState; - } - else if (_sequential) - { - _stateIndex = (_reverse ? (_stateIndex - 1) : (_stateIndex + 1)); - if (_loop) - { - if (_stateIndex < 0) - { - _stateIndex = _states.Length - 1; - } - else if (_stateIndex > _states.Length - 1) - { - _stateIndex = 0; - } - } - else - { - _stateIndex = Mathf.Clamp(_stateIndex, 0, _states.Length - 1); - } - } - else - { + + public override bool ChangeQuantumState(bool skipInstantVisibilityCheck) + { + for (int i = 0; i < _prerequisiteObjects.Length; i++) + { + if (!_prerequisiteObjects[i].HasCollapsed()) + { + return false; + } + } + int stateIndex = _stateIndex; + if (_stateIndex == -1 && _initialState != -1) + { + _stateIndex = _initialState; + } + else if (_sequential) + { + _stateIndex = _reverse ? _stateIndex - 1 : _stateIndex + 1; + if (_loop) + { + if (_stateIndex < 0) + { + _stateIndex = _states.Length - 1; + } + else if (_stateIndex > _states.Length - 1) + { + _stateIndex = 0; + } + } + else + { + _stateIndex = Mathf.Clamp(_stateIndex, 0, _states.Length - 1); + } + } + else + { // TODO: perform this roll for number of states, each time adding the selected state to the end of a list and removing it from the source list // this gets us a randomly ordered list that respects states' probability // then we can sequentially attempt collapsing to them, checking at each state whether the new state is invalid due to the player being able to see it, according to this: // // if (!((!IsPlayerEntangled()) ? (CheckIllumination() ? CheckVisibilityInstantly() : CheckPointInside(Locator.GetPlayerCamera().transform.position)) : CheckIllumination())) - // { - // return true; // this is a valid state - // } + // { + // return true; // this is a valid state + // } // List indices = new List(); for (var i = 0; i < _states.Length; i++) if (i != stateIndex) indices.Add(i); - + var previousIndex = stateIndex; do @@ -69,31 +69,31 @@ namespace NewHorizons.Components _stateIndex = RollState(stateIndex, indices); if (previousIndex >= 0 && previousIndex < _states.Length) _states[previousIndex].SetVisible(visible: false); _states[_stateIndex].SetVisible(visible: true); - + Logger.LogVerbose($"MultiStateQuantumObject - Trying to change state {_stateIndex}"); indices.Remove(_stateIndex); } while (!CurrentStateIsValid() && indices.Count > 0); - } + } var stateIndexIsValid = stateIndex >= 0 && stateIndex < _states.Length; - if (stateIndexIsValid) _states[stateIndex].SetVisible(visible: false); + if (stateIndexIsValid) _states[stateIndex].SetVisible(visible: false); - _states[_stateIndex].SetVisible(visible: true); + _states[_stateIndex].SetVisible(visible: true); if (!CurrentStateIsValid() && stateIndexIsValid) { - _states[_stateIndex].SetVisible(visible: false); - _states[stateIndex] .SetVisible(visible: true); - _stateIndex = stateIndex; + _states[_stateIndex].SetVisible(visible: false); + _states[stateIndex].SetVisible(visible: true); + _stateIndex = stateIndex; return false; } - if (_sequential && !_loop && _stateIndex == _states.Length - 1) - { - SetActivation(active: false); - } - return true; - } + if (_sequential && !_loop && _stateIndex == _states.Length - 1) + { + SetActivation(active: false); + } + return true; + } public bool CurrentStateIsValid() { @@ -102,20 +102,20 @@ namespace NewHorizons.Components var visibility = CheckVisibilityInstantly(); var playerInside = CheckPointInside(Locator.GetPlayerCamera().transform.position); - var isVisible = + var isVisible = isPlayerEntangled ? illumination - : ( - illumination + : + illumination ? visibility : playerInside - ); + ; return !isVisible; } public int RollState(int excludeIndex, List indices) - { + { var stateIndex = excludeIndex; // this function constructs a sort of segmented range: @@ -132,32 +132,32 @@ namespace NewHorizons.Components // // the second for looop uses num3 and num4 to figure out which segment num2 landed in - int num = 0; - foreach (int j in indices) - { - if (j != stateIndex) - { - _probabilities[j] = _states[j].GetProbability(); - num += _probabilities[j]; - } - } - int num2 = UnityEngine.Random.Range(0, num); - int num3 = 0; - int num4 = 0; - foreach (int k in indices) - { - if (k != stateIndex) - { - num3 = num4; - num4 += _probabilities[k]; - if (_probabilities[k] > 0 && num2 >= num3 && num2 < num4) - { - return k; - } - } - } + int num = 0; + foreach (int j in indices) + { + if (j != stateIndex) + { + _probabilities[j] = _states[j].GetProbability(); + num += _probabilities[j]; + } + } + int num2 = UnityEngine.Random.Range(0, num); + int num3 = 0; + int num4 = 0; + foreach (int k in indices) + { + if (k != stateIndex) + { + num3 = num4; + num4 += _probabilities[k]; + if (_probabilities[k] > 0 && num2 >= num3 && num2 < num4) + { + return k; + } + } + } - return indices[indices.Count-1]; + return indices[indices.Count - 1]; } } } diff --git a/NewHorizons/Components/QuantumPlanet.cs b/NewHorizons/Components/Quantum/QuantumPlanet.cs similarity index 98% rename from NewHorizons/Components/QuantumPlanet.cs rename to NewHorizons/Components/Quantum/QuantumPlanet.cs index 2b853b76..568a364b 100644 --- a/NewHorizons/Components/QuantumPlanet.cs +++ b/NewHorizons/Components/Quantum/QuantumPlanet.cs @@ -9,7 +9,7 @@ using System.Linq; using UnityEngine; using Logger = NewHorizons.Utility.Logger; using Random = UnityEngine.Random; -namespace NewHorizons.Components +namespace NewHorizons.Components.Quantum { public class QuantumPlanet : QuantumObject { @@ -162,9 +162,9 @@ namespace NewHorizons.Components private void OnPlayerBlink() { - if (base.IsVisible()) + if (IsVisible()) { - base.Collapse(true); + Collapse(true); } } diff --git a/NewHorizons/Components/RingOpacityController.cs b/NewHorizons/Components/RingOpacityController.cs index fde75452..df707a33 100644 --- a/NewHorizons/Components/RingOpacityController.cs +++ b/NewHorizons/Components/RingOpacityController.cs @@ -1,3 +1,4 @@ +using NewHorizons.Components.Volumes; using NewHorizons.External.Modules.VariableSize; using NewHorizons.Utility; using UnityEngine; diff --git a/NewHorizons/Components/ShipLogDetail.cs b/NewHorizons/Components/ShipLog/ShipLogDetail.cs similarity index 92% rename from NewHorizons/Components/ShipLogDetail.cs rename to NewHorizons/Components/ShipLog/ShipLogDetail.cs index a2ce809d..3852ff88 100644 --- a/NewHorizons/Components/ShipLogDetail.cs +++ b/NewHorizons/Components/ShipLog/ShipLogDetail.cs @@ -1,8 +1,8 @@ -using NewHorizons.External.Modules; +using NewHorizons.External.Modules; using UnityEngine; using UnityEngine.UI; using Logger = NewHorizons.Utility.Logger; -namespace NewHorizons.Components +namespace NewHorizons.Components.ShipLog { public class ShipLogDetail : MonoBehaviour { @@ -51,7 +51,7 @@ namespace NewHorizons.Components private void SetGreyScale(bool greyScale) { - _revealedImage.material = (greyScale ? _greyScaleMaterial : null); + _revealedImage.material = greyScale ? _greyScaleMaterial : null; } } } \ No newline at end of file diff --git a/NewHorizons/Components/ShipLogStarChartMode.cs b/NewHorizons/Components/ShipLog/ShipLogStarChartMode.cs similarity index 95% rename from NewHorizons/Components/ShipLogStarChartMode.cs rename to NewHorizons/Components/ShipLog/ShipLogStarChartMode.cs index a5d9dad0..a7b972ed 100644 --- a/NewHorizons/Components/ShipLogStarChartMode.cs +++ b/NewHorizons/Components/ShipLog/ShipLogStarChartMode.cs @@ -6,7 +6,7 @@ using System.Linq; using UnityEngine; using UnityEngine.UI; using Logger = NewHorizons.Utility.Logger; -namespace NewHorizons.Components +namespace NewHorizons.Components.ShipLog { public class ShipLogStarChartMode : ShipLogMode { @@ -41,7 +41,7 @@ namespace NewHorizons.Components public override void Initialize(ScreenPromptList centerPromptList, ScreenPromptList upperRightPromptList, OWAudioSource oneShotSource) { - root = base.transform.Find("ScaleRoot/PanRoot"); + root = transform.Find("ScaleRoot/PanRoot"); _oneShotSource = oneShotSource; _centerPromptList = centerPromptList; @@ -101,11 +101,11 @@ namespace NewHorizons.Components if (_cardTemplate == null) { var panRoot = SearchUtilities.Find("Ship_Body/Module_Cabin/Systems_Cabin/ShipLogPivot/ShipLog/ShipLogPivot/ShipLogCanvas/DetectiveMode/ScaleRoot/PanRoot"); - _cardTemplate = GameObject.Instantiate(panRoot.GetComponentInChildren().gameObject); + _cardTemplate = Instantiate(panRoot.GetComponentInChildren().gameObject); _cardTemplate.SetActive(false); } - var newCard = GameObject.Instantiate(_cardTemplate, parent); + var newCard = Instantiate(_cardTemplate, parent); var textComponent = newCard.transform.Find("EntryCardRoot/NameBackground/Name").GetComponent(); var name = UniqueIDToName(uniqueID); @@ -164,7 +164,7 @@ namespace NewHorizons.Components public override void EnterMode(string entryID = "", List revealQueue = null) { - base.gameObject.SetActive(true); + gameObject.SetActive(true); Locator.GetPromptManager().AddScreenPrompt(_detectiveModePrompt, _upperRightPromptList, TextAnchor.MiddleRight, -1, true); Locator.GetPromptManager().AddScreenPrompt(_targetSystemPrompt, _centerPromptList, TextAnchor.MiddleCenter, -1, true); @@ -172,7 +172,7 @@ namespace NewHorizons.Components public override void ExitMode() { - base.gameObject.SetActive(false); + gameObject.SetActive(false); Locator.GetPromptManager().RemoveScreenPrompt(_detectiveModePrompt); Locator.GetPromptManager().RemoveScreenPrompt(_targetSystemPrompt); @@ -223,7 +223,7 @@ namespace NewHorizons.Components if (oldIndex != _cardIndex) { - _oneShotSource.PlayOneShot(global::AudioType.ShipLogMoveBetweenPlanets, 1f); + _oneShotSource.PlayOneShot(AudioType.ShipLogMoveBetweenPlanets, 1f); _startPanTime = Time.unscaledTime; _startPanPos = _panRootPos; _panDuration = 0.25f; @@ -297,7 +297,7 @@ namespace NewHorizons.Components { if (_warpNotificationData != null) NotificationManager.SharedInstance.UnpinNotification(_warpNotificationData); if (_target == null) return; - if (playSound) _oneShotSource.PlayOneShot(global::AudioType.ShipLogMarkLocation, 1f); + if (playSound) _oneShotSource.PlayOneShot(AudioType.ShipLogMarkLocation, 1f); _target.SetMarkedOnHUD(false); _target = null; } diff --git a/NewHorizons/Components/SizeControllers/StarEvolutionController.cs b/NewHorizons/Components/SizeControllers/StarEvolutionController.cs index ced3222a..4e27972b 100644 --- a/NewHorizons/Components/SizeControllers/StarEvolutionController.cs +++ b/NewHorizons/Components/SizeControllers/StarEvolutionController.cs @@ -1,5 +1,6 @@ using NewHorizons.Builder.Body; using NewHorizons.Components.Orbital; +using NewHorizons.Components.Stars; using NewHorizons.External.Modules.VariableSize; using NewHorizons.Handlers; using NewHorizons.Utility; @@ -286,9 +287,9 @@ namespace NewHorizons.Components.SizeControllers { _stellarRemnant.SetActive(true); var remnantStarController = _stellarRemnant.GetComponentInChildren(); - if (remnantStarController != null) StarLightController.AddStar(remnantStarController); + if (remnantStarController != null) SunLightEffectsController.AddStar(remnantStarController); var remnantStarLight = _stellarRemnant.FindChild("SunLight"); - if (remnantStarLight != null) StarLightController.AddStarLight(remnantStarLight.GetComponent()); + if (remnantStarLight != null) SunLightEffectsController.AddStarLight(remnantStarLight.GetComponent()); } if (Time.time > _supernovaStartTime + supernovaTime) @@ -301,8 +302,8 @@ namespace NewHorizons.Components.SizeControllers private void DisableStar(bool start = false) { - if (controller != null) StarLightController.RemoveStar(controller); - if (!isProxy) StarLightController.RemoveStarLight(gameObject.FindChild("SunLight").GetAddComponent()); + if (controller != null) SunLightEffectsController.RemoveStar(controller); + if (!isProxy) SunLightEffectsController.RemoveStarLight(gameObject.FindChild("SunLight").GetAddComponent()); if (_stellarRemnant != null) { diff --git a/NewHorizons/Components/StarController.cs b/NewHorizons/Components/Stars/StarController.cs similarity index 94% rename from NewHorizons/Components/StarController.cs rename to NewHorizons/Components/Stars/StarController.cs index 8f69f085..374d4437 100644 --- a/NewHorizons/Components/StarController.cs +++ b/NewHorizons/Components/Stars/StarController.cs @@ -1,5 +1,5 @@ -using UnityEngine; -namespace NewHorizons.Components +using UnityEngine; +namespace NewHorizons.Components.Stars { public class StarController : MonoBehaviour { diff --git a/NewHorizons/Components/StarDestructionVolume.cs b/NewHorizons/Components/Stars/StarDestructionVolume.cs similarity index 98% rename from NewHorizons/Components/StarDestructionVolume.cs rename to NewHorizons/Components/Stars/StarDestructionVolume.cs index e0068d24..5354821a 100644 --- a/NewHorizons/Components/StarDestructionVolume.cs +++ b/NewHorizons/Components/Stars/StarDestructionVolume.cs @@ -5,7 +5,7 @@ using System.Text; using System.Threading.Tasks; using UnityEngine; -namespace NewHorizons.Components +namespace NewHorizons.Components.Stars { public class StarDestructionVolume : DestructionVolume { diff --git a/NewHorizons/Components/StarFluidVolume.cs b/NewHorizons/Components/Stars/StarFluidVolume.cs similarity index 96% rename from NewHorizons/Components/StarFluidVolume.cs rename to NewHorizons/Components/Stars/StarFluidVolume.cs index 150d39f4..bd91f025 100644 --- a/NewHorizons/Components/StarFluidVolume.cs +++ b/NewHorizons/Components/Stars/StarFluidVolume.cs @@ -1,6 +1,6 @@ using NewHorizons.Components.SizeControllers; using UnityEngine; -namespace NewHorizons.Components +namespace NewHorizons.Components.Stars { public class StarFluidVolume : SimpleFluidVolume { diff --git a/NewHorizons/Components/StarLightController.cs b/NewHorizons/Components/Stars/StarLightController.cs similarity index 55% rename from NewHorizons/Components/StarLightController.cs rename to NewHorizons/Components/Stars/StarLightController.cs index a9e2b000..c3c7b944 100644 --- a/NewHorizons/Components/StarLightController.cs +++ b/NewHorizons/Components/Stars/StarLightController.cs @@ -2,29 +2,31 @@ using NewHorizons.Builder.Atmosphere; using System.Collections.Generic; using UnityEngine; using Logger = NewHorizons.Utility.Logger; -namespace NewHorizons.Components +namespace NewHorizons.Components.Stars { [RequireComponent(typeof(SunLightController))] [RequireComponent(typeof(SunLightParamUpdater))] - public class StarLightController : MonoBehaviour + public class SunLightEffectsController : MonoBehaviour { private static readonly int SunIntensity = Shader.PropertyToID("_SunIntensity"); private static readonly float hearthSunDistanceSqr = 8593 * 8593; - public static StarLightController Instance { get; private set; } + public static SunLightEffectsController Instance { get; private set; } + + private readonly List _stars = new(); + private readonly List _lights = new(); - private List _stars = new List(); - private List _lights = new List(); private StarController _activeStar; - private SunLightController _sunLightController; private SunLightParamUpdater _sunLightParamUpdater; public void Awake() { Instance = this; + _sunLightController = GetComponent(); _sunLightController.enabled = true; + _sunLightParamUpdater = GetComponent(); _sunLightParamUpdater._sunLightController = _sunLightController; } @@ -70,60 +72,81 @@ namespace NewHorizons.Components public void Update() { - if (_activeStar == null || !_activeStar.gameObject.activeInHierarchy) - { - if (_stars.Contains(_activeStar)) _stars.Remove(_activeStar); - if (_stars.Count > 0) ChangeActiveStar(_stars[0]); - else gameObject.SetActive(false); - - foreach (var (_, material) in AtmosphereBuilder.Skys) - { - material.SetFloat(SunIntensity, 0); - } - - return; - } - - // Update atmo shaders - foreach (var (planet, material) in AtmosphereBuilder.Skys) - { - var sqrDist = (planet.transform.position - _activeStar.transform.position).sqrMagnitude; - var intensity = Mathf.Min(_activeStar.Intensity / (sqrDist / hearthSunDistanceSqr), 1f); - - material.SetFloat(SunIntensity, intensity); - } - // Player is always at 0,0,0 more or less so if they arent using the map camera then wtv var origin = Vector3.zero; - foreach (var star in _stars) - { - if (star == null) continue; - if (!(star.gameObject.activeSelf && star.gameObject.activeInHierarchy)) continue; - - if (PlayerState.InMapView()) - { - origin = Locator.GetActiveCamera().transform.position; - } - - if (star.Intensity * (star.transform.position - origin).sqrMagnitude < _activeStar.Intensity * (_activeStar.transform.position - origin).sqrMagnitude) - { - ChangeActiveStar(star); - break; - } - } - if (PlayerState.InMapView()) { - foreach (var light in _lights) light.enabled = true; - return; + origin = Locator.GetActiveCamera().transform.position; + + // Keep all star lights on in map + foreach (var light in _lights) + { + light.enabled = true; + } + } + else + { + // Outside map, only show lights within 50km range or light.range + // For some reason outside of the actual range of the lights they still show reflection effects on water and glass + foreach (var light in _lights) + { + // Minimum 50km range so it's not badly noticeable for dim stars + if ((light.transform.position - origin).sqrMagnitude <= Mathf.Max(light.range * light.range, 2500000000)) + { + light.enabled = true; + } + else + { + light.enabled = false; + } + } } - foreach (var light in _lights) + if (_stars.Count > 0) { - // Minimum 50km range so it's not badly noticeable for dim stars - if ((light.transform.position - origin).sqrMagnitude <= Mathf.Max(light.range * light.range, 2500000000)) light.enabled = true; - else light.enabled = false; + if (_activeStar == null || !_activeStar.gameObject.activeInHierarchy) + { + if (_stars.Contains(_activeStar)) + { + _stars.Remove(_activeStar); + } + + if (_stars.Count > 0) + { + ChangeActiveStar(_stars[0]); + } + else + { + foreach (var (_, material) in AtmosphereBuilder.Skys) + { + material.SetFloat(SunIntensity, 0); + } + } + } + else + { + // Update atmo shaders + foreach (var (planet, material) in AtmosphereBuilder.Skys) + { + var sqrDist = (planet.transform.position - _activeStar.transform.position).sqrMagnitude; + var intensity = Mathf.Min(_activeStar.Intensity / (sqrDist / hearthSunDistanceSqr), 1f); + + material.SetFloat(SunIntensity, intensity); + } + + foreach (var star in _stars) + { + if (star == null) continue; + if (!(star.gameObject.activeSelf && star.gameObject.activeInHierarchy)) continue; + + if (star.Intensity * (star.transform.position - origin).sqrMagnitude < _activeStar.Intensity * (_activeStar.transform.position - origin).sqrMagnitude) + { + ChangeActiveStar(star); + break; + } + } + } } } @@ -151,8 +174,8 @@ namespace NewHorizons.Components _sunLightParamUpdater._propID_OWSunColorIntensity = Shader.PropertyToID("_OWSunColorIntensity"); // For the param thing to work it wants this to be on the star idk - this.transform.parent = star.transform; - this.transform.localPosition = Vector3.zero; + transform.parent = star.transform; + transform.localPosition = Vector3.zero; } } } diff --git a/NewHorizons/Components/StarSurfaceAudioController.cs b/NewHorizons/Components/Stars/StarSurfaceAudioController.cs similarity index 88% rename from NewHorizons/Components/StarSurfaceAudioController.cs rename to NewHorizons/Components/Stars/StarSurfaceAudioController.cs index aab8d3ce..71bdf525 100644 --- a/NewHorizons/Components/StarSurfaceAudioController.cs +++ b/NewHorizons/Components/Stars/StarSurfaceAudioController.cs @@ -1,7 +1,7 @@ using UnityEngine; using NewHorizons.Components.SizeControllers; -namespace NewHorizons.Components +namespace NewHorizons.Components.Stars { [RequireComponent(typeof(OWAudioSource))] public class StarSurfaceAudioController : SectoredMonoBehaviour @@ -34,7 +34,7 @@ namespace NewHorizons.Components public void Update() { _fade = Mathf.MoveTowards(_fade, 1, Time.deltaTime * 0.2f); - float value = Mathf.Max(0.0f, Vector3.Distance(Locator.GetPlayerCamera().transform.position, this.transform.position) - (_starEvolutionController != null ? _starEvolutionController.CurrentScale : _size)); + float value = Mathf.Max(0.0f, Vector3.Distance(Locator.GetPlayerCamera().transform.position, transform.position) - (_starEvolutionController != null ? _starEvolutionController.CurrentScale : _size)); float num = Mathf.InverseLerp(1600f, 100f, value); _audioSource.SetLocalVolume(num * num * _fade); } diff --git a/NewHorizons/Components/StellarDeathController.cs b/NewHorizons/Components/Stars/StellarDeathController.cs similarity index 96% rename from NewHorizons/Components/StellarDeathController.cs rename to NewHorizons/Components/Stars/StellarDeathController.cs index fe5dadb3..a07fddeb 100644 --- a/NewHorizons/Components/StellarDeathController.cs +++ b/NewHorizons/Components/Stars/StellarDeathController.cs @@ -1,6 +1,6 @@ using UnityEngine; -namespace NewHorizons.Components +namespace NewHorizons.Components.Stars { public class StellarDeathController : MonoBehaviour { @@ -71,7 +71,7 @@ namespace NewHorizons.Components surface.transform.localScale = Vector3.one * _currentSupernovaScale; _localSupernovaMat.color = Color.Lerp(Color.black, supernovaMaterial.color, supernovaAlpha.Evaluate(_time)); - float distanceToPlayer = PlayerState.InDreamWorld() ? 20000f : (Vector3.Distance(transform.position, Locator.GetPlayerCamera().transform.position) - GetSupernovaRadius()); + float distanceToPlayer = PlayerState.InDreamWorld() ? 20000f : Vector3.Distance(transform.position, Locator.GetPlayerCamera().transform.position) - GetSupernovaRadius(); if (_isProxy) return; diff --git a/NewHorizons/Components/BaseVolume.cs b/NewHorizons/Components/Volumes/BaseVolume.cs similarity index 95% rename from NewHorizons/Components/BaseVolume.cs rename to NewHorizons/Components/Volumes/BaseVolume.cs index 6ddccada..f313310c 100644 --- a/NewHorizons/Components/BaseVolume.cs +++ b/NewHorizons/Components/Volumes/BaseVolume.cs @@ -1,6 +1,6 @@ using UnityEngine; -namespace NewHorizons.Components +namespace NewHorizons.Components.Volumes { [RequireComponent(typeof(OWTriggerVolume))] public abstract class BaseVolume : MonoBehaviour diff --git a/NewHorizons/Components/BlackHoleDestructionVolume.cs b/NewHorizons/Components/Volumes/BlackHoleDestructionVolume.cs similarity index 84% rename from NewHorizons/Components/BlackHoleDestructionVolume.cs rename to NewHorizons/Components/Volumes/BlackHoleDestructionVolume.cs index 145a0c5c..e5489760 100644 --- a/NewHorizons/Components/BlackHoleDestructionVolume.cs +++ b/NewHorizons/Components/Volumes/BlackHoleDestructionVolume.cs @@ -1,6 +1,6 @@ using NewHorizons.OtherMods.AchievementsPlus.NH; -namespace NewHorizons.Components +namespace NewHorizons.Components.Volumes { public class BlackHoleDestructionVolume : DestructionVolume { @@ -15,7 +15,7 @@ namespace NewHorizons.Components SurveyorProbe requiredComponent = probeBody.GetRequiredComponent(); if (requiredComponent.IsLaunched()) { - UnityEngine.Object.Destroy(requiredComponent.gameObject); + Destroy(requiredComponent.gameObject); ProbeLostAchievement.Earn(); } } diff --git a/NewHorizons/Components/ChangeStarSystemVolume.cs b/NewHorizons/Components/Volumes/ChangeStarSystemVolume.cs similarity index 94% rename from NewHorizons/Components/ChangeStarSystemVolume.cs rename to NewHorizons/Components/Volumes/ChangeStarSystemVolume.cs index ea57c53f..bdf43aae 100644 --- a/NewHorizons/Components/ChangeStarSystemVolume.cs +++ b/NewHorizons/Components/Volumes/ChangeStarSystemVolume.cs @@ -1,4 +1,4 @@ -namespace NewHorizons.Components +namespace NewHorizons.Components.Volumes { public class ChangeStarSystemVolume : BlackHoleDestructionVolume { diff --git a/NewHorizons/Components/InterferenceVolume.cs b/NewHorizons/Components/Volumes/InterferenceVolume.cs similarity index 97% rename from NewHorizons/Components/InterferenceVolume.cs rename to NewHorizons/Components/Volumes/InterferenceVolume.cs index 43cf34ac..acb3582f 100644 --- a/NewHorizons/Components/InterferenceVolume.cs +++ b/NewHorizons/Components/Volumes/InterferenceVolume.cs @@ -6,7 +6,7 @@ using System.Text; using System.Threading.Tasks; using UnityEngine; -namespace NewHorizons.Components +namespace NewHorizons.Components.Volumes { public class InterferenceVolume : BaseVolume { diff --git a/NewHorizons/Components/MapRestrictionVolume.cs b/NewHorizons/Components/Volumes/MapRestrictionVolume.cs similarity index 94% rename from NewHorizons/Components/MapRestrictionVolume.cs rename to NewHorizons/Components/Volumes/MapRestrictionVolume.cs index 04442840..ee31a2a2 100644 --- a/NewHorizons/Components/MapRestrictionVolume.cs +++ b/NewHorizons/Components/Volumes/MapRestrictionVolume.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using UnityEngine; -namespace NewHorizons.Components +namespace NewHorizons.Components.Volumes { public class MapRestrictionVolume : BaseVolume { diff --git a/NewHorizons/Components/NHFluidVolume.cs b/NewHorizons/Components/Volumes/NHFluidVolume.cs similarity index 86% rename from NewHorizons/Components/NHFluidVolume.cs rename to NewHorizons/Components/Volumes/NHFluidVolume.cs index 3b19f04d..3b5a4a00 100644 --- a/NewHorizons/Components/NHFluidVolume.cs +++ b/NewHorizons/Components/Volumes/NHFluidVolume.cs @@ -1,5 +1,5 @@ -using UnityEngine; -namespace NewHorizons.Components +using UnityEngine; +namespace NewHorizons.Components.Volumes { public class NHFluidVolume : RadialFluidVolume { diff --git a/NewHorizons/Components/NHInnerFogWarpVolume.cs b/NewHorizons/Components/Volumes/NHInnerFogWarpVolume.cs similarity index 83% rename from NewHorizons/Components/NHInnerFogWarpVolume.cs rename to NewHorizons/Components/Volumes/NHInnerFogWarpVolume.cs index 61c5e3ea..70d74979 100644 --- a/NewHorizons/Components/NHInnerFogWarpVolume.cs +++ b/NewHorizons/Components/Volumes/NHInnerFogWarpVolume.cs @@ -1,4 +1,4 @@ -namespace NewHorizons.Components +namespace NewHorizons.Components.Volumes { public class NHInnerFogWarpVolume : InnerFogWarpVolume { diff --git a/NewHorizons/Components/NotificationVolume.cs b/NewHorizons/Components/Volumes/NotificationVolume.cs similarity index 97% rename from NewHorizons/Components/NotificationVolume.cs rename to NewHorizons/Components/Volumes/NotificationVolume.cs index 6fe7b746..70543449 100644 --- a/NewHorizons/Components/NotificationVolume.cs +++ b/NewHorizons/Components/Volumes/NotificationVolume.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Linq; using UnityEngine; -namespace NewHorizons.Components +namespace NewHorizons.Components.Volumes { public class NotificationVolume : BaseVolume { @@ -16,7 +16,7 @@ namespace NewHorizons.Components public void SetPinned(bool pin) => _pin = pin; - public void SetTarget(External.Modules.VolumesModule.NotificationVolumeInfo.NotificationTarget target) => SetTarget(EnumUtils.Parse(target.ToString(), NotificationTarget.All)); + public void SetTarget(External.Modules.VolumesModule.NotificationVolumeInfo.NotificationTarget target) => SetTarget(EnumUtils.Parse(target.ToString(), NotificationTarget.All)); public void SetTarget(NotificationTarget target) => _target = target; diff --git a/NewHorizons/Components/RingFluidVolume.cs b/NewHorizons/Components/Volumes/RingFluidVolume.cs similarity index 95% rename from NewHorizons/Components/RingFluidVolume.cs rename to NewHorizons/Components/Volumes/RingFluidVolume.cs index 83b377a7..66982bdc 100644 --- a/NewHorizons/Components/RingFluidVolume.cs +++ b/NewHorizons/Components/Volumes/RingFluidVolume.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using UnityEngine; -namespace NewHorizons.Components +namespace NewHorizons.Components.Volumes { public class RingFluidVolume : SimpleFluidVolume { @@ -14,7 +14,7 @@ namespace NewHorizons.Components ForceDetector forceDetector = hitObj.GetComponent(); if (forceDetector != null && forceDetector._activeVolumes != null && forceDetector._activeVolumes.Count > 0 && forceDetector._activeVolumes.Where(activeVolume => activeVolume is ForceVolume).Select(activeVolume => activeVolume as ForceVolume).Any(activeVolume => activeVolume.GetAffectsAlignment(forceDetector._attachedBody))) return; - + fluidDetector.AddVolume(this); } diff --git a/NewHorizons/Handlers/InterferenceHandler.cs b/NewHorizons/Handlers/InterferenceHandler.cs index 97ff0046..73912289 100644 --- a/NewHorizons/Handlers/InterferenceHandler.cs +++ b/NewHorizons/Handlers/InterferenceHandler.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; namespace NewHorizons.Handlers { - using InterferenceVolume = NewHorizons.Components.InterferenceVolume; + using InterferenceVolume = Components.Volumes.InterferenceVolume; public static class InterferenceHandler { diff --git a/NewHorizons/Handlers/PlanetCreationHandler.cs b/NewHorizons/Handlers/PlanetCreationHandler.cs index 3c7c2d1d..ffc14739 100644 --- a/NewHorizons/Handlers/PlanetCreationHandler.cs +++ b/NewHorizons/Handlers/PlanetCreationHandler.cs @@ -4,8 +4,9 @@ using NewHorizons.Builder.General; using NewHorizons.Builder.Orbital; using NewHorizons.Builder.Props; using NewHorizons.Builder.Volumes; -using NewHorizons.Components; using NewHorizons.Components.Orbital; +using NewHorizons.Components.Quantum; +using NewHorizons.Components.Stars; using NewHorizons.OtherMods.OWRichPresence; using NewHorizons.Utility; using System; @@ -57,9 +58,9 @@ namespace NewHorizons.Handlers GameObject.Destroy(starLightGO.GetComponent()); starLightGO.name = "StarLightController"; - starLightGO.AddComponent(); - StarLightController.AddStar(starController); - StarLightController.AddStarLight(starController.Light); + starLightGO.AddComponent(); + SunLightEffectsController.AddStar(starController); + SunLightEffectsController.AddStarLight(starController.Light); starLightGO.SetActive(true); @@ -499,10 +500,10 @@ namespace NewHorizons.Handlers { var (star, starController, starEvolutionController) = StarBuilder.Make(go, sector, body.Config.Star, body.Mod, body.Config.isStellarRemnant); - if (starController != null) StarLightController.AddStar(starController); + if (starController != null) SunLightEffectsController.AddStar(starController); var starLight = star.FindChild("SunLight"); - if (starLight != null) StarLightController.AddStarLight(starLight.GetComponent()); + if (starLight != null) SunLightEffectsController.AddStarLight(starLight.GetComponent()); // If it has an evolution controller that means it will die -> we make a remnant (unless its a remnant) if (starEvolutionController != null && !body.Config.isStellarRemnant) diff --git a/NewHorizons/Handlers/PlanetDestructionHandler.cs b/NewHorizons/Handlers/PlanetDestructionHandler.cs index 7f8fbf28..b25e6d00 100644 --- a/NewHorizons/Handlers/PlanetDestructionHandler.cs +++ b/NewHorizons/Handlers/PlanetDestructionHandler.cs @@ -1,4 +1,4 @@ -using NewHorizons.Components; +using NewHorizons.Components.Stars; using NewHorizons.Utility; using OWML.Utils; using System; @@ -117,8 +117,8 @@ namespace NewHorizons.Handlers break; case AstroObject.Name.Sun: var starController = ao.gameObject.GetComponent(); - StarLightController.RemoveStar(starController); - StarLightController.RemoveStarLight(ao.gameObject.FindChild("Sector_SUN/Effects_SUN/SunLight").GetComponent()); + SunLightEffectsController.RemoveStar(starController); + SunLightEffectsController.RemoveStarLight(ao.gameObject.FindChild("Sector_SUN/Effects_SUN/SunLight").GetComponent()); GameObject.Destroy(starController); var audio = ao.GetComponentInChildren(); diff --git a/NewHorizons/Handlers/StarChartHandler.cs b/NewHorizons/Handlers/StarChartHandler.cs index 69e542c7..4ddec030 100644 --- a/NewHorizons/Handlers/StarChartHandler.cs +++ b/NewHorizons/Handlers/StarChartHandler.cs @@ -1,4 +1,4 @@ -using NewHorizons.Components; +using NewHorizons.Components.ShipLog; using NewHorizons.Utility; using System.Collections.Generic; using UnityEngine; diff --git a/NewHorizons/Handlers/VesselCoordinatePromptHandler.cs b/NewHorizons/Handlers/VesselCoordinatePromptHandler.cs index f3578bda..f1e547c2 100644 --- a/NewHorizons/Handlers/VesselCoordinatePromptHandler.cs +++ b/NewHorizons/Handlers/VesselCoordinatePromptHandler.cs @@ -1,4 +1,4 @@ -using NewHorizons.Components; +using NewHorizons.Components.ShipLog; using NewHorizons.Utility; using System; using System.Collections.Generic; diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index 678852bb..20e4a877 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -3,6 +3,7 @@ using NewHorizons.Builder.Atmosphere; using NewHorizons.Builder.Body; using NewHorizons.Builder.Props; using NewHorizons.Components; +using NewHorizons.Components.Fixers; using NewHorizons.Components.SizeControllers; using NewHorizons.External; using NewHorizons.External.Configs; diff --git a/NewHorizons/OtherMods/OWRichPresence/RichPresenceHandler.cs b/NewHorizons/OtherMods/OWRichPresence/RichPresenceHandler.cs index 7b94ca6c..727bc082 100644 --- a/NewHorizons/OtherMods/OWRichPresence/RichPresenceHandler.cs +++ b/NewHorizons/OtherMods/OWRichPresence/RichPresenceHandler.cs @@ -1,4 +1,4 @@ -using NewHorizons.Components; +using NewHorizons.Components.ShipLog; using NewHorizons.Handlers; using NewHorizons.Utility; using System; diff --git a/NewHorizons/Patches/DestructionVolumePatches.cs b/NewHorizons/Patches/DestructionVolumePatches.cs index 23221daa..e29e3d02 100644 --- a/NewHorizons/Patches/DestructionVolumePatches.cs +++ b/NewHorizons/Patches/DestructionVolumePatches.cs @@ -1,5 +1,5 @@ using HarmonyLib; -using NewHorizons.Components; +using NewHorizons.Components.Quantum; using System; using System.Collections.Generic; using System.Linq; diff --git a/NewHorizons/Patches/RaftPatches.cs b/NewHorizons/Patches/RaftPatches.cs index b6f214ac..fea55497 100644 --- a/NewHorizons/Patches/RaftPatches.cs +++ b/NewHorizons/Patches/RaftPatches.cs @@ -1,5 +1,5 @@ using HarmonyLib; -using NewHorizons.Components; +using NewHorizons.Components.Volumes; using UnityEngine; namespace NewHorizons.Patches { diff --git a/NewHorizons/Patches/ShipLogPatches.cs b/NewHorizons/Patches/ShipLogPatches.cs index 96a89c01..52ce5a74 100644 --- a/NewHorizons/Patches/ShipLogPatches.cs +++ b/NewHorizons/Patches/ShipLogPatches.cs @@ -1,7 +1,6 @@ using HarmonyLib; using NewHorizons.OtherMods.AchievementsPlus; using NewHorizons.Builder.ShipLog; -using NewHorizons.Components; using NewHorizons.Handlers; using NewHorizons.Utility; using System; @@ -10,6 +9,8 @@ using System.Linq; using UnityEngine; using Logger = NewHorizons.Utility.Logger; using Object = UnityEngine.Object; +using NewHorizons.Components.ShipLog; + namespace NewHorizons.Patches { [HarmonyPatch] From b1eac0748ef7ab05cd024e62c1a620b7efa6d17d Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 15 Sep 2022 00:57:10 -0400 Subject: [PATCH 169/176] Renamed it and fixed it for the sun --- .../StarEvolutionController.cs | 2 +- ...roller.cs => SunLightEffectsController.cs} | 22 ++++++++++++++----- .../Handlers/PlanetDestructionHandler.cs | 2 +- NewHorizons/Main.cs | 1 + 4 files changed, 19 insertions(+), 8 deletions(-) rename NewHorizons/Components/Stars/{StarLightController.cs => SunLightEffectsController.cs} (91%) diff --git a/NewHorizons/Components/SizeControllers/StarEvolutionController.cs b/NewHorizons/Components/SizeControllers/StarEvolutionController.cs index 4e27972b..5e8a1b2e 100644 --- a/NewHorizons/Components/SizeControllers/StarEvolutionController.cs +++ b/NewHorizons/Components/SizeControllers/StarEvolutionController.cs @@ -303,7 +303,7 @@ namespace NewHorizons.Components.SizeControllers private void DisableStar(bool start = false) { if (controller != null) SunLightEffectsController.RemoveStar(controller); - if (!isProxy) SunLightEffectsController.RemoveStarLight(gameObject.FindChild("SunLight").GetAddComponent()); + if (!isProxy) SunLightEffectsController.RemoveStarLight(gameObject.FindChild("SunLight").GetComponent()); if (_stellarRemnant != null) { diff --git a/NewHorizons/Components/Stars/StarLightController.cs b/NewHorizons/Components/Stars/SunLightEffectsController.cs similarity index 91% rename from NewHorizons/Components/Stars/StarLightController.cs rename to NewHorizons/Components/Stars/SunLightEffectsController.cs index c3c7b944..c5260e54 100644 --- a/NewHorizons/Components/Stars/StarLightController.cs +++ b/NewHorizons/Components/Stars/SunLightEffectsController.cs @@ -1,4 +1,5 @@ using NewHorizons.Builder.Atmosphere; +using NewHorizons.Utility; using System.Collections.Generic; using UnityEngine; using Logger = NewHorizons.Utility.Logger; @@ -31,6 +32,13 @@ namespace NewHorizons.Components.Stars _sunLightParamUpdater._sunLightController = _sunLightController; } + public void Start() + { + // Using GameObject.Find here so that if its null we just dont find it + var sunlight = GameObject.Find("Sun_Body/Sector_SUN/Effects_SUN/SunLight").GetComponent(); + if (sunlight != null) AddStarLight(sunlight); + } + public static void AddStar(StarController star) { if (star == null) return; @@ -58,16 +66,18 @@ namespace NewHorizons.Components.Stars public static void AddStarLight(Light light) { - if (light == null) return; - - Instance._lights.Add(light); + if (light != null) + { + Instance._lights.SafeAdd(light); + } } public static void RemoveStarLight(Light light) { - if (light == null) return; - - if (Instance._lights.Contains(light)) Instance._lights.Remove(light); + if (light != null && Instance._lights.Contains(light)) + { + Instance._lights.Remove(light); + } } public void Update() diff --git a/NewHorizons/Handlers/PlanetDestructionHandler.cs b/NewHorizons/Handlers/PlanetDestructionHandler.cs index b25e6d00..a170ff62 100644 --- a/NewHorizons/Handlers/PlanetDestructionHandler.cs +++ b/NewHorizons/Handlers/PlanetDestructionHandler.cs @@ -118,7 +118,7 @@ namespace NewHorizons.Handlers case AstroObject.Name.Sun: var starController = ao.gameObject.GetComponent(); SunLightEffectsController.RemoveStar(starController); - SunLightEffectsController.RemoveStarLight(ao.gameObject.FindChild("Sector_SUN/Effects_SUN/SunLight").GetComponent()); + SunLightEffectsController.RemoveStarLight(ao.transform.Find("Sector_SUN/Effects_SUN/SunLight").GetComponent()); GameObject.Destroy(starController); var audio = ao.GetComponentInChildren(); diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index 20e4a877..ef13942d 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -354,6 +354,7 @@ namespace NewHorizons var map = GameObject.FindObjectOfType(); if (map != null) map._maxPanDistance = FurthestOrbit * 1.5f; + // Fix the map satellite SearchUtilities.Find("HearthianMapSatellite_Body", false).AddComponent(); From e5bc06dacee3a2b992a6908eedb8777eb872260e Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 15 Sep 2022 13:28:08 -0400 Subject: [PATCH 170/176] Update GameLibs version --- NewHorizons/NewHorizons.csproj | 2 +- SchemaExporter/SchemaExporter.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/NewHorizons/NewHorizons.csproj b/NewHorizons/NewHorizons.csproj index bb43cdbf..a4e7f6e4 100644 --- a/NewHorizons/NewHorizons.csproj +++ b/NewHorizons/NewHorizons.csproj @@ -17,7 +17,7 @@ - + diff --git a/SchemaExporter/SchemaExporter.csproj b/SchemaExporter/SchemaExporter.csproj index 2c55f3ad..40ec8c5e 100644 --- a/SchemaExporter/SchemaExporter.csproj +++ b/SchemaExporter/SchemaExporter.csproj @@ -20,7 +20,7 @@ - + From edf505628046245b16ddf370028216879631b750 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Thu, 15 Sep 2022 18:19:58 -0400 Subject: [PATCH 171/176] Update to match vanilla --- NewHorizons/Components/Stars/StellarDeathController.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/NewHorizons/Components/Stars/StellarDeathController.cs b/NewHorizons/Components/Stars/StellarDeathController.cs index a07fddeb..400297b3 100644 --- a/NewHorizons/Components/Stars/StellarDeathController.cs +++ b/NewHorizons/Components/Stars/StellarDeathController.cs @@ -22,6 +22,7 @@ namespace NewHorizons.Components.Stars private float _currentSupernovaScale; private Material _localSupernovaMat; private bool _isProxy; + private bool _renderingEnabled = true; private ParticleSystemRenderer[] _cachedParticleRenderers; public void Awake() @@ -34,8 +35,12 @@ namespace NewHorizons.Components.Stars public void Activate() { enabled = true; - shockwave.enabled = true; - foreach (var particle in explosionParticles) particle.Play(); + shockwave.enabled = _renderingEnabled; + for (int i = 0; i < explosionParticles.Length; i++) + { + explosionParticles[i].Play(); + _cachedParticleRenderers[i].enabled = _renderingEnabled; + } _time = 0.0f; _currentSupernovaScale = supernovaScale.Evaluate(0.0f); _localSupernovaMat = new Material(supernovaMaterial); @@ -97,6 +102,7 @@ namespace NewHorizons.Components.Stars public void SetRenderingEnabled(bool renderingEnabled) { + _renderingEnabled = renderingEnabled; if (!enabled) return; shockwave.enabled = renderingEnabled; SetParticlesVisibility(renderingEnabled); From 88bda9d388cc941222e8e3b64e287db937ce1f21 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Tue, 13 Sep 2022 16:24:12 -0400 Subject: [PATCH 172/176] Use title instead --- NewHorizons/Handlers/CreditsHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/Handlers/CreditsHandler.cs b/NewHorizons/Handlers/CreditsHandler.cs index f71a73ef..4c45b07a 100644 --- a/NewHorizons/Handlers/CreditsHandler.cs +++ b/NewHorizons/Handlers/CreditsHandler.cs @@ -134,7 +134,7 @@ namespace NewHorizons.Handlers { var rootSection = MakeNode(doc, "section", new Dictionary() { - { "name", "Custom" }, + { "name", title }, { "credits-type", "Final Fast Krazy" } }); From 126547be6f05f168ed5fbd76497425c7929ccd9e Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 15 Sep 2022 18:47:26 -0400 Subject: [PATCH 173/176] Make everything use Path.Combine instead of concat for Linux --- NewHorizons/Builder/Props/DialogueBuilder.cs | 2 +- NewHorizons/Builder/Props/NomaiTextBuilder.cs | 4 ++-- NewHorizons/Builder/Props/ProjectionBuilder.cs | 4 ++-- NewHorizons/Builder/ShipLog/RumorModeBuilder.cs | 3 ++- NewHorizons/Handlers/AudioTypeHandler.cs | 3 ++- NewHorizons/Main.cs | 2 +- NewHorizons/NewHorizonsApi.cs | 10 +++++----- NewHorizons/OtherMods/VoiceActing/VoiceHandler.cs | 4 ++-- NewHorizons/Utility/AssetBundleUtilities.cs | 4 ++-- NewHorizons/Utility/AudioUtilities.cs | 3 ++- NewHorizons/Utility/DebugMenu/DebugMenu.cs | 8 ++++---- NewHorizons/Utility/ImageUtilities.cs | 6 +++--- 12 files changed, 28 insertions(+), 25 deletions(-) diff --git a/NewHorizons/Builder/Props/DialogueBuilder.cs b/NewHorizons/Builder/Props/DialogueBuilder.cs index 39712108..b20a57d5 100644 --- a/NewHorizons/Builder/Props/DialogueBuilder.cs +++ b/NewHorizons/Builder/Props/DialogueBuilder.cs @@ -87,7 +87,7 @@ namespace NewHorizons.Builder.Props var dialogueTree = conversationZone.AddComponent(); - var xml = File.ReadAllText(mod.Manifest.ModFolderPath + info.xmlFile); + var xml = File.ReadAllText(Path.Combine(mod.Manifest.ModFolderPath, info.xmlFile)); var text = new TextAsset(xml); // Text assets need a name to be used with VoiceMod diff --git a/NewHorizons/Builder/Props/NomaiTextBuilder.cs b/NewHorizons/Builder/Props/NomaiTextBuilder.cs index f0e093d2..981f447d 100644 --- a/NewHorizons/Builder/Props/NomaiTextBuilder.cs +++ b/NewHorizons/Builder/Props/NomaiTextBuilder.cs @@ -2,12 +2,12 @@ using NewHorizons.External.Modules; using NewHorizons.Handlers; using NewHorizons.Utility; using OWML.Common; -using Enum = System.Enum; using System.Collections.Generic; using System.IO; using System.Linq; using System.Xml; using UnityEngine; +using Enum = System.Enum; using Logger = NewHorizons.Utility.Logger; using Random = UnityEngine.Random; namespace NewHorizons.Builder.Props @@ -108,7 +108,7 @@ namespace NewHorizons.Builder.Props { if (_scrollPrefab == null) InitPrefabs(); - var xmlPath = File.ReadAllText(mod.ModHelper.Manifest.ModFolderPath + info.xmlFile); + var xmlPath = File.ReadAllText(Path.Combine(mod.ModHelper.Manifest.ModFolderPath, info.xmlFile)); switch (info.type) { diff --git a/NewHorizons/Builder/Props/ProjectionBuilder.cs b/NewHorizons/Builder/Props/ProjectionBuilder.cs index e80d451d..32876489 100644 --- a/NewHorizons/Builder/Props/ProjectionBuilder.cs +++ b/NewHorizons/Builder/Props/ProjectionBuilder.cs @@ -4,6 +4,7 @@ using NewHorizons.Utility; using OWML.Common; using System; using System.Collections.Generic; +using System.IO; using System.Threading; using UnityEngine; using static NewHorizons.External.Modules.PropModule; @@ -346,8 +347,7 @@ namespace NewHorizons.Builder.Props } else { - // Don't use Path.Combine here else you break the Vision - imageLoader.PathsToLoad.Add((i, mod.ModHelper.Manifest.ModFolderPath + slideInfo.imagePath)); + imageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, slideInfo.imagePath))); } AddModules(slideInfo, ref slide, mod); diff --git a/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs b/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs index a5c18d78..9e82ae8b 100644 --- a/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs +++ b/NewHorizons/Builder/ShipLog/RumorModeBuilder.cs @@ -4,6 +4,7 @@ using NewHorizons.Handlers; using NewHorizons.Utility; using System; using System.Collections.Generic; +using System.IO; using System.Xml.Linq; using UnityEngine; using Logger = NewHorizons.Utility.Logger; @@ -53,7 +54,7 @@ namespace NewHorizons.Builder.ShipLog public static void AddBodyToShipLog(ShipLogManager manager, NewHorizonsBody body) { string systemName = body.Config.starSystem; - XElement astroBodyFile = XElement.Load(body.Mod.ModHelper.Manifest.ModFolderPath + "/" + body.Config.ShipLog.xmlFile); + XElement astroBodyFile = XElement.Load(Path.Combine(body.Mod.ModHelper.Manifest.ModFolderPath, body.Config.ShipLog.xmlFile)); XElement astroBodyId = astroBodyFile.Element("ID"); if (astroBodyId == null) { diff --git a/NewHorizons/Handlers/AudioTypeHandler.cs b/NewHorizons/Handlers/AudioTypeHandler.cs index 698514c8..70878ae3 100644 --- a/NewHorizons/Handlers/AudioTypeHandler.cs +++ b/NewHorizons/Handlers/AudioTypeHandler.cs @@ -2,6 +2,7 @@ using NewHorizons.Utility; using OWML.Common; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using UnityEngine; using Logger = NewHorizons.Utility.Logger; @@ -65,7 +66,7 @@ namespace NewHorizons.Handlers var id = mod.ModHelper.Manifest.UniqueName + "_" + audioPath; if (_customAudioTypes.TryGetValue(id, out audioType)) return audioType; - var audioClip = AudioUtilities.LoadAudio(mod.ModHelper.Manifest.ModFolderPath + "/" + audioPath); + var audioClip = AudioUtilities.LoadAudio(Path.Combine(mod.ModHelper.Manifest.ModFolderPath, audioPath)); if (audioClip == null) { diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index ef13942d..96e670d7 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -167,7 +167,7 @@ namespace NewHorizons TextTranslation.Get().SetLanguage(TextTranslation.Get().GetLanguage()); } - LoadTranslations(Instance.ModHelper.Manifest.ModFolderPath + "Assets/", this); + LoadTranslations(Path.Combine(Instance.ModHelper.Manifest.ModFolderPath, "Assets/"), this); } public void Awake() diff --git a/NewHorizons/NewHorizonsApi.cs b/NewHorizons/NewHorizonsApi.cs index 4924834f..a4da089f 100644 --- a/NewHorizons/NewHorizonsApi.cs +++ b/NewHorizons/NewHorizonsApi.cs @@ -36,10 +36,10 @@ namespace NewHorizons if (name == null) return; var relativePath = $"temp/{name}.json"; - var fullPath = Main.Instance.ModHelper.Manifest.ModFolderPath + relativePath; - if (!Directory.Exists(Main.Instance.ModHelper.Manifest.ModFolderPath + "temp")) + var fullPath = Path.Combine(Main.Instance.ModHelper.Manifest.ModFolderPath, relativePath); + if (!Directory.Exists(Path.Combine(Main.Instance.ModHelper.Manifest.ModFolderPath, "temp"))) { - Directory.CreateDirectory(Main.Instance.ModHelper.Manifest.ModFolderPath + "temp"); + Directory.CreateDirectory(Path.Combine(Main.Instance.ModHelper.Manifest.ModFolderPath, "temp")); } JsonHelper.SaveJsonObject(fullPath, config); var body = Main.Instance.LoadConfig(Main.Instance, relativePath); @@ -126,7 +126,7 @@ namespace NewHorizons var planet = Main.BodyDict[Main.Instance.CurrentStarSystem].Find((b) => b.Config.name == bodyName); return planet == null ? null - : QueryJson(outType, planet.Mod.ModHelper.Manifest.ModFolderPath + planet.RelativePath, jsonPath); + : QueryJson(outType, Path.Combine(planet.Mod.ModHelper.Manifest.ModFolderPath, planet.RelativePath), jsonPath); } public object QuerySystem(Type outType, string jsonPath) @@ -134,7 +134,7 @@ namespace NewHorizons var system = Main.SystemDict[Main.Instance.CurrentStarSystem]; return system == null ? null - : QueryJson(outType, system.Mod.ModHelper.Manifest.ModFolderPath + system.RelativePath, jsonPath); + : QueryJson(outType, Path.Combine(system.Mod.ModHelper.Manifest.ModFolderPath, system.RelativePath), jsonPath); } public GameObject SpawnObject(GameObject planet, Sector sector, string propToCopyPath, Vector3 position, Vector3 eulerAngles, diff --git a/NewHorizons/OtherMods/VoiceActing/VoiceHandler.cs b/NewHorizons/OtherMods/VoiceActing/VoiceHandler.cs index 65f66b79..ed7142de 100644 --- a/NewHorizons/OtherMods/VoiceActing/VoiceHandler.cs +++ b/NewHorizons/OtherMods/VoiceActing/VoiceHandler.cs @@ -38,10 +38,10 @@ namespace NewHorizons.OtherMods.VoiceActing { foreach (var mod in Main.Instance.GetDependants().Append(Main.Instance)) { - var folder = $"{mod.ModHelper.Manifest.ModFolderPath}voicemod"; + var folder = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, "voicemod"); if (!Directory.Exists(folder)) { // Fallback to PascalCase bc it used to be like that - folder = $"{mod.ModHelper.Manifest.ModFolderPath}VoiceMod"; + folder = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, "VoiceMod"); } if (Directory.Exists(folder)) { diff --git a/NewHorizons/Utility/AssetBundleUtilities.cs b/NewHorizons/Utility/AssetBundleUtilities.cs index 29841acd..263e08bb 100644 --- a/NewHorizons/Utility/AssetBundleUtilities.cs +++ b/NewHorizons/Utility/AssetBundleUtilities.cs @@ -1,4 +1,4 @@ -using OWML.Common; +using OWML.Common; using System; using System.Collections.Generic; using System.IO; @@ -34,7 +34,7 @@ namespace NewHorizons.Utility } else { - var completePath = mod.ModHelper.Manifest.ModFolderPath + assetBundleRelativeDir; + var completePath = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, assetBundleRelativeDir); bundle = AssetBundle.LoadFromFile(completePath); if (bundle == null) { diff --git a/NewHorizons/Utility/AudioUtilities.cs b/NewHorizons/Utility/AudioUtilities.cs index d78e5b53..4a6801b7 100644 --- a/NewHorizons/Utility/AudioUtilities.cs +++ b/NewHorizons/Utility/AudioUtilities.cs @@ -1,6 +1,7 @@ using OWML.Common; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading.Tasks; using UnityEngine; @@ -19,7 +20,7 @@ namespace NewHorizons.Utility { try { - var clip = LoadAudio(mod.ModHelper.Manifest.ModFolderPath + "/" + audio); + var clip = LoadAudio(Path.Combine(mod.ModHelper.Manifest.ModFolderPath, audio)); source._audioLibraryClip = AudioType.None; source._clipArrayIndex = 0; source._clipArrayLength = 0; diff --git a/NewHorizons/Utility/DebugMenu/DebugMenu.cs b/NewHorizons/Utility/DebugMenu/DebugMenu.cs index be19c867..ca54f738 100644 --- a/NewHorizons/Utility/DebugMenu/DebugMenu.cs +++ b/NewHorizons/Utility/DebugMenu/DebugMenu.cs @@ -210,7 +210,7 @@ namespace NewHorizons.Utility.DebugMenu continue; } - loadedConfigFiles[folder + body.RelativePath] = body.Config; + loadedConfigFiles[Path.Combine(folder, body.RelativePath)] = body.Config; submenus.ForEach(submenu => submenu.LoadConfigFile(this, body.Config)); } } @@ -235,9 +235,9 @@ namespace NewHorizons.Utility.DebugMenu try { - var path = loadedMod.ModHelper.Manifest.ModFolderPath + backupFolderName + relativePath; + var path = Path.Combine(loadedMod.ModHelper.Manifest.ModFolderPath, backupFolderName, relativePath); Logger.LogVerbose($"Backing up... {relativePath} to {path}"); - var oldPath = loadedMod.ModHelper.Manifest.ModFolderPath + relativePath; + var oldPath = Path.Combine(loadedMod.ModHelper.Manifest.ModFolderPath, relativePath); var directoryName = Path.GetDirectoryName(path); Directory.CreateDirectory(directoryName); @@ -254,7 +254,7 @@ namespace NewHorizons.Utility.DebugMenu try { Logger.Log($"Saving... {relativePath} to {filePath}"); - var path = loadedMod.ModHelper.Manifest.ModFolderPath + relativePath; + var path = Path.Combine(loadedMod.ModHelper.Manifest.ModFolderPath, relativePath); var directoryName = Path.GetDirectoryName(path); Directory.CreateDirectory(directoryName); diff --git a/NewHorizons/Utility/ImageUtilities.cs b/NewHorizons/Utility/ImageUtilities.cs index cf884938..db33fd99 100644 --- a/NewHorizons/Utility/ImageUtilities.cs +++ b/NewHorizons/Utility/ImageUtilities.cs @@ -17,14 +17,14 @@ namespace NewHorizons.Utility public static bool IsTextureLoaded(IModBehaviour mod, string filename) { - var path = mod.ModHelper.Manifest.ModFolderPath + filename; + var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, filename); return _loadedTextures.ContainsKey(path); } public static Texture2D GetTexture(IModBehaviour mod, string filename, bool useMipmaps = true, bool wrap = false) { // Copied from OWML but without the print statement lol - var path = mod.ModHelper.Manifest.ModFolderPath + filename; + var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, filename); if (_loadedTextures.ContainsKey(path)) { Logger.LogVerbose($"Already loaded image at path: {path}"); @@ -53,7 +53,7 @@ namespace NewHorizons.Utility public static void DeleteTexture(IModBehaviour mod, string filename, Texture2D texture) { - var path = mod.ModHelper.Manifest.ModFolderPath + filename; + var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, filename); if (_loadedTextures.ContainsKey(path)) { if (_loadedTextures[path] == texture) From 1b30c892373532904c787295b8b529a2cd436e0f Mon Sep 17 00:00:00 2001 From: JohnCorby Date: Thu, 15 Sep 2022 16:40:27 -0700 Subject: [PATCH 174/176] typo: flip this case around, we want it to only check if NOT null --- NewHorizons/Patches/HUDPatches.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/Patches/HUDPatches.cs b/NewHorizons/Patches/HUDPatches.cs index fc37e4e0..f58fa342 100644 --- a/NewHorizons/Patches/HUDPatches.cs +++ b/NewHorizons/Patches/HUDPatches.cs @@ -84,7 +84,7 @@ namespace NewHorizons.Patches bool insideEYE = Locator.GetEyeStateManager() != null && Locator.GetEyeStateManager().IsInsideTheEye(); bool insideQM = __instance._quantumMoon != null && (__instance._quantumMoon.IsPlayerInside() || __instance._quantumMoon.IsShipInside()); bool insideRW = Locator.GetRingWorldController() != null && Locator.GetRingWorldController().isPlayerInside; - bool insideIP = Locator.GetCloakFieldController() != null ? true : Locator.GetCloakFieldController().isPlayerInsideCloak == Locator.GetCloakFieldController().isShipInsideCloak; + bool insideIP = Locator.GetCloakFieldController() != null ? Locator.GetCloakFieldController().isPlayerInsideCloak == Locator.GetCloakFieldController().isShipInsideCloak : true; bool insideCloak = Components.CloakSectorController.isPlayerInside == Components.CloakSectorController.isShipInside; bool sameInterference = InterferenceHandler.IsPlayerSameAsShip(); From 68d2034b38e5a6f475de6dae25b971054130696f Mon Sep 17 00:00:00 2001 From: JohnCorby Date: Thu, 15 Sep 2022 17:13:36 -0700 Subject: [PATCH 175/176] also do AssignBrittleHollowReference lol --- NewHorizons/Patches/ProxyBodyPatches.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/NewHorizons/Patches/ProxyBodyPatches.cs b/NewHorizons/Patches/ProxyBodyPatches.cs index 107967ca..23a710df 100644 --- a/NewHorizons/Patches/ProxyBodyPatches.cs +++ b/NewHorizons/Patches/ProxyBodyPatches.cs @@ -25,6 +25,7 @@ namespace NewHorizons.Patches ProxyPlanet_Initialize(__instance); __instance._moon.SetOriginalBodies(Locator.GetAstroObject(AstroObject.Name.VolcanicMoon).transform, Locator.GetAstroObject(AstroObject.Name.BrittleHollow).transform); if (!__instance._fragmentsResolved) __instance.ResolveFragments(); + __instance.AssignBrittleHollowReference(); __instance._blackHoleMaterial = new Material(__instance._blackHoleRenderer.sharedMaterial); __instance._blackHoleRenderer.sharedMaterial = __instance._blackHoleMaterial; } From e6fe77d2c7502a081975c1af8e137b32157bc7dd Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 15 Sep 2022 23:33:12 -0400 Subject: [PATCH 176/176] Update manifest.json --- NewHorizons/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/manifest.json b/NewHorizons/manifest.json index 8403c18f..015d57e4 100644 --- a/NewHorizons/manifest.json +++ b/NewHorizons/manifest.json @@ -4,7 +4,7 @@ "author": "xen, Bwc9876, clay, MegaPiggy, John, Hawkbar, Trifid, Book", "name": "New Horizons", "uniqueName": "xen.NewHorizons", - "version": "1.5.1", + "version": "1.6.0", "owmlVersion": "2.6.0", "dependencies": [ "JohnCorby.VanillaFix", "_nebula.MenuFramework", "xen.CommonCameraUtility" ], "conflicts": [ "Raicuparta.QuantumSpaceBuddies", "PacificEngine.OW_Randomizer" ],