mirror of
https://github.com/Outer-Wilds-New-Horizons/new-horizons.git
synced 2025-12-11 20:15:44 +01:00
Merge branch 'main' into system-name
This commit is contained in:
commit
42f898a3fa
4
.github/FUNDING.yml
vendored
4
.github/FUNDING.yml
vendored
@ -1,2 +1,2 @@
|
||||
patreon: ownh
|
||||
custom: ["https://paypal.me/xen42"]
|
||||
patreon: xen42
|
||||
custom: ["https://paypal.me/xen42"]
|
||||
|
||||
90
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
90
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -1,45 +1,45 @@
|
||||
name: Bug Report
|
||||
description: File a bug report
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: What Happened?
|
||||
description: Please describe what happened
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: What was supposed to happen?
|
||||
description: If applicable, describe what should have happened instead.
|
||||
validations:
|
||||
required: false
|
||||
- type: dropdown
|
||||
id: platform
|
||||
attributes:
|
||||
label: Platform
|
||||
description: Please provide which platform you were playing on when you encountered this bug.
|
||||
options:
|
||||
- Steam
|
||||
- Epic Games
|
||||
- Xbox Game Pass
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: mods
|
||||
attributes:
|
||||
label: Mods
|
||||
description: Please define which mods you had enabled when the problem occurred.
|
||||
render: Markdown
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Logs
|
||||
description: If you can, try to locate the point in the logs where the error occurred and paste the message displayed here.
|
||||
render: Shell
|
||||
validations:
|
||||
required: false
|
||||
name: Bug Report
|
||||
description: File a bug report
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: What Happened?
|
||||
description: Please describe what happened
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: What was supposed to happen?
|
||||
description: If applicable, describe what should have happened instead.
|
||||
validations:
|
||||
required: false
|
||||
- type: dropdown
|
||||
id: platform
|
||||
attributes:
|
||||
label: Platform
|
||||
description: Please provide which platform you were playing on when you encountered this bug.
|
||||
options:
|
||||
- Steam
|
||||
- Epic Games
|
||||
- Xbox Game Pass
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: mods
|
||||
attributes:
|
||||
label: Mods
|
||||
description: Please define which mods you had enabled when the problem occurred.
|
||||
render: Markdown
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Logs
|
||||
description: If you can, try to locate the point in the logs where the error occurred and paste the message displayed here.
|
||||
render: Shell
|
||||
validations:
|
||||
required: false
|
||||
|
||||
10
.github/ISSUE_TEMPLATE/config.yml
vendored
10
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,5 +1,5 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: "Need Help with Creating an Addon?"
|
||||
url: "https://discord.gg/wusTQYbYTc"
|
||||
about: "Join our discord and look for #nh-addon-discussion"
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: "Need Help with Creating an Addon?"
|
||||
url: "https://discord.gg/wusTQYbYTc"
|
||||
about: "Join our discord and look for #nh-addon-discussion"
|
||||
|
||||
28
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
28
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@ -1,14 +1,14 @@
|
||||
name: Feature Request
|
||||
description: Request a feature you'd like to see in a later release
|
||||
labels: ["feature"]
|
||||
body:
|
||||
- type: textarea
|
||||
id: feature
|
||||
attributes:
|
||||
label: Feature
|
||||
description: Describe the feature you'd like to see in New Horizons.
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Context
|
||||
description: Provide any additional context such as screenshots or diagrams here.
|
||||
name: Feature Request
|
||||
description: Request a feature you'd like to see in a later release
|
||||
labels: ["feature"]
|
||||
body:
|
||||
- type: textarea
|
||||
id: feature
|
||||
attributes:
|
||||
label: Feature
|
||||
description: Describe the feature you'd like to see in New Horizons.
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Context
|
||||
description: Provide any additional context such as screenshots or diagrams here.
|
||||
|
||||
38
.github/pull_request_template.md
vendored
38
.github/pull_request_template.md
vendored
@ -1,15 +1,23 @@
|
||||
<!-- A new module or something else important -->
|
||||
## Major features
|
||||
-
|
||||
|
||||
<!-- A new parameter added to a module, or API feature -->
|
||||
## Minor features
|
||||
-
|
||||
|
||||
<!-- Some improvement that requires no action on the part of add-on creators i.e., improved star graphics -->
|
||||
## Improvements
|
||||
-
|
||||
|
||||
<!-- Be sure to reference the existing issue if it exists -->
|
||||
## Bug fixes
|
||||
-
|
||||
<!-- A new module or something else important -->
|
||||
|
||||
## Major features
|
||||
|
||||
-
|
||||
|
||||
<!-- A new parameter added to a module, or API feature -->
|
||||
|
||||
## Minor features
|
||||
|
||||
-
|
||||
|
||||
<!-- Some improvement that requires no action on the part of add-on creators i.e., improved star graphics -->
|
||||
|
||||
## Improvements
|
||||
|
||||
-
|
||||
|
||||
<!-- Be sure to reference the existing issue if it exists -->
|
||||
|
||||
## Bug fixes
|
||||
|
||||
-
|
||||
|
||||
142
.github/workflows/build.yaml
vendored
142
.github/workflows/build.yaml
vendored
@ -1,73 +1,69 @@
|
||||
# Usage:
|
||||
#
|
||||
# Build:
|
||||
# uses: "./.github/workflows/build"
|
||||
# with:
|
||||
# build_type: Debug
|
||||
#
|
||||
#
|
||||
|
||||
name: Build
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
build_type:
|
||||
description: 'Build type to pass to `dotnet`, should be either "Debug" or "Release"'
|
||||
required: false
|
||||
default: "Debug"
|
||||
type: string
|
||||
outputs:
|
||||
schemas_changed:
|
||||
description: 'Have the schemas been updated?'
|
||||
value: ${{ jobs.Build.outputs.schemas_changed }}
|
||||
|
||||
|
||||
|
||||
jobs:
|
||||
Build:
|
||||
runs-on: windows-latest
|
||||
outputs:
|
||||
schemas_changed: ${{ steps.changed_files.outputs.files_changed }}
|
||||
steps:
|
||||
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
|
||||
# Disable Strong Name Verification to let us pull a switch-a-roo
|
||||
- name: Disable strong name validation
|
||||
run: "C:\\\"Program Files (x86)\"\\\"Microsoft SDKs\"\\Windows\\v10.0A\\bin\\\"NETFX 4.8 Tools\"\\x64\\sn.exe -Vr *"
|
||||
|
||||
- name: Remove .csproj.user
|
||||
run: "rm .\\NewHorizons\\NewHorizons.csproj.user"
|
||||
|
||||
- name: Build Project
|
||||
run: dotnet build -c ${{ inputs.build_type }}
|
||||
|
||||
- name: Generate Schemas
|
||||
run: .\SchemaExporter\bin\${{ inputs.build_type }}\SchemaExporter.exe
|
||||
|
||||
- name: Delete XML documentation
|
||||
run: rm .\NewHorizons\bin\${{ inputs.build_type }}\NewHorizons.xml
|
||||
|
||||
- name: Upload Mod Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: xen.NewHorizons.${{ inputs.build_type }}
|
||||
path: .\NewHorizons\bin\${{ inputs.build_type }}
|
||||
|
||||
- name: Upload Schemas Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: NewHorizons-Schemas-${{ inputs.build_type }}
|
||||
path: .\NewHorizons\Schemas
|
||||
|
||||
- name: Verify Changed Schemas
|
||||
uses: tj-actions/verify-changed-files@v12
|
||||
id: changed_files
|
||||
with:
|
||||
files: NewHorizons/Schemas/**
|
||||
|
||||
# Usage:
|
||||
#
|
||||
# Build:
|
||||
# uses: "./.github/workflows/build"
|
||||
# with:
|
||||
# build_type: Debug
|
||||
#
|
||||
#
|
||||
|
||||
name: Build
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
build_type:
|
||||
description: 'Build type to pass to `dotnet`, should be either "Debug" or "Release"'
|
||||
required: false
|
||||
default: "Debug"
|
||||
type: string
|
||||
outputs:
|
||||
schemas_changed:
|
||||
description: "Have the schemas been updated?"
|
||||
value: ${{ jobs.Build.outputs.schemas_changed }}
|
||||
|
||||
jobs:
|
||||
Build:
|
||||
runs-on: windows-latest
|
||||
outputs:
|
||||
schemas_changed: ${{ steps.changed_files.outputs.files_changed }}
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
|
||||
# Disable Strong Name Verification to let us pull a switch-a-roo
|
||||
- name: Disable strong name validation
|
||||
run: "C:\\\"Program Files (x86)\"\\\"Microsoft SDKs\"\\Windows\\v10.0A\\bin\\\"NETFX 4.8 Tools\"\\x64\\sn.exe -Vr *"
|
||||
|
||||
- name: Remove .csproj.user
|
||||
run: "rm .\\NewHorizons\\NewHorizons.csproj.user"
|
||||
|
||||
- name: Build Project
|
||||
run: dotnet build -c ${{ inputs.build_type }}
|
||||
|
||||
- name: Generate Schemas
|
||||
run: .\SchemaExporter\bin\${{ inputs.build_type }}\SchemaExporter.exe
|
||||
|
||||
- name: Delete XML documentation
|
||||
run: rm .\NewHorizons\bin\${{ inputs.build_type }}\NewHorizons.xml
|
||||
|
||||
- name: Upload Mod Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: xen.NewHorizons.${{ inputs.build_type }}
|
||||
path: .\NewHorizons\bin\${{ inputs.build_type }}
|
||||
|
||||
- name: Upload Schemas Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: NewHorizons-Schemas-${{ inputs.build_type }}
|
||||
path: .\NewHorizons\Schemas
|
||||
|
||||
- name: Verify Changed Schemas
|
||||
uses: tj-actions/verify-changed-files@v20
|
||||
id: changed_files
|
||||
with:
|
||||
files: NewHorizons/Schemas/**
|
||||
|
||||
6
.github/workflows/debug_build.yml
vendored
6
.github/workflows/debug_build.yml
vendored
@ -13,14 +13,14 @@ on:
|
||||
|
||||
jobs:
|
||||
Build:
|
||||
uses: './.github/workflows/build.yaml'
|
||||
uses: "./.github/workflows/build.yaml"
|
||||
with:
|
||||
build_type: Debug
|
||||
Update_Schemas:
|
||||
name: 'Update Schemas'
|
||||
name: "Update Schemas"
|
||||
needs: Build
|
||||
if: ${{ needs.Build.outputs.schemas_changed == 'true' }}
|
||||
uses: './.github/workflows/update_schemas.yml'
|
||||
uses: "./.github/workflows/update_schemas.yml"
|
||||
with:
|
||||
artifact_name: NewHorizons-Schemas-Debug
|
||||
secrets: inherit
|
||||
|
||||
147
.github/workflows/docs_build.yml
vendored
147
.github/workflows/docs_build.yml
vendored
@ -1,89 +1,58 @@
|
||||
name: Build Docs
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
schemas_artifact:
|
||||
description: "Name of the artifact that has updated schemas, set to `null` to not update"
|
||||
default: 'null'
|
||||
required: false
|
||||
type: string
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- docs/**
|
||||
- NewHorizons/Schemas/*
|
||||
|
||||
env:
|
||||
URL_PREFIX: '/'
|
||||
PIPENV_VENV_IN_PROJECT: 1
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
concurrency:
|
||||
group: "pages"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build Docs
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- run: mkdir ./.venv
|
||||
|
||||
- run: cp -r docs/** .
|
||||
|
||||
- name: Cache Dependencies
|
||||
uses: actions/cache@v3
|
||||
id: cache-dependencies
|
||||
with:
|
||||
path: ./.venv
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/Pipfile.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pipenv
|
||||
|
||||
- name: Install dependecies
|
||||
uses: VaultVulp/action-pipenv@v2.0.1
|
||||
with:
|
||||
command: install --dev
|
||||
|
||||
- name: Download Schemas
|
||||
if: ${{ inputs.schemas_artifact != 'null' }}
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ${{ inputs.schemas_artifact }}
|
||||
path: NewHorizons/Schemas
|
||||
|
||||
- name: Copy Schemas
|
||||
run: cp -rf NewHorizons/Schemas content/pages/
|
||||
|
||||
- name: Build Site
|
||||
uses: VaultVulp/action-pipenv@v2.0.1
|
||||
with:
|
||||
command: run menagerie generate
|
||||
|
||||
- name: Upload Artifact
|
||||
if: success() && github.ref == 'refs/heads/main'
|
||||
uses: actions/upload-pages-artifact@v1
|
||||
with:
|
||||
path: out/
|
||||
|
||||
deploy:
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
name: Deploy Docs
|
||||
needs: build
|
||||
if: github.ref == 'refs/heads/main'
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v1
|
||||
|
||||
name: Build Docs
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
schemas_artifact:
|
||||
description: "Name of the artifact that has updated schemas, set to `null` to not update"
|
||||
default: "null"
|
||||
required: false
|
||||
type: string
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- docs/**
|
||||
- NewHorizons/Schemas/*
|
||||
- .github/workflows/docs_build.yml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
concurrency:
|
||||
group: "pages"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Download Schemas
|
||||
if: ${{ inputs.schemas_artifact != 'null' }}
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ${{ inputs.schemas_artifact }}
|
||||
path: NewHorizons/Schemas
|
||||
- name: Move Stuff Becuase PNPM Can't FUCKING INSTALL IF YOU DONT HAVE PACKAGE JSON IN THE ROOT
|
||||
run: |
|
||||
cp docs/package.json .
|
||||
cp docs/pnpm-lock.yaml .
|
||||
- name: Build Site
|
||||
uses: withastro/action@v2
|
||||
with:
|
||||
path: ./docs
|
||||
package-manager: pnpm@latest
|
||||
|
||||
deploy:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
|
||||
10
.github/workflows/release_build.yml
vendored
10
.github/workflows/release_build.yml
vendored
@ -28,13 +28,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: "actions/checkout@v3"
|
||||
uses: "actions/checkout@v4"
|
||||
- name: Read Manifest
|
||||
id: read-manifest
|
||||
uses: notiz-dev/github-action-json-property@release
|
||||
with:
|
||||
path: './NewHorizons/manifest.json'
|
||||
prop_path: 'version'
|
||||
with:
|
||||
path: "./NewHorizons/manifest.json"
|
||||
prop_path: "version"
|
||||
- name: Print version numbers
|
||||
run: |
|
||||
echo "Manifest version: $MANIFEST_VERSION"
|
||||
@ -66,7 +66,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download Asset
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: xen.NewHorizons.Release
|
||||
path: xen.NewHorizons
|
||||
|
||||
46
.github/workflows/update_release.yml
vendored
46
.github/workflows/update_release.yml
vendored
@ -1,29 +1,29 @@
|
||||
name: Create/Update Release
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
types:
|
||||
- ready_for_review
|
||||
- edited
|
||||
- labeled
|
||||
pull_request:
|
||||
branches: [main]
|
||||
types:
|
||||
- ready_for_review
|
||||
- edited
|
||||
- labeled
|
||||
|
||||
jobs:
|
||||
Update_Release:
|
||||
name: Create/Update Release
|
||||
if: contains(github.event.pull_request.labels.*.name, 'update-pr')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Create/Update Release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
allowUpdates: true
|
||||
name: Version ${{ github.event.pull_request.title }}
|
||||
tag: v${{ github.event.pull_request.title }}
|
||||
commit: main
|
||||
body: |
|
||||
${{ github.event.pull_request.body }}
|
||||
Update_Release:
|
||||
name: Create/Update Release
|
||||
if: contains(github.event.pull_request.labels.*.name, 'update-pr')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Create/Update Release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
allowUpdates: true
|
||||
name: Version ${{ github.event.pull_request.title }}
|
||||
tag: v${{ github.event.pull_request.title }}
|
||||
commit: main
|
||||
body: |
|
||||
${{ github.event.pull_request.body }}
|
||||
|
||||
**Generated From PR: ${{ github.event.pull_request.html_url }}**
|
||||
draft: true
|
||||
prerelease: false
|
||||
**Generated From PR: ${{ github.event.pull_request.html_url }}**
|
||||
draft: true
|
||||
prerelease: false
|
||||
|
||||
12
.github/workflows/update_schemas.yml
vendored
12
.github/workflows/update_schemas.yml
vendored
@ -5,10 +5,10 @@ on:
|
||||
inputs:
|
||||
artifact_name:
|
||||
required: true
|
||||
description: 'Name of the artifact to download and check against'
|
||||
description: "Name of the artifact to download and check against"
|
||||
type: string
|
||||
|
||||
# Prevents schemas from trying to update on old commits
|
||||
# Prevents schemas from trying to update on old commits
|
||||
concurrency:
|
||||
group: "schemas-${{ github.ref }}"
|
||||
cancel-in-progress: true
|
||||
@ -18,23 +18,23 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.SCHEMAS_TOKEN }}
|
||||
|
||||
- name: Download Artifact
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ${{ inputs.artifact_name }}
|
||||
path: NewHorizons/Schemas/
|
||||
|
||||
|
||||
- name: Commit Schemas
|
||||
run: |
|
||||
git config --local user.email "bwc9876@gmail.com"
|
||||
git config --local user.name "Ben C"
|
||||
git add NewHorizons/Schemas/**
|
||||
git commit -m "Updated Schemas"
|
||||
|
||||
|
||||
- name: Push Schemas
|
||||
uses: ad-m/github-push-action@master
|
||||
with:
|
||||
|
||||
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@ -1,5 +0,0 @@
|
||||
{
|
||||
"yaml.schemas": {
|
||||
"https://json.schemastore.org/github-workflow.json": "vscode-vfs://github%2B7b2276223a312c22726566223a7b2274797065223a342c226964223a226465766f70732f6e65772d776f726b666c6f7773227d7d/xen-42/outer-wilds-new-horizons/.github/workflows/build.yaml"
|
||||
}
|
||||
}
|
||||
@ -1,128 +1,128 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
xen#5498.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
- Demonstrating empathy and kindness toward other people
|
||||
- Being respectful of differing opinions, viewpoints, and experiences
|
||||
- Giving and gracefully accepting constructive feedback
|
||||
- Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
- Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
- The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
xen#5498.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
|
||||
136
CONTRIBUTING.md
136
CONTRIBUTING.md
@ -1,66 +1,70 @@
|
||||
# Contributing
|
||||
|
||||
Thank you for choosing to contribute to New Horizons!
|
||||
|
||||
## Getting Started
|
||||
|
||||
To get started, [fork this repository](https://github.com/xen-42/outer-wilds-new-horizons/fork).
|
||||
Once you have a fork created, create a new branch off of `dev` where you will make your changes.
|
||||
Then, clone your fork and checkout your new branch.
|
||||
|
||||
## Building
|
||||
|
||||
To build a development release of New Horizons, use the `Debug` build target.
|
||||
This will automatically build to your mods directory in OWML (so long as it's in `%APPDATA%/OuterWildsModManager/OWML`).
|
||||
|
||||
### Getting Line Numbers
|
||||
|
||||
To save yourself the pain of decoding where in a function an error occured, you can [download this dll file](https://cdn.discordapp.com/attachments/929787137895854100/936860223983976448/mono-2.0-bdwgc.dll) and place it in `MonoBleedingEdge/EmbedRuntime` of the game's folder.
|
||||
Then (so long as you build targeting `Debug`), line numbers will be shown in any error that comes from New Horizons
|
||||
|
||||
## Updating The Schema
|
||||
|
||||
When you add fields to config classes, please document them using XML documentation so that our action can generate a proper schema.
|
||||
|
||||
```cs
|
||||
/// <summary>
|
||||
/// This is my new field!
|
||||
/// </summary>
|
||||
public string myField;
|
||||
```
|
||||
|
||||
You can also use `Range` (from `System.ComponentModel.DataAnnotations` NOT Unity), and `DefaultValue`:
|
||||
|
||||
```cs
|
||||
/// <summary>
|
||||
/// This is my new field!
|
||||
/// </summary>
|
||||
[Range(0, 100)]
|
||||
[DefaultValue(50)]
|
||||
public int myField;
|
||||
```
|
||||
|
||||
### Enums
|
||||
|
||||
You can also setup enums in the config classes:
|
||||
|
||||
```cs
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum MyCoolEnum {
|
||||
[EnumMember(Value = @"value1")]
|
||||
Value1 = 0,
|
||||
[EnumMember(Value = @"value2")]
|
||||
Value2 = 1,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// My enum field
|
||||
/// </summary>
|
||||
public MyCoolEnum enumField;
|
||||
```
|
||||
|
||||
These will automatically be converted from strings to the proper enum type.
|
||||
|
||||
## Contributing to Documentation
|
||||
|
||||
If you wish to contribute to the documentation, take a look at [Setup.md](docs/Setup.md) in the docs folder.
|
||||
# Contributing
|
||||
|
||||
Thank you for choosing to contribute to New Horizons!
|
||||
|
||||
## Getting Started
|
||||
|
||||
To get started, [fork this repository](https://github.com/xen-42/outer-wilds-new-horizons/fork).
|
||||
Once you have a fork created, create a new branch off of `dev` where you will make your changes.
|
||||
Then, clone your fork and checkout your new branch.
|
||||
|
||||
## Building
|
||||
|
||||
To build a development release of New Horizons, use the `Debug` build target.
|
||||
This will automatically build to your mods directory in OWML (so long as it's in `%APPDATA%/OuterWildsModManager/OWML`).
|
||||
|
||||
### Getting Line Numbers
|
||||
|
||||
To save yourself the pain of decoding where in a function an error occured, you can [download this dll file](https://cdn.discordapp.com/attachments/929787137895854100/936860223983976448/mono-2.0-bdwgc.dll) and place it in `MonoBleedingEdge/EmbedRuntime` of the game's folder.
|
||||
Then (so long as you build targeting `Debug`), line numbers will be shown in any error that comes from New Horizons
|
||||
|
||||
## Provide examples
|
||||
|
||||
When adding a new feature, include a complete set of planet config files that will sufficiently demonstrate the functionality of the feature/bug fix/improvement. This way reviewers can just copy paste these files into the New Horizons planets folder.
|
||||
|
||||
## Updating The Schema
|
||||
|
||||
When you add fields to config classes, please document them using XML documentation so that our action can generate a proper schema.
|
||||
|
||||
```cs
|
||||
/// <summary>
|
||||
/// This is my new field!
|
||||
/// </summary>
|
||||
public string myField;
|
||||
```
|
||||
|
||||
You can also use `Range` (from `System.ComponentModel.DataAnnotations` NOT Unity), and `DefaultValue`:
|
||||
|
||||
```cs
|
||||
/// <summary>
|
||||
/// This is my new field!
|
||||
/// </summary>
|
||||
[Range(0, 100)]
|
||||
[DefaultValue(50)]
|
||||
public int myField;
|
||||
```
|
||||
|
||||
### Enums
|
||||
|
||||
You can also setup enums in the config classes:
|
||||
|
||||
```cs
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum MyCoolEnum {
|
||||
[EnumMember(Value = @"value1")]
|
||||
Value1 = 0,
|
||||
[EnumMember(Value = @"value2")]
|
||||
Value2 = 1,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// My enum field
|
||||
/// </summary>
|
||||
public MyCoolEnum enumField;
|
||||
```
|
||||
|
||||
These will automatically be converted from strings to the proper enum type.
|
||||
|
||||
## Contributing to Documentation
|
||||
|
||||
If you wish to contribute to the documentation, take a look at [CONTRIBUTING.md](docs/CONTRIBUTING.md) in the docs folder.
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 127 KiB After Width: | Height: | Size: 128 KiB |
@ -1,17 +1,28 @@
|
||||
{
|
||||
"name" : "Ship",
|
||||
"$schema": "https://raw.githubusercontent.com/xen-42/outer-wilds-new-horizons/master/NewHorizons/body_schema.json",
|
||||
"Props" :
|
||||
{
|
||||
"dialogue": [
|
||||
{
|
||||
"position":{"x": -0.3071011, "y": 2.741472, "z": -4.005298},
|
||||
"radius": 0,
|
||||
"remoteTriggerRadius": 1,
|
||||
"xmlFile":"Assets/WarpDriveDialogue.xml",
|
||||
"remoteTriggerPosition": {"x": -0.05656214, "y": 0.5362684, "z": 0.5467669},
|
||||
"blockAfterPersistentCondition" : "KnowsAboutWarpDrive"
|
||||
}
|
||||
]
|
||||
}
|
||||
"name": "Ship",
|
||||
"$schema": "https://raw.githubusercontent.com/Outer-Wilds-New-Horizons/new-horizons/main/NewHorizons/Schemas/body_schema.json",
|
||||
"Props": {
|
||||
"dialogue": [
|
||||
{
|
||||
"position": {
|
||||
"x": -0.3071011,
|
||||
"y": 2.741472,
|
||||
"z": -4.005298
|
||||
},
|
||||
"radius": 0,
|
||||
"rename": "WarpDriveDialogue",
|
||||
"xmlFile": "Assets/WarpDriveDialogue.xml",
|
||||
"blockAfterPersistentCondition": "KnowsAboutWarpDrive",
|
||||
"remoteTrigger": {
|
||||
"radius": 1,
|
||||
"position": {
|
||||
"x": -0.05656214,
|
||||
"y": 0.5362684,
|
||||
"z": 0.5467669
|
||||
},
|
||||
"rename": "WarpDriveRemoteTrigger"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -10,7 +10,7 @@
|
||||
"Trifid#Tester\n#Programmer",
|
||||
"Nageld#Programmer",
|
||||
"Ernesto#Fish",
|
||||
"With help from#Raicuparta\n#dgarroDC\n#jtsalomo\n#and the modding community",
|
||||
"With help from#Raicuparta\n#dgarroDC\n#jtsalomo\n#coderCleric\n#TRSasasusu\n#and the modding community",
|
||||
" ",
|
||||
"Based off Marshmallow made by#_nebula",
|
||||
"With help from#AmazingAlek\n#Raicuparta\n#and the Outer Wilds discord server",
|
||||
|
||||
Binary file not shown.
@ -1,12 +1,12 @@
|
||||
ManifestFileVersion: 0
|
||||
CRC: 3537427957
|
||||
CRC: 2245901288
|
||||
Hashes:
|
||||
AssetFileHash:
|
||||
serializedVersion: 2
|
||||
Hash: c4d8f41970054074bb375ac5cbe82855
|
||||
Hash: e765e5fc418c1ed69586a3826e0cdea3
|
||||
TypeTreeHash:
|
||||
serializedVersion: 2
|
||||
Hash: de71b9c55befb829b1640ea21774b932
|
||||
Hash: 65942a71d50cdc9f2387a8fa9383a3f8
|
||||
HashAppended: 0
|
||||
ClassTypes:
|
||||
- Class: 1
|
||||
@ -17,6 +17,8 @@ ClassTypes:
|
||||
Script: {instanceID: 0}
|
||||
- Class: 23
|
||||
Script: {instanceID: 0}
|
||||
- Class: 28
|
||||
Script: {instanceID: 0}
|
||||
- Class: 33
|
||||
Script: {instanceID: 0}
|
||||
- Class: 43
|
||||
@ -39,6 +41,8 @@ ClassTypes:
|
||||
Script: {instanceID: 0}
|
||||
- Class: 114
|
||||
Script: {fileID: 11500000, guid: 70edf1000ebf31e4eb3ab4e289a345c0, type: 3}
|
||||
- Class: 114
|
||||
Script: {fileID: 11500000, guid: 86d5ae109bbc920409997135e88f1755, type: 3}
|
||||
- Class: 114
|
||||
Script: {fileID: 11500000, guid: 77b727c07614b4041a5fe1fba0cfacff, type: 3}
|
||||
- Class: 114
|
||||
@ -75,6 +79,8 @@ ClassTypes:
|
||||
Script: {fileID: 11500000, guid: 040dd594681f07a4a975890a61d44be5, type: 3}
|
||||
- Class: 114
|
||||
Script: {fileID: 11500000, guid: 327eb94566c9e284dae5d7b1cfe11ccd, type: 3}
|
||||
- Class: 114
|
||||
Script: {fileID: 11500000, guid: 8d56b3759dd12424c8425ed62fc02796, type: 3}
|
||||
- Class: 114
|
||||
Script: {fileID: 11500000, guid: c317f6a5634f15f4c80f89e306616924, type: 3}
|
||||
- Class: 114
|
||||
@ -97,10 +103,12 @@ ClassTypes:
|
||||
Script: {fileID: 11500000, guid: b4b79e57677045045a95bfe4fe447ce5, type: 3}
|
||||
- Class: 114
|
||||
Script: {fileID: 11500000, guid: 64247dd7b0c5ac640a6d9ae5360a0f5a, type: 3}
|
||||
- Class: 114
|
||||
Script: {fileID: 11500000, guid: f645b92850d716a4488617b651223700, type: 3}
|
||||
- Class: 114
|
||||
Script: {fileID: 11500000, guid: 8ef66a28deb09ab4aaba30bb60b9f19a, type: 3}
|
||||
- Class: 114
|
||||
Script: {fileID: 11500000, guid: 0863077874402f14dba0ca4ae81752dd, type: 3}
|
||||
Script: {fileID: 11500000, guid: bf998978a8a701b4eb09fcd94048f916, type: 3}
|
||||
- Class: 114
|
||||
Script: {fileID: 11500000, guid: a9da74c8b134add4ba1d884336a5e075, type: 3}
|
||||
- Class: 114
|
||||
@ -171,7 +179,24 @@ ClassTypes:
|
||||
Script: {instanceID: 0}
|
||||
SerializeReferenceClassIdentifiers: []
|
||||
Assets:
|
||||
- Assets/SlideReels/Prefab_DW_Reel_Whole.prefab
|
||||
- Assets/SlideReels/Prefab_IP_Reel_Rusted_7.prefab
|
||||
- Assets/SlideReels/Prefab_IP_Reel_Destroyed_6.prefab
|
||||
- Assets/SlideReels/Effects_IP_SIM_SlideReel.prefab
|
||||
- Assets/SlideReels/Prefab_DW_Reel_7.prefab
|
||||
- Assets/SlideReels/Prefab_IP_Reel_Rusted_6.prefab
|
||||
- Assets/SlideReels/Prefab_IP_Reel_Rusted_8.prefab
|
||||
- Assets/SlideReels/Prefab_DW_Reel_6.prefab
|
||||
- Assets/SlideReels/Prefab_IP_Reel_8.prefab
|
||||
- Assets/SlideReels/Prefab_IP_Reel_6.prefab
|
||||
- Assets/SlideReels/Prefab_IP_Reel_Destroyed_7.prefab
|
||||
- Assets/SlideReels/Prefab_IP_Reel_Destroyed_Whole.prefab
|
||||
- Assets/SlideReels/Prefab_IP_Reel_Destroyed_8.prefab
|
||||
- Assets/SlideReels/Prefab_IP_Reel_Whole.prefab
|
||||
- Assets/SlideReels/Prefab_IP_Reel_Rusted_Whole.prefab
|
||||
- Assets/BrambleCollision.prefab
|
||||
- Assets/SlideReels/Prefab_DW_Reel_8.prefab
|
||||
- Assets/Vessel_Body.prefab
|
||||
- Assets/AmbientLight_QM.png
|
||||
- Assets/SlideReels/Prefab_IP_Reel_7.prefab
|
||||
Dependencies: []
|
||||
|
||||
BIN
NewHorizons/Assets/textures/blank_slide_reel.png
Normal file
BIN
NewHorizons/Assets/textures/blank_slide_reel.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 176 B |
BIN
NewHorizons/Assets/textures/inverted_blank_slide_reel.png
Normal file
BIN
NewHorizons/Assets/textures/inverted_blank_slide_reel.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 176 B |
@ -22,7 +22,8 @@
|
||||
"DEBUG_PLACE_TEXT": "Place Nomai Text",
|
||||
"DEBUG_UNDO": "Undo",
|
||||
"DEBUG_REDO": "Redo",
|
||||
"Vessel": "Vessel"
|
||||
"Vessel": "Vessel",
|
||||
"DLC_REQUIRED": "WARNING\n\nYou have addons (like {0}) installed which require the DLC but it is not enabled.\n\nYour mods may not function as intended."
|
||||
},
|
||||
"OtherDictionary": {
|
||||
"NOMAI_SHUTTLE_COMPUTER": "The <![CDATA[<color=orange>shuttle</color>]]> is currently resting at <![CDATA[<color=lightblue>{0}</color>]]>."
|
||||
|
||||
@ -17,7 +17,8 @@
|
||||
"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.",
|
||||
"JSON_FAILED_TO_LOAD": "Fichier(s) invalide(s): {0}",
|
||||
"Vessel": "Vaisseau"
|
||||
"Vessel": "Vaisseau",
|
||||
"DLC_REQUIRED": "AVERTISSEMENT\n\nVous avez installé des addons (par exemple, {0}) qui nécessitent le DLC mais il n'est pas activé.\n\nVos mods peuvent ne pas fonctionner."
|
||||
},
|
||||
"AchievementTranslations": {
|
||||
"NH_EATEN_OUTSIDE_BRAMBLE": {
|
||||
|
||||
@ -1,6 +1,68 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/xen-42/outer-wilds-new-horizons/main/NewHorizons/Schemas/translation_schema.json",
|
||||
"DialogueDictionary": {
|
||||
"NEW_HORIZONS_WARP_DRIVE_DIALOGUE_1": "探査艇にワープドライブが搭載されました!",
|
||||
"NEW_HORIZONS_WARP_DRIVE_DIALOGUE_2": "航行記録に新たに追加された“インターステラーモード”にて、別の星系をロックオンします。",
|
||||
"NEW_HORIZONS_WARP_DRIVE_DIALOGUE_3": "あとはシートベルトを締めて、それから自動操縦ボタンを押すだけでワープできます!"
|
||||
},
|
||||
"UIDictionary": {
|
||||
"INTERSTELLAR_MODE": "インターステラーモード",
|
||||
"FREQ_STATUE": "Nomai像",
|
||||
"FREQ_WARP_CORE": "反重力流束",
|
||||
"FREQ_UNKNOWN": "???",
|
||||
"ENGAGE_WARP_PROMPT": "{0}へのワープを開始",
|
||||
"WARP_LOCKED": "自動操縦のロックオン先:\n{0}",
|
||||
"LOCK_AUTOPILOT_WARP": "星系に自動操縦をロックオンする",
|
||||
"RICH_PRESENCE_EXPLORING": "{0}を探検中。",
|
||||
"RICH_PRESENCE_WARPING": "{0}へワープ中。",
|
||||
"OUTDATED_VERSION_WARNING": "警告\n\nNew Horizonsはバージョン{0}以上でのみ動作します。このOuter Wildsのバージョンは{1}です。\n\nOuter Wildsをアップデートするか、もしくはNew Horizonsをアンインストールしてください。",
|
||||
"JSON_FAILED_TO_LOAD": "無効なファイル:{0}",
|
||||
"DEBUG_RAYCAST": "Raycast",
|
||||
"DEBUG_PLACE": "Place Object",
|
||||
"DEBUG_PLACE_TEXT": "Place Nomai Text",
|
||||
"DEBUG_UNDO": "Undo",
|
||||
"DEBUG_REDO": "Redo",
|
||||
"Vessel": "船"
|
||||
},
|
||||
"OtherDictionary": {
|
||||
"NOMAI_SHUTTLE_COMPUTER": "<![CDATA[<color=orange>シャトル</color>]]>は現在<![CDATA[<color=lightblue>{0}</color>]]>に停泊している。"
|
||||
},
|
||||
"AchievementTranslations": {
|
||||
"NH_EATEN_OUTSIDE_BRAMBLE": {
|
||||
"Name": "収容違反",
|
||||
"Description": "闇のイバラの外で食べられる。"
|
||||
},
|
||||
"NH_MULTIPLE_SYSTEM": {
|
||||
"Name": "旅人",
|
||||
"Description": "5つの異なる星系を連続して訪れる。"
|
||||
},
|
||||
"NH_NEW_FREQ": {
|
||||
"Name": "異常な周波数",
|
||||
"Description": "新たな周波数を発見する。"
|
||||
},
|
||||
"NH_PROBE_LOST": {
|
||||
"Name": "失われた接続",
|
||||
"Description": "偵察機との接続が断たれる。"
|
||||
},
|
||||
"NH_WARP_DRIVE": {
|
||||
"Name": "不正確な伝承",
|
||||
"Description": "探査艇のワープドライブを使う。"
|
||||
},
|
||||
"NH_VESSEL_WARP": {
|
||||
"Name": "正確な伝承",
|
||||
"Description": "船でどこかの星系へワープする。"
|
||||
},
|
||||
"NH_RAFTING": {
|
||||
"Name": "イカダといかり",
|
||||
"Description": "イカダに乗る。"
|
||||
},
|
||||
"NH_SUCKED_INTO_LAVA_BY_TORNADO": {
|
||||
"Name": "ダイクロン",
|
||||
"Description": "竜巻で溶岩へと吸い込まれる。"
|
||||
},
|
||||
"NH_TALK_TO_FIVE_CHARACTERS": {
|
||||
"Name": "社会",
|
||||
"Description": "誰か5人と話す。"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,20 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/xen-42/outer-wilds-new-horizons/main/NewHorizons/Schemas/translation_schema.json",
|
||||
"DialogueDictionary": {
|
||||
"NEW_HORIZONS_WARP_DRIVE_DIALOGUE_1": "Twój statek jest teraz wyposażony z napędem warp!",
|
||||
"NEW_HORIZONS_WARP_DRIVE_DIALOGUE_2": "Możesz użyć nowy \"Tryb międzygwiezdny\" w Dzienniku Pokładowym żeby zablokować twój autopilot na inny Układ Gwiazdy.",
|
||||
"NEW_HORIZONS_WARP_DRIVE_DIALOGUE_3": "A potem zapnij pasy i zaangażój wypaczanie!"
|
||||
},
|
||||
"UIDictionary": {
|
||||
"INTERSTELLAR_MODE": "Tryb międzygwiezdny",
|
||||
"FREQ_STATUE": "Statua Nomai",
|
||||
"FREQ_WARP_CORE": "Strumień Anty-Grawitonowy",
|
||||
"FREQ_UNKNOWN": "???",
|
||||
"ENGAGE_WARP_PROMPT": "Zaangażować Wypaczanie do {0}",
|
||||
"WARP_LOCKED": "AUTOPILOT ZABLOKOWANY NA:\n{0}",
|
||||
"LOCK_AUTOPILOT_WARP": "Zablokuj Autopilot na Układ Gwiazdy",
|
||||
"RICH_PRESENCE_EXPLORING": "Eksploruje {0}.",
|
||||
"RICH_PRESENCE_WARPING": "Wypaczanie do {0}.",
|
||||
"Vessel": "Statku"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
using NewHorizons.External.Configs;
|
||||
using NewHorizons.External.Modules;
|
||||
using NewHorizons.Utility;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Builder.Atmosphere
|
||||
{
|
||||
public static class EffectsBuilder
|
||||
@ -51,55 +54,69 @@ namespace NewHorizons.Builder.Atmosphere
|
||||
if (_fogEmitterPrefab == null) _fogEmitterPrefab = SearchUtilities.Find("DB_EscapePodDimension_Body/Sector_EscapePodDimension/Effects_EscapePodDimension/Effects_DB_Fog (1)").InstantiateInactive().Rename("Prefab_Effects_Fog").DontDestroyOnLoad();
|
||||
}
|
||||
|
||||
|
||||
#region obsolete
|
||||
// Never change method signatures, people directly reference the NH dll and it can break backwards compatibility
|
||||
// Dreamstalker needed this one
|
||||
[Obsolete]
|
||||
public static void Make(GameObject planetGO, Sector sector, PlanetConfig config, float surfaceHeight)
|
||||
=> InternalMake(planetGO, sector, config, surfaceHeight);
|
||||
#endregion
|
||||
|
||||
public static void Make(GameObject planetGO, Sector sector, PlanetConfig config)
|
||||
=> InternalMake(planetGO, sector, config, null);
|
||||
|
||||
/// <summary>
|
||||
/// Nullable surface height for backwards compat
|
||||
/// </summary>
|
||||
/// <param name="planetGO"></param>
|
||||
/// <param name="sector"></param>
|
||||
/// <param name="config"></param>
|
||||
/// <param name="surfaceHeight"></param>
|
||||
private static void InternalMake(GameObject planetGO, Sector sector, PlanetConfig config, float? surfaceHeight)
|
||||
{
|
||||
InitPrefabs();
|
||||
|
||||
GameObject effectsGO = new GameObject("Effects");
|
||||
var effectsGO = new GameObject("Effects");
|
||||
effectsGO.SetActive(false);
|
||||
effectsGO.transform.parent = sector?.transform ?? planetGO.transform;
|
||||
effectsGO.transform.position = planetGO.transform.position;
|
||||
|
||||
SectorCullGroup SCG = effectsGO.AddComponent<SectorCullGroup>();
|
||||
SCG._sector = sector;
|
||||
SCG._particleSystemSuspendMode = CullGroup.ParticleSystemSuspendMode.Stop;
|
||||
SCG._occlusionCulling = false;
|
||||
SCG._dynamicCullingBounds = false;
|
||||
SCG._waitForStreaming = false;
|
||||
var sectorCullGroup = effectsGO.AddComponent<SectorCullGroup>();
|
||||
sectorCullGroup._sector = sector;
|
||||
sectorCullGroup._particleSystemSuspendMode = CullGroup.ParticleSystemSuspendMode.Stop;
|
||||
sectorCullGroup._occlusionCulling = false;
|
||||
sectorCullGroup._dynamicCullingBounds = false;
|
||||
sectorCullGroup._waitForStreaming = false;
|
||||
|
||||
var minHeight = config.Base.surfaceSize;
|
||||
if (config.HeightMap?.minHeight != null)
|
||||
var (minHeight, maxHeight) = GetDefaultHeightRange(config);
|
||||
// min height override for backwards compat
|
||||
minHeight = surfaceHeight ?? minHeight;
|
||||
|
||||
if (config.ParticleFields != 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;
|
||||
|
||||
var maxHeight = config.Atmosphere.size;
|
||||
if (config.Atmosphere.clouds?.outerCloudRadius != null) maxHeight = config.Atmosphere.clouds.outerCloudRadius;
|
||||
|
||||
foreach (var particleField in config.ParticleFields)
|
||||
{
|
||||
var prefab = GetPrefabByType(particleField.type);
|
||||
var emitter = Object.Instantiate(prefab, effectsGO.transform);
|
||||
emitter.name = !string.IsNullOrWhiteSpace(particleField.rename) ? particleField.rename : prefab.name.Replace("Prefab_", "");
|
||||
emitter.transform.position = planetGO.transform.position;
|
||||
|
||||
var vfe = emitter.GetComponent<VectionFieldEmitter>();
|
||||
var pvc = emitter.GetComponent<PlanetaryVectionController>();
|
||||
pvc._vectionFieldEmitter = vfe;
|
||||
pvc._densityByHeight = particleField.densityByHeightCurve != null ? particleField.densityByHeightCurve.ToAnimationCurve() : new AnimationCurve(new Keyframe[]
|
||||
foreach (var particleField in config.ParticleFields)
|
||||
{
|
||||
new Keyframe(minHeight - 0.5f, 0),
|
||||
new Keyframe(minHeight, 10f),
|
||||
new Keyframe(maxHeight, 0f)
|
||||
});
|
||||
pvc._followTarget = particleField.followTarget == ParticleFieldModule.FollowTarget.Probe ? PlanetaryVectionController.FollowTarget.Probe : PlanetaryVectionController.FollowTarget.Player;
|
||||
pvc._activeInSector = sector;
|
||||
pvc._exclusionSectors = new Sector[] { };
|
||||
var prefab = GetPrefabByType(particleField.type);
|
||||
var emitter = GameObject.Instantiate(prefab, effectsGO.transform);
|
||||
emitter.name = !string.IsNullOrWhiteSpace(particleField.rename) ? particleField.rename : prefab.name.Replace("Prefab_", "");
|
||||
emitter.transform.position = planetGO.transform.position;
|
||||
|
||||
emitter.SetActive(true);
|
||||
var vfe = emitter.GetComponent<VectionFieldEmitter>();
|
||||
var pvc = emitter.GetComponent<PlanetaryVectionController>();
|
||||
pvc._vectionFieldEmitter = vfe;
|
||||
pvc._densityByHeight = particleField.densityByHeightCurve != null ? particleField.densityByHeightCurve.ToAnimationCurve() : new AnimationCurve(new Keyframe[]
|
||||
{
|
||||
new Keyframe(minHeight - 0.5f, 0),
|
||||
new Keyframe(minHeight, 10f),
|
||||
new Keyframe(maxHeight, 0f)
|
||||
});
|
||||
pvc._followTarget = particleField.followTarget == ParticleFieldModule.FollowTarget.Probe ? PlanetaryVectionController.FollowTarget.Probe : PlanetaryVectionController.FollowTarget.Player;
|
||||
pvc._activeInSector = sector;
|
||||
pvc._exclusionSectors = new Sector[] { };
|
||||
|
||||
emitter.SetActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
effectsGO.transform.position = planetGO.transform.position;
|
||||
@ -130,5 +147,48 @@ namespace NewHorizons.Builder.Atmosphere
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
private static (float, float) GetDefaultHeightRange(PlanetConfig config)
|
||||
{
|
||||
var minHeight = 0f;
|
||||
|
||||
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;
|
||||
}
|
||||
else if (config.Base != null)
|
||||
{
|
||||
minHeight = config.Base.surfaceSize;
|
||||
}
|
||||
|
||||
var maxHeight = 100f;
|
||||
|
||||
if (config.Atmosphere != null)
|
||||
{
|
||||
if (config.Atmosphere.clouds?.outerCloudRadius != null)
|
||||
{
|
||||
maxHeight = config.Atmosphere.clouds.outerCloudRadius;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxHeight = config.Atmosphere.size;
|
||||
}
|
||||
}
|
||||
else if (minHeight != 0f)
|
||||
{
|
||||
maxHeight = minHeight * 2f;
|
||||
}
|
||||
|
||||
return (minHeight, maxHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,9 @@ using NewHorizons.External.Modules;
|
||||
using NewHorizons.Utility;
|
||||
using NewHorizons.Utility.Files;
|
||||
using OWML.Common;
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Builder.Atmosphere
|
||||
{
|
||||
public static class FogBuilder
|
||||
@ -24,7 +26,9 @@ namespace NewHorizons.Builder.Atmosphere
|
||||
|
||||
internal static void InitPrefabs()
|
||||
{
|
||||
if (_ramp == null) _ramp = ImageUtilities.GetTexture(Main.Instance, "Assets/textures/FogColorRamp.png");
|
||||
// Checking null here it was getting destroyed and wouldnt reload and never worked outside of the first loop
|
||||
// GetTexture caches itself anyway so it doesn't matter that this gets called multiple times
|
||||
_ramp = ImageUtilities.GetTexture(Main.Instance, "Assets/textures/FogColorRamp.png");
|
||||
|
||||
if (_isInit) return;
|
||||
|
||||
@ -36,6 +40,14 @@ namespace NewHorizons.Builder.Atmosphere
|
||||
if (_dbImpostorMaterials == null) _dbImpostorMaterials = SearchUtilities.Find("DarkBramble_Body/Atmosphere_DB/FogLOD").GetComponent<MeshRenderer>().sharedMaterials.MakePrefabMaterials();
|
||||
}
|
||||
|
||||
#region obsolete
|
||||
// Never change method signatures, people directly reference the NH dll and it can break backwards compatibility
|
||||
// Dreamstalker needs this method signature
|
||||
[Obsolete]
|
||||
public static PlanetaryFogController Make(GameObject planetGO, Sector sector, AtmosphereModule atmo)
|
||||
=> Make(planetGO, sector, atmo, null);
|
||||
#endregion
|
||||
|
||||
public static PlanetaryFogController Make(GameObject planetGO, Sector sector, AtmosphereModule atmo, IModBehaviour mod)
|
||||
{
|
||||
InitPrefabs();
|
||||
@ -63,6 +75,7 @@ namespace NewHorizons.Builder.Atmosphere
|
||||
atmo.fogRampPath != null ? ImageUtilities.GetTexture(mod, atmo.fogRampPath) :
|
||||
atmo.fogTint != null ? ImageUtilities.TintImage(_ramp, atmo.fogTint.ToColor()) :
|
||||
_ramp;
|
||||
|
||||
PFC.fogColorRampTexture = colorRampTexture;
|
||||
PFC.fogColorRampIntensity = 1f;
|
||||
if (atmo.fogTint != null)
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using NewHorizons.External.Modules;
|
||||
using NewHorizons.External.Modules.VariableSize;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Builder.Atmosphere
|
||||
{
|
||||
public static class SunOverrideBuilder
|
||||
|
||||
@ -36,7 +36,6 @@ namespace NewHorizons.Builder.Body
|
||||
|
||||
config.Base = new BaseModule()
|
||||
{
|
||||
hasMapMarker = false,
|
||||
surfaceGravity = 1,
|
||||
surfaceSize = size,
|
||||
gravityFallOff = GravityFallOff.InverseSquared
|
||||
@ -58,6 +57,11 @@ namespace NewHorizons.Builder.Body
|
||||
enabled = false
|
||||
};
|
||||
|
||||
config.MapMarker = new MapMarkerModule()
|
||||
{
|
||||
enabled = false
|
||||
};
|
||||
|
||||
config.ProcGen = belt.procGen;
|
||||
if (config.ProcGen == null)
|
||||
{
|
||||
|
||||
@ -6,6 +6,7 @@ using NewHorizons.External.Modules;
|
||||
using NewHorizons.External.Modules.Props;
|
||||
using NewHorizons.Utility;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using OWML.Common;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
@ -80,7 +81,7 @@ namespace NewHorizons.Builder.Body
|
||||
if (_wallCollision == null) _wallCollision = Main.NHPrivateAssetBundle.LoadAsset<GameObject>("BrambleCollision");
|
||||
}
|
||||
|
||||
public static GameObject Make(NewHorizonsBody body, GameObject go, NHAstroObject ao, Sector sector, OWRigidbody owRigidBody)
|
||||
public static GameObject Make(NewHorizonsBody body, GameObject go, NHAstroObject ao, Sector sector, IModBehaviour mod, OWRigidbody owRigidBody)
|
||||
{
|
||||
InitPrefabs();
|
||||
|
||||
@ -102,7 +103,7 @@ namespace NewHorizons.Builder.Body
|
||||
default: geometryPrefab = _hubGeometry; break;
|
||||
}
|
||||
|
||||
var geometry = DetailBuilder.Make(go, sector, geometryPrefab, new DetailInfo());
|
||||
var geometry = DetailBuilder.Make(go, sector, mod, geometryPrefab, new DetailInfo());
|
||||
|
||||
var exitWarps = _exitWarps.InstantiateInactive();
|
||||
var repelVolume = _repelVolume.InstantiateInactive();
|
||||
|
||||
@ -11,6 +11,8 @@ using NewHorizons.Builder.Props;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using NewHorizons.Utility.OuterWilds;
|
||||
using NewHorizons.External.SerializableData;
|
||||
using NewHorizons.Builder.Volumes;
|
||||
using System;
|
||||
|
||||
namespace NewHorizons.Builder.Body
|
||||
{
|
||||
@ -109,31 +111,52 @@ namespace NewHorizons.Builder.Body
|
||||
{
|
||||
foreach (var pair in _pairsToLink)
|
||||
{
|
||||
var (blackHoleID, whiteHoleID) = pair;
|
||||
if (!_singularitiesByID.TryGetValue(blackHoleID, out GameObject blackHole))
|
||||
try
|
||||
{
|
||||
NHLogger.LogWarning($"Black hole [{blackHoleID}] is missing.");
|
||||
continue;
|
||||
var (blackHoleID, whiteHoleID) = pair;
|
||||
if (!_singularitiesByID.TryGetValue(blackHoleID, out GameObject blackHole))
|
||||
{
|
||||
NHLogger.LogWarning($"Black hole [{blackHoleID}] is missing.");
|
||||
continue;
|
||||
}
|
||||
if (!_singularitiesByID.TryGetValue(whiteHoleID, out GameObject whiteHole))
|
||||
{
|
||||
NHLogger.LogWarning($"White hole [{whiteHoleID}] is missing.");
|
||||
continue;
|
||||
}
|
||||
var whiteHoleVolume = whiteHole.GetComponentInChildren<WhiteHoleVolume>();
|
||||
var blackHoleVolume = blackHole.GetComponentInChildren<BlackHoleVolume>();
|
||||
if (whiteHoleVolume == null || blackHoleVolume == null)
|
||||
{
|
||||
NHLogger.LogWarning($"Singularities [{blackHoleID}] and [{whiteHoleID}] do not have compatible polarities.");
|
||||
continue;
|
||||
}
|
||||
if (blackHoleVolume._whiteHole != null && blackHoleVolume._whiteHole != whiteHoleVolume)
|
||||
{
|
||||
NHLogger.LogWarning($"Black hole [{blackHoleID}] has already been linked!");
|
||||
continue;
|
||||
}
|
||||
NHLogger.LogVerbose($"Pairing singularities [{blackHoleID}], [{whiteHoleID}]");
|
||||
blackHoleVolume._whiteHole = whiteHoleVolume;
|
||||
|
||||
// If warping to a vanilla planet, we add a streaming volume to pre-load it
|
||||
var streamingGroup = whiteHoleVolume.GetAttachedOWRigidbody().GetComponentInChildren<StreamingGroup>();
|
||||
if (streamingGroup != null)
|
||||
{
|
||||
var sphereCollider = blackHoleVolume.GetComponent<SphereCollider>();
|
||||
// Shouldn't ever be null but doesn't hurt ig
|
||||
var loadRadius = sphereCollider == null ? 100f : sphereCollider.radius + 50f;
|
||||
var streamingVolume = VolumeBuilder.Make<StreamingWarpVolume>(blackHoleVolume.GetAttachedOWRigidbody().gameObject, blackHoleVolume.GetComponentInParent<Sector>(),
|
||||
new External.Modules.Volumes.VolumeInfos.VolumeInfo() { radius = loadRadius });
|
||||
streamingVolume.streamingGroup = streamingGroup;
|
||||
streamingVolume.transform.parent = blackHoleVolume.transform;
|
||||
streamingVolume.transform.localPosition = Vector3.zero;
|
||||
}
|
||||
}
|
||||
if (!_singularitiesByID.TryGetValue(whiteHoleID, out GameObject whiteHole))
|
||||
catch (Exception e)
|
||||
{
|
||||
NHLogger.LogWarning($"White hole [{whiteHoleID}] is missing.");
|
||||
continue;
|
||||
NHLogger.LogError($"Failed to pair singularities {e}");
|
||||
}
|
||||
var whiteHoleVolume = whiteHole.GetComponentInChildren<WhiteHoleVolume>();
|
||||
var blackHoleVolume = blackHole.GetComponentInChildren<BlackHoleVolume>();
|
||||
if (whiteHoleVolume == null || blackHoleVolume == null)
|
||||
{
|
||||
NHLogger.LogWarning($"Singularities [{blackHoleID}] and [{whiteHoleID}] do not have compatible polarities.");
|
||||
continue;
|
||||
}
|
||||
if (blackHoleVolume._whiteHole != null && blackHoleVolume._whiteHole != whiteHoleVolume)
|
||||
{
|
||||
NHLogger.LogWarning($"Black hole [{blackHoleID}] has already been linked!");
|
||||
continue;
|
||||
}
|
||||
NHLogger.LogVerbose($"Pairing singularities [{blackHoleID}], [{whiteHoleID}]");
|
||||
blackHoleVolume._whiteHole = whiteHoleVolume;
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,7 +190,7 @@ namespace NewHorizons.Builder.Body
|
||||
|
||||
OWAudioSource oneShotOWAudioSource = null;
|
||||
|
||||
var singularityAmbience = Object.Instantiate(_blackHoleAmbience, singularity.transform);
|
||||
var singularityAmbience = GameObject.Instantiate(_blackHoleAmbience, singularity.transform);
|
||||
singularityAmbience.name = polarity ? "BlackHoleAmbience" : "WhiteHoleAmbience";
|
||||
singularityAmbience.SetActive(true);
|
||||
singularityAmbience.GetComponent<SectorAudioGroup>().SetSector(sector);
|
||||
@ -214,7 +237,7 @@ namespace NewHorizons.Builder.Body
|
||||
}
|
||||
else
|
||||
{
|
||||
var blackHoleOneShot = Object.Instantiate(_blackHoleEmissionOneShot, singularity.transform);
|
||||
var blackHoleOneShot = GameObject.Instantiate(_blackHoleEmissionOneShot, singularity.transform);
|
||||
blackHoleOneShot.name = "BlackHoleEmissionOneShot";
|
||||
blackHoleOneShot.SetActive(true);
|
||||
oneShotOWAudioSource = blackHoleOneShot.GetComponent<OWAudioSource>();
|
||||
@ -223,7 +246,7 @@ namespace NewHorizons.Builder.Body
|
||||
oneShotAudioSource.minDistance = horizon;
|
||||
if (sizeController != null) sizeController.oneShotAudioSource = oneShotAudioSource;
|
||||
|
||||
var blackHoleVolume = Object.Instantiate(_blackHoleVolume, singularity.transform);
|
||||
var blackHoleVolume = GameObject.Instantiate(_blackHoleVolume, singularity.transform);
|
||||
blackHoleVolume.name = "BlackHoleVolume";
|
||||
|
||||
// Scale vanish effect to black hole size
|
||||
@ -249,7 +272,7 @@ namespace NewHorizons.Builder.Body
|
||||
{
|
||||
foreach (var renderer in blackHoleVolume.GetComponentsInChildren<ParticleSystemRenderer>(true))
|
||||
{
|
||||
Object.Destroy(renderer);
|
||||
GameObject.Destroy(renderer);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -257,7 +280,7 @@ namespace NewHorizons.Builder.Body
|
||||
}
|
||||
else
|
||||
{
|
||||
GameObject whiteHoleVolumeGO = Object.Instantiate(_whiteHoleVolume);
|
||||
GameObject whiteHoleVolumeGO = GameObject.Instantiate(_whiteHoleVolume);
|
||||
whiteHoleVolumeGO.transform.parent = singularity.transform;
|
||||
whiteHoleVolumeGO.transform.localPosition = Vector3.zero;
|
||||
whiteHoleVolumeGO.transform.localScale = Vector3.one;
|
||||
|
||||
@ -408,10 +408,15 @@ namespace NewHorizons.Builder.Body
|
||||
if (starModule.endTint != null)
|
||||
{
|
||||
var endColour = starModule.endTint.ToColor();
|
||||
darkenedColor = new Color(endColour.r * modifier, endColour.g * modifier, endColour.b * modifier);
|
||||
var adjustedEndColour = new Color(endColour.r * modifier, endColour.g * modifier, endColour.b * modifier);
|
||||
Color.RGBToHSV(adjustedEndColour, out var hEnd, out var sEnd, out var vEnd);
|
||||
var darkenedEndColor = Color.HSVToRGB(hEnd, sEnd * 1.2f, vEnd * 0.1f);
|
||||
surface.sharedMaterial.SetTexture(ColorRamp, ImageUtilities.LerpGreyscaleImageAlongX(_colorOverTime, adjustedColour, darkenedColor, adjustedEndColour, darkenedEndColor));
|
||||
}
|
||||
else
|
||||
{
|
||||
surface.sharedMaterial.SetTexture(ColorRamp, ImageUtilities.LerpGreyscaleImage(_colorOverTime, adjustedColour, darkenedColor));
|
||||
}
|
||||
|
||||
surface.sharedMaterial.SetTexture(ColorRamp, ImageUtilities.LerpGreyscaleImage(_colorOverTime, adjustedColour, darkenedColor));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(starModule.starRampTexture))
|
||||
|
||||
@ -135,7 +135,8 @@ namespace NewHorizons.Builder.Body
|
||||
var fogGO = Object.Instantiate(_oceanFog, waterGO.transform);
|
||||
fogGO.name = "OceanFog";
|
||||
fogGO.transform.localPosition = Vector3.zero;
|
||||
fogGO.transform.localScale = Vector3.one;
|
||||
// In base game GD ocean fog is 550 while the water volume is 500
|
||||
fogGO.transform.localScale = Vector3.one * 550f / 500f;
|
||||
fogGO.SetActive(true);
|
||||
|
||||
if (module.tint != null)
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
using NewHorizons.Components;
|
||||
using NewHorizons.Components.Orbital;
|
||||
using NewHorizons.External.Configs;
|
||||
using NewHorizons.External;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using UnityEngine;
|
||||
|
||||
@ -8,11 +8,15 @@ namespace NewHorizons.Builder.General
|
||||
{
|
||||
public static class AstroObjectBuilder
|
||||
{
|
||||
public static NHAstroObject Make(GameObject body, AstroObject primaryBody, PlanetConfig config, bool isVanilla)
|
||||
public static NHAstroObject Make(GameObject body, AstroObject primaryBody, NewHorizonsBody nhBody, bool isVanilla)
|
||||
{
|
||||
NHAstroObject astroObject = body.AddComponent<NHAstroObject>();
|
||||
astroObject.modUniqueName = nhBody.Mod.ModHelper.Manifest.UniqueName;
|
||||
|
||||
var config = nhBody.Config;
|
||||
|
||||
astroObject.isVanilla = isVanilla;
|
||||
astroObject.HideDisplayName = !config.Base.hasMapMarker;
|
||||
astroObject.HideDisplayName = !config.MapMarker.enabled;
|
||||
astroObject.invulnerableToSun = config.Base.invulnerableToSun;
|
||||
|
||||
if (config.Orbit != null) astroObject.SetOrbitalParametersFromConfig(config.Orbit);
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#region
|
||||
#region
|
||||
|
||||
using NewHorizons.Components;
|
||||
using NewHorizons.External.Configs;
|
||||
using NewHorizons.Handlers;
|
||||
using UnityEngine;
|
||||
@ -12,7 +13,8 @@ namespace NewHorizons.Builder.General
|
||||
{
|
||||
public static void Make(GameObject body, string name, PlanetConfig config)
|
||||
{
|
||||
MapMarker mapMarker = body.AddComponent<MapMarker>();
|
||||
var module = config.MapMarker;
|
||||
NHMapMarker mapMarker = body.AddComponent<NHMapMarker>();
|
||||
mapMarker._labelID = (UITextType)TranslationHandler.AddUI(config.name);
|
||||
|
||||
var markerType = MapMarker.MarkerType.Planet;
|
||||
@ -37,6 +39,9 @@ namespace NewHorizons.Builder.General
|
||||
*/
|
||||
|
||||
mapMarker._markerType = markerType;
|
||||
|
||||
mapMarker.minDisplayDistanceOverride = module.minDisplayDistanceOverride;
|
||||
mapMarker.maxDisplayDistanceOverride = module.maxDisplayDistanceOverride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,6 +12,8 @@ namespace NewHorizons.Builder.General
|
||||
{
|
||||
// We can't not build a reference frame volume, Cloak requires one to be there
|
||||
module.maxTargetDistance = 0f;
|
||||
module.targetWhenClose = true;
|
||||
module.targetColliderRadius = 0.001f;
|
||||
module.hideInMap = true;
|
||||
owrb.SetIsTargetable(false);
|
||||
}
|
||||
|
||||
@ -11,8 +11,8 @@ namespace NewHorizons.Builder.General
|
||||
{
|
||||
var sectorGO = new GameObject("Sector");
|
||||
sectorGO.SetActive(false);
|
||||
sectorGO.transform.parent = planetBody.transform;
|
||||
sectorGO.transform.localPosition = Vector3.zero;
|
||||
// Have to use set parent method without keeping world position to Fix sectors being rotated on tidally locked bodies #870
|
||||
sectorGO.transform.SetParent(planetBody.transform, false);
|
||||
|
||||
var SS = sectorGO.AddComponent<SphereShape>();
|
||||
SS.SetCollisionMode(Shape.CollisionMode.Volume);
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
using HarmonyLib;
|
||||
using NewHorizons.External;
|
||||
using NewHorizons.External.Modules.Props.Audio;
|
||||
using NewHorizons.Utility;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using OWML.Common;
|
||||
using OWML.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
@ -36,27 +38,16 @@ namespace NewHorizons.Builder.Props.Audio
|
||||
};
|
||||
NumberOfFrequencies = EnumUtils.GetValues<SignalFrequency>().Length;
|
||||
|
||||
_qmSignals = new (){ SearchUtilities.Find("QuantumMoon_Body/Signal_Quantum").GetComponent<AudioSignal>() };
|
||||
_qmSignals = new () { SearchUtilities.Find("QuantumMoon_Body/Signal_Quantum").GetComponent<AudioSignal>() };
|
||||
_cloakedSignals = new();
|
||||
|
||||
Initialized = true;
|
||||
|
||||
SceneManager.sceneUnloaded -= OnSceneUnloaded;
|
||||
SceneManager.sceneUnloaded += OnSceneUnloaded;
|
||||
Main.Instance.OnStarSystemLoaded.RemoveListener(OnStarSystemLoaded);
|
||||
Main.Instance.OnStarSystemLoaded.AddListener(OnStarSystemLoaded);
|
||||
}
|
||||
|
||||
private static HashSet<SignalFrequency> _frequenciesInUse = new();
|
||||
|
||||
private static void OnSceneUnloaded(Scene _)
|
||||
{
|
||||
_frequenciesInUse.Clear();
|
||||
}
|
||||
|
||||
private static void OnStarSystemLoaded(string starSystem)
|
||||
{
|
||||
// If its the base game solar system or eye we get all the main frequencies
|
||||
var starSystem = Main.Instance.CurrentStarSystem;
|
||||
if (starSystem == "SolarSystem" || starSystem == "EyeOfTheUniverse")
|
||||
{
|
||||
_frequenciesInUse.Add(SignalFrequency.Quantum);
|
||||
@ -69,19 +60,43 @@ namespace NewHorizons.Builder.Props.Audio
|
||||
// We don't want a scenario where the player knows no frequencies
|
||||
_frequenciesInUse.Add(SignalFrequency.Traveler);
|
||||
|
||||
// Make sure the NH save file has all the right frequencies
|
||||
// Skip "default"
|
||||
for (int i = 1; i < PlayerData._currentGameSave.knownFrequencies.Length; i++)
|
||||
{
|
||||
if (PlayerData._currentGameSave.knownFrequencies[i])
|
||||
{
|
||||
NewHorizonsData.LearnFrequency(AudioSignal.IndexToFrequency(i).ToString());
|
||||
}
|
||||
}
|
||||
|
||||
NHLogger.LogVerbose($"Frequencies in use in {starSystem}: {_frequenciesInUse.Join(x => x.ToString())}");
|
||||
}
|
||||
|
||||
private static HashSet<SignalFrequency> _frequenciesInUse = new();
|
||||
|
||||
private static void OnSceneUnloaded(Scene _)
|
||||
{
|
||||
_frequenciesInUse.Clear();
|
||||
}
|
||||
|
||||
public static bool IsFrequencyInUse(SignalFrequency freq) => _frequenciesInUse.Contains(freq);
|
||||
|
||||
public static bool IsFrequencyInUse(string freqString)
|
||||
{
|
||||
if (Enum.TryParse<SignalFrequency>(freqString, out var freq))
|
||||
{
|
||||
return IsFrequencyInUse(freq);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsCloaked(this AudioSignal signal) => _cloakedSignals.Contains(signal);
|
||||
|
||||
public static bool IsOnQuantumMoon(this AudioSignal signal) => _qmSignals.Contains(signal);
|
||||
|
||||
public static SignalFrequency AddFrequency(string str)
|
||||
{
|
||||
if (_customFrequencyNames == null) Init();
|
||||
|
||||
var freq = CollectionUtilities.KeyByValue(_customFrequencyNames, str);
|
||||
if (freq != default) return freq;
|
||||
|
||||
@ -99,23 +114,20 @@ namespace NewHorizons.Builder.Props.Audio
|
||||
NumberOfFrequencies = EnumUtils.GetValues<SignalFrequency>().Length;
|
||||
|
||||
// This stuff happens after the signalscope is Awake so we have to change the number of frequencies now
|
||||
Object.FindObjectOfType<Signalscope>()._strongestSignals = new AudioSignal[NumberOfFrequencies + 1];
|
||||
GameObject.FindObjectOfType<Signalscope>()._strongestSignals = new AudioSignal[NumberOfFrequencies + 1];
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
public static string GetCustomFrequencyName(SignalFrequency frequencyName)
|
||||
{
|
||||
if (_customFrequencyNames == null) Init();
|
||||
|
||||
if (_customFrequencyNames == null) return string.Empty;
|
||||
_customFrequencyNames.TryGetValue(frequencyName, out string name);
|
||||
return name;
|
||||
}
|
||||
|
||||
public static SignalName AddSignalName(string str)
|
||||
{
|
||||
if (_customSignalNames == null) Init();
|
||||
|
||||
var name = CollectionUtilities.KeyByValue(_customSignalNames, str);
|
||||
if (name != default) return name;
|
||||
|
||||
@ -129,8 +141,7 @@ namespace NewHorizons.Builder.Props.Audio
|
||||
|
||||
public static string GetCustomSignalName(SignalName signalName)
|
||||
{
|
||||
if (_customSignalNames == null) Init();
|
||||
|
||||
if (_customSignalNames == null) return string.Empty;
|
||||
_customSignalNames.TryGetValue(signalName, out string name);
|
||||
return name;
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ using NewHorizons.Handlers;
|
||||
using NewHorizons.Utility;
|
||||
using NewHorizons.Utility.OuterWilds;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using Newtonsoft.Json;
|
||||
using OWML.Common;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@ -33,12 +34,17 @@ namespace NewHorizons.Builder.Props
|
||||
private static GameObject _brambleSeedPrefab;
|
||||
private static GameObject _brambleNodePrefab;
|
||||
|
||||
private static HashSet<FogWarpVolume> _nhFogWarpVolumes = new();
|
||||
|
||||
public static bool IsNHFogWarpVolume(FogWarpVolume volume) => _nhFogWarpVolumes.Contains(volume);
|
||||
|
||||
public static void Init(PlanetConfig[] dimensionConfigs)
|
||||
{
|
||||
_unpairedNodes.Clear();
|
||||
_propagatedSignals.Clear();
|
||||
namedNodes.Clear();
|
||||
builtBrambleNodes.Clear();
|
||||
_nhFogWarpVolumes.Clear();
|
||||
|
||||
PropagateSignals(dimensionConfigs);
|
||||
}
|
||||
@ -107,6 +113,10 @@ namespace NewHorizons.Builder.Props
|
||||
if (dimension.Bramble.nodes == null) continue;
|
||||
foreach (var node in dimension.Bramble.nodes)
|
||||
{
|
||||
if (!dimensionNameToIndex.ContainsKey(node.linksTo))
|
||||
{
|
||||
NHLogger.LogError($"There is no bramble dimension named {node.linksTo}");
|
||||
}
|
||||
var destinationDimensionIndex = dimensionNameToIndex[node.linksTo];
|
||||
access[dimensionIndex, destinationDimensionIndex] = true;
|
||||
}
|
||||
@ -190,6 +200,12 @@ namespace NewHorizons.Builder.Props
|
||||
collider.enabled = true;
|
||||
}
|
||||
|
||||
// We track all the fog warp volumes that NH created so we can only effect those in patches, this way we leave base game stuff alone.
|
||||
foreach (var fogWarpVolume in brambleNode.GetComponentsInChildren<FogWarpVolume>(true).Append(brambleNode.GetComponent<FogWarpVolume>()))
|
||||
{
|
||||
_nhFogWarpVolumes.Add(fogWarpVolume);
|
||||
}
|
||||
|
||||
var innerFogWarpVolume = brambleNode.GetComponent<InnerFogWarpVolume>();
|
||||
var outerFogWarpVolume = GetOuterFogWarpVolumeFromAstroObject(go);
|
||||
var fogLight = brambleNode.GetComponent<FogLight>();
|
||||
@ -235,7 +251,17 @@ namespace NewHorizons.Builder.Props
|
||||
// account for scale (this will fix the issue with screen fog caused by scaled down nodes)
|
||||
|
||||
// Set the main scale
|
||||
brambleNode.transform.localScale = Vector3.one * config.scale;
|
||||
// Can't just use localScale of root, that makes the preview fog lights get pulled in too much
|
||||
foreach(Transform child in brambleNode.transform)
|
||||
{
|
||||
child.localScale = Vector3.one * config.scale;
|
||||
|
||||
// The fog on bramble seeds has a specific scale we need to copy over
|
||||
if (child.name == "VolumetricFogSphere (2)")
|
||||
{
|
||||
child.localScale *= 6.3809f;
|
||||
}
|
||||
}
|
||||
innerFogWarpVolume._warpRadius *= config.scale;
|
||||
innerFogWarpVolume._exitRadius *= config.scale;
|
||||
|
||||
@ -259,7 +285,7 @@ namespace NewHorizons.Builder.Props
|
||||
// (it's also located on a different child path, so the below FindChild calls wouldn't work)
|
||||
// Default size is 70
|
||||
var fog = brambleNode.FindChild("Effects/InnerWarpFogSphere");
|
||||
fog.transform.localScale = Vector3.one * config.scale * 70f;
|
||||
//fog.transform.localScale = Vector3.one * config.scale * 70f; This is already scaled by its parent, don't know why we scale it again
|
||||
|
||||
// Copy shared material to not be shared
|
||||
var fogRenderer = fog.GetComponent<MeshRenderer>();
|
||||
@ -393,11 +419,22 @@ namespace NewHorizons.Builder.Props
|
||||
{
|
||||
foreach (var signalConfig in connectedSignals)
|
||||
{
|
||||
var signalGO = SignalBuilder.Make(go, sector, signalConfig, mod);
|
||||
// Have to ensure that this new signal doesn't use parent path, else it looks for a parent that only exists on the original body
|
||||
// Have to make a copy of it as well to avoid modifying the old body's info
|
||||
var signalConfigCopy = JsonConvert.DeserializeObject<SignalInfo>(JsonConvert.SerializeObject(signalConfig));
|
||||
signalConfigCopy.parentPath = null;
|
||||
signalConfigCopy.isRelativeToParent = false;
|
||||
|
||||
var signalGO = SignalBuilder.Make(go, sector, signalConfigCopy, mod);
|
||||
signalGO.GetComponent<AudioSignal>()._identificationDistance = 0;
|
||||
signalGO.GetComponent<AudioSignal>()._sourceRadius = 1;
|
||||
signalGO.transform.position = brambleNode.transform.position;
|
||||
signalGO.transform.parent = brambleNode.transform;
|
||||
|
||||
//Don't need the unknown signal detection bits
|
||||
Component.Destroy(signalGO.GetComponent<AudioSignalDetectionTrigger>());
|
||||
Component.Destroy(signalGO.GetComponent<OWTriggerVolume>());
|
||||
Component.Destroy(signalGO.GetComponent<SphereShape>());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -27,6 +27,19 @@ namespace NewHorizons.Builder.Props
|
||||
SceneManager.sceneUnloaded += SceneManager_sceneUnloaded;
|
||||
}
|
||||
|
||||
#region obsolete
|
||||
// Never change method signatures, people directly reference the NH dll and it can break backwards compatibility
|
||||
// In particular, Outer Wives needs this method signature
|
||||
[Obsolete]
|
||||
public static GameObject Make(GameObject go, Sector sector, GameObject prefab, DetailInfo detail)
|
||||
=> Make(go, sector, mod: null, prefab, detail);
|
||||
|
||||
// Dreamstalker needed this one
|
||||
[Obsolete]
|
||||
public static GameObject Make(GameObject go, Sector sector, DetailInfo detail)
|
||||
=> Make(go, sector, mod: null, detail);
|
||||
#endregion
|
||||
|
||||
private static void SceneManager_sceneUnloaded(Scene scene)
|
||||
{
|
||||
foreach (var prefab in _fixedPrefabCache.Values)
|
||||
@ -52,24 +65,16 @@ namespace NewHorizons.Builder.Props
|
||||
/// <summary>
|
||||
/// Create a detail using an asset bundle or a path in the scene hierarchy of the item to copy.
|
||||
/// </summary>
|
||||
public static GameObject Make(GameObject go, Sector sector, IModBehaviour mod, DetailInfo detail)
|
||||
public static GameObject Make(GameObject planetGO, Sector sector, IModBehaviour mod, DetailInfo info)
|
||||
{
|
||||
if (detail.assetBundle != null)
|
||||
if (info.assetBundle != null)
|
||||
{
|
||||
// Shouldn't happen
|
||||
if (mod == null) return null;
|
||||
|
||||
return Make(go, sector, AssetBundleUtilities.LoadPrefab(detail.assetBundle, detail.path, mod), detail);
|
||||
return Make(planetGO, sector, mod, AssetBundleUtilities.LoadPrefab(info.assetBundle, info.path, mod), info);
|
||||
}
|
||||
else
|
||||
return Make(go, sector, detail);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a detail using a path in the scene hierarchy of the item to copy.
|
||||
/// </summary>
|
||||
public static GameObject Make(GameObject planetGO, Sector sector, DetailInfo info)
|
||||
{
|
||||
if (_emptyPrefab == null) _emptyPrefab = new GameObject("Empty");
|
||||
|
||||
// Allow for empty game objects so you can set up conditional activation on them and parent other props to them
|
||||
@ -82,20 +87,21 @@ namespace NewHorizons.Builder.Props
|
||||
}
|
||||
else
|
||||
{
|
||||
return Make(planetGO, sector, prefab, info);
|
||||
return Make(planetGO, sector, mod, prefab, info);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a detail using a prefab.
|
||||
/// </summary>
|
||||
public static GameObject Make(GameObject go, Sector sector, GameObject prefab, DetailInfo detail)
|
||||
public static GameObject Make(GameObject go, Sector sector, IModBehaviour mod, GameObject prefab, DetailInfo detail)
|
||||
{
|
||||
if (prefab == null) return null;
|
||||
|
||||
GameObject prop;
|
||||
bool isItem;
|
||||
bool invalidComponentFound = false;
|
||||
bool isFromAssetBundle = !string.IsNullOrEmpty(detail.assetBundle);
|
||||
|
||||
// We save copies with all their components fixed, good if the user is placing the same detail more than once
|
||||
if (detail?.path != null && _fixedPrefabCache.TryGetValue((sector, detail.path), out var storedPrefab))
|
||||
@ -134,7 +140,29 @@ namespace NewHorizons.Builder.Props
|
||||
}
|
||||
else
|
||||
{
|
||||
FixSectoredComponent(component, sector, existingSectors, detail.keepLoaded);
|
||||
// Fix cull groups only when not from an asset bundle (because then they're there on purpose!)
|
||||
// keepLoaded should remove existing groups
|
||||
// renderers/colliders get enabled later so we dont have to do that here
|
||||
if (detail.keepLoaded && !isFromAssetBundle && component is SectorCullGroup or SectorCollisionGroup or SectorLightsCullGroup)
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(component);
|
||||
continue;
|
||||
}
|
||||
|
||||
FixSectoredComponent(component, sector, existingSectors);
|
||||
}
|
||||
|
||||
// Asset bundle is a real string -> Object loaded from unity
|
||||
// If they're adding dialogue we have to manually register the xml text
|
||||
if (isFromAssetBundle && component is CharacterDialogueTree dialogue)
|
||||
{
|
||||
DialogueBuilder.HandleUnityCreatedDialogue(dialogue);
|
||||
}
|
||||
|
||||
// copied details need their lanterns fixed
|
||||
if (!isFromAssetBundle && component is DreamLanternController lantern)
|
||||
{
|
||||
lantern.gameObject.AddComponent<DreamLanternControllerFixer>();
|
||||
}
|
||||
|
||||
FixComponent(component, go, detail.ignoreSun);
|
||||
@ -163,6 +191,21 @@ namespace NewHorizons.Builder.Props
|
||||
}
|
||||
}
|
||||
|
||||
if (detail.item != null)
|
||||
{
|
||||
ItemBuilder.MakeItem(prop, go, sector, detail.item, mod);
|
||||
isItem = true;
|
||||
if (detail.hasPhysics)
|
||||
{
|
||||
NHLogger.LogWarning($"An item with the path {detail.path} has both '{nameof(DetailInfo.hasPhysics)}' and '{nameof(DetailInfo.item)}' set. This will usually result in undesirable behavior.");
|
||||
}
|
||||
}
|
||||
|
||||
if (detail.itemSocket != null)
|
||||
{
|
||||
ItemBuilder.MakeSocket(prop, go, sector, detail.itemSocket);
|
||||
}
|
||||
|
||||
// Items should always be kept loaded else they will vanish in your hand as you leave the sector
|
||||
if (isItem) detail.keepLoaded = true;
|
||||
|
||||
@ -251,16 +294,8 @@ namespace NewHorizons.Builder.Props
|
||||
/// <summary>
|
||||
/// Fix components that have sectors. Has a specific fix if there is a VisionTorchItem on the object.
|
||||
/// </summary>
|
||||
private static void FixSectoredComponent(Component component, Sector sector, HashSet<Sector> existingSectors, bool keepLoaded)
|
||||
private static void FixSectoredComponent(Component component, Sector sector, HashSet<Sector> existingSectors)
|
||||
{
|
||||
// keepLoaded should remove existing groups
|
||||
// renderers/colliders get enabled later so we dont have to do that here
|
||||
if (keepLoaded && component is SectorCullGroup or SectorCollisionGroup or SectorLightsCullGroup)
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(component);
|
||||
return;
|
||||
}
|
||||
|
||||
// fix Sector stuff, eg SectorCullGroup (without this, props that have a SectorCullGroup component will become invisible inappropriately)
|
||||
if (component is ISectorGroup sectorGroup && !existingSectors.Contains(sectorGroup.GetSector()))
|
||||
{
|
||||
@ -268,26 +303,8 @@ namespace NewHorizons.Builder.Props
|
||||
}
|
||||
|
||||
// Not doing else if here because idk if any of the classes below implement ISectorGroup
|
||||
|
||||
// Null check else shuttles controls break
|
||||
// parent sector is always null before Awake so this code actually never runs lol
|
||||
if (component is Sector s && s.GetParentSector() != null && !existingSectors.Contains(s.GetParentSector()))
|
||||
{
|
||||
s.SetParentSector(sector);
|
||||
}
|
||||
|
||||
else if (component is SectorCullGroup sectorCullGroup)
|
||||
{
|
||||
sectorCullGroup._controllingProxy = null;
|
||||
|
||||
// fixes sector cull group deactivating renderers on map view enter and fast foward
|
||||
// TODO: does this actually work? what? how?
|
||||
sectorCullGroup._inMapView = false;
|
||||
sectorCullGroup._isFastForwarding = false;
|
||||
sectorCullGroup.SetVisible(sectorCullGroup.ShouldBeVisible(), true, false);
|
||||
}
|
||||
|
||||
else if(component is SectoredMonoBehaviour behaviour && !existingSectors.Contains(behaviour._sector))
|
||||
if(component is SectoredMonoBehaviour behaviour && !existingSectors.Contains(behaviour._sector))
|
||||
{
|
||||
// not using SetSector here because it registers the events twice
|
||||
// perhaps this happens with ISectorGroup.SetSector or Sector.SetParentSector too? idk and nothing seems to break because of it yet
|
||||
@ -339,12 +356,6 @@ namespace NewHorizons.Builder.Props
|
||||
component.gameObject.layer = Layer.IgnoreSun;
|
||||
}
|
||||
}
|
||||
// I forget why this is here
|
||||
else if (component is GhostIK or GhostEffects)
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(component);
|
||||
return;
|
||||
}
|
||||
else if (component is DarkMatterVolume)
|
||||
{
|
||||
var probeVisuals = component.gameObject.transform.Find("ProbeVisuals");
|
||||
@ -400,11 +411,23 @@ namespace NewHorizons.Builder.Props
|
||||
else if (component is Renderer renderer && component.gameObject.GetComponent<ElectricityEffect>() == null) renderer.enabled = true;
|
||||
else if(component is Shape shape) shape.enabled = true;
|
||||
|
||||
// If it's not a moving anglerfish make sure the anim controller is regular
|
||||
else if(component is AnglerfishAnimController && component.transform.parent.GetComponent<AnglerfishController>() == null) //Manual parent chain so we can find inactive
|
||||
// If it's not a moving ghostbird (ie Prefab_IP_GhostBird/Ghostbird_IP_ANIM) make sure it doesnt spam NREs
|
||||
// Manual parent chain so we can find inactive
|
||||
else if (component is GhostIK or GhostEffects && component.transform.parent.GetComponent<GhostBrain>() == null)
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(component);
|
||||
}
|
||||
// If it's not a moving anglerfish (ie Anglerfish_Body/Beast_Anglerfish) make sure the anim controller is regular
|
||||
// Manual parent chain so we can find inactive
|
||||
else if(component is AnglerfishAnimController && component.transform.parent.GetComponent<AnglerfishController>() == null)
|
||||
{
|
||||
component.gameObject.AddComponent<AnglerAnimFixer>();
|
||||
}
|
||||
// Add custom logic to NH-spawned rafts to handle fluid changes
|
||||
else if (component is RaftController raft)
|
||||
{
|
||||
component.gameObject.AddComponent<NHRaftController>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -421,7 +444,7 @@ namespace NewHorizons.Builder.Props
|
||||
|
||||
NHLogger.LogVerbose("Fixing anglerfish animation");
|
||||
|
||||
// Remove any event reference to its angler
|
||||
// Remove any event reference to its angler so that they dont change its state
|
||||
if (angler._anglerfishController)
|
||||
{
|
||||
angler._anglerfishController.OnChangeAnglerState -= angler.OnChangeAnglerState;
|
||||
@ -429,7 +452,8 @@ namespace NewHorizons.Builder.Props
|
||||
angler._anglerfishController.OnAnglerSuspended -= angler.OnAnglerSuspended;
|
||||
angler._anglerfishController.OnAnglerUnsuspended -= angler.OnAnglerUnsuspended;
|
||||
}
|
||||
angler.enabled = true;
|
||||
// Disable the angler anim controller because we don't want Update or LateUpdate to run, just need it to set the initial Animator state
|
||||
angler.enabled = false;
|
||||
angler.OnChangeAnglerState(AnglerfishController.AnglerState.Lurking);
|
||||
|
||||
Destroy(this);
|
||||
@ -483,5 +507,53 @@ namespace NewHorizons.Builder.Props
|
||||
Destroy(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// need component here to run after DreamLanternController.Awake
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(DreamLanternController))]
|
||||
private class DreamLanternControllerFixer : MonoBehaviour
|
||||
{
|
||||
private void Start()
|
||||
{
|
||||
// based on https://github.com/Bwc9876/OW-Amogus/blob/master/Amogus/LanternCreator.cs
|
||||
// needed to fix petals looking backwards, among other things
|
||||
|
||||
var lantern = GetComponent<DreamLanternController>();
|
||||
|
||||
// this is set in Awake, we wanna override it
|
||||
|
||||
// Manually copied these values from a artifact lantern so that we don't have to find it (works in Eye)
|
||||
lantern._origLensFlareBrightness = 0f;
|
||||
lantern._focuserPetalsBaseEulerAngles = new Vector3[]
|
||||
{
|
||||
new Vector3(0.7f, 270.0f, 357.5f),
|
||||
new Vector3(288.7f, 270.1f, 357.4f),
|
||||
new Vector3(323.3f, 90.0f, 177.5f),
|
||||
new Vector3(35.3f, 90.0f, 177.5f),
|
||||
new Vector3(72.7f, 270.1f, 357.5f)
|
||||
};
|
||||
lantern._dirtyFlag_focus = true;
|
||||
lantern._concealerRootsBaseScale = new Vector3[]
|
||||
{
|
||||
Vector3.one,
|
||||
Vector3.one,
|
||||
Vector3.one
|
||||
};
|
||||
lantern._concealerCoversStartPos = new Vector3[]
|
||||
{
|
||||
new Vector3(0.0f, 0.0f, 0.0f),
|
||||
new Vector3(0.0f, -0.1f, 0.0f),
|
||||
new Vector3(0.0f, -0.2f, 0.0f),
|
||||
new Vector3(0.0f, 0.2f, 0.0f),
|
||||
new Vector3(0.0f, 0.1f, 0.0f),
|
||||
new Vector3(0.0f, 0.0f, 0.0f)
|
||||
};
|
||||
lantern._dirtyFlag_concealment = true;
|
||||
lantern.UpdateVisuals();
|
||||
|
||||
Destroy(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,10 +5,15 @@ using NewHorizons.Utility;
|
||||
using NewHorizons.Utility.OuterWilds;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using OWML.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlTypes;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.XR;
|
||||
|
||||
namespace NewHorizons.Builder.Props
|
||||
{
|
||||
@ -25,6 +30,18 @@ namespace NewHorizons.Builder.Props
|
||||
// Create dialogue directly from xml string instead of loading it from a file
|
||||
public static (CharacterDialogueTree, RemoteDialogueTrigger) Make(GameObject go, Sector sector, DialogueInfo info, string xml, string dialogueName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(info.pathToExistingDialogue))
|
||||
{
|
||||
return MakeNewDialogue(go, sector, info, xml, dialogueName);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (AddToExistingDialogue(go, sector, info, xml, dialogueName), null);
|
||||
}
|
||||
}
|
||||
|
||||
private static (CharacterDialogueTree, RemoteDialogueTrigger) MakeNewDialogue(GameObject go, Sector sector, DialogueInfo info, string xml, string dialogueName)
|
||||
{
|
||||
NHLogger.LogVerbose($"[DIALOGUE] Created a new character dialogue [{info.rename}] on [{info.parentPath}]");
|
||||
|
||||
// In stock I think they disable dialogue stuff with conditions
|
||||
@ -36,6 +53,8 @@ namespace NewHorizons.Builder.Props
|
||||
}
|
||||
|
||||
var dialogue = MakeConversationZone(go, sector, info, xml, dialogueName);
|
||||
|
||||
MakeAttentionPoints(go, sector, dialogue, info);
|
||||
|
||||
RemoteDialogueTrigger remoteTrigger = null;
|
||||
if (info.remoteTrigger != null)
|
||||
@ -54,6 +73,177 @@ namespace NewHorizons.Builder.Props
|
||||
return (dialogue, remoteTrigger);
|
||||
}
|
||||
|
||||
private static CharacterDialogueTree AddToExistingDialogue(GameObject go, Sector sector, DialogueInfo info, string xml, string dialogueName)
|
||||
{
|
||||
var dialogueObject = go.FindChild(info.pathToExistingDialogue);
|
||||
if (dialogueObject == null) dialogueObject = SearchUtilities.Find(info.pathToExistingDialogue);
|
||||
var existingDialogue = dialogueObject != null ? dialogueObject.GetComponent<CharacterDialogueTree>() : null;
|
||||
|
||||
if (existingDialogue == null)
|
||||
{
|
||||
NHLogger.LogError($"Couldn't find dialogue at {info.pathToExistingDialogue}!");
|
||||
return null;
|
||||
}
|
||||
|
||||
var existingAsset = existingDialogue._xmlCharacterDialogueAsset;
|
||||
if (existingAsset == null)
|
||||
{
|
||||
var dialogueDoc = new XmlDocument();
|
||||
dialogueDoc.LoadXml(xml);
|
||||
var xmlNode = dialogueDoc.SelectSingleNode("DialogueTree");
|
||||
AddTranslation(xmlNode);
|
||||
|
||||
xml = xmlNode.OuterXml;
|
||||
|
||||
var text = new TextAsset(xml)
|
||||
{
|
||||
// Text assets need a name to be used with VoiceMod
|
||||
name = dialogueName
|
||||
};
|
||||
existingDialogue.SetTextXml(text);
|
||||
|
||||
FixDialogueNextFrame(existingDialogue);
|
||||
|
||||
return existingDialogue;
|
||||
}
|
||||
|
||||
var existingText = existingAsset.text;
|
||||
|
||||
var existingDialogueDoc = new XmlDocument();
|
||||
existingDialogueDoc.LoadXml(existingText);
|
||||
var existingDialogueTree = existingDialogueDoc.DocumentElement.SelectSingleNode("//DialogueTree");
|
||||
|
||||
var existingDialogueNodesByName = new Dictionary<string, XmlNode>();
|
||||
foreach (XmlNode existingDialogueNode in existingDialogueTree.GetChildNodes("DialogueNode"))
|
||||
{
|
||||
var name = existingDialogueNode.GetChildNode("Name").InnerText;
|
||||
existingDialogueNodesByName[name] = existingDialogueNode;
|
||||
}
|
||||
|
||||
var additionalDialogueDoc = new XmlDocument();
|
||||
additionalDialogueDoc.LoadXml(xml);
|
||||
var newDialogueNodes = additionalDialogueDoc.DocumentElement.SelectSingleNode("//DialogueTree").GetChildNodes("DialogueNode");
|
||||
|
||||
foreach (XmlNode newDialogueNode in newDialogueNodes)
|
||||
{
|
||||
var name = newDialogueNode.GetChildNode("Name").InnerText;
|
||||
|
||||
if (existingDialogueNodesByName.TryGetValue(name, out var existingNode))
|
||||
{
|
||||
// We just have to merge the dialogue options
|
||||
var dialogueOptions = newDialogueNode.GetChildNode("DialogueOptionsList").GetChildNodes("DialogueOption");
|
||||
var existingDialogueOptionsList = existingNode.GetChildNode("DialogueOptionsList");
|
||||
if (existingDialogueOptionsList == null)
|
||||
{
|
||||
existingDialogueOptionsList = existingDialogueDoc.CreateElement("DialogueOptionsList");
|
||||
existingNode.AppendChild(existingDialogueOptionsList);
|
||||
}
|
||||
foreach (XmlNode node in dialogueOptions)
|
||||
{
|
||||
var importedNode = existingDialogueOptionsList.OwnerDocument.ImportNode(node, true);
|
||||
// We add them to the start because normally the last option is to return to menu or exit
|
||||
existingDialogueOptionsList.PrependChild(importedNode);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We add the new dialogue node to the existing dialogue
|
||||
var importedNode = existingDialogueTree.OwnerDocument.ImportNode(newDialogueNode, true);
|
||||
existingDialogueTree.AppendChild(importedNode);
|
||||
}
|
||||
}
|
||||
|
||||
// Character name is required for adding translations, something to do with how OW prefixes its dialogue
|
||||
var characterName = existingDialogueTree.SelectSingleNode("NameField").InnerText;
|
||||
AddTranslation(additionalDialogueDoc.GetChildNode("DialogueTree"), characterName);
|
||||
|
||||
var newTextAsset = new TextAsset(existingDialogueDoc.OuterXml)
|
||||
{
|
||||
name = existingDialogue._xmlCharacterDialogueAsset.name
|
||||
};
|
||||
|
||||
existingDialogue.SetTextXml(newTextAsset);
|
||||
|
||||
FixDialogueNextFrame(existingDialogue);
|
||||
|
||||
MakeAttentionPoints(go, sector, existingDialogue, info);
|
||||
|
||||
return existingDialogue;
|
||||
}
|
||||
|
||||
private static void FixDialogueNextFrame(CharacterDialogueTree characterDialogueTree)
|
||||
{
|
||||
Delay.FireOnNextUpdate(() =>
|
||||
{
|
||||
var rawText = characterDialogueTree._xmlCharacterDialogueAsset.text;
|
||||
|
||||
var doc = new XmlDocument();
|
||||
doc.LoadXml(rawText);
|
||||
var dialogueTree = doc.DocumentElement.SelectSingleNode("//DialogueTree");
|
||||
|
||||
DoDialogueOptionsListReplacement(dialogueTree);
|
||||
|
||||
var newTextAsset = new TextAsset(doc.OuterXml)
|
||||
{
|
||||
name = characterDialogueTree._xmlCharacterDialogueAsset.name
|
||||
};
|
||||
|
||||
characterDialogueTree.SetTextXml(newTextAsset);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Always call this after adding translations, else it won't update them properly
|
||||
/// </summary>
|
||||
/// <param name="dialogueTree"></param>
|
||||
private static void DoDialogueOptionsListReplacement(XmlNode dialogueTree)
|
||||
{
|
||||
var optionsListsByName = new Dictionary<string, XmlNode>();
|
||||
var dialogueNodes = dialogueTree.GetChildNodes("DialogueNode");
|
||||
foreach (XmlNode dialogueNode in dialogueNodes)
|
||||
{
|
||||
var optionsList = dialogueNode.GetChildNode("DialogueOptionsList");
|
||||
if (optionsList != null)
|
||||
{
|
||||
var name = dialogueNode.GetChildNode("Name").InnerText;
|
||||
optionsListsByName[name] = optionsList;
|
||||
}
|
||||
}
|
||||
foreach (var (name, optionsList) in optionsListsByName)
|
||||
{
|
||||
var replacement = optionsList.GetChildNode("ReuseDialogueOptionsListFrom");
|
||||
if (replacement != null)
|
||||
{
|
||||
var replacementName = replacement.InnerText;
|
||||
if (optionsListsByName.TryGetValue(replacementName, out var replacementOptionsList))
|
||||
{
|
||||
if (replacementOptionsList.GetChildNode("ReuseDialogueOptionsListFrom") != null)
|
||||
{
|
||||
NHLogger.LogError($"Can not target a node with ReuseDialogueOptionsListFrom that also reuses options when making dialogue. Node {name} cannot reuse the list from {replacement.InnerText}");
|
||||
}
|
||||
var dialogueNode = optionsList.ParentNode;
|
||||
dialogueNode.RemoveChild(optionsList);
|
||||
dialogueNode.AppendChild(replacementOptionsList.Clone());
|
||||
|
||||
// Have to manually fix the translations here
|
||||
var characterName = dialogueTree.SelectSingleNode("NameField").InnerText;
|
||||
|
||||
var xmlText = replacementOptionsList.SelectNodes("DialogueOption/Text");
|
||||
foreach (object option in xmlText)
|
||||
{
|
||||
var optionData = (XmlNode)option;
|
||||
var text = optionData.InnerText.Trim();
|
||||
TranslationHandler.ReuseDialogueTranslation(text, new string[] { characterName, replacementName }, new string[] { characterName, name });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NHLogger.LogError($"Could not reuse dialogue options list from node with Name {replacement.InnerText} to node with Name {name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static RemoteDialogueTrigger MakeRemoteDialogueTrigger(GameObject planetGO, Sector sector, DialogueInfo info, CharacterDialogueTree dialogue)
|
||||
{
|
||||
var conversationTrigger = GeneralPropBuilder.MakeNew("ConversationTrigger", planetGO, sector, info.remoteTrigger, defaultPosition: info.position, defaultParentPath: info.pathToAnimController);
|
||||
@ -109,13 +299,19 @@ namespace NewHorizons.Builder.Props
|
||||
|
||||
var dialogueTree = conversationZone.AddComponent<NHCharacterDialogueTree>();
|
||||
|
||||
var dialogueDoc = new XmlDocument();
|
||||
dialogueDoc.LoadXml(xml);
|
||||
var xmlNode = dialogueDoc.SelectSingleNode("DialogueTree");
|
||||
AddTranslation(xmlNode);
|
||||
|
||||
xml = xmlNode.OuterXml;
|
||||
|
||||
var text = new TextAsset(xml)
|
||||
{
|
||||
// Text assets need a name to be used with VoiceMod
|
||||
name = dialogueName
|
||||
};
|
||||
dialogueTree.SetTextXml(text);
|
||||
AddTranslation(xml);
|
||||
|
||||
switch (info.flashlightToggle)
|
||||
{
|
||||
@ -136,9 +332,37 @@ namespace NewHorizons.Builder.Props
|
||||
|
||||
conversationZone.SetActive(true);
|
||||
|
||||
FixDialogueNextFrame(dialogueTree);
|
||||
|
||||
return dialogueTree;
|
||||
}
|
||||
|
||||
private static void MakeAttentionPoints(GameObject go, Sector sector, CharacterDialogueTree dialogue, DialogueInfo info)
|
||||
{
|
||||
if (info.attentionPoint != null)
|
||||
{
|
||||
var ptGo = GeneralPropBuilder.MakeNew("AttentionPoint", go, sector, info.attentionPoint, defaultParent: dialogue.transform);
|
||||
dialogue._attentionPoint = ptGo.transform;
|
||||
dialogue._attentionPointOffset = info.attentionPoint.offset;
|
||||
ptGo.SetActive(true);
|
||||
}
|
||||
if (info.swappedAttentionPoints != null && info.swappedAttentionPoints.Length > 0)
|
||||
{
|
||||
foreach (var pointInfo in info.swappedAttentionPoints)
|
||||
{
|
||||
var ptGo = GeneralPropBuilder.MakeNew($"AttentionPoint_{pointInfo.dialogueNode}_{pointInfo.dialoguePage}", go, sector, pointInfo, defaultParent: dialogue.transform);
|
||||
var swapper = ptGo.AddComponent<DialogueAttentionPointSwapper>();
|
||||
swapper._dialogueTree = dialogue;
|
||||
swapper._attentionPoint = ptGo.transform;
|
||||
swapper._attentionPointOffset = pointInfo.offset;
|
||||
swapper._nodeName = pointInfo.dialogueNode;
|
||||
swapper._dialoguePage = pointInfo.dialoguePage;
|
||||
swapper._lookEasing = pointInfo.lookEasing;
|
||||
ptGo.SetActive(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void MakePlayerTrackingZone(GameObject go, CharacterDialogueTree dialogue, DialogueInfo info)
|
||||
{
|
||||
var character = go.transform.Find(info.pathToAnimController);
|
||||
@ -300,14 +524,26 @@ namespace NewHorizons.Builder.Props
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddTranslation(string xml)
|
||||
[Obsolete("Pass in the DialogueTree XmlNode instead, this is still here because Pikpik was using it in EOTP")]
|
||||
public static void AddTranslation(string xml, string characterName = null)
|
||||
{
|
||||
var xmlDocument = new XmlDocument();
|
||||
xmlDocument.LoadXml(xml);
|
||||
var xmlNode = xmlDocument.SelectSingleNode("DialogueTree");
|
||||
AddTranslation(xmlNode, characterName);
|
||||
}
|
||||
|
||||
public static void AddTranslation(XmlNode xmlNode, string characterName = null)
|
||||
{
|
||||
var xmlNodeList = xmlNode.SelectNodes("DialogueNode");
|
||||
string characterName = xmlNode.SelectSingleNode("NameField").InnerText;
|
||||
TranslationHandler.AddDialogue(characterName);
|
||||
|
||||
// When adding dialogue to existing stuff, we have to pass in the character name
|
||||
// Otherwise we translate it if its from a new dialogue object
|
||||
if (characterName == null)
|
||||
{
|
||||
characterName = xmlNode.SelectSingleNode("NameField").InnerText;
|
||||
TranslationHandler.AddDialogue(characterName);
|
||||
}
|
||||
|
||||
foreach (object obj in xmlNodeList)
|
||||
{
|
||||
@ -333,5 +569,23 @@ namespace NewHorizons.Builder.Props
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void HandleUnityCreatedDialogue(CharacterDialogueTree dialogue)
|
||||
{
|
||||
var asset = dialogue._xmlCharacterDialogueAsset;
|
||||
if (asset == null) return;
|
||||
var text = asset.text;
|
||||
var dialogueDoc = new XmlDocument();
|
||||
dialogueDoc.LoadXml(text);
|
||||
var xmlNode = dialogueDoc.SelectSingleNode("DialogueTree");
|
||||
AddTranslation(xmlNode, null);
|
||||
var newTextAsset = new TextAsset(dialogueDoc.OuterXml)
|
||||
{
|
||||
name = asset.name
|
||||
};
|
||||
dialogue.SetTextXml(newTextAsset);
|
||||
|
||||
FixDialogueNextFrame(dialogue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,11 +11,11 @@ namespace NewHorizons.Builder.Props
|
||||
{
|
||||
public static GameObject MakeFromExisting(GameObject go,
|
||||
GameObject planetGO, Sector sector, GeneralPointPropInfo info,
|
||||
MVector3 defaultPosition = null, string defaultParentPath = null, Transform parentOverride = null)
|
||||
MVector3 defaultPosition = null, string defaultParentPath = null, Transform defaultParent = null)
|
||||
{
|
||||
if (info == null) return go;
|
||||
|
||||
go.transform.parent = parentOverride ?? sector?.transform ?? planetGO?.transform;
|
||||
go.transform.parent = defaultParent ?? sector?.transform ?? planetGO?.transform;
|
||||
|
||||
if (info is GeneralSolarSystemPropInfo solarSystemInfo && !string.IsNullOrEmpty(solarSystemInfo.parentBody))
|
||||
{
|
||||
@ -87,20 +87,20 @@ namespace NewHorizons.Builder.Props
|
||||
|
||||
public static GameObject MakeNew(string defaultName,
|
||||
GameObject planetGO, Sector sector, GeneralPointPropInfo info,
|
||||
MVector3 defaultPosition = null, string defaultParentPath = null, Transform parentOverride = null)
|
||||
MVector3 defaultPosition = null, string defaultParentPath = null, Transform defaultParent = null)
|
||||
{
|
||||
var go = new GameObject(defaultName);
|
||||
go.SetActive(false);
|
||||
return MakeFromExisting(go, planetGO, sector, info, defaultPosition, defaultParentPath, parentOverride);
|
||||
return MakeFromExisting(go, planetGO, sector, info, defaultPosition, defaultParentPath, defaultParent);
|
||||
}
|
||||
|
||||
public static GameObject MakeFromPrefab(GameObject prefab, string defaultName,
|
||||
GameObject planetGO, Sector sector, GeneralPointPropInfo info,
|
||||
MVector3 defaultPosition = null, string defaultParentPath = null, Transform parentOverride = null)
|
||||
MVector3 defaultPosition = null, string defaultParentPath = null, Transform defaultParent = null)
|
||||
{
|
||||
var go = prefab.InstantiateInactive();
|
||||
go.name = defaultName;
|
||||
return MakeFromExisting(go, planetGO, sector, info, defaultPosition, defaultParentPath, parentOverride);
|
||||
return MakeFromExisting(go, planetGO, sector, info, defaultPosition, defaultParentPath, defaultParent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
using NewHorizons.Builder.Props.TranslatorText;
|
||||
using NewHorizons.Components.Orbital;
|
||||
using NewHorizons.External.Modules;
|
||||
using NewHorizons.External.Modules.Props;
|
||||
using NewHorizons.External.Modules.Props.Shuttle;
|
||||
@ -18,7 +17,6 @@ namespace NewHorizons.Builder.Props
|
||||
{
|
||||
private static GameObject _interfacePrefab;
|
||||
private static GameObject _detailedPlatformPrefab, _platformPrefab;
|
||||
private static GameObject _orbPrefab;
|
||||
|
||||
internal static void InitPrefab()
|
||||
{
|
||||
@ -59,24 +57,16 @@ namespace NewHorizons.Builder.Props
|
||||
GameObject.DestroyImmediate(_platformPrefab.FindChild("Structure_NOM_GravityCannon_Crystals"));
|
||||
GameObject.DestroyImmediate(_platformPrefab.FindChild("Structure_NOM_GravityCannon_Geo"));
|
||||
}
|
||||
|
||||
if (_orbPrefab == null)
|
||||
{
|
||||
_orbPrefab = SearchUtilities.Find("Prefab_NOM_InterfaceOrb")
|
||||
.InstantiateInactive()
|
||||
.Rename("Prefab_NOM_InterfaceOrb")
|
||||
.DontDestroyOnLoad();
|
||||
}
|
||||
}
|
||||
|
||||
public static GameObject Make(GameObject planetGO, Sector sector, GravityCannonInfo info, IModBehaviour mod)
|
||||
{
|
||||
InitPrefab();
|
||||
|
||||
if (_interfacePrefab == null || planetGO == null || sector == null || _detailedPlatformPrefab == null || _platformPrefab == null || _orbPrefab == null) return null;
|
||||
if (_interfacePrefab == null || planetGO == null || sector == null || _detailedPlatformPrefab == null || _platformPrefab == null) return null;
|
||||
|
||||
var detailInfo = new DetailInfo(info.controls) { keepLoaded = true };
|
||||
var gravityCannonObject = DetailBuilder.Make(planetGO, sector, _interfacePrefab, detailInfo);
|
||||
var gravityCannonObject = DetailBuilder.Make(planetGO, sector, mod, _interfacePrefab, detailInfo);
|
||||
gravityCannonObject.SetActive(false);
|
||||
|
||||
var gravityCannonController = gravityCannonObject.GetComponent<GravityCannonController>();
|
||||
@ -87,7 +77,7 @@ namespace NewHorizons.Builder.Props
|
||||
gravityCannonController._retrieveShipLogFact = info.retrieveReveal ?? string.Empty;
|
||||
gravityCannonController._launchShipLogFact = info.launchReveal ?? string.Empty;
|
||||
|
||||
CreatePlatform(planetGO, sector, gravityCannonController, info);
|
||||
CreatePlatform(planetGO, sector, mod, gravityCannonController, info);
|
||||
|
||||
if (info.computer != null)
|
||||
{
|
||||
@ -102,56 +92,11 @@ namespace NewHorizons.Builder.Props
|
||||
gravityCannonController._nomaiComputer = null;
|
||||
}
|
||||
|
||||
CreateOrb(planetGO, gravityCannonController);
|
||||
|
||||
gravityCannonObject.SetActive(true);
|
||||
|
||||
return gravityCannonObject;
|
||||
}
|
||||
|
||||
private static void CreateOrb(GameObject planetGO, GravityCannonController gravityCannonController)
|
||||
{
|
||||
var orb = _orbPrefab.InstantiateInactive().Rename(_orbPrefab.name);
|
||||
orb.transform.parent = gravityCannonController.transform;
|
||||
orb.transform.localPosition = new Vector3(0f, 0.9673f, 0f);
|
||||
orb.transform.localScale = Vector3.one;
|
||||
orb.SetActive(true);
|
||||
|
||||
var planetBody = planetGO.GetComponent<OWRigidbody>();
|
||||
var orbBody = orb.GetComponent<OWRigidbody>();
|
||||
|
||||
var nomaiInterfaceOrb = orb.GetComponent<NomaiInterfaceOrb>();
|
||||
nomaiInterfaceOrb._orbBody = orbBody;
|
||||
nomaiInterfaceOrb._slotRoot = gravityCannonController.gameObject;
|
||||
orbBody._origParent = planetGO.transform;
|
||||
orbBody._origParentBody = planetBody;
|
||||
nomaiInterfaceOrb._slots = nomaiInterfaceOrb._slotRoot.GetComponentsInChildren<NomaiInterfaceSlot>();
|
||||
nomaiInterfaceOrb.SetParentBody(planetBody);
|
||||
nomaiInterfaceOrb._safetyRails = new OWRail[0];
|
||||
nomaiInterfaceOrb.RemoveAllLocks();
|
||||
|
||||
var spawnVelocity = planetBody.GetVelocity();
|
||||
var spawnAngularVelocity = planetBody.GetPointTangentialVelocity(orbBody.transform.position);
|
||||
var velocity = spawnVelocity + spawnAngularVelocity;
|
||||
|
||||
orbBody._lastVelocity = velocity;
|
||||
orbBody._currentVelocity = velocity;
|
||||
|
||||
// detect planet gravity
|
||||
// somehow Intervention has GetAttachedOWRigidbody as null sometimes, idk why
|
||||
var gravityVolume = planetGO.GetAttachedOWRigidbody()?.GetAttachedGravityVolume();
|
||||
orb.GetComponent<ConstantForceDetector>()._detectableFields = gravityVolume ? new ForceVolume[] { gravityVolume } : new ForceVolume[0];
|
||||
|
||||
Delay.RunWhenAndInNUpdates(() =>
|
||||
{
|
||||
foreach (var component in orb.GetComponents<MonoBehaviour>())
|
||||
{
|
||||
component.enabled = true;
|
||||
}
|
||||
nomaiInterfaceOrb.RemoveAllLocks();
|
||||
}, () => Main.IsSystemReady, 10);
|
||||
}
|
||||
|
||||
private static NomaiComputer CreateComputer(GameObject planetGO, Sector sector, GeneralPropInfo computerInfo, NomaiShuttleController.ShuttleID id)
|
||||
{
|
||||
// Load the position info from the GeneralPropInfo
|
||||
@ -175,9 +120,9 @@ namespace NewHorizons.Builder.Props
|
||||
return computer;
|
||||
}
|
||||
|
||||
private static GameObject CreatePlatform(GameObject planetGO, Sector sector, GravityCannonController gravityCannonController, GravityCannonInfo platformInfo)
|
||||
private static GameObject CreatePlatform(GameObject planetGO, Sector sector, IModBehaviour mod, GravityCannonController gravityCannonController, GravityCannonInfo platformInfo)
|
||||
{
|
||||
var platform = DetailBuilder.Make(planetGO, sector, platformInfo.detailed ? _detailedPlatformPrefab : _platformPrefab, new DetailInfo(platformInfo) { keepLoaded = true });
|
||||
var platform = DetailBuilder.Make(planetGO, sector, mod, platformInfo.detailed ? _detailedPlatformPrefab : _platformPrefab, new DetailInfo(platformInfo) { keepLoaded = true });
|
||||
|
||||
gravityCannonController._forceVolume = platform.FindChild("ForceVolume").GetComponent<DirectionalForceVolume>();
|
||||
gravityCannonController._platformTrigger = platform.FindChild("PlatformTrigger").GetComponent<OWTriggerVolume>();
|
||||
|
||||
194
NewHorizons/Builder/Props/ItemBuilder.cs
Normal file
194
NewHorizons/Builder/Props/ItemBuilder.cs
Normal file
@ -0,0 +1,194 @@
|
||||
using NewHorizons.Components.Props;
|
||||
using NewHorizons.External.Modules.Props.Item;
|
||||
using NewHorizons.Handlers;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using OWML.Common;
|
||||
using OWML.Utils;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Builder.Props
|
||||
{
|
||||
public static class ItemBuilder
|
||||
{
|
||||
private static Dictionary<string, ItemType> _itemTypes;
|
||||
|
||||
internal static void Init()
|
||||
{
|
||||
if (_itemTypes != null)
|
||||
{
|
||||
foreach (var value in _itemTypes.Values)
|
||||
{
|
||||
EnumUtils.Remove<ItemType>(value);
|
||||
}
|
||||
}
|
||||
_itemTypes = new Dictionary<string, ItemType>();
|
||||
}
|
||||
|
||||
public static NHItem MakeItem(GameObject go, GameObject planetGO, Sector sector, ItemInfo info, IModBehaviour mod)
|
||||
{
|
||||
var itemName = info.name;
|
||||
if (string.IsNullOrEmpty(itemName))
|
||||
{
|
||||
itemName = go.name;
|
||||
}
|
||||
|
||||
var itemTypeName = info.itemType;
|
||||
if (string.IsNullOrEmpty(itemTypeName))
|
||||
{
|
||||
itemTypeName = itemName;
|
||||
}
|
||||
|
||||
var itemType = GetOrCreateItemType(itemTypeName);
|
||||
|
||||
var item = go.GetAddComponent<NHItem>();
|
||||
item._sector = sector;
|
||||
item._interactable = info.interactRange > 0f;
|
||||
item._interactRange = info.interactRange;
|
||||
item._localDropOffset = info.dropOffset ?? Vector3.zero;
|
||||
item._localDropNormal = info.dropNormal ?? Vector3.up;
|
||||
|
||||
item.DisplayName = itemName;
|
||||
item.ItemType = itemType;
|
||||
item.Droppable = info.droppable;
|
||||
item.HoldOffset = info.holdOffset ?? Vector3.zero;
|
||||
item.HoldRotation = info.holdRotation ?? Vector3.zero;
|
||||
item.SocketOffset = info.socketOffset ?? Vector3.zero;
|
||||
item.SocketRotation = info.socketRotation ?? Vector3.zero;
|
||||
if (!string.IsNullOrEmpty(info.pickupAudio))
|
||||
{
|
||||
item.PickupAudio = AudioTypeHandler.GetAudioType(info.pickupAudio, mod);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(info.dropAudio))
|
||||
{
|
||||
item.DropAudio = AudioTypeHandler.GetAudioType(info.dropAudio, mod);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(info.socketAudio))
|
||||
{
|
||||
item.SocketAudio = AudioTypeHandler.GetAudioType(info.socketAudio, mod);
|
||||
}
|
||||
else
|
||||
{
|
||||
item.SocketAudio = item.DropAudio;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(info.unsocketAudio))
|
||||
{
|
||||
item.UnsocketAudio = AudioTypeHandler.GetAudioType(info.unsocketAudio, mod);
|
||||
}
|
||||
else
|
||||
{
|
||||
item.UnsocketAudio = item.PickupAudio;
|
||||
}
|
||||
item.PickupCondition = info.pickupCondition;
|
||||
item.ClearPickupConditionOnDrop = info.clearPickupConditionOnDrop;
|
||||
item.PickupFact = info.pickupFact;
|
||||
|
||||
if (info.colliderRadius > 0f)
|
||||
{
|
||||
go.AddComponent<SphereCollider>().radius = info.colliderRadius;
|
||||
go.GetAddComponent<OWCollider>();
|
||||
}
|
||||
|
||||
Delay.FireOnNextUpdate(() =>
|
||||
{
|
||||
if (item != null && !string.IsNullOrEmpty(info.pathToInitialSocket))
|
||||
{
|
||||
var socketGO = planetGO.transform.Find(info.pathToInitialSocket);
|
||||
if (socketGO != null)
|
||||
{
|
||||
var socket = socketGO.GetComponent<OWItemSocket>();
|
||||
if (socket != null)
|
||||
{
|
||||
if (socket.PlaceIntoSocket(item))
|
||||
{
|
||||
// Successfully socketed
|
||||
}
|
||||
else
|
||||
{
|
||||
NHLogger.LogError($"Could not insert item {itemName} into socket at path {socketGO}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NHLogger.LogError($"Could not find a socket to parent item {itemName} to at path {socketGO}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NHLogger.LogError($"Could not find a socket to parent item {itemName} to at path {socketGO}");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static NHItemSocket MakeSocket(GameObject go, GameObject planetGO, Sector sector, ItemSocketInfo info)
|
||||
{
|
||||
var itemType = EnumUtils.TryParse(info.itemType, true, out ItemType result) ? result : ItemType.Invalid;
|
||||
if (itemType == ItemType.Invalid && !string.IsNullOrEmpty(info.itemType))
|
||||
{
|
||||
itemType = EnumUtilities.Create<ItemType>(info.itemType);
|
||||
}
|
||||
|
||||
var socket = go.GetAddComponent<NHItemSocket>();
|
||||
socket._sector = sector;
|
||||
socket._interactable = info.interactRange > 0f;
|
||||
socket._interactRange = info.interactRange;
|
||||
|
||||
if (!string.IsNullOrEmpty(info.socketPath))
|
||||
{
|
||||
socket._socketTransform = go.transform.Find(info.socketPath);
|
||||
}
|
||||
if (socket._socketTransform == null)
|
||||
{
|
||||
var socketGO = GeneralPropBuilder.MakeNew("Socket", planetGO, sector, info, defaultParent: go.transform);
|
||||
socketGO.SetActive(true);
|
||||
socket._socketTransform = socketGO.transform;
|
||||
}
|
||||
|
||||
socket.ItemType = itemType;
|
||||
socket.UseGiveTakePrompts = info.useGiveTakePrompts;
|
||||
socket.InsertCondition = info.insertCondition;
|
||||
socket.ClearInsertConditionOnRemoval = info.clearInsertConditionOnRemoval;
|
||||
socket.InsertFact = info.insertFact;
|
||||
socket.RemovalCondition = info.removalCondition;
|
||||
socket.ClearRemovalConditionOnInsert = info.clearRemovalConditionOnInsert;
|
||||
socket.RemovalFact = info.removalFact;
|
||||
|
||||
Delay.FireInNUpdates(() =>
|
||||
{
|
||||
if (socket != null && !socket._socketedItem)
|
||||
{
|
||||
socket.TriggerRemovalConditions();
|
||||
}
|
||||
}, 2);
|
||||
|
||||
return socket;
|
||||
}
|
||||
|
||||
public static ItemType GetOrCreateItemType(string name)
|
||||
{
|
||||
var itemType = ItemType.Invalid;
|
||||
if (_itemTypes.ContainsKey(name))
|
||||
{
|
||||
itemType = _itemTypes[name];
|
||||
}
|
||||
else if (EnumUtils.TryParse(name, true, out ItemType result))
|
||||
{
|
||||
itemType = result;
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(name))
|
||||
{
|
||||
itemType = EnumUtils.Create<ItemType>(name);
|
||||
_itemTypes.Add(name, itemType);
|
||||
}
|
||||
return itemType;
|
||||
}
|
||||
|
||||
public static bool IsCustomItemType(ItemType type)
|
||||
{
|
||||
return _itemTypes.ContainsValue(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -376,7 +376,7 @@ namespace NewHorizons.Builder.Props
|
||||
}
|
||||
case NomaiTextType.PreCrashComputer:
|
||||
{
|
||||
var computerObject = DetailBuilder.Make(planetGO, sector, _preCrashComputerPrefab, new DetailInfo(info));
|
||||
var computerObject = DetailBuilder.Make(planetGO, sector, mod, _preCrashComputerPrefab, new DetailInfo(info));
|
||||
computerObject.SetActive(false);
|
||||
|
||||
var up = computerObject.transform.position - planetGO.transform.position;
|
||||
@ -493,7 +493,7 @@ namespace NewHorizons.Builder.Props
|
||||
case NomaiTextType.Recorder:
|
||||
{
|
||||
var prefab = (info.type == NomaiTextType.PreCrashRecorder ? _preCrashRecorderPrefab : _recorderPrefab);
|
||||
var recorderObject = DetailBuilder.Make(planetGO, sector, prefab, new DetailInfo(info));
|
||||
var recorderObject = DetailBuilder.Make(planetGO, sector, mod, prefab, new DetailInfo(info));
|
||||
recorderObject.SetActive(false);
|
||||
|
||||
if (info.rotation == null)
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
using HarmonyLib;
|
||||
using NewHorizons.External.Modules.Props;
|
||||
using NewHorizons.External.Modules.Props.EchoesOfTheEye;
|
||||
using NewHorizons.Handlers;
|
||||
@ -8,14 +9,35 @@ using OWML.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using static NewHorizons.Main;
|
||||
|
||||
namespace NewHorizons.Builder.Props
|
||||
{
|
||||
public static class ProjectionBuilder
|
||||
{
|
||||
private static GameObject _slideReelPrefab;
|
||||
public const string INVERTED_SLIDE_CACHE_FOLDER = "SlideReelCache/Inverted";
|
||||
public const string ATLAS_SLIDE_CACHE_FOLDER = "SlideReelCache/Atlas";
|
||||
|
||||
public static GameObject SlideReelWholePrefab { get; private set; }
|
||||
public static GameObject SlideReelWholePristinePrefab { get; private set; }
|
||||
public static GameObject SlideReelWholeRustedPrefab { get; private set; }
|
||||
public static GameObject SlideReelWholeDestroyedPrefab { get; private set; }
|
||||
public static GameObject SlideReel8Prefab { get; private set; }
|
||||
public static GameObject SlideReel8PristinePrefab { get; private set; }
|
||||
public static GameObject SlideReel8RustedPrefab { get; private set; }
|
||||
public static GameObject SlideReel8DestroyedPrefab { get; private set; }
|
||||
public static GameObject SlideReel7Prefab { get; private set; }
|
||||
public static GameObject SlideReel7PristinePrefab { get; private set; }
|
||||
public static GameObject SlideReel7RustedPrefab { get; private set; }
|
||||
public static GameObject SlideReel7DestroyedPrefab { get; private set; }
|
||||
public static GameObject SlideReel6Prefab { get; private set; }
|
||||
public static GameObject SlideReel6PristinePrefab { get; private set; }
|
||||
public static GameObject SlideReel6RustedPrefab { get; private set; }
|
||||
public static GameObject SlideReel6DestroyedPrefab { get; private set; }
|
||||
|
||||
private static GameObject _autoPrefab;
|
||||
private static GameObject _visionTorchDetectorPrefab;
|
||||
private static GameObject _standingVisionTorchPrefab;
|
||||
@ -23,20 +45,30 @@ namespace NewHorizons.Builder.Props
|
||||
|
||||
private static bool _isInit;
|
||||
|
||||
public static bool CacheExists(IModBehaviour mod) => Directory.Exists(Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ATLAS_SLIDE_CACHE_FOLDER));
|
||||
|
||||
internal static void InitPrefabs()
|
||||
{
|
||||
if (_isInit) return;
|
||||
|
||||
_isInit = true;
|
||||
|
||||
if (_slideReelPrefab == null)
|
||||
{
|
||||
_slideReelPrefab = SearchUtilities.Find("RingWorld_Body/Sector_RingInterior/Sector_Zone1/Sector_SlideBurningRoom_Zone1/Interactables_SlideBurningRoom_Zone1/Prefab_IP_SecretAlcove/RotationPivot/SlideReelSocket/Prefab_IP_Reel_1_LibraryPath")?.gameObject?.InstantiateInactive()?.Rename("Prefab_IP_Reel")?.DontDestroyOnLoad();
|
||||
if (_slideReelPrefab == null)
|
||||
NHLogger.LogWarning($"Tried to make slide reel prefab but couldn't. Do you have the DLC installed?");
|
||||
else
|
||||
_slideReelPrefab.AddComponent<DestroyOnDLC>()._destroyOnDLCNotOwned = true;
|
||||
}
|
||||
SlideReelWholePrefab = NHPrivateAssetBundle.LoadAsset<GameObject>("Prefab_IP_Reel_Whole");
|
||||
SlideReelWholePristinePrefab = NHPrivateAssetBundle.LoadAsset<GameObject>("Prefab_DW_Reel_Whole");
|
||||
SlideReelWholeRustedPrefab = NHPrivateAssetBundle.LoadAsset<GameObject>("Prefab_IP_Reel_Rusted_Whole");
|
||||
SlideReelWholeDestroyedPrefab = NHPrivateAssetBundle.LoadAsset<GameObject>("Prefab_IP_Reel_Destroyed_Whole");
|
||||
SlideReel8Prefab = NHPrivateAssetBundle.LoadAsset<GameObject>("Prefab_IP_Reel_8");
|
||||
SlideReel8PristinePrefab = NHPrivateAssetBundle.LoadAsset<GameObject>("Prefab_DW_Reel_8");
|
||||
SlideReel8RustedPrefab = NHPrivateAssetBundle.LoadAsset<GameObject>("Prefab_IP_Reel_Rusted_8");
|
||||
SlideReel8DestroyedPrefab = NHPrivateAssetBundle.LoadAsset<GameObject>("Prefab_IP_Reel_Destroyed_8");
|
||||
SlideReel7Prefab = NHPrivateAssetBundle.LoadAsset<GameObject>("Prefab_IP_Reel_7");
|
||||
SlideReel7PristinePrefab = NHPrivateAssetBundle.LoadAsset<GameObject>("Prefab_DW_Reel_7");
|
||||
SlideReel7RustedPrefab = NHPrivateAssetBundle.LoadAsset<GameObject>("Prefab_IP_Reel_Rusted_7");
|
||||
SlideReel7DestroyedPrefab = NHPrivateAssetBundle.LoadAsset<GameObject>("Prefab_IP_Reel_Destroyed_7");
|
||||
SlideReel6Prefab = NHPrivateAssetBundle.LoadAsset<GameObject>("Prefab_IP_Reel_6");
|
||||
SlideReel6PristinePrefab = NHPrivateAssetBundle.LoadAsset<GameObject>("Prefab_DW_Reel_6");
|
||||
SlideReel6RustedPrefab = NHPrivateAssetBundle.LoadAsset<GameObject>("Prefab_IP_Reel_Rusted_6");
|
||||
SlideReel6DestroyedPrefab = NHPrivateAssetBundle.LoadAsset<GameObject>("Prefab_IP_Reel_Destroyed_6");
|
||||
|
||||
if (_autoPrefab == null)
|
||||
{
|
||||
@ -88,13 +120,17 @@ namespace NewHorizons.Builder.Props
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetUniqueSlideReelID(IModBehaviour mod, SlideInfo[] slides) => $"{mod.ModHelper.Manifest.UniqueName}{slides.Join(x => x.imagePath)}".GetHashCode().ToString();
|
||||
|
||||
private static GameObject MakeSlideReel(GameObject planetGO, Sector sector, ProjectionInfo info, IModBehaviour mod)
|
||||
{
|
||||
InitPrefabs();
|
||||
|
||||
if (_slideReelPrefab == null) return null;
|
||||
GameObject prefab = GetSlideReelPrefab(info.reelModel, info.reelCondition);
|
||||
|
||||
var slideReelObj = GeneralPropBuilder.MakeFromPrefab(_slideReelPrefab, $"Prefab_IP_Reel_{mod.ModHelper.Manifest.Name}", planetGO, sector, info);
|
||||
if (prefab == null) return null;
|
||||
|
||||
var slideReelObj = GeneralPropBuilder.MakeFromPrefab(prefab, $"Prefab_IP_Reel_{GetSlideReelName(info.reelModel, info.reelCondition)}_{mod.ModHelper.Manifest.Name}", planetGO, sector, info);
|
||||
|
||||
var slideReel = slideReelObj.GetComponent<SlideReelItem>();
|
||||
slideReel.SetSector(sector);
|
||||
@ -110,42 +146,84 @@ namespace NewHorizons.Builder.Props
|
||||
// Now we replace the slides
|
||||
int slidesCount = info.slides.Length;
|
||||
var slideCollection = new SlideCollection(slidesCount);
|
||||
slideCollection.streamingAssetIdentifier = string.Empty; // NREs if null
|
||||
|
||||
// The base game ones only have 15 slides max
|
||||
var textures = new Texture2D[slidesCount >= 15 ? 15 : slidesCount];
|
||||
// We can fit 16 slides max into an atlas
|
||||
var textures = new Texture2D[slidesCount > 16 ? 16 : slidesCount];
|
||||
|
||||
var imageLoader = AddAsyncLoader(slideReelObj, mod, info.slides, ref slideCollection);
|
||||
var (invImageLoader, atlasImageLoader, imageLoader) = StartAsyncLoader(mod, info.slides, ref slideCollection, true, true);
|
||||
|
||||
// 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
|
||||
// to avoid doing a "is every element in the array `textures` not null" check every time a texture finishes loading
|
||||
int displaySlidesLoaded = 0;
|
||||
imageLoader.imageLoadedEvent.AddListener(
|
||||
(Texture2D tex, int index) =>
|
||||
{
|
||||
slideCollection.slides[index]._image = ImageUtilities.Invert(tex);
|
||||
// If the cache doesn't exist it will be created here, slide reels only use the base image loader for cache creation so delete the images after to free memory
|
||||
imageLoader.deleteTexturesWhenDone = !CacheExists(mod);
|
||||
|
||||
// Track the first 15 to put on the slide reel object
|
||||
if (index < textures.Length)
|
||||
var key = GetUniqueSlideReelID(mod, info.slides);
|
||||
|
||||
if (invImageLoader != null && atlasImageLoader != null)
|
||||
{
|
||||
// Loading directly from cache
|
||||
invImageLoader.imageLoadedEvent.AddListener(
|
||||
(Texture2D tex, int index, string originalPath) =>
|
||||
{
|
||||
slideCollection.slides[index]._image = tex;
|
||||
}
|
||||
);
|
||||
atlasImageLoader.imageLoadedEvent.AddListener(
|
||||
(Texture2D tex, int _, string originalPath) =>
|
||||
{
|
||||
// all textures required to build the reel's textures have been loaded
|
||||
var slidesBack = slideReelObj.GetComponentInChildren<TransformAnimator>(true).transform.Find("Slides_Back").GetComponent<MeshRenderer>();
|
||||
var slidesFront = slideReelObj.GetComponentInChildren<TransformAnimator>(true).transform.Find("Slides_Front").GetComponent<MeshRenderer>();
|
||||
|
||||
// Now put together the textures into a 4x4 thing for the materials
|
||||
var reelTexture = tex;
|
||||
slidesBack.material.mainTexture = reelTexture;
|
||||
slidesBack.material.SetTexture(EmissionMap, reelTexture);
|
||||
slidesBack.material.name = reelTexture.name;
|
||||
slidesFront.material.mainTexture = reelTexture;
|
||||
slidesFront.material.SetTexture(EmissionMap, reelTexture);
|
||||
slidesFront.material.name = reelTexture.name;
|
||||
}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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
|
||||
// to avoid doing a "is every element in the array `textures` not null" check every time a texture finishes loading
|
||||
int displaySlidesLoaded = 0;
|
||||
imageLoader.imageLoadedEvent.AddListener(
|
||||
(Texture2D tex, int index, string originalPath) =>
|
||||
{
|
||||
var time = DateTime.Now;
|
||||
|
||||
slideCollection.slides[index]._image = ImageUtilities.InvertSlideReel(mod, tex, originalPath);
|
||||
NHLogger.LogVerbose($"Slide reel make reel invert texture {(DateTime.Now - time).TotalMilliseconds}ms");
|
||||
// Track the first 16 to put on the slide reel object
|
||||
if (index < textures.Length)
|
||||
{
|
||||
textures[index] = tex;
|
||||
if (Interlocked.Increment(ref displaySlidesLoaded) == textures.Length)
|
||||
displaySlidesLoaded++;
|
||||
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<MeshRenderer>();
|
||||
var slidesFront = slideReelObj.transform.Find("Props_IP_SlideReel_7/Slides_Front").GetComponent<MeshRenderer>();
|
||||
var slidesBack = slideReelObj.GetComponentInChildren<TransformAnimator>(true).transform.Find("Slides_Back").GetComponent<MeshRenderer>();
|
||||
var slidesFront = slideReelObj.GetComponentInChildren<TransformAnimator>(true).transform.Find("Slides_Front").GetComponent<MeshRenderer>();
|
||||
|
||||
// Now put together the textures into a 4x4 thing for the materials
|
||||
var reelTexture = ImageUtilities.MakeReelTexture(textures);
|
||||
var reelTexture = ImageUtilities.MakeReelTexture(mod, textures, key);
|
||||
slidesBack.material.mainTexture = reelTexture;
|
||||
slidesBack.material.SetTexture(EmissionMap, reelTexture);
|
||||
slidesBack.material.name = reelTexture.name;
|
||||
slidesFront.material.mainTexture = reelTexture;
|
||||
slidesFront.material.SetTexture(EmissionMap, reelTexture);
|
||||
slidesFront.material.name = reelTexture.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
NHLogger.LogVerbose($"Slide reel make reel texture {(DateTime.Now - time).TotalMilliseconds}ms");
|
||||
});
|
||||
}
|
||||
|
||||
// Else when you put them down you can't pick them back up
|
||||
slideReelObj.GetComponent<OWCollider>()._physicsRemoved = false;
|
||||
@ -161,6 +239,97 @@ namespace NewHorizons.Builder.Props
|
||||
return slideReelObj;
|
||||
}
|
||||
|
||||
private static GameObject GetSlideReelPrefab(ProjectionInfo.SlideReelType model, ProjectionInfo.SlideReelCondition condition)
|
||||
{
|
||||
switch (model)
|
||||
{
|
||||
case ProjectionInfo.SlideReelType.SixSlides:
|
||||
{
|
||||
switch (condition)
|
||||
{
|
||||
case ProjectionInfo.SlideReelCondition.Antique:
|
||||
default:
|
||||
return SlideReel6Prefab;
|
||||
case ProjectionInfo.SlideReelCondition.Pristine:
|
||||
return SlideReel6PristinePrefab;
|
||||
case ProjectionInfo.SlideReelCondition.Rusted:
|
||||
return SlideReel6RustedPrefab;
|
||||
}
|
||||
}
|
||||
case ProjectionInfo.SlideReelType.SevenSlides:
|
||||
default:
|
||||
{
|
||||
switch (condition)
|
||||
{
|
||||
case ProjectionInfo.SlideReelCondition.Antique:
|
||||
default:
|
||||
return SlideReel7Prefab;
|
||||
case ProjectionInfo.SlideReelCondition.Pristine:
|
||||
return SlideReel7PristinePrefab;
|
||||
case ProjectionInfo.SlideReelCondition.Rusted:
|
||||
return SlideReel7RustedPrefab;
|
||||
}
|
||||
}
|
||||
case ProjectionInfo.SlideReelType.EightSlides:
|
||||
{
|
||||
switch (condition)
|
||||
{
|
||||
case ProjectionInfo.SlideReelCondition.Antique:
|
||||
default:
|
||||
return SlideReel8Prefab;
|
||||
case ProjectionInfo.SlideReelCondition.Pristine:
|
||||
return SlideReel8PristinePrefab;
|
||||
case ProjectionInfo.SlideReelCondition.Rusted:
|
||||
return SlideReel8RustedPrefab;
|
||||
}
|
||||
}
|
||||
case ProjectionInfo.SlideReelType.Whole:
|
||||
{
|
||||
switch (condition)
|
||||
{
|
||||
case ProjectionInfo.SlideReelCondition.Antique:
|
||||
default:
|
||||
return SlideReelWholePrefab;
|
||||
case ProjectionInfo.SlideReelCondition.Pristine:
|
||||
return SlideReelWholePristinePrefab;
|
||||
case ProjectionInfo.SlideReelCondition.Rusted:
|
||||
return SlideReelWholeRustedPrefab;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetSlideReelName(ProjectionInfo.SlideReelType model, ProjectionInfo.SlideReelCondition condition)
|
||||
{
|
||||
switch (model)
|
||||
{
|
||||
case ProjectionInfo.SlideReelType.SixSlides:
|
||||
return $"6_{condition}";
|
||||
case ProjectionInfo.SlideReelType.SevenSlides:
|
||||
return $"7_{condition}";
|
||||
case ProjectionInfo.SlideReelType.EightSlides:
|
||||
return $"8_{condition}";
|
||||
case ProjectionInfo.SlideReelType.Whole:
|
||||
default:
|
||||
return $"{model}_{condition}";
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetSlideCount(ProjectionInfo.SlideReelType model)
|
||||
{
|
||||
switch (model)
|
||||
{
|
||||
case ProjectionInfo.SlideReelType.SixSlides:
|
||||
return 6;
|
||||
case ProjectionInfo.SlideReelType.SevenSlides:
|
||||
case ProjectionInfo.SlideReelType.Whole:
|
||||
return 7;
|
||||
case ProjectionInfo.SlideReelType.EightSlides:
|
||||
default:
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
|
||||
public static GameObject MakeAutoProjector(GameObject planetGO, Sector sector, ProjectionInfo info, IModBehaviour mod)
|
||||
{
|
||||
InitPrefabs();
|
||||
@ -177,9 +346,31 @@ namespace NewHorizons.Builder.Props
|
||||
// Now we replace the slides
|
||||
int slidesCount = info.slides.Length;
|
||||
var slideCollection = new SlideCollection(slidesCount);
|
||||
|
||||
var imageLoader = AddAsyncLoader(projectorObj, mod, info.slides, ref slideCollection);
|
||||
imageLoader.imageLoadedEvent.AddListener((Texture2D tex, int index) => { slideCollection.slides[index]._image = ImageUtilities.Invert(tex); });
|
||||
slideCollection.streamingAssetIdentifier = string.Empty; // NREs if null
|
||||
|
||||
var (invImageLoader, _, imageLoader) = StartAsyncLoader(mod, info.slides, ref slideCollection, true, false);
|
||||
|
||||
// Autoprojector only uses the inverted images so the original can be destroyed if they are loaded (when creating the cached inverted images)
|
||||
imageLoader.deleteTexturesWhenDone = true;
|
||||
|
||||
if (invImageLoader != null)
|
||||
{
|
||||
// Loaded directly from cache
|
||||
invImageLoader.imageLoadedEvent.AddListener((Texture2D tex, int index, string originalPath) =>
|
||||
{
|
||||
slideCollection.slides[index]._image = tex;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create the inverted cache from existing images
|
||||
imageLoader.imageLoadedEvent.AddListener((Texture2D tex, int index, string originalPath) =>
|
||||
{
|
||||
var time = DateTime.Now;
|
||||
slideCollection.slides[index]._image = ImageUtilities.InvertSlideReel(mod, tex, originalPath);
|
||||
NHLogger.LogVerbose($"Slide reel invert time {(DateTime.Now - time).TotalMilliseconds}ms");
|
||||
});
|
||||
}
|
||||
|
||||
slideCollectionContainer.slideCollection = slideCollection;
|
||||
|
||||
@ -203,7 +394,7 @@ namespace NewHorizons.Builder.Props
|
||||
if (_visionTorchDetectorPrefab == null) return null;
|
||||
|
||||
// spawn a trigger for the vision torch
|
||||
var g = DetailBuilder.Make(planetGO, sector, _visionTorchDetectorPrefab, new DetailInfo(info) { scale = 2, rename = !string.IsNullOrEmpty(info.rename) ? info.rename : "VisionStaffDetector" });
|
||||
var g = DetailBuilder.Make(planetGO, sector, mod, _visionTorchDetectorPrefab, new DetailInfo(info) { scale = 2, rename = !string.IsNullOrEmpty(info.rename) ? info.rename : "VisionStaffDetector" });
|
||||
|
||||
if (g == null)
|
||||
{
|
||||
@ -214,10 +405,16 @@ namespace NewHorizons.Builder.Props
|
||||
// The number of slides is unlimited, 15 is only for texturing the actual slide reel item. This is not a slide reel item
|
||||
var slides = info.slides;
|
||||
var slidesCount = slides.Length;
|
||||
var slideCollection = new SlideCollection(slidesCount);
|
||||
var slideCollection = new SlideCollection(slidesCount); // TODO: uh I think that info.slides[i].playTimeDuration is not being read here... note to self for when I implement support for that: 0.7 is what to default to if playTimeDuration turns out to be 0
|
||||
slideCollection.streamingAssetIdentifier = string.Empty; // NREs if null
|
||||
|
||||
var imageLoader = AddAsyncLoader(g, mod, info.slides, ref slideCollection);
|
||||
imageLoader.imageLoadedEvent.AddListener((Texture2D tex, int index) => { slideCollection.slides[index]._image = tex; });
|
||||
var (_, _, imageLoader) = StartAsyncLoader(mod, info.slides, ref slideCollection, false, false);
|
||||
imageLoader.imageLoadedEvent.AddListener((Texture2D tex, int index, string originalPath) =>
|
||||
{
|
||||
var time = DateTime.Now;
|
||||
slideCollection.slides[index]._image = tex;
|
||||
NHLogger.LogVerbose($"Slide reel set time {(DateTime.Now - time).TotalMilliseconds}ms");
|
||||
});
|
||||
|
||||
// attach a component to store all the data for the slides that play when a vision torch scans this target
|
||||
var target = g.AddComponent<VisionTorchTarget>();
|
||||
@ -240,7 +437,7 @@ namespace NewHorizons.Builder.Props
|
||||
if (_standingVisionTorchPrefab == null) return null;
|
||||
|
||||
// Spawn the torch itself
|
||||
var standingTorch = DetailBuilder.Make(planetGO, sector, _standingVisionTorchPrefab, new DetailInfo(info));
|
||||
var standingTorch = DetailBuilder.Make(planetGO, sector, mod, _standingVisionTorchPrefab, new DetailInfo(info));
|
||||
|
||||
if (standingTorch == null)
|
||||
{
|
||||
@ -251,19 +448,20 @@ namespace NewHorizons.Builder.Props
|
||||
// Set some required properties on the torch
|
||||
var mindSlideProjector = standingTorch.GetComponent<MindSlideProjector>();
|
||||
mindSlideProjector._mindProjectorImageEffect = SearchUtilities.Find("Player_Body/PlayerCamera").GetComponent<MindProjectorImageEffect>();
|
||||
|
||||
|
||||
// Setup for visually supporting async texture loading
|
||||
mindSlideProjector.enabled = false;
|
||||
mindSlideProjector.enabled = false;
|
||||
var visionBeamEffect = standingTorch.FindChild("VisionBeam");
|
||||
visionBeamEffect.SetActive(false);
|
||||
|
||||
// Set up slides
|
||||
// The number of slides is unlimited, 15 is only for texturing the actual slide reel item. This is not a slide reel item
|
||||
// The number of slides is unlimited, 16 is only for texturing the actual slide reel item. This is not a slide reel item
|
||||
var slides = info.slides;
|
||||
var slidesCount = slides.Length;
|
||||
var slideCollection = new SlideCollection(slidesCount);
|
||||
slideCollection.streamingAssetIdentifier = string.Empty; // NREs if null
|
||||
|
||||
var imageLoader = AddAsyncLoader(standingTorch, mod, slides, ref slideCollection);
|
||||
var (_, _, imageLoader) = StartAsyncLoader(mod, slides, ref slideCollection, false, false);
|
||||
|
||||
// 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
|
||||
@ -271,15 +469,18 @@ namespace NewHorizons.Builder.Props
|
||||
// to avoid doing a "is every element in the array `slideCollection.slides` not null" check every time a texture finishes loading
|
||||
int displaySlidesLoaded = 0;
|
||||
imageLoader.imageLoadedEvent.AddListener(
|
||||
(Texture2D tex, int index) =>
|
||||
{
|
||||
(Texture2D tex, int index, string originalPath) =>
|
||||
{
|
||||
var time = DateTime.Now;
|
||||
slideCollection.slides[index]._image = tex;
|
||||
|
||||
if (Interlocked.Increment(ref displaySlidesLoaded) == slides.Length)
|
||||
displaySlidesLoaded++;
|
||||
if (displaySlidesLoaded == slides.Length)
|
||||
{
|
||||
mindSlideProjector.enabled = true;
|
||||
visionBeamEffect.SetActive(true);
|
||||
}
|
||||
NHLogger.LogVerbose($"Slide reel another set time {(DateTime.Now - time).TotalMilliseconds}ms");
|
||||
}
|
||||
);
|
||||
|
||||
@ -303,21 +504,56 @@ namespace NewHorizons.Builder.Props
|
||||
return standingTorch;
|
||||
}
|
||||
|
||||
private static ImageUtilities.AsyncImageLoader AddAsyncLoader(GameObject gameObject, IModBehaviour mod, SlideInfo[] slides, ref SlideCollection slideCollection)
|
||||
private static (SlideReelAsyncImageLoader inverted, SlideReelAsyncImageLoader atlas, SlideReelAsyncImageLoader slides)
|
||||
StartAsyncLoader(IModBehaviour mod, SlideInfo[] slides, ref SlideCollection slideCollection, bool useInvertedCache, bool useAtlasCache)
|
||||
{
|
||||
var imageLoader = gameObject.AddComponent<ImageUtilities.AsyncImageLoader>();
|
||||
var invertedImageLoader = new SlideReelAsyncImageLoader();
|
||||
var atlasImageLoader = new SlideReelAsyncImageLoader();
|
||||
var imageLoader = new SlideReelAsyncImageLoader();
|
||||
|
||||
var atlasKey = GetUniqueSlideReelID(mod, slides);
|
||||
|
||||
var cacheExists = CacheExists(mod);
|
||||
|
||||
NHLogger.Log($"Does cache exist for slide reels? {cacheExists}");
|
||||
|
||||
if (useAtlasCache && cacheExists)
|
||||
{
|
||||
NHLogger.LogVerbose($"The atlas cache for slide reel containing [{slides.FirstOrDefault(x => !string.IsNullOrEmpty(x.imagePath))?.imagePath}] is {atlasKey}");
|
||||
// Load the atlas texture used to draw onto the physical slide reel object
|
||||
atlasImageLoader.PathsToLoad.Add((0, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ATLAS_SLIDE_CACHE_FOLDER, $"{atlasKey}.png")));
|
||||
}
|
||||
|
||||
for (int i = 0; i < slides.Length; i++)
|
||||
{
|
||||
var slide = new Slide();
|
||||
var slideInfo = slides[i];
|
||||
slide._streamingImageID = i; // for SlideRotationModule
|
||||
|
||||
if (string.IsNullOrEmpty(slideInfo.imagePath))
|
||||
{
|
||||
imageLoader.imageLoadedEvent?.Invoke(Texture2D.blackTexture, i);
|
||||
if (useInvertedCache && cacheExists)
|
||||
{
|
||||
// Load the inverted images used when displaying slide reels to a screen
|
||||
invertedImageLoader.PathsToLoad.Add((i, Path.Combine(Instance.ModHelper.Manifest.ModFolderPath, "Assets/textures/inverted_blank_slide_reel.png")));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Used to then make cached stuff
|
||||
imageLoader.PathsToLoad.Add((i, Path.Combine(Instance.ModHelper.Manifest.ModFolderPath, "Assets/textures/blank_slide_reel.png")));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
imageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, slideInfo.imagePath)));
|
||||
if (useInvertedCache && cacheExists)
|
||||
{
|
||||
// Load the inverted images used when displaying slide reels to a screen
|
||||
invertedImageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, INVERTED_SLIDE_CACHE_FOLDER, slideInfo.imagePath)));
|
||||
}
|
||||
else
|
||||
{
|
||||
imageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, slideInfo.imagePath)));
|
||||
}
|
||||
}
|
||||
|
||||
AddModules(slideInfo, ref slide, mod);
|
||||
@ -325,13 +561,42 @@ namespace NewHorizons.Builder.Props
|
||||
slideCollection.slides[i] = slide;
|
||||
}
|
||||
|
||||
return imageLoader;
|
||||
if (cacheExists)
|
||||
{
|
||||
NHLogger.Log("Loading slide reels from cache");
|
||||
|
||||
if (useAtlasCache)
|
||||
{
|
||||
atlasImageLoader.Start(false, false);
|
||||
}
|
||||
// When using the inverted cache we never need the regular images
|
||||
if (useInvertedCache)
|
||||
{
|
||||
invertedImageLoader.Start(true, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
imageLoader.Start(true, false);
|
||||
}
|
||||
|
||||
return (invertedImageLoader, atlasImageLoader, imageLoader);
|
||||
}
|
||||
else
|
||||
{
|
||||
NHLogger.Log("Generating slide reel cache");
|
||||
|
||||
// Will be slow and create the cache if needed
|
||||
// Will run sequentially to ensure we don't run out of memory
|
||||
imageLoader.Start(true, true);
|
||||
|
||||
return (null, null, imageLoader);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddModules(SlideInfo slideInfo, ref Slide slide, IModBehaviour mod)
|
||||
{
|
||||
var modules = new List<SlideFunctionModule>();
|
||||
if (!String.IsNullOrEmpty(slideInfo.beatAudio))
|
||||
if (!string.IsNullOrEmpty(slideInfo.beatAudio))
|
||||
{
|
||||
var audioBeat = new SlideBeatAudioModule
|
||||
{
|
||||
@ -340,7 +605,7 @@ namespace NewHorizons.Builder.Props
|
||||
};
|
||||
modules.Add(audioBeat);
|
||||
}
|
||||
if (!String.IsNullOrEmpty(slideInfo.backdropAudio))
|
||||
if (!string.IsNullOrEmpty(slideInfo.backdropAudio))
|
||||
{
|
||||
var audioBackdrop = new SlideBackdropAudioModule
|
||||
{
|
||||
@ -349,13 +614,13 @@ namespace NewHorizons.Builder.Props
|
||||
};
|
||||
modules.Add(audioBackdrop);
|
||||
}
|
||||
if (slideInfo.ambientLightIntensity > 0)
|
||||
if (slideInfo.ambientLightIntensity != 0)
|
||||
{
|
||||
var ambientLight = new SlideAmbientLightModule
|
||||
{
|
||||
_intensity = slideInfo.ambientLightIntensity,
|
||||
_range = slideInfo.ambientLightRange,
|
||||
_color = slideInfo.ambientLightColor.ToColor(),
|
||||
_color = slideInfo.ambientLightColor?.ToColor() ?? Color.white,
|
||||
_spotIntensityMod = slideInfo.spotIntensityMod
|
||||
};
|
||||
modules.Add(ambientLight);
|
||||
@ -376,7 +641,7 @@ namespace NewHorizons.Builder.Props
|
||||
};
|
||||
modules.Add(blackFrame);
|
||||
}
|
||||
if (!String.IsNullOrEmpty(slideInfo.reveal))
|
||||
if (!string.IsNullOrEmpty(slideInfo.reveal))
|
||||
{
|
||||
var shipLogEntry = new SlideShipLogEntryModule
|
||||
{
|
||||
@ -384,10 +649,14 @@ namespace NewHorizons.Builder.Props
|
||||
};
|
||||
modules.Add(shipLogEntry);
|
||||
}
|
||||
if (slideInfo.rotate)
|
||||
{
|
||||
modules.Add(new SlideRotationModule());
|
||||
}
|
||||
|
||||
Slide.WriteModules(modules, ref slide._modulesList, ref slide._modulesData, ref slide.lengths);
|
||||
}
|
||||
|
||||
|
||||
private static void LinkShipLogFacts(ProjectionInfo info, SlideCollectionContainer slideCollectionContainer)
|
||||
{
|
||||
// Idk why but it wants reveals to be comma delimited not a list
|
||||
|
||||
@ -4,49 +4,141 @@ using NewHorizons.Builder.Props.TranslatorText;
|
||||
using NewHorizons.Builder.ShipLog;
|
||||
using NewHorizons.External;
|
||||
using NewHorizons.External.Configs;
|
||||
using NewHorizons.External.Modules;
|
||||
using NewHorizons.Utility;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using OWML.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Builder.Props
|
||||
{
|
||||
public static class PropBuildManager
|
||||
{
|
||||
public static string InfoToName<T>() where T : BasePropInfo
|
||||
{
|
||||
var info = typeof(T).Name;
|
||||
if (info.EndsWith("Info"))
|
||||
{
|
||||
return info.Substring(0, info.Length - 4).ToLowercaseNamingConvention();
|
||||
}
|
||||
else if (info.EndsWith("Module"))
|
||||
{
|
||||
return info.Substring(0, info.Length - 6).ToLowercaseNamingConvention();
|
||||
}
|
||||
return info.ToLowercaseNamingConvention();
|
||||
}
|
||||
|
||||
public static List<Action> nextPass;
|
||||
|
||||
public static void MakeGeneralProp<T>(GameObject go, T prop, Action<T> builder, Func<T, string> errorMessage = null) where T : BasePropInfo
|
||||
{
|
||||
if (prop != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (DoesParentExist(go, prop))
|
||||
{
|
||||
builder(prop);
|
||||
}
|
||||
else
|
||||
{
|
||||
nextPass.Add(() => MakeGeneralProp<T>(go, prop, builder, errorMessage));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var rename = !string.IsNullOrEmpty(prop.rename) ? $" [{prop.rename}]" : string.Empty;
|
||||
var extra = errorMessage != null ? $" [{errorMessage(prop)}]" : string.Empty;
|
||||
NHLogger.LogError($"Couldn't make {InfoToName<T>()}{rename}{extra} for [{go.name}]:\n{ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void MakeGeneralProps<T>(GameObject go, IEnumerable<T> props, Action<T> builder, Func<T, string> errorMessage = null) where T : BasePropInfo
|
||||
{
|
||||
if (props != null)
|
||||
{
|
||||
foreach (var prop in props)
|
||||
{
|
||||
MakeGeneralProp(go, prop, builder, errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void RunMultiPass()
|
||||
{
|
||||
// Try at least 10 times going through all builders to allow for parents to be built out of order
|
||||
int i = 0;
|
||||
while (nextPass.Any())
|
||||
{
|
||||
var count = nextPass.Count;
|
||||
var passClone = nextPass.ToList();
|
||||
nextPass.Clear();
|
||||
passClone.ForEach((x) => x.Invoke());
|
||||
|
||||
if (nextPass.Count >= count || i++ > 10)
|
||||
{
|
||||
NHLogger.LogError("Couldn't find any parents. Did you write an invalid parent path?");
|
||||
|
||||
// Ignore the parent this time so that other error handling stuff can deal with these invalid paths like it used to (backwards compat)
|
||||
_ignoreParent = true;
|
||||
nextPass.ForEach((x) => x.Invoke());
|
||||
_ignoreParent = false;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Make(GameObject go, Sector sector, OWRigidbody planetBody, NewHorizonsBody nhBody)
|
||||
{
|
||||
PlanetConfig config = nhBody.Config;
|
||||
IModBehaviour mod = nhBody.Mod;
|
||||
|
||||
if (config.Props.gravityCannons != null)
|
||||
// If a prop has set its parentPath and the parent cannot be found, add it to the next pass and try again later
|
||||
nextPass = new List<Action>();
|
||||
|
||||
MakeGeneralProps(go, config.Props.gravityCannons, (cannon) => GravityCannonBuilder.Make(go, sector, cannon, mod), (cannon) => cannon.shuttleID);
|
||||
MakeGeneralProps(go, config.Props.shuttles, (shuttle) => ShuttleBuilder.Make(go, sector, mod, shuttle), (shuttle) => shuttle.id);
|
||||
MakeGeneralProps(go, config.Props.details, (detail) => DetailBuilder.Make(go, sector, mod, detail), (detail) => detail.path);
|
||||
MakeGeneralProps(go, config.Props.geysers, (geyser) => GeyserBuilder.Make(go, sector, geyser));
|
||||
if (Main.HasDLC) MakeGeneralProps(go, config.Props.rafts, (raft) => RaftBuilder.Make(go, sector, raft, planetBody));
|
||||
MakeGeneralProps(go, config.Props.tornados, (tornado) => TornadoBuilder.Make(go, sector, tornado, config.Atmosphere?.clouds != null));
|
||||
MakeGeneralProps(go, config.Props.volcanoes, (volcano) => VolcanoBuilder.Make(go, sector, volcano));
|
||||
MakeGeneralProps(go, config.Props.dialogue, (dialogueInfo) =>
|
||||
{
|
||||
foreach (var gravityCannonInfo in config.Props.gravityCannons)
|
||||
var (dialogue, trigger) = DialogueBuilder.Make(go, sector, dialogueInfo, mod);
|
||||
if (dialogue == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
GravityCannonBuilder.Make(go, sector, gravityCannonInfo, mod);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NHLogger.LogError($"Couldn't make gravity cannon [{gravityCannonInfo.shuttleID}] for [{go.name}]:\n{ex}");
|
||||
}
|
||||
NHLogger.LogVerbose($"[DIALOGUE] Failed to create dialogue [{dialogueInfo.xmlFile}]");
|
||||
}
|
||||
}
|
||||
if (config.Props.shuttles != null)
|
||||
{
|
||||
foreach (var shuttleInfo in config.Props.shuttles)
|
||||
{
|
||||
try
|
||||
{
|
||||
ShuttleBuilder.Make(go, sector, shuttleInfo);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NHLogger.LogError($"Couldn't make shuttle [{shuttleInfo.id}] for [{go.name}]:\n{ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}, (dialogueInfo) => dialogueInfo.xmlFile);
|
||||
MakeGeneralProps(go, config.Props.entryLocation, (entryLocationInfo) => EntryLocationBuilder.Make(go, sector, entryLocationInfo, mod), (entryLocationInfo) => entryLocationInfo.id);
|
||||
// Backwards compatibility
|
||||
#pragma warning disable 612, 618
|
||||
MakeGeneralProps(go, config.Props.nomaiText, (nomaiTextInfo) => NomaiTextBuilder.Make(go, sector, nomaiTextInfo, mod), (nomaiTextInfo) => nomaiTextInfo.xmlFile);
|
||||
#pragma warning restore 612, 618
|
||||
MakeGeneralProps(go, config.Props.translatorText, (nomaiTextInfo) => TranslatorTextBuilder.Make(go, sector, nomaiTextInfo, nhBody), (nomaiTextInfo) => nomaiTextInfo.xmlFile);
|
||||
if (Main.HasDLC) MakeGeneralProps(go, config.Props.slideShows, (slideReelInfo) => ProjectionBuilder.Make(go, sector, slideReelInfo, mod), (slideReelInfo) => slideReelInfo.type.ToString().ToCamelCase());
|
||||
MakeGeneralProps(go, config.Props.singularities, (singularity) => SingularityBuilder.Make(go, sector, go.GetComponent<OWRigidbody>(), config, singularity), (singularity) => (string.IsNullOrEmpty(singularity.uniqueID) ? config.name : singularity.uniqueID));
|
||||
MakeGeneralProps(go, config.Props.signals, (signal) => SignalBuilder.Make(go, sector, signal, mod), (signal) => signal.name);
|
||||
MakeGeneralProps(go, config.Props.warpReceivers, (warpReceiver) => WarpPadBuilder.Make(go, sector, mod, warpReceiver), (warpReceiver) => warpReceiver.frequency);
|
||||
MakeGeneralProps(go, config.Props.warpTransmitters, (warpTransmitter) => WarpPadBuilder.Make(go, sector, mod, warpTransmitter), (warpTransmitter) => warpTransmitter.frequency);
|
||||
MakeGeneralProps(go, config.Props.audioSources, (audioSource) => AudioSourceBuilder.Make(go, sector, audioSource, mod), (audioSource) => audioSource.audio);
|
||||
RemoteBuilder.MakeGeneralProps(go, sector, config.Props.remotes, nhBody);
|
||||
|
||||
RunMultiPass();
|
||||
|
||||
/*
|
||||
*
|
||||
* Builders below don't inherit the same base class so if they have complicated parentPaths they might just break
|
||||
* If a prop above sets one of these as its parent path it will break (but that was always the case)
|
||||
*
|
||||
*/
|
||||
|
||||
if (config.Props.scatter != null)
|
||||
{
|
||||
try
|
||||
@ -58,156 +150,7 @@ namespace NewHorizons.Builder.Props
|
||||
NHLogger.LogError($"Couldn't make planet scatter for [{go.name}]:\n{ex}");
|
||||
}
|
||||
}
|
||||
if (config.Props.details != null)
|
||||
{
|
||||
foreach (var detail in config.Props.details)
|
||||
{
|
||||
try
|
||||
{
|
||||
var detailGO = DetailBuilder.Make(go, sector, mod, detail);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NHLogger.LogError($"Couldn't make planet detail [{detail.path}] for [{go.name}]:\n{ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (config.Props.geysers != null)
|
||||
{
|
||||
foreach (var geyserInfo in config.Props.geysers)
|
||||
{
|
||||
try
|
||||
{
|
||||
GeyserBuilder.Make(go, sector, geyserInfo);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NHLogger.LogError($"Couldn't make geyser for [{go.name}]:\n{ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Main.HasDLC && config.Props.rafts != null)
|
||||
{
|
||||
foreach (var raftInfo in config.Props.rafts)
|
||||
{
|
||||
try
|
||||
{
|
||||
RaftBuilder.Make(go, sector, raftInfo, planetBody);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NHLogger.LogError($"Couldn't make raft for [{go.name}]:\n{ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (config.Props.tornados != null)
|
||||
{
|
||||
foreach (var tornadoInfo in config.Props.tornados)
|
||||
{
|
||||
try
|
||||
{
|
||||
TornadoBuilder.Make(go, sector, tornadoInfo, config.Atmosphere?.clouds != null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NHLogger.LogError($"Couldn't make tornado for [{go.name}]:\n{ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (config.Props.volcanoes != null)
|
||||
{
|
||||
foreach (var volcanoInfo in config.Props.volcanoes)
|
||||
{
|
||||
try
|
||||
{
|
||||
VolcanoBuilder.Make(go, sector, volcanoInfo);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NHLogger.LogError($"Couldn't make volcano for [{go.name}]:\n{ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
// Reminder that dialogue has to be built after props if they're going to be using CharacterAnimController stuff
|
||||
if (config.Props.dialogue != null)
|
||||
{
|
||||
foreach (var dialogueInfo in config.Props.dialogue)
|
||||
{
|
||||
try
|
||||
{
|
||||
var (dialogue, trigger) = DialogueBuilder.Make(go, sector, dialogueInfo, mod);
|
||||
if (dialogue == null)
|
||||
{
|
||||
NHLogger.LogVerbose($"[DIALOGUE] Failed to create dialogue [{dialogueInfo.xmlFile}]");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NHLogger.LogError($"[DIALOGUE] Couldn't make dialogue [{dialogueInfo.xmlFile}] for [{go.name}]:\n{ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (config.Props.entryLocation != null)
|
||||
{
|
||||
foreach (var entryLocationInfo in config.Props.entryLocation)
|
||||
{
|
||||
try
|
||||
{
|
||||
EntryLocationBuilder.Make(go, sector, entryLocationInfo, mod);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NHLogger.LogError($"Couldn't make entry location [{entryLocationInfo.id}] for [{go.name}]:\n{ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
// Backwards compatibility
|
||||
#pragma warning disable 612, 618
|
||||
if (config.Props.nomaiText != null)
|
||||
{
|
||||
foreach (var nomaiTextInfo in config.Props.nomaiText)
|
||||
{
|
||||
try
|
||||
{
|
||||
NomaiTextBuilder.Make(go, sector, nomaiTextInfo, nhBody.Mod);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NHLogger.LogError($"Couldn't make text [{nomaiTextInfo.xmlFile}] for [{go.name}]:\n{ex}");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#pragma warning restore 612, 618
|
||||
if (config.Props.translatorText != null)
|
||||
{
|
||||
foreach (var nomaiTextInfo in config.Props.translatorText)
|
||||
{
|
||||
try
|
||||
{
|
||||
TranslatorTextBuilder.Make(go, sector, nomaiTextInfo, nhBody);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NHLogger.LogError($"Couldn't make text [{nomaiTextInfo.xmlFile}] for [{go.name}]:\n{ex}");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (Main.HasDLC && config.Props.slideShows != null)
|
||||
{
|
||||
foreach (var slideReelInfo in config.Props.slideShows)
|
||||
{
|
||||
try
|
||||
{
|
||||
ProjectionBuilder.Make(go, sector, slideReelInfo, mod);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NHLogger.LogError($"Couldn't make slide reel for [{go.name}]:\n{ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (config.Props.quantumGroups != null)
|
||||
{
|
||||
Dictionary<string, List<GameObject>> propsByGroup = new Dictionary<string, List<GameObject>>();
|
||||
@ -235,89 +178,23 @@ namespace NewHorizons.Builder.Props
|
||||
}
|
||||
}
|
||||
}
|
||||
if (config.Props.singularities != null)
|
||||
}
|
||||
|
||||
private static bool _ignoreParent;
|
||||
|
||||
private static bool DoesParentExist(GameObject go, BasePropInfo prop)
|
||||
{
|
||||
if (_ignoreParent)
|
||||
{
|
||||
foreach (var singularity in config.Props.singularities)
|
||||
{
|
||||
try
|
||||
{
|
||||
SingularityBuilder.Make(go, sector, go.GetComponent<OWRigidbody>(), config, singularity);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NHLogger.LogError($"Couldn't make singularity \"{(string.IsNullOrEmpty(singularity.uniqueID) ? config.name : singularity.uniqueID)}\" for [{go.name}]::\n{ex}");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (config.Props.signals != null)
|
||||
else if (string.IsNullOrEmpty(prop.parentPath))
|
||||
{
|
||||
foreach (var signal in config.Props.signals)
|
||||
{
|
||||
try
|
||||
{
|
||||
SignalBuilder.Make(go, sector, signal, mod);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NHLogger.LogError($"Couldn't make signal on planet [{config.name}] - {ex}");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (config.Props.remotes != null)
|
||||
else
|
||||
{
|
||||
foreach (var remoteInfo in config.Props.remotes)
|
||||
{
|
||||
try
|
||||
{
|
||||
RemoteBuilder.Make(go, sector, remoteInfo, nhBody);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NHLogger.LogError($"Couldn't make remote [{remoteInfo.id}] for [{go.name}]:\n{ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (config.Props.warpReceivers != null)
|
||||
{
|
||||
foreach (var warpReceiver in config.Props.warpReceivers)
|
||||
{
|
||||
try
|
||||
{
|
||||
WarpPadBuilder.Make(go, sector, warpReceiver);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NHLogger.LogError($"Couldn't make warp receiver [{warpReceiver.frequency}] for [{go.name}]:\n{ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (config.Props.warpTransmitters != null)
|
||||
{
|
||||
foreach (var warpTransmitter in config.Props.warpTransmitters)
|
||||
{
|
||||
try
|
||||
{
|
||||
WarpPadBuilder.Make(go, sector, warpTransmitter);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NHLogger.LogError($"Couldn't make warp transmitter [{warpTransmitter.frequency}] for [{go.name}]:\n{ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (config.Props.audioSources != null)
|
||||
{
|
||||
foreach (var audioSource in config.Props.audioSources)
|
||||
{
|
||||
try
|
||||
{
|
||||
AudioSourceBuilder.Make(go, sector, audioSource, mod);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NHLogger.LogError($"Couldn't make audio source [{audioSource.audio}] for [{go.name}]:\n{ex}");
|
||||
}
|
||||
}
|
||||
return go.transform.Find(prop.parentPath) != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,17 +8,6 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
// BUGS THAT REQUIRE REWRITING MOBIUS CODE
|
||||
// 1) FIXED! - MultiStateQuantumObjects don't check to see if the new state would be visible before choosing it
|
||||
// 2) FIXED? no longer supporting shuffle - QuantumShuffleObjects don't respect rotation, they set rotation to 0 on collapse
|
||||
// 3) - MultiStateQuantumObjects don't get locked by pictures
|
||||
|
||||
// New features to support
|
||||
// 1) multiState._prerequisiteObjects
|
||||
// 2) Socket groups that have an equal number of props and sockets
|
||||
// 3) Nice to have: socket groups that specify a filledSocketObject and an emptySocketObject (eg the archway in the giant's deep tower)
|
||||
|
||||
namespace NewHorizons.Builder.Props
|
||||
{
|
||||
public static class QuantumBuilder
|
||||
@ -34,6 +23,8 @@ namespace NewHorizons.Builder.Props
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Socket groups that have an equal number of props and sockets
|
||||
// Nice to have: socket groups that specify a filledSocketObject and an emptySocketObject (eg the archway in the giant's deep tower)
|
||||
public static void MakeSocketGroup(GameObject go, Sector sector, PlanetConfig config, IModBehaviour mod, QuantumGroupInfo quantumGroup, GameObject[] propsInGroup)
|
||||
{
|
||||
var groupRoot = new GameObject("Quantum Sockets - " + quantumGroup.id);
|
||||
@ -46,10 +37,10 @@ namespace NewHorizons.Builder.Props
|
||||
{
|
||||
var socketInfo = quantumGroup.sockets[i];
|
||||
|
||||
var socket = GeneralPropBuilder.MakeNew("Socket " + i, go, sector, socketInfo, parentOverride: groupRoot.transform);
|
||||
var socket = GeneralPropBuilder.MakeNew("Socket " + i, go, sector, socketInfo, defaultParent: groupRoot.transform);
|
||||
|
||||
sockets[i] = socket.AddComponent<QuantumSocket>();
|
||||
sockets[i]._lightSources = new Light[0];
|
||||
sockets[i]._lightSources = new Light[0]; // TODO: make this customizable?
|
||||
socket.SetActive(true);
|
||||
}
|
||||
|
||||
@ -70,6 +61,12 @@ namespace NewHorizons.Builder.Props
|
||||
|
||||
public static void MakeStateGroup(GameObject go, Sector sector, PlanetConfig config, IModBehaviour mod, QuantumGroupInfo quantumGroup, GameObject[] propsInGroup)
|
||||
{
|
||||
// NOTE: States groups need special consideration that socket groups don't
|
||||
// this is because the base class QuantumObject (and this is important) IGNORES PICTURES TAKEN FROM OVER 100 METERS AWAY
|
||||
// why does this affect states and not sockets? Well because sockets put the QuantumObject component (QuantumSocketedObject) on the actual props themselves
|
||||
// while states put the QuantumObject component (NHMultiStateQuantumObject) on the parent, which is located at the center of the planet
|
||||
// this means that the distance measured by QuantumObject is not accurate, since it's not measuring from the active prop, but from the center of the planet
|
||||
|
||||
var groupRoot = new GameObject("Quantum States - " + quantumGroup.id);
|
||||
groupRoot.transform.parent = sector?.transform ?? go.transform;
|
||||
groupRoot.transform.localPosition = Vector3.zero;
|
||||
@ -110,8 +107,10 @@ namespace NewHorizons.Builder.Props
|
||||
multiState._loop = quantumGroup.loop;
|
||||
multiState._sequential = quantumGroup.sequential;
|
||||
multiState._states = states.ToArray();
|
||||
multiState._prerequisiteObjects = new MultiStateQuantumObject[0]; // TODO: support this
|
||||
multiState._prerequisiteObjects = new MultiStateQuantumObject[0]; // TODO: _prerequisiteObjects
|
||||
multiState._initialState = 0;
|
||||
// snapshot events arent listened to outside of the sector, so fortunately this isnt really infinite
|
||||
multiState._maxSnapshotLockRange = Mathf.Infinity; // TODO: maybe expose this at some point if it breaks a puzzle or something
|
||||
groupRoot.SetActive(true);
|
||||
}
|
||||
|
||||
@ -126,7 +125,7 @@ namespace NewHorizons.Builder.Props
|
||||
|
||||
var shuffle = shuffleParent.AddComponent<QuantumShuffleObject>();
|
||||
shuffle._shuffledObjects = propsInGroup.Select(p => p.transform).ToArray();
|
||||
shuffle.Awake(); // this doesn't get called on its own for some reason
|
||||
shuffle.Awake(); // this doesn't get called on its own for some reason. what? how?
|
||||
|
||||
AddBoundsVisibility(shuffleParent);
|
||||
shuffleParent.SetActive(true);
|
||||
@ -165,6 +164,7 @@ namespace NewHorizons.Builder.Props
|
||||
}
|
||||
}
|
||||
|
||||
// BUG: ignores skinned guys. this coincidentally makes it work without BoxShapeFixer
|
||||
public static Bounds GetBoundsOfSelfAndChildMeshes(GameObject g)
|
||||
{
|
||||
var meshFilters = g.GetComponentsInChildren<MeshFilter>();
|
||||
@ -200,6 +200,13 @@ namespace NewHorizons.Builder.Props
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// for some reason mesh bounds are wrong unless we wait a bit
|
||||
/// so this script contiously checks everything until it is correct
|
||||
///
|
||||
/// this actually only seems to be a problem with skinned renderers. normal ones work fine
|
||||
/// TODO: at some point narrow this down to just skinned, instead of doing everything and checking every frame
|
||||
/// </summary>
|
||||
public class BoxShapeFixer : MonoBehaviour
|
||||
{
|
||||
public BoxShape shape;
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
using NewHorizons.Components.Props;
|
||||
using NewHorizons.External.Modules.Props.EchoesOfTheEye;
|
||||
using NewHorizons.Handlers;
|
||||
using NewHorizons.Utility;
|
||||
@ -73,6 +74,8 @@ namespace NewHorizons.Builder.Props
|
||||
sector.OnSectorOccupantsUpdated += lightSensor.OnSectorOccupantsUpdated;
|
||||
}
|
||||
|
||||
var nhRaftController = raftObject.AddComponent<NHRaftController>();
|
||||
|
||||
var achievementObject = new GameObject("AchievementVolume");
|
||||
achievementObject.transform.SetParent(raftObject.transform, false);
|
||||
|
||||
|
||||
@ -12,6 +12,7 @@ using NewHorizons.Utility.OWML;
|
||||
using OWML.Common;
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using NewHorizons.External.Modules;
|
||||
|
||||
namespace NewHorizons.Builder.Props
|
||||
{
|
||||
@ -122,10 +123,36 @@ namespace NewHorizons.Builder.Props
|
||||
}
|
||||
}
|
||||
|
||||
public static void MakeGeneralProps(GameObject go, Sector sector, RemoteInfo[] remotes, NewHorizonsBody nhBody)
|
||||
{
|
||||
if (remotes != null)
|
||||
{
|
||||
foreach (var remoteInfo in remotes)
|
||||
{
|
||||
try
|
||||
{
|
||||
var mod = nhBody.Mod;
|
||||
var id = RemoteHandler.GetPlatformID(remoteInfo.id);
|
||||
|
||||
Texture2D decal = Texture2D.whiteTexture;
|
||||
if (!string.IsNullOrWhiteSpace(remoteInfo.decalPath)) decal = ImageUtilities.GetTexture(mod, remoteInfo.decalPath, false, false, false);
|
||||
else NHLogger.LogError($"Missing decal path on [{remoteInfo.id}] for [{go.name}]");
|
||||
|
||||
PropBuildManager.MakeGeneralProp(go, remoteInfo.platform, (platform) => MakePlatform(go, sector, id, decal, platform, mod), (platform) => remoteInfo.id);
|
||||
PropBuildManager.MakeGeneralProp(go, remoteInfo.whiteboard, (whiteboard) => MakeWhiteboard(go, sector, id, decal, whiteboard, nhBody), (whiteboard) => remoteInfo.id);
|
||||
PropBuildManager.MakeGeneralProps(go, remoteInfo.stones, (stone) => MakeStone(go, sector, id, decal, stone, mod), (stone) => remoteInfo.id);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NHLogger.LogError($"Couldn't make remote [{remoteInfo.id}] for [{go.name}]:\n{ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Use MakeGeneralProps instead")]
|
||||
public static void Make(GameObject go, Sector sector, RemoteInfo info, NewHorizonsBody nhBody)
|
||||
{
|
||||
InitPrefabs();
|
||||
|
||||
var mod = nhBody.Mod;
|
||||
var id = RemoteHandler.GetPlatformID(info.id);
|
||||
|
||||
@ -175,7 +202,9 @@ namespace NewHorizons.Builder.Props
|
||||
|
||||
public static void MakeWhiteboard(GameObject go, Sector sector, NomaiRemoteCameraPlatform.ID id, Texture2D decal, RemoteWhiteboardInfo info, NewHorizonsBody nhBody)
|
||||
{
|
||||
var whiteboard = DetailBuilder.Make(go, sector, _whiteboardPrefab, new DetailInfo(info));
|
||||
InitPrefabs();
|
||||
var mod = nhBody.Mod;
|
||||
var whiteboard = DetailBuilder.Make(go, sector, mod, _whiteboardPrefab, new DetailInfo(info));
|
||||
whiteboard.SetActive(false);
|
||||
|
||||
var decalMat = new Material(_decalMaterial);
|
||||
@ -213,9 +242,10 @@ namespace NewHorizons.Builder.Props
|
||||
whiteboard.SetActive(true);
|
||||
}
|
||||
|
||||
public static void MakePlatform(GameObject go, Sector sector, NomaiRemoteCameraPlatform.ID id, Texture2D decal, PlatformInfo info, IModBehaviour mod)
|
||||
public static void MakePlatform(GameObject go, Sector sector, NomaiRemoteCameraPlatform.ID id, Texture2D decal, RemotePlatformInfo info, IModBehaviour mod)
|
||||
{
|
||||
var platform = DetailBuilder.Make(go, sector, _remoteCameraPlatformPrefab, new DetailInfo(info));
|
||||
InitPrefabs();
|
||||
var platform = DetailBuilder.Make(go, sector, mod, _remoteCameraPlatformPrefab, new DetailInfo(info));
|
||||
platform.SetActive(false);
|
||||
|
||||
var decalMat = new Material(_decalMaterial);
|
||||
@ -239,8 +269,9 @@ namespace NewHorizons.Builder.Props
|
||||
platform.SetActive(true);
|
||||
}
|
||||
|
||||
public static void MakeStone(GameObject go, Sector sector, NomaiRemoteCameraPlatform.ID id, Texture2D decal, StoneInfo info, IModBehaviour mod)
|
||||
public static void MakeStone(GameObject go, Sector sector, NomaiRemoteCameraPlatform.ID id, Texture2D decal, ProjectionStoneInfo info, IModBehaviour mod)
|
||||
{
|
||||
InitPrefabs();
|
||||
var shareStone = GeneralPropBuilder.MakeFromPrefab(_shareStonePrefab, "ShareStone_" + id.ToString(), go, sector, info);
|
||||
|
||||
shareStone.GetComponent<SharedStone>()._connectedPlatform = id;
|
||||
|
||||
@ -3,6 +3,7 @@ using NewHorizons.External.Modules.Props;
|
||||
using NewHorizons.Utility;
|
||||
using NewHorizons.Utility.Files;
|
||||
using NewHorizons.Utility.Geometry;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using OWML.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@ -77,7 +78,7 @@ namespace NewHorizons.Builder.Props
|
||||
stretch = propInfo.stretch,
|
||||
keepLoaded = propInfo.keepLoaded
|
||||
};
|
||||
var scatterPrefab = DetailBuilder.Make(go, sector, prefab, detailInfo);
|
||||
var scatterPrefab = DetailBuilder.Make(go, sector, mod, prefab, detailInfo);
|
||||
|
||||
for (int i = 0; i < propInfo.count; i++)
|
||||
{
|
||||
@ -120,10 +121,28 @@ namespace NewHorizons.Builder.Props
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var parent = sector?.transform ?? go.transform;
|
||||
|
||||
if (go != null && !string.IsNullOrEmpty(propInfo.parentPath))
|
||||
{
|
||||
var newParent = go.transform.Find(propInfo.parentPath);
|
||||
if (newParent != null)
|
||||
{
|
||||
parent = newParent;
|
||||
sector = newParent.GetComponentInParent<Sector>();
|
||||
}
|
||||
else
|
||||
{
|
||||
NHLogger.LogError($"Cannot find parent object at path: {go.name}/{propInfo.parentPath}");
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
// Have to use SetParent method to work with tidally locked bodies #872
|
||||
prop.transform.SetParent(parent, false);
|
||||
prop.transform.localPosition = point * height;
|
||||
var up = (prop.transform.position - go.transform.position).normalized;
|
||||
prop.transform.rotation = Quaternion.FromToRotation(Vector3.up, up);
|
||||
|
||||
if (propInfo.offset != null) prop.transform.localPosition += prop.transform.TransformVector(propInfo.offset);
|
||||
|
||||
@ -3,6 +3,7 @@ using NewHorizons.External.Modules.Props;
|
||||
using NewHorizons.External.Modules.Props.Shuttle;
|
||||
using NewHorizons.Handlers;
|
||||
using NewHorizons.Utility;
|
||||
using OWML.Common;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
@ -11,7 +12,6 @@ namespace NewHorizons.Builder.Props
|
||||
public static class ShuttleBuilder
|
||||
{
|
||||
private static GameObject _prefab;
|
||||
private static GameObject _orbPrefab;
|
||||
private static GameObject _bodyPrefab;
|
||||
|
||||
public static Dictionary<NomaiShuttleController.ShuttleID, NomaiShuttleController> Shuttles { get; } = new();
|
||||
@ -52,9 +52,9 @@ namespace NewHorizons.Builder.Props
|
||||
neutralSlot._attractive = true;
|
||||
neutralSlot._muteAudio = true;
|
||||
nhShuttleController._neutralSlot = neutralSlot;
|
||||
// TODO: at some point delay rigidbody parenting so we dont have to find orb via references. mainly to fix orbs on existing details and rafts not rotating with planets
|
||||
_orbPrefab = shuttleController._orb.gameObject?.InstantiateInactive()?.Rename("Prefab_QM_Shuttle_InterfaceOrbSmall")?.DontDestroyOnLoad();
|
||||
nhShuttleController._orb = _orbPrefab.GetComponent<NomaiInterfaceOrb>();
|
||||
|
||||
var orb = shuttleController._orb.gameObject;
|
||||
nhShuttleController._orb = orb.GetComponent<NomaiInterfaceOrb>();
|
||||
nhShuttleController._orb._sector = nhShuttleController._interiorSector;
|
||||
nhShuttleController._orb._slotRoot = slots;
|
||||
nhShuttleController._orb._safetyRails = slots.GetComponentsInChildren<OWRail>();
|
||||
@ -69,14 +69,14 @@ namespace NewHorizons.Builder.Props
|
||||
}
|
||||
}
|
||||
|
||||
public static GameObject Make(GameObject planetGO, Sector sector, ShuttleInfo info)
|
||||
public static GameObject Make(GameObject planetGO, Sector sector, IModBehaviour mod, ShuttleInfo info)
|
||||
{
|
||||
InitPrefab();
|
||||
|
||||
if (_prefab == null || planetGO == null || sector == null) return null;
|
||||
|
||||
var detailInfo = new DetailInfo(info) { keepLoaded = true };
|
||||
var shuttleObject = DetailBuilder.Make(planetGO, sector, _prefab, detailInfo);
|
||||
var shuttleObject = DetailBuilder.Make(planetGO, sector, mod, _prefab, detailInfo);
|
||||
shuttleObject.SetActive(false);
|
||||
|
||||
StreamingHandler.SetUpStreaming(shuttleObject, sector);
|
||||
@ -87,7 +87,7 @@ namespace NewHorizons.Builder.Props
|
||||
shuttleController._cannon = Locator.GetGravityCannon(id);
|
||||
|
||||
GameObject slots = shuttleObject.FindChild("Sector_NomaiShuttleInterior/Interactibles_NomaiShuttleInterior/ControlPanel/Slots");
|
||||
GameObject orbObject = _orbPrefab.InstantiateInactive().Rename("InterfaceOrbSmall");
|
||||
GameObject orbObject = shuttleController._orb.gameObject;
|
||||
orbObject.transform.SetParent(slots.transform, false);
|
||||
orbObject.transform.localPosition = new Vector3(-0.0153f, -0.2386f, 0.0205f);
|
||||
shuttleController._orb = orbObject.GetComponent<NomaiInterfaceOrb>();
|
||||
|
||||
@ -242,7 +242,7 @@ namespace NewHorizons.Builder.Props.TranslatorText
|
||||
}
|
||||
case NomaiTextType.PreCrashComputer:
|
||||
{
|
||||
var computerObject = DetailBuilder.Make(planetGO, sector, PreCrashComputerPrefab, new DetailInfo(info));
|
||||
var computerObject = DetailBuilder.Make(planetGO, sector, nhBody.Mod, PreCrashComputerPrefab, new DetailInfo(info));
|
||||
computerObject.SetActive(false);
|
||||
|
||||
var computer = computerObject.GetComponent<NomaiVesselComputer>();
|
||||
@ -323,7 +323,7 @@ namespace NewHorizons.Builder.Props.TranslatorText
|
||||
case NomaiTextType.Recorder:
|
||||
{
|
||||
var prefab = (info.type == NomaiTextType.PreCrashRecorder ? _preCrashRecorderPrefab : _recorderPrefab);
|
||||
var recorderObject = DetailBuilder.Make(planetGO, sector, prefab, new DetailInfo(info));
|
||||
var recorderObject = DetailBuilder.Make(planetGO, sector, nhBody.Mod, prefab, new DetailInfo(info));
|
||||
recorderObject.SetActive(false);
|
||||
|
||||
var nomaiText = recorderObject.GetComponentInChildren<NomaiText>();
|
||||
@ -373,7 +373,7 @@ namespace NewHorizons.Builder.Props.TranslatorText
|
||||
path = "BrittleHollow_Body/Sector_BH/Sector_NorthHemisphere/Sector_NorthPole/Sector_HangingCity/Sector_HangingCity_District2/Interactables_HangingCity_District2/VisibleFrom_HangingCity/Props_NOM_Whiteboard (1)",
|
||||
rename = info.rename ?? "Props_NOM_Whiteboard",
|
||||
};
|
||||
var whiteboardObject = DetailBuilder.Make(planetGO, sector, whiteboardInfo);
|
||||
var whiteboardObject = DetailBuilder.Make(planetGO, sector, nhBody.Mod, whiteboardInfo);
|
||||
|
||||
// Spawn a scroll and insert it into the whiteboard, but only if text is provided
|
||||
if (!string.IsNullOrEmpty(info.xmlFile))
|
||||
@ -471,12 +471,12 @@ namespace NewHorizons.Builder.Props.TranslatorText
|
||||
|
||||
if (info.arcInfo != null && info.arcInfo.Count() != dict.Values.Count())
|
||||
{
|
||||
NHLogger.LogError($"Can't make NomaiWallText, arcInfo length [{info.arcInfo.Count()}] doesn't equal text entries [{dict.Values.Count()}]");
|
||||
NHLogger.LogError($"Can't make NomaiWallText, arcInfo length [{info.arcInfo.Count()}] doesn't equal number of TextBlocks [{dict.Values.Count()}] in the xml");
|
||||
return;
|
||||
}
|
||||
|
||||
ArcCacheData[] cachedData = null;
|
||||
if (nhBody.Cache?.ContainsKey(cacheKey) ?? false)
|
||||
if (nhBody?.Cache?.ContainsKey(cacheKey) ?? false)
|
||||
cachedData = nhBody.Cache.Get<ArcCacheData[]>(cacheKey);
|
||||
|
||||
var arranger = nomaiWallText.gameObject.AddComponent<NomaiTextArcArranger>();
|
||||
|
||||
@ -6,6 +6,7 @@ using NewHorizons.External.Modules.WarpPad;
|
||||
using NewHorizons.Utility;
|
||||
using NewHorizons.Utility.OuterWilds;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using OWML.Common;
|
||||
using OWML.Utils;
|
||||
using UnityEngine;
|
||||
|
||||
@ -27,6 +28,7 @@ namespace NewHorizons.Builder.Props
|
||||
// Trifid is a Nomai ruins genius
|
||||
_platformContainerPrefab = SearchUtilities.Find("BrittleHollow_Body/Sector_BH/Sector_SouthHemisphere/Sector_SouthPole/Sector_Observatory/Interactables_Observatory/Prefab_NOM_RemoteViewer/Structure_NOM_RemoteViewer")
|
||||
.InstantiateInactive()
|
||||
.Rename("Prefab_NOM_PlatformContainer")
|
||||
.DontDestroyOnLoad();
|
||||
_platformContainerPrefab.transform.localScale = new Vector3(0.85f, 3f, 0.85f);
|
||||
}
|
||||
@ -38,12 +40,12 @@ namespace NewHorizons.Builder.Props
|
||||
|
||||
_detailedReceiverPrefab = new GameObject("NomaiWarpReceiver");
|
||||
|
||||
var detailedReceiver = thReceiver.InstantiateInactive();
|
||||
var detailedReceiver = thReceiver.InstantiateInactive().Rename("Prefab_NOM_WarpReceiver");
|
||||
detailedReceiver.transform.parent = _detailedReceiverPrefab.transform;
|
||||
detailedReceiver.transform.localPosition = Vector3.zero;
|
||||
detailedReceiver.transform.localRotation = Quaternion.identity;
|
||||
|
||||
var lamp = thReceiverLamp.InstantiateInactive();
|
||||
var lamp = thReceiverLamp.InstantiateInactive().Rename("Structure_NOM_WarpReceiver_Lamp");
|
||||
lamp.transform.parent = _detailedReceiverPrefab.transform;
|
||||
lamp.transform.localPosition = thReceiver.transform.InverseTransformPoint(thReceiverLamp.transform.position);
|
||||
lamp.transform.localRotation = thReceiver.transform.InverseTransformRotation(thReceiverLamp.transform.rotation);
|
||||
@ -61,10 +63,11 @@ namespace NewHorizons.Builder.Props
|
||||
{
|
||||
_receiverPrefab = SearchUtilities.Find("SunStation_Body/Sector_SunStation/Sector_WarpModule/Interactables_WarpModule/Prefab_NOM_WarpReceiver")
|
||||
.InstantiateInactive()
|
||||
.Rename("Prefab_NOM_WarpReceiver")
|
||||
.DontDestroyOnLoad();
|
||||
Object.Destroy(_receiverPrefab.GetComponentInChildren<NomaiWarpStreaming>().gameObject);
|
||||
|
||||
var structure = _platformContainerPrefab.Instantiate();
|
||||
var structure = _platformContainerPrefab.Instantiate().Rename("Structure_NOM_PlatformContainer");
|
||||
structure.transform.parent = _receiverPrefab.transform;
|
||||
structure.transform.localPosition = new Vector3(0, 0.8945f, 0);
|
||||
structure.transform.localRotation = Quaternion.identity;
|
||||
@ -75,10 +78,11 @@ namespace NewHorizons.Builder.Props
|
||||
{
|
||||
_transmitterPrefab = SearchUtilities.Find("TowerTwin_Body/Sector_TowerTwin/Sector_Tower_SS/Interactables_Tower_SS/Tower_SS_VisibleFrom_TowerTwin/Prefab_NOM_WarpTransmitter")
|
||||
.InstantiateInactive()
|
||||
.Rename("Prefab_NOM_WarpTransmitter")
|
||||
.DontDestroyOnLoad();
|
||||
Object.Destroy(_transmitterPrefab.GetComponentInChildren<NomaiWarpStreaming>().gameObject);
|
||||
|
||||
var structure = _platformContainerPrefab.Instantiate();
|
||||
var structure = _platformContainerPrefab.Instantiate().Rename("Structure_NOM_PlatformContainer");
|
||||
structure.transform.parent = _transmitterPrefab.transform;
|
||||
structure.transform.localPosition = new Vector3(0, 0.8945f, 0);
|
||||
structure.transform.localRotation = Quaternion.identity;
|
||||
@ -86,10 +90,10 @@ namespace NewHorizons.Builder.Props
|
||||
}
|
||||
}
|
||||
|
||||
public static void Make(GameObject planetGO, Sector sector, NomaiWarpReceiverInfo info)
|
||||
public static void Make(GameObject planetGO, Sector sector, IModBehaviour mod, NomaiWarpReceiverInfo info)
|
||||
{
|
||||
var detailInfo = new DetailInfo(info);
|
||||
var receiverObject = DetailBuilder.Make(planetGO, sector, info.detailed ? _detailedReceiverPrefab : _receiverPrefab, detailInfo);
|
||||
var receiverObject = DetailBuilder.Make(planetGO, sector, mod, info.detailed ? _detailedReceiverPrefab : _receiverPrefab, detailInfo);
|
||||
|
||||
NHLogger.Log($"Position is {detailInfo.position} was {info.position}");
|
||||
|
||||
@ -122,13 +126,13 @@ namespace NewHorizons.Builder.Props
|
||||
|
||||
if (info.computer != null)
|
||||
{
|
||||
CreateComputer(planetGO, sector, info.computer, receiver);
|
||||
CreateComputer(planetGO, sector, mod, info.computer, receiver);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Make(GameObject planetGO, Sector sector, NomaiWarpTransmitterInfo info)
|
||||
public static void Make(GameObject planetGO, Sector sector, IModBehaviour mod, NomaiWarpTransmitterInfo info)
|
||||
{
|
||||
var transmitterObject = DetailBuilder.Make(planetGO, sector, _transmitterPrefab, new DetailInfo(info));
|
||||
var transmitterObject = DetailBuilder.Make(planetGO, sector, mod, _transmitterPrefab, new DetailInfo(info));
|
||||
|
||||
var transmitter = transmitterObject.GetComponentInChildren<NomaiWarpTransmitter>();
|
||||
transmitter._frequency = GetFrequency(info.frequency);
|
||||
@ -145,9 +149,9 @@ namespace NewHorizons.Builder.Props
|
||||
transmitterObject.SetActive(true);
|
||||
}
|
||||
|
||||
private static void CreateComputer(GameObject planetGO, Sector sector, GeneralPropInfo computerInfo, NomaiWarpReceiver receiver)
|
||||
private static void CreateComputer(GameObject planetGO, Sector sector, IModBehaviour mod, GeneralPropInfo computerInfo, NomaiWarpReceiver receiver)
|
||||
{
|
||||
var computerObject = DetailBuilder.Make(planetGO, sector, TranslatorTextBuilder.ComputerPrefab, new DetailInfo(computerInfo));
|
||||
var computerObject = DetailBuilder.Make(planetGO, sector, mod, TranslatorTextBuilder.ComputerPrefab, new DetailInfo(computerInfo));
|
||||
|
||||
var computer = computerObject.GetComponentInChildren<NomaiComputer>();
|
||||
computer.SetSector(sector);
|
||||
@ -156,6 +160,7 @@ namespace NewHorizons.Builder.Props
|
||||
|
||||
var computerLogger = computerObject.AddComponent<NomaiWarpComputerLogger>();
|
||||
computerLogger._warpReceiver = receiver;
|
||||
computerLogger.Awake(); // Redo awake because OnReceiveWarpedBody doesn't get added to otherwise
|
||||
|
||||
computerObject.SetActive(true);
|
||||
}
|
||||
|
||||
@ -7,19 +7,47 @@ using NewHorizons.Utility;
|
||||
using NewHorizons.Utility.Files;
|
||||
using NewHorizons.Utility.OuterWilds;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using OWML.ModHelper;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using static NewHorizons.External.Modules.ShipLogModule;
|
||||
|
||||
namespace NewHorizons.Builder.ShipLog
|
||||
{
|
||||
public static class MapModeBuilder
|
||||
{
|
||||
// Takes the game object because sometimes we change the AO to an NHAO and it breaks
|
||||
private static Dictionary<GameObject, ShipLogAstroObject> _astroObjectToShipLog = new();
|
||||
|
||||
#region General
|
||||
public static ShipLogAstroObject[][] ConstructMapMode(string systemName, GameObject transformParent, ShipLogAstroObject[][] currentNav, int layer)
|
||||
{
|
||||
_astroObjectToShipLog = new();
|
||||
|
||||
// Add stock planets
|
||||
foreach (var shipLogAstroObject in currentNav.SelectMany(x => x))
|
||||
{
|
||||
var astroObject = Locator.GetAstroObject(AstroObject.StringIDToAstroObjectName(shipLogAstroObject._id));
|
||||
if (astroObject == null)
|
||||
{
|
||||
// Outsider compat
|
||||
if (shipLogAstroObject._id == "POWER_STATION")
|
||||
{
|
||||
astroObject = GameObject.FindObjectsOfType<AstroObject>().FirstOrDefault(x => x._customName == "Power Station");
|
||||
if (astroObject == null) continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
NHLogger.LogError($"Couldn't find stock (?) astro object [{shipLogAstroObject?._id}]");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
_astroObjectToShipLog[astroObject.gameObject] = shipLogAstroObject;
|
||||
}
|
||||
|
||||
Material greyScaleMaterial = SearchUtilities.Find(ShipLogHandler.PAN_ROOT_PATH + "/TimberHearth/Sprite").GetComponent<Image>().material;
|
||||
List<NewHorizonsBody> bodies = Main.BodyDict[systemName].Where(
|
||||
b => !(b.Config.ShipLog?.mapMode?.remove ?? false) && !b.Config.isQuantumState
|
||||
@ -30,7 +58,7 @@ namespace NewHorizons.Builder.ShipLog
|
||||
{
|
||||
if (body.Config.ShipLog == null) continue;
|
||||
|
||||
if (body.Config.ShipLog?.mapMode?.manualPosition == null)
|
||||
if (body.Config.ShipLog.mapMode?.manualPosition == null)
|
||||
{
|
||||
flagAutoPositionUsed = true;
|
||||
}
|
||||
@ -45,18 +73,46 @@ namespace NewHorizons.Builder.ShipLog
|
||||
}
|
||||
}
|
||||
|
||||
if (flagManualPositionUsed)
|
||||
// If they're both false, just default to auto (this means that no planets even have ship log info)
|
||||
if (!flagManualPositionUsed && !flagAutoPositionUsed)
|
||||
{
|
||||
if (flagAutoPositionUsed && flagManualPositionUsed)
|
||||
NHLogger.LogWarning("Can't mix manual and automatic layout of ship log map mode, defaulting to manual");
|
||||
return ConstructMapModeManual(bodies, transformParent, greyScaleMaterial, currentNav, layer);
|
||||
}
|
||||
else if (flagAutoPositionUsed)
|
||||
{
|
||||
return ConstructMapModeAuto(bodies, transformParent, greyScaleMaterial, layer);
|
||||
flagAutoPositionUsed = true;
|
||||
}
|
||||
|
||||
return null;
|
||||
var isBaseSolarSystem = systemName == "SolarSystem";
|
||||
|
||||
// Default to MANUAL in Base Solar System (we can't automatically fix them so it might just break, but AUTO breaks even more!)
|
||||
var useManual = (flagManualPositionUsed && !flagAutoPositionUsed) || (flagAutoPositionUsed && flagManualPositionUsed && isBaseSolarSystem);
|
||||
|
||||
// Default to AUTO in other solar systems (since we can actually fix them)
|
||||
var useAuto = (flagAutoPositionUsed && !flagManualPositionUsed) || (flagAutoPositionUsed && flagManualPositionUsed && !isBaseSolarSystem);
|
||||
|
||||
if (flagAutoPositionUsed && flagManualPositionUsed)
|
||||
{
|
||||
if (useAuto)
|
||||
{
|
||||
NHLogger.LogWarning("Can't mix manual and automatic layout of ship log map mode, defaulting to AUTOMATIC");
|
||||
}
|
||||
else
|
||||
{
|
||||
NHLogger.LogWarning("Can't mix manual and automatic layout of ship log map mode, defaulting to MANUAL");
|
||||
}
|
||||
}
|
||||
|
||||
ShipLogAstroObject[][] newNavMatrix = null;
|
||||
|
||||
if (useAuto)
|
||||
{
|
||||
newNavMatrix = ConstructMapModeAuto(bodies, transformParent, greyScaleMaterial, layer);
|
||||
}
|
||||
else if (useManual)
|
||||
{
|
||||
newNavMatrix = ConstructMapModeManual(bodies, transformParent, greyScaleMaterial, currentNav, layer);
|
||||
}
|
||||
|
||||
ReplaceExistingMapModeIcons();
|
||||
|
||||
return newNavMatrix;
|
||||
}
|
||||
|
||||
public static string GetAstroBodyShipLogName(string id)
|
||||
@ -109,6 +165,7 @@ namespace NewHorizons.Builder.ShipLog
|
||||
|
||||
ShipLogAstroObject astroObject = gameObject.AddComponent<ShipLogAstroObject>();
|
||||
astroObject._id = ShipLogHandler.GetAstroObjectId(body);
|
||||
_astroObjectToShipLog[body.Object] = astroObject;
|
||||
|
||||
Texture2D image = null;
|
||||
Texture2D outline = null;
|
||||
@ -124,6 +181,7 @@ namespace NewHorizons.Builder.ShipLog
|
||||
|
||||
astroObject._imageObj = CreateImage(gameObject, image, body.Config.name + " Revealed", layer);
|
||||
astroObject._outlineObj = CreateImage(gameObject, outline, body.Config.name + " Outline", layer);
|
||||
|
||||
if (ShipLogHandler.BodyHasEntries(body))
|
||||
{
|
||||
Image revealedImage = astroObject._imageObj.GetComponent<Image>();
|
||||
@ -138,6 +196,12 @@ namespace NewHorizons.Builder.ShipLog
|
||||
|
||||
Rect imageRect = astroObject._imageObj.GetComponent<RectTransform>().rect;
|
||||
astroObject._unviewedObj.transform.localPosition = new Vector3(imageRect.width / 2 + unviewedIconOffset, imageRect.height / 2 + unviewedIconOffset, 0);
|
||||
|
||||
// Set all icons inactive, they will be conditionally activated when the map mode is opened for the first time
|
||||
astroObject._unviewedObj.SetActive(false);
|
||||
astroObject._imageObj.SetActive(false);
|
||||
astroObject._outlineObj.SetActive(false);
|
||||
|
||||
return astroObject;
|
||||
}
|
||||
#endregion
|
||||
@ -475,7 +539,12 @@ namespace NewHorizons.Builder.ShipLog
|
||||
|
||||
private static void MakeNode(ref MapModeObject node, GameObject parent, Material greyScaleMaterial, int layer)
|
||||
{
|
||||
const float padding = 100f;
|
||||
// Space between this node and the previous node
|
||||
// Take whatever scale will prevent overlap
|
||||
var lastSiblingScale = node.lastSibling?.mainBody?.Config?.ShipLog?.mapMode?.scale ?? 1f;
|
||||
var scale = node.mainBody?.Config?.ShipLog?.mapMode?.scale ?? 1f;
|
||||
float padding = 100f * (scale + lastSiblingScale) / 2f;
|
||||
|
||||
Vector2 position = Vector2.zero;
|
||||
if (node.lastSibling != null)
|
||||
{
|
||||
@ -568,5 +637,68 @@ namespace NewHorizons.Builder.ShipLog
|
||||
|
||||
return Color.white;
|
||||
}
|
||||
|
||||
#region Replacement
|
||||
private static List<(NewHorizonsBody, ModBehaviour, MapModeInfo)> _mapModIconsToUpdate = new();
|
||||
public static void TryReplaceExistingMapModeIcon(NewHorizonsBody body, ModBehaviour mod, MapModeInfo info)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(info.revealedSprite) || !string.IsNullOrEmpty(info.outlineSprite))
|
||||
{
|
||||
_mapModIconsToUpdate.Add((body, mod, info));
|
||||
}
|
||||
}
|
||||
|
||||
private static void ReplaceExistingMapModeIcons()
|
||||
{
|
||||
foreach (var (body, mod, info) in _mapModIconsToUpdate)
|
||||
{
|
||||
ReplaceExistingMapModeIcon(body, mod, info);
|
||||
}
|
||||
_mapModIconsToUpdate.Clear();
|
||||
}
|
||||
|
||||
private static void ReplaceExistingMapModeIcon(NewHorizonsBody body, ModBehaviour mod, MapModeInfo info)
|
||||
{
|
||||
var astroObject = _astroObjectToShipLog[body.Object];
|
||||
var gameObject = astroObject.gameObject;
|
||||
var layer = gameObject.layer;
|
||||
|
||||
if (!string.IsNullOrEmpty(info.revealedSprite))
|
||||
{
|
||||
var revealedTexture = ImageUtilities.GetTexture(body.Mod, info.revealedSprite);
|
||||
if (revealedTexture == null)
|
||||
{
|
||||
NHLogger.LogError($"Couldn't load replacement revealed texture {info.revealedSprite}");
|
||||
}
|
||||
else
|
||||
{
|
||||
GameObject.Destroy(astroObject._imageObj);
|
||||
if (ShipLogHandler.IsVanillaBody(body) || ShipLogHandler.BodyHasEntries(body))
|
||||
{
|
||||
Image revealedImage = astroObject._imageObj.GetComponent<Image>();
|
||||
revealedImage.material = astroObject._greyscaleMaterial;
|
||||
revealedImage.color = Color.white;
|
||||
astroObject._image = revealedImage;
|
||||
}
|
||||
astroObject._imageObj = CreateImage(gameObject, revealedTexture, body.Config.name + " Revealed", layer);
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrEmpty(info.outlineSprite))
|
||||
{
|
||||
var outlineTexture = ImageUtilities.GetTexture(body.Mod, info.outlineSprite);
|
||||
if (outlineTexture == null)
|
||||
{
|
||||
NHLogger.LogError($"Couldn't load replacement outline texture {info.outlineSprite}");
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
GameObject.Destroy(astroObject._outlineObj);
|
||||
astroObject._outlineObj = CreateImage(gameObject, outlineTexture, body.Config.name + " Outline", layer);
|
||||
}
|
||||
}
|
||||
astroObject._invisibleWhenHidden = info.invisibleWhenHidden;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,7 +51,7 @@ namespace NewHorizons.Builder.StarSystem
|
||||
{
|
||||
if (!tex)
|
||||
{
|
||||
NHLogger.LogError($"Failed to load texture for skybox {name.ToLower()} face");
|
||||
NHLogger.LogError($"Failed to load texture for skybox {name.ToLowerInvariant()} face");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
32
NewHorizons/Components/EOTE/DreamWorldEndTimes.cs
Normal file
32
NewHorizons/Components/EOTE/DreamWorldEndTimes.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using NewHorizons.Utility.Files;
|
||||
using OWML.Common;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Components.EOTE
|
||||
{
|
||||
public class DreamWorldEndTimes : MonoBehaviour
|
||||
{
|
||||
private AudioType _endTimesAudio = AudioType.EndOfTime;
|
||||
private AudioType _endTimesDreamAudio = AudioType.EndOfTime_Dream;
|
||||
|
||||
public void SetEndTimesAudio(AudioType audio)
|
||||
{
|
||||
_endTimesAudio = audio;
|
||||
}
|
||||
|
||||
public void AssignEndTimes(OWAudioSource endTimesSource) => Assign(endTimesSource, _endTimesAudio);
|
||||
|
||||
public void SetEndTimesDreamAudio(AudioType audio)
|
||||
{
|
||||
_endTimesDreamAudio = audio;
|
||||
}
|
||||
|
||||
public void AssignEndTimesDream(OWAudioSource endTimesSource) => Assign(endTimesSource, _endTimesDreamAudio);
|
||||
|
||||
public static void Assign(OWAudioSource endTimesSource, AudioType audio)
|
||||
{
|
||||
endTimesSource.Stop();
|
||||
endTimesSource.AssignAudioLibraryClip(audio);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,15 @@
|
||||
using NewHorizons.Components.Orbital;
|
||||
|
||||
namespace NewHorizons.Components.EyeOfTheUniverse
|
||||
{
|
||||
public class EyeAstroObject : AstroObject
|
||||
public class EyeAstroObject : NHAstroObject
|
||||
{
|
||||
public EyeAstroObject()
|
||||
{
|
||||
isVanilla = true;
|
||||
modUniqueName = Main.Instance.ModHelper.Manifest.UniqueName;
|
||||
}
|
||||
|
||||
public new void Awake()
|
||||
{
|
||||
_owRigidbody = GetComponent<OWRigidbody>();
|
||||
|
||||
29
NewHorizons/Components/FixPhysics.cs
Normal file
29
NewHorizons/Components/FixPhysics.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using NewHorizons.Utility.OuterWilds;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Components;
|
||||
|
||||
[DisallowMultipleComponent]
|
||||
public class FixPhysics : MonoBehaviour
|
||||
{
|
||||
private OWRigidbody _body;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_body = GetComponent<OWRigidbody>();
|
||||
_body._lastPosition = transform.position;
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
var parentBody = _body.GetOrigParentBody();
|
||||
if (parentBody == null) return;
|
||||
_body.SetVelocity(parentBody.GetPointVelocity(_body.GetWorldCenterOfMass()));
|
||||
_body.SetAngularVelocity(parentBody.GetAngularVelocity());
|
||||
if (_body._simulateInSector) _body.OnSectorOccupantsUpdated();
|
||||
var gravity = parentBody.GetComponentInChildren<GravityVolume>();
|
||||
if (gravity != null) gravity.GetComponent<OWTriggerVolume>().AddObjectToVolume(_body.GetComponentInChildren<ForceDetector>().gameObject);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
using NewHorizons.Utility.OWML;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Components.Fixers;
|
||||
|
||||
/// <summary>
|
||||
/// Fixes a bug where spawning into the ship would not trigger the hatch entryway, so the player could drown if the ship flew into water
|
||||
/// </summary>
|
||||
internal class PlayerShipAtmosphereDetectorFix : MonoBehaviour
|
||||
{
|
||||
private PlayerCameraFluidDetector _fluidDetector;
|
||||
private SimpleFluidVolume _shipAtmosphereVolume;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
_fluidDetector = Locator.GetPlayerCameraDetector().GetComponent<PlayerCameraFluidDetector>();
|
||||
_shipAtmosphereVolume = Locator.GetShipBody()?.transform?.Find("Volumes/ShipAtmosphereVolume")?.GetComponent<SimpleFluidVolume>();
|
||||
if (_shipAtmosphereVolume == null)
|
||||
{
|
||||
Destroy(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (PlayerState.IsInsideShip())
|
||||
{
|
||||
if (!_fluidDetector._activeVolumes.Contains(_shipAtmosphereVolume))
|
||||
{
|
||||
NHLogger.LogVerbose($"{nameof(PlayerShipAtmosphereDetectorFix)} had to add the ship atmosphere volume [{_shipAtmosphereVolume}] to the fluid detector");
|
||||
_fluidDetector.AddVolume(_shipAtmosphereVolume);
|
||||
}
|
||||
NHLogger.LogVerbose($"{nameof(PlayerShipAtmosphereDetectorFix)} applied its fix");
|
||||
Component.Destroy(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -30,6 +30,13 @@ namespace NewHorizons.Components
|
||||
nnc._inactiveMaterial = materials[0];
|
||||
nnc._activeMaterial = materials[1];
|
||||
}
|
||||
|
||||
NomaiLamp nl = GetComponentInParent<NomaiLamp>();
|
||||
if (nl != null)
|
||||
{
|
||||
nl.enabled = true;
|
||||
nl.Awake();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
27
NewHorizons/Components/NHMapMarker.cs
Normal file
27
NewHorizons/Components/NHMapMarker.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NewHorizons.Components
|
||||
{
|
||||
public class NHMapMarker : MapMarker
|
||||
{
|
||||
public float minDisplayDistanceOverride = -1;
|
||||
public float maxDisplayDistanceOverride = -1;
|
||||
|
||||
public new void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
if (minDisplayDistanceOverride >= 0)
|
||||
{
|
||||
_minDisplayDistance = minDisplayDistanceOverride;
|
||||
}
|
||||
if (maxDisplayDistanceOverride >= 0)
|
||||
{
|
||||
_maxDisplayDistance = maxDisplayDistanceOverride;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -14,6 +14,11 @@ namespace NewHorizons.Components.Orbital
|
||||
public bool invulnerableToSun;
|
||||
public bool isVanilla;
|
||||
|
||||
/// <summary>
|
||||
/// The unique name of the mod that created this body or, if it is an existing body being edited, the last mod to edit it
|
||||
/// </summary>
|
||||
public string modUniqueName;
|
||||
|
||||
public void SetOrbitalParametersFromConfig(OrbitModule orbit)
|
||||
{
|
||||
SetOrbitalParametersFromTrueAnomaly(orbit.eccentricity, orbit.semiMajorAxis, orbit.inclination, orbit.argumentOfPeriapsis, orbit.longitudeOfAscendingNode, orbit.trueAnomaly);
|
||||
|
||||
@ -6,6 +6,8 @@ namespace NewHorizons.Components.Props
|
||||
{
|
||||
public class ConditionalObjectActivation : MonoBehaviour
|
||||
{
|
||||
private bool _playerAwake, _playerDoneAwake;
|
||||
|
||||
public GameObject GameObject;
|
||||
public string DialogueCondition;
|
||||
public bool CloseEyes;
|
||||
@ -19,6 +21,7 @@ namespace NewHorizons.Components.Props
|
||||
{
|
||||
var conditionalObjectActivationGO = new GameObject($"{go.name}_{condition}");
|
||||
var component = conditionalObjectActivationGO.AddComponent<ConditionalObjectActivation>();
|
||||
component.transform.parent = go.transform.parent;
|
||||
component.GameObject = go;
|
||||
component.DialogueCondition = condition;
|
||||
component.CloseEyes = closeEyes;
|
||||
@ -46,6 +49,7 @@ namespace NewHorizons.Components.Props
|
||||
GlobalMessenger<string, bool>.AddListener("DialogueConditionChanged", OnDialogueConditionChanged);
|
||||
GlobalMessenger.AddListener("ExitConversation", OnExitConversation);
|
||||
GlobalMessenger.AddListener("EnterConversation", OnEnterConversation);
|
||||
GlobalMessenger.AddListener("WakeUp", OnWakeUp);
|
||||
}
|
||||
|
||||
public void OnDestroy()
|
||||
@ -53,6 +57,23 @@ namespace NewHorizons.Components.Props
|
||||
GlobalMessenger<string, bool>.RemoveListener("DialogueConditionChanged", OnDialogueConditionChanged);
|
||||
GlobalMessenger.RemoveListener("ExitConversation", OnExitConversation);
|
||||
GlobalMessenger.RemoveListener("EnterConversation", OnEnterConversation);
|
||||
GlobalMessenger.RemoveListener("WakeUp", OnWakeUp);
|
||||
}
|
||||
|
||||
private void OnWakeUp()
|
||||
{
|
||||
_playerAwake = true;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (!_playerDoneAwake && _playerAwake)
|
||||
{
|
||||
if (!_playerCameraEffectController._isOpeningEyes)
|
||||
{
|
||||
_playerDoneAwake = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnExitConversation()
|
||||
@ -87,7 +108,7 @@ namespace NewHorizons.Components.Props
|
||||
|
||||
public void SetActive(bool active)
|
||||
{
|
||||
if (CloseEyes)
|
||||
if (CloseEyes && _playerDoneAwake && LateInitializerManager.isDoneInitializing)
|
||||
{
|
||||
Delay.StartCoroutine(Coroutine(active));
|
||||
}
|
||||
|
||||
111
NewHorizons/Components/Props/NHItem.cs
Normal file
111
NewHorizons/Components/Props/NHItem.cs
Normal file
@ -0,0 +1,111 @@
|
||||
using NewHorizons.Builder.Props;
|
||||
using NewHorizons.Handlers;
|
||||
using OWML.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Components.Props
|
||||
{
|
||||
public class NHItem : OWItem
|
||||
{
|
||||
public string DisplayName;
|
||||
public bool Droppable;
|
||||
public AudioType PickupAudio;
|
||||
public AudioType DropAudio;
|
||||
public AudioType SocketAudio;
|
||||
public AudioType UnsocketAudio;
|
||||
public Vector3 HoldOffset;
|
||||
public Vector3 HoldRotation;
|
||||
public Vector3 SocketOffset;
|
||||
public Vector3 SocketRotation;
|
||||
public string PickupCondition;
|
||||
public bool ClearPickupConditionOnDrop;
|
||||
public string PickupFact;
|
||||
|
||||
public ItemType ItemType
|
||||
{
|
||||
get => _type;
|
||||
set => _type = value;
|
||||
}
|
||||
|
||||
public override string GetDisplayName()
|
||||
{
|
||||
return TranslationHandler.GetTranslation(DisplayName, TranslationHandler.TextType.UI);
|
||||
}
|
||||
|
||||
public override bool CheckIsDroppable()
|
||||
{
|
||||
return Droppable;
|
||||
}
|
||||
|
||||
public override void PickUpItem(Transform holdTranform)
|
||||
{
|
||||
base.PickUpItem(holdTranform);
|
||||
transform.localPosition = HoldOffset;
|
||||
transform.localEulerAngles = HoldRotation;
|
||||
TriggerPickupConditions();
|
||||
PlayCustomSound(PickupAudio);
|
||||
}
|
||||
|
||||
public override void DropItem(Vector3 position, Vector3 normal, Transform parent, Sector sector, IItemDropTarget customDropTarget)
|
||||
{
|
||||
base.DropItem(position, normal, parent, sector, customDropTarget);
|
||||
TriggerDropConditions();
|
||||
PlayCustomSound(DropAudio);
|
||||
}
|
||||
|
||||
public override void SocketItem(Transform socketTransform, Sector sector)
|
||||
{
|
||||
base.SocketItem(socketTransform, sector);
|
||||
transform.localPosition = SocketOffset;
|
||||
transform.localEulerAngles = SocketRotation;
|
||||
TriggerDropConditions();
|
||||
PlayCustomSound(SocketAudio);
|
||||
}
|
||||
|
||||
public override void OnCompleteUnsocket()
|
||||
{
|
||||
base.OnCompleteUnsocket();
|
||||
TriggerPickupConditions();
|
||||
PlayCustomSound(UnsocketAudio);
|
||||
}
|
||||
|
||||
internal void TriggerPickupConditions()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(PickupCondition))
|
||||
{
|
||||
DialogueConditionManager.SharedInstance.SetConditionState(PickupCondition, true);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(PickupFact))
|
||||
{
|
||||
Locator.GetShipLogManager().RevealFact(PickupFact);
|
||||
}
|
||||
}
|
||||
|
||||
internal void TriggerDropConditions()
|
||||
{
|
||||
if (ClearPickupConditionOnDrop && !string.IsNullOrEmpty(PickupCondition))
|
||||
{
|
||||
DialogueConditionManager.SharedInstance.SetConditionState(PickupCondition, false);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayCustomSound(AudioType audioType)
|
||||
{
|
||||
if (ItemBuilder.IsCustomItemType(ItemType))
|
||||
{
|
||||
Locator.GetPlayerAudioController()._oneShotExternalSource.PlayOneShot(audioType);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Vanilla items play sounds via hard-coded ItemType switch statements
|
||||
// in the PlayerAudioController code, so there's no clean way to override them
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
92
NewHorizons/Components/Props/NHItemSocket.cs
Normal file
92
NewHorizons/Components/Props/NHItemSocket.cs
Normal file
@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Components.Props
|
||||
{
|
||||
public class NHItemSocket : OWItemSocket
|
||||
{
|
||||
public bool UseGiveTakePrompts;
|
||||
public string InsertCondition;
|
||||
public bool ClearInsertConditionOnRemoval;
|
||||
public string InsertFact;
|
||||
public string RemovalCondition;
|
||||
public bool ClearRemovalConditionOnInsert;
|
||||
public string RemovalFact;
|
||||
|
||||
public ItemType ItemType
|
||||
{
|
||||
get => _acceptableType;
|
||||
set => _acceptableType = value;
|
||||
}
|
||||
|
||||
public override bool UsesGiveTakePrompts()
|
||||
{
|
||||
return UseGiveTakePrompts;
|
||||
}
|
||||
|
||||
public override bool AcceptsItem(OWItem item)
|
||||
{
|
||||
if (item == null || item._type == ItemType.Invalid)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return ItemType == item._type;
|
||||
}
|
||||
|
||||
public override bool PlaceIntoSocket(OWItem item)
|
||||
{
|
||||
if (base.PlaceIntoSocket(item))
|
||||
{
|
||||
TriggerInsertConditions();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public override OWItem RemoveFromSocket()
|
||||
{
|
||||
var removedItem = base.RemoveFromSocket();
|
||||
if (removedItem != null)
|
||||
{
|
||||
TriggerRemovalConditions();
|
||||
}
|
||||
return removedItem;
|
||||
}
|
||||
|
||||
internal void TriggerInsertConditions()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(InsertCondition))
|
||||
{
|
||||
DialogueConditionManager.SharedInstance.SetConditionState(InsertCondition, true);
|
||||
}
|
||||
if (ClearRemovalConditionOnInsert && !string.IsNullOrEmpty(RemovalCondition))
|
||||
{
|
||||
DialogueConditionManager.SharedInstance.SetConditionState(RemovalCondition, false);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(InsertFact))
|
||||
{
|
||||
Locator.GetShipLogManager().RevealFact(InsertFact);
|
||||
}
|
||||
}
|
||||
|
||||
internal void TriggerRemovalConditions()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(RemovalCondition))
|
||||
{
|
||||
DialogueConditionManager.SharedInstance.SetConditionState(RemovalCondition, true);
|
||||
}
|
||||
if (ClearInsertConditionOnRemoval && !string.IsNullOrEmpty(InsertCondition))
|
||||
{
|
||||
DialogueConditionManager.SharedInstance.SetConditionState(InsertCondition, false);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(RemovalFact))
|
||||
{
|
||||
Locator.GetShipLogManager().RevealFact(RemovalFact);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
33
NewHorizons/Components/Props/NHRaftController.cs
Normal file
33
NewHorizons/Components/Props/NHRaftController.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Components.Props
|
||||
{
|
||||
public class NHRaftController : MonoBehaviour
|
||||
{
|
||||
RaftController raft;
|
||||
|
||||
public void OnEnable()
|
||||
{
|
||||
raft = GetComponent<RaftController>();
|
||||
raft._fluidDetector.OnEnterFluid += OnEnterFluid;
|
||||
}
|
||||
|
||||
public void OnDisable()
|
||||
{
|
||||
raft._fluidDetector.OnEnterFluid -= OnEnterFluid;
|
||||
}
|
||||
|
||||
private void OnEnterFluid(FluidVolume volume)
|
||||
{
|
||||
if (volume.GetFluidType() == FluidVolume.Type.WATER)
|
||||
{
|
||||
raft._fluidDetector._alignmentFluid = volume;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -184,11 +184,15 @@ namespace NewHorizons.Components.Props
|
||||
{
|
||||
float collapseProgress = SunController.GetCollapseProgress();
|
||||
|
||||
if (_ambientLight != null)
|
||||
if (_ambientLight != null && _ambientLightOrigIntensity != null)
|
||||
{
|
||||
for (int i = 0; i < _ambientLight.Length; i++)
|
||||
{
|
||||
_ambientLight[i].intensity = _ambientLightOrigIntensity[i] * (1f - collapseProgress);
|
||||
var ambientLight = _ambientLight[i];
|
||||
if (ambientLight != null)
|
||||
{
|
||||
ambientLight.intensity = _ambientLightOrigIntensity[i] * (1f - collapseProgress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -221,11 +225,14 @@ namespace NewHorizons.Components.Props
|
||||
{
|
||||
if (_shockLayer != null) _shockLayer.enabled = false;
|
||||
|
||||
if (_ambientLight != null)
|
||||
if (_ambientLight != null && _ambientLightOrigIntensity != null)
|
||||
{
|
||||
for (int i = 0; i < _ambientLight.Length; i++)
|
||||
{
|
||||
_ambientLight[i].intensity = _ambientLightOrigIntensity[i];
|
||||
if (_ambientLight[i] != null)
|
||||
{
|
||||
_ambientLight[i].intensity = _ambientLightOrigIntensity[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -4,9 +4,12 @@ using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Components.Quantum
|
||||
{
|
||||
/// <summary>
|
||||
/// exists because MultiStateQuantumObject only checks visibility on the current state,
|
||||
/// whereas this one also checks on each new state, in case they are bigger
|
||||
/// </summary>
|
||||
public class NHMultiStateQuantumObject : MultiStateQuantumObject
|
||||
{
|
||||
|
||||
public override bool ChangeQuantumState(bool skipInstantVisibilityCheck)
|
||||
{
|
||||
for (int i = 0; i < _prerequisiteObjects.Length; i++)
|
||||
@ -43,15 +46,9 @@ namespace NewHorizons.Components.Quantum
|
||||
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
|
||||
// }
|
||||
//
|
||||
// Iterate over list of possible states to find a valid state to collapse to
|
||||
// current state is excluded, and states are randomly ordered using a weighted random roll to prioritize states with higher probability
|
||||
// NOTE: they aren't actually pre-sorted into this random order, this random ordering is done on the fly using RollState
|
||||
|
||||
List<int> indices = new List<int>();
|
||||
for (var i = 0; i < _states.Length; i++) if (i != stateIndex) indices.Add(i);
|
||||
@ -94,17 +91,15 @@ namespace NewHorizons.Components.Quantum
|
||||
{
|
||||
var isPlayerEntangled = IsPlayerEntangled();
|
||||
var illumination = CheckIllumination();
|
||||
// faster than full CheckVisibility
|
||||
var visibility = CheckVisibilityInstantly();
|
||||
var playerInside = CheckPointInside(Locator.GetPlayerCamera().transform.position);
|
||||
// does not check probe, but thats okay
|
||||
|
||||
var isVisible =
|
||||
isPlayerEntangled
|
||||
? illumination
|
||||
:
|
||||
illumination
|
||||
? visibility
|
||||
: playerInside
|
||||
;
|
||||
var notEntangledCheck = illumination ? visibility : playerInside;
|
||||
var isVisible = isPlayerEntangled ? illumination : notEntangledCheck;
|
||||
// I think this is what the above two lines simplify to but I don't want to test this:
|
||||
// illumination ? visibility || isPlayerEntangled : playerInside
|
||||
|
||||
return !isVisible;
|
||||
}
|
||||
|
||||
@ -22,6 +22,16 @@ namespace NewHorizons.Components.Quantum
|
||||
private OWRigidbody _rb;
|
||||
private OrbitLine _orbitLine;
|
||||
|
||||
public NHAstroObject astroObject
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_astroObject == null)
|
||||
_astroObject = GetComponent<NHAstroObject>();
|
||||
return _astroObject;
|
||||
}
|
||||
}
|
||||
|
||||
public int CurrentIndex { get; private set; }
|
||||
|
||||
public override void Awake()
|
||||
@ -97,7 +107,7 @@ namespace NewHorizons.Components.Quantum
|
||||
|
||||
primaryBody = AstroObjectLocator.GetAstroObject(newOrbit.primaryBody);
|
||||
var primaryGravity = new Gravity(primaryBody.GetGravityVolume());
|
||||
var secondaryGravity = new Gravity(_astroObject.GetGravityVolume());
|
||||
var secondaryGravity = new Gravity(astroObject.GetGravityVolume());
|
||||
orbitalParams = newOrbit.GetOrbitalParameters(primaryGravity, secondaryGravity);
|
||||
|
||||
var pos = primaryBody.transform.position + orbitalParams.InitialPosition;
|
||||
@ -139,15 +149,16 @@ namespace NewHorizons.Components.Quantum
|
||||
|
||||
private void SetNewOrbit(AstroObject primaryBody, OrbitalParameters orbitalParameters)
|
||||
{
|
||||
_astroObject._primaryBody = primaryBody;
|
||||
DetectorBuilder.SetDetector(primaryBody, _astroObject, _detector);
|
||||
astroObject._primaryBody = primaryBody;
|
||||
DetectorBuilder.SetDetector(primaryBody, astroObject, _detector);
|
||||
_detector._activeInheritedDetector = primaryBody.GetComponentInChildren<ForceDetector>();
|
||||
_detector._activeVolumes = new List<EffectVolume>() { primaryBody.GetGravityVolume() };
|
||||
if (_alignment != null) _alignment.SetTargetBody(primaryBody.GetComponent<OWRigidbody>());
|
||||
|
||||
_astroObject.SetOrbitalParametersFromTrueAnomaly(orbitalParameters.eccentricity, orbitalParameters.semiMajorAxis, orbitalParameters.inclination, orbitalParameters.argumentOfPeriapsis, orbitalParameters.longitudeOfAscendingNode, orbitalParameters.trueAnomaly);
|
||||
astroObject.SetOrbitalParametersFromTrueAnomaly(orbitalParameters.eccentricity, orbitalParameters.semiMajorAxis, orbitalParameters.inclination, orbitalParameters.argumentOfPeriapsis, orbitalParameters.longitudeOfAscendingNode, orbitalParameters.trueAnomaly);
|
||||
|
||||
PlanetCreationHandler.UpdatePosition(gameObject, orbitalParameters, primaryBody, _astroObject);
|
||||
PlanetCreationHandler.UpdatePosition(gameObject, orbitalParameters, primaryBody, astroObject);
|
||||
gameObject.transform.parent = null;
|
||||
|
||||
if (!Physics.autoSyncTransforms)
|
||||
{
|
||||
|
||||
@ -33,13 +33,16 @@ namespace NewHorizons.Components.Sectored
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
DisableRenderers();
|
||||
}
|
||||
|
||||
private void GetRenderers()
|
||||
{
|
||||
_renderers = gameObject.GetComponentsInChildren<Renderer>();
|
||||
_tessellatedRenderers = gameObject.GetComponentsInChildren<TessellatedRenderer>();
|
||||
_colliders = gameObject.GetComponentsInChildren<Collider>();
|
||||
_lights = gameObject.GetComponentsInChildren<Light>();
|
||||
|
||||
DisableRenderers();
|
||||
}
|
||||
|
||||
private void OnSectorOccupantsUpdated()
|
||||
@ -54,54 +57,35 @@ namespace NewHorizons.Components.Sectored
|
||||
}
|
||||
}
|
||||
|
||||
private void EnableRenderers()
|
||||
private void EnableRenderers() => ToggleRenderers(true);
|
||||
|
||||
private void DisableRenderers() => ToggleRenderers(false);
|
||||
|
||||
private void ToggleRenderers(bool visible)
|
||||
{
|
||||
GetRenderers();
|
||||
|
||||
foreach (var renderer in _renderers)
|
||||
{
|
||||
renderer.forceRenderingOff = false;
|
||||
renderer.forceRenderingOff = !visible;
|
||||
}
|
||||
|
||||
foreach (var tessellatedRenderer in _tessellatedRenderers)
|
||||
{
|
||||
tessellatedRenderer.enabled = true;
|
||||
tessellatedRenderer.enabled = visible;
|
||||
}
|
||||
|
||||
foreach (var collider in _colliders)
|
||||
{
|
||||
collider.enabled = true;
|
||||
collider.enabled = visible;
|
||||
}
|
||||
|
||||
foreach (var light in _lights)
|
||||
{
|
||||
light.enabled = true;
|
||||
light.enabled = visible;
|
||||
}
|
||||
|
||||
_renderersShown = true;
|
||||
}
|
||||
|
||||
private void DisableRenderers()
|
||||
{
|
||||
foreach (var renderer in _renderers)
|
||||
{
|
||||
renderer.forceRenderingOff = true;
|
||||
}
|
||||
|
||||
foreach (var tessellatedRenderer in _tessellatedRenderers)
|
||||
{
|
||||
tessellatedRenderer.enabled = false;
|
||||
}
|
||||
|
||||
foreach (var collider in _colliders)
|
||||
{
|
||||
collider.enabled = false;
|
||||
}
|
||||
|
||||
foreach (var light in _lights)
|
||||
{
|
||||
light.enabled = false;
|
||||
}
|
||||
|
||||
_renderersShown = false;
|
||||
_renderersShown = visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,12 +47,6 @@ namespace NewHorizons.Components.Ship
|
||||
public void Start()
|
||||
{
|
||||
_isWarpingIn = false;
|
||||
GlobalMessenger.AddListener("FinishOpenEyes", new Callback(OnFinishOpenEyes));
|
||||
}
|
||||
|
||||
public void OnDestroy()
|
||||
{
|
||||
GlobalMessenger.RemoveListener("FinishOpenEyes", new Callback(OnFinishOpenEyes));
|
||||
}
|
||||
|
||||
private void MakeBlackHole()
|
||||
@ -144,6 +138,12 @@ namespace NewHorizons.Components.Ship
|
||||
resources._currentHealth = 100f;
|
||||
if (!PlayerState.AtFlightConsole()) TeleportToShip();
|
||||
}
|
||||
|
||||
if (PlayerState.IsInsideShip() && !_eyesOpen)
|
||||
{
|
||||
_eyesOpen = true;
|
||||
Locator.GetPlayerCamera().GetComponent<PlayerCameraEffectController>().OpenEyesImmediate();
|
||||
}
|
||||
}
|
||||
|
||||
// Idk whats making this work but now it works and idc
|
||||
@ -154,11 +154,6 @@ namespace NewHorizons.Components.Ship
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFinishOpenEyes()
|
||||
{
|
||||
_eyesOpen = true;
|
||||
}
|
||||
|
||||
private void StartWarpInEffect()
|
||||
{
|
||||
NHLogger.LogVerbose("Starting warp-in effect");
|
||||
@ -203,6 +198,7 @@ namespace NewHorizons.Components.Ship
|
||||
PlayerState.OnEnterShip();
|
||||
|
||||
PlayerSpawnHandler.SpawnShip();
|
||||
OWInput.ChangeInputMode(InputMode.ShipCockpit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,10 +34,13 @@ namespace NewHorizons.Components.ShipLog
|
||||
|
||||
private int _nextCardIndex;
|
||||
|
||||
private HashSet<string> _systemCards = new();
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// Prompts
|
||||
Locator.GetPromptManager().AddScreenPrompt(_warpPrompt, PromptPosition.UpperLeft, false);
|
||||
_systemCards.Clear();
|
||||
}
|
||||
|
||||
public override void Initialize(ScreenPromptList centerPromptList, ScreenPromptList upperRightPromptList, OWAudioSource oneShotSource)
|
||||
@ -54,20 +57,7 @@ namespace NewHorizons.Components.ShipLog
|
||||
_nextCardIndex = 0;
|
||||
foreach (var starSystem in Main.SystemDict.Keys)
|
||||
{
|
||||
// Get rid of the warp option for the current system
|
||||
if (starSystem == Main.Instance.CurrentStarSystem) continue;
|
||||
|
||||
var config = Main.SystemDict[starSystem];
|
||||
|
||||
// Conditions to allow warping into that system (either no planets (stock system) or has a ship spawn point)
|
||||
var flag = false;
|
||||
if (starSystem.Equals("SolarSystem")) flag = true;
|
||||
else if (starSystem.Equals("EyeOfTheUniverse")) flag = false;
|
||||
else if (config.Spawn?.shipSpawn != null) flag = true;
|
||||
|
||||
if (!StarChartHandler.HasUnlockedSystem(starSystem)) continue;
|
||||
|
||||
if (flag && Main.SystemDict[starSystem].Config.canEnterViaWarpDrive)
|
||||
if (StarChartHandler.CanWarpToSystem(starSystem))
|
||||
{
|
||||
AddSystemCard(starSystem);
|
||||
}
|
||||
@ -83,8 +73,15 @@ namespace NewHorizons.Components.ShipLog
|
||||
|
||||
public void AddSystemCard(string uniqueID)
|
||||
{
|
||||
var card = CreateCard(uniqueID, root.transform, new Vector2(_nextCardIndex++ * 200, 0));
|
||||
_starSystemCards.Add(card);
|
||||
if (!_systemCards.Contains(uniqueID))
|
||||
{
|
||||
var card = CreateCard(uniqueID, root.transform, new Vector2(_nextCardIndex++ * 200, 0));
|
||||
_starSystemCards.Add(card);
|
||||
}
|
||||
else
|
||||
{
|
||||
NHLogger.LogWarning($"Tried making duplicate system card {uniqueID}");
|
||||
}
|
||||
}
|
||||
|
||||
public void OnDestroy()
|
||||
@ -99,7 +96,7 @@ namespace NewHorizons.Components.ShipLog
|
||||
if (_cardTemplate == null)
|
||||
{
|
||||
var panRoot = SearchUtilities.Find("Ship_Body/Module_Cabin/Systems_Cabin/ShipLogPivot/ShipLog/ShipLogPivot/ShipLogCanvas/DetectiveMode/ScaleRoot/PanRoot");
|
||||
_cardTemplate = Instantiate(panRoot.GetComponentInChildren<ShipLogEntryCard>().gameObject);
|
||||
_cardTemplate = Instantiate(panRoot.GetComponentInChildren<ShipLogEntryCard>(true).gameObject);
|
||||
_cardTemplate.SetActive(false);
|
||||
}
|
||||
|
||||
@ -212,6 +209,12 @@ namespace NewHorizons.Components.ShipLog
|
||||
|
||||
private void UpdateMapCamera()
|
||||
{
|
||||
if (_starSystemCards.Count == 0)
|
||||
{
|
||||
NHLogger.LogWarning("Showing star chart mode when there are no avaialble systems");
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 b = -_starSystemCards[_cardIndex].transform.localPosition;
|
||||
float num = Mathf.InverseLerp(_startPanTime, _startPanTime + _panDuration, Time.unscaledTime);
|
||||
num = 1f - (num - 1f) * (num - 1f);
|
||||
@ -295,7 +298,7 @@ namespace NewHorizons.Components.ShipLog
|
||||
|
||||
var name = UniqueIDToName(shipLogEntryCard.name);
|
||||
|
||||
var warpNotificationDataText = TranslationHandler.GetTranslation("WARP_LOCKED", TranslationHandler.TextType.UI).Replace("{0}", name.ToUpper());
|
||||
var warpNotificationDataText = TranslationHandler.GetTranslation("WARP_LOCKED", TranslationHandler.TextType.UI).Replace("{0}", name.ToUpperFixed());
|
||||
_warpNotificationData = new NotificationData(warpNotificationDataText);
|
||||
NotificationManager.SharedInstance.PostNotification(_warpNotificationData, true);
|
||||
|
||||
|
||||
@ -13,6 +13,9 @@ namespace NewHorizons.Components
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// So that mods can turn the time loop on/off using the TimLoop.SetTimeLoopEnabled method
|
||||
if (!TimeLoop._timeLoopEnabled) return;
|
||||
|
||||
// Stock gives like 33 seconds after the sun collapses
|
||||
if (_supernovaHappened && Time.time > _supernovaTime + 50f)
|
||||
{
|
||||
|
||||
@ -45,11 +45,55 @@ namespace NewHorizons.Components
|
||||
_powerOrb.AddLock();
|
||||
}
|
||||
|
||||
public void RemoveLocks()
|
||||
public void RemoveLockFromCoordinateOrb()
|
||||
{
|
||||
_coordinateInterfaceOrb.RemoveLock();
|
||||
}
|
||||
|
||||
public void RemoveLockFromWarpOrb()
|
||||
{
|
||||
_coordinateInterfaceUpperOrb.RemoveLock();
|
||||
}
|
||||
|
||||
public void RemoveLockFromPowerOrb()
|
||||
{
|
||||
_powerOrb.RemoveLock();
|
||||
}
|
||||
|
||||
public void RemoveAllLocksFromCoordinateOrb()
|
||||
{
|
||||
_coordinateInterfaceOrb.RemoveAllLocks();
|
||||
}
|
||||
|
||||
public void RemoveAllLocksFromWarpOrb()
|
||||
{
|
||||
_coordinateInterfaceUpperOrb.RemoveAllLocks();
|
||||
}
|
||||
|
||||
public void RemoveAllLocksFromPowerOrb()
|
||||
{
|
||||
_powerOrb.RemoveAllLocks();
|
||||
}
|
||||
|
||||
public void AddLock()
|
||||
{
|
||||
AddLockToCoordinateOrb();
|
||||
AddLockToWarpOrb();
|
||||
AddLockToPowerOrb();
|
||||
}
|
||||
|
||||
public void RemoveLock()
|
||||
{
|
||||
RemoveLockFromCoordinateOrb();
|
||||
RemoveLockFromWarpOrb();
|
||||
RemoveLockFromPowerOrb();
|
||||
}
|
||||
|
||||
public void RemoveAllLocks()
|
||||
{
|
||||
RemoveAllLocksFromCoordinateOrb();
|
||||
RemoveAllLocksFromWarpOrb();
|
||||
RemoveAllLocksFromPowerOrb();
|
||||
}
|
||||
}
|
||||
}
|
||||
78
NewHorizons/Components/Vessel/VesselSpawnPoint.cs
Normal file
78
NewHorizons/Components/Vessel/VesselSpawnPoint.cs
Normal file
@ -0,0 +1,78 @@
|
||||
using NewHorizons.Handlers;
|
||||
using NewHorizons.Utility;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Components
|
||||
{
|
||||
[UsedInUnityProject]
|
||||
public class VesselSpawnPoint : EyeSpawnPoint
|
||||
{
|
||||
public GameObject warpControllerObject;
|
||||
private VesselWarpController _warpController;
|
||||
|
||||
public VesselWarpController WarpController
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_warpController == null && warpControllerObject != null) WarpController = warpControllerObject.GetComponent<VesselWarpController>();
|
||||
return _warpController;
|
||||
}
|
||||
set
|
||||
{
|
||||
_warpController = value;
|
||||
if (_warpController != null) _triggerVolumes = new OWTriggerVolume[1] { _warpController._bridgeVolume };
|
||||
}
|
||||
}
|
||||
|
||||
public VesselSpawnPoint()
|
||||
{
|
||||
_eyeState = EyeState.AboardVessel;
|
||||
}
|
||||
|
||||
public int index = 0;
|
||||
|
||||
public void WarpPlayer(bool spawn = false)
|
||||
{
|
||||
var warpController = WarpController;
|
||||
var player = Locator.GetPlayerBody();
|
||||
var relativeTransform = warpController.transform;
|
||||
var relativeBody = relativeTransform.GetAttachedOWRigidbody();
|
||||
if (!spawn) Locator.GetPlayerCamera().GetComponent<PlayerCameraEffectController>().OpenEyesImmediate();
|
||||
if (VesselWarpController.s_relativeLocationSaved)
|
||||
{
|
||||
Locator.GetPlayerBody().MoveToRelativeLocation(VesselWarpController.s_playerWarpLocation, relativeBody, relativeTransform);
|
||||
}
|
||||
else
|
||||
{
|
||||
player.SetPosition(warpController._defaultPlayerWarpPoint.position);
|
||||
player.SetRotation(warpController._defaultPlayerWarpPoint.rotation);
|
||||
player.SetVelocity(relativeBody.GetPointVelocity(warpController._defaultPlayerWarpPoint.position));
|
||||
}
|
||||
AddPlayerToVolume(warpController._bridgeVolume);
|
||||
AddPlayerToTriggerVolumes();
|
||||
}
|
||||
|
||||
public override void OnSpawnPlayer()
|
||||
{
|
||||
Delay.FireOnNextUpdate(() => WarpPlayer(true));
|
||||
}
|
||||
|
||||
public void AddPlayerToVolume(OWTriggerVolume volume)
|
||||
{
|
||||
volume.AddObjectToVolume(Locator.GetPlayerDetector());
|
||||
volume.AddObjectToVolume(Locator.GetPlayerCameraDetector());
|
||||
}
|
||||
|
||||
public void AddPlayerToTriggerVolumes()
|
||||
{
|
||||
AddObjectToTriggerVolumes(Locator.GetPlayerDetector());
|
||||
AddObjectToTriggerVolumes(Locator.GetPlayerCameraDetector());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,22 +3,27 @@ using UnityEngine;
|
||||
namespace NewHorizons.Components.Volumes
|
||||
{
|
||||
[RequireComponent(typeof(OWTriggerVolume))]
|
||||
public abstract class BaseVolume : MonoBehaviour
|
||||
public abstract class BaseVolume : SectoredMonoBehaviour
|
||||
{
|
||||
private OWTriggerVolume _triggerVolume;
|
||||
|
||||
public virtual void Awake()
|
||||
public override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
_triggerVolume = this.GetRequiredComponent<OWTriggerVolume>();
|
||||
_triggerVolume.OnEntry += OnTriggerVolumeEntry;
|
||||
_triggerVolume.OnExit += OnTriggerVolumeExit;
|
||||
}
|
||||
|
||||
public virtual void OnDestroy()
|
||||
public override void OnDestroy()
|
||||
{
|
||||
if (_triggerVolume == null) return;
|
||||
_triggerVolume.OnEntry -= OnTriggerVolumeEntry;
|
||||
_triggerVolume.OnExit -= OnTriggerVolumeExit;
|
||||
base.OnDestroy();
|
||||
|
||||
if (_triggerVolume != null)
|
||||
{
|
||||
_triggerVolume.OnEntry -= OnTriggerVolumeEntry;
|
||||
_triggerVolume.OnExit -= OnTriggerVolumeExit;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void OnTriggerVolumeEntry(GameObject hitObj);
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
namespace NewHorizons.Components.Volumes
|
||||
{
|
||||
public class NHInnerFogWarpVolume : InnerFogWarpVolume
|
||||
{
|
||||
public override bool IsProbeOnly() => _exitRadius <= 6;
|
||||
public override float GetFogThickness() => _exitRadius;
|
||||
}
|
||||
}
|
||||
128
NewHorizons/Components/Volumes/StreamingWarpVolume.cs
Normal file
128
NewHorizons/Components/Volumes/StreamingWarpVolume.cs
Normal file
@ -0,0 +1,128 @@
|
||||
using NewHorizons.Utility.OWML;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Components.Volumes
|
||||
{
|
||||
/// <summary>
|
||||
/// Currently only relevant for Vanilla planets, which actually have streaming groups
|
||||
/// </summary>
|
||||
internal class StreamingWarpVolume : BaseVolume
|
||||
{
|
||||
public StreamingGroup streamingGroup;
|
||||
private bool _probeInVolume;
|
||||
private bool _playerInVolume;
|
||||
private bool _preloadingRequiredAssets;
|
||||
private bool _preloadingGeneralAssets;
|
||||
|
||||
private SurveyorProbe _probe;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
_probe = Locator.GetProbe();
|
||||
base.enabled = false;
|
||||
}
|
||||
|
||||
public void FixedUpdate()
|
||||
{
|
||||
// Bug report on Astral Codec mod page - Huge lag inside streaming warp volume, possible NRE?
|
||||
if (_probe == null)
|
||||
{
|
||||
_probe = Locator.GetProbe();
|
||||
if (_probe == null)
|
||||
{
|
||||
NHLogger.LogError($"How is your scout probe null? Destroying {nameof(StreamingWarpVolume)}");
|
||||
GameObject.DestroyImmediate(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
if (streamingGroup == null)
|
||||
{
|
||||
NHLogger.LogError($"{nameof(StreamingWarpVolume)} has no streaming group. Destroying {nameof(StreamingWarpVolume)}");
|
||||
GameObject.DestroyImmediate(gameObject);
|
||||
}
|
||||
|
||||
bool probeActive = _probe.IsLaunched() && !_probe.IsAnchored();
|
||||
|
||||
bool shouldBeLoadingRequiredAssets = _playerInVolume || (_probeInVolume && probeActive);
|
||||
bool shouldBeLoadingGeneralAssets = _playerInVolume;
|
||||
|
||||
UpdatePreloadingState(shouldBeLoadingRequiredAssets, shouldBeLoadingGeneralAssets);
|
||||
}
|
||||
|
||||
private void UpdatePreloadingState(bool shouldBeLoadingRequiredAssets, bool shouldBeLoadingGeneralAssets)
|
||||
{
|
||||
if (!this._preloadingRequiredAssets && shouldBeLoadingRequiredAssets)
|
||||
{
|
||||
this.streamingGroup.RequestRequiredAssets(0);
|
||||
this._preloadingRequiredAssets = true;
|
||||
}
|
||||
else if (this._preloadingRequiredAssets && !shouldBeLoadingRequiredAssets)
|
||||
{
|
||||
this.streamingGroup.ReleaseRequiredAssets();
|
||||
this._preloadingRequiredAssets = false;
|
||||
}
|
||||
if (!this._preloadingGeneralAssets && shouldBeLoadingGeneralAssets)
|
||||
{
|
||||
this.streamingGroup.RequestGeneralAssets(0);
|
||||
this._preloadingGeneralAssets = true;
|
||||
return;
|
||||
}
|
||||
if (this._preloadingGeneralAssets && !shouldBeLoadingGeneralAssets)
|
||||
{
|
||||
this.streamingGroup.ReleaseGeneralAssets();
|
||||
this._preloadingGeneralAssets = false;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnSectorOccupantsUpdated()
|
||||
{
|
||||
if (this._sector.ContainsAnyOccupants(DynamicOccupant.Player | DynamicOccupant.Probe))
|
||||
{
|
||||
if (StreamingManager.isStreamingEnabled && this.streamingGroup != null)
|
||||
{
|
||||
base.enabled = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.UpdatePreloadingState(false, false);
|
||||
base.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnTriggerVolumeEntry(GameObject hitObj)
|
||||
{
|
||||
OWRigidbody attachedOWRigidbody = hitObj.GetAttachedOWRigidbody(false);
|
||||
if (attachedOWRigidbody != null)
|
||||
{
|
||||
if (attachedOWRigidbody.CompareTag("Player"))
|
||||
{
|
||||
this._playerInVolume = true;
|
||||
return;
|
||||
}
|
||||
if (attachedOWRigidbody.CompareTag("Probe"))
|
||||
{
|
||||
this._probeInVolume = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnTriggerVolumeExit(GameObject hitObj)
|
||||
{
|
||||
OWRigidbody attachedOWRigidbody = hitObj.GetAttachedOWRigidbody(false);
|
||||
if (attachedOWRigidbody != null)
|
||||
{
|
||||
if (attachedOWRigidbody.CompareTag("Player"))
|
||||
{
|
||||
this._playerInVolume = false;
|
||||
return;
|
||||
}
|
||||
if (attachedOWRigidbody.CompareTag("Probe"))
|
||||
{
|
||||
this._probeInVolume = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
NewHorizons/External/Configs/AddonConfig.cs
vendored
12
NewHorizons/External/Configs/AddonConfig.cs
vendored
@ -31,5 +31,17 @@ namespace NewHorizons.External.Configs
|
||||
/// If popupMessage is set, should it repeat every time the game starts or only once
|
||||
/// </summary>
|
||||
public bool repeatPopup;
|
||||
|
||||
/// <summary>
|
||||
/// These asset bundles will be loaded on the title screen and stay loaded. Will improve initial load time at the cost of increased memory use.
|
||||
/// The path is the relative directory of the asset bundle in the mod folder.
|
||||
/// </summary>
|
||||
public string[] preloadAssetBundles;
|
||||
|
||||
/// <summary>
|
||||
/// The path to the addons subtitle for the main menu.
|
||||
/// Defaults to "subtitle.png".
|
||||
/// </summary>
|
||||
public string subtitlePath = "subtitle.png";
|
||||
}
|
||||
}
|
||||
|
||||
14
NewHorizons/External/Configs/PlanetConfig.cs
vendored
14
NewHorizons/External/Configs/PlanetConfig.cs
vendored
@ -110,6 +110,11 @@ namespace NewHorizons.External.Configs
|
||||
/// </summary>
|
||||
public LavaModule Lava;
|
||||
|
||||
/// <summary>
|
||||
/// Map marker properties of this body
|
||||
/// </summary>
|
||||
public MapMarkerModule MapMarker;
|
||||
|
||||
/// <summary>
|
||||
/// Describes this Body's orbit (or lack there of)
|
||||
/// </summary>
|
||||
@ -210,8 +215,8 @@ namespace NewHorizons.External.Configs
|
||||
// Always have to have a base module
|
||||
if (Base == null) Base = new BaseModule();
|
||||
if (Orbit == null) Orbit = new OrbitModule();
|
||||
if (ShipLog == null) ShipLog = new ShipLogModule();
|
||||
if (ReferenceFrame == null) ReferenceFrame = new ReferenceFrameModule();
|
||||
if (MapMarker == null) MapMarker = new MapMarkerModule();
|
||||
}
|
||||
|
||||
public void Validate()
|
||||
@ -278,7 +283,7 @@ namespace NewHorizons.External.Configs
|
||||
|
||||
public void Migrate()
|
||||
{
|
||||
// Backwards compatability
|
||||
// Backwards compatibility
|
||||
// Should be the only place that obsolete things are referenced
|
||||
#pragma warning disable 612, 618
|
||||
if (Base.waterSize != 0)
|
||||
@ -305,6 +310,8 @@ namespace NewHorizons.External.Configs
|
||||
|
||||
if (!Base.hasReferenceFrame) ReferenceFrame.enabled = false;
|
||||
|
||||
if (Base.hasMapMarker) MapMarker.enabled = true;
|
||||
|
||||
if (childrenToDestroy != null) removeChildren = childrenToDestroy;
|
||||
|
||||
if (Base.cloakRadius != 0)
|
||||
@ -456,6 +463,9 @@ namespace NewHorizons.External.Configs
|
||||
if (Star != null)
|
||||
{
|
||||
if (!Star.goSupernova) Star.stellarDeathType = StellarDeathType.None;
|
||||
|
||||
// Gave up on supporting pulsars
|
||||
if (Star.stellarRemnantType == StellarRemnantType.Pulsar) Star.stellarRemnantType = StellarRemnantType.NeutronStar;
|
||||
}
|
||||
|
||||
// Signals no longer use two different variables for audio
|
||||
|
||||
108
NewHorizons/External/Configs/StarSystemConfig.cs
vendored
108
NewHorizons/External/Configs/StarSystemConfig.cs
vendored
@ -2,6 +2,7 @@ using System;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using NewHorizons.External.Modules;
|
||||
using NewHorizons.External.SerializableData;
|
||||
using Newtonsoft.Json;
|
||||
@ -36,10 +37,29 @@ namespace NewHorizons.External.Configs
|
||||
public float farClipPlaneOverride;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this system can be warped to via the warp drive. If you set factRequiredForWarp, this will be true.
|
||||
/// Whether this system can be warped to via the warp drive. If you set `factRequiredForWarp`, this will be true.
|
||||
/// Does NOT effect the base SolarSystem. For that, see `canExitViaWarpDrive` and `factRequiredToExitViaWarpDrive`
|
||||
/// </summary>
|
||||
[DefaultValue(true)] public bool canEnterViaWarpDrive = true;
|
||||
|
||||
/// <summary>
|
||||
/// The FactID that must be revealed before it can be warped to. Don't set `canEnterViaWarpDrive` to `false` if
|
||||
/// you're using this, because it will be overwritten.
|
||||
/// </summary>
|
||||
public string factRequiredForWarp;
|
||||
|
||||
/// <summary>
|
||||
/// Can you use the warp drive to leave this system? If you set `factRequiredToExitViaWarpDrive`
|
||||
/// this will be true.
|
||||
/// </summary>
|
||||
[DefaultValue(true)] public bool canExitViaWarpDrive = true;
|
||||
|
||||
/// <summary>
|
||||
/// The FactID that must be revealed for you to warp back to the main solar system from here. Don't set `canWarpHome`
|
||||
/// to `false` if you're using this, because it will be overwritten.
|
||||
/// </summary>
|
||||
public string factRequiredToExitViaWarpDrive;
|
||||
|
||||
/// <summary>
|
||||
/// Do you want a clean slate for this star system? Or will it be a modified version of the original.
|
||||
/// </summary>
|
||||
@ -50,12 +70,6 @@ namespace NewHorizons.External.Configs
|
||||
/// </summary>
|
||||
[DefaultValue(true)] public bool enableTimeLoop = true;
|
||||
|
||||
/// <summary>
|
||||
/// The FactID that must be revealed before it can be warped to. Don't set `canEnterViaWarpDrive` to `false` if
|
||||
/// you're using this, because it will be overwritten.
|
||||
/// </summary>
|
||||
public string factRequiredForWarp;
|
||||
|
||||
/// <summary>
|
||||
/// The duration of the time loop in minutes. This is the time the sun explodes. End Times plays 85 seconds before this time, and your memories get sent back about 40 seconds after this time.
|
||||
/// </summary>
|
||||
@ -87,11 +101,14 @@ namespace NewHorizons.External.Configs
|
||||
[Obsolete("travelAudioFilePath is deprecated, please use travelAudio instead")]
|
||||
public string travelAudioFilePath;
|
||||
|
||||
/// <summary>
|
||||
/// The audio that will play when travelling in space. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list.
|
||||
/// </summary>
|
||||
[Obsolete("travelAudio is deprecated, please use travelAudio instead")]
|
||||
public string travelAudio;
|
||||
|
||||
/// <summary>
|
||||
/// Replace music that plays globally
|
||||
/// </summary>
|
||||
public GlobalMusicModule GlobalMusic;
|
||||
|
||||
/// <summary>
|
||||
/// Configure warping to this system with the vessel
|
||||
/// </summary>
|
||||
@ -122,6 +139,11 @@ namespace NewHorizons.External.Configs
|
||||
/// </summary>
|
||||
public string[] initialReveal;
|
||||
|
||||
/// <summary>
|
||||
/// The planet to focus on when entering the ship log for the first time in a loop. If not set this will be the planet at navtigation position (1, 0)
|
||||
/// </summary>
|
||||
public string shipLogStartingPlanetID;
|
||||
|
||||
/// <summary>
|
||||
/// List colors of curiosity entries
|
||||
/// </summary>
|
||||
@ -192,6 +214,45 @@ namespace NewHorizons.External.Configs
|
||||
public string backPath;
|
||||
}
|
||||
|
||||
[JsonObject]
|
||||
public class GlobalMusicModule
|
||||
{
|
||||
/// <summary>
|
||||
/// The audio that will play when travelling in space. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list.
|
||||
/// </summary>
|
||||
public string travelAudio;
|
||||
|
||||
/// <summary>
|
||||
/// The audio that will play right before the loop ends. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list.
|
||||
/// </summary>
|
||||
public string endTimesAudio;
|
||||
|
||||
/// <summary>
|
||||
/// The audio that will play right before the loop ends while inside the dreamworld. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list.
|
||||
/// </summary>
|
||||
public string endTimesDreamAudio;
|
||||
|
||||
/// <summary>
|
||||
/// The audio that will play when travelling through a bramble dimension. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list.
|
||||
/// </summary>
|
||||
public string brambleDimensionAudio;
|
||||
|
||||
/// <summary>
|
||||
/// The audio that will play when you leave the ash twin project after taking out the advanced warp core. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list.
|
||||
/// </summary>
|
||||
public string finalEndTimesIntroAudio;
|
||||
|
||||
/// <summary>
|
||||
/// The audio that will loop after the final end times intro. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list.
|
||||
/// </summary>
|
||||
public string finalEndTimesLoopAudio;
|
||||
|
||||
/// <summary>
|
||||
/// The audio that will loop after the final end times intro while inside a bramble dimension. Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list.
|
||||
/// </summary>
|
||||
public string finalEndTimesBrambleDimensionAudio;
|
||||
}
|
||||
|
||||
[JsonObject]
|
||||
public class VesselModule
|
||||
{
|
||||
@ -283,7 +344,6 @@ namespace NewHorizons.External.Configs
|
||||
// If current one is null take the other
|
||||
factRequiredForWarp = string.IsNullOrEmpty(factRequiredForWarp) ? otherConfig.factRequiredForWarp : factRequiredForWarp;
|
||||
Skybox = Skybox == null ? otherConfig.Skybox : Skybox;
|
||||
travelAudio = string.IsNullOrEmpty(travelAudio) ? otherConfig.travelAudio : travelAudio;
|
||||
|
||||
// False by default so if one is true go true
|
||||
mapRestricted = mapRestricted || otherConfig.mapRestricted;
|
||||
@ -302,6 +362,21 @@ namespace NewHorizons.External.Configs
|
||||
Vessel ??= otherConfig.Vessel;
|
||||
}
|
||||
|
||||
if (GlobalMusic != null && otherConfig.GlobalMusic != null)
|
||||
{
|
||||
GlobalMusic.travelAudio = string.IsNullOrEmpty(GlobalMusic.travelAudio) ? otherConfig.GlobalMusic.travelAudio : GlobalMusic.travelAudio;
|
||||
GlobalMusic.endTimesAudio = string.IsNullOrEmpty(GlobalMusic.endTimesAudio) ? otherConfig.GlobalMusic.endTimesAudio : GlobalMusic.endTimesAudio;
|
||||
GlobalMusic.endTimesDreamAudio = string.IsNullOrEmpty(GlobalMusic.endTimesDreamAudio) ? otherConfig.GlobalMusic.endTimesDreamAudio : GlobalMusic.endTimesDreamAudio;
|
||||
GlobalMusic.brambleDimensionAudio = string.IsNullOrEmpty(GlobalMusic.brambleDimensionAudio) ? otherConfig.GlobalMusic.brambleDimensionAudio : GlobalMusic.brambleDimensionAudio;
|
||||
GlobalMusic.finalEndTimesIntroAudio = string.IsNullOrEmpty(GlobalMusic.finalEndTimesIntroAudio) ? otherConfig.GlobalMusic.finalEndTimesIntroAudio : GlobalMusic.finalEndTimesIntroAudio;
|
||||
GlobalMusic.finalEndTimesLoopAudio = string.IsNullOrEmpty(GlobalMusic.finalEndTimesLoopAudio) ? otherConfig.GlobalMusic.finalEndTimesLoopAudio : GlobalMusic.finalEndTimesLoopAudio;
|
||||
GlobalMusic.finalEndTimesBrambleDimensionAudio = string.IsNullOrEmpty(GlobalMusic.finalEndTimesBrambleDimensionAudio) ? otherConfig.GlobalMusic.finalEndTimesBrambleDimensionAudio : GlobalMusic.finalEndTimesBrambleDimensionAudio;
|
||||
}
|
||||
else
|
||||
{
|
||||
GlobalMusic ??= otherConfig.GlobalMusic;
|
||||
}
|
||||
|
||||
entryPositions = Concatenate(entryPositions, otherConfig.entryPositions);
|
||||
curiosities = Concatenate(curiosities, otherConfig.curiosities);
|
||||
initialReveal = Concatenate(initialReveal, otherConfig.initialReveal);
|
||||
@ -314,11 +389,16 @@ namespace NewHorizons.External.Configs
|
||||
|
||||
public void Migrate()
|
||||
{
|
||||
// Backwards compatability
|
||||
// Backwards compatibility
|
||||
// Should be the only place that obsolete things are referenced
|
||||
#pragma warning disable 612, 618
|
||||
if (!string.IsNullOrEmpty(travelAudioClip)) travelAudio = travelAudioClip;
|
||||
if (!string.IsNullOrEmpty(travelAudioFilePath)) travelAudio = travelAudioFilePath;
|
||||
if (!string.IsNullOrEmpty(travelAudio))
|
||||
{
|
||||
if (GlobalMusic == null) GlobalMusic = new GlobalMusicModule();
|
||||
if (string.IsNullOrEmpty(GlobalMusic.travelAudio)) GlobalMusic.travelAudio = travelAudio;
|
||||
}
|
||||
if (coords != null || vesselPosition != null || vesselRotation != null || warpExitPosition != null || warpExitRotation != null)
|
||||
{
|
||||
if (Vessel == null)
|
||||
@ -353,6 +433,10 @@ namespace NewHorizons.External.Configs
|
||||
Vessel.warpExit.attachToVessel = true;
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrEmpty(factRequiredToExitViaWarpDrive))
|
||||
{
|
||||
canExitViaWarpDrive = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
8
NewHorizons/External/Modules/BaseModule.cs
vendored
8
NewHorizons/External/Modules/BaseModule.cs
vendored
@ -36,11 +36,6 @@ namespace NewHorizons.External.Modules
|
||||
/// </summary>
|
||||
public float groundSize;
|
||||
|
||||
/// <summary>
|
||||
/// If the body should have a marker on the map screen.
|
||||
/// </summary>
|
||||
public bool hasMapMarker;
|
||||
|
||||
/// <summary>
|
||||
/// Can this planet survive entering a star?
|
||||
/// </summary>
|
||||
@ -108,6 +103,9 @@ namespace NewHorizons.External.Modules
|
||||
[Obsolete("AmbientLight is deprecated, please use AmbientLightModule instead")]
|
||||
public float ambientLight;
|
||||
|
||||
[Obsolete("HasMapMarker is deprecated, please use MapMarkerModule instead")]
|
||||
public bool hasMapMarker;
|
||||
|
||||
[Obsolete("HasReferenceFrame is deprecated, please use ReferenceModule instead")]
|
||||
[DefaultValue(true)] public bool hasReferenceFrame = true;
|
||||
|
||||
|
||||
26
NewHorizons/External/Modules/GeneralPropInfo.cs
vendored
26
NewHorizons/External/Modules/GeneralPropInfo.cs
vendored
@ -5,27 +5,31 @@ using Newtonsoft.Json;
|
||||
namespace NewHorizons.External.Modules
|
||||
{
|
||||
[JsonObject]
|
||||
public abstract class GeneralPointPropInfo
|
||||
public abstract class BasePropInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The relative path from the planet to the parent of this object. Optional (will default to the root sector).
|
||||
/// </summary>
|
||||
public string parentPath;
|
||||
|
||||
/// <summary>
|
||||
/// An optional rename of this object
|
||||
/// </summary>
|
||||
public string rename;
|
||||
}
|
||||
|
||||
[JsonObject]
|
||||
public abstract class GeneralPointPropInfo : BasePropInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Position of the object
|
||||
/// </summary>
|
||||
public MVector3 position;
|
||||
|
||||
/// <summary>
|
||||
/// The relative path from the planet to the parent of this object. Optional (will default to the root sector).
|
||||
/// </summary>
|
||||
public string parentPath;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the positional and rotational coordinates are relative to parent instead of the root planet object.
|
||||
/// </summary>
|
||||
public bool isRelativeToParent;
|
||||
|
||||
/// <summary>
|
||||
/// An optional rename of this object
|
||||
/// </summary>
|
||||
public string rename;
|
||||
}
|
||||
|
||||
[JsonObject]
|
||||
|
||||
25
NewHorizons/External/Modules/MapMarkerModule.cs
vendored
Normal file
25
NewHorizons/External/Modules/MapMarkerModule.cs
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
using System.ComponentModel;
|
||||
using NewHorizons.External.SerializableData;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace NewHorizons.External.Modules
|
||||
{
|
||||
[JsonObject]
|
||||
public class MapMarkerModule
|
||||
{
|
||||
/// <summary>
|
||||
/// If the body should have a marker on the map screen.
|
||||
/// </summary>
|
||||
public bool enabled;
|
||||
|
||||
/// <summary>
|
||||
/// Lowest distance away from the body that the marker can be shown. This is automatically set to 0 for all bodies except focal points where it is 5,000.
|
||||
/// </summary>
|
||||
public float minDisplayDistanceOverride = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Highest distance away from the body that the marker can be shown. For planets and focal points the automatic value is 50,000. Moons and planets in focal points are 5,000. Stars are 1E+10 (10,000,000,000).
|
||||
/// </summary>
|
||||
public float maxDisplayDistanceOverride = -1;
|
||||
}
|
||||
}
|
||||
15
NewHorizons/External/Modules/Props/DetailInfo.cs
vendored
15
NewHorizons/External/Modules/Props/DetailInfo.cs
vendored
@ -1,3 +1,4 @@
|
||||
using NewHorizons.External.Modules.Props.Item;
|
||||
using NewHorizons.External.SerializableData;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
@ -53,7 +54,9 @@ namespace NewHorizons.External.Modules.Props
|
||||
public string quantumGroupID;
|
||||
|
||||
/// <summary>
|
||||
/// Should this detail stay loaded even if you're outside the sector (good for very large props)
|
||||
/// Should this detail stay loaded (visible and collideable) even if you're outside the sector (good for very large props)?
|
||||
/// Also makes this detail visible on the map.
|
||||
/// Most logic/behavior scripts will still only work inside the sector, as most of those scripts break if a sector is not provided.
|
||||
/// </summary>
|
||||
public bool keepLoaded;
|
||||
|
||||
@ -101,6 +104,16 @@ namespace NewHorizons.External.Modules.Props
|
||||
/// </summary>
|
||||
[DefaultValue(true)] public bool blinkWhenActiveChanged = true;
|
||||
|
||||
/// <summary>
|
||||
/// Should this detail be treated as an interactible item
|
||||
/// </summary>
|
||||
public ItemInfo item;
|
||||
|
||||
/// <summary>
|
||||
/// Should this detail be treated as a socket for an interactible item
|
||||
/// </summary>
|
||||
public ItemSocketInfo itemSocket;
|
||||
|
||||
[Obsolete("alignToNormal is deprecated. Use alignRadial instead")] public bool alignToNormal;
|
||||
}
|
||||
|
||||
|
||||
32
NewHorizons/External/Modules/Props/Dialogue/AttentionPointInfo.cs
vendored
Normal file
32
NewHorizons/External/Modules/Props/Dialogue/AttentionPointInfo.cs
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
using NewHorizons.External.SerializableData;
|
||||
using Newtonsoft.Json;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace NewHorizons.External.Modules.Props.Dialogue
|
||||
{
|
||||
[JsonObject]
|
||||
public class AttentionPointInfo : GeneralPointPropInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// An additional offset to apply to apply when the camera looks at this attention point.
|
||||
/// </summary>
|
||||
public MVector3 offset;
|
||||
}
|
||||
|
||||
[JsonObject]
|
||||
public class SwappedAttentionPointInfo : AttentionPointInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the dialogue node to activate this attention point for. If null or blank, activates for every node.
|
||||
/// </summary>
|
||||
public string dialogueNode;
|
||||
/// <summary>
|
||||
/// The index of the page in the current dialogue node to activate this attention point for, if the node has multiple pages.
|
||||
/// </summary>
|
||||
public int dialoguePage;
|
||||
/// <summary>
|
||||
/// The easing factor which determines how 'snappy' the camera is when looking at the attention point.
|
||||
/// </summary>
|
||||
[DefaultValue(1)] public float lookEasing = 1f;
|
||||
}
|
||||
}
|
||||
@ -31,6 +31,11 @@ namespace NewHorizons.External.Modules.Props.Dialogue
|
||||
/// </summary>
|
||||
public string pathToAnimController;
|
||||
|
||||
/// <summary>
|
||||
/// If this dialogue is adding to existing character dialogue, put a path to the game object with the dialogue on it here
|
||||
/// </summary>
|
||||
public string pathToExistingDialogue;
|
||||
|
||||
/// <summary>
|
||||
/// Radius of the spherical collision volume where you get the "talk to" prompt when looking at. If you use a
|
||||
/// remoteTrigger, you can set this to 0 to make the dialogue only trigger remotely.
|
||||
@ -42,6 +47,16 @@ namespace NewHorizons.External.Modules.Props.Dialogue
|
||||
/// </summary>
|
||||
[DefaultValue(2f)] public float range = 2f;
|
||||
|
||||
/// <summary>
|
||||
/// The point that the camera looks at when dialogue advances.
|
||||
/// </summary>
|
||||
public AttentionPointInfo attentionPoint;
|
||||
|
||||
/// <summary>
|
||||
/// Additional points that the camera looks at when dialogue advances through specific dialogue nodes and pages.
|
||||
/// </summary>
|
||||
public SwappedAttentionPointInfo[] swappedAttentionPoints;
|
||||
|
||||
/// <summary>
|
||||
/// Allows you to trigger dialogue from a distance when you walk into an area.
|
||||
/// </summary>
|
||||
|
||||
@ -21,6 +21,28 @@ namespace NewHorizons.External.Modules.Props.EchoesOfTheEye
|
||||
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum SlideReelType
|
||||
{
|
||||
[EnumMember(Value = @"sixSlides")] SixSlides = 6,
|
||||
|
||||
[EnumMember(Value = @"sevenSlides")] SevenSlides = 7,
|
||||
|
||||
[EnumMember(Value = @"eightSlides")] EightSlides = 8,
|
||||
|
||||
[EnumMember(Value = @"whole")] Whole = 9,
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum SlideReelCondition
|
||||
{
|
||||
[EnumMember(Value = @"antique")] Antique = 0,
|
||||
|
||||
[EnumMember(Value = @"pristine")] Pristine = 1,
|
||||
|
||||
[EnumMember(Value = @"rusted")] Rusted = 2,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The ship log facts revealed after finishing this slide reel.
|
||||
/// </summary>
|
||||
@ -43,6 +65,16 @@ namespace NewHorizons.External.Modules.Props.EchoesOfTheEye
|
||||
/// The type of object this is.
|
||||
/// </summary>
|
||||
[DefaultValue("slideReel")] public SlideShowType type = SlideShowType.SlideReel;
|
||||
|
||||
/// <summary>
|
||||
/// Exclusive to the slide reel type. Model/mesh of the reel. Each model has a different number of slides on it. Whole has 7 slides but a full ring like 8.
|
||||
/// </summary>
|
||||
[DefaultValue("sevenSlides")] public SlideReelType reelModel = SlideReelType.SevenSlides;
|
||||
|
||||
/// <summary>
|
||||
/// Exclusive to the slide reel type. Condition/material of the reel. Antique is the Stranger, Pristine is the Dreamworld, Rusted is a burned reel.
|
||||
/// </summary>
|
||||
[DefaultValue("antique")] public SlideReelCondition reelCondition = SlideReelCondition.Antique;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using NewHorizons.External.SerializableData;
|
||||
using Newtonsoft.Json;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace NewHorizons.External.Modules.Props.EchoesOfTheEye
|
||||
{
|
||||
@ -7,80 +8,88 @@ namespace NewHorizons.External.Modules.Props.EchoesOfTheEye
|
||||
public class SlideInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Ambient light colour when viewing this slide.
|
||||
/// The path to the image file for this slide.
|
||||
/// </summary>
|
||||
public MColor ambientLightColor;
|
||||
|
||||
public string imagePath;
|
||||
|
||||
// SlideAmbientLightModule
|
||||
|
||||
/// <summary>
|
||||
/// Ambient light intensity when viewing this slide.
|
||||
/// Set this to add ambient light module. Base game default is 1.
|
||||
/// </summary>
|
||||
public float ambientLightIntensity;
|
||||
|
||||
/// <summary>
|
||||
/// Ambient light range when viewing this slide.
|
||||
/// </summary>
|
||||
public float ambientLightRange;
|
||||
[DefaultValue(20f)] public float ambientLightRange = 20f;
|
||||
|
||||
/// <summary>
|
||||
/// Ambient light colour when viewing this slide. Defaults to white.
|
||||
/// </summary>
|
||||
public MColor ambientLightColor;
|
||||
|
||||
/// <summary>
|
||||
/// Spotlight intensity modifier when viewing this slide.
|
||||
/// </summary>
|
||||
[DefaultValue(0f)] public float spotIntensityMod = 0f;
|
||||
|
||||
// SlideBackdropAudioModule
|
||||
|
||||
/// <summary>
|
||||
/// The name of the AudioClip that will continuously play while watching these slides
|
||||
/// The name of the AudioClip that will continuously loop while watching these slides.
|
||||
/// Set this to include backdrop audio module. Base game default is Reel_1_Backdrop_A.
|
||||
/// </summary>
|
||||
public string backdropAudio;
|
||||
|
||||
/// <summary>
|
||||
/// The time to fade into the backdrop audio
|
||||
/// The time to fade into the backdrop audio.
|
||||
/// </summary>
|
||||
public float backdropFadeTime;
|
||||
[DefaultValue(2f)] public float backdropFadeTime = 2f;
|
||||
|
||||
// SlideBeatAudioModule
|
||||
|
||||
/// <summary>
|
||||
/// The name of the AudioClip for a one-shot sound when opening the slide.
|
||||
/// Set this to include beat audio module. Base game default is Reel_1_Beat_A.
|
||||
/// </summary>
|
||||
public string beatAudio;
|
||||
|
||||
/// <summary>
|
||||
/// The time delay until the one-shot audio
|
||||
/// The time delay until the one-shot audio.
|
||||
/// </summary>
|
||||
public float beatDelay;
|
||||
|
||||
[DefaultValue(0f)] public float beatDelay = 0f;
|
||||
|
||||
// SlideBlackFrameModule
|
||||
|
||||
/// <summary>
|
||||
/// Before viewing this slide, there will be a black frame for this many seconds.
|
||||
/// Set this to include black frame module. Base game default is 0.
|
||||
/// </summary>
|
||||
public float blackFrameDuration;
|
||||
|
||||
/// <summary>
|
||||
/// The path to the image file for this slide.
|
||||
/// </summary>
|
||||
public string imagePath;
|
||||
|
||||
|
||||
// SlidePlayTimeModule
|
||||
|
||||
/// <summary>
|
||||
/// Play-time duration for auto-projector slides.
|
||||
/// Set this to include play time module. Base game default is 0.
|
||||
/// </summary>
|
||||
public float playTimeDuration;
|
||||
|
||||
|
||||
// SlideShipLogEntryModule
|
||||
|
||||
/// <summary>
|
||||
/// Ship log fact revealed when viewing this slide
|
||||
/// Ship log fact revealed when viewing this slide.
|
||||
/// Set this to include ship log entry module. Base game default is "".
|
||||
/// </summary>
|
||||
public string reveal;
|
||||
|
||||
/// <summary>
|
||||
/// Spotlight intensity modifier when viewing this slide.
|
||||
/// </summary>
|
||||
public float spotIntensityMod;
|
||||
}
|
||||
// SlideRotationModule
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// Exclusive to slide reels. Whether this slide should rotate the reel item while inside a projector.
|
||||
/// </summary>
|
||||
[DefaultValue(true)] public bool rotate = true;
|
||||
}
|
||||
}
|
||||
99
NewHorizons/External/Modules/Props/Item/ItemInfo.cs
vendored
Normal file
99
NewHorizons/External/Modules/Props/Item/ItemInfo.cs
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
using NewHorizons.External.SerializableData;
|
||||
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.Props.Item
|
||||
{
|
||||
[JsonObject]
|
||||
public class ItemInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the item to be displayed in the UI. Defaults to the name of the detail object.
|
||||
/// </summary>
|
||||
public string name;
|
||||
/// <summary>
|
||||
/// The type of the item, which determines its orientation when held and what sockets it fits into. This can be a custom string, or a vanilla ItemType (Scroll, WarpCore, SharedStone, ConversationStone, Lantern, SlideReel, DreamLantern, or VisionTorch). Defaults to the item name.
|
||||
/// </summary>
|
||||
public string itemType;
|
||||
/// <summary>
|
||||
/// The furthest distance where the player can interact with this item. Defaults to two meters, same as most vanilla items. Set this to zero to disable all interaction by default.
|
||||
/// </summary>
|
||||
[DefaultValue(2f)] public float interactRange = 2f;
|
||||
/// <summary>
|
||||
/// The radius that the added sphere collider will use for collision and hover detection.
|
||||
/// If there's already a collider on the detail, you can make this 0.
|
||||
/// </summary>
|
||||
[DefaultValue(0.5f)] public float colliderRadius = 0.5f;
|
||||
/// <summary>
|
||||
/// Whether the item can be dropped. Defaults to true.
|
||||
/// </summary>
|
||||
[DefaultValue(true)] public bool droppable = true;
|
||||
/// <summary>
|
||||
/// A relative offset to apply to the item's position when dropping it on the ground.
|
||||
/// </summary>
|
||||
public MVector3 dropOffset;
|
||||
/// <summary>
|
||||
/// The direction the item will be oriented when dropping it on the ground. Defaults to up (0, 1, 0).
|
||||
/// </summary>
|
||||
public MVector3 dropNormal;
|
||||
/// <summary>
|
||||
/// A relative offset to apply to the item's position when holding it. The initial position varies for vanilla item types.
|
||||
/// </summary>
|
||||
public MVector3 holdOffset;
|
||||
/// <summary>
|
||||
/// A relative offset to apply to the item's rotation when holding it.
|
||||
/// </summary>
|
||||
public MVector3 holdRotation;
|
||||
/// <summary>
|
||||
/// A relative offset to apply to the item's position when placing it into a socket.
|
||||
/// </summary>
|
||||
public MVector3 socketOffset;
|
||||
/// <summary>
|
||||
/// A relative offset to apply to the item's rotation when placing it into a socket.
|
||||
/// </summary>
|
||||
public MVector3 socketRotation;
|
||||
/// <summary>
|
||||
/// The audio to play when this item is picked up. Only applies to custom/non-vanilla item types.
|
||||
/// Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list.
|
||||
/// </summary>
|
||||
public string pickupAudio;
|
||||
/// <summary>
|
||||
/// The audio to play when this item is dropped. Only applies to custom/non-vanilla item types.
|
||||
/// Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list.
|
||||
/// </summary>
|
||||
public string dropAudio;
|
||||
/// <summary>
|
||||
/// The audio to play when this item is inserted into a socket. Only applies to custom/non-vanilla item types.
|
||||
/// Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list.
|
||||
/// </summary>
|
||||
public string socketAudio;
|
||||
/// <summary>
|
||||
/// The audio to play when this item is removed from a socket. Only applies to custom/non-vanilla item types.
|
||||
/// Can be a path to a .wav/.ogg/.mp3 file, or taken from the AudioClip list.
|
||||
/// </summary>
|
||||
public string unsocketAudio;
|
||||
/// <summary>
|
||||
/// A dialogue condition to set when picking up this item.
|
||||
/// </summary>
|
||||
public string pickupCondition;
|
||||
/// <summary>
|
||||
/// Whether the pickup condition should be cleared when dropping the item. Defaults to true.
|
||||
/// </summary>
|
||||
[DefaultValue(true)] public bool clearPickupConditionOnDrop = true;
|
||||
/// <summary>
|
||||
/// A ship log fact to reveal when picking up this item.
|
||||
/// </summary>
|
||||
public string pickupFact;
|
||||
/// <summary>
|
||||
/// A relative path from the planet to a socket that this item will be automatically inserted into.
|
||||
/// </summary>
|
||||
public string pathToInitialSocket;
|
||||
}
|
||||
}
|
||||
58
NewHorizons/External/Modules/Props/Item/ItemSocketInfo.cs
vendored
Normal file
58
NewHorizons/External/Modules/Props/Item/ItemSocketInfo.cs
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
using NewHorizons.External.SerializableData;
|
||||
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.Props.Item
|
||||
{
|
||||
[JsonObject]
|
||||
public class ItemSocketInfo : GeneralPropInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The relative path to a child game object of this detail that will act as the socket point for the item. Will be used instead of this socket's positioning info if set.
|
||||
/// </summary>
|
||||
public string socketPath;
|
||||
/// <summary>
|
||||
/// The type of item allowed in this socket. This can be a custom string, or a vanilla ItemType (Scroll, WarpCode, SharedStone, ConversationStone, Lantern, SlideReel, DreamLantern, or VisionTorch).
|
||||
/// </summary>
|
||||
public string itemType;
|
||||
/// <summary>
|
||||
/// The furthest distance where the player can interact with this item socket. Defaults to two meters, same as most vanilla item sockets. Set this to zero to disable all interaction by default.
|
||||
/// </summary>
|
||||
[DefaultValue(2f)] public float interactRange = 2f;
|
||||
/// <summary>
|
||||
/// Whether to use "Give Item" / "Take Item" prompts instead of "Insert Item" / "Remove Item".
|
||||
/// </summary>
|
||||
public bool useGiveTakePrompts;
|
||||
/// <summary>
|
||||
/// A dialogue condition to set when inserting an item into this socket.
|
||||
/// </summary>
|
||||
public string insertCondition;
|
||||
/// <summary>
|
||||
/// Whether the insert condition should be cleared when removing the socketed item. Defaults to true.
|
||||
/// </summary>
|
||||
[DefaultValue(true)] public bool clearInsertConditionOnRemoval = true;
|
||||
/// <summary>
|
||||
/// A ship log fact to reveal when inserting an item into this socket.
|
||||
/// </summary>
|
||||
public string insertFact;
|
||||
/// <summary>
|
||||
/// A dialogue condition to set when removing an item from this socket, or when the socket is empty.
|
||||
/// </summary>
|
||||
public string removalCondition;
|
||||
/// <summary>
|
||||
/// Whether the removal condition should be cleared when inserting a socketed item. Defaults to true.
|
||||
/// </summary>
|
||||
[DefaultValue(true)] public bool clearRemovalConditionOnInsert = true;
|
||||
/// <summary>
|
||||
/// A ship log fact to reveal when removing an item from this socket, or when the socket is empty.
|
||||
/// </summary>
|
||||
public string removalFact;
|
||||
}
|
||||
}
|
||||
@ -3,7 +3,7 @@ using Newtonsoft.Json;
|
||||
namespace NewHorizons.External.Modules.Props.Remote
|
||||
{
|
||||
[JsonObject]
|
||||
public class StoneInfo : GeneralPropInfo
|
||||
public class ProjectionStoneInfo : GeneralPropInfo
|
||||
{
|
||||
|
||||
}
|
||||
@ -23,11 +23,11 @@ namespace NewHorizons.External.Modules.Props.Remote
|
||||
/// <summary>
|
||||
/// Camera platform that the stones can project to and from
|
||||
/// </summary>
|
||||
public PlatformInfo platform;
|
||||
public RemotePlatformInfo platform;
|
||||
|
||||
/// <summary>
|
||||
/// Projection stones
|
||||
/// </summary>
|
||||
public StoneInfo[] stones;
|
||||
public ProjectionStoneInfo[] stones;
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ using System.ComponentModel;
|
||||
namespace NewHorizons.External.Modules.Props.Remote
|
||||
{
|
||||
[JsonObject]
|
||||
public class PlatformInfo : GeneralPropInfo
|
||||
public class RemotePlatformInfo : GeneralPropInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// A ship log fact to reveal when the platform is connected to.
|
||||
@ -66,5 +66,10 @@ namespace NewHorizons.External.Modules.Props
|
||||
/// Should this detail stay loaded even if you're outside the sector (good for very large props)
|
||||
/// </summary>
|
||||
public bool keepLoaded;
|
||||
|
||||
/// <summary>
|
||||
/// The relative path from the planet to the parent of this object. Optional (will default to the root sector). This parent should be at the position where you'd like to scatter (which would usually be zero).
|
||||
/// </summary>
|
||||
public string parentPath;
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,6 +154,7 @@ namespace NewHorizons.External.Modules.VariableSize
|
||||
[EnumMember(Value = @"default")] Default,
|
||||
[EnumMember(Value = @"whiteDwarf")] WhiteDwarf,
|
||||
[EnumMember(Value = @"neutronStar")] NeutronStar,
|
||||
[Obsolete] Pulsar,
|
||||
[EnumMember(Value = @"blackHole")] BlackHole,
|
||||
[EnumMember(Value = @"custom")] Custom
|
||||
}
|
||||
|
||||
18
NewHorizons/External/NewHorizonBody.cs
vendored
18
NewHorizons/External/NewHorizonBody.cs
vendored
@ -33,6 +33,24 @@ namespace NewHorizons.External
|
||||
|
||||
public GameObject Object;
|
||||
|
||||
public bool RequiresDLC()
|
||||
{
|
||||
try
|
||||
{
|
||||
var detailPaths = Config?.Props?.details?.Select(x => x.path) ?? Array.Empty<string>();
|
||||
return Config?.Cloak != null
|
||||
|| Config?.Props?.rafts != null
|
||||
|| Config?.Props?.slideShows != null
|
||||
|| detailPaths.Any(x => x.StartsWith("RingWorld_Body") || x.StartsWith("DreamWorld_Body"));
|
||||
}
|
||||
catch
|
||||
{
|
||||
NHLogger.LogWarning($"Failed to check if {Mod.ModHelper.Manifest.Name} requires the DLC");
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#region Cache
|
||||
public void LoadCache()
|
||||
{
|
||||
|
||||
17
NewHorizons/External/NewHorizonsData.cs
vendored
17
NewHorizons/External/NewHorizonsData.cs
vendored
@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NewHorizons.Builder.Props.Audio;
|
||||
using NewHorizons.Utility.OWML;
|
||||
|
||||
namespace NewHorizons.External
|
||||
@ -124,7 +126,6 @@ namespace NewHorizons.External
|
||||
if (!KnowsFrequency(frequency))
|
||||
{
|
||||
_activeProfile.KnownFrequencies.Add(frequency);
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
@ -134,13 +135,12 @@ namespace NewHorizons.External
|
||||
if (KnowsFrequency(frequency))
|
||||
{
|
||||
_activeProfile.KnownFrequencies.Remove(frequency);
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
public static bool KnowsMultipleFrequencies()
|
||||
{
|
||||
return _activeProfile != null && _activeProfile.KnownFrequencies.Count > 0;
|
||||
return _activeProfile?.KnownFrequencies != null && _activeProfile.KnownFrequencies.Count(SignalBuilder.IsFrequencyInUse) > 1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -159,7 +159,6 @@ namespace NewHorizons.External
|
||||
if (!KnowsSignal(signal))
|
||||
{
|
||||
_activeProfile.KnownSignals.Add(signal);
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
@ -170,7 +169,6 @@ namespace NewHorizons.External
|
||||
public static void AddNewlyRevealedFactID(string id)
|
||||
{
|
||||
_activeProfile?.NewlyRevealedFactIDs.Add(id);
|
||||
Save();
|
||||
}
|
||||
|
||||
public static List<string> GetNewlyRevealedFactIDs()
|
||||
@ -181,7 +179,6 @@ namespace NewHorizons.External
|
||||
public static void ClearNewlyRevealedFactIDs()
|
||||
{
|
||||
_activeProfile?.NewlyRevealedFactIDs.Clear();
|
||||
Save();
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -190,8 +187,11 @@ namespace NewHorizons.External
|
||||
|
||||
public static void ReadOneTimePopup(string id)
|
||||
{
|
||||
_activeProfile?.PopupsRead.Add(id);
|
||||
Save();
|
||||
// else it re-adds it each time
|
||||
if (_activeProfile != null && !_activeProfile.PopupsRead.Contains(id))
|
||||
{
|
||||
_activeProfile.PopupsRead.Add(id);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool HasReadOneTimePopup(string id)
|
||||
@ -208,7 +208,6 @@ namespace NewHorizons.External
|
||||
{
|
||||
if (name == CharacterDialogueTree.RECORDING_NAME || name == CharacterDialogueTree.SIGN_NAME) return;
|
||||
_activeProfile?.CharactersTalkedTo.SafeAdd(name);
|
||||
Save();
|
||||
}
|
||||
|
||||
public static bool HasTalkedToFiveCharacters()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user