Merge branch 'master' into dev

This commit is contained in:
Ben C 2022-04-03 17:30:07 -04:00
commit 10304765b5
6 changed files with 363 additions and 114 deletions

View File

@ -25,16 +25,6 @@ jobs:
if: ${{ needs.pre_job.outputs.should_skip != 'true' }} if: ${{ needs.pre_job.outputs.should_skip != 'true' }}
runs-on: windows-latest runs-on: windows-latest
steps: steps:
# Replace / with _ in ref name so that it can be used in a filename
- uses: mad9000/actions-find-and-replace-string@2
id: sanitizeRef
with:
source: ${{ github.ref_name }}
find: '/'
replace: '_'
# Get short-sha so that it can be used in a filename
- uses: benjlevesque/short-sha@v1.2
id: short-sha
- uses: actions/checkout@v2 - uses: actions/checkout@v2
@ -52,13 +42,21 @@ jobs:
$data = Get-Content NewHorizons/NewHorizons.csproj.user $data = Get-Content NewHorizons/NewHorizons.csproj.user
$data = $data.Replace("<OuterWildsModsDirectory>`$(AppData)\OuterWildsModManager\OWML\Mods</OuterWildsModsDirectory>", "<OuterWildsModsDirectory>.</OuterWildsModsDirectory>") $data = $data.Replace("<OuterWildsModsDirectory>`$(AppData)\OuterWildsModManager\OWML\Mods</OuterWildsModsDirectory>", "<OuterWildsModsDirectory>.</OuterWildsModsDirectory>")
$data | Out-File -encoding ASCII NewHorizons/NewHorizons.csproj.user $data | Out-File -encoding ASCII NewHorizons/NewHorizons.csproj.user
# Set to Release if we're in master, otherwise keep us in Debug
- name: Set Release
if: github.ref == 'refs/heads/master'
run: echo "BUILD_TYPE=Release" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append
- name: Set Debug
if: github.ref != 'refs/heads/master'
run: echo "BUILD_TYPE=Debug" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append
- uses: actions/setup-dotnet@v1 - uses: actions/setup-dotnet@v1
with: with:
dotnet-version: "5.0.x" dotnet-version: "5.0.x"
- run: dotnet build -c Release -o .\NewHorizons\Bin\Release - run: dotnet build -c $Env:BUILD_TYPE -o .\NewHorizons\Bin\$Env:BUILD_TYPE
- run: tree
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v2
with: with:
name: NewHorizons-${{ steps.sanitizeRef.outputs.value }}-${{ steps.short-sha.outputs.sha }} name: NewHorizons-${{ env.BUILD_TYPE }}
path: .\NewHorizons\Bin\Release path: .\NewHorizons\Bin\${{ env.BUILD_TYPE }}

View File

@ -6,6 +6,7 @@ on:
paths: paths:
- docs/** - docs/**
- NewHorizons/*schema*.json - NewHorizons/*schema*.json
- NewHorizons/*.xsd
pull_request: pull_request:
paths: paths:
@ -23,6 +24,7 @@ on:
env: env:
OUT_DIR: ${{ github.events.inputs.relative_path }} OUT_DIR: ${{ github.events.inputs.relative_path }}
BASE_URL: https://nh.outerwildsmods.com/ BASE_URL: https://nh.outerwildsmods.com/
PIPENV_VENV_IN_PROJECT: enabled
jobs: jobs:
build: build:
@ -36,6 +38,15 @@ jobs:
- if: github.ref == 'refs/heads/master' - if: github.ref == 'refs/heads/master'
run: | run: |
echo "OUT_DIR=/" >> $GITHUB_ENV echo "OUT_DIR=/" >> $GITHUB_ENV
- name: Cache Dependencies
uses: actions/cache@v2
id: cache-dependencies
with:
path: ./.venv
key: ${{ runner.os }}-pip-${{ hashFiles('**/Pipfile.lock') }}
restore-keys: |
${{ runner.os }}-pipenv
- name: Install dependecies - name: Install dependecies
uses: VaultVulp/action-pipenv@v2.0.1 uses: VaultVulp/action-pipenv@v2.0.1

View File

@ -5,8 +5,20 @@
<xs:element name="DialogueTree"> <xs:element name="DialogueTree">
<xs:complexType> <xs:complexType>
<xs:sequence> <xs:sequence>
<xs:element name="NameField" type="xs:string"/> <xs:element name="NameField" type="xs:string">
<xs:element name="DialogueNode" type="DialogueNode" maxOccurs="unbounded"/> <xs:annotation>
<xs:documentation>
The name of the dialogue tree
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="DialogueNode" type="DialogueNode" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>
The different nodes of this dialogue tree
</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence> </xs:sequence>
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>
@ -14,42 +26,121 @@
<!-- Dialogue Node Info --> <!-- Dialogue Node Info -->
<xs:complexType name="DialogueNode"> <xs:complexType name="DialogueNode">
<xs:sequence> <xs:sequence>
<xs:element name="Name" type="xs:string"/> <xs:element name="Name" type="xs:string">
<xs:element name="EntryCondition" type="xs:string" minOccurs="0"/> <xs:annotation>
<xs:element name="Dialogue" type="Dialogue"/> <xs:documentation>
<xs:element name="RevealFacts" type="RevealFacts" minOccurs="0"/> The name of this dialogue node
<xs:element name="DialogueOptionsList" type="DialogueOptionsList" minOccurs="0"/> </xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="EntryCondition" type="xs:string" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>
The condition that needs to be met in order to get to this node
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="DialogueTargetShipLogCondition" type="xs:string" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>
A ship log fact that must be revealed in order to get to this node
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="Dialogue" type="Dialogue">
<xs:annotation>
<xs:documentation>
The dialogue to show to the player
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="RevealFacts" type="RevealFacts" minOccurs="0">
<xs:annotation>
<xs:documentation>
Facts to reveal when the player sees this dialogue node
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="DialogueOptionsList" type="DialogueOptionsList" minOccurs="0">
<xs:annotation>
<xs:documentation>
A list of options to show to the player once the character is one talking
</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence> </xs:sequence>
</xs:complexType> </xs:complexType>
<!-- Dialogue Info --> <!-- Dialogue Info -->
<xs:complexType name="Dialogue"> <xs:complexType name="Dialogue">
<xs:sequence> <xs:sequence>
<xs:element name="Page" type="xs:string" maxOccurs="unbounded"/> <xs:element name="Page" type="xs:string" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>
A page of dialogue to show to the player
</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence> </xs:sequence>
</xs:complexType> </xs:complexType>
<!-- Reveal Facts Info --> <!-- Reveal Facts Info -->
<xs:complexType name="RevealFacts"> <xs:complexType name="RevealFacts">
<xs:sequence> <xs:sequence>
<xs:element name="FactID" type="xs:string" maxOccurs="unbounded"/> <xs:element name="FactID" type="xs:string" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>
The ID of a fact to reveal
</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence> </xs:sequence>
</xs:complexType> </xs:complexType>
<!-- Dialogue Options List Info --> <!-- Dialogue Options List Info -->
<xs:complexType name="DialogueOptionsList"> <xs:complexType name="DialogueOptionsList">
<xs:sequence> <xs:sequence>
<xs:element name="DialogueOption" type="DialogueOption" maxOccurs="unbounded"/> <xs:element name="DialogueOption" type="DialogueOption" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>
Options the player can select from
</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence> </xs:sequence>
</xs:complexType> </xs:complexType>
<!-- Dialogue Option Info --> <!-- Dialogue Option Info -->
<xs:complexType name="DialogueOption"> <xs:complexType name="DialogueOption">
<xs:sequence> <xs:sequence>
<xs:element name="RequiredPersistentCondition" type="xs:string" minOccurs="0"/> <xs:element name="RequiredPersistentCondition" type="xs:string" minOccurs="0">
<xs:element name="CancelledPersistentCondition" type="xs:string" minOccurs="0"/> <xs:annotation>
<xs:element name="Text" type="xs:string"/> <xs:documentation>
<xs:element name="DialogueTarget" type="xs:string"/> Require a prior condition to be met to show this option
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="CancelledPersistentCondition" type="xs:string" minOccurs="0">
<xs:annotation>
<xs:documentation>
Hide this option if a condition has been met
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="Text" type="xs:string">
<xs:annotation>
<xs:documentation>
The text to show for this option
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="DialogueTarget" type="xs:string">
<xs:annotation>
<xs:documentation>
The name of the DialogueNode to go to when this option is selected
</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence> </xs:sequence>
</xs:complexType> </xs:complexType>

View File

@ -507,65 +507,65 @@
"type": "object", "type": "object",
"properties": { "properties": {
"scatter": { "scatter": {
"type": "array", "type": "array",
"items": { "items": {
"type": "object", "type": "object",
"properties": { "properties": {
"count": { "count": {
"type": "integer" "type": "integer"
}, },
"path": { "path": {
"type": "string", "type": "string",
"description": "Either the path in the scene hierarchy of the item to copy or the path to the object in the supplied asset bundle" "description": "Either the path in the scene hierarchy of the item to copy or the path to the object in the supplied asset bundle"
}, },
"assetBundle": { "assetBundle": {
"type": "string", "type": "string",
"description": "Relative filepath to an asset-bundle" "description": "Relative filepath to an asset-bundle"
}, },
"offset": { "offset": {
"$ref": "#/$defs/vector3" "$ref": "#/$defs/vector3"
}, },
"rotation": { "rotation": {
"$ref": "#/$defs/vector3", "$ref": "#/$defs/vector3",
"description": "Euler angle degrees" "description": "Euler angle degrees"
}, },
"scale": { "scale": {
"type": "number", "type": "number",
"default": 1, "default": 1,
"description" : "How many props to scatter around the planet." "description": "How many props to scatter around the planet."
}
} }
} }
}
}, },
"details": { "details": {
"type": "array", "type": "array",
"items": { "items": {
"type": "object", "type": "object",
"properties": { "properties": {
"path": { "path": {
"type": "string", "type": "string",
"description": "Either the path in the scene hierarchy of the item to copy or the path to the object in the supplied asset bundle" "description": "Either the path in the scene hierarchy of the item to copy or the path to the object in the supplied asset bundle"
}, },
"assetBundle": { "assetBundle": {
"type": "string", "type": "string",
"description": "Relative filepath to an asset-bundle" "description": "Relative filepath to an asset-bundle"
}, },
"position": { "position": {
"$ref": "#/$defs/vector3" "$ref": "#/$defs/vector3"
}, },
"rotation": { "rotation": {
"$ref": "#/$defs/vector3", "$ref": "#/$defs/vector3",
"description": "Euler angle degrees" "description": "Euler angle degrees"
}, },
"scale": { "scale": {
"type": "number", "type": "number",
"default": 1 "default": 1
}, },
"alignToNormal": { "alignToNormal": {
"type": "boolean", "type": "boolean",
"description": "Do we override rotation and try to automatically align this object to stand upright on the body's surface?", "description": "Do we override rotation and try to automatically align this object to stand upright on the body's surface?",
"default": false "default": false
} }
}, },
"scale": { "scale": {
"type": "number", "type": "number",
@ -580,20 +580,20 @@
"properties": { "properties": {
"position": { "position": {
"$ref": "#/$defs/vector3", "$ref": "#/$defs/vector3",
"description" : "When you enter into dialogue, you will look here." "description": "When you enter into dialogue, you will look here."
}, },
"radius": { "radius": {
"type": "number", "type": "number",
"default": 0, "default": 0,
"description" : "Radius of the spherical collision volume where you get the \"talk to\" prompt when looking at. If you use a remoteTriggerPosition, this will instead be the size of the volume that will trigger the dialogue when you enter it." "description": "Radius of the spherical collision volume where you get the \"talk to\" prompt when looking at. If you use a remoteTriggerPosition, this will instead be the size of the volume that will trigger the dialogue when you enter it."
}, },
"xmlFile": { "xmlFile": {
"type": "string", "type": "string",
"description" : "Relative path to the xml file defining the dialogue." "description": "Relative path to the xml file defining the dialogue."
}, },
"remoteTriggerPosition": { "remoteTriggerPosition": {
"$ref": "#/$defs/vector3", "$ref": "#/$defs/vector3",
"description" : "Allows you to trigger dialogue from a distance when you walk into an area." "description": "Allows you to trigger dialogue from a distance when you walk into an area."
} }
} }
} }
@ -606,7 +606,7 @@
"properties": { "properties": {
"revealOn": { "revealOn": {
"type": "string", "type": "string",
"enum": ["enter", "observe", "snapshot"], "enum": [ "enter", "observe", "snapshot" ],
"description": "'enter', 'observe', or 'snapshot' what needs to be done to the volume to unlock the facts" "description": "'enter', 'observe', or 'snapshot' what needs to be done to the volume to unlock the facts"
}, },
"reveals": { "reveals": {
@ -658,6 +658,19 @@
} }
} }
} }
},
"geysers": {
"type": "array",
"description": "A set of geysers",
"items": {
"type": "object",
"properties": {
"position": {
"$ref": "#/$defs/vector3",
"description": "The position of this geyser"
}
}
}
} }
} }
}, },

View File

@ -5,8 +5,20 @@
<xs:element name="AstroObjectEntry"> <xs:element name="AstroObjectEntry">
<xs:complexType> <xs:complexType>
<xs:sequence> <xs:sequence>
<xs:element name="ID" type="xs:string"/> <xs:element name="ID" type="xs:string">
<xs:element name="Entry" type="Entry" maxOccurs="unbounded"/> <xs:annotation>
<xs:documentation>
ID of the planet these entries are for
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="Entry" type="Entry" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>
A set of entries that belong to this planet
</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence> </xs:sequence>
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>
@ -17,27 +29,117 @@
<!-- Entry Info --> <!-- Entry Info -->
<xs:complexType name="Entry"> <xs:complexType name="Entry">
<xs:sequence> <xs:sequence>
<xs:element name="ID" type="xs:string"/> <xs:element name="ID" type="xs:string">
<xs:element name="Name" type="xs:string"/> <xs:annotation>
<xs:element name="Curiosity" type="xs:string" minOccurs="0" maxOccurs="unbounded"/> <xs:documentation>
<xs:element name="IsCuriosity" type="empty" minOccurs="0" maxOccurs="unbounded"/> The ID of this entry
<xs:element name="IgnoreMoreToExplore" type="empty" minOccurs="0"/> </xs:documentation>
<xs:element name="IgnoreMoreToExploreCondition" type="xs:string" minOccurs="0"/> </xs:annotation>
<xs:element name="AltPhotoCondition" type="xs:string" minOccurs="0"/> </xs:element>
<xs:element name="RumorFact" type="RumorFact" minOccurs="0" maxOccurs="unbounded"/> <xs:element name="Name" type="xs:string">
<xs:element name="ExploreFact" type="ExploreFact" minOccurs="0" maxOccurs="unbounded"/> <xs:annotation>
<xs:element name="Entry" type="Entry" minOccurs="0" maxOccurs="unbounded"/> <xs:documentation>
Name of this entry
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="Curiosity" type="xs:string" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>
The curiosity this entry belongs to
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="IsCuriosity" type="empty" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>
Whether this entry is a curiosity
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="IgnoreMoreToExplore" type="empty" minOccurs="0">
<xs:annotation>
<xs:documentation>
Whether to hide the "More To Explore" text on this entry
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="IgnoreMoreToExploreCondition" type="xs:string" minOccurs="0">
<xs:annotation>
<xs:documentation>
Ignore more to explore if a fact is known
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="AltPhotoCondition" type="xs:string" minOccurs="0">
<xs:annotation>
<xs:documentation>
If this fact is revealed, show the Alt picture
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="RumorFact" type="RumorFact" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>
Rumor facts for this entry
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="ExploreFact" type="ExploreFact" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>
Explore facts for this entry
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="Entry" type="Entry" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>
Child entires within this entry
</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence> </xs:sequence>
</xs:complexType> </xs:complexType>
<!-- Rumor Fact Info --> <!-- Rumor Fact Info -->
<xs:complexType name="RumorFact"> <xs:complexType name="RumorFact">
<xs:sequence> <xs:sequence>
<xs:element name="ID" type="xs:string"/> <xs:element name="ID" type="xs:string">
<xs:element name="SourceID" type="xs:string" minOccurs="0"/> <xs:annotation>
<xs:element name="RumorName" type="xs:string" minOccurs="0"/> <xs:documentation>
<xs:element name="RumorNamePriority" type="xs:int" minOccurs="0"/> The ID of this rumor fact
<xs:element name="IgnoreMoreToExplore" type="empty" minOccurs="0"/> </xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="SourceID" type="xs:string" minOccurs="0">
<xs:annotation>
<xs:documentation>
The source of this rumor, this draws a line in detective mode
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="RumorName" type="xs:string" minOccurs="0">
<xs:annotation>
<xs:documentation>
Displays on the card in detective mode if no ExploreFacts have been revealed on the parent entry
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="RumorNamePriority" type="xs:int" minOccurs="0">
<xs:annotation>
<xs:documentation>
Priority over other RumorFacts to appear as the entry card's title
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="IgnoreMoreToExplore" type="empty" minOccurs="0">
<xs:annotation>
<xs:documentation>
Whether to hide the "More to explore" on this rumor fact
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:group ref="TextData"/> <xs:group ref="TextData"/>
</xs:sequence> </xs:sequence>
</xs:complexType> </xs:complexType>
@ -45,8 +147,20 @@
<!-- Explore Fact Info --> <!-- Explore Fact Info -->
<xs:complexType name="ExploreFact"> <xs:complexType name="ExploreFact">
<xs:sequence> <xs:sequence>
<xs:element name="ID" type="xs:string"/> <xs:element name="ID" type="xs:string">
<xs:element name="IgnoreMoreToExplore" type="empty" minOccurs="0"/> <xs:annotation>
<xs:documentation>
The ID of this explore fact
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="IgnoreMoreToExplore" type="empty" minOccurs="0">
<xs:annotation>
<xs:documentation>
Whether to hide the "More to explore" text for this fact
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:group ref="TextData"/> <xs:group ref="TextData"/>
</xs:sequence> </xs:sequence>
</xs:complexType> </xs:complexType>
@ -54,12 +168,35 @@
<!-- Text Data Group --> <!-- Text Data Group -->
<xs:group name="TextData"> <xs:group name="TextData">
<xs:sequence> <xs:sequence>
<xs:element name="Text" type="xs:string"/> <xs:element name="Text" type="xs:string">
<xs:annotation>
<xs:documentation>
The text content for this fact
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="AltText" minOccurs="0"> <xs:element name="AltText" minOccurs="0">
<xs:annotation>
<xs:documentation>
Display alt-text given a certain fact is revealed
</xs:documentation>
</xs:annotation>
<xs:complexType> <xs:complexType>
<xs:sequence> <xs:sequence>
<xs:element name="Text" type="xs:string"/> <xs:element name="Text" type="xs:string">
<xs:element name="Condition" type="xs:string"/> <xs:annotation>
<xs:documentation>
The text to display if the condition is met
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="Condition" type="xs:string">
<xs:annotation>
<xs:documentation>
The condition that needs to be fulfilled to have the alt text be displayed
</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence> </xs:sequence>
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>

View File

@ -15,6 +15,8 @@ A custom world creation tool for Outer Wilds.
You can view the addons creators have made (or upload one yourself) [here](https://outerwildsmods.com/custom-worlds)! You can view the addons creators have made (or upload one yourself) [here](https://outerwildsmods.com/custom-worlds)!
If you want to see examples of what NH can do check out the [examples add-on](https://github.com/xen-42/ow-new-horizons-examples) or [real solar system add-on](https://github.com/xen-42/outer-wilds-real-solar-system).
Check the ship's log for how to use your warp drive to travel between star systems! Check the ship's log for how to use your warp drive to travel between star systems!
<!-- TOC --> <!-- TOC -->
@ -36,7 +38,7 @@ Check the ship's log for how to use your warp drive to travel between star syste
- Separate solar system scenes accessible via wormhole OR via the ship's new warp drive feature accessible via the ship's log - Separate solar system scenes accessible via wormhole OR via the ship's new warp drive feature accessible via the ship's log
- Remove existing planets - Remove existing planets
- Create basic planets from heightmaps/texturemaps - Create basic planets from heightmaps/texturemaps
- Stars, comets, asteroid belts, satellites - Stars, comets, asteroid belts, satellites, geysers, cloak fields
- Binary orbits - Binary orbits
- Signalscope signals and custom frequencies - Signalscope signals and custom frequencies
- Surface scatter: rocks, trees, etc, using in-game models, or custom ones - Surface scatter: rocks, trees, etc, using in-game models, or custom ones
@ -52,14 +54,11 @@ Check the ship's log for how to use your warp drive to travel between star syste
- Implement all planet features: - Implement all planet features:
- Tornados + floating islands - Tornados + floating islands
- Let any star go supernova - Let any star go supernova
- Geysers
- Meteors - Meteors
- Pocket dimensions - Pocket dimensions
- Timed position/velocity changes - Timed position/velocity changes
- Implement custom Nomai scrolls - Implement custom Nomai scrolls
- Implement custom translatable writing - Implement custom translatable writing
- Destroy planets that fall into a star
- Add cloaking volumes
## Contact ## Contact
Join the [Outer Wilds Modding Discord](https://discord.gg/MvbCbBz6Q6) if you have any questions or just want to chat about modding! Theres a New Horizons category there dedicated to discussion of this mod. Join the [Outer Wilds Modding Discord](https://discord.gg/MvbCbBz6Q6) if you have any questions or just want to chat about modding! Theres a New Horizons category there dedicated to discussion of this mod.