mirror of
https://github.com/Outer-Wilds-New-Horizons/new-horizons.git
synced 2025-12-11 20:15:44 +01:00
Merge branch 'dev' into improve-credit-volumes
This commit is contained in:
commit
1b1bf47266
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@ -1,2 +1,2 @@
|
||||
patreon: ownh
|
||||
patreon: xen42
|
||||
custom: ["https://paypal.me/xen42"]
|
||||
|
||||
10
.github/workflows/build.yaml
vendored
10
.github/workflows/build.yaml
vendored
@ -29,10 +29,10 @@ jobs:
|
||||
schemas_changed: ${{ steps.changed_files.outputs.files_changed }}
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
uses: actions/setup-dotnet@v4
|
||||
|
||||
# Disable Strong Name Verification to let us pull a switch-a-roo
|
||||
- name: Disable strong name validation
|
||||
@ -51,19 +51,19 @@ jobs:
|
||||
run: rm .\NewHorizons\bin\${{ inputs.build_type }}\NewHorizons.xml
|
||||
|
||||
- name: Upload Mod Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
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@v3
|
||||
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@v17
|
||||
uses: tj-actions/verify-changed-files@v20
|
||||
id: changed_files
|
||||
with:
|
||||
files: NewHorizons/Schemas/**
|
||||
|
||||
8
.github/workflows/docs_build.yml
vendored
8
.github/workflows/docs_build.yml
vendored
@ -29,10 +29,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Download Schemas
|
||||
if: ${{ inputs.schemas_artifact != 'null' }}
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ${{ inputs.schemas_artifact }}
|
||||
path: NewHorizons/Schemas
|
||||
@ -41,7 +41,7 @@ jobs:
|
||||
cp docs/package.json .
|
||||
cp docs/pnpm-lock.yaml .
|
||||
- name: Build Site
|
||||
uses: withastro/action@v1
|
||||
uses: withastro/action@v2
|
||||
with:
|
||||
path: ./docs
|
||||
package-manager: pnpm@latest
|
||||
@ -55,4 +55,4 @@ jobs:
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v1
|
||||
uses: actions/deploy-pages@v4
|
||||
|
||||
4
.github/workflows/release_build.yml
vendored
4
.github/workflows/release_build.yml
vendored
@ -28,7 +28,7 @@ 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
|
||||
@ -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
|
||||
|
||||
4
.github/workflows/update_schemas.yml
vendored
4
.github/workflows/update_schemas.yml
vendored
@ -18,12 +18,12 @@ 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/
|
||||
|
||||
@ -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",
|
||||
|
||||
BIN
NewHorizons/Assets/bundles/newhorizons_private
Normal file
BIN
NewHorizons/Assets/bundles/newhorizons_private
Normal file
Binary file not shown.
@ -1,12 +1,12 @@
|
||||
ManifestFileVersion: 0
|
||||
CRC: 3537427957
|
||||
CRC: 2772415118
|
||||
Hashes:
|
||||
AssetFileHash:
|
||||
serializedVersion: 2
|
||||
Hash: c4d8f41970054074bb375ac5cbe82855
|
||||
Hash: a1c50cfbd83ea025ed5ff8a920ceb135
|
||||
TypeTreeHash:
|
||||
serializedVersion: 2
|
||||
Hash: de71b9c55befb829b1640ea21774b932
|
||||
Hash: 8a96558a3d9470a68866aeaaa30830cf
|
||||
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,12 +79,16 @@ 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
|
||||
Script: {fileID: 11500000, guid: 13ab18a571ddf1b4f8dc92e3fa31b22e, type: 3}
|
||||
- Class: 114
|
||||
Script: {fileID: 11500000, guid: 3d5c87c5a00ca19449219c7c54f41ee7, type: 3}
|
||||
- Class: 114
|
||||
Script: {fileID: 11500000, guid: db82bd26294f5b848aaa8cb1ad0dc252, type: 3}
|
||||
- Class: 114
|
||||
Script: {fileID: 11500000, guid: 840ab63696e59254eb425242136805dd, type: 3}
|
||||
- Class: 114
|
||||
@ -97,10 +105,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 +181,25 @@ 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/QuantumLightning/Prefab_EYE_QuantumLightningObject.prefab
|
||||
- Assets/SlideReels/Prefab_IP_Reel_7.prefab
|
||||
Dependencies: []
|
||||
Binary file not shown.
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 |
@ -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,6 +1,69 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/xen-42/outer-wilds-new-horizons/main/NewHorizons/Schemas/translation_schema.json",
|
||||
"$schema": "https://raw.githubusercontent.com/outer-wilds-new-horizons/new-horizons/main/NewHorizons/Schemas/translation_schema.json",
|
||||
"DialogueDictionary": {
|
||||
"NEW_HORIZONS_WARP_DRIVE_DIALOGUE_1": "Sua nave agora está equipada com uma unidade de translocação!",
|
||||
"NEW_HORIZONS_WARP_DRIVE_DIALOGUE_2": "Você pode usar a nova aba \"Modo Interestelar\" no diário de bordo de sua nave para ajustar o piloto automático rumo a outro sistema solar.",
|
||||
"NEW_HORIZONS_WARP_DRIVE_DIALOGUE_3": "Então é só acionar o piloto automático e relaxar!"
|
||||
},
|
||||
"UIDictionary": {
|
||||
"Vessel": "Hospedeiro"
|
||||
"INTERSTELLAR_MODE": "Modo Interestelar",
|
||||
"FREQ_STATUE": "Estátua Nomai",
|
||||
"FREQ_WARP_CORE": "Fluxo Anti-Gravitacional",
|
||||
"FREQ_UNKNOWN": "???",
|
||||
"ENGAGE_WARP_PROMPT": "Acionar translocação a {0}",
|
||||
"WARP_LOCKED": "PILOTO AUTOMÁTICO AJUSTADO PARA:\n{0}",
|
||||
"LOCK_AUTOPILOT_WARP": "Ajustar Piloto Automático para sistema solar",
|
||||
"RICH_PRESENCE_EXPLORING": "Explorando {0}.",
|
||||
"RICH_PRESENCE_WARPING": "Translocando para {0}.",
|
||||
"OUTDATED_VERSION_WARNING": "AVISO\n\nA mod New Horizons funciona apenas na versão {0} ou superior. Você está usando a versão {1}.\n\nAtualize Outer Wilds ou desinstale NH.",
|
||||
"JSON_FAILED_TO_LOAD": "Arquivo(s) inválido(s): {0}",
|
||||
"DEBUG_RAYCAST": "Raycast",
|
||||
"DEBUG_PLACE": "Posicionar objeto",
|
||||
"DEBUG_PLACE_TEXT": "Colocar texto Nomai",
|
||||
"DEBUG_UNDO": "Desfazer",
|
||||
"DEBUG_REDO": "Refazer",
|
||||
"Vessel": "Hospedeiro",
|
||||
"DLC_REQUIRED": "AVISO\n\nVocê tem addons (ex. {0}) instalados que requerem a DLC, mas a mesma não está habilitada.\n\nSuas mods podem não funcionar como esperado."
|
||||
},
|
||||
"OtherDictionary": {
|
||||
"NOMAI_SHUTTLE_COMPUTER": "A <![CDATA[<color=orange>exploradora</color>]]> está repousando por hora em: <![CDATA[<color=lightblue>{0}</color>]]>."
|
||||
},
|
||||
"AchievementTranslations": {
|
||||
"NH_EATEN_OUTSIDE_BRAMBLE": {
|
||||
"Name": "Fenda de Contenção",
|
||||
"Description": "Seja devorado fora dos limites de Abrolho Sombrio"
|
||||
},
|
||||
"NH_MULTIPLE_SYSTEM": {
|
||||
"Name": "Viajante",
|
||||
"Description": "Visite 5 sistemas solares seguidos."
|
||||
},
|
||||
"NH_NEW_FREQ": {
|
||||
"Name": "Frequências Anômalas",
|
||||
"Description": "Descubra uma nova frequência."
|
||||
},
|
||||
"NH_PROBE_LOST": {
|
||||
"Name": "Conexão Perdida",
|
||||
"Description": "Perca seu pequeno batedor."
|
||||
},
|
||||
"NH_WARP_DRIVE": {
|
||||
"Name": "História Mal Contada",
|
||||
"Description": "Use a unidade de translocação da nave."
|
||||
},
|
||||
"NH_VESSEL_WARP": {
|
||||
"Name": "História Acertada",
|
||||
"Description": "Transloque para um sistema estelar usando o Hospedeiro."
|
||||
},
|
||||
"NH_RAFTING": {
|
||||
"Name": "A Jangada e os Furiosos",
|
||||
"Description": "Dê um rolé de jangada."
|
||||
},
|
||||
"NH_SUCKED_INTO_LAVA_BY_TORNADO": {
|
||||
"Name": "Dieclone",
|
||||
"Description": "Seja sugado na lava por um tornado."
|
||||
},
|
||||
"NH_TALK_TO_FIVE_CHARACTERS": {
|
||||
"Name": "Social",
|
||||
"Description": "Converse com 5 personagens."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ namespace NewHorizons.Builder.Atmosphere
|
||||
sfv._priority = 0;
|
||||
sfv._density = 1.2f;
|
||||
sfv._fluidType = FluidVolume.Type.AIR;
|
||||
sfv._allowShipAutoroll = true;
|
||||
sfv._allowShipAutoroll = config.Atmosphere.allowShipAutoroll;
|
||||
sfv._disableOnStart = false;
|
||||
|
||||
if (config.Atmosphere.hasShockLayer)
|
||||
|
||||
@ -66,7 +66,7 @@ namespace NewHorizons.Builder.Atmosphere
|
||||
}
|
||||
_qmBottomMeshGroup.variants = variants.ToArray();
|
||||
}
|
||||
if (_transparentCloud == null) _transparentCloud = Main.NHAssetBundle.LoadAsset<Material>("Assets/Resources/TransparentCloud.mat");
|
||||
if (_transparentCloud == null) _transparentCloud = AssetBundleUtilities.NHAssetBundle.LoadAsset<Material>("Assets/Resources/TransparentCloud.mat");
|
||||
}
|
||||
|
||||
public static void Make(GameObject planetGO, Sector sector, AtmosphereModule atmo, bool cloaked, IModBehaviour mod)
|
||||
@ -147,7 +147,7 @@ namespace NewHorizons.Builder.Atmosphere
|
||||
fluidCLFV._priority = 1;
|
||||
fluidCLFV._density = 1.2f;
|
||||
fluidCLFV._fluidType = atmo.clouds.fluidType.ConvertToOW(FluidVolume.Type.CLOUD);
|
||||
fluidCLFV._allowShipAutoroll = true;
|
||||
fluidCLFV._allowShipAutoroll = atmo.allowShipAutoroll;
|
||||
fluidCLFV._disableOnStart = false;
|
||||
|
||||
// Fix the rotations once the rest is done
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using NewHorizons.External;
|
||||
using NewHorizons.External.Configs;
|
||||
using NewHorizons.External.Modules;
|
||||
using NewHorizons.External.Modules.Props;
|
||||
using NewHorizons.External.SerializableData;
|
||||
using NewHorizons.Handlers;
|
||||
using NewHorizons.Utility.OWML;
|
||||
@ -36,8 +37,7 @@ namespace NewHorizons.Builder.Body
|
||||
|
||||
config.Base = new BaseModule()
|
||||
{
|
||||
hasMapMarker = false,
|
||||
surfaceGravity = 1,
|
||||
surfaceGravity = belt.gravity,
|
||||
surfaceSize = size,
|
||||
gravityFallOff = GravityFallOff.InverseSquared
|
||||
};
|
||||
@ -50,7 +50,8 @@ namespace NewHorizons.Builder.Body
|
||||
trueAnomaly = 360f * (i + Random.Range(-0.2f, 0.2f)) / (float)count,
|
||||
primaryBody = bodyName,
|
||||
semiMajorAxis = Random.Range(belt.innerRadius, belt.outerRadius),
|
||||
showOrbitLine = false
|
||||
showOrbitLine = false,
|
||||
isTidallyLocked = true
|
||||
};
|
||||
|
||||
config.ReferenceFrame = new ReferenceFrameModule()
|
||||
@ -58,8 +59,35 @@ namespace NewHorizons.Builder.Body
|
||||
enabled = false
|
||||
};
|
||||
|
||||
config.ProcGen = belt.procGen;
|
||||
if (config.ProcGen == null)
|
||||
config.MapMarker = new MapMarkerModule()
|
||||
{
|
||||
enabled = false
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(belt.assetBundle) || !string.IsNullOrEmpty(belt.path))
|
||||
{
|
||||
config.Props = new PropModule()
|
||||
{
|
||||
details = new DetailInfo[1]
|
||||
{
|
||||
new DetailInfo()
|
||||
{
|
||||
assetBundle = belt.assetBundle,
|
||||
path = belt.path,
|
||||
scale = size,
|
||||
rotation = belt.randomOrientation ? Random.rotation.eulerAngles : Vector3.zero,
|
||||
keepLoaded = true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
else if (belt.procGen != null)
|
||||
{
|
||||
config.ProcGen = belt.procGen;
|
||||
config.ProcGen.scale = size;
|
||||
}
|
||||
else
|
||||
{
|
||||
config.ProcGen = new ProcGenModule()
|
||||
{
|
||||
@ -67,11 +95,6 @@ namespace NewHorizons.Builder.Body
|
||||
color = new MColor(126, 94, 73)
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
// Still update the size
|
||||
config.ProcGen.scale = size;
|
||||
}
|
||||
|
||||
var asteroid = new NewHorizonsBody(config, mod);
|
||||
PlanetCreationHandler.GenerateBody(asteroid);
|
||||
|
||||
@ -5,6 +5,7 @@ using NewHorizons.External;
|
||||
using NewHorizons.External.Modules;
|
||||
using NewHorizons.External.Modules.Props;
|
||||
using NewHorizons.Utility;
|
||||
using NewHorizons.Utility.Files;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using OWML.Common;
|
||||
using System.Collections.Generic;
|
||||
@ -78,7 +79,7 @@ namespace NewHorizons.Builder.Body
|
||||
if (_exitWarps == null) _exitWarps = SearchUtilities.Find("DB_HubDimension_Body/Sector_HubDimension/Interactables_HubDimension/OuterWarp_Hub").InstantiateInactive().Rename("Prefab_Bramble_OuterWarp").DontDestroyOnLoad();
|
||||
if (_repelVolume == null) _repelVolume = SearchUtilities.Find("DB_HubDimension_Body/BrambleRepelVolume").InstantiateInactive().Rename("Prefab_Bramble_RepelVolume").DontDestroyOnLoad();
|
||||
if (_material == null) _material = new Material(GameObject.Find("DB_PioneerDimension_Body/Sector_PioneerDimension").GetComponent<EffectRuleset>()._material).DontDestroyOnLoad();
|
||||
if (_wallCollision == null) _wallCollision = Main.NHPrivateAssetBundle.LoadAsset<GameObject>("BrambleCollision");
|
||||
if (_wallCollision == null) _wallCollision = AssetBundleUtilities.NHPrivateAssetBundle.LoadAsset<GameObject>("BrambleCollision");
|
||||
}
|
||||
|
||||
public static GameObject Make(NewHorizonsBody body, GameObject go, NHAstroObject ao, Sector sector, IModBehaviour mod, OWRigidbody owRigidBody)
|
||||
@ -230,6 +231,7 @@ namespace NewHorizons.Builder.Body
|
||||
if (config.fogTint != null)
|
||||
{
|
||||
var color = config.fogTint.ToColor();
|
||||
// Fog alpha has no impact: Must instead use fogDensity.
|
||||
color.a = 1f;
|
||||
fog.fogTint = color;
|
||||
outerFogWarpVolume._fogColor = color;
|
||||
|
||||
107
NewHorizons/Builder/Body/DreamDimensionBuilder.cs
Normal file
107
NewHorizons/Builder/Body/DreamDimensionBuilder.cs
Normal file
@ -0,0 +1,107 @@
|
||||
using NewHorizons.Components.EOTE;
|
||||
using NewHorizons.Components.Props;
|
||||
using NewHorizons.External;
|
||||
using NewHorizons.External.Configs;
|
||||
using NewHorizons.Utility;
|
||||
using NewHorizons.Utility.OuterWilds;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Builder.Body
|
||||
{
|
||||
public static class DreamDimensionBuilder
|
||||
{
|
||||
private static Material gridMaterial;
|
||||
private static Material waterMaterial;
|
||||
|
||||
private readonly static string[] EXCLUDED_OBJECT_NAMES =
|
||||
{
|
||||
"Prefab_IP_SIM_",
|
||||
"Props_IP_SIM_",
|
||||
"Effects_IP_SIM_",
|
||||
};
|
||||
|
||||
private readonly static string[] EXCLUDED_SHADER_NAMES =
|
||||
{
|
||||
"Fog",
|
||||
"Simulation Bubble",
|
||||
"Foliage",
|
||||
"Flame",
|
||||
};
|
||||
|
||||
public static void Make(GameObject planetGO, Sector sector, NewHorizonsBody body)
|
||||
{
|
||||
var bodyWantsSimMeshes = body.Config.Dream?.generateSimulationMeshes ?? false;
|
||||
var propsWantSimMeshes = body.Config.Props?.dreamArrivalPoints?.Any(p => p.generateSimulationMeshes) ?? false;
|
||||
if (bodyWantsSimMeshes || propsWantSimMeshes)
|
||||
{
|
||||
MakeDreamSimulationMeshes(sector ? sector.gameObject : planetGO);
|
||||
}
|
||||
|
||||
if (body.Config?.Dream?.inDreamWorld ?? false)
|
||||
{
|
||||
var dreamDimension = planetGO.AddComponent<DreamDimension>();
|
||||
Delay.FireInNUpdates(() =>
|
||||
{
|
||||
dreamDimension.Initialize();
|
||||
}, 4);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void MakeDreamSimulationMeshes(GameObject go)
|
||||
{
|
||||
if (gridMaterial == null) gridMaterial = SearchUtilities.FindResourceOfTypeAndName<Material>("Terrain_IP_DreamGrid_mat");
|
||||
if (waterMaterial == null) waterMaterial = SearchUtilities.FindResourceOfTypeAndName<Material>("Terrain_IP_DreamGrid_mat");
|
||||
|
||||
foreach (var mr in go.GetComponentsInChildren<MeshRenderer>(true))
|
||||
{
|
||||
if (mr.GetType() != typeof(MeshRenderer)) continue;
|
||||
var mf = mr.GetComponent<MeshFilter>();
|
||||
if (mf == null) continue;
|
||||
if (!CheckMeshCreationHeuristic(mr.gameObject, mr.sharedMaterials)) continue;
|
||||
var simMesh = new GameObject("SimulationMesh").AddComponent<DreamSimulationMesh>();
|
||||
simMesh.Init(mr.transform, GetMeshMaterial(go, mr.sharedMaterials));
|
||||
}
|
||||
}
|
||||
|
||||
private static Material GetMeshMaterial(GameObject go, Material[] materials)
|
||||
{
|
||||
if (materials.Any(m => m.name.Contains("Ocean_Stencil_mat"))) return waterMaterial;
|
||||
return gridMaterial;
|
||||
}
|
||||
|
||||
private static bool CheckMeshCreationHeuristic(GameObject go, Material[] materials)
|
||||
{
|
||||
if (go.layer == Layer.DreamSimulation) return false;
|
||||
var mr = go.GetComponent<MeshRenderer>();
|
||||
if (EXCLUDED_SHADER_NAMES.Any(name => materials.Any(mat => mat.shader.name.Contains(name)))) return false;
|
||||
if (go.transform.parent)
|
||||
{
|
||||
foreach (Transform c in go.transform.parent)
|
||||
{
|
||||
if (c && c.gameObject.layer == Layer.DreamSimulation) return false;
|
||||
}
|
||||
if (go.transform.parent.parent)
|
||||
{
|
||||
foreach (Transform c in go.transform.parent.parent)
|
||||
{
|
||||
if (c && c.gameObject.layer == Layer.DreamSimulation) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
var t = go.transform;
|
||||
while (t != null)
|
||||
{
|
||||
if (EXCLUDED_OBJECT_NAMES.Any(t.name.Contains)) return false;
|
||||
t = t.parent;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -62,7 +62,7 @@ namespace NewHorizons.Builder.Body
|
||||
cubeSphere.SetActive(false);
|
||||
cubeSphere.transform.SetParent(sector?.transform ?? planetGO.transform, false);
|
||||
|
||||
if (PlanetShader == null) PlanetShader = Main.NHAssetBundle.LoadAsset<Shader>("Assets/Shaders/SphereTextureWrapperTriplanar.shader");
|
||||
if (PlanetShader == null) PlanetShader = AssetBundleUtilities.NHAssetBundle.LoadAsset<Shader>("Assets/Shaders/SphereTextureWrapperTriplanar.shader");
|
||||
|
||||
var stretch = module.stretch != null ? (Vector3)module.stretch : Vector3.one;
|
||||
|
||||
@ -91,14 +91,15 @@ namespace NewHorizons.Builder.Body
|
||||
level2.name += "1";
|
||||
|
||||
LODGroup.RecalculateBounds();
|
||||
|
||||
// do this only for LOD because only the main body uses LOD, while title screen and proxies dont
|
||||
var superGroup = planetGO.GetComponent<ProxyShadowCasterSuperGroup>();
|
||||
if (superGroup != null) level2.gameObject.AddComponent<ProxyShadowCaster>()._superGroup = superGroup;
|
||||
}
|
||||
|
||||
var cubeSphereSC = cubeSphere.AddComponent<SphereCollider>();
|
||||
cubeSphereSC.radius = Mathf.Min(module.minHeight, module.maxHeight) * Mathf.Min(stretch.x, stretch.y, stretch.z);
|
||||
|
||||
var superGroup = planetGO.GetComponent<ProxyShadowCasterSuperGroup>();
|
||||
if (superGroup != null) cubeSphere.AddComponent<ProxyShadowCaster>()._superGroup = superGroup;
|
||||
|
||||
cubeSphere.SetActive(true);
|
||||
|
||||
// Now that we've made the mesh we can delete the heightmap texture
|
||||
|
||||
@ -96,6 +96,12 @@ namespace NewHorizons.Builder.Body
|
||||
// We want to take the largest size I think
|
||||
var realSize = body.Config.Base.surfaceSize;
|
||||
|
||||
if (realSize <= 0)
|
||||
{
|
||||
// #941 handle proxy body edge case when all scales = 0
|
||||
realSize = 1;
|
||||
}
|
||||
|
||||
if (body.Config.HeightMap != null)
|
||||
{
|
||||
HeightMapBuilder.Make(proxy, null, body.Config.HeightMap, body.Mod, 20);
|
||||
|
||||
@ -84,10 +84,10 @@ namespace NewHorizons.Builder.Body
|
||||
var ringMesh = ringMF.mesh;
|
||||
var ringMR = ringGO.AddComponent<MeshRenderer>();
|
||||
|
||||
if (RingShader == null) RingShader = Main.NHAssetBundle.LoadAsset<Shader>("Assets/Shaders/Ring.shader");
|
||||
if (UnlitRingShader == null) UnlitRingShader = Main.NHAssetBundle.LoadAsset<Shader>("Assets/Shaders/UnlitTransparent.shader");
|
||||
if (RingShader1Pixel == null) RingShader1Pixel = Main.NHAssetBundle.LoadAsset<Shader>("Assets/Shaders/Ring1Pixel.shader");
|
||||
if (UnlitRingShader1Pixel == null) UnlitRingShader1Pixel = Main.NHAssetBundle.LoadAsset<Shader>("Assets/Shaders/UnlitRing1Pixel.shader");
|
||||
if (RingShader == null) RingShader = AssetBundleUtilities.NHAssetBundle.LoadAsset<Shader>("Assets/Shaders/Ring.shader");
|
||||
if (UnlitRingShader == null) UnlitRingShader = AssetBundleUtilities.NHAssetBundle.LoadAsset<Shader>("Assets/Shaders/UnlitTransparent.shader");
|
||||
if (RingShader1Pixel == null) RingShader1Pixel = AssetBundleUtilities.NHAssetBundle.LoadAsset<Shader>("Assets/Shaders/Ring1Pixel.shader");
|
||||
if (UnlitRingShader1Pixel == null) UnlitRingShader1Pixel = AssetBundleUtilities.NHAssetBundle.LoadAsset<Shader>("Assets/Shaders/UnlitRing1Pixel.shader");
|
||||
|
||||
var mat = new Material(ring.unlit ? UnlitRingShader : RingShader);
|
||||
if (ringTexture.width == 1)
|
||||
|
||||
@ -1,18 +1,17 @@
|
||||
using NewHorizons.External.Configs;
|
||||
using NewHorizons.Utility;
|
||||
using NewHorizons.External.Modules.VariableSize;
|
||||
using UnityEngine;
|
||||
|
||||
using System.Collections.Generic;
|
||||
using NewHorizons.Components.SizeControllers;
|
||||
using Color = UnityEngine.Color;
|
||||
using NewHorizons.Components.Volumes;
|
||||
using NewHorizons.Builder.Props;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using NewHorizons.Utility.OuterWilds;
|
||||
using NewHorizons.External.SerializableData;
|
||||
using NewHorizons.Builder.Volumes;
|
||||
using NewHorizons.Components.SizeControllers;
|
||||
using NewHorizons.Components.Volumes;
|
||||
using NewHorizons.External.Configs;
|
||||
using NewHorizons.External.Modules.VariableSize;
|
||||
using NewHorizons.External.SerializableData;
|
||||
using NewHorizons.Utility;
|
||||
using NewHorizons.Utility.OuterWilds;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Color = UnityEngine.Color;
|
||||
|
||||
namespace NewHorizons.Builder.Body
|
||||
{
|
||||
@ -88,7 +87,7 @@ namespace NewHorizons.Builder.Body
|
||||
Vector3 localRotation = singularity?.rotation == null ? Vector3.zero : singularity.rotation;
|
||||
|
||||
GameObject newSingularity = MakeSingularity(go, sector, localPosition, localRotation, polarity, horizonRadius, distortRadius,
|
||||
hasHazardVolume, singularity.targetStarSystem, singularity.curve, singularity.hasWarpEffects, singularity.renderQueueOverride, singularity.rename, singularity.parentPath, singularity.isRelativeToParent);
|
||||
hasHazardVolume, singularity.targetStarSystem, singularity.spawnPointID, singularity.curve, singularity.hasWarpEffects, singularity.renderQueueOverride, singularity.rename, singularity.parentPath, singularity.isRelativeToParent);
|
||||
|
||||
var uniqueID = string.IsNullOrEmpty(singularity.uniqueID) ? config.name : singularity.uniqueID;
|
||||
|
||||
@ -161,7 +160,7 @@ namespace NewHorizons.Builder.Body
|
||||
}
|
||||
|
||||
public static GameObject MakeSingularity(GameObject planetGO, Sector sector, Vector3 position, Vector3 rotation, bool polarity, float horizon, float distort,
|
||||
bool hasDestructionVolume, string targetStarSystem = null, TimeValuePair[] curve = null, bool warpEffects = true, int renderQueue = 2985, string rename = null, string parentPath = null, bool isRelativeToParent = false)
|
||||
bool hasDestructionVolume, string targetStarSystem = null, string targetSpawnID = null, TimeValuePair[] curve = null, bool warpEffects = true, int renderQueue = 2985, string rename = null, string parentPath = null, bool isRelativeToParent = false)
|
||||
{
|
||||
// polarity true = black, false = white
|
||||
|
||||
@ -233,6 +232,7 @@ namespace NewHorizons.Builder.Body
|
||||
{
|
||||
var wormholeVolume = destructionVolumeGO.AddComponent<BlackHoleWarpVolume>();
|
||||
wormholeVolume.TargetSolarSystem = targetStarSystem;
|
||||
wormholeVolume.TargetSpawnID = targetSpawnID;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@ -26,6 +26,7 @@ namespace NewHorizons.Builder.Body
|
||||
NHLogger.Log($"Creating stellar remnant for [{star.Config.name}]");
|
||||
|
||||
var sector = SectorBuilder.Make(go, rb, soi);
|
||||
sector._idString = star.Config.name;
|
||||
sector.name = "StellarRemnant";
|
||||
|
||||
sector.gameObject.SetActive(false);
|
||||
|
||||
@ -129,7 +129,7 @@ namespace NewHorizons.Builder.Body
|
||||
fluidVolume._density = module.density;
|
||||
fluidVolume._layer = 5;
|
||||
fluidVolume._priority = 3;
|
||||
fluidVolume._allowShipAutoroll = true;
|
||||
fluidVolume._allowShipAutoroll = module.allowShipAutoroll;
|
||||
fluidVolume._disableOnStart = false;
|
||||
|
||||
var fogGO = Object.Instantiate(_oceanFog, waterGO.transform);
|
||||
|
||||
@ -2,6 +2,7 @@ using UnityEngine;
|
||||
using NewHorizons.Utility;
|
||||
using NewHorizons.External.Modules;
|
||||
using NewHorizons.Utility.Files;
|
||||
using NewHorizons.Components;
|
||||
|
||||
namespace NewHorizons.Builder.General
|
||||
{
|
||||
@ -41,7 +42,7 @@ namespace NewHorizons.Builder.General
|
||||
}
|
||||
else
|
||||
{
|
||||
var baseCubemap = Main.NHPrivateAssetBundle.LoadAsset<Cubemap>("AmbientLight_QM");
|
||||
var baseCubemap = AssetBundleUtilities.NHPrivateAssetBundle.LoadAsset<Cubemap>("AmbientLight_QM");
|
||||
var cubemap = new Cubemap(baseCubemap.width, baseCubemap.format, baseCubemap.mipmapCount != 1);
|
||||
cubemap.name = key;
|
||||
cubemap.wrapMode = baseCubemap.wrapMode;
|
||||
|
||||
@ -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,12 +8,18 @@ namespace NewHorizons.Builder.General
|
||||
{
|
||||
public static class AstroObjectBuilder
|
||||
{
|
||||
public static NHAstroObject Make(GameObject body, AstroObject primaryBody, PlanetConfig config, bool isVanilla)
|
||||
public static GameObject CenterOfUniverse { get; private set; }
|
||||
|
||||
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.invulnerableToSun = config.Base.invulnerableToSun;
|
||||
astroObject.HideDisplayName = !config.MapMarker.enabled;
|
||||
astroObject.invulnerableToSun = !config.Base.hasFluidDetector;
|
||||
|
||||
if (config.Orbit != null) astroObject.SetOrbitalParametersFromConfig(config.Orbit);
|
||||
|
||||
@ -58,6 +64,8 @@ namespace NewHorizons.Builder.General
|
||||
|
||||
if (config.Base.centerOfSolarSystem)
|
||||
{
|
||||
CenterOfUniverse = body;
|
||||
|
||||
NHLogger.Log($"Setting center of universe to {config.name}");
|
||||
|
||||
Delay.RunWhen(
|
||||
|
||||
@ -90,7 +90,7 @@ namespace NewHorizons.Builder.General
|
||||
OWRB.RegisterAttachedForceDetector(forceDetector);
|
||||
|
||||
// For falling into sun
|
||||
if (!config.Base.invulnerableToSun && config.Star == null && config.FocalPoint == null)
|
||||
if (config.Base.hasFluidDetector && config.Star == null && config.FocalPoint == null)
|
||||
{
|
||||
detectorGO.layer = Layer.AdvancedDetector;
|
||||
|
||||
|
||||
@ -15,10 +15,11 @@ namespace NewHorizons.Builder.General
|
||||
var gravityRadius = GM / 0.1f;
|
||||
if (exponent == 2f) gravityRadius = Mathf.Sqrt(gravityRadius);
|
||||
|
||||
if (config.FocalPoint != null) gravityRadius = 0; // keep it at the lowest possible
|
||||
else if (config.Base.soiOverride != 0f) gravityRadius = config.Base.soiOverride;
|
||||
else if (config.Star != null) gravityRadius = Mathf.Min(gravityRadius, 15 * config.Base.surfaceSize);
|
||||
// To let you actually orbit things the way you would expect we cap this at 4x the diameter if its not a star (this is what giants deep has)
|
||||
if (config.Star == null) gravityRadius = Mathf.Min(gravityRadius, 4 * config.Base.surfaceSize);
|
||||
else gravityRadius = Mathf.Min(gravityRadius, 15 * config.Base.surfaceSize);
|
||||
if (config.Base.soiOverride != 0f) gravityRadius = config.Base.soiOverride;
|
||||
else gravityRadius = Mathf.Min(gravityRadius, 4 * config.Base.surfaceSize);
|
||||
|
||||
var gravityGO = new GameObject("GravityWell");
|
||||
gravityGO.transform.parent = planetGO.transform;
|
||||
@ -26,9 +27,9 @@ namespace NewHorizons.Builder.General
|
||||
gravityGO.layer = Layer.BasicEffectVolume;
|
||||
gravityGO.SetActive(false);
|
||||
|
||||
var SC = gravityGO.AddComponent<SphereCollider>();
|
||||
SC.isTrigger = true;
|
||||
SC.radius = gravityRadius;
|
||||
var sphereCollider = gravityGO.AddComponent<SphereCollider>();
|
||||
sphereCollider.isTrigger = true;
|
||||
sphereCollider.radius = gravityRadius;
|
||||
|
||||
var owCollider = gravityGO.AddComponent<OWCollider>();
|
||||
owCollider.SetLODActivationMask(DynamicOccupant.Player);
|
||||
@ -47,8 +48,9 @@ namespace NewHorizons.Builder.General
|
||||
var alignmentRadius = config.Atmosphere?.clouds?.outerCloudRadius ?? 1.5f * config.Base.surfaceSize;
|
||||
if (config.Base.surfaceGravity == 0) alignmentRadius = 0;
|
||||
|
||||
gravityVolume._alignmentRadius = alignmentRadius;
|
||||
gravityVolume._upperSurfaceRadius = config.Base.surfaceSize;
|
||||
gravityVolume._alignmentRadius = config.Base.gravityAlignmentRadiusOverride ?? alignmentRadius;
|
||||
// Nobody write any FocalPoint overriding here, those work as intended gravitationally so deal with it!
|
||||
gravityVolume._upperSurfaceRadius = config.Base.surfaceSize;
|
||||
gravityVolume._lowerSurfaceRadius = 0;
|
||||
gravityVolume._layer = 3;
|
||||
gravityVolume._priority = config.Base.gravityVolumePriority;
|
||||
@ -58,6 +60,21 @@ namespace NewHorizons.Builder.General
|
||||
gravityVolume._isPlanetGravityVolume = true;
|
||||
gravityVolume._cutoffRadius = 0f;
|
||||
|
||||
// If it's a focal point dont add collision stuff
|
||||
// This is overkill
|
||||
if (config.FocalPoint != null)
|
||||
{
|
||||
owCollider.enabled = false;
|
||||
owTriggerVolume.enabled = false;
|
||||
sphereCollider.radius = 0;
|
||||
sphereCollider.enabled = false;
|
||||
sphereCollider.isTrigger = false;
|
||||
// This should ensure that even if the player enters the volume, it counts them as being inside the zero-gee cave equivalent
|
||||
gravityVolume._cutoffRadius = gravityVolume._upperSurfaceRadius;
|
||||
gravityVolume._lowerSurfaceRadius = gravityVolume._upperSurfaceRadius;
|
||||
gravityVolume._cutoffAcceleration = 0;
|
||||
}
|
||||
|
||||
gravityGO.SetActive(true);
|
||||
|
||||
ao._gravityVolume = gravityVolume;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -4,6 +4,7 @@ using NewHorizons.Utility;
|
||||
using NewHorizons.Utility.OuterWilds;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
@ -13,52 +14,105 @@ namespace NewHorizons.Builder.General
|
||||
public static class SpawnPointBuilder
|
||||
{
|
||||
private static bool suitUpQueued = false;
|
||||
|
||||
// Ship
|
||||
public static SpawnModule.ShipSpawnPoint ShipSpawnInfo { get; private set; }
|
||||
public static SpawnPoint ShipSpawn { get; private set; }
|
||||
public static Vector3 ShipSpawnOffset { get; private set; }
|
||||
|
||||
// Player
|
||||
public static SpawnModule.PlayerSpawnPoint PlayerSpawnInfo { get; private set; }
|
||||
public static SpawnPoint PlayerSpawn { get; private set; }
|
||||
|
||||
public static void OverridePlayerSpawn(SpawnPoint newSpawn)
|
||||
{
|
||||
PlayerSpawn = newSpawn;
|
||||
PlayerSpawnInfo = null;
|
||||
}
|
||||
|
||||
public static SpawnPoint Make(GameObject planetGO, SpawnModule module, OWRigidbody owRigidBody)
|
||||
{
|
||||
SpawnPoint playerSpawn = null;
|
||||
|
||||
// Make the spawn point even if it won't be used this loop
|
||||
if (module.playerSpawn != null)
|
||||
if (module.playerSpawnPoints != null)
|
||||
{
|
||||
GameObject spawnGO = GeneralPropBuilder.MakeNew("PlayerSpawnPoint", planetGO, null, module.playerSpawn);
|
||||
spawnGO.layer = Layer.PlayerSafetyCollider;
|
||||
foreach (var point in module.playerSpawnPoints)
|
||||
{
|
||||
GameObject spawnGO = GeneralPropBuilder.MakeNew("PlayerSpawnPoint", planetGO, null, point);
|
||||
spawnGO.layer = Layer.PlayerSafetyCollider;
|
||||
|
||||
playerSpawn = spawnGO.AddComponent<SpawnPoint>();
|
||||
playerSpawn._attachedBody = owRigidBody;
|
||||
playerSpawn._spawnLocation = SpawnLocation.None;
|
||||
// #601 we need to actually set the right trigger volumes here
|
||||
playerSpawn._triggerVolumes = new OWTriggerVolume[0];
|
||||
playerSpawn = spawnGO.AddComponent<SpawnPoint>();
|
||||
playerSpawn._attachedBody = owRigidBody;
|
||||
playerSpawn._spawnLocation = SpawnLocation.None;
|
||||
// #601 we need to actually set the right trigger volumes here
|
||||
playerSpawn._triggerVolumes = new OWTriggerVolume[0];
|
||||
|
||||
// This was a stupid hack to stop players getting stuck in the ground and now we have to keep it forever
|
||||
spawnGO.transform.position += spawnGO.transform.TransformDirection(module.playerSpawn.offset ?? Vector3.up * 4f);
|
||||
// This was a stupid hack to stop players getting stuck in the ground and now we have to keep it forever
|
||||
spawnGO.transform.position += spawnGO.transform.TransformDirection(point.offset ?? Vector3.up * 4f);
|
||||
|
||||
var flagUseTHSpawn = false;
|
||||
if (Main.Instance.CurrentStarSystem == "SolarSystem")
|
||||
{
|
||||
// When in the base solar system, treat the TH spawn point as being isDefault
|
||||
// If the priority of any new spawn point is less than that, ignore it
|
||||
// Do take them if they're equal tho
|
||||
var minPriority = new SpawnModule.PlayerSpawnPoint() { isDefault = true }.GetPriority();
|
||||
if (point.GetPriority() < minPriority)
|
||||
{
|
||||
flagUseTHSpawn = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!flagUseTHSpawn && (PlayerSpawn == null || point.GetPriority() > PlayerSpawnInfo.GetPriority()))
|
||||
{
|
||||
PlayerSpawn = playerSpawn;
|
||||
PlayerSpawnInfo = point;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (module.shipSpawn != null)
|
||||
if (module.shipSpawnPoints != null)
|
||||
{
|
||||
var spawnGO = GeneralPropBuilder.MakeNew("ShipSpawnPoint", planetGO, null, module.shipSpawn);
|
||||
spawnGO.SetActive(false);
|
||||
spawnGO.layer = Layer.PlayerSafetyCollider;
|
||||
foreach (var point in module.shipSpawnPoints)
|
||||
{
|
||||
var spawnGO = GeneralPropBuilder.MakeNew("ShipSpawnPoint", planetGO, null, point);
|
||||
spawnGO.SetActive(false);
|
||||
spawnGO.layer = Layer.PlayerSafetyCollider;
|
||||
|
||||
ShipSpawn = spawnGO.AddComponent<SpawnPoint>();
|
||||
ShipSpawn._isShipSpawn = true;
|
||||
ShipSpawn._attachedBody = owRigidBody;
|
||||
ShipSpawn._spawnLocation = SpawnLocation.None;
|
||||
var shipSpawn = spawnGO.AddComponent<SpawnPoint>();
|
||||
shipSpawn._isShipSpawn = true;
|
||||
shipSpawn._attachedBody = owRigidBody;
|
||||
shipSpawn._spawnLocation = SpawnLocation.None;
|
||||
|
||||
// #601 we need to actually set the right trigger volumes here
|
||||
ShipSpawn._triggerVolumes = new OWTriggerVolume[0];
|
||||
// #601 we need to actually set the right trigger volumes here
|
||||
shipSpawn._triggerVolumes = new OWTriggerVolume[0];
|
||||
|
||||
ShipSpawnOffset = module.shipSpawn.offset ?? (module.shipSpawn.alignRadial.GetValueOrDefault() ? Vector3.up * 4 : Vector3.zero);
|
||||
var shipSpawnOffset = point.offset ?? (point.alignRadial.GetValueOrDefault() ? Vector3.up * 4 : Vector3.zero);
|
||||
|
||||
spawnGO.SetActive(true);
|
||||
if (ShipSpawn == null || point.GetPriority() > ShipSpawnInfo.GetPriority())
|
||||
{
|
||||
ShipSpawn = shipSpawn;
|
||||
ShipSpawnOffset = shipSpawnOffset;
|
||||
ShipSpawnInfo = point;
|
||||
}
|
||||
|
||||
spawnGO.SetActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
if ((Main.Instance.IsWarpingFromVessel || (!Main.Instance.IsWarpingFromShip && (module.playerSpawn?.startWithSuit ?? false))) && !suitUpQueued)
|
||||
// Make sure to queue this up if any spawn point building is happening
|
||||
if (!suitUpQueued)
|
||||
{
|
||||
suitUpQueued = true;
|
||||
Delay.RunWhen(() => Main.IsSystemReady, SuitUp);
|
||||
Delay.RunWhen(() => Main.IsSystemReady, () =>
|
||||
{
|
||||
suitUpQueued = false;
|
||||
if (Main.Instance.IsWarpingFromVessel || (!Main.Instance.IsWarpingFromShip && (PlayerSpawnInfo?.startWithSuit ?? false)))
|
||||
{
|
||||
SuitUp();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
NHLogger.Log($"Made spawnpoint on [{planetGO.name}]");
|
||||
@ -68,7 +122,6 @@ namespace NewHorizons.Builder.General
|
||||
|
||||
public static void SuitUp()
|
||||
{
|
||||
suitUpQueued = false;
|
||||
if (!Locator.GetPlayerController()._isWearingSuit)
|
||||
{
|
||||
Locator.GetPlayerSuit().SuitUp(false, true, true);
|
||||
@ -87,8 +140,22 @@ namespace NewHorizons.Builder.General
|
||||
handler.Method.Invoke(handler.Target, new object[] { command });
|
||||
}
|
||||
spv._interactVolume._listInteractions.First(x => x.promptText == UITextType.SuitUpPrompt).interactionEnabled = true;
|
||||
|
||||
// Fix Disappearing Signalscope UI #934 after warping to new system wearing suit
|
||||
Delay.StartCoroutine(SignalScopeZoomCoroutine());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerator SignalScopeZoomCoroutine()
|
||||
{
|
||||
while (!Locator.GetToolModeSwapper().GetSignalScope().InZoomMode())
|
||||
{
|
||||
yield return new WaitForEndOfFrame();
|
||||
}
|
||||
yield return null;
|
||||
Locator.GetToolModeSwapper().GetSignalScope().ExitSignalscopeZoom();
|
||||
Locator.GetToolModeSwapper().GetSignalScope().EnterSignalscopeZoom();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,12 +57,13 @@ namespace NewHorizons.Builder.Orbital
|
||||
config.Base.surfaceGravity = gravitationalMass * GravityVolume.GRAVITATIONAL_CONSTANT;
|
||||
config.Base.gravityFallOff = primary.Config.Base.gravityFallOff;
|
||||
|
||||
// Other stuff to make the barycenter not interact with anything in any way
|
||||
config.Base.soiOverride = 0;
|
||||
|
||||
var separation = primary.Config.Orbit.semiMajorAxis + secondary.Config.Orbit.semiMajorAxis;
|
||||
config.ReferenceFrame.bracketRadius = separation;
|
||||
config.ReferenceFrame.targetColliderRadius = separation;
|
||||
var separationRadius = (separation / 2);
|
||||
config.Base.soiOverride = separationRadius * 1.5f;
|
||||
config.ReferenceFrame.bracketRadius = separationRadius;
|
||||
config.ReferenceFrame.targetColliderRadius = separationRadius;
|
||||
|
||||
config.Base.showMinimap = false;
|
||||
}
|
||||
|
||||
private static float GetGravitationalMass(PlanetConfig config)
|
||||
|
||||
@ -10,16 +10,25 @@ namespace NewHorizons.Builder.Orbital
|
||||
private static Material _dottedLineMaterial;
|
||||
private static Material _lineMaterial;
|
||||
|
||||
public static OrbitLine Make(GameObject planetGO, NHAstroObject astroObject, bool isMoon, PlanetConfig config)
|
||||
public static GameObject Make(GameObject planetGO, bool isMoon, PlanetConfig config)
|
||||
{
|
||||
var orbitGO = new GameObject("Orbit");
|
||||
orbitGO.transform.parent = planetGO.transform;
|
||||
orbitGO.transform.localPosition = Vector3.zero;
|
||||
|
||||
Delay.FireOnNextUpdate(() => PostMake(orbitGO, planetGO, isMoon, config));
|
||||
return orbitGO;
|
||||
}
|
||||
|
||||
private static void PostMake(GameObject orbitGO, GameObject planetGO, bool isMoon, PlanetConfig config)
|
||||
{
|
||||
if (_dottedLineMaterial == null) _dottedLineMaterial = SearchUtilities.FindResourceOfTypeAndName<Material>("Effects_SPA_OrbitLine_Dotted_mat");
|
||||
if (_lineMaterial == null) _lineMaterial = SearchUtilities.FindResourceOfTypeAndName<Material>("Effects_SPA_OrbitLine_mat");
|
||||
|
||||
if (_dottedLineMaterial == null || _lineMaterial == null) return null;
|
||||
// Might've been otherwise destroyed when updating
|
||||
if (orbitGO == null) return;
|
||||
|
||||
var orbitGO = new GameObject("Orbit");
|
||||
orbitGO.transform.parent = planetGO.transform;
|
||||
orbitGO.transform.localPosition = Vector3.zero;
|
||||
var astroObject = planetGO.GetComponent<NHAstroObject>();
|
||||
|
||||
var lineRenderer = orbitGO.AddComponent<LineRenderer>();
|
||||
|
||||
@ -47,7 +56,6 @@ namespace NewHorizons.Builder.Orbital
|
||||
else
|
||||
{
|
||||
orbitLine = orbitGO.AddComponent<NHOrbitLine>();
|
||||
|
||||
(orbitLine as NHOrbitLine).SetFromParameters(astroObject);
|
||||
}
|
||||
|
||||
@ -94,8 +102,6 @@ namespace NewHorizons.Builder.Orbital
|
||||
orbitGO.SetActive(false);
|
||||
};
|
||||
}
|
||||
|
||||
return orbitLine;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,6 +121,7 @@ namespace NewHorizons.Builder.Props.Audio
|
||||
|
||||
public static string GetCustomFrequencyName(SignalFrequency frequencyName)
|
||||
{
|
||||
if (_customFrequencyNames == null) return string.Empty;
|
||||
_customFrequencyNames.TryGetValue(frequencyName, out string name);
|
||||
return name;
|
||||
}
|
||||
@ -140,6 +141,7 @@ namespace NewHorizons.Builder.Props.Audio
|
||||
|
||||
public static string GetCustomSignalName(SignalName signalName)
|
||||
{
|
||||
if (_customSignalNames == null) return string.Empty;
|
||||
_customSignalNames.TryGetValue(signalName, out string name);
|
||||
return name;
|
||||
}
|
||||
@ -169,7 +171,15 @@ namespace NewHorizons.Builder.Props.Audio
|
||||
audioSignal._onlyAudibleToScope = info.onlyAudibleToScope;
|
||||
audioSignal._identificationDistance = info.identificationRadius;
|
||||
audioSignal._canBePickedUpByScope = true;
|
||||
audioSignal._outerFogWarpVolume = planetGO.GetComponentInChildren<OuterFogWarpVolume>(); // shouldn't break non-bramble signals
|
||||
// The outsider adds outer fog warp volumes to Bramble which break any signals NH places there
|
||||
if (Main.Instance.ModHelper.Interaction.ModExists("SBtT.TheOutsider") && planetGO?.GetComponent<AstroObject>()?._name == AstroObject.Name.DarkBramble)
|
||||
{
|
||||
audioSignal._outerFogWarpVolume = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
audioSignal._outerFogWarpVolume = planetGO.GetComponentInChildren<OuterFogWarpVolume>(); // shouldn't break non-bramble signals
|
||||
}
|
||||
|
||||
// If it can be heard regularly then we play it immediately
|
||||
owAudioSource.playOnAwake = !info.onlyAudibleToScope;
|
||||
|
||||
@ -113,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;
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using NewHorizons.Builder.General;
|
||||
using NewHorizons.Components;
|
||||
using NewHorizons.Components.Orbital;
|
||||
using NewHorizons.Components.Props;
|
||||
using NewHorizons.External.Modules.Props;
|
||||
using NewHorizons.Handlers;
|
||||
@ -18,10 +19,23 @@ namespace NewHorizons.Builder.Props
|
||||
{
|
||||
public static class DetailBuilder
|
||||
{
|
||||
private static readonly Dictionary<DetailInfo, GameObject> _detailInfoToCorrespondingSpawnedGameObject = new();
|
||||
private static readonly Dictionary<(Sector, string), (GameObject prefab, bool isItem)> _fixedPrefabCache = new();
|
||||
private static GameObject _emptyPrefab;
|
||||
|
||||
private static readonly Dictionary<DetailInfo, GameObject> _detailInfoToGameObject = new();
|
||||
|
||||
public static GameObject GetGameObjectFromDetailInfo(DetailInfo info)
|
||||
{
|
||||
if (_detailInfoToGameObject.ContainsKey(info))
|
||||
{
|
||||
return _detailInfoToGameObject[info];
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static DetailBuilder()
|
||||
{
|
||||
SceneManager.sceneUnloaded += SceneManager_sceneUnloaded;
|
||||
@ -47,19 +61,7 @@ namespace NewHorizons.Builder.Props
|
||||
UnityEngine.Object.Destroy(prefab.prefab);
|
||||
}
|
||||
_fixedPrefabCache.Clear();
|
||||
_detailInfoToCorrespondingSpawnedGameObject.Clear();
|
||||
}
|
||||
|
||||
public static GameObject GetSpawnedGameObjectByDetailInfo(DetailInfo detail)
|
||||
{
|
||||
if (!_detailInfoToCorrespondingSpawnedGameObject.ContainsKey(detail))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _detailInfoToCorrespondingSpawnedGameObject[detail];
|
||||
}
|
||||
_detailInfoToGameObject.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -67,6 +69,8 @@ namespace NewHorizons.Builder.Props
|
||||
/// </summary>
|
||||
public static GameObject Make(GameObject planetGO, Sector sector, IModBehaviour mod, DetailInfo info)
|
||||
{
|
||||
if (sector == null) info.keepLoaded = true;
|
||||
|
||||
if (info.assetBundle != null)
|
||||
{
|
||||
// Shouldn't happen
|
||||
@ -98,6 +102,8 @@ namespace NewHorizons.Builder.Props
|
||||
{
|
||||
if (prefab == null) return null;
|
||||
|
||||
if (sector == null) detail.keepLoaded = true;
|
||||
|
||||
GameObject prop;
|
||||
bool isItem;
|
||||
bool invalidComponentFound = false;
|
||||
@ -156,7 +162,13 @@ namespace NewHorizons.Builder.Props
|
||||
// If they're adding dialogue we have to manually register the xml text
|
||||
if (isFromAssetBundle && component is CharacterDialogueTree dialogue)
|
||||
{
|
||||
DialogueBuilder.AddTranslation(dialogue._xmlCharacterDialogueAsset.text, null);
|
||||
DialogueBuilder.HandleUnityCreatedDialogue(dialogue);
|
||||
}
|
||||
|
||||
// copied details need their lanterns fixed
|
||||
if (!isFromAssetBundle && component is DreamLanternController lantern)
|
||||
{
|
||||
lantern.gameObject.AddComponent<DreamLanternControllerFixer>();
|
||||
}
|
||||
|
||||
FixComponent(component, go, detail.ignoreSun);
|
||||
@ -260,6 +272,15 @@ namespace NewHorizons.Builder.Props
|
||||
}
|
||||
|
||||
if (!detail.keepLoaded) GroupsBuilder.Make(prop, sector);
|
||||
|
||||
// For DLC related props
|
||||
// Make sure to do this before its set active
|
||||
if (!string.IsNullOrEmpty(detail?.path) &&
|
||||
(detail.path.ToLowerInvariant().StartsWith("ringworld") || detail.path.ToLowerInvariant().StartsWith("dreamworld")))
|
||||
{
|
||||
prop.AddComponent<DestroyOnDLC>()._destroyOnDLCNotOwned = true;
|
||||
}
|
||||
|
||||
prop.SetActive(true);
|
||||
|
||||
if (detail.hasPhysics)
|
||||
@ -280,7 +301,7 @@ namespace NewHorizons.Builder.Props
|
||||
ConditionalObjectActivation.SetUp(prop, detail.deactivationCondition, detail.blinkWhenActiveChanged, false);
|
||||
}
|
||||
|
||||
_detailInfoToCorrespondingSpawnedGameObject[detail] = prop;
|
||||
_detailInfoToGameObject[detail] = prop;
|
||||
|
||||
return prop;
|
||||
}
|
||||
@ -320,6 +341,16 @@ namespace NewHorizons.Builder.Props
|
||||
{
|
||||
remoteCameraPlatform._visualSector = sector;
|
||||
}
|
||||
|
||||
else if(component is SingleLightSensor singleLightSensor && !existingSectors.Contains(singleLightSensor._sector))
|
||||
{
|
||||
if (singleLightSensor._sector != null)
|
||||
{
|
||||
singleLightSensor._sector.OnSectorOccupantsUpdated -= singleLightSensor.OnSectorOccupantsUpdated;
|
||||
}
|
||||
singleLightSensor._sector = sector;
|
||||
singleLightSensor._sector.OnSectorOccupantsUpdated += singleLightSensor.OnSectorOccupantsUpdated;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -364,6 +395,12 @@ namespace NewHorizons.Builder.Props
|
||||
// Fix anglerfish speed on orbiting planets
|
||||
else if (component is AnglerfishController angler)
|
||||
{
|
||||
if (planetGO?.GetComponent<NHAstroObject>() is NHAstroObject nhao && !nhao.invulnerableToSun)
|
||||
{
|
||||
// Has a fluid detector, will go gorp (#830)
|
||||
NHLogger.LogWarning("Having an anglerfish on a planet that has a fluid detector can lead to things breaking!");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
angler._chaseSpeed += OWPhysics.CalculateOrbitVelocity(planetGO.GetAttachedOWRigidbody(), planetGO.GetComponent<AstroObject>().GetPrimaryBody().GetAttachedOWRigidbody()).magnitude;
|
||||
@ -417,6 +454,20 @@ namespace NewHorizons.Builder.Props
|
||||
{
|
||||
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>();
|
||||
}
|
||||
else if (component is RaftDock dock)
|
||||
{
|
||||
// These flood toggles are to disable flooded docks on the Stranger
|
||||
// Presumably the user isn't making one of those
|
||||
foreach (var toggle in dock.GetComponents<FloodToggle>())
|
||||
{
|
||||
Component.DestroyImmediate(toggle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -433,7 +484,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;
|
||||
@ -441,9 +492,13 @@ 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);
|
||||
|
||||
|
||||
angler._animator.SetFloat("MoveSpeed", angler._moveCurrent);
|
||||
angler._animator.SetFloat("Jaw", angler._jawCurrent);
|
||||
|
||||
Destroy(this);
|
||||
}
|
||||
}
|
||||
@ -495,5 +550,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
|
||||
{
|
||||
@ -31,7 +36,7 @@ namespace NewHorizons.Builder.Props
|
||||
}
|
||||
else
|
||||
{
|
||||
return (AddToExistingDialogue(info, xml), null);
|
||||
return (AddToExistingDialogue(go, sector, info, xml, dialogueName), null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,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)
|
||||
@ -66,9 +73,11 @@ namespace NewHorizons.Builder.Props
|
||||
return (dialogue, remoteTrigger);
|
||||
}
|
||||
|
||||
private static CharacterDialogueTree AddToExistingDialogue(DialogueInfo info, string xml)
|
||||
private static CharacterDialogueTree AddToExistingDialogue(GameObject go, Sector sector, DialogueInfo info, string xml, string dialogueName)
|
||||
{
|
||||
var existingDialogue = SearchUtilities.Find(info.pathToExistingDialogue)?.GetComponent<CharacterDialogueTree>();
|
||||
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)
|
||||
{
|
||||
@ -76,7 +85,29 @@ namespace NewHorizons.Builder.Props
|
||||
return null;
|
||||
}
|
||||
|
||||
var existingText = existingDialogue._xmlCharacterDialogueAsset.text;
|
||||
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);
|
||||
@ -102,10 +133,16 @@ namespace NewHorizons.Builder.Props
|
||||
// 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);
|
||||
existingDialogueOptionsList.AppendChild(importedNode);
|
||||
// We add them to the start because normally the last option is to return to menu or exit
|
||||
existingDialogueOptionsList.PrependChild(importedNode);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -116,6 +153,10 @@ namespace NewHorizons.Builder.Props
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
@ -123,13 +164,86 @@ namespace NewHorizons.Builder.Props
|
||||
|
||||
existingDialogue.SetTextXml(newTextAsset);
|
||||
|
||||
// Chracter name is required for adding translations, something to do with how OW prefixes its dialogue
|
||||
var characterName = existingDialogueTree.SelectSingleNode("NameField").InnerText;
|
||||
AddTranslation(xml, characterName);
|
||||
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);
|
||||
@ -185,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)
|
||||
{
|
||||
@ -212,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 ?? Vector3.zero;
|
||||
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 ?? Vector3.zero;
|
||||
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);
|
||||
@ -376,11 +524,17 @@ namespace NewHorizons.Builder.Props
|
||||
}
|
||||
}
|
||||
|
||||
[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");
|
||||
|
||||
// When adding dialogue to existing stuff, we have to pass in the character name
|
||||
@ -415,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,60 @@
|
||||
using NewHorizons.External.Modules.Props;
|
||||
using NewHorizons.External.Modules.Props.EchoesOfTheEye;
|
||||
using NewHorizons.Handlers;
|
||||
using NewHorizons.Utility;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using OWML.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Builder.Props.EchoesOfTheEye
|
||||
{
|
||||
public static class AlarmTotemBuilder
|
||||
{
|
||||
private static GameObject _prefab;
|
||||
|
||||
internal static void InitPrefab()
|
||||
{
|
||||
if (_prefab == null)
|
||||
{
|
||||
_prefab = SearchUtilities.Find("DreamWorld_Body/Sector_DreamWorld/Sector_Underground/IslandsRoot/IslandPivot_C/Island_C/Interactibles_Island_C/Prefab_IP_AlarmTotem").InstantiateInactive().Rename("Prefab_AlarmTotem").DontDestroyOnLoad();
|
||||
if (_prefab == null)
|
||||
{
|
||||
NHLogger.LogWarning($"Tried to make a grapple totem but couldn't. Do you have the DLC installed?");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
_prefab.AddComponent<DestroyOnDLC>()._destroyOnDLCNotOwned = true;
|
||||
var alarmTotem = _prefab.GetComponent<AlarmTotem>();
|
||||
alarmTotem._sector = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static GameObject Make(GameObject planetGO, Sector sector, AlarmTotemInfo info, IModBehaviour mod)
|
||||
{
|
||||
InitPrefab();
|
||||
|
||||
if (_prefab == null || sector == null) return null;
|
||||
|
||||
var totemObj = DetailBuilder.Make(planetGO, sector, mod, _prefab, new DetailInfo(info));
|
||||
|
||||
var alarmTotem = totemObj.GetComponent<AlarmTotem>();
|
||||
alarmTotem._sightAngle = info.sightAngle;
|
||||
alarmTotem._sightDistance = info.sightDistance;
|
||||
|
||||
if (info.stretchVisionCone != null)
|
||||
{
|
||||
var visionCone = totemObj.transform.Find("Effects_IP_SIM_AlarmTotem/AlarmTotemVisionCone");
|
||||
visionCone.localScale = Vector3.Scale(visionCone.localScale, info.stretchVisionCone);
|
||||
}
|
||||
|
||||
return totemObj;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
using NewHorizons.Components.Props;
|
||||
using NewHorizons.External.Modules.Props;
|
||||
using NewHorizons.External.Modules.Props.EchoesOfTheEye;
|
||||
using NewHorizons.Handlers;
|
||||
using NewHorizons.Utility;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using OWML.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Builder.Props.EchoesOfTheEye
|
||||
{
|
||||
public static class DreamArrivalPointBuilder
|
||||
{
|
||||
private static GameObject _prefab;
|
||||
|
||||
internal static void InitPrefab()
|
||||
{
|
||||
if (_prefab == null)
|
||||
{
|
||||
_prefab = SearchUtilities.Find("DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_1/DreamFireHouse_1/Interactibles_DreamFireHouse_1/Prefab_IP_DreamArrivalPoint_Zone1").InstantiateInactive().Rename("Prefab_DreamArrivalPoint").DontDestroyOnLoad();
|
||||
if (_prefab == null)
|
||||
{
|
||||
NHLogger.LogWarning($"Tried to make a dream arrival point but couldn't. Do you have the DLC installed?");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
_prefab.AddComponent<DestroyOnDLC>()._destroyOnDLCNotOwned = true;
|
||||
var dreamArrivalPoint = _prefab.GetComponent<DreamArrivalPoint>();
|
||||
dreamArrivalPoint._location = DreamArrivalPoint.Location.Undefined;
|
||||
dreamArrivalPoint._sector = null;
|
||||
dreamArrivalPoint._entrywayVolumes = new OWTriggerVolume[0];
|
||||
dreamArrivalPoint._raftSpawn = null;
|
||||
dreamArrivalPoint._connectedDreamCampfire = null;
|
||||
dreamArrivalPoint._campfire._sector = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static GameObject Make(GameObject planetGO, Sector sector, DreamArrivalPointInfo info, IModBehaviour mod)
|
||||
{
|
||||
InitPrefab();
|
||||
|
||||
if (_prefab == null || sector == null) return null;
|
||||
|
||||
var arrivalPointObj = DetailBuilder.Make(planetGO, sector, mod, _prefab, new DetailInfo(info));
|
||||
|
||||
StreamingHandler.SetUpStreaming(arrivalPointObj, sector);
|
||||
|
||||
DreamArrivalPoint arrivalPoint = arrivalPointObj.GetComponent<DreamArrivalPoint>();
|
||||
arrivalPoint._sector = arrivalPoint.GetComponentInParent<Sector>();
|
||||
arrivalPoint._location = DreamHandler.GetDreamArrivalLocation(info.id);
|
||||
Locator.RegisterDreamArrivalPoint(arrivalPoint, arrivalPoint._location);
|
||||
|
||||
return arrivalPointObj;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
using NewHorizons.External.Modules.Props;
|
||||
using NewHorizons.External.Modules.Props.EchoesOfTheEye;
|
||||
using NewHorizons.Handlers;
|
||||
using NewHorizons.Utility;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using OWML.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Builder.Props.EchoesOfTheEye
|
||||
{
|
||||
public static class DreamCampfireBuilder
|
||||
{
|
||||
private static GameObject _prefab;
|
||||
|
||||
internal static void InitPrefab()
|
||||
{
|
||||
if (_prefab == null)
|
||||
{
|
||||
_prefab = SearchUtilities.Find("RingWorld_Body/Sector_RingInterior/Sector_Zone4/Sector_PrisonDocks/Sector_PrisonInterior/Interactibles_PrisonInterior/Prefab_IP_DreamCampfire").InstantiateInactive().Rename("Prefab_DreamCampfire").DontDestroyOnLoad();
|
||||
if (_prefab == null)
|
||||
{
|
||||
NHLogger.LogWarning($"Tried to make a dream campfire but couldn't. Do you have the DLC installed?");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
_prefab.AddComponent<DestroyOnDLC>()._destroyOnDLCNotOwned = true;
|
||||
var campfire = _prefab.GetComponentInChildren<DreamCampfire>();
|
||||
campfire._dreamArrivalLocation = DreamArrivalPoint.Location.Undefined;
|
||||
campfire._sector = null;
|
||||
campfire._entrywayVolumes = new OWTriggerVolume[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static GameObject Make(GameObject planetGO, Sector sector, DreamCampfireInfo info, IModBehaviour mod)
|
||||
{
|
||||
InitPrefab();
|
||||
|
||||
if (_prefab == null || sector == null) return null;
|
||||
|
||||
var campfireObj = DetailBuilder.Make(planetGO, sector, mod, _prefab, new DetailInfo(info));
|
||||
|
||||
var campfire = campfireObj.GetComponentInChildren<DreamCampfire>();
|
||||
campfire._dreamArrivalLocation = DreamHandler.GetDreamArrivalLocation(info.id);
|
||||
|
||||
// The streaming groups on DreamCampfires get set on Start() so we wait until after to change it again
|
||||
Delay.FireInNUpdates(() => {
|
||||
var streaming = campfireObj.GetComponentInChildren<DreamCampfireStreaming>();
|
||||
if (streaming != null)
|
||||
{
|
||||
var targetArrivalPoint = Locator.GetDreamArrivalPoint(campfire._dreamArrivalLocation);
|
||||
if (targetArrivalPoint != null)
|
||||
{
|
||||
var streamingGroup = targetArrivalPoint.transform.root.GetComponentInChildren<StreamingGroup>();
|
||||
if (streamingGroup)
|
||||
{
|
||||
streaming._streamingGroup = streamingGroup;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 2);
|
||||
|
||||
Locator.RegisterDreamCampfire(campfire, campfire._dreamArrivalLocation);
|
||||
|
||||
return campfireObj;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,76 @@
|
||||
using NewHorizons.External.Modules.Props;
|
||||
using NewHorizons.External.Modules.Props.EchoesOfTheEye;
|
||||
using NewHorizons.Handlers;
|
||||
using NewHorizons.Utility;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using OWML.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Builder.Props.EchoesOfTheEye
|
||||
{
|
||||
public static class DreamCandleBuilder
|
||||
{
|
||||
private static Dictionary<DreamCandleType, GameObject> _prefabs = new();
|
||||
|
||||
internal static void InitPrefabs()
|
||||
{
|
||||
InitPrefab(DreamCandleType.Ground, "DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_1/Interactibles_DreamZone_1/DreamHouseIsland/Prefab_IP_DreamCandle");
|
||||
InitPrefab(DreamCandleType.GroundSmall, "DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_2/Structure_DreamZone_2/City/StartingAreaLanterns/Prefab_IP_DreamCandle_Ground_Small");
|
||||
InitPrefab(DreamCandleType.GroundLarge, "DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_1/Interactibles_DreamZone_1/DreamHouseIsland/Prefab_IP_DreamCandle_Ground_Large");
|
||||
InitPrefab(DreamCandleType.GroundSingle, "DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_1/Sector_PartyHouse/Interactables_PartyHouse/Prefab_IP_DreamCandle_Ground_Single");
|
||||
InitPrefab(DreamCandleType.Wall, "DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_2/Structure_DreamZone_2/City/ParkLanterns/Prefab_IP_DreamCandle_Wall");
|
||||
InitPrefab(DreamCandleType.WallLargeFlame, "DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_2/Structure_DreamZone_2/City/FalseKnightHouse/CandleDoor/FirstDoorLanterns/Prefab_IP_DreamCandle_LargeFlame_Wall");
|
||||
InitPrefab(DreamCandleType.WallBigWick, "DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_2/Structure_DreamZone_2/DreamFireHouse_2/Interactibles_DreamFireHouse_2/Pivot_SlideReelRoom/CandleController/CandlePivot_0/Prefab_IP_DreamCandle_BigWick_Wall");
|
||||
InitPrefab(DreamCandleType.Standing, "DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_2/Structure_DreamZone_2/City/ElevatorHouse/CandleDoor/DoorTutorial/Prefab_IP_DreamCandle_LargeFlame_Standing");
|
||||
InitPrefab(DreamCandleType.Pile, "DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_3/Sector_Hotel/Gallery/Interactibles_Gallery/DreamCandles/Prefab_IP_DreamCandle_Pile");
|
||||
}
|
||||
|
||||
private static void InitPrefab(DreamCandleType type, string path)
|
||||
{
|
||||
var prefab = _prefabs.ContainsKey(type) ? _prefabs[type] : null;
|
||||
if (prefab == null)
|
||||
{
|
||||
prefab = SearchUtilities.Find(path).InstantiateInactive().Rename($"Prefab_DreamCandle_{type}").DontDestroyOnLoad();
|
||||
if (prefab == null)
|
||||
{
|
||||
NHLogger.LogWarning($"Tried to make a dream candle but couldn't. Do you have the DLC installed?");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
prefab.AddComponent<DestroyOnDLC>()._destroyOnDLCNotOwned = true;
|
||||
var sensor = prefab.GetComponentInChildren<SingleLightSensor>();
|
||||
sensor._sector = null;
|
||||
}
|
||||
_prefabs.Add(type, prefab);
|
||||
}
|
||||
}
|
||||
|
||||
public static GameObject Make(GameObject planetGO, Sector sector, DreamCandleInfo info, IModBehaviour mod)
|
||||
{
|
||||
InitPrefabs();
|
||||
|
||||
var prefab = _prefabs.ContainsKey(info.type) ? _prefabs[info.type] : null;
|
||||
|
||||
if (prefab == null || sector == null) return null;
|
||||
|
||||
var candleObj = DetailBuilder.Make(planetGO, sector, mod, prefab, new DetailInfo(info));
|
||||
|
||||
var dreamCandle = candleObj.GetComponent<DreamCandle>();
|
||||
|
||||
var sensor = candleObj.GetComponentInChildren<SingleLightSensor>();
|
||||
sensor._detectFlashlight = true;
|
||||
sensor._lightSourceMask |= LightSourceType.FLASHLIGHT;
|
||||
|
||||
dreamCandle._startLit = info.startLit;
|
||||
dreamCandle.SetLit(info.startLit, false, true);
|
||||
|
||||
return candleObj;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
using NewHorizons.External.Modules.Props;
|
||||
using NewHorizons.External.Modules.Props.EchoesOfTheEye;
|
||||
using NewHorizons.Handlers;
|
||||
using NewHorizons.Utility;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using OWML.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Builder.Props.EchoesOfTheEye
|
||||
{
|
||||
public static class GrappleTotemBuilder
|
||||
{
|
||||
private static GameObject _prefab;
|
||||
|
||||
internal static void InitPrefab()
|
||||
{
|
||||
if (_prefab == null)
|
||||
{
|
||||
_prefab = SearchUtilities.Find("DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_4/Interactibles_DreamZone_4_Upper/Prefab_IP_GrappleTotem").InstantiateInactive().Rename("Prefab_GrappleTotem").DontDestroyOnLoad();
|
||||
if (_prefab == null)
|
||||
{
|
||||
NHLogger.LogWarning($"Tried to make a grapple totem but couldn't. Do you have the DLC installed?");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
_prefab.AddComponent<DestroyOnDLC>()._destroyOnDLCNotOwned = true;
|
||||
var zoomPoint = _prefab.GetComponentInChildren<LanternZoomPoint>();
|
||||
zoomPoint._sector = null;
|
||||
var sensor = _prefab.GetComponentInChildren<SingleLightSensor>();
|
||||
sensor._sector = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static GameObject Make(GameObject planetGO, Sector sector, GrappleTotemInfo info, IModBehaviour mod)
|
||||
{
|
||||
InitPrefab();
|
||||
|
||||
if (_prefab == null || sector == null) return null;
|
||||
|
||||
var totemObj = DetailBuilder.Make(planetGO, sector, mod, _prefab, new DetailInfo(info));
|
||||
|
||||
var zoomPoint = totemObj.GetComponentInChildren<LanternZoomPoint>();
|
||||
zoomPoint._minActivationDistance = info.minDistance;
|
||||
zoomPoint._arrivalDistance = info.arrivalDistance;
|
||||
|
||||
var sensor = totemObj.GetComponentInChildren<SingleLightSensor>();
|
||||
sensor._detectionAngle = info.maxAngle;
|
||||
sensor._maxDistance = info.maxDistance;
|
||||
|
||||
sensor._detectFlashlight = true;
|
||||
sensor._lightSourceMask |= LightSourceType.FLASHLIGHT;
|
||||
|
||||
return totemObj;
|
||||
}
|
||||
}
|
||||
}
|
||||
94
NewHorizons/Builder/Props/EchoesOfTheEye/PortholeBuilder.cs
Normal file
94
NewHorizons/Builder/Props/EchoesOfTheEye/PortholeBuilder.cs
Normal file
@ -0,0 +1,94 @@
|
||||
using NewHorizons.External.Modules.Props;
|
||||
using NewHorizons.External.Modules.Props.EchoesOfTheEye;
|
||||
using NewHorizons.Handlers;
|
||||
using NewHorizons.Utility;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using OWML.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Builder.Props.EchoesOfTheEye
|
||||
{
|
||||
public static class PortholeBuilder
|
||||
{
|
||||
private static GameObject _mainPrefab;
|
||||
private static GameObject _simPrefab;
|
||||
|
||||
internal static void InitPrefabs()
|
||||
{
|
||||
if (_mainPrefab == null)
|
||||
{
|
||||
_mainPrefab = SearchUtilities.Find("DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_4/Interactibles_DreamZone_4_Upper/Props_IP_Peephole_Prison").InstantiateInactive().Rename("Prefab_Porthole").DontDestroyOnLoad();
|
||||
if (_mainPrefab == null)
|
||||
{
|
||||
NHLogger.LogWarning($"Tried to make a grapple totem but couldn't. Do you have the DLC installed?");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
_mainPrefab.AddComponent<DestroyOnDLC>()._destroyOnDLCNotOwned = true;
|
||||
var peephole = _mainPrefab.GetComponentInChildren<Peephole>();
|
||||
peephole._factIDs = new string[0];
|
||||
peephole._viewingSector = null;
|
||||
}
|
||||
}
|
||||
if (_simPrefab == null)
|
||||
{
|
||||
_simPrefab = SearchUtilities.Find("DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_4/Simulation_DreamZone_4/Geo_DreamZone_4_Upper/Effects_IP_SIM_Porthole").InstantiateInactive().Rename("Prefab_SIM_Porthole").DontDestroyOnLoad();
|
||||
if (_simPrefab == null)
|
||||
{
|
||||
NHLogger.LogWarning($"Tried to make a grapple totem but couldn't. Do you have the DLC installed?");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
_simPrefab.AddComponent<DestroyOnDLC>()._destroyOnDLCNotOwned = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static GameObject Make(GameObject planetGO, Sector sector, PortholeInfo info, IModBehaviour mod)
|
||||
{
|
||||
InitPrefabs();
|
||||
|
||||
if (_mainPrefab == null || _simPrefab == null || sector == null) return null;
|
||||
|
||||
var portholeObj = DetailBuilder.Make(planetGO, sector, mod, _mainPrefab, new DetailInfo(info));
|
||||
portholeObj.name = "Prefab_Porthole";
|
||||
|
||||
var simObj = DetailBuilder.Make(planetGO, sector, mod, _simPrefab, new DetailInfo(info));
|
||||
simObj.transform.parent = portholeObj.transform;
|
||||
|
||||
var parentObj = GeneralPropBuilder.MakeNew("Porthole", planetGO, sector, info);
|
||||
parentObj.SetActive(true);
|
||||
portholeObj.transform.SetParent(parentObj.transform, true);
|
||||
portholeObj.transform.localPosition = new Vector3(0f, -4f, 8f);
|
||||
portholeObj.transform.localEulerAngles = new Vector3(0f, 315f, 0f);
|
||||
|
||||
var peephole = portholeObj.GetComponentInChildren<Peephole>();
|
||||
if (info.revealFacts != null)
|
||||
{
|
||||
peephole._factIDs = info.revealFacts;
|
||||
}
|
||||
|
||||
peephole._peepholeCamera.farClipPlane = 4000f;
|
||||
peephole._peepholeCamera.fieldOfView = info.fieldOfView;
|
||||
|
||||
// Reposition the peephole camera later, after all planets are built, in case the target point is on a different astro body.
|
||||
Delay.FireInNUpdates(() =>
|
||||
{
|
||||
var cameraObj = GeneralPropBuilder.MakeFromExisting(peephole._peepholeCamera.gameObject, planetGO, sector, info.target);
|
||||
cameraObj.transform.Rotate(Vector3.up, 180f, Space.Self);
|
||||
cameraObj.transform.position += cameraObj.transform.up;
|
||||
var viewingSector = cameraObj.GetComponentInParent<Sector>();
|
||||
peephole._viewingSector = viewingSector;
|
||||
}, 2);
|
||||
|
||||
return portholeObj;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,129 @@
|
||||
using NewHorizons.External.Modules.Props;
|
||||
using NewHorizons.External.Modules.Props.EchoesOfTheEye;
|
||||
using NewHorizons.Handlers;
|
||||
using NewHorizons.Utility;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using OWML.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
namespace NewHorizons.Builder.Props.EchoesOfTheEye
|
||||
{
|
||||
public static class ProjectionTotemBuilder
|
||||
{
|
||||
private static GameObject _prefab;
|
||||
|
||||
internal static void InitPrefab()
|
||||
{
|
||||
if (_prefab == null)
|
||||
{
|
||||
_prefab = SearchUtilities.Find("DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_3/Interactibles_DreamZone_3/Prefab_IP_DreamObjectProjector_Bridge").InstantiateInactive().Rename("Prefab_ProjectionTotem").DontDestroyOnLoad();
|
||||
if (_prefab == null)
|
||||
{
|
||||
NHLogger.LogWarning($"Tried to make a grapple totem but couldn't. Do you have the DLC installed?");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
_prefab.AddComponent<DestroyOnDLC>()._destroyOnDLCNotOwned = true;
|
||||
var projector = _prefab.GetComponent<DreamObjectProjector>();
|
||||
projector._projections = new DreamObjectProjection[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static GameObject Make(GameObject planetGO, Sector sector, ProjectionTotemInfo info, IModBehaviour mod)
|
||||
{
|
||||
InitPrefab();
|
||||
|
||||
if (_prefab == null || sector == null) return null;
|
||||
|
||||
var totemObj = DetailBuilder.Make(planetGO, sector, mod, _prefab, new DetailInfo(info));
|
||||
|
||||
var projector = totemObj.GetComponent<DreamObjectProjector>();
|
||||
|
||||
if (!string.IsNullOrEmpty(info.pathToAlarmTotem))
|
||||
{
|
||||
var alarmTotemObj = planetGO.transform.Find(info.pathToAlarmTotem);
|
||||
if (alarmTotemObj != null)
|
||||
{
|
||||
var alarmTotem = alarmTotemObj.GetComponentInChildren<AlarmTotem>();
|
||||
if (alarmTotem != null)
|
||||
{
|
||||
projector._alarmTotem = alarmTotem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (info.pathsToDreamCandles != null)
|
||||
{
|
||||
var dreamCandles = new List<DreamCandle>();
|
||||
foreach (var pathToDreamCandles in info.pathsToDreamCandles)
|
||||
{
|
||||
if (string.IsNullOrEmpty(pathToDreamCandles)) continue;
|
||||
var dreamCandleObj = planetGO.transform.Find(pathToDreamCandles);
|
||||
if (dreamCandleObj != null)
|
||||
{
|
||||
dreamCandles.AddRange(dreamCandleObj.GetComponentsInChildren<DreamCandle>());
|
||||
}
|
||||
}
|
||||
projector._dreamCandles = dreamCandles.ToArray();
|
||||
}
|
||||
|
||||
if (info.pathsToProjectionTotems != null)
|
||||
{
|
||||
var projectionTotems = new List<DreamObjectProjector>();
|
||||
foreach (var pathToProjectionTotems in info.pathsToProjectionTotems)
|
||||
{
|
||||
if (string.IsNullOrEmpty(pathToProjectionTotems)) continue;
|
||||
var projectionTotemObj = planetGO.transform.Find(pathToProjectionTotems);
|
||||
if (projectionTotemObj != null)
|
||||
{
|
||||
projectionTotems.AddRange(projectionTotemObj.GetComponentsInChildren<DreamObjectProjector>());
|
||||
}
|
||||
}
|
||||
projector._extinguishedProjectors = projectionTotems.ToArray();
|
||||
}
|
||||
|
||||
if (info.pathsToProjectedObjects != null)
|
||||
{
|
||||
var projections = new List<DreamObjectProjection>();
|
||||
foreach (var pathToProjectedObject in info.pathsToProjectedObjects)
|
||||
{
|
||||
if (string.IsNullOrEmpty(pathToProjectedObject)) continue;
|
||||
var projectionObj = planetGO.transform.Find(pathToProjectedObject);
|
||||
if (projectionObj != null)
|
||||
{
|
||||
projectionObj.gameObject.AddComponent<DitheringAnimator>();
|
||||
var projection = projectionObj.gameObject.AddComponent<DreamObjectProjection>();
|
||||
projection._setActive = info.toggleProjectedObjectsActive;
|
||||
projection.Awake();
|
||||
projections.Add(projection);
|
||||
}
|
||||
}
|
||||
projector._projections = projections.ToArray();
|
||||
}
|
||||
|
||||
var sensor = projector._lightSensor as SingleLightSensor;
|
||||
sensor._detectFlashlight = true;
|
||||
sensor._lightSourceMask |= LightSourceType.FLASHLIGHT;
|
||||
|
||||
projector._lit = info.startLit;
|
||||
projector._startLit = info.startLit;
|
||||
projector._extinguishOnly = info.extinguishOnly;
|
||||
/*
|
||||
Delay.FireOnNextUpdate(() =>
|
||||
{
|
||||
projector.Start();
|
||||
});
|
||||
*/
|
||||
|
||||
return totemObj;
|
||||
}
|
||||
}
|
||||
}
|
||||
258
NewHorizons/Builder/Props/EyeOfTheUniverseBuilder.cs
Normal file
258
NewHorizons/Builder/Props/EyeOfTheUniverseBuilder.cs
Normal file
@ -0,0 +1,258 @@
|
||||
using NewHorizons.Builder.Props.Audio;
|
||||
using NewHorizons.Components.EyeOfTheUniverse;
|
||||
using NewHorizons.External;
|
||||
using NewHorizons.External.Modules;
|
||||
using NewHorizons.External.Modules.Props.Audio;
|
||||
using NewHorizons.External.Modules.Props.EyeOfTheUniverse;
|
||||
using NewHorizons.Handlers;
|
||||
using NewHorizons.Utility;
|
||||
using NewHorizons.Utility.OuterWilds;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Builder.Props
|
||||
{
|
||||
public static class EyeOfTheUniverseBuilder
|
||||
{
|
||||
public static TravelerEyeController MakeEyeTraveler(GameObject planetGO, Sector sector, EyeTravelerInfo info, NewHorizonsBody nhBody)
|
||||
{
|
||||
var travelerData = EyeSceneHandler.CreateEyeTravelerData(info.id);
|
||||
travelerData.info = info;
|
||||
travelerData.requirementsMet = true;
|
||||
|
||||
if (!string.IsNullOrEmpty(info.requiredFact) && !ShipLogHandler.KnowsFact(info.requiredFact))
|
||||
{
|
||||
travelerData.requirementsMet = false;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(info.requiredPersistentCondition) && !DialogueConditionManager.SharedInstance.GetConditionState(info.requiredPersistentCondition))
|
||||
{
|
||||
travelerData.requirementsMet = false;
|
||||
}
|
||||
|
||||
if (!travelerData.requirementsMet)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var go = DetailBuilder.Make(planetGO, sector, nhBody.Mod, info);
|
||||
|
||||
var travelerController = go.GetAddComponent<TravelerEyeController>();
|
||||
if (!string.IsNullOrEmpty(info.startPlayingCondition))
|
||||
{
|
||||
travelerController._startPlayingCondition = info.startPlayingCondition;
|
||||
}
|
||||
else if (string.IsNullOrEmpty(travelerController._startPlayingCondition))
|
||||
{
|
||||
NHLogger.LogError($"Eye Traveler with ID \"{info.id}\" does not have a Start Playing condition set");
|
||||
}
|
||||
if (travelerController._animator == null)
|
||||
{
|
||||
travelerController._animator = go.GetComponentInChildren<Animator>();
|
||||
}
|
||||
if (info.dialogue != null)
|
||||
{
|
||||
var (dialogueTree, remoteTrigger) = DialogueBuilder.Make(planetGO, sector, info.dialogue, nhBody.Mod);
|
||||
if (info.dialogue.position == null && info.dialogue.parentPath == null)
|
||||
{
|
||||
info.dialogue.isRelativeToParent = true;
|
||||
}
|
||||
GeneralPropBuilder.MakeFromExisting(dialogueTree.gameObject, planetGO, sector, info.dialogue, defaultParent: go.transform);
|
||||
|
||||
if (travelerController._dialogueTree != null)
|
||||
{
|
||||
travelerController._dialogueTree.OnStartConversation -= travelerController.OnStartConversation;
|
||||
travelerController._dialogueTree.OnEndConversation -= travelerController.OnEndConversation;
|
||||
}
|
||||
travelerController._dialogueTree = dialogueTree;
|
||||
travelerController._dialogueTree.OnStartConversation += travelerController.OnStartConversation;
|
||||
travelerController._dialogueTree.OnEndConversation += travelerController.OnEndConversation;
|
||||
}
|
||||
else if (travelerController._dialogueTree == null)
|
||||
{
|
||||
travelerController._dialogueTree = go.GetComponentInChildren<CharacterDialogueTree>();
|
||||
if (travelerController._dialogueTree == null)
|
||||
{
|
||||
NHLogger.LogError($"Eye Traveler with ID \"{info.id}\" does not have any dialogue set");
|
||||
}
|
||||
}
|
||||
|
||||
travelerData.controller = travelerController;
|
||||
|
||||
OWAudioSource loopAudioSource = null;
|
||||
|
||||
if (info.signal != null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(info.signal.name))
|
||||
{
|
||||
info.signal.name = go.name;
|
||||
}
|
||||
if (string.IsNullOrEmpty(info.signal.frequency))
|
||||
{
|
||||
info.signal.frequency = "Traveler";
|
||||
}
|
||||
var signalGO = SignalBuilder.Make(planetGO, sector, info.signal, nhBody.Mod);
|
||||
if (info.signal.position == null && info.signal.parentPath == null)
|
||||
{
|
||||
info.signal.isRelativeToParent = true;
|
||||
}
|
||||
GeneralPropBuilder.MakeFromExisting(signalGO, planetGO, sector, info.signal, defaultParent: go.transform);
|
||||
|
||||
var signal = signalGO.GetComponent<AudioSignal>();
|
||||
travelerController._signal = signal;
|
||||
signal.SetSignalActivation(false);
|
||||
loopAudioSource = signal.GetOWAudioSource();
|
||||
|
||||
}
|
||||
else if (travelerController._signal == null)
|
||||
{
|
||||
NHLogger.LogError($"Eye Traveler with ID \"{info.id}\" does not have any loop audio set");
|
||||
}
|
||||
|
||||
travelerData.loopAudioSource = loopAudioSource;
|
||||
|
||||
OWAudioSource finaleAudioSource = null;
|
||||
|
||||
if (!string.IsNullOrEmpty(info.finaleAudio))
|
||||
{
|
||||
var finaleAudioInfo = new AudioSourceInfo()
|
||||
{
|
||||
audio = info.finaleAudio,
|
||||
track = External.SerializableEnums.NHAudioMixerTrackName.Music,
|
||||
volume = 1f,
|
||||
};
|
||||
finaleAudioSource = GeneralAudioBuilder.Make(planetGO, sector, finaleAudioInfo, nhBody.Mod);
|
||||
finaleAudioSource.SetTrack(finaleAudioInfo.track.ConvertToOW());
|
||||
finaleAudioSource.loop = false;
|
||||
finaleAudioSource.spatialBlend = 0f;
|
||||
finaleAudioSource.playOnAwake = false;
|
||||
finaleAudioSource.gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
travelerData.finaleAudioSource = finaleAudioSource;
|
||||
|
||||
return travelerController;
|
||||
}
|
||||
|
||||
public static QuantumInstrument MakeQuantumInstrument(GameObject planetGO, Sector sector, QuantumInstrumentInfo info, NewHorizonsBody nhBody)
|
||||
{
|
||||
var travelerData = EyeSceneHandler.GetEyeTravelerData(info.id);
|
||||
|
||||
if (travelerData != null && !travelerData.requirementsMet)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var go = DetailBuilder.Make(planetGO, sector, nhBody.Mod, info);
|
||||
go.layer = Layer.Interactible;
|
||||
if (info.interactRadius > 0f)
|
||||
{
|
||||
var collider = go.AddComponent<SphereCollider>();
|
||||
collider.radius = info.interactRadius;
|
||||
collider.isTrigger = true;
|
||||
go.GetAddComponent<OWCollider>();
|
||||
}
|
||||
|
||||
go.GetAddComponent<InteractReceiver>();
|
||||
var quantumInstrument = go.GetAddComponent<QuantumInstrument>();
|
||||
quantumInstrument._gatherWithScope = info.gatherWithScope;
|
||||
ArrayHelpers.Append(ref quantumInstrument._deactivateObjects, go);
|
||||
|
||||
var trigger = go.AddComponent<QuantumInstrumentTrigger>();
|
||||
trigger.gatherCondition = info.gatherCondition;
|
||||
|
||||
if (travelerData != null)
|
||||
{
|
||||
travelerData.quantumInstruments.Add(quantumInstrument);
|
||||
}
|
||||
else
|
||||
{
|
||||
NHLogger.LogError($"Quantum instrument with ID \"{info.id}\" has no matching eye traveler");
|
||||
}
|
||||
|
||||
info.signal ??= new SignalInfo();
|
||||
|
||||
if (travelerData?.info != null && travelerData.info.signal != null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(info.signal.name))
|
||||
{
|
||||
info.signal.name = travelerData.info.signal.name;
|
||||
}
|
||||
if (string.IsNullOrEmpty(info.signal.audio))
|
||||
{
|
||||
info.signal.audio = travelerData.info.signal.audio;
|
||||
}
|
||||
if (string.IsNullOrEmpty(info.signal.frequency))
|
||||
{
|
||||
info.signal.frequency = travelerData.info.signal.frequency;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(info.signal.audio))
|
||||
{
|
||||
var signalGO = SignalBuilder.Make(planetGO, sector, info.signal, nhBody.Mod);
|
||||
if (info.signal.position == null && info.signal.parentPath == null)
|
||||
{
|
||||
info.signal.isRelativeToParent = true;
|
||||
}
|
||||
GeneralPropBuilder.MakeFromExisting(signalGO, planetGO, sector, info.signal, defaultParent: go.transform);
|
||||
}
|
||||
else
|
||||
{
|
||||
NHLogger.LogError($"Eye Traveler with ID \"{info.id}\" does not have any loop audio set");
|
||||
}
|
||||
|
||||
return quantumInstrument;
|
||||
}
|
||||
|
||||
public static InstrumentZone MakeInstrumentZone(GameObject planetGO, Sector sector, InstrumentZoneInfo info, NewHorizonsBody nhBody)
|
||||
{
|
||||
var travelerData = EyeSceneHandler.GetEyeTravelerData(info.id);
|
||||
|
||||
if (travelerData != null && !travelerData.requirementsMet)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var go = DetailBuilder.Make(planetGO, sector, nhBody.Mod, info);
|
||||
|
||||
var instrumentZone = go.AddComponent<InstrumentZone>();
|
||||
|
||||
if (travelerData != null)
|
||||
{
|
||||
travelerData.instrumentZones.Add(instrumentZone);
|
||||
}
|
||||
else
|
||||
{
|
||||
NHLogger.LogError($"Instrument zone with ID \"{info.id}\" has no matching eye traveler");
|
||||
}
|
||||
|
||||
return instrumentZone;
|
||||
}
|
||||
|
||||
public static void Make(GameObject go, Sector sector, EyeOfTheUniverseModule module, NewHorizonsBody nhBody)
|
||||
{
|
||||
if (module.eyeTravelers != null)
|
||||
{
|
||||
foreach (var info in module.eyeTravelers)
|
||||
{
|
||||
MakeEyeTraveler(go, sector, info, nhBody);
|
||||
}
|
||||
}
|
||||
if (module.instrumentZones != null)
|
||||
{
|
||||
foreach (var info in module.instrumentZones)
|
||||
{
|
||||
MakeInstrumentZone(go, sector, info, nhBody);
|
||||
}
|
||||
}
|
||||
if (module.quantumInstruments != null)
|
||||
{
|
||||
foreach (var info in module.quantumInstruments)
|
||||
{
|
||||
MakeQuantumInstrument(go, sector, info, nhBody);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -51,6 +51,10 @@ namespace NewHorizons.Builder.Props
|
||||
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);
|
||||
@ -139,6 +143,11 @@ namespace NewHorizons.Builder.Props
|
||||
if (socket._socketTransform == null)
|
||||
{
|
||||
var socketGO = GeneralPropBuilder.MakeNew("Socket", planetGO, sector, info, defaultParent: go.transform);
|
||||
if (info.colliderRadius > 0f)
|
||||
{
|
||||
go.AddComponent<SphereCollider>().radius = info.colliderRadius;
|
||||
go.GetAddComponent<OWCollider>();
|
||||
}
|
||||
socketGO.SetActive(true);
|
||||
socket._socketTransform = socketGO.transform;
|
||||
}
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
using HarmonyLib;
|
||||
using NewHorizons.Components.EOTE;
|
||||
using NewHorizons.External.Modules.Props;
|
||||
using NewHorizons.External.Modules.Props.EchoesOfTheEye;
|
||||
using NewHorizons.Handlers;
|
||||
@ -8,14 +10,36 @@ 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.Utility.Files.AssetBundleUtilities;
|
||||
|
||||
namespace NewHorizons.Builder.Props
|
||||
{
|
||||
public static class ProjectionBuilder
|
||||
{
|
||||
private static GameObject _slideReelPrefab;
|
||||
public static string CurrentSlideReelFolder => "SlideReelCache_" + Main.Instance.CurrentStarSystem;
|
||||
public static string InvertedSlideReelCacheFolder => CurrentSlideReelFolder + "/Inverted";
|
||||
public static string AtlasSlideReelCacheFolder => CurrentSlideReelFolder + "/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 +47,30 @@ namespace NewHorizons.Builder.Props
|
||||
|
||||
private static bool _isInit;
|
||||
|
||||
public static bool CacheExists(IModBehaviour mod) => Directory.Exists(Path.Combine(mod.ModHelper.Manifest.ModFolderPath, AtlasSlideReelCacheFolder));
|
||||
|
||||
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,19 +122,26 @@ 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);
|
||||
slideReel.SetVisible(true);
|
||||
|
||||
var slideCollectionContainer = slideReelObj.GetRequiredComponent<SlideCollectionContainer>();
|
||||
var toDestroy = slideReelObj.GetComponent<SlideCollectionContainer>();
|
||||
var slideCollectionContainer = slideReelObj.AddComponent<NHSlideCollectionContainer>();
|
||||
slideReel._slideCollectionContainer = slideCollectionContainer;
|
||||
Component.DestroyImmediate(toDestroy);
|
||||
|
||||
foreach (var renderer in slideReelObj.GetComponentsInChildren<Renderer>())
|
||||
{
|
||||
@ -109,44 +150,86 @@ namespace NewHorizons.Builder.Props
|
||||
|
||||
// Now we replace the slides
|
||||
int slidesCount = info.slides.Length;
|
||||
var slideCollection = new SlideCollection(slidesCount);
|
||||
SlideCollection slideCollection = new NHSlideCollection(slidesCount, mod, info.slides.Select(x => x.imagePath).ToArray());
|
||||
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);
|
||||
// Slide reels dynamically load the inverted cached images when needed. We only need to load raw images to generate the cache or atlases
|
||||
var (_, atlasImageLoader, imageLoader) = StartAsyncLoader(mod, info.slides, ref slideCollection, false, true, false);
|
||||
|
||||
// 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 = true;
|
||||
|
||||
// Track the first 15 to put on the slide reel object
|
||||
if (index < textures.Length)
|
||||
var key = GetUniqueSlideReelID(mod, info.slides);
|
||||
|
||||
if (atlasImageLoader != null)
|
||||
{
|
||||
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;
|
||||
|
||||
// inverted slides will be loaded for the whole loop but its fine since this is only when generating cache
|
||||
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);
|
||||
// Now put together the textures into a 4x4 thing for the materials #888
|
||||
var displayTextures = textures;
|
||||
if (info.displaySlides != null && info.displaySlides.Length > 0)
|
||||
{
|
||||
displayTextures = info.displaySlides.Select(x => textures[x]).ToArray();
|
||||
}
|
||||
|
||||
var reelTexture = ImageUtilities.MakeReelTexture(mod, displayTextures, 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;
|
||||
@ -162,6 +245,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();
|
||||
@ -173,17 +347,43 @@ namespace NewHorizons.Builder.Props
|
||||
var autoProjector = projectorObj.GetComponent<AutoSlideProjector>();
|
||||
autoProjector._sector = sector;
|
||||
|
||||
var slideCollectionContainer = autoProjector.GetRequiredComponent<SlideCollectionContainer>();
|
||||
var toDestroy = autoProjector.GetComponent<SlideCollectionContainer>();
|
||||
var slideCollectionContainer = autoProjector.gameObject.AddComponent<NHSlideCollectionContainer>();
|
||||
slideCollectionContainer.doAsyncLoading = false;
|
||||
autoProjector._slideCollectionItem = slideCollectionContainer;
|
||||
Component.DestroyImmediate(toDestroy);
|
||||
|
||||
// Now we replace the slides
|
||||
int slidesCount = info.slides.Length;
|
||||
var slideCollection = new SlideCollection(slidesCount);
|
||||
SlideCollection slideCollection = new NHSlideCollection(slidesCount, mod, info.slides.Select(x => x.imagePath).ToArray());
|
||||
slideCollection.streamingAssetIdentifier = string.Empty; // NREs if null
|
||||
|
||||
var imageLoader = AddAsyncLoader(projectorObj, mod, info.slides, ref slideCollection);
|
||||
imageLoader.imageLoadedEvent.AddListener((Texture2D tex, int index) => { slideCollection.slides[index]._image = ImageUtilities.Invert(tex); });
|
||||
var (invImageLoader, _, imageLoader) = StartAsyncLoader(mod, info.slides, ref slideCollection, true, false, 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;
|
||||
slideCollectionContainer._playWithShipLogFacts = Array.Empty<string>(); // else it NREs in container initialize
|
||||
|
||||
StreamingHandler.SetUpStreaming(projectorObj, sector);
|
||||
|
||||
@ -205,9 +405,9 @@ namespace NewHorizons.Builder.Props
|
||||
if (_visionTorchDetectorPrefab == null) return null;
|
||||
|
||||
// spawn a trigger for the vision torch
|
||||
var g = DetailBuilder.Make(planetGO, sector, mod, _visionTorchDetectorPrefab, new DetailInfo(info) { scale = 2, rename = !string.IsNullOrEmpty(info.rename) ? info.rename : "VisionStaffDetector" });
|
||||
var visionTorchTargetGO = DetailBuilder.Make(planetGO, sector, mod, _visionTorchDetectorPrefab, new DetailInfo(info) { scale = 2, rename = !string.IsNullOrEmpty(info.rename) ? info.rename : "VisionStaffDetector" });
|
||||
|
||||
if (g == null)
|
||||
if (visionTorchTargetGO == null)
|
||||
{
|
||||
NHLogger.LogWarning($"Tried to make a vision torch target but couldn't. Do you have the DLC installed?");
|
||||
return null;
|
||||
@ -219,21 +419,27 @@ namespace NewHorizons.Builder.Props
|
||||
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, true);
|
||||
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>();
|
||||
var slideCollectionContainer = g.AddComponent<SlideCollectionContainer>();
|
||||
var target = visionTorchTargetGO.AddComponent<VisionTorchTarget>();
|
||||
var slideCollectionContainer = visionTorchTargetGO.AddComponent<NHSlideCollectionContainer>();
|
||||
slideCollectionContainer.doAsyncLoading = false;
|
||||
slideCollectionContainer.slideCollection = slideCollection;
|
||||
target.slideCollection = g.AddComponent<MindSlideCollection>();
|
||||
target.slideCollection = visionTorchTargetGO.AddComponent<MindSlideCollection>();
|
||||
target.slideCollection._slideCollectionContainer = slideCollectionContainer;
|
||||
|
||||
LinkShipLogFacts(info, slideCollectionContainer);
|
||||
|
||||
g.SetActive(true);
|
||||
visionTorchTargetGO.SetActive(true);
|
||||
|
||||
return g;
|
||||
return visionTorchTargetGO;
|
||||
}
|
||||
|
||||
public static GameObject MakeStandingVisionTorch(GameObject planetGO, Sector sector, ProjectionInfo info, IModBehaviour mod)
|
||||
@ -254,20 +460,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, true);
|
||||
|
||||
// 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
|
||||
@ -275,20 +481,24 @@ 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");
|
||||
}
|
||||
);
|
||||
|
||||
// Set up the containers for the slides
|
||||
var slideCollectionContainer = standingTorch.AddComponent<SlideCollectionContainer>();
|
||||
var slideCollectionContainer = standingTorch.AddComponent<NHSlideCollectionContainer>();
|
||||
slideCollectionContainer.doAsyncLoading = false;
|
||||
slideCollectionContainer.slideCollection = slideCollection;
|
||||
|
||||
var mindSlideCollection = standingTorch.AddComponent<MindSlideCollection>();
|
||||
@ -307,21 +517,66 @@ namespace NewHorizons.Builder.Props
|
||||
return standingTorch;
|
||||
}
|
||||
|
||||
private static ImageUtilities.AsyncImageLoader AddAsyncLoader(GameObject gameObject, IModBehaviour mod, SlideInfo[] slides, ref SlideCollection slideCollection)
|
||||
/// <summary>
|
||||
/// start loading all the slide stuff we need async.
|
||||
/// </summary>
|
||||
/// <param name="mod">the mod to load slides from</param>
|
||||
/// <param name="slides">slides to load</param>
|
||||
/// <param name="slideCollection">where to assign the slide objects</param>
|
||||
/// <param name="useInvertedCache">should we load cached inverted images?</param>
|
||||
/// <param name="useAtlasCache">should we load cached atlas images?</param>
|
||||
/// <param name="loadRawImages">should we load the original images? happens anyway if cache doesnt exist since atlas or inverted will need it</param>
|
||||
/// <returns>the 3 loaders (inverted, atlas, original). inverted and atlas will be null if cache doesnt exist, so check those to find out if cache exists</returns>
|
||||
private static (SlideReelAsyncImageLoader inverted, SlideReelAsyncImageLoader atlas, SlideReelAsyncImageLoader slides)
|
||||
StartAsyncLoader(IModBehaviour mod, SlideInfo[] slides, ref SlideCollection slideCollection, bool useInvertedCache, bool useAtlasCache, bool loadRawImages)
|
||||
{
|
||||
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, AtlasSlideReelCacheFolder, $"{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(Main.Instance.ModHelper.Manifest.ModFolderPath, "Assets/textures/inverted_blank_slide_reel.png")));
|
||||
}
|
||||
else if (!cacheExists || loadRawImages)
|
||||
{
|
||||
// Used to then make cached stuff
|
||||
imageLoader.PathsToLoad.Add((i, Path.Combine(Main.Instance.ModHelper.Manifest.ModFolderPath, "Assets/textures/blank_slide_reel.png")));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
imageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, slideInfo.imagePath)));
|
||||
if (cacheExists && useInvertedCache)
|
||||
{
|
||||
// Load the inverted images used when displaying slide reels to a screen
|
||||
invertedImageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, InvertedSlideReelCacheFolder, slideInfo.imagePath)));
|
||||
}
|
||||
if (!cacheExists || loadRawImages)
|
||||
{
|
||||
imageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, slideInfo.imagePath)));
|
||||
}
|
||||
}
|
||||
|
||||
AddModules(slideInfo, ref slide, mod);
|
||||
@ -329,7 +584,35 @@ 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);
|
||||
}
|
||||
if (useInvertedCache)
|
||||
{
|
||||
invertedImageLoader.Start(true, false);
|
||||
}
|
||||
if (loadRawImages)
|
||||
{
|
||||
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)
|
||||
@ -388,16 +671,23 @@ 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)
|
||||
|
||||
private static void LinkShipLogFacts(ProjectionInfo info, NHSlideCollectionContainer slideCollectionContainer)
|
||||
{
|
||||
// Idk why but it wants reveals to be comma delimited not a list
|
||||
if (info.reveals != null) slideCollectionContainer._shipLogOnComplete = string.Join(",", info.reveals);
|
||||
// Don't use null value, NRE in SlideCollectionContainer.Initialize
|
||||
slideCollectionContainer._playWithShipLogFacts = info.playWithShipLogFacts ?? Array.Empty<string>();
|
||||
|
||||
slideCollectionContainer.conditionsToSet = info.conditionsToSet;
|
||||
slideCollectionContainer.persistentConditionsToSet = info.persistentConditionsToSet;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,52 +1,159 @@
|
||||
using NewHorizons.Builder.Body;
|
||||
using NewHorizons.Builder.Props.Audio;
|
||||
using NewHorizons.Builder.Props.EchoesOfTheEye;
|
||||
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)
|
||||
{
|
||||
foreach (var gravityCannonInfo in config.Props.gravityCannons)
|
||||
{
|
||||
try
|
||||
{
|
||||
GravityCannonBuilder.Make(go, sector, gravityCannonInfo, mod);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NHLogger.LogError($"Couldn't make gravity cannon [{gravityCannonInfo.shuttleID}] for [{go.name}]:\n{ex}");
|
||||
}
|
||||
}
|
||||
MakeGeneralProps(go, config.Props.dreamCandles, (candle) => DreamCandleBuilder.Make(go, sector, candle, mod));
|
||||
MakeGeneralProps(go, config.Props.portholes, (porthole) => PortholeBuilder.Make(go, sector, porthole, mod));
|
||||
MakeGeneralProps(go, config.Props.alarmTotems, (totem) => AlarmTotemBuilder.Make(go, sector, totem, mod));
|
||||
MakeGeneralProps(go, config.Props.grappleTotems, (totem) => GrappleTotemBuilder.Make(go, sector, totem, mod));
|
||||
MakeGeneralProps(go, config.Props.dreamCampfires, (campfire) => DreamCampfireBuilder.Make(go, sector, campfire, mod), (campfire) => campfire.id);
|
||||
MakeGeneralProps(go, config.Props.dreamArrivalPoints, (point) => DreamArrivalPointBuilder.Make(go, sector, point, mod), (point) => point.id);
|
||||
MakeGeneralProps(go, config.Props.rafts, (raft) => RaftBuilder.Make(go, sector, raft, planetBody));
|
||||
}
|
||||
if (config.Props.shuttles != null)
|
||||
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 shuttleInfo in config.Props.shuttles)
|
||||
var (dialogue, trigger) = DialogueBuilder.Make(go, sector, dialogueInfo, mod);
|
||||
if (dialogue == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
ShuttleBuilder.Make(go, sector, nhBody.Mod, shuttleInfo);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NHLogger.LogError($"Couldn't make shuttle [{shuttleInfo.id}] for [{go.name}]:\n{ex}");
|
||||
}
|
||||
NHLogger.LogVerbose($"[DIALOGUE] Failed to create dialogue [{dialogueInfo.xmlFile}]");
|
||||
}
|
||||
}
|
||||
}, (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);
|
||||
if (Main.HasDLC) MakeGeneralProps(go, config.Props.projectionTotems, (totem) => ProjectionTotemBuilder.Make(go, sector, totem, mod));
|
||||
// For quantum groups, make the details in advance
|
||||
if (config.Props.socketQuantumGroups != null) MakeGeneralProps(go, config.Props.socketQuantumGroups.SelectMany(x => x.details), (detail) => DetailBuilder.Make(go, sector, mod, detail), (detail) => detail.path);
|
||||
if (config.Props.stateQuantumGroups != null) MakeGeneralProps(go, config.Props.stateQuantumGroups.SelectMany(x => x.details), (detail) => DetailBuilder.Make(go, sector, mod, detail), (detail) => detail.path);
|
||||
if (config.Props.lightningQuantumGroups != null) MakeGeneralProps(go, config.Props.lightningQuantumGroups, (lightning) => QuantumBuilder.MakeQuantumLightning(go, sector, mod, lightning));
|
||||
|
||||
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,266 +165,26 @@ 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 (config.Props.socketQuantumGroups != null) QuantumBuilder.Make(go, sector, mod, config.Props.socketQuantumGroups);
|
||||
if (config.Props.stateQuantumGroups != null) QuantumBuilder.Make(go, sector, mod, config.Props.stateQuantumGroups);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
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>>();
|
||||
foreach (var detail in config.Props.details)
|
||||
{
|
||||
if (detail.quantumGroupID != null)
|
||||
{
|
||||
if (!propsByGroup.ContainsKey(detail.quantumGroupID)) propsByGroup[detail.quantumGroupID] = new List<GameObject>();
|
||||
propsByGroup[detail.quantumGroupID].Add(DetailBuilder.GetSpawnedGameObjectByDetailInfo(detail));
|
||||
}
|
||||
}
|
||||
private static bool _ignoreParent;
|
||||
|
||||
foreach (var quantumGroup in config.Props.quantumGroups)
|
||||
{
|
||||
if (!propsByGroup.ContainsKey(quantumGroup.id)) continue;
|
||||
var propsInGroup = propsByGroup[quantumGroup.id];
|
||||
|
||||
try
|
||||
{
|
||||
QuantumBuilder.Make(go, sector, config, mod, quantumGroup, propsInGroup.ToArray());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NHLogger.LogError($"Couldn't make quantum group \"{quantumGroup.id}\" for [{go.name}]:\n{ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (config.Props.singularities != null)
|
||||
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, nhBody.Mod, 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, nhBody.Mod, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using NewHorizons.Components.Quantum;
|
||||
using NewHorizons.External.Configs;
|
||||
using NewHorizons.External.Modules.Props;
|
||||
using NewHorizons.External.Modules.Props.Quantum;
|
||||
using NewHorizons.Utility.Files;
|
||||
using NewHorizons.Utility.Geometry;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using OWML.Common;
|
||||
@ -12,54 +13,148 @@ namespace NewHorizons.Builder.Props
|
||||
{
|
||||
public static class QuantumBuilder
|
||||
{
|
||||
|
||||
public static void Make(GameObject go, Sector sector, PlanetConfig config, IModBehaviour mod, QuantumGroupInfo quantumGroup, GameObject[] propsInGroup)
|
||||
public static void Make(GameObject planetGO, Sector sector, IModBehaviour mod, BaseQuantumGroupInfo[] quantumGroups)
|
||||
{
|
||||
switch(quantumGroup.type)
|
||||
foreach (var group in quantumGroups)
|
||||
{
|
||||
case QuantumGroupType.Sockets: MakeSocketGroup (go, sector, config, mod, quantumGroup, propsInGroup); return;
|
||||
case QuantumGroupType.States: MakeStateGroup (go, sector, config, mod, quantumGroup, propsInGroup); return;
|
||||
// case PropModule.QuantumGroupType.Shuffle: MakeShuffleGroup(go, sector, config, mod, quantumGroup, propsInGroup); return;
|
||||
Make(planetGO, sector, mod, group);
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
public static void Make(GameObject planetGO, Sector sector, IModBehaviour mod, BaseQuantumGroupInfo quantumGroup)
|
||||
{
|
||||
var groupRoot = new GameObject("Quantum Sockets - " + quantumGroup.id);
|
||||
groupRoot.transform.parent = sector?.transform ?? go.transform;
|
||||
if (quantumGroup.details == null || !quantumGroup.details.Any())
|
||||
{
|
||||
NHLogger.LogError($"Found quantum group with no details - [{planetGO.name}] [{quantumGroup.rename}]");
|
||||
return;
|
||||
}
|
||||
|
||||
if (quantumGroup is SocketQuantumGroupInfo socketGroup)
|
||||
{
|
||||
MakeSocketGroup(planetGO, sector, mod, socketGroup);
|
||||
}
|
||||
else if (quantumGroup is StateQuantumGroupInfo stateGroup)
|
||||
{
|
||||
MakeStateGroup(planetGO, sector, mod, stateGroup);
|
||||
}
|
||||
}
|
||||
|
||||
public static void MakeQuantumLightning(GameObject planetGO, Sector sector, IModBehaviour mod, LightningQuantumInfo quantumGroup)
|
||||
{
|
||||
(GameObject go, DetailInfo detail)[] propsInGroup = quantumGroup.details.Select(x => (DetailBuilder.Make(planetGO, sector, mod, x), x)).ToArray();
|
||||
|
||||
var lightning = DetailBuilder.Make(planetGO, sector, Main.Instance, AssetBundleUtilities.NHPrivateAssetBundle.LoadAsset<GameObject>("Prefab_EYE_QuantumLightningObject"), new DetailInfo(quantumGroup));
|
||||
AssetBundleUtilities.ReplaceShaders(lightning);
|
||||
|
||||
foreach (var (go, _) in propsInGroup)
|
||||
{
|
||||
go.transform.parent = lightning.transform;
|
||||
go.transform.localPosition = Vector3.zero;
|
||||
go.transform.localRotation = Quaternion.identity;
|
||||
}
|
||||
|
||||
var lightningController = lightning.GetComponent<QuantumLightningObject>();
|
||||
lightningController._models = propsInGroup.Select(x => x.go).ToArray();
|
||||
lightningController.enabled = true;
|
||||
|
||||
lightning.name = quantumGroup.rename;
|
||||
lightning.SetActive(true);
|
||||
|
||||
// Not sure why but it isn't enabling itself
|
||||
Delay.FireOnNextUpdate(() => lightningController.enabled = true);
|
||||
}
|
||||
|
||||
// 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 planetGO, Sector sector, IModBehaviour mod, SocketQuantumGroupInfo quantumGroup)
|
||||
{
|
||||
(GameObject go, QuantumDetailInfo detail)[] propsInGroup = quantumGroup.details.Select(x => (DetailBuilder.GetGameObjectFromDetailInfo(x), x)).ToArray();
|
||||
|
||||
GameObject specialProp = null;
|
||||
QuantumDetailInfo specialInfo = null;
|
||||
if (propsInGroup.Length == quantumGroup.sockets.Length)
|
||||
{
|
||||
// Special case!
|
||||
specialProp = propsInGroup.Last().go;
|
||||
specialInfo = propsInGroup.Last().detail;
|
||||
var propsInGroupList = propsInGroup.ToList();
|
||||
propsInGroupList.RemoveAt(propsInGroup.Length - 1);
|
||||
propsInGroup = propsInGroupList.ToArray();
|
||||
}
|
||||
|
||||
var groupRoot = new GameObject(quantumGroup.rename);
|
||||
groupRoot.transform.parent = sector?.transform ?? planetGO.transform;
|
||||
groupRoot.transform.localPosition = Vector3.zero;
|
||||
groupRoot.transform.localEulerAngles = Vector3.zero;
|
||||
|
||||
|
||||
var sockets = new QuantumSocket[quantumGroup.sockets.Length];
|
||||
for (int i = 0; i < quantumGroup.sockets.Length; i++)
|
||||
{
|
||||
var socketInfo = quantumGroup.sockets[i];
|
||||
|
||||
var socket = GeneralPropBuilder.MakeNew("Socket " + i, go, sector, socketInfo, defaultParent: groupRoot.transform);
|
||||
var socket = GeneralPropBuilder.MakeNew("Socket " + i, planetGO, sector, socketInfo, defaultParent: groupRoot.transform);
|
||||
|
||||
sockets[i] = socket.AddComponent<QuantumSocket>();
|
||||
sockets[i]._lightSources = new Light[0]; // TODO: make this customizable?
|
||||
socket.SetActive(true);
|
||||
}
|
||||
|
||||
foreach(var prop in propsInGroup)
|
||||
foreach (var prop in propsInGroup)
|
||||
{
|
||||
prop.SetActive(false);
|
||||
var quantumObject = prop.AddComponent<SocketedQuantumObject>();
|
||||
prop.go.SetActive(false);
|
||||
var quantumObject = prop.go.AddComponent<SocketedQuantumObject>();
|
||||
quantumObject._socketRoot = groupRoot;
|
||||
quantumObject._socketList = sockets.ToList();
|
||||
quantumObject._sockets = sockets;
|
||||
quantumObject._prebuilt = true;
|
||||
quantumObject._alignWithSocket = !prop.detail.alignWithGravity;
|
||||
quantumObject._randomYRotation = prop.detail.randomizeYRotation;
|
||||
quantumObject._alignWithGravity = prop.detail.alignWithGravity;
|
||||
quantumObject._childSockets = new List<QuantumSocket>();
|
||||
// TODO: support _alignWithGravity?
|
||||
if (prop.GetComponentInChildren<VisibilityTracker>() == null) AddBoundsVisibility(prop);
|
||||
prop.SetActive(true);
|
||||
if (prop.go.GetComponentInChildren<VisibilityTracker>() == null)
|
||||
{
|
||||
BoundsUtilities.AddBoundsVisibility(prop.go);
|
||||
}
|
||||
prop.go.SetActive(true);
|
||||
}
|
||||
|
||||
if (specialProp != null)
|
||||
{
|
||||
// Can't have 4 objects in 4 slots
|
||||
// Instead we have a duplicate of the final object for each slot, which appears when that slot is "empty"
|
||||
for (int i = 0; i < sockets.Length; i++)
|
||||
{
|
||||
var emptySocketObject = DetailBuilder.Make(planetGO, sector, mod, specialProp, new DetailInfo());
|
||||
var socket = sockets[i];
|
||||
socket._emptySocketObject = emptySocketObject;
|
||||
emptySocketObject.SetActive(socket._quantumObject == null);
|
||||
emptySocketObject.transform.parent = socket.transform;
|
||||
emptySocketObject.transform.localPosition = Vector3.zero;
|
||||
emptySocketObject.transform.localRotation = Quaternion.identity;
|
||||
|
||||
// Need to add a visibility tracker for this socket else it doesn't stay "empty" when photographed
|
||||
socket.SetActive(false);
|
||||
var tracker = new GameObject("VisibilityTracker");
|
||||
tracker.transform.parent = socket.transform;
|
||||
tracker.transform.localPosition = Vector3.zero;
|
||||
tracker.transform.localRotation = Quaternion.identity;
|
||||
var box = tracker.AddComponent<BoxShape>();
|
||||
box.size = new Vector3(0.2f, 0.6f, 0.2f);
|
||||
box.center = new Vector3(0, 0.3f, 0);
|
||||
tracker.AddComponent<ShapeVisibilityTracker>();
|
||||
// Using a quantum object bc it can be locked by camera
|
||||
var quantumObject = socket.gameObject.AddComponent<SnapshotLockableVisibilityObject>();
|
||||
quantumObject._alignWithSocket = !specialInfo.alignWithGravity;
|
||||
quantumObject._randomYRotation = specialInfo.randomizeYRotation;
|
||||
quantumObject._alignWithGravity = specialInfo.alignWithGravity;
|
||||
quantumObject.emptySocketObject = emptySocketObject;
|
||||
socket._visibilityObject = quantumObject;
|
||||
|
||||
socket.SetActive(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void MakeStateGroup(GameObject go, Sector sector, PlanetConfig config, IModBehaviour mod, QuantumGroupInfo quantumGroup, GameObject[] propsInGroup)
|
||||
public static void MakeStateGroup(GameObject go, Sector sector, IModBehaviour mod, StateQuantumGroupInfo quantumGroup)
|
||||
{
|
||||
// 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
|
||||
@ -67,12 +162,14 @@ namespace NewHorizons.Builder.Props
|
||||
// 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);
|
||||
var propsInGroup = quantumGroup.details.Select(x => DetailBuilder.GetGameObjectFromDetailInfo(x)).ToArray();
|
||||
|
||||
var groupRoot = new GameObject(quantumGroup.rename);
|
||||
groupRoot.transform.parent = sector?.transform ?? go.transform;
|
||||
groupRoot.transform.localPosition = Vector3.zero;
|
||||
|
||||
var states = new List<QuantumState>();
|
||||
foreach(var prop in propsInGroup)
|
||||
foreach (var prop in propsInGroup)
|
||||
{
|
||||
prop.transform.parent = groupRoot.transform;
|
||||
var state = prop.AddComponent<QuantumState>();
|
||||
@ -81,24 +178,23 @@ namespace NewHorizons.Builder.Props
|
||||
|
||||
if (prop.GetComponentInChildren<ShapeVisibilityTracker>() != null) continue;
|
||||
|
||||
AddBoundsVisibility(prop);
|
||||
BoundsUtilities.AddBoundsVisibility(prop);
|
||||
}
|
||||
|
||||
if (quantumGroup.hasEmptyState)
|
||||
{
|
||||
var template = propsInGroup[0];
|
||||
|
||||
|
||||
var empty = new GameObject("Empty State");
|
||||
empty.transform.parent = groupRoot.transform;
|
||||
var state = empty.AddComponent<QuantumState>();
|
||||
states.Add(state);
|
||||
|
||||
var boxBounds = GetBoundsOfSelfAndChildMeshes(template);
|
||||
var boxBounds = BoundsUtilities.GetBoundsOfSelfAndChildMeshes(template);
|
||||
var boxShape = empty.AddComponent<BoxShape>();
|
||||
boxShape.center = boxBounds.center;
|
||||
boxShape.extents = boxBounds.size;
|
||||
if (Main.Debug) empty.AddComponent<BoxShapeVisualizer>();
|
||||
|
||||
empty.AddComponent<BoxShapeVisualizer>();
|
||||
empty.AddComponent<ShapeVisibilityTracker>();
|
||||
}
|
||||
|
||||
@ -114,120 +210,21 @@ namespace NewHorizons.Builder.Props
|
||||
groupRoot.SetActive(true);
|
||||
}
|
||||
|
||||
public static void MakeShuffleGroup(GameObject go, Sector sector, PlanetConfig config, IModBehaviour mod, QuantumGroupInfo quantumGroup, GameObject[] propsInGroup)
|
||||
public static void MakeShuffleGroup(GameObject go, Sector sector, BaseQuantumGroupInfo quantumGroup, GameObject[] propsInGroup)
|
||||
{
|
||||
//var averagePosition = propsInGroup.Aggregate(Vector3.zero, (avg, prop) => avg + prop.transform.position) / propsInGroup.Count();
|
||||
GameObject shuffleParent = new GameObject("Quantum Shuffle - " + quantumGroup.id);
|
||||
GameObject shuffleParent = new GameObject(quantumGroup.rename);
|
||||
shuffleParent.SetActive(false);
|
||||
shuffleParent.transform.parent = sector?.transform ?? go.transform;
|
||||
shuffleParent.transform.localPosition = Vector3.zero;
|
||||
propsInGroup.ToList().ForEach(p => p.transform.parent = shuffleParent.transform);
|
||||
propsInGroup.ToList().ForEach(p => p.transform.parent = shuffleParent.transform);
|
||||
|
||||
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. what? how?
|
||||
|
||||
AddBoundsVisibility(shuffleParent);
|
||||
|
||||
BoundsUtilities.AddBoundsVisibility(shuffleParent);
|
||||
shuffleParent.SetActive(true);
|
||||
}
|
||||
|
||||
|
||||
struct BoxShapeReciever
|
||||
{
|
||||
public MeshFilter f;
|
||||
public SkinnedMeshRenderer s;
|
||||
public GameObject g;
|
||||
}
|
||||
|
||||
public static void AddBoundsVisibility(GameObject g)
|
||||
{
|
||||
var meshFilters = g.GetComponentsInChildren<MeshFilter>();
|
||||
var skinnedMeshRenderers = g.GetComponentsInChildren<SkinnedMeshRenderer>();
|
||||
|
||||
var boxShapeRecievers = meshFilters
|
||||
.Select(f => new BoxShapeReciever() { f=f, g=f.gameObject })
|
||||
.Concat (
|
||||
skinnedMeshRenderers.Select(s => new BoxShapeReciever() { s=s, g=s.gameObject })
|
||||
)
|
||||
.ToList();
|
||||
|
||||
foreach(var boxshapeReciever in boxShapeRecievers)
|
||||
{
|
||||
var box = boxshapeReciever.g.AddComponent<BoxShape>();
|
||||
boxshapeReciever.g.AddComponent<ShapeVisibilityTracker>();
|
||||
if (Main.Debug) boxshapeReciever.g.AddComponent<BoxShapeVisualizer>();
|
||||
|
||||
var fixer = boxshapeReciever.g.AddComponent<BoxShapeFixer>();
|
||||
fixer.shape = box;
|
||||
fixer.meshFilter = boxshapeReciever.f;
|
||||
fixer.skinnedMeshRenderer = boxshapeReciever.s;
|
||||
}
|
||||
}
|
||||
|
||||
// BUG: ignores skinned guys. this coincidentally makes it work without BoxShapeFixer
|
||||
public static Bounds GetBoundsOfSelfAndChildMeshes(GameObject g)
|
||||
{
|
||||
var meshFilters = g.GetComponentsInChildren<MeshFilter>();
|
||||
var corners = meshFilters.SelectMany(m => GetMeshCorners(m, g)).ToList();
|
||||
|
||||
Bounds b = new Bounds(corners[0], Vector3.zero);
|
||||
corners.ForEach(corner => b.Encapsulate(corner));
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
public static Vector3[] GetMeshCorners(MeshFilter m, GameObject relativeTo = null)
|
||||
{
|
||||
var bounds = m.mesh.bounds;
|
||||
|
||||
var localCorners = new Vector3[]
|
||||
{
|
||||
bounds.min,
|
||||
bounds.max,
|
||||
new Vector3(bounds.min.x, bounds.min.y, bounds.max.z),
|
||||
new Vector3(bounds.min.x, bounds.max.y, bounds.min.z),
|
||||
new Vector3(bounds.max.x, bounds.min.y, bounds.min.z),
|
||||
new Vector3(bounds.min.x, bounds.max.y, bounds.max.z),
|
||||
new Vector3(bounds.max.x, bounds.min.y, bounds.max.z),
|
||||
new Vector3(bounds.max.x, bounds.max.y, bounds.min.z),
|
||||
};
|
||||
|
||||
var globalCorners = localCorners.Select(localCorner => m.transform.TransformPoint(localCorner)).ToArray();
|
||||
|
||||
if (relativeTo == null) return globalCorners;
|
||||
|
||||
return globalCorners.Select(globalCorner => relativeTo.transform.InverseTransformPoint(globalCorner)).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <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;
|
||||
public MeshFilter meshFilter;
|
||||
public SkinnedMeshRenderer skinnedMeshRenderer;
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (meshFilter == null && skinnedMeshRenderer == null) { NHLogger.LogVerbose("Useless BoxShapeFixer, destroying"); DestroyImmediate(this); }
|
||||
|
||||
Mesh sharedMesh = null;
|
||||
if (meshFilter != null) sharedMesh = meshFilter.sharedMesh;
|
||||
if (skinnedMeshRenderer != null) sharedMesh = skinnedMeshRenderer.sharedMesh;
|
||||
|
||||
if (sharedMesh == null) return;
|
||||
if (sharedMesh.bounds.size == Vector3.zero) return;
|
||||
|
||||
shape.size = sharedMesh.bounds.size;
|
||||
shape.center = sharedMesh.bounds.center;
|
||||
|
||||
DestroyImmediate(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
using NewHorizons.Components.Props;
|
||||
using NewHorizons.External.Modules.Props.EchoesOfTheEye;
|
||||
using NewHorizons.Handlers;
|
||||
using NewHorizons.Utility;
|
||||
@ -65,6 +66,9 @@ namespace NewHorizons.Builder.Props
|
||||
var waterVolume = planetGO.GetComponentInChildren<RadialFluidVolume>();
|
||||
fluidDetector._alignmentFluid = waterVolume;
|
||||
fluidDetector._buoyancy.checkAgainstWaves = true;
|
||||
// Rafts were unable to trigger docks because these were disabled for some reason
|
||||
fluidDetector.GetComponent<BoxShape>().enabled = true;
|
||||
fluidDetector.GetComponent<OWCollider>().enabled = true;
|
||||
|
||||
// Light sensors
|
||||
foreach (var lightSensor in raftObject.GetComponentsInChildren<SingleLightSensor>())
|
||||
@ -73,6 +77,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);
|
||||
|
||||
@ -149,7 +176,7 @@ namespace NewHorizons.Builder.Props
|
||||
{
|
||||
try
|
||||
{
|
||||
MakeWhiteboard(go, sector, nhBody.Mod, id, decal, info.whiteboard, nhBody);
|
||||
MakeWhiteboard(go, sector, id, decal, info.whiteboard, nhBody);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -173,8 +200,10 @@ namespace NewHorizons.Builder.Props
|
||||
}
|
||||
}
|
||||
|
||||
public static void MakeWhiteboard(GameObject go, Sector sector, IModBehaviour mod, NomaiRemoteCameraPlatform.ID id, Texture2D decal, RemoteWhiteboardInfo info, NewHorizonsBody nhBody)
|
||||
public static void MakeWhiteboard(GameObject go, Sector sector, NomaiRemoteCameraPlatform.ID id, Texture2D decal, RemoteWhiteboardInfo info, NewHorizonsBody nhBody)
|
||||
{
|
||||
InitPrefabs();
|
||||
var mod = nhBody.Mod;
|
||||
var whiteboard = DetailBuilder.Make(go, sector, mod, _whiteboardPrefab, new DetailInfo(info));
|
||||
whiteboard.SetActive(false);
|
||||
|
||||
@ -213,8 +242,9 @@ 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)
|
||||
{
|
||||
InitPrefabs();
|
||||
var platform = DetailBuilder.Make(go, sector, mod, _remoteCameraPlatformPrefab, new DetailInfo(info));
|
||||
platform.SetActive(false);
|
||||
|
||||
@ -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;
|
||||
@ -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);
|
||||
|
||||
@ -158,7 +158,7 @@ namespace NewHorizons.Builder.Props
|
||||
// Resize it so the force volume goes all the way up
|
||||
var fluidGO = tornadoGO.transform.Find(downwards ? "MockDownTornado_FluidCenter" : "MockUpTornado_FluidCenter");
|
||||
fluidGO.GetComponent<TornadoFluidVolume>()._fluidType = info.fluidType.ConvertToOW(FluidVolume.Type.CLOUD);
|
||||
fluidGO.localScale = new Vector3(1, 2f, 1);
|
||||
fluidGO.localPosition = Vector3.up * 4.8f;
|
||||
|
||||
if (info.tint != null)
|
||||
{
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
using NewHorizons.Utility.OWML;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
@ -37,7 +38,6 @@ namespace NewHorizons.Builder.Props.TranslatorText
|
||||
)
|
||||
.ToArray();
|
||||
|
||||
|
||||
return BuildSpiralGameObject(_points, mesh, goName);
|
||||
}
|
||||
|
||||
@ -67,15 +67,52 @@ namespace NewHorizons.Builder.Props.TranslatorText
|
||||
public struct SpiralProfile {
|
||||
// all of the Vector2 params here refer to a range of valid values
|
||||
public string profileName;
|
||||
|
||||
/// <summary>
|
||||
/// What is this
|
||||
/// </summary>
|
||||
public Vector2 a;
|
||||
|
||||
/// <summary>
|
||||
/// What is this
|
||||
/// </summary>
|
||||
public Vector2 b;
|
||||
|
||||
/// <summary>
|
||||
/// What is this
|
||||
/// </summary>
|
||||
public Vector2 startS;
|
||||
|
||||
/// <summary>
|
||||
/// What is this
|
||||
/// </summary>
|
||||
public Vector2 endS;
|
||||
|
||||
/// <summary>
|
||||
/// What is this
|
||||
/// </summary>
|
||||
public Vector2 skeletonScale;
|
||||
|
||||
/// <summary>
|
||||
/// What is this
|
||||
/// </summary>
|
||||
public int numSkeletonPoints;
|
||||
|
||||
/// <summary>
|
||||
/// What is this
|
||||
/// </summary>
|
||||
public float uvScale;
|
||||
public float innerWidth; // width at the tip
|
||||
public float outerWidth; // width at the base
|
||||
|
||||
/// <summary>
|
||||
/// Width at tip
|
||||
/// </summary>
|
||||
public float innerWidth;
|
||||
|
||||
/// <summary>
|
||||
/// Width at base
|
||||
/// </summary>
|
||||
public float outerWidth;
|
||||
|
||||
public Material material;
|
||||
}
|
||||
|
||||
@ -119,8 +156,6 @@ namespace NewHorizons.Builder.Props.TranslatorText
|
||||
innerWidth = 0.75f,
|
||||
outerWidth = 0.75f,
|
||||
uvScale = 1f/1.8505f,
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -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>();
|
||||
@ -583,7 +583,12 @@ namespace NewHorizons.Builder.Props.TranslatorText
|
||||
case NomaiTextArcInfo.NomaiTextArcType.Stranger when _ghostArcMaterial != null:
|
||||
profile = NomaiTextArcBuilder.strangerSpiralProfile;
|
||||
mat = _ghostArcMaterial;
|
||||
overrideMesh = MeshUtilities.RectangleMeshFromCorners(new Vector3[]{ new Vector3(-0.9f, 0.0f, 0.0f), new Vector3(0.9f, 0.0f, 0.0f), new Vector3(-0.9f, 2.0f, 0.0f), new Vector3(0.9f, 2.0f, 0.0f) });
|
||||
overrideMesh = MeshUtilities.RectangleMeshFromCorners(new Vector3[]{
|
||||
new Vector3(-0.9f, 0.0f, 0.0f),
|
||||
new Vector3(0.9f, 0.0f, 0.0f),
|
||||
new Vector3(-0.9f, 2.0f, 0.0f),
|
||||
new Vector3(0.9f, 2.0f, 0.0f)
|
||||
});
|
||||
overrideColor = new Color(0.0158f, 1.0f, 0.5601f, 1f);
|
||||
break;
|
||||
case NomaiTextArcInfo.NomaiTextArcType.Adult:
|
||||
@ -603,6 +608,19 @@ namespace NewHorizons.Builder.Props.TranslatorText
|
||||
else arc = NomaiTextArcArranger.CreateSpiral(profile, conversationZone).gameObject;
|
||||
}
|
||||
|
||||
// Hardcoded stranger point fix
|
||||
if (type == NomaiTextArcInfo.NomaiTextArcType.Stranger)
|
||||
{
|
||||
Delay.FireOnNextUpdate(() =>
|
||||
{
|
||||
var text = arc.GetComponent<NomaiTextLine>();
|
||||
for (int i = 0; i < text._points.Length; i++)
|
||||
{
|
||||
text._points[i] = new Vector3(0f, 2f * i / text._points.Length, 0f);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (mat != null) arc.GetComponent<MeshRenderer>().sharedMaterial = mat;
|
||||
|
||||
arc.transform.parent = conversationZone.transform;
|
||||
|
||||
@ -28,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);
|
||||
}
|
||||
@ -39,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);
|
||||
@ -62,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;
|
||||
@ -76,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;
|
||||
@ -157,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,22 +7,64 @@ 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();
|
||||
private static Dictionary<ShipLogAstroObject, MapModeInfo> _astroObjectToMapModeInfo = new();
|
||||
|
||||
public static MapModeInfo GetMapModeInfoForAstroObject(ShipLogAstroObject slao)
|
||||
{
|
||||
if (_astroObjectToMapModeInfo.TryGetValue(slao, out var mapModeInfo))
|
||||
{
|
||||
return mapModeInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#region General
|
||||
public static ShipLogAstroObject[][] ConstructMapMode(string systemName, GameObject transformParent, ShipLogAstroObject[][] currentNav, int layer)
|
||||
{
|
||||
_astroObjectToShipLog = new();
|
||||
_astroObjectToMapModeInfo = 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
|
||||
b => !(b.Config.ShipLog?.mapMode?.remove ?? false) && !b.Config.isQuantumState && !b.Config.destroy
|
||||
).ToList();
|
||||
bool flagManualPositionUsed = systemName == "SolarSystem";
|
||||
bool flagAutoPositionUsed = false;
|
||||
@ -37,7 +79,7 @@ namespace NewHorizons.Builder.ShipLog
|
||||
else
|
||||
{
|
||||
flagManualPositionUsed = true;
|
||||
if (body.Config.ShipLog?.mapMode?.manualNavigationPosition == null)
|
||||
if (body.Config.ShipLog?.mapMode != null && body.Config.ShipLog.mapMode.manualNavigationPosition == null && body.Config.ShipLog.mapMode.selectable)
|
||||
{
|
||||
NHLogger.LogError("Navigation position is missing for: " + body.Config.name);
|
||||
return null;
|
||||
@ -71,16 +113,20 @@ namespace NewHorizons.Builder.ShipLog
|
||||
}
|
||||
}
|
||||
|
||||
ShipLogAstroObject[][] newNavMatrix = null;
|
||||
|
||||
if (useAuto)
|
||||
{
|
||||
return ConstructMapModeAuto(bodies, transformParent, greyScaleMaterial, layer);
|
||||
newNavMatrix = ConstructMapModeAuto(bodies, transformParent, greyScaleMaterial, layer);
|
||||
}
|
||||
else if (useManual)
|
||||
{
|
||||
return ConstructMapModeManual(bodies, transformParent, greyScaleMaterial, currentNav, layer);
|
||||
newNavMatrix = ConstructMapModeManual(bodies, transformParent, greyScaleMaterial, currentNav, layer);
|
||||
}
|
||||
|
||||
return null;
|
||||
ReplaceExistingMapModeIcons();
|
||||
|
||||
return newNavMatrix;
|
||||
}
|
||||
|
||||
public static string GetAstroBodyShipLogName(string id)
|
||||
@ -125,6 +171,12 @@ namespace NewHorizons.Builder.ShipLog
|
||||
|
||||
private static ShipLogAstroObject AddShipLogAstroObject(GameObject gameObject, NewHorizonsBody body, Material greyScaleMaterial, int layer)
|
||||
{
|
||||
if (body.Object == null)
|
||||
{
|
||||
NHLogger.LogError($"Tried to make ship logs for planet with null Object: [{body?.Config?.name}]");
|
||||
return null;
|
||||
}
|
||||
|
||||
const float unviewedIconOffset = 15;
|
||||
|
||||
NHLogger.LogVerbose($"Adding ship log astro object for {body.Config.name}");
|
||||
@ -133,6 +185,8 @@ namespace NewHorizons.Builder.ShipLog
|
||||
|
||||
ShipLogAstroObject astroObject = gameObject.AddComponent<ShipLogAstroObject>();
|
||||
astroObject._id = ShipLogHandler.GetAstroObjectId(body);
|
||||
_astroObjectToShipLog[body.Object] = astroObject;
|
||||
_astroObjectToMapModeInfo[astroObject] = body.Config.ShipLog?.mapMode;
|
||||
|
||||
Texture2D image = null;
|
||||
Texture2D outline = null;
|
||||
@ -407,6 +461,11 @@ namespace NewHorizons.Builder.ShipLog
|
||||
|
||||
private static MapModeObject ConstructPrimaryNode(List<NewHorizonsBody> bodies)
|
||||
{
|
||||
float DistanceFromPrimary(NewHorizonsBody body)
|
||||
{
|
||||
return Mathf.Max(body.Config.Orbit.semiMajorAxis, body.Config.Orbit.staticPosition?.Length() ?? 0f);
|
||||
}
|
||||
|
||||
foreach (NewHorizonsBody body in bodies.Where(b => b.Config.Base.centerOfSolarSystem))
|
||||
{
|
||||
bodies.Sort((b, o) => b.Config.Orbit.semiMajorAxis.CompareTo(o.Config.Orbit.semiMajorAxis));
|
||||
@ -538,7 +597,10 @@ namespace NewHorizons.Builder.ShipLog
|
||||
astroObject._unviewedObj.GetComponent<Image>().enabled = false;
|
||||
}
|
||||
node.astroObject = astroObject;
|
||||
if (node.lastSibling != null) ConnectNodeToLastSibling(node, greyScaleMaterial);
|
||||
if (NewHorizons.Main.Debug)
|
||||
{
|
||||
if (node.lastSibling != null) ConnectNodeToLastSibling(node, greyScaleMaterial);
|
||||
}
|
||||
MakeDetails(node.mainBody, newNodeGO.transform, greyScaleMaterial);
|
||||
}
|
||||
#endregion
|
||||
@ -604,5 +666,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
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,6 +154,35 @@ namespace NewHorizons.Builder.ShipLog
|
||||
}
|
||||
}
|
||||
|
||||
public static void MergeEntries(ShipLogManager manager, ShipLogEntry entry, ShipLogEntry existing)
|
||||
{
|
||||
foreach (var fact in entry.GetRumorFacts())
|
||||
{
|
||||
existing._rumorFacts.Add(fact);
|
||||
fact.OnFactRevealed += existing.OnFactRevealed;
|
||||
|
||||
manager._factRevealCount = Mathf.Max(manager._factRevealCount, fact.GetRevealOrder());
|
||||
manager._factList.Add(fact);
|
||||
manager._factDict.Add(fact.GetID(), fact);
|
||||
}
|
||||
foreach (var fact in entry.GetExploreFacts())
|
||||
{
|
||||
existing._exploreFacts.Add(fact);
|
||||
existing._completionFacts.Add(fact);
|
||||
fact.OnFactRevealed += existing.OnFactRevealed;
|
||||
|
||||
manager._factRevealCount = Mathf.Max(manager._factRevealCount, fact.GetRevealOrder());
|
||||
manager._factList.Add(fact);
|
||||
manager._factDict.Add(fact.GetID(), fact);
|
||||
}
|
||||
foreach (var child in entry.GetChildren())
|
||||
{
|
||||
existing._childEntries.Add(child);
|
||||
|
||||
manager.AddEntry(child);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddTranslation(XElement entry)
|
||||
{
|
||||
XElement nameElement = entry.Element("Name");
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@ namespace NewHorizons.Builder.Volumes
|
||||
var volume = VolumeBuilder.Make<WarpVolume>(planetGO, sector, info);
|
||||
|
||||
volume.TargetSolarSystem = info.targetStarSystem;
|
||||
volume.TargetSpawnID = info.spawnPointID;
|
||||
|
||||
return volume;
|
||||
}
|
||||
|
||||
66
NewHorizons/Components/EOTE/DreamDimension.cs
Normal file
66
NewHorizons/Components/EOTE/DreamDimension.cs
Normal file
@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Components.EOTE
|
||||
{
|
||||
public class DreamDimension : MonoBehaviour
|
||||
{
|
||||
private bool initialized;
|
||||
private bool active;
|
||||
private List<GameObject> toggledObjects = new();
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
if (initialized) return;
|
||||
|
||||
foreach (Transform child in transform)
|
||||
{
|
||||
if (child.gameObject.name == "FieldDetector") continue;
|
||||
toggledObjects.Add(child.gameObject);
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
public void SetActive(bool active)
|
||||
{
|
||||
if (this.active != active)
|
||||
{
|
||||
this.active = active;
|
||||
UpdateState();
|
||||
}
|
||||
}
|
||||
|
||||
void Awake()
|
||||
{
|
||||
GlobalMessenger.AddListener("EnterDreamWorld", OnEnterDreamWorld);
|
||||
GlobalMessenger.AddListener("ExitDreamWorld", OnExitDreamWorld);
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
GlobalMessenger.RemoveListener("EnterDreamWorld", OnEnterDreamWorld);
|
||||
GlobalMessenger.RemoveListener("ExitDreamWorld", OnExitDreamWorld);
|
||||
}
|
||||
|
||||
void UpdateState()
|
||||
{
|
||||
foreach (var obj in toggledObjects) obj.SetActive(active);
|
||||
}
|
||||
|
||||
void OnEnterDreamWorld()
|
||||
{
|
||||
SetActive(true);
|
||||
}
|
||||
|
||||
void OnExitDreamWorld()
|
||||
{
|
||||
SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
23
NewHorizons/Components/EOTE/LanternExtinguisher.cs
Normal file
23
NewHorizons/Components/EOTE/LanternExtinguisher.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Components.EOTE;
|
||||
|
||||
public class LanternExtinguisher : MonoBehaviour
|
||||
{
|
||||
public void Update()
|
||||
{
|
||||
if (PlayerState.InDreamWorld() && PlayerState.IsCameraUnderwater())
|
||||
{
|
||||
var heldItem = Locator.GetToolModeSwapper().GetItemCarryTool().GetHeldItem();
|
||||
if (heldItem is DreamLanternItem lantern && lantern._lanternController._lit)
|
||||
{
|
||||
Locator.GetDreamWorldController().ExitDreamWorld(DreamWakeType.LanternSubmerged);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
215
NewHorizons/Components/EOTE/NHSlideCollection.cs
Normal file
215
NewHorizons/Components/EOTE/NHSlideCollection.cs
Normal file
@ -0,0 +1,215 @@
|
||||
using HarmonyLib;
|
||||
using NewHorizons.Builder.Props;
|
||||
using NewHorizons.Utility;
|
||||
using NewHorizons.Utility.Files;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using OWML.Common;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace NewHorizons.Components.EOTE;
|
||||
|
||||
[HarmonyPatch]
|
||||
public class NHSlideCollection : SlideCollection
|
||||
{
|
||||
public string[] slidePaths;
|
||||
public IModBehaviour mod;
|
||||
private HashSet<string> _pathsBeingLoaded = new();
|
||||
/// <summary>
|
||||
/// map of slide path to collections that have this path loaded. used to only unload slide when nothing else is using it
|
||||
/// </summary>
|
||||
public static Dictionary<string, HashSet<NHSlideCollection>> _slidesRequiringPath = new();
|
||||
|
||||
private static ShipLogSlideProjector _shipLogSlideProjector;
|
||||
|
||||
static NHSlideCollection()
|
||||
{
|
||||
SceneManager.sceneUnloaded += (_) =>
|
||||
{
|
||||
foreach (var (slide, collections) in _slidesRequiringPath)
|
||||
{
|
||||
// If it has null, that means some other permanent thing loaded this texture and it will get cleared elsewhere
|
||||
// Otherwise it was loaded by an NHSlideCollection and should be deleted
|
||||
if (collections.Any() && !collections.Contains(null))
|
||||
{
|
||||
ImageUtilities.DeleteTexture(slide);
|
||||
}
|
||||
}
|
||||
_slidesRequiringPath.Clear();
|
||||
};
|
||||
}
|
||||
|
||||
public NHSlideCollection(int startArrSize, IModBehaviour mod, string[] slidePaths) : base(startArrSize)
|
||||
{
|
||||
this.mod = mod;
|
||||
this.slidePaths = slidePaths;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(SlideCollection), nameof(SlideCollection.RequestStreamSlides))]
|
||||
public static bool SlideCollection_RequestStreamSlides(SlideCollection __instance, int[] slideIndices)
|
||||
{
|
||||
if (__instance is NHSlideCollection collection)
|
||||
{
|
||||
foreach (var id in slideIndices)
|
||||
{
|
||||
collection.LoadSlide(id);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(SlideCollection), nameof(SlideCollection.RequestRelease))]
|
||||
public static bool SlideCollection_RequestRelease(SlideCollection __instance, int[] slideIndices)
|
||||
{
|
||||
if (__instance is NHSlideCollection collection)
|
||||
{
|
||||
foreach (var id in slideIndices)
|
||||
{
|
||||
collection.UnloadSlide(id);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(SlideCollection), nameof(SlideCollection.IsStreamedTextureIndexLoaded))]
|
||||
public static bool SlideCollection_IsStreamedTextureIndexLoaded(SlideCollection __instance, int streamIdx, ref bool __result)
|
||||
{
|
||||
if (__instance is NHSlideCollection collection)
|
||||
{
|
||||
__result = collection.IsSlideLoaded(streamIdx);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(SlideCollection), nameof(SlideCollection.GetStreamingTexture))]
|
||||
public static bool SlideCollection_GetStreamingTexture(SlideCollection __instance, int id, ref Texture __result)
|
||||
{
|
||||
if (__instance is NHSlideCollection collection)
|
||||
{
|
||||
__result = collection.LoadSlide(id);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public Texture LoadSlide(int index)
|
||||
{
|
||||
Texture LoadSlideInt(int index)
|
||||
{
|
||||
var wrappedIndex = (index + slides.Length) % slides.Length;
|
||||
var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ProjectionBuilder.InvertedSlideReelCacheFolder, slidePaths[wrappedIndex]);
|
||||
|
||||
// We are the first slide collection container to try and load this image
|
||||
var key = ImageUtilities.GetKey(mod, path);
|
||||
if (!_slidesRequiringPath.ContainsKey(key))
|
||||
{
|
||||
// Something else has loaded this image i.e., AutoProjector or Vision torch. We want to ensure we do not delete it
|
||||
if (ImageUtilities.IsTextureLoaded(mod, path))
|
||||
{
|
||||
// null is dummy value to ensure its never empty (so its not deleted)
|
||||
_slidesRequiringPath[key] = new() { null };
|
||||
}
|
||||
else
|
||||
{
|
||||
_slidesRequiringPath[key] = new();
|
||||
}
|
||||
_slidesRequiringPath[key].Add(this);
|
||||
}
|
||||
|
||||
if (ImageUtilities.IsTextureLoaded(mod, path))
|
||||
{
|
||||
// already loaded
|
||||
var texture = ImageUtilities.GetTexture(mod, path);
|
||||
slides[wrappedIndex]._image = texture;
|
||||
return texture;
|
||||
}
|
||||
else if (!_pathsBeingLoaded.Contains(path))
|
||||
{
|
||||
// not loaded yet, we need to load it
|
||||
var loader = new SlideReelAsyncImageLoader();
|
||||
loader.PathsToLoad.Add((wrappedIndex, path));
|
||||
loader.Start(true, false);
|
||||
loader.imageLoadedEvent.AddListener((Texture2D tex, int index, string originalPath) =>
|
||||
{
|
||||
// weird: sometimes we set image, sometimes we return from GetStreamingTexture. oh well
|
||||
slides[wrappedIndex]._image = tex;
|
||||
_pathsBeingLoaded.Remove(path);
|
||||
if (_shipLogSlideProjector == null)
|
||||
{
|
||||
// Object.FindObjectOfType doesnt work with inactive
|
||||
_shipLogSlideProjector = Resources.FindObjectsOfTypeAll<ShipLogSlideProjector>().FirstOrDefault();
|
||||
}
|
||||
if (_shipLogSlideProjector != null)
|
||||
{
|
||||
// gotta tell ship log we updated the image
|
||||
_shipLogSlideProjector._slideDirty = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
NHLogger.LogVerbose("No ship log slide reel projector exists");
|
||||
}
|
||||
});
|
||||
_pathsBeingLoaded.Add(path);
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// It is being loaded so we just wait
|
||||
return null;
|
||||
}
|
||||
}
|
||||
var texture = LoadSlideInt(index);
|
||||
LoadSlideInt(index - 1);
|
||||
LoadSlideInt(index + 1);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
public bool IsSlideLoaded(int index)
|
||||
{
|
||||
var wrappedIndex = (index + slides.Length) % slides.Length;
|
||||
var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ProjectionBuilder.InvertedSlideReelCacheFolder, slidePaths[wrappedIndex]);
|
||||
return ImageUtilities.IsTextureLoaded(mod, path);
|
||||
}
|
||||
|
||||
public void UnloadSlide(int index)
|
||||
{
|
||||
var wrappedIndex = (index + slides.Length) % slides.Length;
|
||||
var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ProjectionBuilder.InvertedSlideReelCacheFolder, slidePaths[wrappedIndex]);
|
||||
|
||||
// Only unload textures that we were the ones to load in
|
||||
if (ImageUtilities.IsTextureLoaded(mod, path))
|
||||
{
|
||||
var key = ImageUtilities.GetKey(mod, path);
|
||||
_slidesRequiringPath[key].Remove(this);
|
||||
if (!_slidesRequiringPath[key].Any())
|
||||
{
|
||||
NHLogger.LogVerbose($"Slide reel deleting {key} since nobody is using it anymore");
|
||||
ImageUtilities.DeleteTexture(mod, path, ImageUtilities.GetTexture(mod, path));
|
||||
slides[wrappedIndex]._image = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
162
NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs
Normal file
162
NewHorizons/Components/EOTE/NHSlideCollectionContainer.cs
Normal file
@ -0,0 +1,162 @@
|
||||
using HarmonyLib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Components.EOTE;
|
||||
|
||||
[HarmonyPatch(typeof(SlideCollectionContainer))]
|
||||
public class NHSlideCollectionContainer : SlideCollectionContainer
|
||||
{
|
||||
public string[] conditionsToSet;
|
||||
public string[] persistentConditionsToSet;
|
||||
// at some point we'll do streaming on all slides. until then just have an off switch
|
||||
public bool doAsyncLoading = true;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(SlideCollectionContainer), nameof(SlideCollectionContainer.Initialize))]
|
||||
public static bool SlideCollectionContainer_Initialize(SlideCollectionContainer __instance)
|
||||
{
|
||||
if (__instance is NHSlideCollectionContainer container)
|
||||
{
|
||||
if (__instance._initialized)
|
||||
return false;
|
||||
__instance.SetupReadFlags();
|
||||
__instance.RegisterPerSlideCompletion();
|
||||
if (__instance.streamingTexturesAvailable)
|
||||
__instance.SetupStreaming();
|
||||
__instance.BuildMusicRangesIndex();
|
||||
__instance._changeSlidesAllowed = true;
|
||||
__instance._initialized = true;
|
||||
__instance._slideCollection.isVision = __instance._owningItem == null;
|
||||
foreach (var factID in __instance._playWithShipLogFacts)
|
||||
{
|
||||
var fact = Locator.GetShipLogManager().GetFact(factID);
|
||||
fact?.RegisterSlideCollection(__instance._slideCollection);
|
||||
// in original it logs. we dont want that here ig
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(SlideCollectionContainer), nameof(SlideCollectionContainer.SetReadFlag))]
|
||||
public static void SlideCollectionContainer_SetReadFlag(SlideCollectionContainer __instance)
|
||||
{
|
||||
if (__instance is NHSlideCollectionContainer container)
|
||||
{
|
||||
if (container._unreadSlideIndices.Count == 0)
|
||||
{
|
||||
if (container.conditionsToSet != null)
|
||||
{
|
||||
foreach (var condition in container.conditionsToSet)
|
||||
{
|
||||
DialogueConditionManager.SharedInstance.SetConditionState(condition, true);
|
||||
}
|
||||
}
|
||||
if (container.persistentConditionsToSet != null)
|
||||
{
|
||||
foreach (var condition in container.persistentConditionsToSet)
|
||||
{
|
||||
PlayerData.SetPersistentCondition(condition, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(SlideCollectionContainer), nameof(SlideCollectionContainer.NextSlideAvailable))]
|
||||
public static bool SlideCollectionContainer_NextSlideAvailable(SlideCollectionContainer __instance, ref bool __result)
|
||||
{
|
||||
if (__instance is NHSlideCollectionContainer container && container.doAsyncLoading)
|
||||
{
|
||||
__result = ((NHSlideCollection)container.slideCollection).IsSlideLoaded(container.slideIndex + 1);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(SlideCollectionContainer), nameof(SlideCollectionContainer.PrevSlideAvailable))]
|
||||
public static bool SlideCollectionContainer_PrevSlideAvailable(SlideCollectionContainer __instance, ref bool __result)
|
||||
{
|
||||
if (__instance is NHSlideCollectionContainer container && container.doAsyncLoading)
|
||||
{
|
||||
__result = ((NHSlideCollection)container.slideCollection).IsSlideLoaded(container.slideIndex - 1);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(SlideCollectionContainer), nameof(SlideCollectionContainer.UnloadStreamingTextures))]
|
||||
public static bool SlideCollectionContainer_UnloadStreamingTextures(SlideCollectionContainer __instance)
|
||||
{
|
||||
if (__instance is NHSlideCollectionContainer container && container.doAsyncLoading)
|
||||
{
|
||||
for (int i = 0; i < ((NHSlideCollection)container.slideCollection).slidePaths.Length; i++)
|
||||
{
|
||||
((NHSlideCollection)container.slideCollection).UnloadSlide(i);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(SlideCollectionContainer), nameof(SlideCollectionContainer.GetStreamingTexture))]
|
||||
public static bool SlideCollectionContainer_GetStreamingTexture(SlideCollectionContainer __instance, int id, ref Texture __result)
|
||||
{
|
||||
if (__instance is NHSlideCollectionContainer container && container.doAsyncLoading)
|
||||
{
|
||||
__result = ((NHSlideCollection)container.slideCollection).LoadSlide(id);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(SlideCollectionContainer), nameof(SlideCollectionContainer.RequestManualStreamSlides))]
|
||||
public static bool SlideCollectionContainer_RequestManualStreamSlides(SlideCollectionContainer __instance)
|
||||
{
|
||||
if (__instance is NHSlideCollectionContainer container && container.doAsyncLoading)
|
||||
{
|
||||
((NHSlideCollection)container.slideCollection).LoadSlide(__instance._currentSlideIndex);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(SlideCollectionContainer), nameof(SlideCollectionContainer.streamingTexturesAvailable), MethodType.Getter)]
|
||||
public static bool SlideCollectionContainer_streamingTexturesAvailable(SlideCollectionContainer __instance, ref bool __result)
|
||||
{
|
||||
if (__instance is NHSlideCollectionContainer container && container.doAsyncLoading)
|
||||
{
|
||||
__result = ((NHSlideCollection)container.slideCollection).slidePaths != null && ((NHSlideCollection)container.slideCollection).slidePaths.Any();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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>();
|
||||
|
||||
147
NewHorizons/Components/EyeOfTheUniverse/EyeMusicController.cs
Normal file
147
NewHorizons/Components/EyeOfTheUniverse/EyeMusicController.cs
Normal file
@ -0,0 +1,147 @@
|
||||
using NewHorizons.Utility;
|
||||
using NewHorizons.Utility.OWML;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Components.EyeOfTheUniverse
|
||||
{
|
||||
public class EyeMusicController : MonoBehaviour
|
||||
{
|
||||
// Delay between game logic and audio to ensure audio system has time to schedule all loops for the same tick
|
||||
const double TIME_BUFFER_WINDOW = 0.5;
|
||||
|
||||
private List<OWAudioSource> _loopSources = new();
|
||||
private List<OWAudioSource> _finaleSources = new();
|
||||
private bool _transitionToFinale;
|
||||
private bool _isPlaying;
|
||||
private double _segmentEndAudioTime;
|
||||
private float _segmentEndGameTime;
|
||||
public CosmicInflationController CosmicInflationController { get; private set; }
|
||||
|
||||
public void RegisterLoopSource(OWAudioSource src)
|
||||
{
|
||||
src.loop = false;
|
||||
src.SetLocalVolume(1f);
|
||||
src.Stop();
|
||||
src.playOnAwake = false;
|
||||
_loopSources.Add(src);
|
||||
}
|
||||
|
||||
public void RegisterFinaleSource(OWAudioSource src)
|
||||
{
|
||||
src.loop = false;
|
||||
src.SetLocalVolume(1f);
|
||||
src.Stop();
|
||||
src.playOnAwake = false;
|
||||
_finaleSources.Add(src);
|
||||
}
|
||||
|
||||
public void StartPlaying()
|
||||
{
|
||||
if (_isPlaying) return;
|
||||
_isPlaying = true;
|
||||
StartCoroutine(DoLoop());
|
||||
}
|
||||
|
||||
public void TransitionToFinale()
|
||||
{
|
||||
_transitionToFinale = true;
|
||||
|
||||
// Schedule finale for as soon as the current segment loop ends
|
||||
double finaleAudioTime = _segmentEndAudioTime;
|
||||
float finaleGameTime = _segmentEndGameTime;
|
||||
|
||||
// Cancel loop audio
|
||||
foreach (var loopSrc in _loopSources)
|
||||
{
|
||||
loopSrc._audioSource.SetScheduledEndTime(finaleAudioTime);
|
||||
}
|
||||
|
||||
// Set quantum sphere inflation timer
|
||||
var finaleDuration = CosmicInflationController._travelerFinaleSource.clip.length;
|
||||
CosmicInflationController._startFormationTime = Time.time;
|
||||
CosmicInflationController._finishFormationTime = finaleGameTime + finaleDuration - 4f;
|
||||
|
||||
// Play finale in sync
|
||||
foreach (var finaleSrc in _finaleSources)
|
||||
{
|
||||
finaleSrc._audioSource.PlayScheduled(finaleAudioTime);
|
||||
}
|
||||
}
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
// EOTP makes 2 new CosmicInflationControllers for no reason
|
||||
CosmicInflationController = SearchUtilities.Find("EyeOfTheUniverse_Body/Sector_EyeOfTheUniverse/Sector_Campfire/InflationController").GetComponent<CosmicInflationController>();
|
||||
}
|
||||
|
||||
private IEnumerator DoLoop()
|
||||
{
|
||||
// Determine timing using the first loop audio clip (should be Riebeck's banjo loop)
|
||||
var referenceLoopClip = _loopSources.First().clip;
|
||||
double loopDuration = referenceLoopClip.samples / (double)referenceLoopClip.frequency;
|
||||
|
||||
// Vanilla audio divides the loop into 4 segments, but that actually causes weird key shifting during the crossfade
|
||||
int segmentCount = 2;
|
||||
double segmentDuration = loopDuration / segmentCount;
|
||||
|
||||
// Track when the next loop will play, in both audio system time and game time
|
||||
double nextLoopAudioTime = AudioSettings.dspTime + TIME_BUFFER_WINDOW;
|
||||
float nextLoopGameTime = Time.time + (float)TIME_BUFFER_WINDOW;
|
||||
|
||||
while (!_transitionToFinale)
|
||||
{
|
||||
// Play loops in sync
|
||||
double loopStartAudioTime = nextLoopAudioTime;
|
||||
float loopStartGameTime = nextLoopGameTime;
|
||||
|
||||
foreach (var loopSrc in _loopSources)
|
||||
{
|
||||
if (!loopSrc.gameObject.activeInHierarchy) continue;
|
||||
if (loopSrc.loop) continue;
|
||||
// We only need to schedule once and then Unity will loop it for us
|
||||
loopSrc._audioSource.PlayScheduled(loopStartAudioTime);
|
||||
loopSrc.loop = true;
|
||||
}
|
||||
|
||||
// Schedule next loop
|
||||
nextLoopAudioTime += loopDuration;
|
||||
nextLoopGameTime += (float)loopDuration;
|
||||
|
||||
// Track loop segment timing (the current musical verse should always finish playing before the finale)
|
||||
for (int i = 0; i < segmentCount; i++)
|
||||
{
|
||||
_segmentEndAudioTime = loopStartAudioTime + segmentDuration * (i + 1);
|
||||
_segmentEndGameTime = loopStartGameTime + (float)(segmentDuration * (i + 1));
|
||||
|
||||
// Wait until the next segment
|
||||
while (Time.time < _segmentEndGameTime && !_transitionToFinale)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// Interrupt the remaining segments for the finale
|
||||
if (_transitionToFinale) break;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait until the bubble has finished expanding
|
||||
while (Time.time < CosmicInflationController._finishFormationTime)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// Disable audio signals
|
||||
foreach (var loopSrc in _loopSources)
|
||||
{
|
||||
var signal = loopSrc.GetComponent<AudioSignal>();
|
||||
if (signal != null)
|
||||
{
|
||||
signal.SetSignalActivation(false, 0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
NewHorizons/Components/EyeOfTheUniverse/InstrumentZone.cs
Normal file
12
NewHorizons/Components/EyeOfTheUniverse/InstrumentZone.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Components.EyeOfTheUniverse
|
||||
{
|
||||
/// <summary>
|
||||
/// Class does nothing but is used with GetComponent to find instrument zones in the hierarchy
|
||||
/// </summary>
|
||||
public class InstrumentZone : MonoBehaviour
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Components.EyeOfTheUniverse
|
||||
{
|
||||
public class QuantumInstrumentTrigger : MonoBehaviour
|
||||
{
|
||||
public string gatherCondition;
|
||||
|
||||
private QuantumInstrument _quantumInstrument;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_quantumInstrument = GetComponent<QuantumInstrument>();
|
||||
_quantumInstrument.OnFinishGather += OnFinishGather;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
_quantumInstrument.OnFinishGather -= OnFinishGather;
|
||||
}
|
||||
|
||||
private void OnFinishGather()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(gatherCondition))
|
||||
{
|
||||
DialogueConditionManager.SharedInstance.SetConditionState(gatherCondition, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -14,7 +14,11 @@ internal class PlayerShipAtmosphereDetectorFix : MonoBehaviour
|
||||
public void Start()
|
||||
{
|
||||
_fluidDetector = Locator.GetPlayerCameraDetector().GetComponent<PlayerCameraFluidDetector>();
|
||||
_shipAtmosphereVolume = Locator.GetShipBody().transform.Find("Volumes/ShipAtmosphereVolume").GetComponent<SimpleFluidVolume>();
|
||||
_shipAtmosphereVolume = Locator.GetShipBody()?.transform?.Find("Volumes/ShipAtmosphereVolume")?.GetComponent<SimpleFluidVolume>();
|
||||
if (_shipAtmosphereVolume == null)
|
||||
{
|
||||
Destroy(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
|
||||
@ -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;
|
||||
@ -47,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()
|
||||
@ -54,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()
|
||||
@ -88,7 +108,7 @@ namespace NewHorizons.Components.Props
|
||||
|
||||
public void SetActive(bool active)
|
||||
{
|
||||
if (CloseEyes)
|
||||
if (CloseEyes && _playerDoneAwake && LateInitializerManager.isDoneInitializing)
|
||||
{
|
||||
Delay.StartCoroutine(Coroutine(active));
|
||||
}
|
||||
|
||||
76
NewHorizons/Components/Props/DreamSimulationMesh.cs
Normal file
76
NewHorizons/Components/Props/DreamSimulationMesh.cs
Normal file
@ -0,0 +1,76 @@
|
||||
using NewHorizons.Utility.OuterWilds;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.Remoting;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Components.Props
|
||||
{
|
||||
public class DreamSimulationMesh : MonoBehaviour
|
||||
{
|
||||
Transform target;
|
||||
Material mat;
|
||||
MeshFilter sourceMeshFilter;
|
||||
MeshRenderer sourceMeshRenderer;
|
||||
MeshFilter targetMeshFilter;
|
||||
MeshRenderer targetMeshRenderer;
|
||||
StreamingRenderMeshHandle streamingHandle;
|
||||
|
||||
bool initialized;
|
||||
|
||||
public void Init(Transform target, Material mat)
|
||||
{
|
||||
this.target = target;
|
||||
this.mat = mat;
|
||||
|
||||
gameObject.layer = Layer.DreamSimulation;
|
||||
|
||||
sourceMeshFilter = target.GetComponent<MeshFilter>();
|
||||
sourceMeshRenderer = target.GetComponent<MeshRenderer>();
|
||||
targetMeshFilter = gameObject.AddComponent<MeshFilter>();
|
||||
targetMeshRenderer = gameObject.AddComponent<MeshRenderer>();
|
||||
|
||||
transform.SetParent(target.transform, false);
|
||||
|
||||
streamingHandle = target.GetComponent<StreamingRenderMeshHandle>();
|
||||
if (streamingHandle != null)
|
||||
{
|
||||
streamingHandle.OnMeshLoaded += OnMeshLoaded;
|
||||
streamingHandle.OnMeshUnloaded += OnMeshUnloaded;
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
Sync();
|
||||
}
|
||||
|
||||
public void Sync()
|
||||
{
|
||||
if (!initialized) return;
|
||||
targetMeshFilter.sharedMesh = sourceMeshFilter.sharedMesh;
|
||||
targetMeshRenderer.sharedMaterials = sourceMeshRenderer.sharedMaterials.Select(_ => mat).ToArray();
|
||||
targetMeshRenderer.enabled = sourceMeshRenderer.enabled;
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
if (streamingHandle != null)
|
||||
{
|
||||
streamingHandle.OnMeshLoaded -= OnMeshLoaded;
|
||||
streamingHandle.OnMeshUnloaded -= OnMeshUnloaded;
|
||||
}
|
||||
}
|
||||
|
||||
void OnMeshLoaded()
|
||||
{
|
||||
Sync();
|
||||
}
|
||||
|
||||
void OnMeshUnloaded()
|
||||
{
|
||||
Sync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,5 @@
|
||||
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
|
||||
@ -19,6 +12,10 @@ namespace NewHorizons.Components.Props
|
||||
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;
|
||||
@ -42,6 +39,8 @@ namespace NewHorizons.Components.Props
|
||||
public override void PickUpItem(Transform holdTranform)
|
||||
{
|
||||
base.PickUpItem(holdTranform);
|
||||
transform.localPosition = HoldOffset;
|
||||
transform.localEulerAngles = HoldRotation;
|
||||
TriggerPickupConditions();
|
||||
PlayCustomSound(PickupAudio);
|
||||
}
|
||||
@ -56,6 +55,8 @@ namespace NewHorizons.Components.Props
|
||||
public override void SocketItem(Transform socketTransform, Sector sector)
|
||||
{
|
||||
base.SocketItem(socketTransform, sector);
|
||||
transform.localPosition = SocketOffset;
|
||||
transform.localEulerAngles = SocketRotation;
|
||||
TriggerDropConditions();
|
||||
PlayCustomSound(SocketAudio);
|
||||
}
|
||||
|
||||
@ -1,10 +1,3 @@
|
||||
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
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -188,7 +188,11 @@ namespace NewHorizons.Components.Props
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -0,0 +1,78 @@
|
||||
using HarmonyLib;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Components.Quantum;
|
||||
|
||||
/// <summary>
|
||||
/// A quantum object that does nothing but track if its been photographed
|
||||
///
|
||||
/// Adapted from Escape Room (mod I (xen) worked on for Jam 4)
|
||||
/// </summary>
|
||||
internal class SnapshotLockableVisibilityObject : SocketedQuantumObject
|
||||
{
|
||||
public GameObject emptySocketObject;
|
||||
|
||||
public override bool ChangeQuantumState(bool skipInstantVisibilityCheck) => true;
|
||||
}
|
||||
|
||||
|
||||
[HarmonyPatch(typeof(SocketedQuantumObject))]
|
||||
public static class SocketedQuantumObjectPatches
|
||||
{
|
||||
private static bool _skipPatch;
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(SocketedQuantumObject), nameof(SocketedQuantumObject.MoveToSocket))]
|
||||
private static bool SocketedQuantumObject_MoveToSocket(SocketedQuantumObject __instance, QuantumSocket socket)
|
||||
{
|
||||
// Double check this is a normal allowed move, then do rotation stuff
|
||||
if (socket.GetVisibilityObject() is SnapshotLockableVisibilityObject qObj1 && (!qObj1.IsLocked() || _skipPatch))
|
||||
{
|
||||
if (qObj1._gravityVolume != null && qObj1._alignWithGravity)
|
||||
{
|
||||
Vector3 toDirection = qObj1.emptySocketObject.transform.position - qObj1._gravityVolume.GetCenterPosition();
|
||||
Quaternion lhs = Quaternion.FromToRotation(qObj1.emptySocketObject.transform.up, toDirection);
|
||||
qObj1.emptySocketObject.transform.rotation = lhs * qObj1.emptySocketObject.transform.rotation;
|
||||
}
|
||||
if (qObj1._randomYRotation)
|
||||
{
|
||||
float d = Random.Range(0f, 360f);
|
||||
qObj1.emptySocketObject.transform.localRotation *= Quaternion.Euler(Vector3.up * d);
|
||||
}
|
||||
}
|
||||
|
||||
if (_skipPatch)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (socket.GetVisibilityObject() is SnapshotLockableVisibilityObject qObj)
|
||||
{
|
||||
// Do not allow it to switch to a socket that is photographed
|
||||
// Instead try to swap with an occupied socket
|
||||
if (qObj.IsLockedByProbeSnapshot())
|
||||
{
|
||||
_skipPatch = true;
|
||||
// Try to swap
|
||||
var swapSocket = __instance._socketList.FirstOrDefault(x => x._quantumObject != null && x._quantumObject != __instance && !x._quantumObject.IsLocked());
|
||||
if (swapSocket != null)
|
||||
{
|
||||
var originalSocket = __instance.GetCurrentSocket();
|
||||
var otherQObj = swapSocket._quantumObject as SocketedQuantumObject;
|
||||
otherQObj.MoveToSocket(socket);
|
||||
__instance.MoveToSocket(swapSocket);
|
||||
otherQObj.MoveToSocket(originalSocket);
|
||||
}
|
||||
else
|
||||
{
|
||||
__instance.MoveToSocket(__instance.GetCurrentSocket());
|
||||
}
|
||||
|
||||
_skipPatch = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
@ -70,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()
|
||||
@ -86,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);
|
||||
}
|
||||
|
||||
@ -199,6 +209,12 @@ namespace NewHorizons.Components.ShipLog
|
||||
|
||||
private void UpdateMapCamera()
|
||||
{
|
||||
if (_starSystemCards.Count == 0)
|
||||
{
|
||||
NHLogger.LogWarning("Showing star chart mode when there are no available systems");
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 b = -_starSystemCards[_cardIndex].transform.localPosition;
|
||||
float num = Mathf.InverseLerp(_startPanTime, _startPanTime + _panDuration, Time.unscaledTime);
|
||||
num = 1f - (num - 1f) * (num - 1f);
|
||||
@ -282,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);
|
||||
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,11 @@
|
||||
using NewHorizons.Handlers;
|
||||
|
||||
namespace NewHorizons.Components.Volumes
|
||||
{
|
||||
public class BlackHoleWarpVolume : BlackHoleDestructionVolume
|
||||
{
|
||||
public string TargetSolarSystem { get; set; }
|
||||
public string TargetSpawnID { get; set; }
|
||||
|
||||
public override void Awake()
|
||||
{
|
||||
@ -19,6 +22,7 @@ namespace NewHorizons.Components.Volumes
|
||||
{
|
||||
Locator.GetPlayerAudioController().PlayOneShotInternal(AudioType.BH_BlackHoleEmission);
|
||||
Main.Instance.ChangeCurrentStarSystem(TargetSolarSystem, PlayerState.AtFlightConsole());
|
||||
PlayerSpawnHandler.TargetSpawnID = TargetSpawnID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
using NewHorizons.Handlers;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.Components.Volumes
|
||||
@ -5,6 +6,7 @@ namespace NewHorizons.Components.Volumes
|
||||
internal class WarpVolume : BaseVolume
|
||||
{
|
||||
public string TargetSolarSystem;
|
||||
public string TargetSpawnID;
|
||||
|
||||
public override void OnTriggerVolumeEntry(GameObject hitObj)
|
||||
{
|
||||
@ -13,6 +15,7 @@ namespace NewHorizons.Components.Volumes
|
||||
if (Main.Instance.CurrentStarSystem != TargetSolarSystem) // Otherwise it really breaks idk why
|
||||
{
|
||||
Main.Instance.ChangeCurrentStarSystem(TargetSolarSystem, PlayerState.AtFlightConsole());
|
||||
PlayerSpawnHandler.TargetSpawnID = TargetSpawnID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1
NewHorizons/External/Configs/AddonConfig.cs
vendored
1
NewHorizons/External/Configs/AddonConfig.cs
vendored
@ -41,6 +41,7 @@ namespace NewHorizons.External.Configs
|
||||
/// <summary>
|
||||
/// The path to the addons subtitle for the main menu.
|
||||
/// Defaults to "subtitle.png".
|
||||
/// The dimensions of the Echoes of the Eye subtitle is 669 x 67, so aim for that size
|
||||
/// </summary>
|
||||
public string subtitlePath = "subtitle.png";
|
||||
}
|
||||
|
||||
189
NewHorizons/External/Configs/PlanetConfig.cs
vendored
189
NewHorizons/External/Configs/PlanetConfig.cs
vendored
@ -11,9 +11,7 @@ using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NewHorizons.External.Configs
|
||||
{
|
||||
@ -25,9 +23,8 @@ namespace NewHorizons.External.Configs
|
||||
{
|
||||
#region Fields
|
||||
/// <summary>
|
||||
/// Unique name of your planet
|
||||
/// Unique name of your planet. If not specified, the file name (without the extension) is used.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string name;
|
||||
|
||||
/// <summary>
|
||||
@ -55,6 +52,13 @@ namespace NewHorizons.External.Configs
|
||||
/// </summary>
|
||||
public bool destroy;
|
||||
|
||||
/// <summary>
|
||||
/// Do we track the position of this body when calculating the solar system radius?
|
||||
/// `true` if you want the map zoom speed, map panning distance/speed, map camera farclip plane,
|
||||
/// and autopilot-returning-to-solar-system to adjust to this planet's orbit
|
||||
/// </summary>
|
||||
[DefaultValue(true)] public bool trackForSolarSystemRadius = true;
|
||||
|
||||
/// <summary>
|
||||
/// A list of paths to child GameObjects to destroy on this planet
|
||||
/// </summary>
|
||||
@ -93,6 +97,16 @@ namespace NewHorizons.External.Configs
|
||||
/// </summary>
|
||||
public CloakModule Cloak;
|
||||
|
||||
/// <summary>
|
||||
/// Make this planet part of the dream world
|
||||
/// </summary>
|
||||
public DreamModule Dream;
|
||||
|
||||
/// <summary>
|
||||
/// Add features exclusive to the Eye of the Universe scene
|
||||
/// </summary>
|
||||
public EyeOfTheUniverseModule EyeOfTheUniverse;
|
||||
|
||||
/// <summary>
|
||||
/// Make this body into a focal point (barycenter)
|
||||
/// </summary>
|
||||
@ -113,6 +127,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>
|
||||
@ -214,6 +233,7 @@ namespace NewHorizons.External.Configs
|
||||
if (Base == null) Base = new BaseModule();
|
||||
if (Orbit == null) Orbit = new OrbitModule();
|
||||
if (ReferenceFrame == null) ReferenceFrame = new ReferenceFrameModule();
|
||||
if (MapMarker == null) MapMarker = new MapMarkerModule();
|
||||
}
|
||||
|
||||
public void Validate()
|
||||
@ -226,56 +246,19 @@ namespace NewHorizons.External.Configs
|
||||
if (Bramble?.dimension != null) canShowOnTitle = false;
|
||||
if (Orbit?.staticPosition != null) Orbit.isStatic = true;
|
||||
|
||||
// For each quantum group, verify the following:
|
||||
// this group's id should be unique
|
||||
// if type == sockets, group.sockets should not be null or empty
|
||||
// if type == sockets, count every prop that references this group. the number should be < group.sockets.Count
|
||||
// if type == sockets, for each socket, if rotation == null, rotation = Vector3.zero
|
||||
// if type == sockets, for each socket, position must not be null
|
||||
// For each detail prop,
|
||||
// if detail.quantumGroupID != null, there exists a quantum group with that id
|
||||
if (Props?.quantumGroups != null && Props?.details != null)
|
||||
{
|
||||
Dictionary<string, QuantumGroupInfo> existingGroups = new Dictionary<string, QuantumGroupInfo>();
|
||||
foreach (var quantumGroup in Props.quantumGroups)
|
||||
{
|
||||
if (existingGroups.ContainsKey(quantumGroup.id)) { NHLogger.LogWarning($"Duplicate quantumGroup id found: {quantumGroup.id}"); quantumGroup.type = QuantumGroupType.FailedValidation; }
|
||||
|
||||
existingGroups[quantumGroup.id] = quantumGroup;
|
||||
if (quantumGroup.type == QuantumGroupType.Sockets)
|
||||
{
|
||||
if (quantumGroup.sockets?.Length == 0) { NHLogger.LogError($"quantumGroup {quantumGroup.id} is of type \"sockets\" but has no defined sockets."); quantumGroup.type = QuantumGroupType.FailedValidation; }
|
||||
else
|
||||
{
|
||||
foreach (var socket in quantumGroup.sockets)
|
||||
{
|
||||
if (socket.rotation == null) socket.rotation = UnityEngine.Vector3.zero;
|
||||
if (socket.position == null) { NHLogger.LogError($"quantumGroup {quantumGroup.id} has a socket without a position."); quantumGroup.type = QuantumGroupType.FailedValidation; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var existingGroupsPropCounts = new Dictionary<string, int>();
|
||||
foreach (var prop in Props?.details)
|
||||
{
|
||||
if (prop.quantumGroupID == null) continue;
|
||||
if (!existingGroups.ContainsKey(prop.quantumGroupID)) NHLogger.LogWarning($"A prop wants to be a part of quantum group {prop.quantumGroupID}, but this group does not exist.");
|
||||
else existingGroupsPropCounts[prop.quantumGroupID] = existingGroupsPropCounts.GetValueOrDefault(prop.quantumGroupID) + 1;
|
||||
}
|
||||
|
||||
foreach (var quantumGroup in Props.quantumGroups)
|
||||
{
|
||||
if (quantumGroup.type == QuantumGroupType.Sockets && existingGroupsPropCounts.GetValueOrDefault(quantumGroup.id) >= quantumGroup.sockets?.Length)
|
||||
{
|
||||
NHLogger.LogError($"quantumGroup {quantumGroup.id} is of type \"sockets\" and has more props than sockets.");
|
||||
quantumGroup.type = QuantumGroupType.FailedValidation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stars and focal points shouldnt be destroyed by stars
|
||||
if (Star != null || FocalPoint != null) Base.invulnerableToSun = true;
|
||||
if (Star != null || FocalPoint != null) Base.hasFluidDetector = false;
|
||||
|
||||
// Disable map marker for dream dimensions
|
||||
if (Dream != null && Dream.inDreamWorld) MapMarker.enabled = false;
|
||||
|
||||
// User error #983
|
||||
// This will not catch if they wrote the two names slightly differently but oh well don't be stupid
|
||||
// Ideally we should just check for loops in PlanetGraph
|
||||
if (Orbit.primaryBody == name && !string.IsNullOrEmpty(Orbit.primaryBody))
|
||||
{
|
||||
throw new Exception($"You set {name} to orbit itself, that is invalid. The planet will not load.");
|
||||
}
|
||||
}
|
||||
|
||||
public void Migrate()
|
||||
@ -307,6 +290,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)
|
||||
@ -535,6 +520,22 @@ namespace NewHorizons.External.Configs
|
||||
};
|
||||
}
|
||||
|
||||
// Spawn points are now a list
|
||||
if (Spawn != null && Spawn.playerSpawn != null && Spawn.playerSpawnPoints == null)
|
||||
{
|
||||
Spawn.playerSpawnPoints = new SpawnModule.PlayerSpawnPoint[] { Spawn.playerSpawn };
|
||||
}
|
||||
if (Spawn != null && Spawn.shipSpawn != null && Spawn.shipSpawnPoints == null)
|
||||
{
|
||||
Spawn.shipSpawnPoints = new SpawnModule.ShipSpawnPoint[] { Spawn.shipSpawn };
|
||||
}
|
||||
|
||||
// Because these guys put TWO spawn points
|
||||
if (starSystem == "2walker2.OogaBooga" && name == "The Campground")
|
||||
{
|
||||
Spawn.playerSpawnPoints[0].isDefault = true;
|
||||
}
|
||||
|
||||
// Remote dialogue trigger reorganized to use GeneralPointPropInfo
|
||||
if (Props?.dialogue != null)
|
||||
{
|
||||
@ -656,6 +657,88 @@ namespace NewHorizons.External.Configs
|
||||
if (destructionVolume.onlyAffectsPlayerAndShip) destructionVolume.onlyAffectsPlayerRelatedBodies = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (Base.invulnerableToSun)
|
||||
{
|
||||
Base.hasFluidDetector = false;
|
||||
}
|
||||
|
||||
// OLD QUANTUM VALIDATION
|
||||
// For each quantum group, verify the following:
|
||||
// this group's id should be unique
|
||||
// if type == sockets, group.sockets should not be null or empty
|
||||
// if type == sockets, count every prop that references this group. the number should be < group.sockets.Count
|
||||
// if type == sockets, for each socket, if rotation == null, rotation = Vector3.zero
|
||||
// if type == sockets, for each socket, position must not be null
|
||||
// For each detail prop,
|
||||
// if detail.quantumGroupID != null, there exists a quantum group with that id
|
||||
if (Props?.quantumGroups != null && Props?.details != null)
|
||||
{
|
||||
Dictionary<string, QuantumGroupInfo> existingGroups = new Dictionary<string, QuantumGroupInfo>();
|
||||
foreach (var quantumGroup in Props.quantumGroups)
|
||||
{
|
||||
if (existingGroups.ContainsKey(quantumGroup.id)) { NHLogger.LogWarning($"Duplicate quantumGroup id found: {quantumGroup.id}"); quantumGroup.type = QuantumGroupType.FailedValidation; }
|
||||
|
||||
existingGroups[quantumGroup.id] = quantumGroup;
|
||||
if (quantumGroup.type == QuantumGroupType.Sockets)
|
||||
{
|
||||
if (quantumGroup.sockets?.Length == 0) { NHLogger.LogError($"quantumGroup {quantumGroup.id} is of type \"sockets\" but has no defined sockets."); quantumGroup.type = QuantumGroupType.FailedValidation; }
|
||||
else
|
||||
{
|
||||
foreach (var socket in quantumGroup.sockets)
|
||||
{
|
||||
if (socket.rotation == null) socket.rotation = UnityEngine.Vector3.zero;
|
||||
if (socket.position == null) { NHLogger.LogError($"quantumGroup {quantumGroup.id} has a socket without a position."); quantumGroup.type = QuantumGroupType.FailedValidation; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var existingGroupsPropCounts = new Dictionary<string, int>();
|
||||
foreach (var prop in Props?.details)
|
||||
{
|
||||
if (prop.quantumGroupID == null) continue;
|
||||
if (!existingGroups.ContainsKey(prop.quantumGroupID)) NHLogger.LogWarning($"A prop wants to be a part of quantum group {prop.quantumGroupID}, but this group does not exist.");
|
||||
else existingGroupsPropCounts[prop.quantumGroupID] = existingGroupsPropCounts.GetValueOrDefault(prop.quantumGroupID) + 1;
|
||||
}
|
||||
|
||||
foreach (var quantumGroup in Props.quantumGroups)
|
||||
{
|
||||
if (quantumGroup.type == QuantumGroupType.Sockets && existingGroupsPropCounts.GetValueOrDefault(quantumGroup.id) > quantumGroup.sockets?.Length)
|
||||
{
|
||||
NHLogger.LogError($"quantumGroup {quantumGroup.id} is of type \"sockets\" and has more props than sockets.");
|
||||
quantumGroup.type = QuantumGroupType.FailedValidation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Props != null && Props.quantumGroups != null)
|
||||
{
|
||||
var socketQuantumGroups = Props.quantumGroups.Where(x => x.type == QuantumGroupType.Sockets).Select(x => new SocketQuantumGroupInfo()
|
||||
{
|
||||
rename = "Quantum Sockets - " + x.id,
|
||||
sockets = x.sockets,
|
||||
details = Props.details.Where(y => y.quantumGroupID == x.id).Select(x => new QuantumDetailInfo(x)).ToArray()
|
||||
});
|
||||
if (socketQuantumGroups.Any())
|
||||
{
|
||||
Props.socketQuantumGroups = socketQuantumGroups.ToArray();
|
||||
}
|
||||
var stateQuantumGroups = Props.quantumGroups.Where(x => x.type == QuantumGroupType.States).Select(x => new StateQuantumGroupInfo()
|
||||
{
|
||||
rename = "Quantum States - " + x.id,
|
||||
hasEmptyState = x.hasEmptyState,
|
||||
loop = x.loop,
|
||||
sequential = x.sequential,
|
||||
details = Props.details.Where(y => y.quantumGroupID == x.id).Select(x => new QuantumDetailInfo(x)).ToArray()
|
||||
});
|
||||
if (stateQuantumGroups.Any())
|
||||
{
|
||||
Props.stateQuantumGroups = stateQuantumGroups.ToArray();
|
||||
}
|
||||
|
||||
Props.details = Props.details.Where(x => string.IsNullOrEmpty(x.quantumGroupID)).ToArray();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
112
NewHorizons/External/Configs/StarSystemConfig.cs
vendored
112
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;
|
||||
@ -15,6 +16,17 @@ namespace NewHorizons.External.Configs
|
||||
[JsonObject]
|
||||
public class StarSystemConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique name of your system. If not specified, the file name (without the extension) is used.
|
||||
/// </summary>
|
||||
public string name;
|
||||
|
||||
/// <summary>
|
||||
/// When changing star systems are you allowed to bring items into this system?
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
public bool allowOutsideItems = true;
|
||||
|
||||
/// <summary>
|
||||
/// In this system should the player be able to rotate their map camera freely or be stuck above the plane of the solar system?
|
||||
/// </summary>
|
||||
@ -31,10 +43,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>
|
||||
@ -45,12 +76,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>
|
||||
@ -82,11 +107,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>
|
||||
@ -192,6 +220,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 +350,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 +368,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);
|
||||
@ -319,6 +400,11 @@ namespace NewHorizons.External.Configs
|
||||
#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 +439,10 @@ namespace NewHorizons.External.Configs
|
||||
Vessel.warpExit.attachToVessel = true;
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrEmpty(factRequiredToExitViaWarpDrive))
|
||||
{
|
||||
canExitViaWarpDrive = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
@ -13,7 +13,7 @@ namespace NewHorizons.External.Modules
|
||||
[Range(-1, 200)] [DefaultValue(-1)] public int amount = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Angle between the rings and the equatorial plane of the planet.
|
||||
/// Angle between the belt and the equatorial plane of the planet.
|
||||
/// </summary>
|
||||
public float inclination;
|
||||
|
||||
@ -23,7 +23,7 @@ namespace NewHorizons.External.Modules
|
||||
[Range(0f, double.MaxValue)] public float innerRadius;
|
||||
|
||||
/// <summary>
|
||||
/// Angle defining the point where the rings rise up from the planet's equatorial plane if inclination is nonzero.
|
||||
/// Angle defining the point where the belt rises up from the planet's equatorial plane if inclination is nonzero.
|
||||
/// </summary>
|
||||
public float longitudeOfAscendingNode;
|
||||
|
||||
@ -45,7 +45,7 @@ namespace NewHorizons.External.Modules
|
||||
[Range(0f, double.MaxValue)] public float outerRadius;
|
||||
|
||||
/// <summary>
|
||||
/// How the asteroids are generated
|
||||
/// How the asteroids are generated, unless you supply a detail yourself using "assetBundle" and "path"
|
||||
/// </summary>
|
||||
public ProcGenModule procGen;
|
||||
|
||||
@ -53,5 +53,30 @@ namespace NewHorizons.External.Modules
|
||||
/// Number used to randomize asteroid positions
|
||||
/// </summary>
|
||||
public int randomSeed;
|
||||
|
||||
/// <summary>
|
||||
/// You can use this to load a custom asset or ingame object, instead of using ProcGen. It will be scaled by "minSize" and "maxSize", so ideally it should be near a 1 meter radius.
|
||||
/// This is a relative filepath to an asset-bundle to load the prefab defined in `path` from.
|
||||
/// </summary>
|
||||
public string assetBundle;
|
||||
|
||||
/// <summary>
|
||||
/// You can use this to load a custom asset or ingame object, instead of using ProcGen. It will be scaled by "minSize" and "maxSize", so ideally it should be near a 1 meter radius.
|
||||
/// This is either the path in the scene hierarchy of the item to copy or the path to the object in the supplied asset bundle.
|
||||
/// </summary>
|
||||
public string path;
|
||||
|
||||
/// <summary>
|
||||
/// Surface gravity of the asteroids.
|
||||
/// </summary>
|
||||
[Range(0f, double.MaxValue)]
|
||||
[DefaultValue(1)]
|
||||
public float gravity = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Should the detail of the asteroid be randomly oriented, or should it point towards the center.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
public bool randomOrientation = true;
|
||||
}
|
||||
}
|
||||
@ -61,6 +61,7 @@ namespace NewHorizons.External.Modules
|
||||
/// <summary>
|
||||
/// Relative filepath to the fog color ramp texture, if you put fog.
|
||||
/// x axis is angle to sun (left at midnight, right at noon), y axis is distance to camera (close at bottom, far at top).
|
||||
/// Optional. If you set fogTint, a default fog ramp will be tinted for you.
|
||||
/// </summary>
|
||||
public string fogRampPath;
|
||||
|
||||
@ -107,6 +108,12 @@ namespace NewHorizons.External.Modules
|
||||
/// </summary>
|
||||
[DefaultValue(300f)] public float maxShockSpeed = 300f;
|
||||
|
||||
/// <summary>
|
||||
/// Will the ship automatically try to orient itself to face upwards while in this volume?
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
public bool allowShipAutoroll = true;
|
||||
|
||||
[JsonObject]
|
||||
public class CloudInfo
|
||||
{
|
||||
|
||||
24
NewHorizons/External/Modules/BaseModule.cs
vendored
24
NewHorizons/External/Modules/BaseModule.cs
vendored
@ -37,14 +37,11 @@ namespace NewHorizons.External.Modules
|
||||
public float groundSize;
|
||||
|
||||
/// <summary>
|
||||
/// If the body should have a marker on the map screen.
|
||||
/// Is this planet able to detect fluid volumes? Disabling this means that entering a star or lava volume will not destroy this planet
|
||||
/// May have adverse effects if anglerfish are added to this planet, disable this if you want those to work (they have fluid volumes in their mouths)
|
||||
/// </summary>
|
||||
public bool hasMapMarker;
|
||||
|
||||
/// <summary>
|
||||
/// Can this planet survive entering a star?
|
||||
/// </summary>
|
||||
public bool invulnerableToSun;
|
||||
[DefaultValue(true)]
|
||||
public bool hasFluidDetector = true;
|
||||
|
||||
/// <summary>
|
||||
/// Do we show the minimap when walking around this planet?
|
||||
@ -63,6 +60,8 @@ namespace NewHorizons.External.Modules
|
||||
|
||||
/// <summary>
|
||||
/// A scale height used for a number of things. Should be the approximate radius of the body.
|
||||
///
|
||||
/// Affected settings include: Base sector size, proxy body scaling, surface gravity
|
||||
/// </summary>
|
||||
public float surfaceSize;
|
||||
|
||||
@ -71,6 +70,11 @@ namespace NewHorizons.External.Modules
|
||||
/// </summary>
|
||||
[DefaultValue(0)] public int gravityVolumePriority = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Optional. Overrides how far the player must be from the planet for their feet to automatically orient towards the ground.
|
||||
/// </summary>
|
||||
public int? gravityAlignmentRadiusOverride = null;
|
||||
|
||||
/// <summary>
|
||||
/// Apply physics to this planet when you bump into it. Will have a spherical collider the size of surfaceSize.
|
||||
/// For custom colliders they have to all be convex and you can leave surface size as 0.
|
||||
@ -87,6 +91,9 @@ namespace NewHorizons.External.Modules
|
||||
|
||||
#region Obsolete
|
||||
|
||||
[Obsolete("invulnerableToSun is deprecated, please use hasFluidDetector instead")]
|
||||
public bool invulnerableToSun;
|
||||
|
||||
[Obsolete("IsSatellite is deprecated, please use ShowMinimap instead")]
|
||||
public bool isSatellite;
|
||||
|
||||
@ -108,6 +115,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;
|
||||
|
||||
|
||||
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